【问题标题】:About function loader in Vulkan关于 Vulkan 中的函数加载器
【发布时间】:2016-08-15 01:26:00
【问题描述】:

在 Vulkan 中,我们有两个需要使用 dlopen/LoadLibrary 加载的全局函数。它们是 vkGetInstanceProcAddr 和 vkGetDeviceProcAddr。

我有一个 gpu 并安装了 vulkan 驱动程序。我应该在运行时还是链接时更好地加载库?我将不同的逻辑设备(从相同的 gpu 创建)提供给 vkGetDeviceProcAddr 以查询相同的函数。他们都向我返回相同的地址。我认为重新加载可能是浪费。

我的问题是这个想法是怎么来的?是多实现还是多GPU?

我的加载器函数目前如下所示:

class VulkanDevice
{
public:
    VkDevice m_Device;
    void LoadAllCoreFunctions();
    void LoadExtension(const char *name);
    void LoadExtensions(const char *postfix); // for example: "KHR"

    PFN_vkCreateCommandBuffers vkCreateCommandBuffers;
    // Then a lot of function pointers.....
}

int main() {
    // After creating instance and creating device with vkCreateDevice
    VulkanDevice vkd(device);
    vkd.LoadAllCoreFunctions();
    vkd.vkCreateCommandBuffers(vkd.m_Device, ....);
}

如您所见,如果我有多个设备,重新加载将非常浪费......而且函数指针也会占用大量内存......

【问题讨论】:

    标签: c++ vulkan


    【解决方案1】:

    在 Vulkan 中,我们有两个需要使用 dlopen/LoadLibrary 加载的全局函数。它们是 vkGetInstanceProcAddr 和 vkGetDeviceProcAddr。

    错了。只有vkGetInstanceProcAddrvkGetDeviceProcAddr本身可以从vkGetInstanceProcAddr加载。

    我应该在运行时还是链接时更好地加载库?我将不同的逻辑设备(从相同的 gpu 创建)提供给 vkGetDeviceProcAddr 以查询相同的函数。他们都返回相同的地址给我。

    VkInstance 获取从vkGetInstanceProcAddr 获取的命令仅限于与获取它们时使用的instance 完全相同。

    vkGetDeviceProcAddr 获得的类似命令只能与用于获得它们的device(VkDevice 类型)一起使用。

    它们可能并且经常是相同的,但您无法提前知道并让它在每个平台/PC 上都以这种方式工作。您可以只加载单个命令,以测试它是否是相同的指针并推断其他命令也会如此——但这只是如履薄冰,没有合理的好处。

    为方便起见,在链接时使用官方加载器执行此操作,除非您有理由不这样做。

    我认为重新加载可能是浪费。

    除非您打算用数百万个 GPU 不断连接和断开连接来制作硬件,否则不要担心它并正确加载命令。这是很小的一次(或很少)时间成本,它将很快在您之后进行的所有渲染中分摊。

    也没有“重新加载”。新指针可以与旧指针和平共处。在 C++ 中,您可能会将这些成员函数设为 Instance 或 Device...

    我的问题是这个想法是怎么来的?是多实现还是多GPU?

    是的,有点。

    您肯定会看到,如果您有两个 GPU(来自不同的供应商),具有不同的驱动程序文件(通常是一些 *.dll 或其他平台上的等效文件),那么必须以某种方式选择指向正确文件的指针。

    vkGetInstanceProcAddr 解决了它,以便它为您提供指向另一个函数的指针,该函数选择并调用正确的指针。 (“一切都可以通过另一个级别的间接来解决”,对吧?)。静态加载 Khronos/Official/LunargSDK 加载器可能会做类似的事情。

    对于vkGetDeviceProcAddr,您只需提供确切的设备,然后它会简单地为您提供指向该特定 GPU 驱动程序的直接函数指针。

    实例也可能发生同样的情况(加载程序必须只导出vkGetInstanceProcAddr,其余的可以在其他地方)。虽然通常(如 The Loader 的情况)它会导出所有实例级甚至间接设备级函数指针以方便使用。

    如您所见,如果我有多个设备,重新加载将非常浪费......而且函数指针也会占用大量内存......

    并没有像之前所说的那样浪费过多的 CPU 时间。

    如果您没有这几 kB 的指针,您可能无论如何都无法开发可行的基于 Vulkan 的应用程序。您只能加载您实际使用的那些命令,但我看不出这种类型的过早优化的原因。你有什么特殊的硬件,需要如此绝望的措施或做一个 64K 演示?

    【讨论】:

      【解决方案2】:

      “实例函数指针”和“设备函数指针”的区别是针对那些想要更快的函数调用性能的人。

      您可以仅使用 vkGetInstanceProcAddr 就可以很好地使用 Vulkan。此函数将检索 所有 Vulkan 函数的函数指针。这些函数指针将使用存储在您传递的各种 Vulkan 对象中的调度信息,以确定您正在与哪个设备通信。这些指针可用于任何实例、设备或设备相关对象。

      您从vkGetDeviceProcAddr 获得的指针知道它们适用于特定设备。他们不需要使用调度逻辑来调用该设备。所以他们的水平略低。缺点是您只能将它们与特定的VkDevice 或设备派生对象一起使用。

      对于大多数人来说,调度开销可能不会大到值得费心。但是,如果您真的很在意此类事情,则可以选择避免。

      如果我有多个设备,重新加载将非常浪费......并且函数指针也可能会使用大量内存......

      无论你是否查询它们的指针,这些函数都存在;因此,获取它们的指针只会占用您用来存储指向它们的指针的内存。 Vulkan 的 API 包含大约 140 个函数。每个函数指针 8 个字节,即 1120 个字节,刚好超过 1KB。

      至于加载它们所花费的时间...如果加载 140 个函数指针花费的时间超过几微秒,我会感到震惊。微秒。您在启动时执行一次

      【讨论】:

      • 创建第二个逻辑设备时不需要重新加载函数,对吗?
      • 使用其他设备加载的函数ptr安全吗?
      • @TimHsu:设备指针特定于设备。我不确定我能对此有多清楚。如果您想获得可与 any 设备一起使用的指针,请使用实例指针。
      • 那么比如vkGetInstanceProcAddr和vkGetDeviceProcAddr加载的vkCreateCommandBuffers的地址不一样?
      • 谢谢,所以我的问题是依赖于实现?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-01
      • 1970-01-01
      • 2020-12-14
      相关资源
      最近更新 更多