OpenGL 3.0 context

“Great” news : OpenGL 3.0 is out! Well, after skimming over the new specs there’s really no new stuff – besindes of some manufacturer specific extensions going EXT or ARB and the deprecated markings sig. NVIDIA announced full GL3.0 support on G80 series with driver version 181.00+ so I tried to play around with the new context type – here’s some code 🙂

#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094

typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int* attribList);

void GLWindow::CreateGL() {
    if(m_hWnd == NULL) {
        throw std::exception("Window handle cannot be NULL");
    }    // If we have no window, we cannot continue

    // If any of the following steps fail, we shutdown any OpenGL elements that have been started and exit
    if (!(m_hDC=GetDC(m_hWnd))) {                        // Retrieve the Device Context for this window
        ShutdownGL();
        throw std::exception("Could not get window Device Context");
    }

    //Create the PixelFormatDescriptor, which describes to OpenGL the pixel properties such as depth, color, and alpha channels
    PIXELFORMATDESCRIPTOR pfd;
    ZeroMemory( &pfd, sizeof(PIXELFORMATDESCRIPTOR) );
    pfd.nVersion = 1;                                                                // The PFD version, always 1
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;        // The properties of the PFD, in this case OpenGL support and double buffering
    pfd.iPixelType = PFD_TYPE_RGBA;                                                    // The type of Pixels we're dealing with
    pfd.cColorBits = m_displaySettings.colorBits;                                    // The color depth of the window
    pfd.cAlphaBits = m_displaySettings.alphaBits;                                    // The alpha depth of the window
    pfd.cDepthBits = m_displaySettings.depthBits;                                    // The number of bits to use for the depth buffer
    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);                                        // The size of this structure

    int pixelFormat;
    if (!(pixelFormat = ChoosePixelFormat(m_hDC,&pfd))) {
        ShutdownGL();
        throw std::exception("Could not find a suitable Pixel Format");
    }

    if(!SetPixelFormat(m_hDC,pixelFormat,&pfd)) {            // Set the format of the Device Context
        ShutdownGL();
        throw std::exception("Could not set the Pixel Format");
    }

    // NV GL 3.0
    HGLRC tempContext = NULL;
    if(!(tempContext = wglCreateContext(m_hDC))) {                    // Bind the render context for drawing
        ShutdownGL();
        throw std::exception("Could not create temp. Render Context");
    }

    if(!wglMakeCurrent(m_hDC, tempContext)) {                    // Bind the render context for drawing
        wglDeleteContext(tempContext);
        ShutdownGL();
        throw std::exception("Could not make temp. Render Context current");
    }

    int attribs[] = {
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,//we want a 3.0 context
        WGL_CONTEXT_MINOR_VERSION_ARB, 0,//and it shall be forward compatible so that we can only use up to date functionality
        WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
        0 //zero indicates the end of the array
    };

    PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; //pointer to the method
    wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");

    if(wglCreateContextAttribsARB == NULL) { //OpenGL 3.0 is not supported
        // Bind the render context for drawing
        wglDeleteContext(tempContext);
        ShutdownGL();
        throw std::exception("Cannot get Proc Adress for GL 3.0 CreateContextAttribs");
    }

    if (!(m_hRC=wglCreateContextAttribsARB(m_hDC,0, attribs))) {
        wglDeleteContext(tempContext);
        ShutdownGL();
        throw std::exception("Can't Create A GL 3.0 Rendering Context.");
    }
    wglDeleteContext(tempContext);

    if(!wglMakeCurrent(m_hDC,m_hRC)) { // Try To Activate The Rendering Context
        ShutdownGL();
        throw std::exception("Could not make Render Context current");
    }
}

The key is the PFNWGLCREATECONTEXTATTRIBSARBPROC function pointer that is supplied by the opengl dll (see its declation on line 5). Using this layout we can fetch the function using a casted wglGetProcAddress() to the new function “wglCreateContextAttribsARB”. So the difference with GL3.0 here is to use a different method when creating a GL-context. The former method would still create an GL2 context. Alternatively one may use a new version of a utility/ext wrapper (glut, glew, glext, etc.) which supplies the new function directly.

Here’s the complete test project: gl30context_sample_code.

Direct3D9 Depth Buffer access

If you have to deal with Depth Buffer reading in Direct3D9, you’ll propably encounter the documentation saying that you cannot read the GPU depth buffer directly. But there’s a workaround with special buffer formats on NVIDIA Cards (RAWZ on older cards and INTZ on G80+ series) and on ATI (DF16/24, I think). There’s also a paragraph in the G80 documentation by NVIDIA (see http://developer.nvidia.com/object/gpu_programming_guide.html). I think GTA4 is a game that uses the formats. Here’s some code I have been playing around with using Microsofts DirectX SDK of November 2008:

// decls

IDirect3DTexture9* m_pSMZTexture;   // rawz for using dss as input for next shader
LPDIRECT3DSURFACE9 m_pBackBuffer;   // default

// creates NVIDIA INTZ Format DSS. Use as DSS and render scene. Then use as input texture for another pass.
BOOL vmD3D::SetupDepthBufferAccess()
{
if(!m_pd3dDevice)
return FALSE;

if(FAILED(m_pd3dDevice->CreateTexture(TEXDEPTH_WIDTH, TEXDEPTH_HEIGHT, 1,
D3DUSAGE_DEPTHSTENCIL, (D3DFORMAT)MAKEFOURCC('I','N','T','Z'),
D3DPOOL_DEFAULT, &m_pSMZTexture, NULL)))
{
return FALSE;
}

return TRUE;
}

This actually creates an INTZ-Format DSS. Be sure to use the same dimensions of the render target you’ll bind on the same drawcall. Next, before your dracall bind the dss:

// setdss (our custom zsurf) -> intz!
// first fetch surface from tex at level 0
IDirect3DSurface9 *pSMZSurf = NULL;
m_pSMZTexture->GetSurfaceLevel(0, &pSMZSurf);
// set dss:
if(FAILED(m_pd3dDevice->SetDepthStencilSurface(pSMZSurf)))
return FALSE;
pSMZSurf->Release();

When drawing, there’s no need for any shader stuff if you simply want to output depth-only values (just write black color for example). But obviosly your vertex shader should do the needed transformations (multiply your WorldProjectionMatrix for example). After drawing with the intz-dss bound, you can use the texture as shader input for a next render pass. Here you can simply render a Full-Screen Quad to visualize the depth values:

if(FAILED(pFxDisplayDepth->SetTexture("DepthMap", m_pSMZTexture)))
return FALSE;

//set render target back to normal back buffer / depth buffer (or disable Z - as it's a dumb quad!)
if(FAILED(m_pd3dDevice->SetRenderTarget(0, m_pBackBuffer)))
return FALSE;

Then just draw the quad, i.e. with lazy UP RHW drawing:

UINT uPasses;
if (SUCCEEDED(pFxDisplayDepth->Begin(&uPasses, 0)))
{
for (UINT uPass = 0; uPass BeginPass(uPass);
m_pd3dDevice->SetFVF( m_pQuad->FVF );
m_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, m_pQuad->m_Vertex, sizeof( FullScreenQuad::Vertex ) );
pFxDisplayDepth->EndPass();
}
pFxDisplayDepth->End();
}

Be aware that Microsoft PIX cannot handle the INTZ resource, so you won’t be able to see a preview or the metadata of the INTZ-Surface.