手动检索和存储指向 OpenGL 函数的指针时遇到问题

Trouble retrieving and storing pointers to OpenGL functions manually

本文关键字:函数 指针 问题 遇到 OpenGL 检索 存储      更新时间:2023-10-16

>我在手动检索和存储指向 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)并检查它是否良好(再次,如果失败,我们停止),我们使用LoadFunctions方法检索并存储指向OpenGL函数(glBeginglEndglVertex3f)的指针,如果一切正常或发生错误,我们返回。

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

我一直在尝试通过互联网和StackOverflow搜索来找到解决方案,但我还没有找到任何可以给我解决问题的答案。

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

谢谢你的回答。

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

这只是常见的、破碎的C++。_pFunc是一个void*.指针是值。更改_pFunc的值不会更改传入的变量的值。

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

如果要返回函数指针,则需要在存储之前将返回的void*强制转换为相应的函数指针类型。这就是为什么您经常看到 OpenGL 加载程序对函数指针类型使用 typedefs 的原因。

typedef void (APIENTRY *(PFN_GLBEGIN))( GLenum );
...
PFN_GLBEGIN glBegin;
...
glBegin = static_cast<PFN_GLBEGIN>(GetAnyGLFuncAddress("glBegin"));

类似的东西。

忘记了你的最终目标是什么,我看到了基本的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 *
//...
}

GetAnyGLFuncAddress内部对pFunc的任何后续使用也应反映取消引用的值。


另一种解决方案(由于这是C++),您可以放弃void *类 C 编码,并使函数成为将函数指针类型作为模板参数的模板:

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

由于该类型现在是FnPtr*的,因此您传入的任何内容都将自动具有该类型FnPtr。 不再void *(除了wglGetProcAddress的返回值,它是强制转换的)。

*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 库(存根)的加载器相反),检索作为操作系统 OpenGL ABI 合约一部分的函数是愚蠢的*gl*GetProcAddress因为这些函数无论如何都可以通过常规 API 库获得。wglGetProcAddress与 OpenGL-1.1 函数一起导出,glXGetProcAddress与所有 OpenGL-1.2 函数一起导出(至少),因此如果您的程序看到*GetProcAddress函数,它也会看到其他 OpenGL 函数。

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