Is there a limit of object in openGL?

Viewed 350

I want to draw 2000 spheres using OpenGL in Visual C++. The following code draw 1000 spheres and the result looks fine.

But when I increase the number of spheres of 2000 (see the partial below code and highlighted by ** ), it failed. The following error message appears.

"freeglut : fgInitGL2 : fghGenBuffers is NULL"

Could you help me to solve this problem?

    for (int j = 0; j < 10; j++) {
        for (int k = 0; k < 10; k++) {
            **for (int l = 0; l < 20; l++) { \\ for (int l = 0; l < 10; l++)**
                glPushMatrix();
                glTranslatef(j, k, l);
                gluSphere(myQuad, 0.5, 100, 100);
                glPopMatrix();
            }
        }
    }

This is a full code for test.

#include <GL/glew.h>
#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fstream>
#include <string>
#include <cstring>
#include <cmath>
#include <iostream>

int selectedObject = 1;
bool drawThatAxis = 0;
bool lightEffect = 1;

float fovy = 60.0, aspect = 1.0, zNear = 1.0, zFar = 100.0;

float depth = 8;
float phi = 0, theta = 0;
float downX, downY;
bool leftButton = false, middleButton = false;

GLfloat white[3] = { 1.0, 1.0, 1.0 };

void displayCallback(void);

GLdouble width, height;
int wd;

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH);
    glutInitWindowSize(800,600);

    wd = glutCreateWindow("3D Molecules");

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);
    glClearColor(0.0, 0.0, 0.0, 0.0);

    GLuint id;
    id = glGenLists(1);

    GLUquadric* myQuad;
    myQuad = gluNewQuadric();

    glNewList(id, GL_COMPILE);
    
    for (int j = 0; j < 10; j++) {
        for (int k = 0; k < 10; k++) {
            for (int l = 0; l < 10; l++) {
                glPushMatrix();
                glTranslatef(j, k, l);
                gluSphere(myQuad, 0.5, 100, 100);
                glPopMatrix();
            }
        }
    }
    
    glEndList();

    glutDisplayFunc(displayCallback);
    glutMainLoop();
    return 0;
}
void displayCallback(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(fovy, aspect, zNear, zFar);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    gluLookAt(0, 0, 40, 0, 0, 0, 0, 1, 0);

    glTranslatef(0.0, 0.0, -depth);
    glRotatef(-theta, 1.0, 0.0, 0.0);
    glRotatef(phi, 0.0, 1.0, 0.0);

    if (lightEffect) {
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
    }
    else
    {
        glDisable(GL_LIGHTING);
        glDisable(GL_LIGHT0);
    }

    switch (selectedObject)
    {
    case (1):
        glCallList(1);
        break;
    default:
        break;
    }

    glFlush();
}
2 Answers

Is there a limit of object in openGL?

OpenGL doesn't define limits like a "maximum number of objects". Application is able to draw as many objects as possible as they fit into CPU memory, but usually drawing becomes unreliably slow before application hits memory limits. Even when all texture and vertex data do not fit into GPU memory, OpenGL still doesn't fail and keep drawing by constantly uploading CPU->GPU memory on each frame.

So if we come back to the question of OpenGL limits - indeed, there are memory limits, as you may see from another similar question. Your code doesn't actually check for any OpenGL errors using glGetError(), hence your conclusion about fghGenBuffers() being a root cause is misleading. I would expected GL_OUT_OF_MEMORY error to appear in your case. Modern OpenGL also defines a more sophisticated mechanism for reporting errors - ARB_debug_output.

Display Lists is a very archaic mechanism in OpenGL world, intended to optimize drawing of large amounts of data by "remembering" a sequence of OpenGL commands into some internal driver-managed caches. This mechanism was commonly used before wide adoption of Vertex Buffer Objects, which have been added to OpenGL 1.5 as a more straightforward and efficient way to control vertex data memory, and before Vulkan and GL_NV_command_list re-invented Command Buffers as a more reliable interface for caching a sequence of GPU commands.

A big design issue of Display Lists mechanism is an unpredictable memory management and extremely varying implementation across vendors (from very poor to extremely optimized). Modern graphic drivers try to uploaded vertex data onto GPU memory implicitly while compiling Display Lists, but what they actually do remains hidden.

Archaic GLU library is another source of a mystery in your code, as it is difficult to estimate a memory utilized by gluSphere(). A pessimistic calculations show:

size_t aNbSpheres = 10 * 10 * 20;
size_t aNbSlices, aNbStacks = 100;
size_t aNbTriangles = aNbSlices * aNbSlices * 2;
size_t aNbNodes = aNbSpheres * aNbTriangles * 3; // non-indexed array
size_t aNodeSize = (3 * sizeof(GLfloat)) * 2; // position + normal
size_t aMemSize = aNbNodes * aNodeSize;
size_t aMemSizeMiB = aMemSize / 1024 / 1024;

that just vertex data of 2000 spheres may utilize about 2.746 GiB of memory! If your application is built in 32-bit mode then no surprise it does hit 32-bit address space memory limits. But even in case of 64-bit application, OpenGL driver implementation might hit some internal limits, which will be reported by the same GL_OUT_OF_MEMORY error.

Regardless of memory limits, your code is trying to draw around 40M of triangles. This is not something impossible for a fast modern GPU hardware, but it might be really slow on low-end embedded graphics.

So what could be done next?

  • Learn OpenGL debugging practices - using glGetError() and/or ARB_debug_output to localize the place and root cause of this and other issues.
  • Reduce gluSphere() tessellation parameters.
  • Generate a Display List of a single sphere and draw it many times. The instancing dramatically reduces memory consumption. This, however, may be a slower alternative to drawing all sphere at once (but 2000 draw calls is not that big for modern CPU).
  • Replace obsolete GLU library with direct generation of vertex data - sphere tessellation is not that difficult to implement and there are a lot of samples around the web.
  • Learn Vertex Buffer Objects and use them instead of obsolete Display Lists.
  • Learn GLSL and modern OpenGL so that you may implement hardware instancing for drawing sphere most efficiently.

From the other side, fghGenBuffers error looks really weird as glGenBuffers() should present in every modern OpenGL implementation. Print driver information via glGetString(GL_VENDOR)/glGetString(GL_RENDERER)/glGetString(GL_VERSION) to see if your system has a proper GPU driver installation and doesn't use an obsolete Microsoft software implementation of OpenGL 1.1.

Buffer allocations can fail, of course — everything in computers is finite — but your error message isn't related to your problem.

You received the error:

freeglut : fgInitGL2 : fghGenBuffers is NULL

That's an error from freeglut, which is not part of OpenGL. So look up the implementation of freeglut's fgInitGL2.

If fghGenBuffers failed, that means that the following line failed:

CHECK("fghGenBuffers", fghGenBuffers = (FGH_PFNGLGENBUFFERSPROC)glutGetProcAddress("glGenBuffers"));

i.e. GLUT was unable to obtain the address of the glGenBuffers function. It didn't ask for buffers and fail to get them, it asked for the address of the function it should ask for buffers, and didn't even get that.

That, however is only an fgWarning, i.e. a warning, not an error. I would dare guess that you would see that message on your terminal from the moment your program starts, irrespective of whether it subsequently fails. It's something GLUT wants you to know, but it isn't proximate to your failure.

As to your actual problem: it is almost certainly to do with attempting to overfill a display list. Your best solution in context is to put only a single sphere into a display list, and issue 2000 calls to draw that, modifying the model-view matrix between each.

As a quick aside: display lists proved to be a bad idea, not offering much scope for optimisation and becoming mostly unused by OpenGL 1.5. They were deprecated in OpenGL 3.0 in 2008, as was the entire fixed-functionality pipeline — including glPushMatrix, glTranslate and glPopMatrix.

That's not to harangue, but be aware that the way your code is formed relies on lingering deprecated functionality. It may contain hard limits that nobody has bothered to update, or in any other way see very limited maintenance.

It's far and away the simplest way to get going though, and you're probably in the company of a thousand CAD or other scientific programs, so the best advice right now really is just not to try to put all your spheres in one display list.

Related