渲染调用性能消耗
Render-call performance drain
我的 Vulkan 程序 运行 非常慢,我正在尝试找出原因。我注意到即使是一些绘制调用也已经比它们应该消耗的性能要多得多。
例如,这是用于渲染一些网格的提取物(伪代码):
int32_t numCalls = 0;
int32_t numIndices = 0;
for(auto &mesh : meshes)
{
auto vertexBuffer = mesh.GetVertexBuffer();
auto indexBuffer = mesh.GetIndexBuffer();
vk::DeviceSize offset = 0;
drawCmd.bindVertexBuffers(0,1,&vertexBuffer,&offset); // drawCmd = CommandBuffer for all drawing commands (single thread)
drawCmd.bindIndexBuffer(indexBuffer,offset,vk::IndexType::eUint16);
drawCmd.drawIndexed(mesh.GetIndexCount(),1,0,0,0);
numIndices += mesh.GetIndexCount();
++numCalls;
}
渲染了238个网格,顶点索引总数为52050。GPU绝对不会负担过重(着色器非常便宜)。
如果我 运行 我的程序使用上面的代码,帧的渲染时间大约为 46 毫秒 。没有它只是 9ms.
我正在使用带有 2 个交换链图像的 fifo 呈现模式。此时只有一个主要命令缓冲区(没有辅助命令 buffers/pre-recorded 缓冲区),所有帧的缓冲区相同。
我的问题是,我真的不知道要寻找什么。这几个渲染调用应该几乎没有影响,所以问题的根源一定是在别的地方。
任何人都可以给我任何提示,我应该如何解决这个问题?是否已经有 Vulkan 的分析器?我只需要朝着正确的方向推动。
// 编辑:
因此,如果渲染了所有 238 个网格,vkDeviceWaitIdle 似乎需要大约 32 毫秒才能执行。 (如果 none 被渲染,则小于 1 毫秒)。
大多数拖延都源于此,但我仍然不知道该怎么办。
So, it looks like vkDeviceWaitIdle takes about 32ms to execute, if all 238 meshes are rendered. (If none are rendered, it's < 1ms). Most of the stalling stems from there, but I still don't know what to do about it.
避免使用 vkDeviceWaitIdle。这是可用的最繁重的同步操作,将强制 GPU 完成并刷新所有工作。
尝试使用其他更轻量级的同步对象,如信号量、屏障、栅栏和事件,并指定范围尽可能窄的访问掩码和管道阶段。
一个狭窄的范围,特别是对于管道阶段,确保管道的其他部分可以继续工作,而使用 vkDeviceWatiIdle 你可能会停止管道的所有部分。
绝对没有理由在渲染循环中使用 vkDeviceWaitIdle
。
相反,您应该向 vkQueueSubmit
调用添加一个 vkFence 并使用 vkGetFenceStatus
查看您是否可以触及命令缓冲区使用的内存。
这将像环形缓冲区一样使用,因此会存储可变数据(视图矩阵等)的多个副本,直到 GPU 处理完它们。
我的 Vulkan 程序 运行 非常慢,我正在尝试找出原因。我注意到即使是一些绘制调用也已经比它们应该消耗的性能要多得多。 例如,这是用于渲染一些网格的提取物(伪代码):
int32_t numCalls = 0;
int32_t numIndices = 0;
for(auto &mesh : meshes)
{
auto vertexBuffer = mesh.GetVertexBuffer();
auto indexBuffer = mesh.GetIndexBuffer();
vk::DeviceSize offset = 0;
drawCmd.bindVertexBuffers(0,1,&vertexBuffer,&offset); // drawCmd = CommandBuffer for all drawing commands (single thread)
drawCmd.bindIndexBuffer(indexBuffer,offset,vk::IndexType::eUint16);
drawCmd.drawIndexed(mesh.GetIndexCount(),1,0,0,0);
numIndices += mesh.GetIndexCount();
++numCalls;
}
渲染了238个网格,顶点索引总数为52050。GPU绝对不会负担过重(着色器非常便宜)。
如果我 运行 我的程序使用上面的代码,帧的渲染时间大约为 46 毫秒 。没有它只是 9ms.
我正在使用带有 2 个交换链图像的 fifo 呈现模式。此时只有一个主要命令缓冲区(没有辅助命令 buffers/pre-recorded 缓冲区),所有帧的缓冲区相同。
我的问题是,我真的不知道要寻找什么。这几个渲染调用应该几乎没有影响,所以问题的根源一定是在别的地方。
任何人都可以给我任何提示,我应该如何解决这个问题?是否已经有 Vulkan 的分析器?我只需要朝着正确的方向推动。
// 编辑:
因此,如果渲染了所有 238 个网格,vkDeviceWaitIdle 似乎需要大约 32 毫秒才能执行。 (如果 none 被渲染,则小于 1 毫秒)。 大多数拖延都源于此,但我仍然不知道该怎么办。
So, it looks like vkDeviceWaitIdle takes about 32ms to execute, if all 238 meshes are rendered. (If none are rendered, it's < 1ms). Most of the stalling stems from there, but I still don't know what to do about it.
避免使用 vkDeviceWaitIdle。这是可用的最繁重的同步操作,将强制 GPU 完成并刷新所有工作。
尝试使用其他更轻量级的同步对象,如信号量、屏障、栅栏和事件,并指定范围尽可能窄的访问掩码和管道阶段。
一个狭窄的范围,特别是对于管道阶段,确保管道的其他部分可以继续工作,而使用 vkDeviceWatiIdle 你可能会停止管道的所有部分。
绝对没有理由在渲染循环中使用 vkDeviceWaitIdle
。
相反,您应该向 vkQueueSubmit
调用添加一个 vkFence 并使用 vkGetFenceStatus
查看您是否可以触及命令缓冲区使用的内存。
这将像环形缓冲区一样使用,因此会存储可变数据(视图矩阵等)的多个副本,直到 GPU 处理完它们。