在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的Dynamic统一缓冲区示例(https://github.com/SaschaWillems/Vulkan/tree/master/dynamicuniformbuffer),他在一个大的统一缓冲区中打包了很多矩阵,虽然很有用,但并不是我想要的。
提前感谢您的帮助。
答案 0 :(得分:1)
您必须使用推送常量或为每个位置分别使用统一缓冲区。这些可以使用每个动态偏移位置的描述符进行绑定。
在Sasha的例子中,你可以拥有的不仅仅是制服内的一个矩阵。
这意味着在UploadUniformBuffer中你将新矩阵附加到缓冲区并绑定新位置。
答案 1 :(得分:1)
我在D3D 12 API中找不到名为SetDynamicConstantBufferView
的函数。我认为这是你的发明的一些功能,但不知道它的作用,我只能猜测。
看起来您在渲染时会将数据上传到缓冲区对象。如果是这样的话,那么,Vulkan无法做到这一点。这是好事。上传到您当前正在读取的内存需要同步。您必须在正在读取您要覆盖的数据的最后一个渲染命令和下一个渲染命令之间发出障碍。如果你喜欢表演,这不是一个好主意。
但同样,我不确定该功能到底在做什么,所以我的理解可能是错误的。
在Vulkan中,描述符通常不会在渲染帧的过程中被更改。然而,Vulkan的制造者意识到用户有时想要使用相同VkBuffer
对象的不同子集进行绘制。这就是动态统一/存储缓冲区的用途。
从技术上讲,你没有多个统一缓冲区;你只有一个。但是您可以使用提供给vkCmdBindDescriptorSets
的偏移量来移动该缓冲区中下一个渲染命令将从中获取数据的位置。因此,它是一种轻量级的方式,可以提供不同数据的渲染命令。
基本上,您重新绑定了描述符集,但具有不同的pDynamicOffset
数组值。要使这些工作,您需要提前计划。您的管道布局必须明确地将这些描述符声明为动态描述符。每次绑定集合时,您都需要将偏移量提供给该描述符使用的缓冲区。
话虽这么说,使用统一缓冲区存储更大的矩阵数组可能会更好,使用动态偏移量从一个矩阵块跳转到另一个矩阵块。你会tehn
关键在于,您提供的统一数据(取决于硬件)将保留在着色器内存中,除非您执行某些操作来更改偏移或着色器。上传此类数据需要一些小的成本,因此最大限度地减少对这种上传的需求可能不是一个坏主意。
因此,您应该在单个DMA操作中上传所有对象缓冲区数据。然后你发出一个障碍,并使用动态偏移进行渲染,并告诉每个偏移它的位置。