scalar
访问 GL_EXT_scalar_block_layout 中的存储缓冲区时,布局说明符有什么用?(例如见下文)
什么是用例scalar
?
我最近使用 Vulkan 和 NVidias VkRayTracing 扩展程序编写了一个简单的 Raytracer,并且正在学习本教程。在有关最近命中着色器的部分中,需要访问存储在井存储缓冲区(带有使用标志vk::BufferUsageFlagBits::eStorageBuffer
)中的一些数据。
在着色器中,GL_EXT_scalar_block_layout
使用了扩展,并像这样访问这些缓冲区:
layout(binding = 4, set = 1, scalar) buffer Vertices { Vertex v[]; } vertices[];
Run Code Online (Sandbox Code Playgroud)
当我第一次使用这段代码时,验证层告诉我结构体Vertex
的布局无效,所以我将它们更改为每个成员在 16 字节块上对齐:
struct Vertex {
vec4 position;
vec4 normal;
vec4 texCoord;
};
Run Code Online (Sandbox Code Playgroud)
使用 C++ 中的相应结构:
#pragma pack(push, 1)
struct Vertex {
glm::vec4 position_1unused;
glm::vec4 normal_1unused;
glm::vec4 texCoord_2unused;
};
#pragma pack(pop)
Run Code Online (Sandbox Code Playgroud)
错误消失了,我得到了一个可以工作的 Raytracer。但我仍然不明白为什么scalar
这里使用关键字。我发现这个文档在谈论 GL_EXT_scalar_block_layout-extension,但我真的不明白。可能我只是不习惯 glsl 术语?我看不出我必须使用它的任何理由。
此外,我只是试图删除它scalar
,但它仍然可以正常工作,没有任何区别、警告或错误。将不胜感激有关此主题的任何澄清或更多资源。
在std140
和std430
布局做了不少对象的偏移/比对尺寸的四舍五入。std140
基本上使任何非标量类型与 a 对齐到相同的对齐方式vec4
。std430
稍微放松了一点,但它仍然做了很多四舍五入到 avec4
的对齐。
scalar
布局基本上是指根据对象的组件标量来布局对象。任何聚合组件(向量、矩阵、数组和struct
s)的东西都不会影响布局。特别是:
所有类型的大小/对齐都只与它们实际使用的标量组件的最高对齐。因此,包含单个的结构的uint
大小/对齐方式与 a uint
: 4 字节的大小/对齐方式相同。根据std140
规则,它将具有 16 字节的大小和对齐方式。
请注意,这种布局使vec3
类似的类型实际上可行,因为 C 和 C++ 将能够创建映射到 GLSL 的对齐规则。
数组中元素的数组步长完全基于元素类型的大小/对齐方式,递归。所以数组uint
的数组步长为 4 个字节;根据std140
规则,它将有一个 16 字节的 stride。
对齐和填充仅对标量重要。如果你有一个包含一个结构uint
,然后一uvec2
,在std140/430
,这将需要16个字节,与第一后4个字节的填充uint
。在scalar
布局下,这样的结构只需要 12 个字节(并且对齐到 4 个字节),uvec2
并且概念上没有对齐。因此,仅当您有较小的标量时才存在填充,例如 auint16
后跟 a uint
。
在您展示的特定情况下,scalar
布局是不必要的,因为您使用的所有类型都是vec4
s。