通过在同一个着色器中多次绑定 SSBO 来别名
Aliasing a SSBO by binding it multiple times in the same shader
玩无绑定渲染,我有一个大型静态 SSBO 来保存我的顶点数据。顶点作为连续数组打包在内存中,其中每个顶点具有以下布局:
| Position (floats) | Normal (snorm shorts) | Pad |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| P.x | P.y | P.z | N.x | N.y | N.z | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| float | float | float | uint | uint |
请注意每个顶点是 20 字节/5 个“字”/1.25 vec4
s。不完全是 GPU 的整数。因此,我没有做一堆填充和使用不必要的内存,而是选择“手动”解压数据。
顶点着色器:
...
layout(std430, set = 0, binding = 1)
readonly buffer FloatStaticBuffer
{
float staticBufferFloats[];
};
layout(std430, set = 0, binding = 1) // Using the same binding?!
readonly buffer UintStaticBuffer
{
uint staticBufferUInts[];
};
...
void main()
{
const uint vertexBaseDataI = gl_VertexIndex * 5u;
// Unpack position
const vec3 position = vec3(
staticBufferFloats[vertexBaseDataI + 0u],
staticBufferFloats[vertexBaseDataI + 1u],
staticBufferFloats[vertexBaseDataI + 2u]);
// Unpack normal
const vec3 normal = vec3(
unpackSnorm2x16(staticBufferUInts[vertexBaseDataI + 3u]),
unpackSnorm2x16(staticBufferUInts[vertexBaseDataI + 4u]).x);
...
}
能够将缓冲区“别名”为 float
和 uint
数据非常方便。
问题: 以这种方式“别名”SSBO 是一个糟糕的主意,我只是走运,或者这实际上是一个跨平台工作的有效选项?
备选方案:
- 只使用一个缓冲区,比如
staticBufferUInts
,然后使用uintBitsToFloat
提取位置。没什么大不了的,但性能成本可能很小?
- 将 CPU 上的同一缓冲区两次绑定到两个不同的绑定。同样,没什么大不了的,只是有点烦人。
只要没有从中读取格式错误的值,Vulkan 就允许不兼容的资源在内存中使用别名。 (实际上,我认为即使您从无效部分阅读也是允许的 - 您应该只是得到垃圾。但我现在找不到标准的部分说明这一点。Vulkan 标准太复杂了。)
来自标准的“内存别名”部分:
Otherwise, the aliases interpret the contents of the memory
differently, and writes via one alias make the contents of memory
partially or completely undefined to the other alias. If the first alias is a host-accessible subresource, then the bytes affected are those written by the memory operations according to its addressing scheme. If the first alias is not host-accessible, then the bytes
affected are those overlapped by the image subresources that were
written. If the second alias is a host-accessible subresource, the
affected bytes become undefined. If the second alias is not
host-accessible, all sparse image blocks (for sparse
partially-resident images) or all image subresources (for non-sparse
image and fully resident sparse images) that overlap the affected
bytes become undefined.
请注意,该标准谈到 字节 被写入并在别名资源中变得未定义。不是整个资源都失效了。
让我们这样看:您有两个不同类型(float、short int)的别名 SSBO(实际上只有一个绑定了两次)。您写入浮动的任何字节在您写入缓冲区的那一刻在“浮动视图”中变得有效并且在“int 视图”中无效。 int也一样:它们占用的字节在int视图中变为有效,在float视图中变为无效。根据标准,这意味着两个视图中都有无效部分;但是,它们都不是完全无效的。特别是,您关心的部分仍然有效,可以阅读。
简而言之:允许。
玩无绑定渲染,我有一个大型静态 SSBO 来保存我的顶点数据。顶点作为连续数组打包在内存中,其中每个顶点具有以下布局:
| Position (floats) | Normal (snorm shorts) | Pad |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| P.x | P.y | P.z | N.x | N.y | N.z | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| float | float | float | uint | uint |
请注意每个顶点是 20 字节/5 个“字”/1.25 vec4
s。不完全是 GPU 的整数。因此,我没有做一堆填充和使用不必要的内存,而是选择“手动”解压数据。
顶点着色器:
...
layout(std430, set = 0, binding = 1)
readonly buffer FloatStaticBuffer
{
float staticBufferFloats[];
};
layout(std430, set = 0, binding = 1) // Using the same binding?!
readonly buffer UintStaticBuffer
{
uint staticBufferUInts[];
};
...
void main()
{
const uint vertexBaseDataI = gl_VertexIndex * 5u;
// Unpack position
const vec3 position = vec3(
staticBufferFloats[vertexBaseDataI + 0u],
staticBufferFloats[vertexBaseDataI + 1u],
staticBufferFloats[vertexBaseDataI + 2u]);
// Unpack normal
const vec3 normal = vec3(
unpackSnorm2x16(staticBufferUInts[vertexBaseDataI + 3u]),
unpackSnorm2x16(staticBufferUInts[vertexBaseDataI + 4u]).x);
...
}
能够将缓冲区“别名”为 float
和 uint
数据非常方便。
问题: 以这种方式“别名”SSBO 是一个糟糕的主意,我只是走运,或者这实际上是一个跨平台工作的有效选项?
备选方案:
- 只使用一个缓冲区,比如
staticBufferUInts
,然后使用uintBitsToFloat
提取位置。没什么大不了的,但性能成本可能很小? - 将 CPU 上的同一缓冲区两次绑定到两个不同的绑定。同样,没什么大不了的,只是有点烦人。
只要没有从中读取格式错误的值,Vulkan 就允许不兼容的资源在内存中使用别名。 (实际上,我认为即使您从无效部分阅读也是允许的 - 您应该只是得到垃圾。但我现在找不到标准的部分说明这一点。Vulkan 标准太复杂了。)
来自标准的“内存别名”部分:
Otherwise, the aliases interpret the contents of the memory differently, and writes via one alias make the contents of memory partially or completely undefined to the other alias. If the first alias is a host-accessible subresource, then the bytes affected are those written by the memory operations according to its addressing scheme. If the first alias is not host-accessible, then the bytes affected are those overlapped by the image subresources that were written. If the second alias is a host-accessible subresource, the affected bytes become undefined. If the second alias is not host-accessible, all sparse image blocks (for sparse partially-resident images) or all image subresources (for non-sparse image and fully resident sparse images) that overlap the affected bytes become undefined.
请注意,该标准谈到 字节 被写入并在别名资源中变得未定义。不是整个资源都失效了。
让我们这样看:您有两个不同类型(float、short int)的别名 SSBO(实际上只有一个绑定了两次)。您写入浮动的任何字节在您写入缓冲区的那一刻在“浮动视图”中变得有效并且在“int 视图”中无效。 int也一样:它们占用的字节在int视图中变为有效,在float视图中变为无效。根据标准,这意味着两个视图中都有无效部分;但是,它们都不是完全无效的。特别是,您关心的部分仍然有效,可以阅读。
简而言之:允许。