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
}
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
}