Shepard62700FR Shepard62700FR - 6 months ago 21
Linux Question

Trouble retrieving and storing pointers to OpenGL functions manually

I have some trouble retrieving and storing manually the pointers to OpenGL functions, here is a "simplified snippet" version of my code :

#ifdef WIN32
#include <windows.h>
#endif

#include <GL/gl.h>

class CGLManager
{
public:
// Manager functions
bool GetAnyGLFuncAddress( const char *_cName, void *_pFunc );
bool LoadFunctions( void );

// OpenGL functions
void (APIENTRY *glBegin)( GLenum mode );
void (APIENTRY *glEnd)( void );
void (APIENTRY *glVertex3f)( GLfloat x, GLfloat y, GLfloat z );

private:
#ifdef WIN32
HMODULE m_hLib; // opengl32.dll
#else
void *m_hLib; // libGL.so
#endif
};


And here's the source :

extern CGLManager gGL;

// GetAnyGLFuncAddress - Attempt to retrieve the OpenGL function named "_cName" and store it in "_pFunc", returns true if success or false otherwise
bool CGLManager::GetAnyGLFuncAddress( const char *_cName, void *_pFunc )
{
#ifdef WIN32
// Similar to https://www.opengl.org/wiki/Load_OpenGL_Functions#Windows
_pFunc = (void *)wglGetProcAddress( _cName );
if ( _pFunc == 0 || (_pFunc == (void *)0x1) || (_pFunc == (void *)0x2) || (_pFunc == (void *)0x3) || (_pFunc == (void *)-1) )
_pFunc = (void *)GetProcAddress( m_hLib, _cName );
#else
// TODO: Test this
// According to some websites, NVIDIA drivers prefer the ARB implementation over the core one
_pFunc = (void *)glXGetProcAddressARB( _cName );
if ( _pFunc == 0 || (_pFunc == (void *)0x1) || (_pFunc == (void *)0x2) || (_pFunc == (void *)0x3) || (_pFunc == (void *)-1) )
_pFunc = (void *)glXGetProcAddress( _cName );
#endif

return (_pFunc != NULL);
}

// LoadFunctions - Attempt to retrieve all used OpenGL functions, returns true if all of them were retrieved or false if there is a single failure
bool CGLManager::LoadFunctions( void )
{
if ( !(GetAnyGLFuncAddress( "glBegin", &gGL.glBegin )) )
return false;

if ( !(GetAnyGLFuncAddress( "glEnd", &gGL.glEnd )) )
return false;

if ( !(GetAnyGLFuncAddress( "glVertex3f", &gGL.glVertex3f )) )
return false;

return true;
}


Here's how my manager work in general : it first check which renderer the game's engine uses (Software, OpenGL or Direct3D), if it's not OpenGL, then we stop getting any further. Otherwise, we load the library (
opengl32.dll
or
libGL.so
) and we check if it's good or not (again, if failed, we stop), we retrieve and store the pointers to OpenGL's functions (
glBegin
,
glEnd
,
glVertex3f
) with the
LoadFunctions
method and we return if everything's fine or something wrong happened.

Now the problem : the
GetAnyGLFuncAddress
method retrieve successfully OpenGL functions (in other words,
glBegin
will return true,
glARandomMethodThatDontExist
will return false) but for some reason,
gGL.glBegin
(and it's "friends") in
LoadFunctions
doesn't get updated and it will be always NULL causing a crash.

I have been trying for hours to find out a solution by searching on Internet and on StackOverflow but I haven't found any answer that can give me the solution to the problem.

In many websites and answers I've found on StackOverflow, a lot of people suggested to use an OpenGL loading library like GLEW and even the OpenGL wiki recommend it. However, due to the nature of the environment I'm working on, I can't use those kind of libraries and neither I can't use OpenGL functions directly, I know I'm going through the painful way by doing everything manually but I have no other choice.

Thank you for your answers.

Answer

Forgetting about what your ultimate goal is, I see basic C++ mistakes.

Let's cut out all of the code that is irrelevant and focus on this:

bool CGLManager::GetAnyGLFuncAddress( const char *_cName, void *_pFunc )
{
    _pFunc = (void *)wglGetProcAddress( _cName ); // <-- This sets the local pointer
    //...
}

Then you call the above like this:

bool CGLManager::LoadFunctions( void )
{
    if ( !(GetAnyGLFuncAddress( "glBegin", &gGL.glBegin )) )
        return false;
}

You're passing the address in the second argument, but inside the function GetAnyGLFuncAddress, you're not updating the value so that the new value is reflected back to the caller. You're setting the local value instead, thus gGL.glBegin (and all the other addresses from the other two calls), will not be set.

Inside of GetAnyGLFuncAddress, I would have expected this to work:

bool CGLManager::GetAnyGLFuncAddress( const char *_cName, void *_pFunc )
{
    *_pFunc = (void *)wglGetProcAddress( _cName ); // <-- Note the *
    //...
}

And any subsequent usage of pFunc inside of GetAnyGLFuncAddress also should reflect the dereferenced value.


Another solution (and since this is C++), you can forego the void * C-like coding and make the function a template that takes a function pointer type as the template argument:

template <typename FnPtr>
bool CGLManager::GetAnyGLFuncAddress( const char *_cName, FnPtr* _pFunc )
{
    *_pFunc = reinterpret_cast<FnPtr>(wglGetProcAddress( _cName )); 
    //...
}

Since the type is now FnPtr*, whatever you pass in will automatically have FnPtr be of that type. No more void * (except for the return value of wglGetProcAddress, which is casted).