如何将可变数量的纹理绑定到金属着色器?

How to bind a variable number of textures to Metal shader?

在 CPU 上,我正在收集要发送到片段着色器的 MTLTexture 对象数组。在任何给定时刻可以有任意数量的这些纹理。如何将 MTLTexture 的可变长度数组发送到片段着色器?

例子。) CPU:

var txrs: [MTLTexture] = []
for ... {
   txrs.append(...)
}
// Send array of textures to fragment shader.

显卡:

fragment half4 my_fragment(Vertex v [[stage_in]], <array of textures>, ...) {
   ...
   for(int i = 0; i < num_textures; i++) {
      texture2d<half> txr = array_of_textures[i];
   }
   ...
}

其他人建议的array是行不通的,因为贴图会占用所有的绑定点,最多31个,到那个时候就会运行出来。

相反,您需要使用参数缓冲区。

因此,要使其正常工作,您需要第 2 层参数缓冲区支持。您可以在 MTLDevice.

上使用 argumentBuffersSupport 属性 检查它

您可以阅读有关参数缓冲区的更多信息 here or watch this talk 关于无绑定渲染。

基本思想是使用 MTLArgumentEncoder 对参数缓冲区中所需的纹理进行编码。不幸的是,我不认为有直接的方法来编码一堆 MTLTextures,所以相反,你会在你的着色器中创建一个结构,就像这样

struct SingleTexture
{
    texture2d<half> texture;
};

此结构中的 texture 隐式 id 为 0。要了解有关 id 的更多信息,请阅读 the spec 中的参数缓冲区部分,但它基本上是一个ab 中每个条目的唯一索引。

然后,将您的函数签名更改为

fragment half4 my_fragment(Vertex v [[stage_in]], device ushort& textureCount [[ buffer(0), device SingleTexture* textures [[ buffer(1) ]]) 

然后您需要绑定计数(在大多数情况下使用 uint16_t 而不是 uint32_t)。就像一个 2(或 4)字节的缓冲区。 (您可以为此在编码器上使用 set<...>Bytes 函数)。

然后,您需要将该函数编译为 MTLFunction,然后您可以使用 newArgumentEncoderForBufferAtIndex 方法从中创建一个 MTLArgumentEncoder。在这种情况下,您将使用缓冲区索引 1,因为这是您的 AB 在函数中绑定的位置。

MTLArgumentEncoder 可以得到 encodedLength,这基本上是 AB 中一个 SingleTexture 结构的大小。在你得到它之后,将它乘以纹理数量以获得适当大小的缓冲区来将你的参数缓冲区编码到。

之后,在你的设置代码中,你可以这样做

for(size_t i = 0; i < textureCount; i++)
{
    // We basically just offset into an array of SignlaTexture
    [argumentEncoder setArgumentBuffer:<your buffer you just created> offset:argumentEncoder.encodedLength * i];
    [argumentEncoder setTexture:textures[i] atIndex:0];
}

然后,当您完成对缓冲区的编码后,您可以保留它,直到您的纹理数组发生变化(您不需要每帧都重新编码)。

然后,您需要将参数缓冲区绑定到缓冲区绑定点 1,就像绑定任何其他缓冲区一样。

您需要做的最后一件事是确保间接引用的所有资源都驻留在 GPU 上。由于您将纹理编码为 AB,驱动程序无法知道您是否使用它们,因为您没有直接绑定它们。

为此,请在您使用的编码器上使用 useResourceuseResources 变体,有点像这样:

[encoder useResources:&textures[0] count:textureCount usage:MTLResourceUsageRead];

这有点啰嗦,但这是将您想要的任何内容绑定到着色器的正确方法。