0

在 DirectX12 中,您可以使用相当于世界变换的单个统一缓冲区来渲染不同位置的多个对象,例如:

// Basic simplified pseudocode
SetRootSignature();
SetPrimitiveTopology();
SetPipelineState();
SetDepthStencilTarget();
SetViewportAndScissor();

for (auto object : objects)
{
    SetIndexBuffer();
    SetVertexBuffer();

    struct VSConstants
    {
        QEDx12::Math::Matrix4 modelToProjection;
    } vsConstants;
    vsConstants.modelToProjection = ViewProjMat * object->GetWorldProj();
    SetDynamicConstantBufferView(0, sizeof(vsConstants), &vsConstants);

    DrawIndexed();
}

但是,在 Vulkan 中,如果您对单个统一缓冲区执行类似操作,则所有对象都将呈现在最后一个世界矩阵的位置:

for (auto object : objects)
{
    SetIndexBuffer();
    SetVertexBuffer();

    UploadUniformBuffer(object->GetWorldProj());

    DrawIndexed();
}

有没有办法在 Vulkan 中使用单个统一缓冲区绘制多个对象,就像在 DirectX12 中一样?

我知道 Sascha Willem 的动态统一缓冲区示例 ( https://github.com/SaschaWillems/Vulkan/tree/master/dynamicuniformbuffer ),他在一个大的统一缓冲区中打包了许多矩阵,虽然有用,但并不完全是我的我正在寻找。

提前感谢您的帮助。

4

2 回答 2

2

我找不到SetDynamicConstantBufferView在 D3D 12 API 中调用的函数。我想这是你发明的一些功能,但不知道它做了什么,我只能猜测。

看起来您在渲染时正在将数据上传到缓冲区对象。如果是这样的话,那么,Vulkan 就无法做到这一点。这是一件好事。上传到您当前正在读取的内存需要同步。您必须在读取要覆盖的数据的最后一个渲染命令和下一个渲染命令之间设置屏障。如果您喜欢性能,这不是一个好主意。

但同样,我不确定该函数到底在做什么,所以我的理解可能是错误的。


在 Vulkan 中,通常不会在渲染帧的过程中更改描述符。然而,Vulkan 的制造商意识到用户有时希望使用同一VkBuffer对象的不同子集进行绘制。这就是动态统一/存储缓冲区的用途。

从技术上讲,您没有多个统一缓冲区;你只有一个。但是您可以使用提供的偏移量vkCmdBindDescriptorSets来转移下一个渲染命令将从该缓冲区中获取数据的位置。所以这是一种用不同数据提供不同渲染命令的轻量级方法。

基本上,您重新绑定描述符集,但使用不同的pDynamicOffset数组值。为了使这些工作,您需要提前计划。您的管道布局必须明确将这些描述符声明为动态描述符。每次绑定集合时,都需要提供该描述符使用的缓冲区的偏移量。

话虽如此,最好让您的统一缓冲区存储更大的矩阵数组,使用动态偏移量从一个矩阵块跳转到另一个矩阵块。你会

关键是您提供的统一数据(取决于硬件)将保留在着色器内存中,除非您更改偏移量或着色器。上传此类数据的成本很小,因此尽量减少此类上传的需求可能不是一个坏主意。

因此,您应该在一次 DMA 操作中上传所有对象缓冲区数据。然后你发出一个障碍,并进行渲染,使用动态偏移等来告诉每个偏移它去哪里。

于 2017-08-01T02:38:54.037 回答
1

您要么必须使用 Push 常量,要么必须为每个位置使用单独的统一缓冲区。这些可以与每个动态偏移位置的描述符绑定。

在 Sasha 的示例中,您可以在制服中拥有不止一个矩阵。

这意味着在 UploadUniformBuffer 中,您将新矩阵附加到缓冲区并绑定新位置。

于 2017-07-31T21:37:50.923 回答