当我发现以下代码及其大量变体产生非空内存地址时,我感到非常惊讶。我尝试过的变化包括:
glXGetProcAddressARB
而非glXGetProcAddress
。使用GLFW提供的跨平台替代方案:glfwGetProcAddress
。
#include <GL/glx.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
void *ptr;
ptr = glXGetProcAddress((const GLubyte *)"glottis");
printf("ptr: %x\n", ptr);
return 0;
}
程序编译时使用-lGL
(并在需要时-lglfw
)并且没有警告也没有错误。
获取0
输出(NULL
指针)的唯一方法是询问名称不以gl
开头的函数的地址,例如manny
}。
我对这种行为感到非常惊讶,因为glottis
和manny
应该同样不存在,我希望两者都产生NULL
指针。
这是glXGetProcAddress
文档的摘录。
注释
如果未支持请求的函数,则返回NULL指针 正在查询的实现。
由于库可能,GLU函数不可查询 在查询时不加载。
答案 0 :(得分:7)
嗯,从正确的角度来看,您观察到的行为是合规的。您不能断定NULL
(或其他)的非glXGetProcAddress
返回值意味着函数已存在或可以使用。您必须始终查询扩展字符串。尝试获取未被扩展字符串公布的函数的函数指针(或其上下文的核心GL版本暗示的存在)将在概念上是未定义的行为。
你引用了reference page on glXGetProcAddress
。不幸的是,那些参考页面显然是不精确的,不完整的,有时甚至是平坦的错误的。在这种情况下,措辞至少是不幸的。
我一般建议使用官方规格查找此类详细信息。在这种情况下,GLX 1.4 specification将是相关文档。第3.3.12节“获取扩展函数指针”说明glXGetProcAddress
(强调我的):
返回值
NULL
表示实现不存在指定的函数。可以查询
NULL
的非glXGetProcAddress
返回值并不保证在运行时实际支持扩展函数。客户端还必须查询glGetString(GL_EXTENSIONS)
或glXQueryExtensionsString
确定是否 扩展由特定上下文支持。 [...]
glXGetProcAddress
以获取以下所有功能:
- 实施所支持的所有GL和GLX扩展功能(当前上下文是否支持这些扩展)。
- GL和GLX中从版本1.0到包括实现支持的那些规范版本的所有核心(非扩展)功能, 由
glGetString(GL_VERSION)
和glXQueryVersion
查询确定。
看起来Mesa3D实现实际上能够为从gl
开始的每个查询函数动态生成一些存根。
查看当前版本的/src/mapi/glapi/glapi_getproc.c
会显示函数_glapi_get_proc_address()
执行此操作:
/**
* Return pointer to the named function. If the function name isn't found
* in the name of static functions, try generating a new API entrypoint on
* the fly with assembly language.
*/
_glapi_proc
_glapi_get_proc_address(const char *funcName)
{
_glapi_proc func;
struct _glapi_function * entry;
init_glapi_relocs_once();
#ifdef MANGLE
/* skip the prefix on the name */
if (funcName[1] != 'g' || funcName[2] != 'l')
return NULL;
#else
if (funcName[0] != 'g' || funcName[1] != 'l')
return NULL;
#endif
/* search extension functions first */
func = get_extension_proc_address(funcName);
if (func)
return func;
/* search static functions */
func = get_static_proc_address(funcName);
if (func)
return func;
/* generate entrypoint, dispatch offset must be filled in by the driver */
entry = add_function_name(funcName);
if (entry == NULL)
return NULL;
return entry->dispatch_stub;
}
因此它实际上会检查gl
前缀,如果该函数未知,则会为其动态创建存根。稍后,当加载一个hw后端驱动程序时,它可能会注册gl函数,如果它为它提供了实现,则存根代码会将调用转发给驱动程序。