Vulkan 和旧版 API 的内存使用情况
Vulkan and older APIs memory usage
我是 vulkan 新手。问题在于转换对象。使用 DX11 和 OpenGL 时,我曾经更新统一缓冲区,然后将绘图命令发送到 gpu,但在 vulkan 中,所有命令都是预先录制的。所以我看不出有什么办法可以做到这一点。我在网上读到要转换每个对象,我可以在绘制时使用统一缓冲区数组和索引。这是在 vulkan 中转换每个对象的唯一方法吗?
如果是,那么 vulkan 不是比旧的 API 使用更多的内存吗?较旧的 APIs 可以有一个单一的统一缓冲区并在绘制调用之前更新它,但在 vulkan 中我们为每个对象使用一个缓冲区。 Vulkan 作为高性能 API 很受欢迎,但较旧的 API 使用的内存较少。
如果不是,我怎样才能更有效地做到这一点?
谢谢。
所以,假设 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
和后续的统一更新有潜在好处。
你在网上看到的信息里面有。一种方法是拥有一组制服并使用索引访问适当的制服。
另一种方法是通过 vkCmdBindDescriptorSets
.
绑定不同的描述符或 pDynamicOffsets
内存使用注意事项:
4x4 sp 矩阵是 64 B。假设您有 1024 个 3D 对象,即 64 kB。在这个时代,main\GP GPU 内存是微不足道的,即使是单个纹理或您需要的其他资源也会相形见绌。
如果您的内存使用量明显增加,则问题可能出在其他地方。
在像 OpenGL 这样的 high-level 图形 API 中,统一变量也位于 global/general 统一缓冲区中。为方便起见,它只是不向开发人员公开。但统一变量更新的执行方式与 Vulkan 中的类似 - 这是向统一缓冲区的正常数据传输。
现在,如果您想在绘制对象之前更新统一变量,您可以在 Vulkan 中执行完全相同的操作。有像 vkCmdUpdateBuffer() 或 vkCmdCopyBuffer() 这样的方法可以做到这一点。但是为什么开发人员不使用这种方法呢?由于同步而影响性能。在 OpenGL 中,这是由驱动程序自动完成的,但它具有与 Vulkan 中相同的效果。它只是没有暴露给开发人员。 Vulkan 表明,如果您正在考虑性能,这不是最佳方法。保留一组统一缓冲区(每个对象一个)或带有一组统一变量的单个统一缓冲区更好。您也可以为此目的使用推送常量。使用它们类似于旧的 OpenGL-like 更新存储在全局命名空间中的统一变量,但数据量有限(规范保证为 128 字节)。
我是 vulkan 新手。问题在于转换对象。使用 DX11 和 OpenGL 时,我曾经更新统一缓冲区,然后将绘图命令发送到 gpu,但在 vulkan 中,所有命令都是预先录制的。所以我看不出有什么办法可以做到这一点。我在网上读到要转换每个对象,我可以在绘制时使用统一缓冲区数组和索引。这是在 vulkan 中转换每个对象的唯一方法吗?
如果是,那么 vulkan 不是比旧的 API 使用更多的内存吗?较旧的 APIs 可以有一个单一的统一缓冲区并在绘制调用之前更新它,但在 vulkan 中我们为每个对象使用一个缓冲区。 Vulkan 作为高性能 API 很受欢迎,但较旧的 API 使用的内存较少。
如果不是,我怎样才能更有效地做到这一点? 谢谢。
所以,假设 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
和后续的统一更新有潜在好处。
你在网上看到的信息里面有。一种方法是拥有一组制服并使用索引访问适当的制服。
另一种方法是通过 vkCmdBindDescriptorSets
.
pDynamicOffsets
内存使用注意事项:
4x4 sp 矩阵是 64 B。假设您有 1024 个 3D 对象,即 64 kB。在这个时代,main\GP GPU 内存是微不足道的,即使是单个纹理或您需要的其他资源也会相形见绌。
如果您的内存使用量明显增加,则问题可能出在其他地方。
在像 OpenGL 这样的 high-level 图形 API 中,统一变量也位于 global/general 统一缓冲区中。为方便起见,它只是不向开发人员公开。但统一变量更新的执行方式与 Vulkan 中的类似 - 这是向统一缓冲区的正常数据传输。
现在,如果您想在绘制对象之前更新统一变量,您可以在 Vulkan 中执行完全相同的操作。有像 vkCmdUpdateBuffer() 或 vkCmdCopyBuffer() 这样的方法可以做到这一点。但是为什么开发人员不使用这种方法呢?由于同步而影响性能。在 OpenGL 中,这是由驱动程序自动完成的,但它具有与 Vulkan 中相同的效果。它只是没有暴露给开发人员。 Vulkan 表明,如果您正在考虑性能,这不是最佳方法。保留一组统一缓冲区(每个对象一个)或带有一组统一变量的单个统一缓冲区更好。您也可以为此目的使用推送常量。使用它们类似于旧的 OpenGL-like 更新存储在全局命名空间中的统一变量,但数据量有限(规范保证为 128 字节)。