用 "const constant" 声明变量的目的是什么?

what the purpose of declaring a variable with "const constant"?

在 Metal 着色器中,声明像 const constant Vertex *vertexArray [[buffer(0)]] 这样的变量的目的是什么(我的意思是 const constant)?为什么只有常数是不够的?另外, constantconst 有什么区别?

同样的道理,const deviceconstant有什么区别?

const 类型限定符 constantdevice地址spaces.

const 阻止您修改它适用的事物:

int a = 15;
a = 16; // Fine; reassigning to a non-const variable

const int b = 15;
b = a; // Error: attempt to write to a read-only constant

int d = 18;
int const* c = &a;
*c = 17; // Error: attempt to write value through a pointer to const int
c = &d;  // Fine; reassigning (a different address) to a (non-const) pointer

int *const e = &d;
*e = a; // Fine; assigning to pointee through pointer to non-const int
e = c;  // Error: attempt to reassign const pointer

希望这些示例能够充分说明变量和常量的语义,以及规则在指针情况下的工作原理。

在 Metal 中,指针总是位于特定地址 space。如果您在 Metal 着色器函数中使用自动存储的变量地址(即 "local" 变量),该指针位于 thread 地址 space 中。另一方面,缓冲区参数始终位于 constantdevice 地址 space.

device 缓冲区用于保存其元素将被粗略访问一次的内存,就像您在顶点函数中顺序获取顶点数据时可能会做的那样。另一方面,constant 缓冲区保存的数据可能会被函数的多次调用访问,就像统一数据一样。

您不能写入 constant 地址 space 中的缓冲区。这是这个答案中最重要的一句话:constant 地址 space 中的所有指针都隐含地限定了 const。

你可以在常量地址space中形成新的指针,并根据上面解释的规则,你可以重新分配它们。但是尝试写入他们的指针会产生编译器错误。

假设您使用以下参数编写片段函数:

constant Light *lights [[buffer(0)]]

然后在函数体中你可以这样写:

constant Light *light = &lights[0];

还有这个:

light = &lights[1];

但不是这个:

light->color = float4(1, 1, 1, 1); // Error: attempt to write to variable with const-qualified type "const constant Light *"

再次注意,在最后一个示例中,即使我们没有说常量指针应该是指向常量的指针,但它确实是。因此,用 const(在星号之前)进一步限定 constant 指针是多余的。

现在让我们谈谈 device 指针。

与始终只读的 constant 缓冲区相反,在许多情况下可以写入 device 缓冲区。但是,您经常将设备缓冲区视为只读(例如,在大多数顶点函数中)。要向编译器指示此意图,您可以将 const 添加到设备缓冲区指针参数。这将防止您无意中写入您只打算读取的缓冲区。如果您在不适当的上下文中使用指向非常量类型的 device 指针,Metal 着色器编译器的最新版本会发出警告,这就是为什么养成编写 const device 的习惯通常是个好主意的原因对于这样的参数。

但是写 const constant 是多余的,从来没有必要。