无法手动检索和存储指向OpenGL函数的指针

时间:2016-06-02 16:20:53

标签: c++ linux windows opengl graphics

我在检索和存储指向OpenGL函数的指针时遇到了一些麻烦,这是我的代码的“简化代码段”版本:

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

这是来源:

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

以下是我的经理一般工作的方式:首先检查游戏引擎使用的渲染器(软件,OpenGL或Direct3D),如果不是OpenGL,那么我们就不再进一步了解。否则,我们加载库(opengl32.dlllibGL.so)并检查它是否好(再次,如果失败,我们停止),我们检索并存储指向OpenGL函数的指针({{1 {}},glBeginglEnd)使用glVertex3f方法,如果一切正常或发生了错误,我们会返回。

现在问题: LoadFunctions方法成功检索OpenGL函数(换句话说,GetAnyGLFuncAddress将返回true,glBegin将返回false)但是某些原因,glARandomMethodThatDontExist中的gGL.glBegin(以及它的“朋友”)没有得到更新,它将始终为NULL,从而导致崩溃。

我一直在尝试通过在Internet和StackOverflow上搜索找到解决方案,但我没有找到任何可以解决问题的答案。

在StackOverflow上发现的许多网站和答案中,很多人建议使用像GLEW这样的OpenGL加载库,甚至OpenGL维基也推荐它。 但是,由于我正在处理的环境的性质,我不能使用那些类型的库,也不能直接使用OpenGL函数,我知道我正在经历通过手动完成所有事情的痛苦方式,但我别无选择。

感谢您的回答。

3 个答案:

答案 0 :(得分:4)

bool CGLManager::GetAnyGLFuncAddress( const char *_cName, void *_pFunc )

这只是普通的,破碎的C ++。 _pFuncvoid*。指针是值。更改_pFunc 的值不会更改传入的变量的值。

您应该只返回指针(NULL表示失败条件),或_pFunc应该是void**。这可能需要来自调用者的强制转换,并且GetAnyGLFuncAddress需要*_pFunc =来设置值。

如果您要返回函数指针,则需要在返回之前将返回的void*强制转换为相应的函数指针类型。这就是为什么你经常看到OpenGL加载器使用typedef作为函数指针类型。

typedef void (APIENTRY *(PFN_GLBEGIN))( GLenum );

...

PFN_GLBEGIN glBegin;

...

glBegin = static_cast<PFN_GLBEGIN>(GetAnyGLFuncAddress("glBegin"));

类似的东西。

答案 1 :(得分:-1)

*gl*GetProcAddres仅指定用于扩展功能的返回入口点地址。它不需要返回操作系统OpenGL ABI要求中的函数的地址(OpenGL-1.1 for Windows,OpenGL-1.2 for Linux Softare Base(LSB)4,OpenGL-2.1 for LSB-5)。

通常,如果您的程序二进制文件直接链接到API库(存根)(与使用在运行时动态加载API库(存根)的加载程序相反),它就是&#39 ; s quity愚蠢地检索函数是操作系统与*gl*GetProcAddress的OpenGL ABI合同的一部分,因为这些函数无论如何都可以通过常规API库获得。 wglGetProcAddress与OpenGL-1.1函数一起导出,glXGetProcAddress与所有OpenGL-1.2函数一起导出(至少),因此如果您的程序看到*GetProcAddress函数,它也会看到其他OpenGL函数

通过GetProcAddress加载所有 OpenGL符号的唯一原因是,如果您使用LoadLibrary加载opengl32.dll或使用dlopen加载libGL.so

答案 2 :(得分:-1)

忘记你的最终目标是什么,我看到了基本的C ++错误。

让我们删除所有不相关的代码并专注于此:

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

然后你打电话给上面这样:

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

您在第二个参数中传递了地址,但在函数GetAnyGLFuncAddress中,您没有更新该值,以便将新值反映回调用者。您改为设置本地值,因此将不会设置gGL.glBegin(以及其他两个呼叫中的所有其他地址)。

GetAnyGLFuncAddress内部,我原本希望这可行:

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

pFuncGetAnyGLFuncAddress的任何后续使用也应该反映出取消引用的值。

另一个解决方案(因为这个 C ++),你可以放弃void *类C编码,并使函数成为一个模板,它将函数指针类型作为模板参数:

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

由于类型现在为FnPtr*,因此无论您传入的是什么,都会自动FnPtr属于该类型。不再有void *wglGetProcAddress的返回值除外)。