Search code examples
metal

how to define functions with a referenced parameter in metal shader language except for vertex, fragment and kernel functions


When porting some basic OpenGL ES 2.0 shader to Metal shader, I don't know how to convert the in/inout/out qualifier in glsl to metal shader language(MSL). For example,

//OpenGL vertex shader

...

void getPositionAndNormal(out vec4 position, out vec3 normal, out vec3 tangent, out vec3 binormal)
{
    position = vec4(1.0, 0.0, 1.0, 1.0);
    normal = vec3(1.0, 0.0, 1.0);
    tangent = vec3(1.0, 0.0, 1.0);
    binormal = vec3(1.0, 0.0, 1.0);
}

void main()

{
    ...

    vec4 position;
    vec3 normal;
    vec3 tangent;
    vec3 binormal;
    getPositionAndNormal(position, normal, tangent, binormal);

    ...
}

when attempting convert to MSL, I try to use reference in the getPositionAndNormal parameters:

void getPositionAndNormal(float4 & position, float3 & normal, float3 & tangent, float3 & binormal);

I get the following compile error information: reference type must have explicit address space qualifier

According to section 4.2 in Metal-Shading-Language-Specification.pdf, I use device address space to reference the getPositionAndNormal parameters in the following forms:

void getPositionAndNormal(device float4 & position, device float3 & normal, device float3 & tangent, device float3 & binormal);

This time, the error message I got was no matching function for call to 'getPositionAndNormal'.

void getPositionAndNormal (
  device float4 & position,
  device float3 & normal,
  device float3 & tangent,
  device float3 & binormal)
{
  position = float4(1.0, 0.0, 1.0, 1.0);
  normal = float3(1.0, 0.0, 1.0);
  tangent = float3(1.0, 0.0, 1.0);
  binormal = float3(1.0, 0.0, 1.0);
}

vertex ShaderOutput vertexShader (ShaderInput inputMTL [[stage_in]], constant ShaderUniform& uniformMTL [[buffer(1)]])
{
  ...

  float3 binormal = 0;
  float3 tangent = 0;
  float3 normal = 0;
  float4 position = 0;
  getPositionAndNormal (position, normal, tangent, binormal);

  ...
}

What's the right 'metal' way to do this?


Solution

  • The local variables in vertexShader() are in the thread address space, not the device address space. Therefore, you should declare the parameters of getPositionAndNormal() to be of thread address space, too.

    The device address space are for things shared across the device; that is, across all "threads" or invocations of your shader. These variables are specific to each thread/invocation. They're not shared.

    I suspect the full "no matching function" compiler error would have explained the mismatch between the declared parameter and the actual passed argument, although possibly somewhat cryptically.