1
\$\begingroup\$

I have written a benchmark program that compares the performance of OpenGL and DirectX libraries. The problem is that OpenGL gets much lower performance than Direct3D. At the end the program generates a comparison chart. You can look at it here:

enter image description here

As you can see the OpenGL performance is poor compared with Direct3D (I know that FPS's aren't the best method of measuring out there but that was my choise from the begining). Rendered object is a BIG CUBE made of smaller cubes. BIG CUBE passes certain states that are:

  COLOR  
  COLOR + LIGHTS 
  TEXTURE 
  TEXTURE + LIGHTS 
  WIRE 
  TRANSPARENT

OpenGL context uses atioglxx.dll (AMD) and nvoglv32.dll (NVidia) for HW rendering. Now I will provide some code for better view of what and how I am doing things. First of all I'm creating a rendering context that is loaded on WM_CREATE:

GLvoid StartOpenGL(HWND hwnd, HDC * hdC, HGLRC * hrc)
{
  PIXELFORMATDESCRIPTOR pfd;
  int format;
  *hdC = GetDC(hwnd);

  ZeroMemory( &pfd, sizeof( pfd ) );
  pfd.nSize = sizeof( pfd );
  pfd.nVersion = 1;
  pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
  pfd.iPixelType = PFD_TYPE_RGBA;
  pfd.cColorBits = 32;
  pfd.cDepthBits = 24;
  pfd.cStencilBits = 8;
  pfd.iLayerType = PFD_MAIN_PLANE;
  format = ChoosePixelFormat(*hdC, &pfd);
  SetPixelFormat(*hdC, format, &pfd);

  *hrc = wglCreateContext(*hdC);
  wglMakeCurrent(*hdC, *hrc);
}
// also these functions are called on WM_CREATE:
    ApplyTextures();  //load texture
    reshape();        // reshape
    LOADcubeGL();     // load VBO's (for full code check LINK2 on Blockquote)
    ACTIVATEcubeGL(); // activate VBO's (for full code check LINK2 on Blockquote)

Context is destroyed on WM_DESTROY:

GLvoid StopOpenGL(HWND hwnd, HDC hdC, HGLRC hrc)
{
    wglMakeCurrent( NULL, NULL );
    wglDeleteContext( hrc );
    ReleaseDC(hwnd, hdC);
}
// also these functions are called on WM_DESTROY:
KILLcubeGL(); // zero VBO's (for full code check LINK2 on Blockquote)

On WINAPI WinMain I do

animationGL(); // rotate the cube
init();        // inits GLlight
display();     // set view + transformation + rendering the cube object with glEnable(); glDisable(); states
SwapBuffers(hdC);

Here is the full code:

GLvoid LOADcubeGL(GLvoid){

        glGenBuffersARB         = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");
        glBindBufferARB         = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");
        glBufferDataARB             = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");
        glBufferSubDataARB      = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB");
        glDeleteBuffersARB      = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB");
        glGetBufferParameterivARB   = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB");

        glGenBuffersARB(1, &gVerts);
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, gVerts);
        glBufferDataARB(GL_ARRAY_BUFFER_ARB, 
                        sizeof(vertices) + sizeof(normals) + sizeof(colors) + sizeof(texcoords), 
                        0, 
                        GL_STATIC_DRAW_ARB   );
        glBufferSubDataARB(GL_ARRAY_BUFFER_ARB,                0, sizeof(vertices),  vertices);
        glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, sizeof(vertices),  sizeof(normals),  normals);
        glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, sizeof(vertices) + sizeof(normals),  sizeof(colors), colors);
        glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, sizeof(vertices) + sizeof(normals) + sizeof(colors), sizeof(texcoords), texcoords);

        glGenBuffersARB(1, &gIndices);
        glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, gIndices);
        glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(indices), indices, GL_STATIC_DRAW_ARB);
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_COLOR_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    };

GLvoid ACTIVATEcubeGL(GLvoid){
        gVerts = 0;
        gIndices = 0;
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, gVerts);
        glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, gIndices);
        glVertexPointer(3, GL_FLOAT, 0, vertices);
        glNormalPointer(GL_FLOAT, 0, normals);
        glColorPointer(4, GL_FLOAT, 0, colors);
        glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
};

GLvoid KILLcubeGL(GLvoid){
        glDisableClientState(GL_COLOR_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glDisableClientState(GL_NORMAL_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
        glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
        glDeleteBuffersARB(1, &gVerts);
        glDeleteBuffersARB(1, &gIndices);
        delete[] vertices;
        delete[] indices;
    };

The BIG CUBE is drawn in display(); stage by:

glNewList(1, GL_COMPILE);
  glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_BYTE, indices);
glEndList();

Now I know that mixing VBO's with display list may cause problems and moreover DirectX 9 doesn't have equivalent for display list but otherwise my OPENGL code runs even slower: at 20FPS! What I am doing wrong?

My DirectX calls look pretty same except that there is no rendering context like OpenGL have. I draw by vertex_buffer->Lock index_buffer->Lock and finaly d3ddev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 36, 0, 12);. My equivalent for OpenGL's PIXELFORMATDESCRIPTOR is:

d3dp.Windowed = TRUE;
d3dp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dp.hDeviceWindow = dxhwnd;
d3dp.BackBufferCount = 1;
d3dp.BackBufferFormat = d3ddm.Format;
d3dp.MultiSampleType = D3DMULTISAMPLE_NONE;  // in OpenGL I set that elsewhere than pfd
d3dp.MultiSampleQuality = 0;
d3dp.BackBufferWidth = width;
d3dp.BackBufferHeight = height;
d3dp.EnableAutoDepthStencil  = TRUE;
d3dp.AutoDepthStencilFormat  = D3DFMT_D24S8;
d3dp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
d3dp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // in OpenGL I use wglSwapIntervalEXT(0);
\$\endgroup\$
5
  • 1
    \$\begingroup\$ Lots of issues here... Display lists are deprecated to begin with. If you are using them, you shouldn't create a new one every frame. You need to use glGenLists to generate a valid ID to use in glNewList. GL_COMPILE means it won't actually execute it at that time anyway. You are using glDrawElements as if you are using client-side arrays. When a buffer is bound to ELEMENT_ARRAY_BUFFER the last parameter is an offset into that buffer, not a pointer. \$\endgroup\$
    – bcrist
    Commented Apr 10, 2014 at 22:02
  • 2
    \$\begingroup\$ Looking closer, you're uploading your vertex data to a buffer, but you're never actually using it. You enable client-side arrays but never give OpenGL pointers to your client side data. There's no way this code actually renders properly. Before you benchmark, its usually best to make sure your code is actually working. \$\endgroup\$
    – bcrist
    Commented Apr 10, 2014 at 22:18
  • \$\begingroup\$ @bcrist you were right I was never actually using the VBO. It was hard hours for me but thanks to your advices and some tutorials link I have finaly made it! Well there is still a bit difference between the libraries but until now it is the best method for me with no display lists... If you like to see the updated functions go ahead and: link. I am looking forward to improving the code. If someone spots that something else is in the code that needs to be repaired-inform me. Thank you. \$\endgroup\$
    – NightKn8
    Commented Apr 11, 2014 at 3:04
  • \$\begingroup\$ Interleave your vertex data in the buffer. Instead of pos0, pos1, pos1, tex0, tex1, tex2, etc. do pos0,tex0,norm0,... This generally helps for large meshes, not sure how much it will help for a small one. The idea is that when a GPU fetches a vertex, it can fetch the first N bytes rather than data from 5 places that are non-contiguous. \$\endgroup\$
    – PatrickB
    Commented Apr 11, 2014 at 22:19
  • \$\begingroup\$ GL_UNSIGNED_BYTE is a very bad choice of index size in the first place. The API supports it, but GPUs don't. The fundamental problem is that it leads to unaligned memory access when fetching indices, the driver can help the GPU to overcome this problem but it will not give good performance. If you enable Debug Output on one of your AMD GPUs, it will basically tell you the same thing I did in the form of a performance warning, only without the explanation. I am sure you must have noticed the smallest index size in D3D is 16-bit? :) \$\endgroup\$ Commented Apr 16, 2014 at 23:12

1 Answer 1

1
\$\begingroup\$

I can't read all of the source code (dang firewalls), but a display list is something that you compile once, then execute many times. What you're doing is telling OpenGL to regenerate an optimized execution list containing such and such triangle data every frame. That isn't what display lists were* used for -- they originally were more for things like old SGI workstations that could literally compile OpenGL commands into GPU command buffers and execute them. Even if you were executing this code on an SGI machine, this would perform terribly because you're re-creating this display list!

Modern GPUs don't function like this. If your data is in a VBO, doing a display list means OpenGL must read the data back to create it. JUST SAY NO. Do a glDrawElements() and be done with it. Nothing fancy. As an aside, display lists are gone in later versions of GL, so definitely don't use them. It pretty much guarantees problems going forward.

* "were", as in, don't do it now! Ever! No! Nope!

\$\endgroup\$
1
  • \$\begingroup\$ If I turn off display list the FPS drop under 20 for OpenGL. Look here: link. I call glDrawElements() 8 times what gives 497664 verts to draw at once. GTX 660 should do it smoothly just like in DirectX case. \$\endgroup\$
    – NightKn8
    Commented Apr 10, 2014 at 21:45

Not the answer you're looking for? Browse other questions tagged .