Glsl 如何重复更新单个Vulkan渲染过程中对象数量的统一数据并使更新同步?
我正在尝试将我的OpenGL 3D游戏引擎移植到Vulkan。游戏场景中有大量3D对象,每个对象都有自己的属性(模型矩阵、灯光等),并且对象是完全动态的,这意味着在游戏过程中可能会出现一些3D对象,而其他对象可能会被移除。使用OpenGL,我将3D对象的属性分组到着色器中的统一缓冲区中(代码简化): 我现在要做的是对游戏场景中的每个3D对象使用这个统一的缓冲区,通过Vulkan渲染它们。 我使用一个Vulkan渲染过程,在开始渲染过程和结束渲染过程中,我使用for-each循环遍历每个3D对象,并执行以下操作来渲染它们。请参阅下面的伪代码Glsl 如何重复更新单个Vulkan渲染过程中对象数量的统一数据并使更新同步?,glsl,shader,vulkan,Glsl,Shader,Vulkan,我正在尝试将我的OpenGL 3D游戏引擎移植到Vulkan。游戏场景中有大量3D对象,每个对象都有自己的属性(模型矩阵、灯光等),并且对象是完全动态的,这意味着在游戏过程中可能会出现一些3D对象,而其他对象可能会被移除。使用OpenGL,我将3D对象的属性分组到着色器中的统一缓冲区中(代码简化): 我现在要做的是对游戏场景中的每个3D对象使用这个统一的缓冲区,通过Vulkan渲染它们。 我使用一个Vulkan渲染过程,在开始渲染过程和结束渲染过程中,我使用for-each循环遍历每个3D对象,
vkBeginCommandBuffer(cmdBuffer, ...);
vkCmdBeginRenderPass(cmdBuffer, ...);
for(object3D obj : scene->objects)
{
// Step 1 - update object's uniform data by memcpy()
_updateUniformBuffer(obj);
// Step 2 - build draw command for this object
// bind vertex buffer, bind index buffer, bind pipeline, ..., draw
_buildDrawCommands(obj);
}
vkCmdEndRenderPass(cmdBuffer, ...);
vkEndCommandBuffer(cmdBuffer, ...);
vkQueueSubmit(...); // Finally, submit the commands to queue to render the scene
显然,我的解决方案将不起作用,因为缓冲区中的所有Vulkan命令只有在调用vkQueueSubmit()之后才能在GPU上执行。但是对_updateUniformBuffer(obj)(由memcpy(…)调用)的调用与命令记录“交错”,并立即执行,因此序列混乱,最终每个对象将无法获得自己的属性
所以,可能的问题是,Vulkan如何正确地为单个渲染过程中的每个对象重复更新统一缓冲区,并确保每个对象获得正确的属性数据
在我发布这个问题之前,我试图思考以下解决方案,但没有一个是好的:
- 使用“每个对象渲染过程”并使用“围栏”确保一个对象完全渲染,直到我开始渲染下一个对象。如果有1000个对象,每帧将有1000个渲染过程?这是不可能的
- 我可以在一个渲染过程中重复提交命令缓冲区吗?我的意思是,在为一个对象生成绘制命令以渲染该对象之后,立即提交命令缓冲区,使用fence确保渲染完成,然后转到下一个对象。这将有一个渲染过程和1000个vkQueueSubmit()调用
- 使用动态统一缓冲区创建一个巨大的统一缓冲区,包含1000个对象的数据。由于对象编号是动态的,因此很难实现
- 使用推力常数?这也是不可能的,因为最大数据大小只有128字节
在每帧结束时(假设每帧有一个命令缓冲区),记住您在缓冲区中走了多远,并将其与表示该命令缓冲区完成的事件相关联。该事件将告诉您何时可以覆盖该帧中使用的缓冲区区域。如果在重新获得足够的可用空间之前,您需要更多的空间来放置制服,您可以创建一个新的VkBuffer并开始使用它,最终在其数据失效时返回到原始状态。这样,您就可以得到由多个Vkbuffer组成的统一数据动态大小的环形缓冲区。值得注意的是,“巨大”的统一缓冲区通常比逐顶点数据小几个数量级,因此,不要害怕保守,分配一个比您认为需要的更大的块。它通常有助于以字节表示大小,1024x1024 RGB纹理是4MB,一千个256字节的统一块每个是256KB。GPU的可用内存通常是GB。谢谢大家给我提供的有用建议。我认为动态UBO似乎是最可行的解决方案。但是,该解决方案添加了一些额外的工作来保持每对象偏移。既然游戏场景或CAD应用程序中的对象是动态的(不断进出),那么为5000个对象预先分配UBO,然后使用唯一的每个对象ID来执行动态偏移计算是一个好主意,还是有一个更聪明的方法?我不会保留每个对象的偏移,我会每次绘制。偏移量只是“下一次绘制的制服放置位置”,每次绘制对象时,对象使用的偏移量可能会改变。嗨,Jesse,我想我已经了解了动态缓冲区解决方案的概念,但对于下半部分,我不是很清楚-如何实现“由多个VKBuffer组成的统一数据的动态大小的环形缓冲区”。你能推荐一个Internet上的示例项目,可以给我提供更多的实现细节吗?谢谢。
vkBeginCommandBuffer(cmdBuffer, ...);
vkCmdBeginRenderPass(cmdBuffer, ...);
for(object3D obj : scene->objects)
{
// Step 1 - update object's uniform data by memcpy()
_updateUniformBuffer(obj);
// Step 2 - build draw command for this object
// bind vertex buffer, bind index buffer, bind pipeline, ..., draw
_buildDrawCommands(obj);
}
vkCmdEndRenderPass(cmdBuffer, ...);
vkEndCommandBuffer(cmdBuffer, ...);
vkQueueSubmit(...); // Finally, submit the commands to queue to render the scene