【问题标题】:Why are OpenGL functions loaded at runtime instead of dynamically linked?为什么OpenGL函数在运行时加载而不是动态链接?
【发布时间】:2017-11-23 00:01:53
【问题描述】:

OpenGL API 的用户通常使用 GLEW 之类的库,或者乐于在运行时加载 OpenGL 函数。为什么动态加载是首选的链接方式?

静态链接显然不是一种选择,因为使用 OpenGL 的程序通常由作者编译并分发给具有不同 OpenGL 库以用于其特定显卡的用户。

剩下的是动态链接和动态加载。看起来动态链接可以工作,因为我们知道我们要访问的所有函数的名称。

动态链接是否可行?如果不是,为什么?如果是,为什么首选动态加载?

【问题讨论】:

    标签: opengl dynamic-linking dynamic-loading


    【解决方案1】:

    你想要的都是可能的。您可以制作一个带有静态接口的 DLL/SO 加载器,它将从幕后的实现中加载函数指针。它将根据当前的上下文将函数调用传递给实现。事实上,这就是现代 Linux 发行版的本质。

    这也是 WGL 在 Windows 上的工作方式……至少 OpenGL 版本 1.1。

    它不是“首选”的原因是因为它需要努力。必须有人创建和维护这个位于应用程序和驱动程序之间的中间库。每当出现新的扩展时,都必须更新库。每当新版本出现时,都必须更新库。然后你必须确保链接到这个库的正确版本。

    这个额外的依赖在 Linux 上运行良好,因为它一直是最新的。 Windows 上的 OpenGL32.dll 但不是。 WinNT 采用 OpenGL 有一段时间了,但微软也离开并创建为 Win9x 购买了他们自己的图形 API。因此,他们从未为更高版本的 OpenGL 或新扩展更新 OpenGL32.dll。如果他们愿意不向后兼容,我想他们会完全从他们的操作系统中放弃 OpenGL32.dll。

    由于您需要一个运行时加载程序才能在 Windows 中工作,因此使用相同的运行时加载程序让所有平台都工作会更容易(尽管显然使用不同的函数来获取函数指针)。虽然您可以创建您正在谈论的那种 DLL,但它只是多了一个 OpenGL 加载库,多了一个您必须维护的依赖项。如果没有 Linux/MESA 带来的集中化,那么麻烦就没有意义了。


    我在想一个应用程序可以直接动态链接到驱动程序本身而无需中间库

    你可以这样做。但是,您将使用静态导入库链接到该特定驱动程序的库。这意味着如果您链接到 NVIDIA 的实现,您的应用程序将无法在 AMD 的实现上运行。

    而且您不能静态链接到两个 DLL 的导入库。原因是如果一个静态导入库无法加载,您的应用程序终止。因此,除非您的计算机上同时拥有 AMD 和 NVIDIA 的实现,否则您将无法在其中任何一个上运行。

    还有:

    即使驱动库在编译时不可用,我们仍然知道 OpenGL 函数名称

    假设他们的驱动程序直接导出这些名称。这很难保证。

    毕竟WGL/GLX接口是使用wgl/glXGetProcAddress来获取函数指针的。如果WGL/GLX与驱动的接口只是简单的将字符串传递给驱动的函数,那么驱动就不需要直接导出任何函数。它通过单个入口点公开其 API,强制加载运行时函数指针而不是允许静态链接。

    我的理解是 OpenGL 从 dll/dylib/so 库文件中加载库 LoadLibrary/dlopen,这些库文件本身就是显卡驱动程序,并且这些驱动程序库具有带有 OpenGL 函数的符号表。其中哪些部分是错误的?

    所有部分都是错误的。您链接到基本接口 API(如 WGL 或 GLX)。加载库使用该接口来加载函数指针。接口 API 如何将函数指针加载到驱动程序 DLL 是一个特定于平台的问题。在 Windows 上,wglGetProcAddress 调用驱动程序中的函数,传递函数的字符串名称并取回函数指针。

    在 Linux 上,GLX 倾向于在内部制造函数,使用后期绑定技术稍后加载实际的驱动程序函数。也就是说,您可以使用一些虚假名称调用glXGetProcAddress,它会返回一个函数指针 shim,当调用该函数指针时,将从驱动程序加载当前上下文的函数。

    【讨论】:

    • 我在想一个应用程序可以直接动态链接到驱动程序本身而无需中间库(因为即使驱动程序库在编译时不可用,我们仍然知道 OpenGL 函数名称)。听起来我完全误解了 OpenGL 或显卡驱动程序的本质。我的理解是 OpenGL 从 dll/dylib/so 库文件中加载库 LoadLibrary/dlopen,这些库文件本身就是显卡驱动程序,并且这些驱动程序库具有带有 OpenGL 函数的符号表。其中哪些部分是错误的?谢谢。
    • @Praxeolitic:你怎么知道哪个 DLL 是驱动程序?请记住:当您通过导入库链接到 DLL 时,您将始终加载该特定 DLL,加载失败将终止您的程序。您如何知道驱动程序使用那些 特定 名称导出了这些函数名称。请记住:您通过第三方获得函数指针:wgl/glXGetProcAddress,它可能会或可能不会直接获取导出的函数。可以直接链接到驱动程序的 DLL,但您仍然必须在运行时查询其函数指针,因为它可能不会导出它们。
    • "毕竟WGL/GLX接口是使用wgl/glXGetProcAddress来获取函数指针的。"我不知道这一点,这个事实回答了这个问题。只是有一个层会强制这种行为,就是这样。我想我仍然可以问为什么 WGL 和 GLX 这样做,但我现在必须在此之前对 WGL 和 GLX 做一些功课。很好的答案。
    • “你可以制作一个加载器”——或者使用像Implib.so这样的加载器生成器。
    【解决方案2】:

    OpenGL wiki 提供了对 OpenGL function loading process 的一些见解。并不是说使用GetProcAddress 函数相当于在操作系统本身之外完成的一种动态链接形式。来自Wikipedia

    在计算中,动态链接器是操作系统的一部分,它通过将库的内容从持久存储复制到 RAM 来加载和链接可执行文件在执行时(在“运行时”)所需的共享库, 并填充跳转表和重定位指针。

    OpenGL 的扩展一直都在出现,根据供应商和驱动程序平台的不同,可以使用也可以不使用。 OpenGL wiki 声明在 MacOSX 上:

    自 OSX 10.2 以来,OSX 上的 GL 功能已被弱链接;这意味着您可以直接调用它们,未实现的扩展将解析为 NULL。请注意,这意味着您必须解析扩展字符串以确定函数是否有效,否则您的程序将崩溃。

    因此,即使 Apple 维护了一个动态链接库,您仍然需要添加一个层来检查给定函数是否可用,具体取决于支持的 OpenGL 版本和扩展。

    【讨论】:

    • 检查某个功能是否可用听起来像是在回答这个问题的正确轨道,但需要进一步充实。为什么除了错误输出的动态链接行为之外还需要其他东西?
    • @Praxeolitic:为什么需要出错?如果实现不提供扩展,并且您的应用程序不使用该扩展程序,那么您的应用程序仍然可以。
    • 添加到 Nicol 的评论中,应用程序可能有多种方法来实现所需的图形输出。其中一些可能会提供更好的性能,但也取决于更新的 OpenGL 功能。例如,如果计算着色器可用,则可以使用计算着色器(更快)完成纹理模糊,如果不可用(广泛支持),则可以渲染到纹理。
    • @NicolBolas 如果要求动态链接器查找不可用的函数,则动态链接器会出错,在这种情况下,出错是可取的,因为您的程序需要动态链接不可用的函数'不可用,这是一个早期的展示者。
    • @bernie 这种情况确实有道理。
    猜你喜欢
    • 1970-01-01
    • 2023-03-24
    • 2021-08-10
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    • 1970-01-01
    • 2014-02-08
    相关资源
    最近更新 更多