我是新来的vulkan。问题在于转换对象。当使用DX11和OpenGL时,我用来更新统一缓冲区,然后将绘图命令发送到gpu,但是在vulkan中,所有命令都是预先记录的。所以我认为我无法做到这一点。我在网上读到,为了转换每个对象,我可以使用一系列均匀缓冲区并在绘制时从中索引。这是改变vulkan中每个对象的唯一方法吗?
如果不是那么使用比旧API更多的内存?较旧的API可以具有单个统一缓冲区并在绘制调用之前更新它,但在vulkan中,我们为每个对象使用缓冲区。 Vulkan作为高性能API很受欢迎,但较旧的API使用较少的内存。
如果不是,我怎样才能更有效地做到这一点? 感谢。
答案 0 :(得分:3)
在像OpenGL这样的高级图形API中,统一变量也位于全局/通用统一缓冲区中。为方便起见,它只是没有暴露给开发人员。但是统一变量更新的执行方式与Vulkan类似 - 这是向统一缓冲区的正常数据传输。
现在,如果您想在绘制对象之前更新统一变量,您可以在Vulkan中完全相同。有像vkCmdUpdateBuffer()或vkCmdCopyBuffer()这样的方法。但为什么开发人员不使用这种方法呢?由于同步和对性能的影响。在OpenGL中,这是由驱动程序自动完成的,但它与Vulkan具有相同的影响。它并没有暴露给开发人员。 Vulkan表明,如果您正在考虑性能,这不是最好的方法。保持一组统一缓冲区(每个对象一个)或一个统一缓冲区与一系列统一变量更好。您也可以使用推送常量来实现此目的。使用它们类似于旧的,类似OpenGL的更新存储在全局命名空间中的统一变量,但数据量有限(规范保证128字节)。
答案 1 :(得分:2)
因此,假装Vulkan是OpenGL \ immediate API是完全有效的:
for( int i = 0; i < N; ++i ){
cmdbuff.begin(); cmdUpdateUniform(u[i]); cmdbuff.end();
vkQueueSubmit( q, cmdbuff ); // lookitme ama glUniform*()
// some sychronization omitted
cmdbuff.begin(); vkCmdDraw(obj[i]); cmdbuff.end();
vkQueueSubmit( q, cmdbuff ); // lookitme ama glDraw*()
vkQueueWaitIdle( q ); // lookitme ama glFinish()
}
但是,这有一个问题。 OpenGL驱动程序会尝试使用延迟与吞吐量交易来优化这一点。但是在Vulkan中,我们希望对延迟有一定程度的控制,因此Vulkan驱动程序不会(不应该)以这种方式对其进行优化。
因此我们可以尝试猜测OpenGL驱动程序会做什么:
cmdbuff.begin();
for( int i = 0; i < N; ++i ){
cmdUpdateUniform(u[i]); // probably vkCmdUpdateBuffer
// some sychronization omitted
vkCmdDraw(obj[i]);
}
cmdbuff.end();
vkQueueSubmit( q, cmdbuff );
正如您所看到的那样,内存使用已经恢复(vkCmdUpdateBuffer
将所有制服存储在命令缓冲区中),如果OpenGL驱动程序希望具有高性能,则可能必须执行相同操作(尝试聚合所有绘制)到一个GPU提交)。
这种方法也存在一个小问题。所有vkCmdDraw
使用相同的uniform \ buffer内存,因此之前的vkCmdDraw
需要在更新之前完成使用该统一。允许驱动程序继续进行,并且不必同步vkCmdDraw
和随后的统一更新,可能会有好处。
你在网上看到的信息。一种方法是使用一系列制服并使用索引访问适当的制服。
另一种方法是通过pDynamicOffsets
绑定不同的描述符或vkCmdBindDescriptorSets
。
<小时/> 关于内存使用的注意事项:
4x4 sp矩阵是64 B.假设你让我们说1024个3D对象是64 kB。在这个时代,作为主要的GP GPU内存是微不足道的,即使是单一的纹理或其他资源,你也需要相形见绌。
如果您的内存使用率显着提高,问题可能出在其他地方。