Search code examples
c++winapiopenglmfcglsl

MFC App Using OpenGL to draw control, glGetUniformLocation() fails on second call


I've stripped the below code down to try and isolate/illustrate my problem. I have an MFC Picture CWnd control that I have derived to create my own CGLImage control.

I have used this method of drawing just fine with OpenGL immediate mode. Now I am trying to use shader programs from my other SDL apps and I'm having problems accessing the shader program variables on the second call to the OnPaint() method.

I create my shader program once on the first OnPaint() call. The program handle is still valid on subsequent calls, but the return of hColor in this line is always -1 for subsequent OnPaint() calls:

GLuint hColor = glGetUniformLocation( g_SolidProgram, "vColor4" );

Why can't I access hColor after the first time? I shouldn't need to recreate the program on each OnPaint() call, surely.

#include "stdafx.h"

#include "MFC_GL.h"
#include "GLImage.h"

#include <glew.h>
#include <gl\glu.h>                             // Header File For The GLu32 Library
#include <freeglut.h>

IMPLEMENT_DYNAMIC(CGLImage, CWnd)

CGLImage::CGLImage()
{

}

CGLImage::~CGLImage()
{
}

#define ID_UPDATE_TIMER 102

BEGIN_MESSAGE_MAP(CGLImage, CWnd)
  ON_WM_PAINT()
  ON_WM_TIMER()
END_MESSAGE_MAP()

int MySetPixelFormat( HDC hdc )
{
    PIXELFORMATDESCRIPTOR pfd = { 
        sizeof(PIXELFORMATDESCRIPTOR),    // size of this pfd 
        1,                                // version number 
        PFD_DRAW_TO_WINDOW |              // support window 
        PFD_SUPPORT_OPENGL |              // support OpenGL 
        PFD_DOUBLEBUFFER,                 // double buffered 
        PFD_TYPE_RGBA,                    // RGBA type 
        24,                               // 24-bit color depth 
        0, 0, 0, 0, 0, 0,                 // color bits ignored 
        0,                                // no alpha buffer 
        0,                                // shift bit ignored 
        0,                                // no accumulation buffer 
        0, 0, 0, 0,                       // accum bits ignored 
        32,                               // 32-bit z-buffer     
        0,                                // no stencil buffer 
        0,                                // no auxiliary buffer 
        PFD_MAIN_PLANE,                   // main layer 
        0,                                // reserved 
        0, 0, 0                           // layer masks ignored 
    }; 
    
    int  iPixelFormat; 
 
    // get the device context's best, available pixel format match 
    if((iPixelFormat = ChoosePixelFormat(hdc, &pfd)) == 0)
    {
        MessageBox(NULL, "ChoosePixelFormat Failed", NULL, MB_OK);
        return 0;
    }
     
    // make that match the device context's current pixel format 
    if(SetPixelFormat(hdc, iPixelFormat, &pfd) == FALSE)
    {
        MessageBox(NULL, "SetPixelFormat Failed", NULL, MB_OK);
        return 0;
    }

    return 1;
}


#ifndef STRINGIFY
#define STRINGIFY(a) #a
#endif

char *fsSolid  = STRINGIFY(

precision mediump float;
uniform vec4 vColor4;
                           
void main()
{
  gl_FragColor = vColor4;
}
);


char *vsSolid  = STRINGIFY(

attribute vec4 vPosition;

uniform   mat4 MVP;

void main()
{
  gl_Position = MVP * vPosition;
}
);


GLuint BuildShader( char *pszSource, GLenum shaderType )
{
  GLuint hShader = glCreateShader( shaderType );
  glShaderSource( hShader, 1, (char const**) &pszSource, 0 );
  glCompileShader( hShader );
  
  GLint compileSuccess = GL_FALSE;
  
  glGetShaderiv( hShader, GL_COMPILE_STATUS, &compileSuccess );
  
  if( compileSuccess == GL_FALSE )
  {
    GLchar message[ 256 ];
    glGetShaderInfoLog( hShader, sizeof( message ), 0, &message[ 0 ] );
    printf( "FATAL: SHADER (%s) %s\n", pszSource, message );
    exit( 1 );
  }
  
  return hShader;
}

GLuint BuildProgram( char *pszVertexShaderSource, char *pszFragmentShaderSource )
{
  GLuint vShader = BuildShader( pszVertexShaderSource,   GL_VERTEX_SHADER );
  GLuint fShader = BuildShader( pszFragmentShaderSource, GL_FRAGMENT_SHADER );
  
  GLuint hProgram = glCreateProgram();
  glAttachShader( hProgram, vShader );
  glAttachShader( hProgram, fShader );
  glLinkProgram( hProgram );
  
  GLint linkSuccess;
  
  glGetProgramiv( hProgram, GL_LINK_STATUS, &linkSuccess );
  
  if( linkSuccess == GL_FALSE )
  {
    GLchar message[ 256 ];
    glGetProgramInfoLog( hProgram, sizeof( message ), 0, &message[ 0 ] );
    printf( "FATAL: %s\n", message );
    exit( 1 );
  }
  
  return hProgram;
}

int g_ScreenWidth  = 320;
int g_ScreenHeight = 200;
GLuint g_SolidProgram;


void CGLImage::OnTimer( UINT_PTR nIDEvent )
{
  switch( nIDEvent )
  {
    case ID_UPDATE_TIMER:
      Invalidate();
      break;
  }
}


void CGLImage::OnPaint()
{
  CPaintDC dc( this ); // device context for painting
  HDC hdc = ::GetDC(m_hWnd);

  MySetPixelFormat( hdc );

  HGLRC hglrc = wglCreateContext( hdc );

  if( hglrc )
  { 
    // try to make it the thread's current rendering context
    if( wglMakeCurrent( hdc, hglrc ) )
    {
      static BOOL fFirstTime = TRUE;

      if( fFirstTime )
      {
        fFirstTime = FALSE;
        //glewExperimental=GL_TRUE;
        GLenum glewRC = glewInit();

        if( glewRC != GLEW_OK ) {
          printf( "glewInit: %s\n", glewGetErrorString( glewRC ) );
          exit( 1 );
        }

        g_SolidProgram = BuildProgram( vsSolid, fsSolid );

        m_UpdateTimer = ::SetTimer( m_hWnd, ID_UPDATE_TIMER, 100, NULL );
      }

      glUseProgram( g_SolidProgram );
      GLuint hColor = glGetUniformLocation( g_SolidProgram, "vColor4" );  // ### THIS FAILS

      SwapBuffers( hdc );
    } 
  }

  wglMakeCurrent(NULL, NULL) ; 

  ::ReleaseDC (m_hWnd, hdc) ; 
  wglDeleteContext(hglrc); 
}

UPDATE:

It looks as though the wglCreateContext() and wglMakeCurrent() calls destroy the shader program somehow.

  glUseProgram( g_SolidProgram );
  GLuint hColor = glGetUniformLocation( g_SolidProgram, "vColor4" );  // OK HERE

  hglrc = wglCreateContext( hdc );

  if( wglMakeCurrent( hdc, hglrc ) ) {
    glUseProgram( g_SolidProgram );
    hColor = glGetUniformLocation( g_SolidProgram, "vColor4" );  // BROKEN HERE
  }

Solution

  • It seems like the OpenGL context might be getting lost between OnPaint() calls. MFC, from what I remember, can be picky about when the rendering context is valid. Try this change:

    void CGLImage::OnPaint() {
        CPaintDC dc( this ); 
        HDC hdc = ::GetDC(m_hWnd);
        MySetPixelFormat( hdc );
     
        if (!hglrc) { 
            hglrc = wglCreateContext( hdc );
            if( hglrc ) { 
                wglMakeCurrent( hdc, hglrc ); 
                // Init GLEW (assuming done elsewhere already)
                g_SolidProgram = BuildProgram( vsSolid, fsSolid ); // Build shader once
                m_UpdateTimer = ::SetTimer( m_hWnd, ID_UPDATE_TIMER, 100, NULL ); 
            }
        }
    
        wglMakeCurrent( hdc, hglrc );
    
        glUseProgram( g_SolidProgram );
        GLuint hColor = glGetUniformLocation( g_SolidProgram, "vColor4" ); // Should work now 
    
        // ... rest of ur code
    }