Search code examples
macosmetalfragment-shader

How to pass a variable number of MTLTextures to a fragment shader?


What is the correct syntax for passing a variable number of MTLTexture's as an array to a fragment shader?

This StackOverflow Question: "How to use texture2d_array array in metal?" mentions the use of:

array<texture2d<half>, 5>

However, this requires specifying the size of the array. In Metal Shading Language Specification.pdf (Sec. 2.11) they also demonstrate this type. However, they also refer to array_ref but it's not clear to me how to use it, or if it's even allowed as a parameter type for a fragment shared given this statement:

"The array_ref type cannot be passed as an argument to graphics and kernel functions."

What I'm currently doing is just declaring the parameter as:

fragment float4 fragmentShader(RasterizerData in [[ stage_in ]],
                               sampler s [[ sampler(0) ]],
                               const array<texture2d<half>, 128> textures [[ texture(0) ]]) {

  const half4 c = textures[in.textureIndex].sample(s, in.coords);
}

Since the limit is 128 fragment textures. In any render pass, I might use between 1..n textures, where I ensure that n does not exceed 128. That seems to work for me, but am I Doing It Wrong?


My use-case is drawing a 2D plane that is sub-divided into a bunch of tiles. Each tile's content is sampled from a designated texture in the array based on a pre-computed texture index. The textures are set using setFragmentTexture:atIndex in the correct order at the start of the render pass. The texture index is passed from the vertex shader to the fragment shader.


Solution

  • You should consider an array texture instead of a texture array. That is, a texture whose type is MTLTextureType2DArray. You use the arrayLength property of the texture descriptor to specify how many 2-D slices the array texture contains.

    To populate the texture, you specify which slice you're writing to with methods such as -replaceRegion:... or -copyFrom{Buffer,Texture}:...toTexture:....

    In a shader, you can specify which element to sample or read from using the array parameter.