渲染调用性能消耗

Render-call performance drain

本文关键字:性能 调用      更新时间:2023-10-16

我的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绝对不会负担过重(着色器非常便宜)。

如果我使用上面的代码运行程序,则帧将在大约46ms中渲染。没有它,它只是9ms

我使用fifo呈现模式与2交换链图像。此时只有一个主命令缓冲区(没有辅助命令缓冲区/预记录缓冲区),所有帧都有相同的缓冲区。

我的问题是,我真的不知道该找什么。这几个渲染调用应该不会产生什么影响,所以问题的根源一定在其他地方。

有人能给我一些建议吗?Vulkan的评测人员已经来了吗?我只需要朝着正确的方向轻轻一推。

//编辑:

因此,如果渲染了所有238个网格,那么vkDeviceWaitIdle似乎需要大约32ms才能执行。(如果没有渲染,则为<1ms)。大多数停滞都源于此,但我仍然不知道该怎么办。

因此,如果渲染了所有238个网格,那么vkDeviceWaitIdle似乎需要大约32ms才能执行。(如果没有渲染,则为<1ms)。大多数拖延都源于此,但我仍然不知道该怎么办

避免使用vkDeviceWaitIdle。这是可用的最重的同步操作,将迫使GPU完成并刷新所有工作。

尝试使用其他更轻量级的同步对象,如信号量、屏障、围栏和事件,并指定范围尽可能窄的访问掩码和管道阶段。

狭窄的范围,特别是对于管道阶段,可以确保管道的其他部分可以继续工作,而对于vkDeviceWatiIdle,您可以暂停管道的所有部分。

绝对没有理由在渲染循环中使用vkDeviceWaitIdle

相反,您应该在vkQueueSubmit调用中添加一个vkFence,并使用vkGetFenceStatus查看是否可以触摸命令缓冲区使用的内存。

这将像环形缓冲区一样使用,因此存储可变数据(视图矩阵等)的多个副本,直到GPU处理完它们。