为了减少OpenGL API调用并将数据存储在连续内存中,我正在考虑为应上传到GPU的每个数据结构创建多个大型缓冲区。每个缓冲区的最大大小为16kb(根据我的理解,这保证可以用于UBO)。当对象希望能够将制服上传到GPU时,它会获取待上载类型的第一个尚未满的缓冲区,并在该缓冲区中获取下一个可用索引。绘制对象时,它将绑定UBO(如果尚未绑定)并上传UBO的元素索引。
这将导致如下所示:
layout(std140) uniform ModelData {
mat4 model_matrix[kNumInstancesPerModelUbo];
}
uniform int u_ModelDataIndex;
layout(std140) uniform SkeletonData {
mat4 bone_transforms[kNumInstancesPerSkeletonUbo][kMaxBones];
}
uniform int u_SkeletonDataIndex;
,但我还在考虑以下内容:与要上传的网格相关的数据。另一方面,这可能会失控(缓冲区大小大于16kb,处理不相关的数据(例如,无骨架的网格),甚至出现同步问题,因为在上传模型矩阵时不允许访问骨骼)而且我也不知道这会如何影响GPU上的内存布局。
坦率地说,感觉就像我被困在这里,我找不到一个很好的具体示例来说明如何着手快速而灵活地处理UBO。
您对我有什么建议或资源可以帮助我吗?
#1 楼
注意,从更大的缓冲区进行子分配绝对是必经之路。我从DirectX / Vulkan的角度来看更多,但这应该同样适用于OpenGL(在此答案中,我只是没有直接的API调用)。需要考虑的事情如下:是否需要索引到较大的缓冲区中,还是可以每次将资源绑定到偏移量吗?
是否已请注意打包在一起的制服的所有对齐限制(常见的对齐方式是256字节对齐)?间接访问缓冲区子区域的快速方法。但是,假设您是可变的三重缓冲数据,则在驱动程序中应该几乎没有争用来绑定数据(只是一些固定的开销)。内存/缓冲区并分配这些区域被认为是最佳实践。这甚至适用于具有不同着色器的对象(如果您的分配器可以处理)。
#2 楼
在您的应用程序中包括两个解决方案的基准测试阶段,然后在运行时选择胜出的解决方案。这是简单,可移植的,并且是面向未来的。我的意思是,您确实对此进行了测试,对吗? ;-)我知道这是实现高性能的“最佳实践”的相当通用的答案,但是如果您想到它,则有成千上万种可能的目标配置,许多供应商也需要考虑。如果您需要一点额外费用,请向您的供应商付款,以针对您的应用程序优化驱动程序。