Fragment shader parameter with attribute "color" for macOS

I am trying to pass a simple example of an iOS app on macOS. Metalist compiler says

Fragment shader parameter with attribute 'color' is supported only on iOS (requires -std=ios-metal1.[0|1|2]).

      

How to overcome this error? Where can I find a document that outlines the distinctive features of API APIs between macOS and iOS?

Here is an Xcode project with a target macOS

. The above error is thrown Light.metal

.

macOS 10.13 Intel i5-5257u Iris 6100

+3


source to share


1 answer


In the future, show which particular file from this sample application generates the error and the exact string. Or, since you are porting (modifying) the example, show the shader code snippet from your project. Also indicate which version of macOS you are using.

In theory, this PDF should show the differences between platforms. It may not be obvious, but I believe these functions fall under the "Programmable Blend" in this table. Basically, getting an existing color from render targets is mostly useful to use the original value when calculating a new value, i.e. Perform a custom blend of the new values ​​with the old color.

The metallic shade spec says that in section 4.3.4.4 the attribute attributes for a fragment function input:

The attribute attribute [[color(m)]]

is only supported on iOS.

If the shader fails https://developer.apple.com/library/content/samplecode/MetalDeferredLighting/Listings/MetalDeferredLighting_Light_metal.html , then it really only outputs to the render target light

(Appendix 3), The rest it just saves (and reads as initial data for their calculations). For a texture, light

it (efficiently) reads and writes to it.



So, on macOS 10.13+, you can use the texture read and write ability, depending on the light texture pixel format and GPU capabilities. You must make the visa pass for this part differently than on iOS. You wouldn't specify any render targets. The fragment shader will return void

. All of its output would be written in texture. Instead of using textures as render objects (color nesting), you should pass them through a texture table. You wouldn't use a type FragOutput

with its fields [[color(n)]]

, you got textures as inputs using an attribute [[texture(n)]]

. You would need to read()

(not sample()

) from textures at fragment position ( uint2(in.position)

).

So something like:

fragment void lightFrag(VertexOutput in [[stage_in]],
                        constant LightFragmentInputs *lightData [[buffer(0)]],
                        texture2d<float, access::read> normal [[texture(0)]],
                        depth2d<float, access::read> depth [[texture(1)]],
                        texture2d<float, access::read_write> light_tex [[texture(2), raster_order_group(0)]])
{
    uint2 pos = uint2(in.position);
    float3 n_s = normal.read(pos).rgb;

    float scene_z = depth.read(pos);

    float3 n = n_s * 2.0 - 1.0;

    // Derive the view-space position of the scene fragment in the G-buffer
    // Since the light primitive and the G-buffer were rendered with the same view-projection matrix,
    // we can treat the view-space position of the current light primitive fragment as a ray from the origin,
    // and derive the view-space position of the scene by projecting along the ray with (scene_z / v_view.z).
    // Our scene view-space position is also the view-vector to the scene fragment.
    float3 v = in.v_view * (scene_z / in.v_view.z);

    // Now, we have everything we need to calculate our view-space lighting vectors.
    float3 l = (lightData->view_light_position.xyz - v);
    float n_ls = dot(n, n);
    float v_ls = dot(v, v);
    float l_ls = dot(l, l);
    float3 h = (l * rsqrt(l_ls / v_ls) - v);
    float h_ls = dot(h, h);
    float nl = dot(n, l) * rsqrt(n_ls * l_ls);
    float nh = dot(n, h) * rsqrt(n_ls * h_ls);
    float d_atten = sqrt(l_ls);
    float atten = fmax(1.0 - d_atten / lightData->light_color_radius.w, 0.0);
    float diffuse = fmax(nl, 0.0) * atten;

    float4 light = light_tex.read(pos);
    light.rgb += lightData->light_color_radius.xyz * diffuse;
    light.a += pow(fmax(nh, 0.0), 32.0) * step(0.0, nl) * atten * 1.0001;
    light_tex.write(light, pos);
}    

      

If you cannot read and write a texture, you probably have to use two textures. You would pass the old light texture as a read-only parameter, and the fragment shader would return the new value (as a return type float4

) to the new light texture. You would use this new light texture for the rest of the frame rendering, where the original code used the old light texture, or you would copy the result into the new light texture back to the old one.

+3


source







All Articles