如何从 Vulkan 渲染到 OpenGL?
How to render to OpenGL from Vulkan?
是否可以从 Vulkan 渲染到 OpenGL?
nVidia 似乎有一些东西:
https://lunarg.com/faqs/mix-opengl-vulkan-rendering/
其他 GPU 也可以吗?
NVIDIA 已经创建了一个 OpenGL 扩展,NV_draw_vulkan_image,它可以在 OpenGL 中渲染 VkImage
。它甚至有一些与 Vulkan 信号量等交互的机制。
但是,according to the documentation,您必须绕过所有 Vulkan 层,因为层可以修改不可分派的句柄,而 OpenGL 扩展不知道这些修改。他们推荐的方法是对所有 Vulkan 函数使用 glGetVkProcAddrNV
。
这也意味着您无法访问依赖于 Vulkan 层的任何调试。
SIGGRAPH 2016 的最新 slide deck 中提供了更多信息。幻灯片 63-65 描述了如何将 Vulkan 图像 blit 到 OpenGL 后备缓冲区。我的观点是,由于 Vulkan 驱动程序包含在 libGL.so(在 Linux 上),NVIDIA 可能很容易支持它。因此,将 Vulkan 图像句柄提供给驱动程序的 GL 端并使其有用可能并不难。
正如另一个答案所指出的,仍然没有官方注册的多供应商互操作扩展。这种方法只适用于 NVIDIA。
是的,如果如果Vulkan 实现和 OpenGL 实现都有可用的适当扩展,这是可能的。
这是从 example app in the Vulkan Samples repository which uses OpenGL to render a simple shadertoy 到纹理的屏幕截图,然后在渲染的 Vulkan 中使用该纹理 window。
虽然你的问题似乎表明你想做相反的事情(使用 Vulkan 渲染某些东西,然后使用 OpenGL 显示结果),但同样的概念适用......在一个纹理中填充纹理 API ,使用同步来确保GPU工作完成,然后使用另一个API中的纹理。您也可以对缓冲区执行相同的操作,例如,您可以使用 Vulkan 进行计算操作,然后在 OpenGL 渲染中使用结果。
要求
这样做需要 OpenGL 和 Vulkan 实现都支持所需的扩展,但是,根据 this site,这些扩展在 OS 版本和 GPU 供应商中得到广泛支持,只要您我正在使用最新的 (> 1.0.51) 版本的 Vulkan。
您需要 Vulkan 的 External Objects extension for OpenGL and the External Memory/Fence/Sempahore 扩展。
扩展的 Vulkan 端允许您分配内存、创建信号量或栅栏,同时将生成的对象标记为可导出。相应的 GL 扩展允许您获取对象并使用新的 GL 命令操作它们,这些命令允许您等待栅栏、发出信号并等待信号量,或使用 Vulkan 分配的内存来支持 OpenGL 纹理。通过在 OpenGL 帧缓冲区中使用这样的纹理,您几乎可以渲染任何您想要的东西,然后在 Vulkan 中使用渲染结果。
导出/导入示例代码
例如,在 Vulkan 端,当您为图像分配内存时,您可以这样做...
vk::Image image;
... // create the image as normal
vk::MemoryRequirements memReqs = device.getImageMemoryRequirements(image);
vk::MemoryAllocateInfo memAllocInfo;
vk::ExportMemoryAllocateInfo exportAllocInfo{
vk::ExternalMemoryHandleTypeFlagBits::eOpaqueWin32
};
memAllocInfo.pNext = &exportAllocInfo;
memAllocInfo.allocationSize = memReqs.size;
memAllocInfo.memoryTypeIndex = context.getMemoryType(
memReqs.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal);
vk::DeviceMemory memory;
memory = device.allocateMemory(memAllocInfo);
device.bindImageMemory(image, memory, 0);
HANDLE sharedMemoryHandle = device.getMemoryWin32HandleKHR({
texture.memory, vk::ExternalMemoryHandleTypeFlagBits::eOpaqueWin32
});
这是使用 C++ 接口和扩展的 Win32 变体。对于 Posix 平台,有替代方法获取文件描述符而不是 WIN32 句柄。
sharedMemoryHandle
是您需要传递给 OpenGL 的值,以及实际分配大小。在 GL 端,您可以执行此操作...
// These values should be populated by the vulkan code
HANDLE sharedMemoryHandle;
GLuint64 sharedMemorySize;
// Create a 'memory object' in OpenGL, and associate it with the memory
// allocated in vulkan
GLuint mem;
glCreateMemoryObjectsEXT(1, mem);
glImportMemoryWin32HandleEXT(mem, sharedMemorySize,
GL_HANDLE_TYPE_OPAQUE_WIN32_EXT, sharedMemoryHandle);
// Having created the memory object we can now create a texture and use
// the memory object for backing it
glCreateTextures(GL_TEXTURE_2D, 1, &color);
// The internalFormat here should correspond to the format of
// the Vulkan image. Similarly, the w & h values should correspond to
// the extent of the Vulkan image
glTextureStorageMem2DEXT(color, 1, GL_RGBA8, w, h, mem, 0 );
同步
这里最棘手的一点是同步。 Vulkan 规范要求图像在执行相应操作之前处于特定状态(布局)。因此,为了正确执行此操作(根据我的理解),您需要...
- 在 Vulkan 中,创建一个将图像转换为 ColorAttachmentOptimal 布局的命令缓冲区
- 提交命令缓冲区,使其发出信号量,该信号量已类似地导出到 OpenGL
- 在 OpenGL 中,使用
glWaitSemaphoreEXT
函数使 GL 驱动程序等待转换完成。
- 注意这是一个GPU端等待,所以这个函数根本不会阻塞。在这方面,它类似于
glWaitSync
(相对于 glClientWaitSync
)。
- 执行渲染到帧缓冲区的 GL 命令
- 在 GL 端用
glSignalSemaphoreEXT
函数向不同的导出信号量发出信号
- 在 Vulkan 中,执行另一个从 ColorAttachmentOptimal 到 ShaderReadOnlyOptimal 的图像布局转换
- 提交转换命令缓冲区,并将等待信号量设置为您刚刚从 GL 端发出的信号量。
那将是最佳路径。或者,快速而肮脏的方法是进行 vulkan 转换,然后执行队列和设备 waitIdle
命令以确保工作完成,执行 GL 命令,然后是 glFlush
& glFinish
命令以确保 GPU 完成 that 工作,然后恢复 Vulkan 命令。这更像是一种蛮力方法,可能会产生比正确同步更差的性能。
是否可以从 Vulkan 渲染到 OpenGL?
nVidia 似乎有一些东西: https://lunarg.com/faqs/mix-opengl-vulkan-rendering/
其他 GPU 也可以吗?
NVIDIA 已经创建了一个 OpenGL 扩展,NV_draw_vulkan_image,它可以在 OpenGL 中渲染 VkImage
。它甚至有一些与 Vulkan 信号量等交互的机制。
但是,according to the documentation,您必须绕过所有 Vulkan 层,因为层可以修改不可分派的句柄,而 OpenGL 扩展不知道这些修改。他们推荐的方法是对所有 Vulkan 函数使用 glGetVkProcAddrNV
。
这也意味着您无法访问依赖于 Vulkan 层的任何调试。
SIGGRAPH 2016 的最新 slide deck 中提供了更多信息。幻灯片 63-65 描述了如何将 Vulkan 图像 blit 到 OpenGL 后备缓冲区。我的观点是,由于 Vulkan 驱动程序包含在 libGL.so(在 Linux 上),NVIDIA 可能很容易支持它。因此,将 Vulkan 图像句柄提供给驱动程序的 GL 端并使其有用可能并不难。
正如另一个答案所指出的,仍然没有官方注册的多供应商互操作扩展。这种方法只适用于 NVIDIA。
是的,如果如果Vulkan 实现和 OpenGL 实现都有可用的适当扩展,这是可能的。
这是从 example app in the Vulkan Samples repository which uses OpenGL to render a simple shadertoy 到纹理的屏幕截图,然后在渲染的 Vulkan 中使用该纹理 window。
虽然你的问题似乎表明你想做相反的事情(使用 Vulkan 渲染某些东西,然后使用 OpenGL 显示结果),但同样的概念适用......在一个纹理中填充纹理 API ,使用同步来确保GPU工作完成,然后使用另一个API中的纹理。您也可以对缓冲区执行相同的操作,例如,您可以使用 Vulkan 进行计算操作,然后在 OpenGL 渲染中使用结果。
要求
这样做需要 OpenGL 和 Vulkan 实现都支持所需的扩展,但是,根据 this site,这些扩展在 OS 版本和 GPU 供应商中得到广泛支持,只要您我正在使用最新的 (> 1.0.51) 版本的 Vulkan。
您需要 Vulkan 的 External Objects extension for OpenGL and the External Memory/Fence/Sempahore 扩展。
扩展的 Vulkan 端允许您分配内存、创建信号量或栅栏,同时将生成的对象标记为可导出。相应的 GL 扩展允许您获取对象并使用新的 GL 命令操作它们,这些命令允许您等待栅栏、发出信号并等待信号量,或使用 Vulkan 分配的内存来支持 OpenGL 纹理。通过在 OpenGL 帧缓冲区中使用这样的纹理,您几乎可以渲染任何您想要的东西,然后在 Vulkan 中使用渲染结果。
导出/导入示例代码
例如,在 Vulkan 端,当您为图像分配内存时,您可以这样做...
vk::Image image;
... // create the image as normal
vk::MemoryRequirements memReqs = device.getImageMemoryRequirements(image);
vk::MemoryAllocateInfo memAllocInfo;
vk::ExportMemoryAllocateInfo exportAllocInfo{
vk::ExternalMemoryHandleTypeFlagBits::eOpaqueWin32
};
memAllocInfo.pNext = &exportAllocInfo;
memAllocInfo.allocationSize = memReqs.size;
memAllocInfo.memoryTypeIndex = context.getMemoryType(
memReqs.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal);
vk::DeviceMemory memory;
memory = device.allocateMemory(memAllocInfo);
device.bindImageMemory(image, memory, 0);
HANDLE sharedMemoryHandle = device.getMemoryWin32HandleKHR({
texture.memory, vk::ExternalMemoryHandleTypeFlagBits::eOpaqueWin32
});
这是使用 C++ 接口和扩展的 Win32 变体。对于 Posix 平台,有替代方法获取文件描述符而不是 WIN32 句柄。
sharedMemoryHandle
是您需要传递给 OpenGL 的值,以及实际分配大小。在 GL 端,您可以执行此操作...
// These values should be populated by the vulkan code
HANDLE sharedMemoryHandle;
GLuint64 sharedMemorySize;
// Create a 'memory object' in OpenGL, and associate it with the memory
// allocated in vulkan
GLuint mem;
glCreateMemoryObjectsEXT(1, mem);
glImportMemoryWin32HandleEXT(mem, sharedMemorySize,
GL_HANDLE_TYPE_OPAQUE_WIN32_EXT, sharedMemoryHandle);
// Having created the memory object we can now create a texture and use
// the memory object for backing it
glCreateTextures(GL_TEXTURE_2D, 1, &color);
// The internalFormat here should correspond to the format of
// the Vulkan image. Similarly, the w & h values should correspond to
// the extent of the Vulkan image
glTextureStorageMem2DEXT(color, 1, GL_RGBA8, w, h, mem, 0 );
同步
这里最棘手的一点是同步。 Vulkan 规范要求图像在执行相应操作之前处于特定状态(布局)。因此,为了正确执行此操作(根据我的理解),您需要...
- 在 Vulkan 中,创建一个将图像转换为 ColorAttachmentOptimal 布局的命令缓冲区
- 提交命令缓冲区,使其发出信号量,该信号量已类似地导出到 OpenGL
- 在 OpenGL 中,使用
glWaitSemaphoreEXT
函数使 GL 驱动程序等待转换完成。- 注意这是一个GPU端等待,所以这个函数根本不会阻塞。在这方面,它类似于
glWaitSync
(相对于glClientWaitSync
)。
- 注意这是一个GPU端等待,所以这个函数根本不会阻塞。在这方面,它类似于
- 执行渲染到帧缓冲区的 GL 命令
- 在 GL 端用
glSignalSemaphoreEXT
函数向不同的导出信号量发出信号 - 在 Vulkan 中,执行另一个从 ColorAttachmentOptimal 到 ShaderReadOnlyOptimal 的图像布局转换
- 提交转换命令缓冲区,并将等待信号量设置为您刚刚从 GL 端发出的信号量。
那将是最佳路径。或者,快速而肮脏的方法是进行 vulkan 转换,然后执行队列和设备 waitIdle
命令以确保工作完成,执行 GL 命令,然后是 glFlush
& glFinish
命令以确保 GPU 完成 that 工作,然后恢复 Vulkan 命令。这更像是一种蛮力方法,可能会产生比正确同步更差的性能。