KhronosGroup / MoltenVK

MoltenVK is a Vulkan Portability implementation. It layers a subset of the high-performance, industry-standard Vulkan graphics and compute API over Apple's Metal graphics framework, enabling Vulkan applications to run on macOS, iOS and tvOS.
Apache License 2.0
4.86k stars 429 forks source link

Bindless textures not always work with 1.2.10 on mac #2300

Open AlexanderDevaikinEnscape opened 3 months ago

AlexanderDevaikinEnscape commented 3 months ago

Hello. Thank you so much for the recent fixes to argument buffers and especially argument buffers for iOS support! Unfortunately I still have some issues with bindless textures which prevents me from upgrading from version 1.2.4 which was the last one I could use.

Short version: In some renderer passes sampling bindless textures returns 0s with 1.2.10 where it has worked just fine with MoltenVK 1.2.4. macOS 14.5, Macbook 1m Max

Long version: I have an array of textures (the one with update-after-bind flag), and an array of materials where texture index is an index in that array of textures for every material. So, in some cases sampling a texture returns only 0s - e.g. in shadow map pass I get 0s, while in GBuffer pass I get valid values for the same mask texture.

What is different in 1.2.10 compared to 1.2.4 is that in Xcode shader debugger, array of textures is shown to have only one item, while with version 1.2.4 it shows it's complete size with valid textures in all the used indices. It's spvDescriptorSet1 on the screenshot:

Screenshot 2024-08-05 at 15 23 29

Textures addressed by the indices > 0 in this shader return only 0s while sampling. If I hardcode index 0, sampling returns expected values for the texture with index 0. But this happens only in some render passes, although they share the same code for binding resources and binding declaration in shaders with the other render passes that work. Shader debugger shows only one element in array of textures in working passes too though, and it also shows 0s returned by sampling but in the end values are correct. So I don't trust Xcode shader debugger any more.

Interesting is that Xcode capture memory viewer displays the array with textures as expected - they are all present there in 1.2.10. But not in 1.2.4 - although all render passes work, memory viewer shows only a couple on nulls in the array.

Summary:

  1. Argument buffer with textures has only 1 element in shader debugger
  2. This argument buffer has correct contents with 62 textures in memory view
  3. Some render passes access data with index > 0 from that argument buffer work, some not
  4. The ones that don't work still can sample texture at index 0 if hardcoded
  5. Both working and broken render passes share the same code for render pass bindings setup and the same shader code for resources access

Help. I don't have a repro case code I can publish unfortunately.

AlexanderDevaikinEnscape commented 3 months ago

Had yet another try to find out what is going on and here is an interesting point: spvDescriptorSetBuffer1 which holds textures has type array<texture2d<float>, 1> in shader while spvDescriptorSetBuffer1 is much larger in reality. Changing type in shader debugger to e.g. array<texture2d<float>, 30> also fixes the sampling from texture with index > 0, as expected.

Shader:

Screenshot 2024-08-16 at 11 10 35

Memory:

Screenshot 2024-08-16 at 11 11 28
billhollings commented 3 months ago

The restriction to an array length of 1 is generally because the descriptor array is defined as a runtime length array. Since the array length is not truly known at pipeline (and shader) compilation time, we define it to be an array of length 1. Metal happily indexes beyond that length at runtime.

I am not able to replicate this using the Vulkan Samples descriptor_indexing sample, which has a similar large array of textures, the length of which is set at runtime:

[mvk-debug] Created VkDescriptorSetLayout 0x6000034aca80 with 1 bindings:
    0: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE              with up to 1000000 elements at binding 0

[mvk-debug] Created VkDescriptorPool 0x122051a00 with 2 descriptor sets, and pooled descriptors:
    VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:            2112  (2112 remaining)

----------------

         %10 = OpTypeImage %6 2D 0 0 0 1 Unknown
         %11 = OpTypeRuntimeArray %10
         %12 = OpTypePointer UniformConstant %11
         %13 = OpVariable %12 UniformConstant

----------------

struct spvDescriptorSetBuffer0
{
    array<texture2d<float>, 1> Textures [[id(0)]];
};

struct spvDescriptorSetBuffer1
{
    sampler ImmutableSampler [[id(0)]];
};

struct main0_out
{
    float4 out_frag_color [[color(0)]];
};

struct main0_in
{
    float2 in_uv [[user(locn0)]];
    int in_texture_index [[user(locn1)]];
};

fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]])
{
    main0_out out = {};
    out.out_frag_color = spvDescriptorSet0.Textures[in.in_texture_index].sample(spvDescriptorSet1.ImmutableSampler, in.in_uv);
    return out;
}

It would help to see your descriptor definitions, and shader conversions. Can you run your environment with the following environment variables, and post the sections of the log showing the descriptor definitions (as above), and the SPIR-V, MSL, and estimated GLSL for the shader under concern?

MVK_CONFIG_DEBUG=1
MVK_CONFIG_LOG_LEVEL=4

Since you are not able to generate a repo case, can you try to modify the Vulkan Samples descriptor_indexing sample, to cause it to trigger the same problem, and post the modifications here, so we can replicate it?

AlexanderDevaikinEnscape commented 3 months ago

Thank you for the reply. I have tried to reproduce it with descriptor_indexing sample without luck :/ Happens only in our shadowmap pass. Array size of 1 is indeed OK, it is also the same in other shaders that do work correctly.

Info collected using debug levels. The problematic shader:

[mvk-debug] Created VkDescriptorSetLayout 0x600003d39b00 with 1 bindings:
    0: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE              with up to 65536 elements at binding 1

[mvk-debug] Created VkDescriptorSetLayout 0x600003d39a40 with 6 bindings:
    0: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER             with 1 elements at binding 0
    1: VK_DESCRIPTOR_TYPE_SAMPLER                    with 1 elements at binding 1
    2: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER             with 1 elements at binding 2
    3: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER             with 1 elements at binding 3
    4: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER             with 1 elements at binding 4
    5: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER             with 1 elements at binding 5

[mvk-debug] Created VkPipelineLayout 0x129f26fb0 with 2 descriptor set layouts:
    0: 0x600003d39a40
    1: 0x600003d39b00

---------------------

%31 = OpTypeImage %13 2D 0 0 0 1 Unknown
%32 = OpTypeRuntimeArray %31
%33 = OpTypePointer UniformConstant %32
%34 = OpVariable %33 UniformConstant
%37 = OpTypePointer UniformConstant %31
%40 = OpTypeSampler
%41 = OpTypePointer UniformConstant %40
%42 = OpVariable %41 UniformConstant
%44 = OpTypeSampledImage %31

--------------------

struct spvDescriptorSetBuffer0
{
    constant void* _m0_pad [[id(0)]];
    constant void* _m1_pad [[id(1)]];
    sampler SceneTextureSampler [[id(2)]];
};

struct spvDescriptorSetBuffer1
{
    array<texture2d<float>, 1> SceneTextures [[id(0)]];
};

static inline __attribute__((always_inline))
float4 getTextureLodValue(thread const uint& samplerId, thread const float2& uv, thread const float& lod, constant array<texture2d<float>, 1>& SceneTextures, sampler SceneTextureSampler)
{
    uint _36 = samplerId;
    return SceneTextures[_36].sample(SceneTextureSampler, uv, level(lod));
}

static inline __attribute__((always_inline))
void opaqueMaskedDoubleSided(constant array<texture2d<float>, 1>& SceneTextures, sampler SceneTextureSampler, thread VertexData& inoutData)
{
    uint param = inoutData.maskSamplerId;
    bool _61 = isValidSamplerId(param);
    bool _79;
    if (_61)
    {
        uint param_1 = inoutData.maskSamplerId;
        float2 param_2 = inoutData.uv;
        float param_3 = 0.0;
        _79 = getTextureLodValue(param_1, param_2, param_3, SceneTextures, SceneTextureSampler).x <= 0.5;
    }
    else
    {
        _79 = _61;
    }
    if (_79)
    {
        discard_fragment();
    }
}

fragment void main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]])
{
    VertexData inoutData = {};
    inoutData.uv = in.inoutData_uv;
    inoutData.maskSamplerId = in.inoutData_maskSamplerId;
    inoutData.vViewPos = in.inoutData_vViewPos;
    opaqueMaskedDoubleSided(spvDescriptorSet1.SceneTextures, spvDescriptorSet0.SceneTextureSampler, inoutData);
}

And the working shader that uses the same texture array bound using the same descriptor set layout:

[mvk-debug] Created VkDescriptorSetLayout 0x600003d39b00 with 1 bindings:
    0: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE              with up to 65536 elements at binding 1

[mvk-debug] Created VkDescriptorSetLayout 0x600003d46640 with 7 bindings:
    0: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER             with 1 elements at binding 0
    1: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER             with 1 elements at binding 2
    2: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER             with 1 elements at binding 3
    3: VK_DESCRIPTOR_TYPE_SAMPLER                    with 1 elements at binding 4
    4: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER             with 1 elements at binding 5
    5: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER             with 1 elements at binding 6
    6: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER             with 1 elements at binding 7

[mvk-debug] Created VkPipelineLayout 0x109f77380 with 2 descriptor set layouts:
    0: 0x600003d46640
    1: 0x600003d39b00

------------------

        %853 = OpTypeImage %6 2D 0 0 0 1 Unknown
        %854 = OpTypeRuntimeArray %853
        %855 = OpTypePointer UniformConstant %854
        %856 = OpVariable %855 UniformConstant
        %859 = OpTypePointer UniformConstant %853
        %862 = OpTypeSampler
        %863 = OpTypePointer UniformConstant %862
        %864 = OpVariable %863 UniformConstant
        %866 = OpTypeSampledImage %853

------------------

struct spvDescriptorSetBuffer0
{
    constant void* _m0_pad [[id(0)]];
    constant CameraDataUbo* m_1254 [[id(1)]];
    constant GBufferParams* m_883 [[id(2)]];
    const device TextureTransformsBuffer* m_716 [[id(3)]];
    sampler SceneTextureSampler [[id(4)]];
};

struct spvDescriptorSetBuffer1
{
    array<texture2d<float>, 1> SceneTextures [[id(0)]];
};

static inline __attribute__((always_inline))
float4 getTextureLodValue(thread const uint& samplerId, thread const float2& uv, thread const float& lod, constant array<texture2d<float>, 1>& SceneTextures, sampler SceneTextureSampler)
{
    uint _858 = samplerId;
    return SceneTextures[_858].sample(SceneTextureSampler, uv, level(lod));
}

static inline __attribute__((always_inline))
float getMaskTextureValue(thread const float2& baseUvs, thread const uint& maskSamplerId, thread const MaterialIndexTransformFlag& matIndexTransformFlag, const device TextureTransformsBuffer& _716, constant array<texture2d<float>, 1>& SceneTextures, sampler SceneTextureSampler)
{
    ...
    return getTextureLodValue(param_3, param_4, param_5, SceneTextures, SceneTextureSampler).x;
}

static inline __attribute__((always_inline))
bool isMaskedOut(thread const float2& baseUvs, thread const uint& maskId, thread const MaterialIndexTransformFlag& matIndexTransformFlag, thread const float& visibility, thread const float& maskThreshold, thread const float& NoV, const device TextureTransformsBuffer& _716, constant array<texture2d<float>, 1>& SceneTextures, sampler SceneTextureSampler)
{
    float maskVisibility = 1.0;
    uint param = maskId;
    if (isValidSamplerId(param))
    {
        float2 param_1 = baseUvs;
        uint param_2 = maskId;
        MaterialIndexTransformFlag param_3 = matIndexTransformFlag;
        float param_4 = getMaskTextureValue(param_1, param_2, param_3, _716, SceneTextures, SceneTextureSampler);
        float param_5 = NoV;
        maskVisibility = geometricOpacity(param_4, param_5);
    }
    return (visibility * maskVisibility) < (maskThreshold * 0.800000011920928955078125);
}

fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], bool gl_FrontFacing [[front_facing]], float4 gl_FragCoord [[position]])
{
    ...
    bool discardFragment = isMaskedOut(param_8, param_9, param_10, param_11, param_12, param_13, (*spvDescriptorSet0.m_716), spvDescriptorSet1.SceneTextures, spvDescriptorSet0.SceneTextureSampler) || geometricFragmentDiscard;
    ...
    opaqueShading(...);
    ...
    if (discardFragment)
    {
        discard_fragment();
    }
    return out;
}
AlexanderDevaikinEnscape commented 3 months ago

I don't see any significant difference between working and not-working shaders. Also dumped shaders using version 1.2.6 which we currently use in production where issue is not present. The main difference compared to 1.2.10 is that with 1.2.6 texture array is defined with the size of 65536:

         %40 = OpTypeImage %6 2D 0 0 0 1 Unknown
         %41 = OpTypeRuntimeArray %40
         %42 = OpTypePointer UniformConstant %41
         %43 = OpVariable %42 UniformConstant
         %46 = OpTypePointer UniformConstant %40
         %49 = OpTypeSampler
         %50 = OpTypePointer UniformConstant %49
         %51 = OpVariable %50 UniformConstant
         %53 = OpTypeSampledImage %40

-------------------

struct spvDescriptorSetBuffer0
{
    const device TextureTransformsBuffer* m_196 [[id(0)]];
    sampler SceneTextureSampler [[id(1)]];
    const device TextureHandles* m_201 [[id(2)]];
    constant ShadowMapParamsBuffer* m_105 [[id(3)]];
    const device InstanceDataBuffer* m_207 [[id(4)]];
    const device MaterialsBuffer* m_213 [[id(5)]];
};

struct spvDescriptorSetBuffer1
{
    array<texture2d<float>, 65536> SceneTextures [[id(0)]];
};

static inline __attribute__((always_inline))
float4 getTextureLodValue(thread const uint& samplerId, thread const float2& uv, thread const float& lod, constant array<texture2d<float>, 65536>& SceneTextures, sampler SceneTextureSampler)
{
    uint _45 = samplerId;
    return SceneTextures[_45].sample(SceneTextureSampler, uv, level(lod));
}
billhollings commented 2 months ago
struct spvDescriptorSetBuffer1
{
    array<texture2d<float>, 65536> SceneTextures [[id(0)]];
};

Not that it likely matters for the posted issue, but when running 1.2.6, what do you define the length of the runtime array to be? Metal validation typically complains when a large array is indicated by the shader, but a smaller argument buffer is passed to it.

It is probable that MoltenVK 1.2.6 was passing an argument buffer large enough to hold 65536 textures, which would avoid the Metal validation error. This has since been changed, because it means a descriptor set & descriptor pool really can't be dynamically sized.

AlexanderDevaikinEnscape commented 2 months ago

In the end the array has the size equal to the number of actually used textures, which is much less than 65536. No Metal validation errors - neither with 1.2.6, nor with 1.2.10.

Fun fact: when I enable Shader Validation in Xcode, shadow map works in 1.2.10 = mask textures are sampled correctly.

billhollings commented 2 months ago

Although it might not matter, I'm not sure I understand what the fragment shader is doing. There is no output, and no writes that I can see. What is it doing?

Can you provide the full source code for the problematic shader under 1.2.10, including SPIR-V, MSL & GLSL?

You can do this by setting the following two environment variables when you run your app:

MVK_CONFIG_DEBUG=1
MVK_CONFIG_LOG_LEVEL=4

then copy the text for the SPIR-V, MSL & GLSL source code logged for the problematic shader.

If you can do that for the 1.2.4 MoltenVK as well, we can better compare what's working and what's not.

AlexanderDevaikinEnscape commented 2 months ago

Yes I've cut some parts out to not have a wall of text. Here are the complete SPIR-V, MSL and guessed GLSL sources for both 1.2.10 and 1.2.6.

This is a shadow map shader. Based on the material, if cut-out mask is present it samples the mask and discards fragment where mask values are <= 0.5. If not discarded, depth value is written into the attachment. With 1.2.10 sampling mask texture returns 0.0 for textures at index > 0.

1.2.10, not working ``` [mvk-info] Compiling Metal shader with FastMath enabled. [mvk-info] Converting SPIR-V: ; SPIR-V ; Version: 1.4 ; Generator: Google Shaderc over Glslang; 11 ; Bound: 214 ; Schema: 0 OpCapability Shader OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability SampledImageArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %43 %51 %63 %105 %146 %196 %201 %207 %213 OpExecutionMode %4 OriginUpperLeft OpExecutionMode %4 DepthReplacing OpSource GLSL 460 OpSourceExtension "GL_ARB_enhanced_layouts" OpSourceExtension "GL_EXT_debug_printf" OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" OpSourceExtension "GL_GOOGLE_include_directive" OpName %4 "main" OpName %10 "square(f1;" OpName %9 "x" OpName %17 "isValidSamplerId(u1;" OpName %16 "samplerId" OpName %26 "getTextureLodValue(u1;vf2;f1;" OpName %23 "samplerId" OpName %24 "uv" OpName %25 "lod" OpName %28 "opaqueMaskedDoubleSided(" OpName %43 "SceneTextures" OpName %51 "SceneTextureSampler" OpName %61 "VertexData" OpMemberName %61 0 "uv" OpMemberName %61 1 "maskSamplerId" OpMemberName %61 2 "vViewPos" OpName %63 "inoutData" OpName %66 "param" OpName %75 "param" OpName %78 "param" OpName %82 "param" OpName %92 "dist" OpName %102 "ShadowMapParameters" OpMemberName %102 0 "CameraMatrices" OpMemberName %102 1 "ViewInverse" OpMemberName %102 2 "WindScroll" OpMemberName %102 3 "WindVec" OpMemberName %102 4 "NearFar" OpMemberName %102 5 "BacksideClipDistanceSq" OpMemberName %102 6 "ShadowOffsetFactor" OpMemberName %102 7 "ShadowOffsetUnits" OpMemberName %102 8 "ShadowOffsetDynamic" OpMemberName %102 9 "ProjectionType" OpName %103 "ShadowMapParamsBuffer" OpMemberName %103 0 "params" OpName %105 "" OpName %119 "m" OpName %122 "param" OpName %126 "param" OpName %130 "depth" OpName %146 "gl_FragDepth" OpName %194 "TextureTransformsBuffer" OpMemberName %194 0 "TextureTransforms" OpName %196 "" OpName %197 "TextureSamplerIds" OpMemberName %197 0 "AlbedoId" OpMemberName %197 1 "NormalId" OpMemberName %197 2 "RoughnessId" OpMemberName %197 3 "MaskId" OpName %199 "TextureHandles" OpMemberName %199 0 "samplerIds" OpName %201 "" OpName %203 "InstanceData" OpMemberName %203 0 "transposeTransform" OpMemberName %203 1 "customData0" OpMemberName %203 2 "transposeInverseTransform" OpMemberName %203 3 "customData1" OpName %205 "InstanceDataBuffer" OpMemberName %205 0 "instanceData" OpName %207 "" OpName %209 "Material" OpMemberName %209 0 "BaseColorOpacityPacked" OpMemberName %209 1 "TintColorRoughnessPacked" OpMemberName %209 2 "MetallicSpecularImageFadeTypePacked" OpMemberName %209 3 "texTransformTypeIsVideoTexDispAmountTexClass" OpMemberName %209 4 "EmissiveColorPacked" OpMemberName %209 5 "avgColor" OpMemberName %209 6 "padding" OpName %211 "MaterialsBuffer" OpMemberName %211 0 "Materials" OpName %213 "" OpDecorate %43 DescriptorSet 1 OpDecorate %43 Binding 1 OpDecorate %45 NonUniform OpDecorate %47 NonUniform OpDecorate %48 NonUniform OpDecorate %51 DescriptorSet 0 OpDecorate %51 Binding 1 OpMemberDecorate %61 1 Flat OpDecorate %61 Block OpDecorate %63 Location 0 OpDecorate %101 ArrayStride 64 OpMemberDecorate %102 0 ColMajor OpMemberDecorate %102 0 Offset 0 OpMemberDecorate %102 0 MatrixStride 16 OpMemberDecorate %102 1 ColMajor OpMemberDecorate %102 1 Offset 128 OpMemberDecorate %102 1 MatrixStride 16 OpMemberDecorate %102 2 Offset 192 OpMemberDecorate %102 3 Offset 208 OpMemberDecorate %102 4 Offset 224 OpMemberDecorate %102 5 Offset 232 OpMemberDecorate %102 6 Offset 236 OpMemberDecorate %102 7 Offset 240 OpMemberDecorate %102 8 Offset 244 OpMemberDecorate %102 9 Offset 248 OpMemberDecorate %103 0 Offset 0 OpDecorate %103 Block OpDecorate %105 DescriptorSet 0 OpDecorate %105 Binding 3 OpDecorate %146 BuiltIn FragDepth OpDecorate %193 ArrayStride 4 OpMemberDecorate %194 0 NonWritable OpMemberDecorate %194 0 Offset 0 OpDecorate %194 Block OpDecorate %196 DescriptorSet 0 OpDecorate %196 Binding 0 OpMemberDecorate %197 0 Offset 0 OpMemberDecorate %197 1 Offset 4 OpMemberDecorate %197 2 Offset 8 OpMemberDecorate %197 3 Offset 12 OpDecorate %198 ArrayStride 16 OpMemberDecorate %199 0 NonWritable OpMemberDecorate %199 0 Offset 0 OpDecorate %199 Block OpDecorate %201 DescriptorSet 0 OpDecorate %201 Binding 2 OpMemberDecorate %203 0 ColMajor OpMemberDecorate %203 0 Offset 0 OpMemberDecorate %203 0 MatrixStride 16 OpMemberDecorate %203 1 Offset 48 OpMemberDecorate %203 2 ColMajor OpMemberDecorate %203 2 Offset 64 OpMemberDecorate %203 2 MatrixStride 16 OpMemberDecorate %203 3 Offset 112 OpDecorate %204 ArrayStride 128 OpMemberDecorate %205 0 NonWritable OpMemberDecorate %205 0 Offset 0 OpDecorate %205 Block OpDecorate %207 DescriptorSet 0 OpDecorate %207 Binding 4 OpMemberDecorate %209 0 Offset 0 OpMemberDecorate %209 1 Offset 4 OpMemberDecorate %209 2 Offset 8 OpMemberDecorate %209 3 Offset 12 OpMemberDecorate %209 4 Offset 16 OpMemberDecorate %209 5 Offset 24 OpMemberDecorate %209 6 Offset 28 OpDecorate %210 ArrayStride 32 OpMemberDecorate %211 0 NonWritable OpMemberDecorate %211 0 Offset 0 OpDecorate %211 Block OpDecorate %213 DescriptorSet 0 OpDecorate %213 Binding 5 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypePointer Function %6 %8 = OpTypeFunction %6 %7 %12 = OpTypeInt 32 0 %13 = OpTypePointer Function %12 %14 = OpTypeBool %15 = OpTypeFunction %14 %13 %19 = OpTypeVector %6 2 %20 = OpTypePointer Function %19 %21 = OpTypeVector %6 4 %22 = OpTypeFunction %21 %13 %20 %7 %36 = OpConstant %12 4294967295 %40 = OpTypeImage %6 2D 0 0 0 1 Unknown %41 = OpTypeRuntimeArray %40 %42 = OpTypePointer UniformConstant %41 %43 = OpVariable %42 UniformConstant %46 = OpTypePointer UniformConstant %40 %49 = OpTypeSampler %50 = OpTypePointer UniformConstant %49 %51 = OpVariable %50 UniformConstant %53 = OpTypeSampledImage %40 %60 = OpTypeVector %6 3 %61 = OpTypeStruct %19 %12 %60 %62 = OpTypePointer Input %61 %63 = OpVariable %62 Input %64 = OpTypeInt 32 1 %65 = OpConstant %64 1 %67 = OpTypePointer Input %12 %73 = OpConstant %64 0 %74 = OpConstant %6 0 %79 = OpTypePointer Input %19 %84 = OpConstant %12 0 %86 = OpConstant %6 0.5 %93 = OpConstant %6 1 %94 = OpConstant %64 2 %95 = OpConstant %12 2 %96 = OpTypePointer Input %6 %100 = OpTypeMatrix %21 4 %101 = OpTypeArray %100 %95 %102 = OpTypeStruct %101 %100 %21 %21 %19 %6 %6 %6 %6 %64 %103 = OpTypeStruct %102 %104 = OpTypePointer Uniform %103 %105 = OpVariable %104 Uniform %106 = OpConstant %64 4 %107 = OpTypePointer Uniform %6 %111 = OpConstant %12 1 %132 = OpConstant %64 7 %135 = OpConstant %6 7.62939453e-06 %139 = OpConstant %64 6 %145 = OpTypePointer Output %6 %146 = OpVariable %145 Output %148 = OpConstant %6 0.800000012 %149 = OpConstantComposite %19 %86 %74 %150 = OpConstant %6 65504 %151 = OpConstant %6 3.40282347e+38 %152 = OpTypeVector %64 2 %153 = OpConstant %12 9 %154 = OpTypeArray %152 %153 %155 = OpConstantComposite %152 %73 %73 %156 = OpConstant %64 -1 %157 = OpConstantComposite %152 %156 %156 %158 = OpConstantComposite %152 %156 %65 %159 = OpConstantComposite %152 %65 %156 %160 = OpConstantComposite %152 %65 %65 %161 = OpConstantComposite %152 %65 %73 %162 = OpConstantComposite %152 %73 %65 %163 = OpConstantComposite %152 %156 %73 %164 = OpConstantComposite %152 %73 %156 %165 = OpConstantComposite %154 %155 %157 %158 %159 %160 %161 %162 %163 %164 %166 = OpTypeMatrix %60 3 %167 = OpConstant %12 6 %168 = OpTypeArray %166 %167 %169 = OpConstant %6 -1 %170 = OpConstantComposite %60 %74 %74 %169 %171 = OpConstantComposite %60 %74 %169 %74 %172 = OpConstantComposite %60 %93 %74 %74 %173 = OpConstantComposite %166 %170 %171 %172 %174 = OpConstantComposite %60 %74 %74 %93 %175 = OpConstantComposite %60 %169 %74 %74 %176 = OpConstantComposite %166 %174 %171 %175 %177 = OpConstantComposite %60 %74 %93 %74 %178 = OpConstantComposite %166 %172 %174 %177 %179 = OpConstantComposite %166 %172 %170 %171 %180 = OpConstantComposite %166 %172 %171 %174 %181 = OpConstantComposite %166 %175 %171 %170 %182 = OpConstantComposite %168 %173 %176 %178 %179 %180 %181 %183 = OpConstant %6 6 %184 = OpConstantComposite %21 %74 %74 %74 %74 %185 = OpConstant %6 0.25 %186 = OpConstant %6 0.379999995 %187 = OpConstant %6 0.479999989 %188 = OpConstant %6 0.00499999989 %189 = OpConstant %6 0.899999976 %190 = OpConstant %6 0.0399999991 %191 = OpConstant %6 1.5 %192 = OpConstant %6 0.0199999996 %193 = OpTypeRuntimeArray %6 %194 = OpTypeStruct %193 %195 = OpTypePointer StorageBuffer %194 %196 = OpVariable %195 StorageBuffer %197 = OpTypeStruct %12 %12 %12 %12 %198 = OpTypeRuntimeArray %197 %199 = OpTypeStruct %198 %200 = OpTypePointer StorageBuffer %199 %201 = OpVariable %200 StorageBuffer %202 = OpTypeMatrix %21 3 %203 = OpTypeStruct %202 %21 %202 %21 %204 = OpTypeRuntimeArray %203 %205 = OpTypeStruct %204 %206 = OpTypePointer StorageBuffer %205 %207 = OpVariable %206 StorageBuffer %208 = OpTypeVector %12 2 %209 = OpTypeStruct %12 %12 %12 %12 %208 %12 %6 %210 = OpTypeRuntimeArray %209 %211 = OpTypeStruct %210 %212 = OpTypePointer StorageBuffer %211 %213 = OpVariable %212 StorageBuffer %4 = OpFunction %2 None %3 %5 = OpLabel %92 = OpVariable %7 Function %119 = OpVariable %7 Function %122 = OpVariable %7 Function %126 = OpVariable %7 Function %130 = OpVariable %7 Function %97 = OpAccessChain %96 %63 %94 %95 %98 = OpLoad %6 %97 %99 = OpFNegate %6 %98 %108 = OpAccessChain %107 %105 %73 %106 %84 %109 = OpLoad %6 %108 %110 = OpFSub %6 %99 %109 %112 = OpAccessChain %107 %105 %73 %106 %111 %113 = OpLoad %6 %112 %114 = OpAccessChain %107 %105 %73 %106 %84 %115 = OpLoad %6 %114 %116 = OpFSub %6 %113 %115 %117 = OpFDiv %6 %110 %116 %118 = OpFSub %6 %93 %117 OpStore %92 %118 %120 = OpLoad %6 %92 %121 = OpDPdx %6 %120 OpStore %122 %121 %123 = OpFunctionCall %6 %10 %122 %124 = OpLoad %6 %92 %125 = OpDPdy %6 %124 OpStore %126 %125 %127 = OpFunctionCall %6 %10 %126 %128 = OpFAdd %6 %123 %127 %129 = OpExtInst %6 %1 Sqrt %128 OpStore %119 %129 %131 = OpLoad %6 %92 %133 = OpAccessChain %107 %105 %73 %132 %134 = OpLoad %6 %133 %136 = OpFMul %6 %134 %135 %137 = OpFSub %6 %131 %136 %138 = OpLoad %6 %119 %140 = OpAccessChain %107 %105 %73 %139 %141 = OpLoad %6 %140 %142 = OpFMul %6 %138 %141 %143 = OpFSub %6 %137 %142 OpStore %130 %143 %144 = OpFunctionCall %2 %28 %147 = OpLoad %6 %130 OpStore %146 %147 OpReturn OpFunctionEnd %10 = OpFunction %6 None %8 %9 = OpFunctionParameter %7 %11 = OpLabel %30 = OpLoad %6 %9 %31 = OpLoad %6 %9 %32 = OpFMul %6 %30 %31 OpReturnValue %32 OpFunctionEnd %17 = OpFunction %14 None %15 %16 = OpFunctionParameter %13 %18 = OpLabel %35 = OpLoad %12 %16 %37 = OpINotEqual %14 %35 %36 OpReturnValue %37 OpFunctionEnd %26 = OpFunction %21 None %22 %23 = OpFunctionParameter %13 %24 = OpFunctionParameter %20 %25 = OpFunctionParameter %7 %27 = OpLabel %44 = OpLoad %12 %23 %45 = OpCopyObject %12 %44 %47 = OpAccessChain %46 %43 %45 %48 = OpLoad %40 %47 %52 = OpLoad %49 %51 %54 = OpSampledImage %53 %48 %52 %55 = OpLoad %19 %24 %56 = OpLoad %6 %25 %57 = OpImageSampleExplicitLod %21 %54 %55 Lod %56 OpReturnValue %57 OpFunctionEnd %28 = OpFunction %2 None %3 %29 = OpLabel %66 = OpVariable %13 Function %75 = OpVariable %13 Function %78 = OpVariable %20 Function %82 = OpVariable %7 Function %68 = OpAccessChain %67 %63 %65 %69 = OpLoad %12 %68 OpStore %66 %69 %70 = OpFunctionCall %14 %17 %66 OpSelectionMerge %72 None OpBranchConditional %70 %71 %72 %71 = OpLabel %76 = OpAccessChain %67 %63 %65 %77 = OpLoad %12 %76 OpStore %75 %77 %80 = OpAccessChain %79 %63 %73 %81 = OpLoad %19 %80 OpStore %78 %81 OpStore %82 %74 %83 = OpFunctionCall %21 %26 %75 %78 %82 %85 = OpCompositeExtract %6 %83 0 %87 = OpFOrdLessThanEqual %14 %85 %86 OpBranch %72 %72 = OpLabel %88 = OpPhi %14 %70 %29 %87 %71 OpSelectionMerge %90 None OpBranchConditional %88 %89 %90 %89 = OpLabel OpKill %90 = OpLabel OpReturn OpFunctionEnd End SPIR-V Converted MSL: #pragma clang diagnostic ignored "-Wmissing-prototypes" #pragma clang diagnostic ignored "-Wmissing-braces" #include #include using namespace metal; template struct spvUnsafeArray { T elements[Num ? Num : 1]; thread T& operator [] (size_t pos) thread { return elements[pos]; } constexpr const thread T& operator [] (size_t pos) const thread { return elements[pos]; } device T& operator [] (size_t pos) device { return elements[pos]; } constexpr const device T& operator [] (size_t pos) const device { return elements[pos]; } constexpr const constant T& operator [] (size_t pos) const constant { return elements[pos]; } threadgroup T& operator [] (size_t pos) threadgroup { return elements[pos]; } constexpr const threadgroup T& operator [] (size_t pos) const threadgroup { return elements[pos]; } }; struct VertexData { float2 uv; uint maskSamplerId; float3 vViewPos; }; struct ShadowMapParameters { float4x4 CameraMatrices[2]; float4x4 ViewInverse; float4 WindScroll; float4 WindVec; float2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct ShadowMapParamsBuffer { ShadowMapParameters params; }; struct TextureTransformsBuffer { float TextureTransforms[1]; }; struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct TextureHandles { TextureSamplerIds samplerIds[1]; }; struct InstanceData { float3x4 transposeTransform; float4 customData0; float3x4 transposeInverseTransform; float4 customData1; }; struct InstanceDataBuffer { InstanceData instanceData[1]; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uint2 EmissiveColorPacked; uint avgColor; float padding; }; struct MaterialsBuffer { Material Materials[1]; }; struct spvDescriptorSetBuffer0 { constant void* _m0_pad [[id(0)]]; constant void* _m1_pad [[id(1)]]; sampler SceneTextureSampler [[id(2)]]; constant void* _m3_pad [[id(3)]]; constant ShadowMapParamsBuffer* m_105 [[id(4)]]; }; struct spvDescriptorSetBuffer1 { array, 1> SceneTextures [[id(0)]]; }; constant spvUnsafeArray _165 = spvUnsafeArray({ int2(0), int2(-1), int2(-1, 1), int2(1, -1), int2(1), int2(1, 0), int2(0, 1), int2(-1, 0), int2(0, -1) }); constant spvUnsafeArray _182 = spvUnsafeArray({ float3x3(float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0), float3(1.0, 0.0, 0.0)), float3x3(float3(0.0, 0.0, 1.0), float3(0.0, -1.0, 0.0), float3(-1.0, 0.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, 1.0), float3(0.0, 1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, 1.0)), float3x3(float3(-1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, -1.0)) }); struct main0_out { float gl_FragDepth [[depth(any)]]; }; struct main0_in { float2 inoutData_uv [[user(locn0)]]; uint inoutData_maskSamplerId [[user(locn1)]]; float3 inoutData_vViewPos [[user(locn2)]]; }; static inline __attribute__((always_inline)) float square(thread const float& x) { return x * x; } static inline __attribute__((always_inline)) bool isValidSamplerId(thread const uint& samplerId) { return samplerId != 4294967295u; } static inline __attribute__((always_inline)) float4 getTextureLodValue(thread const uint& samplerId, thread const float2& uv, thread const float& lod, constant array, 1>& SceneTextures, sampler SceneTextureSampler) { uint _45 = samplerId; return SceneTextures[_45].sample(SceneTextureSampler, uv, level(lod)); } static inline __attribute__((always_inline)) void opaqueMaskedDoubleSided(constant array, 1>& SceneTextures, sampler SceneTextureSampler, thread VertexData& inoutData) { uint param = inoutData.maskSamplerId; bool _70 = isValidSamplerId(param); bool _88; if (_70) { uint param_1 = inoutData.maskSamplerId; float2 param_2 = inoutData.uv; float param_3 = 0.0; _88 = getTextureLodValue(param_1, param_2, param_3, SceneTextures, SceneTextureSampler).x <= 0.5; } else { _88 = _70; } if (_88) { discard_fragment(); } } fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]]) { main0_out out = {}; VertexData inoutData = {}; inoutData.uv = in.inoutData_uv; inoutData.maskSamplerId = in.inoutData_maskSamplerId; inoutData.vViewPos = in.inoutData_vViewPos; float dist = 1.0 - (((-inoutData.vViewPos.z) - (*spvDescriptorSet0.m_105).params.NearFar.x) / ((*spvDescriptorSet0.m_105).params.NearFar.y - (*spvDescriptorSet0.m_105).params.NearFar.x)); float param = dfdx(dist); float param_1 = dfdy(dist); float m = sqrt(square(param) + square(param_1)); float depth = (dist - ((*spvDescriptorSet0.m_105).params.ShadowOffsetUnits * 7.62939453125e-06)) - (m * (*spvDescriptorSet0.m_105).params.ShadowOffsetFactor); opaqueMaskedDoubleSided(spvDescriptorSet1.SceneTextures, spvDescriptorSet0.SceneTextureSampler, inoutData); out.gl_FragDepth = depth; return out; } End MSL Estimated original GLSL: #version 460 #extension GL_EXT_nonuniform_qualifier : require struct ShadowMapParameters { mat4 CameraMatrices[2]; mat4 ViewInverse; vec4 WindScroll; vec4 WindVec; vec2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct InstanceData { mat3x4 transposeTransform; vec4 customData0; mat3x4 transposeInverseTransform; vec4 customData1; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uvec2 EmissiveColorPacked; uint avgColor; float padding; }; layout(set = 0, binding = 3, std140) uniform ShadowMapParamsBuffer { ShadowMapParameters params; } _105; layout(set = 0, binding = 0, std430) readonly buffer TextureTransformsBuffer { float TextureTransforms[]; } _196; layout(set = 0, binding = 2, std430) readonly buffer TextureHandles { TextureSamplerIds samplerIds[]; } _201; layout(set = 0, binding = 4, std430) readonly buffer InstanceDataBuffer { InstanceData instanceData[]; } _207; layout(set = 0, binding = 5, std430) readonly buffer MaterialsBuffer { Material Materials[]; } _213; layout(set = 1, binding = 1) uniform texture2D SceneTextures[]; layout(set = 0, binding = 1) uniform sampler SceneTextureSampler; layout(location = 0) in VertexData { vec2 uv; flat uint maskSamplerId; vec3 vViewPos; } inoutData; float square(float x) { return x * x; } bool isValidSamplerId(uint samplerId) { return samplerId != 4294967295u; } vec4 getTextureLodValue(uint samplerId, vec2 uv, float lod) { uint _45 = samplerId; return textureLod(sampler2D(SceneTextures[_45], SceneTextureSampler), uv, lod); } void opaqueMaskedDoubleSided() { uint param = inoutData.maskSamplerId; bool _70 = isValidSamplerId(param); bool _88; if (_70) { uint param_1 = inoutData.maskSamplerId; vec2 param_2 = inoutData.uv; float param_3 = 0.0; _88 = getTextureLodValue(param_1, param_2, param_3).x <= 0.5; } else { _88 = _70; } if (_88) { discard; } } void main() { float dist = 1.0 - (((-inoutData.vViewPos.z) - _105.params.NearFar.x) / (_105.params.NearFar.y - _105.params.NearFar.x)); float param = dFdx(dist); float param_1 = dFdy(dist); float m = sqrt(square(param) + square(param_1)); float depth = (dist - (_105.params.ShadowOffsetUnits * 7.62939453125e-06)) - (m * _105.params.ShadowOffsetFactor); opaqueMaskedDoubleSided(); gl_FragDepth = depth; } End GLSL ```
1.2.6, working ``` [mvk-info] Compiling Metal shader with FastMath enabled. [mvk-info] Converting SPIR-V: ; SPIR-V ; Version: 1.4 ; Generator: Google Shaderc over Glslang; 11 ; Bound: 214 ; Schema: 0 OpCapability Shader OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability SampledImageArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %43 %51 %63 %105 %146 %196 %201 %207 %213 OpExecutionMode %4 OriginUpperLeft OpExecutionMode %4 DepthReplacing OpSource GLSL 460 OpSourceExtension "GL_ARB_enhanced_layouts" OpSourceExtension "GL_EXT_debug_printf" OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" OpSourceExtension "GL_GOOGLE_include_directive" OpName %4 "main" OpName %10 "square(f1;" OpName %9 "x" OpName %17 "isValidSamplerId(u1;" OpName %16 "samplerId" OpName %26 "getTextureLodValue(u1;vf2;f1;" OpName %23 "samplerId" OpName %24 "uv" OpName %25 "lod" OpName %28 "opaqueMaskedDoubleSided(" OpName %43 "SceneTextures" OpName %51 "SceneTextureSampler" OpName %61 "VertexData" OpMemberName %61 0 "uv" OpMemberName %61 1 "maskSamplerId" OpMemberName %61 2 "vViewPos" OpName %63 "inoutData" OpName %66 "param" OpName %75 "param" OpName %78 "param" OpName %82 "param" OpName %92 "dist" OpName %102 "ShadowMapParameters" OpMemberName %102 0 "CameraMatrices" OpMemberName %102 1 "ViewInverse" OpMemberName %102 2 "WindScroll" OpMemberName %102 3 "WindVec" OpMemberName %102 4 "NearFar" OpMemberName %102 5 "BacksideClipDistanceSq" OpMemberName %102 6 "ShadowOffsetFactor" OpMemberName %102 7 "ShadowOffsetUnits" OpMemberName %102 8 "ShadowOffsetDynamic" OpMemberName %102 9 "ProjectionType" OpName %103 "ShadowMapParamsBuffer" OpMemberName %103 0 "params" OpName %105 "" OpName %119 "m" OpName %122 "param" OpName %126 "param" OpName %130 "depth" OpName %146 "gl_FragDepth" OpName %194 "TextureTransformsBuffer" OpMemberName %194 0 "TextureTransforms" OpName %196 "" OpName %197 "TextureSamplerIds" OpMemberName %197 0 "AlbedoId" OpMemberName %197 1 "NormalId" OpMemberName %197 2 "RoughnessId" OpMemberName %197 3 "MaskId" OpName %199 "TextureHandles" OpMemberName %199 0 "samplerIds" OpName %201 "" OpName %203 "InstanceData" OpMemberName %203 0 "transposeTransform" OpMemberName %203 1 "customData0" OpMemberName %203 2 "transposeInverseTransform" OpMemberName %203 3 "customData1" OpName %205 "InstanceDataBuffer" OpMemberName %205 0 "instanceData" OpName %207 "" OpName %209 "Material" OpMemberName %209 0 "BaseColorOpacityPacked" OpMemberName %209 1 "TintColorRoughnessPacked" OpMemberName %209 2 "MetallicSpecularImageFadeTypePacked" OpMemberName %209 3 "texTransformTypeIsVideoTexDispAmountTexClass" OpMemberName %209 4 "EmissiveColorPacked" OpMemberName %209 5 "avgColor" OpMemberName %209 6 "padding" OpName %211 "MaterialsBuffer" OpMemberName %211 0 "Materials" OpName %213 "" OpDecorate %43 DescriptorSet 1 OpDecorate %43 Binding 1 OpDecorate %45 NonUniform OpDecorate %47 NonUniform OpDecorate %48 NonUniform OpDecorate %51 DescriptorSet 0 OpDecorate %51 Binding 1 OpMemberDecorate %61 1 Flat OpDecorate %61 Block OpDecorate %63 Location 0 OpDecorate %101 ArrayStride 64 OpMemberDecorate %102 0 ColMajor OpMemberDecorate %102 0 Offset 0 OpMemberDecorate %102 0 MatrixStride 16 OpMemberDecorate %102 1 ColMajor OpMemberDecorate %102 1 Offset 128 OpMemberDecorate %102 1 MatrixStride 16 OpMemberDecorate %102 2 Offset 192 OpMemberDecorate %102 3 Offset 208 OpMemberDecorate %102 4 Offset 224 OpMemberDecorate %102 5 Offset 232 OpMemberDecorate %102 6 Offset 236 OpMemberDecorate %102 7 Offset 240 OpMemberDecorate %102 8 Offset 244 OpMemberDecorate %102 9 Offset 248 OpMemberDecorate %103 0 Offset 0 OpDecorate %103 Block OpDecorate %105 DescriptorSet 0 OpDecorate %105 Binding 3 OpDecorate %146 BuiltIn FragDepth OpDecorate %193 ArrayStride 4 OpMemberDecorate %194 0 NonWritable OpMemberDecorate %194 0 Offset 0 OpDecorate %194 Block OpDecorate %196 DescriptorSet 0 OpDecorate %196 Binding 0 OpMemberDecorate %197 0 Offset 0 OpMemberDecorate %197 1 Offset 4 OpMemberDecorate %197 2 Offset 8 OpMemberDecorate %197 3 Offset 12 OpDecorate %198 ArrayStride 16 OpMemberDecorate %199 0 NonWritable OpMemberDecorate %199 0 Offset 0 OpDecorate %199 Block OpDecorate %201 DescriptorSet 0 OpDecorate %201 Binding 2 OpMemberDecorate %203 0 ColMajor OpMemberDecorate %203 0 Offset 0 OpMemberDecorate %203 0 MatrixStride 16 OpMemberDecorate %203 1 Offset 48 OpMemberDecorate %203 2 ColMajor OpMemberDecorate %203 2 Offset 64 OpMemberDecorate %203 2 MatrixStride 16 OpMemberDecorate %203 3 Offset 112 OpDecorate %204 ArrayStride 128 OpMemberDecorate %205 0 NonWritable OpMemberDecorate %205 0 Offset 0 OpDecorate %205 Block OpDecorate %207 DescriptorSet 0 OpDecorate %207 Binding 4 OpMemberDecorate %209 0 Offset 0 OpMemberDecorate %209 1 Offset 4 OpMemberDecorate %209 2 Offset 8 OpMemberDecorate %209 3 Offset 12 OpMemberDecorate %209 4 Offset 16 OpMemberDecorate %209 5 Offset 24 OpMemberDecorate %209 6 Offset 28 OpDecorate %210 ArrayStride 32 OpMemberDecorate %211 0 NonWritable OpMemberDecorate %211 0 Offset 0 OpDecorate %211 Block OpDecorate %213 DescriptorSet 0 OpDecorate %213 Binding 5 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypePointer Function %6 %8 = OpTypeFunction %6 %7 %12 = OpTypeInt 32 0 %13 = OpTypePointer Function %12 %14 = OpTypeBool %15 = OpTypeFunction %14 %13 %19 = OpTypeVector %6 2 %20 = OpTypePointer Function %19 %21 = OpTypeVector %6 4 %22 = OpTypeFunction %21 %13 %20 %7 %36 = OpConstant %12 4294967295 %40 = OpTypeImage %6 2D 0 0 0 1 Unknown %41 = OpTypeRuntimeArray %40 %42 = OpTypePointer UniformConstant %41 %43 = OpVariable %42 UniformConstant %46 = OpTypePointer UniformConstant %40 %49 = OpTypeSampler %50 = OpTypePointer UniformConstant %49 %51 = OpVariable %50 UniformConstant %53 = OpTypeSampledImage %40 %60 = OpTypeVector %6 3 %61 = OpTypeStruct %19 %12 %60 %62 = OpTypePointer Input %61 %63 = OpVariable %62 Input %64 = OpTypeInt 32 1 %65 = OpConstant %64 1 %67 = OpTypePointer Input %12 %73 = OpConstant %64 0 %74 = OpConstant %6 0 %79 = OpTypePointer Input %19 %84 = OpConstant %12 0 %86 = OpConstant %6 0.5 %93 = OpConstant %6 1 %94 = OpConstant %64 2 %95 = OpConstant %12 2 %96 = OpTypePointer Input %6 %100 = OpTypeMatrix %21 4 %101 = OpTypeArray %100 %95 %102 = OpTypeStruct %101 %100 %21 %21 %19 %6 %6 %6 %6 %64 %103 = OpTypeStruct %102 %104 = OpTypePointer Uniform %103 %105 = OpVariable %104 Uniform %106 = OpConstant %64 4 %107 = OpTypePointer Uniform %6 %111 = OpConstant %12 1 %132 = OpConstant %64 7 %135 = OpConstant %6 7.62939453e-06 %139 = OpConstant %64 6 %145 = OpTypePointer Output %6 %146 = OpVariable %145 Output %148 = OpConstant %6 0.800000012 %149 = OpConstantComposite %19 %86 %74 %150 = OpConstant %6 65504 %151 = OpConstant %6 3.40282347e+38 %152 = OpTypeVector %64 2 %153 = OpConstant %12 9 %154 = OpTypeArray %152 %153 %155 = OpConstantComposite %152 %73 %73 %156 = OpConstant %64 -1 %157 = OpConstantComposite %152 %156 %156 %158 = OpConstantComposite %152 %156 %65 %159 = OpConstantComposite %152 %65 %156 %160 = OpConstantComposite %152 %65 %65 %161 = OpConstantComposite %152 %65 %73 %162 = OpConstantComposite %152 %73 %65 %163 = OpConstantComposite %152 %156 %73 %164 = OpConstantComposite %152 %73 %156 %165 = OpConstantComposite %154 %155 %157 %158 %159 %160 %161 %162 %163 %164 %166 = OpTypeMatrix %60 3 %167 = OpConstant %12 6 %168 = OpTypeArray %166 %167 %169 = OpConstant %6 -1 %170 = OpConstantComposite %60 %74 %74 %169 %171 = OpConstantComposite %60 %74 %169 %74 %172 = OpConstantComposite %60 %93 %74 %74 %173 = OpConstantComposite %166 %170 %171 %172 %174 = OpConstantComposite %60 %74 %74 %93 %175 = OpConstantComposite %60 %169 %74 %74 %176 = OpConstantComposite %166 %174 %171 %175 %177 = OpConstantComposite %60 %74 %93 %74 %178 = OpConstantComposite %166 %172 %174 %177 %179 = OpConstantComposite %166 %172 %170 %171 %180 = OpConstantComposite %166 %172 %171 %174 %181 = OpConstantComposite %166 %175 %171 %170 %182 = OpConstantComposite %168 %173 %176 %178 %179 %180 %181 %183 = OpConstant %6 6 %184 = OpConstantComposite %21 %74 %74 %74 %74 %185 = OpConstant %6 0.25 %186 = OpConstant %6 0.379999995 %187 = OpConstant %6 0.479999989 %188 = OpConstant %6 0.00499999989 %189 = OpConstant %6 0.899999976 %190 = OpConstant %6 0.0399999991 %191 = OpConstant %6 1.5 %192 = OpConstant %6 0.0199999996 %193 = OpTypeRuntimeArray %6 %194 = OpTypeStruct %193 %195 = OpTypePointer StorageBuffer %194 %196 = OpVariable %195 StorageBuffer %197 = OpTypeStruct %12 %12 %12 %12 %198 = OpTypeRuntimeArray %197 %199 = OpTypeStruct %198 %200 = OpTypePointer StorageBuffer %199 %201 = OpVariable %200 StorageBuffer %202 = OpTypeMatrix %21 3 %203 = OpTypeStruct %202 %21 %202 %21 %204 = OpTypeRuntimeArray %203 %205 = OpTypeStruct %204 %206 = OpTypePointer StorageBuffer %205 %207 = OpVariable %206 StorageBuffer %208 = OpTypeVector %12 2 %209 = OpTypeStruct %12 %12 %12 %12 %208 %12 %6 %210 = OpTypeRuntimeArray %209 %211 = OpTypeStruct %210 %212 = OpTypePointer StorageBuffer %211 %213 = OpVariable %212 StorageBuffer %4 = OpFunction %2 None %3 %5 = OpLabel %92 = OpVariable %7 Function %119 = OpVariable %7 Function %122 = OpVariable %7 Function %126 = OpVariable %7 Function %130 = OpVariable %7 Function %97 = OpAccessChain %96 %63 %94 %95 %98 = OpLoad %6 %97 %99 = OpFNegate %6 %98 %108 = OpAccessChain %107 %105 %73 %106 %84 %109 = OpLoad %6 %108 %110 = OpFSub %6 %99 %109 %112 = OpAccessChain %107 %105 %73 %106 %111 %113 = OpLoad %6 %112 %114 = OpAccessChain %107 %105 %73 %106 %84 %115 = OpLoad %6 %114 %116 = OpFSub %6 %113 %115 %117 = OpFDiv %6 %110 %116 %118 = OpFSub %6 %93 %117 OpStore %92 %118 %120 = OpLoad %6 %92 %121 = OpDPdx %6 %120 OpStore %122 %121 %123 = OpFunctionCall %6 %10 %122 %124 = OpLoad %6 %92 %125 = OpDPdy %6 %124 OpStore %126 %125 %127 = OpFunctionCall %6 %10 %126 %128 = OpFAdd %6 %123 %127 %129 = OpExtInst %6 %1 Sqrt %128 OpStore %119 %129 %131 = OpLoad %6 %92 %133 = OpAccessChain %107 %105 %73 %132 %134 = OpLoad %6 %133 %136 = OpFMul %6 %134 %135 %137 = OpFSub %6 %131 %136 %138 = OpLoad %6 %119 %140 = OpAccessChain %107 %105 %73 %139 %141 = OpLoad %6 %140 %142 = OpFMul %6 %138 %141 %143 = OpFSub %6 %137 %142 OpStore %130 %143 %144 = OpFunctionCall %2 %28 %147 = OpLoad %6 %130 OpStore %146 %147 OpReturn OpFunctionEnd %10 = OpFunction %6 None %8 %9 = OpFunctionParameter %7 %11 = OpLabel %30 = OpLoad %6 %9 %31 = OpLoad %6 %9 %32 = OpFMul %6 %30 %31 OpReturnValue %32 OpFunctionEnd %17 = OpFunction %14 None %15 %16 = OpFunctionParameter %13 %18 = OpLabel %35 = OpLoad %12 %16 %37 = OpINotEqual %14 %35 %36 OpReturnValue %37 OpFunctionEnd %26 = OpFunction %21 None %22 %23 = OpFunctionParameter %13 %24 = OpFunctionParameter %20 %25 = OpFunctionParameter %7 %27 = OpLabel %44 = OpLoad %12 %23 %45 = OpCopyObject %12 %44 %47 = OpAccessChain %46 %43 %45 %48 = OpLoad %40 %47 %52 = OpLoad %49 %51 %54 = OpSampledImage %53 %48 %52 %55 = OpLoad %19 %24 %56 = OpLoad %6 %25 %57 = OpImageSampleExplicitLod %21 %54 %55 Lod %56 OpReturnValue %57 OpFunctionEnd %28 = OpFunction %2 None %3 %29 = OpLabel %66 = OpVariable %13 Function %75 = OpVariable %13 Function %78 = OpVariable %20 Function %82 = OpVariable %7 Function %68 = OpAccessChain %67 %63 %65 %69 = OpLoad %12 %68 OpStore %66 %69 %70 = OpFunctionCall %14 %17 %66 OpSelectionMerge %72 None OpBranchConditional %70 %71 %72 %71 = OpLabel %76 = OpAccessChain %67 %63 %65 %77 = OpLoad %12 %76 OpStore %75 %77 %80 = OpAccessChain %79 %63 %73 %81 = OpLoad %19 %80 OpStore %78 %81 OpStore %82 %74 %83 = OpFunctionCall %21 %26 %75 %78 %82 %85 = OpCompositeExtract %6 %83 0 %87 = OpFOrdLessThanEqual %14 %85 %86 OpBranch %72 %72 = OpLabel %88 = OpPhi %14 %70 %29 %87 %71 OpSelectionMerge %90 None OpBranchConditional %88 %89 %90 %89 = OpLabel OpKill %90 = OpLabel OpReturn OpFunctionEnd End SPIR-V Converted MSL: #pragma clang diagnostic ignored "-Wmissing-prototypes" #pragma clang diagnostic ignored "-Wmissing-braces" #include #include using namespace metal; template struct spvUnsafeArray { T elements[Num ? Num : 1]; thread T& operator [] (size_t pos) thread { return elements[pos]; } constexpr const thread T& operator [] (size_t pos) const thread { return elements[pos]; } device T& operator [] (size_t pos) device { return elements[pos]; } constexpr const device T& operator [] (size_t pos) const device { return elements[pos]; } constexpr const constant T& operator [] (size_t pos) const constant { return elements[pos]; } threadgroup T& operator [] (size_t pos) threadgroup { return elements[pos]; } constexpr const threadgroup T& operator [] (size_t pos) const threadgroup { return elements[pos]; } }; struct VertexData { float2 uv; uint maskSamplerId; float3 vViewPos; }; struct ShadowMapParameters { float4x4 CameraMatrices[2]; float4x4 ViewInverse; float4 WindScroll; float4 WindVec; float2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct ShadowMapParamsBuffer { ShadowMapParameters params; }; struct TextureTransformsBuffer { float TextureTransforms[1]; }; struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct TextureHandles { TextureSamplerIds samplerIds[1]; }; struct InstanceData { float3x4 transposeTransform; float4 customData0; float3x4 transposeInverseTransform; float4 customData1; }; struct InstanceDataBuffer { InstanceData instanceData[1]; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uint2 EmissiveColorPacked; uint avgColor; float padding; }; struct MaterialsBuffer { Material Materials[1]; }; struct spvDescriptorSetBuffer0 { const device TextureTransformsBuffer* m_196 [[id(0)]]; sampler SceneTextureSampler [[id(1)]]; const device TextureHandles* m_201 [[id(2)]]; constant ShadowMapParamsBuffer* m_105 [[id(3)]]; const device InstanceDataBuffer* m_207 [[id(4)]]; const device MaterialsBuffer* m_213 [[id(5)]]; }; struct spvDescriptorSetBuffer1 { array, 65536> SceneTextures [[id(0)]]; }; constant spvUnsafeArray _165 = spvUnsafeArray({ int2(0), int2(-1), int2(-1, 1), int2(1, -1), int2(1), int2(1, 0), int2(0, 1), int2(-1, 0), int2(0, -1) }); constant spvUnsafeArray _182 = spvUnsafeArray({ float3x3(float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0), float3(1.0, 0.0, 0.0)), float3x3(float3(0.0, 0.0, 1.0), float3(0.0, -1.0, 0.0), float3(-1.0, 0.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, 1.0), float3(0.0, 1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, 1.0)), float3x3(float3(-1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, -1.0)) }); struct main0_out { float gl_FragDepth [[depth(any)]]; }; struct main0_in { float2 inoutData_uv [[user(locn0)]]; uint inoutData_maskSamplerId [[user(locn1)]]; float3 inoutData_vViewPos [[user(locn2)]]; }; static inline __attribute__((always_inline)) float square(thread const float& x) { return x * x; } static inline __attribute__((always_inline)) bool isValidSamplerId(thread const uint& samplerId) { return samplerId != 4294967295u; } static inline __attribute__((always_inline)) float4 getTextureLodValue(thread const uint& samplerId, thread const float2& uv, thread const float& lod, constant array, 65536>& SceneTextures, sampler SceneTextureSampler) { uint _45 = samplerId; return SceneTextures[_45].sample(SceneTextureSampler, uv, level(lod)); } static inline __attribute__((always_inline)) void opaqueMaskedDoubleSided(constant array, 65536>& SceneTextures, sampler SceneTextureSampler, thread VertexData& inoutData) { uint param = inoutData.maskSamplerId; bool _70 = isValidSamplerId(param); bool _88; if (_70) { uint param_1 = inoutData.maskSamplerId; float2 param_2 = inoutData.uv; float param_3 = 0.0; _88 = getTextureLodValue(param_1, param_2, param_3, SceneTextures, SceneTextureSampler).x <= 0.5; } else { _88 = _70; } if (_88) { discard_fragment(); } } fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]]) { main0_out out = {}; VertexData inoutData = {}; inoutData.uv = in.inoutData_uv; inoutData.maskSamplerId = in.inoutData_maskSamplerId; inoutData.vViewPos = in.inoutData_vViewPos; float dist = 1.0 - (((-inoutData.vViewPos.z) - (*spvDescriptorSet0.m_105).params.NearFar.x) / ((*spvDescriptorSet0.m_105).params.NearFar.y - (*spvDescriptorSet0.m_105).params.NearFar.x)); float param = dfdx(dist); float param_1 = dfdy(dist); float m = sqrt(square(param) + square(param_1)); float depth = (dist - ((*spvDescriptorSet0.m_105).params.ShadowOffsetUnits * 7.62939453125e-06)) - (m * (*spvDescriptorSet0.m_105).params.ShadowOffsetFactor); opaqueMaskedDoubleSided(spvDescriptorSet1.SceneTextures, spvDescriptorSet0.SceneTextureSampler, inoutData); out.gl_FragDepth = depth; return out; } End MSL Estimated original GLSL: #version 460 #extension GL_EXT_nonuniform_qualifier : require struct ShadowMapParameters { mat4 CameraMatrices[2]; mat4 ViewInverse; vec4 WindScroll; vec4 WindVec; vec2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct InstanceData { mat3x4 transposeTransform; vec4 customData0; mat3x4 transposeInverseTransform; vec4 customData1; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uvec2 EmissiveColorPacked; uint avgColor; float padding; }; layout(set = 0, binding = 3, std140) uniform ShadowMapParamsBuffer { ShadowMapParameters params; } _105; layout(set = 0, binding = 0, std430) readonly buffer TextureTransformsBuffer { float TextureTransforms[]; } _196; layout(set = 0, binding = 2, std430) readonly buffer TextureHandles { TextureSamplerIds samplerIds[]; } _201; layout(set = 0, binding = 4, std430) readonly buffer InstanceDataBuffer { InstanceData instanceData[]; } _207; layout(set = 0, binding = 5, std430) readonly buffer MaterialsBuffer { Material Materials[]; } _213; layout(set = 1, binding = 1) uniform texture2D SceneTextures[]; layout(set = 0, binding = 1) uniform sampler SceneTextureSampler; layout(location = 0) in VertexData { vec2 uv; flat uint maskSamplerId; vec3 vViewPos; } inoutData; float square(float x) { return x * x; } bool isValidSamplerId(uint samplerId) { return samplerId != 4294967295u; } vec4 getTextureLodValue(uint samplerId, vec2 uv, float lod) { uint _45 = samplerId; return textureLod(sampler2D(SceneTextures[_45], SceneTextureSampler), uv, lod); } void opaqueMaskedDoubleSided() { uint param = inoutData.maskSamplerId; bool _70 = isValidSamplerId(param); bool _88; if (_70) { uint param_1 = inoutData.maskSamplerId; vec2 param_2 = inoutData.uv; float param_3 = 0.0; _88 = getTextureLodValue(param_1, param_2, param_3).x <= 0.5; } else { _88 = _70; } if (_88) { discard; } } void main() { float dist = 1.0 - (((-inoutData.vViewPos.z) - _105.params.NearFar.x) / (_105.params.NearFar.y - _105.params.NearFar.x)); float param = dFdx(dist); float param_1 = dFdy(dist); float m = sqrt(square(param) + square(param_1)); float depth = (dist - (_105.params.ShadowOffsetUnits * 7.62939453125e-06)) - (m * _105.params.ShadowOffsetFactor); opaqueMaskedDoubleSided(); gl_FragDepth = depth; } End GLSL ```
billhollings commented 2 months ago

Unfortunately, going back to declaring the full size of the array in MSL will result in Metal validation errors like:

struct spvDescriptorSetBuffer0
{
    array<texture2d<float>, 1000000> Textures [[id(0)]];
};

-[MTLDebugRenderCommandEncoder validateCommonDrawErrors:]:5775: failed assertion `Draw Errors Validation
Fragment Function(main0): argument spvDescriptorSet0[0] from buffer(0) with offset(16384) and length(16928) has space for 544 bytes, but argument has a length(8000000).'

~What mechanism are you using to update descriptors?~

[Edit:] NM. Upon further checking, this won't make a difference.

AlexanderDevaikinEnscape commented 2 months ago

It may be yet another weird Metal behaviour we observe sometimes and have to workaround. Like inability to query texture LOD on some GPUs for example. I don't see any reason why the same descriptor set should work in one render pass and not work in another, especially considering the fact that it works in both with shader validation enabled.

AlexanderDevaikinEnscape commented 2 months ago

Some updates after more debugging.

  1. The shaders I have shown above are not the ones that make problems. I have posted another variation of that shader that actually works. Pardon for confusion. But it does not make much difference - the problematic version of the shader with 1.2.6 and 1.2.10 has the same diff as those two I have posted - mainly texture array size 1 vs. 65536. But...
  2. That made me think why the other variant works, while the problematic does not work. Shuffling lines and calls has resulted in a workaround - do not do discard. If I instead write depth values for both cases (masked out and not), texture sampling works as expected. I have no idea how this change can influence the sampling though.

For reference, all problematic shader versions:

Working version, MoltenVK 1.2.6 ``` [mvk-info] Compiling Metal shader with FastMath enabled. [mvk-info] Converting SPIR-V: ; SPIR-V ; Version: 1.4 ; Generator: Google Shaderc over Glslang; 11 ; Bound: 158 ; Schema: 0 OpCapability Shader OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability SampledImageArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %34 %42 %54 %133 %138 %145 %151 %157 OpExecutionMode %4 OriginUpperLeft OpSource GLSL 460 OpSourceExtension "GL_ARB_enhanced_layouts" OpSourceExtension "GL_EXT_debug_printf" OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" OpSourceExtension "GL_GOOGLE_include_directive" OpName %4 "main" OpName %11 "isValidSamplerId(u1;" OpName %10 "samplerId" OpName %22 "getTextureLodValue(u1;vf2;f1;" OpName %19 "samplerId" OpName %20 "uv" OpName %21 "lod" OpName %24 "opaqueMaskedDoubleSided(" OpName %34 "SceneTextures" OpName %42 "SceneTextureSampler" OpName %52 "VertexData" OpMemberName %52 0 "uv" OpMemberName %52 1 "maskSamplerId" OpMemberName %52 2 "vViewPos" OpName %54 "inoutData" OpName %57 "param" OpName %66 "param" OpName %69 "param" OpName %73 "param" OpName %131 "TextureTransformsBuffer" OpMemberName %131 0 "TextureTransforms" OpName %133 "" OpName %134 "TextureSamplerIds" OpMemberName %134 0 "AlbedoId" OpMemberName %134 1 "NormalId" OpMemberName %134 2 "RoughnessId" OpMemberName %134 3 "MaskId" OpName %136 "TextureHandles" OpMemberName %136 0 "samplerIds" OpName %138 "" OpName %142 "ShadowMapParameters" OpMemberName %142 0 "CameraMatrices" OpMemberName %142 1 "ViewInverse" OpMemberName %142 2 "WindScroll" OpMemberName %142 3 "WindVec" OpMemberName %142 4 "NearFar" OpMemberName %142 5 "BacksideClipDistanceSq" OpMemberName %142 6 "ShadowOffsetFactor" OpMemberName %142 7 "ShadowOffsetUnits" OpMemberName %142 8 "ShadowOffsetDynamic" OpMemberName %142 9 "ProjectionType" OpName %143 "ShadowMapParamsBuffer" OpMemberName %143 0 "params" OpName %145 "" OpName %147 "InstanceData" OpMemberName %147 0 "transposeTransform" OpMemberName %147 1 "customData0" OpMemberName %147 2 "transposeInverseTransform" OpMemberName %147 3 "customData1" OpName %149 "InstanceDataBuffer" OpMemberName %149 0 "instanceData" OpName %151 "" OpName %153 "Material" OpMemberName %153 0 "BaseColorOpacityPacked" OpMemberName %153 1 "TintColorRoughnessPacked" OpMemberName %153 2 "MetallicSpecularImageFadeTypePacked" OpMemberName %153 3 "texTransformTypeIsVideoTexDispAmountTexClass" OpMemberName %153 4 "EmissiveColorPacked" OpMemberName %153 5 "avgColor" OpMemberName %153 6 "padding" OpName %155 "MaterialsBuffer" OpMemberName %155 0 "Materials" OpName %157 "" OpDecorate %34 DescriptorSet 1 OpDecorate %34 Binding 1 OpDecorate %36 NonUniform OpDecorate %38 NonUniform OpDecorate %39 NonUniform OpDecorate %42 DescriptorSet 0 OpDecorate %42 Binding 1 OpMemberDecorate %52 1 Flat OpDecorate %52 Block OpDecorate %54 Location 0 OpDecorate %130 ArrayStride 4 OpMemberDecorate %131 0 NonWritable OpMemberDecorate %131 0 Offset 0 OpDecorate %131 Block OpDecorate %133 DescriptorSet 0 OpDecorate %133 Binding 0 OpMemberDecorate %134 0 Offset 0 OpMemberDecorate %134 1 Offset 4 OpMemberDecorate %134 2 Offset 8 OpMemberDecorate %134 3 Offset 12 OpDecorate %135 ArrayStride 16 OpMemberDecorate %136 0 NonWritable OpMemberDecorate %136 0 Offset 0 OpDecorate %136 Block OpDecorate %138 DescriptorSet 0 OpDecorate %138 Binding 2 OpDecorate %141 ArrayStride 64 OpMemberDecorate %142 0 ColMajor OpMemberDecorate %142 0 Offset 0 OpMemberDecorate %142 0 MatrixStride 16 OpMemberDecorate %142 1 ColMajor OpMemberDecorate %142 1 Offset 128 OpMemberDecorate %142 1 MatrixStride 16 OpMemberDecorate %142 2 Offset 192 OpMemberDecorate %142 3 Offset 208 OpMemberDecorate %142 4 Offset 224 OpMemberDecorate %142 5 Offset 232 OpMemberDecorate %142 6 Offset 236 OpMemberDecorate %142 7 Offset 240 OpMemberDecorate %142 8 Offset 244 OpMemberDecorate %142 9 Offset 248 OpMemberDecorate %143 0 Offset 0 OpDecorate %143 Block OpDecorate %145 DescriptorSet 0 OpDecorate %145 Binding 3 OpMemberDecorate %147 0 ColMajor OpMemberDecorate %147 0 Offset 0 OpMemberDecorate %147 0 MatrixStride 16 OpMemberDecorate %147 1 Offset 48 OpMemberDecorate %147 2 ColMajor OpMemberDecorate %147 2 Offset 64 OpMemberDecorate %147 2 MatrixStride 16 OpMemberDecorate %147 3 Offset 112 OpDecorate %148 ArrayStride 128 OpMemberDecorate %149 0 NonWritable OpMemberDecorate %149 0 Offset 0 OpDecorate %149 Block OpDecorate %151 DescriptorSet 0 OpDecorate %151 Binding 4 OpMemberDecorate %153 0 Offset 0 OpMemberDecorate %153 1 Offset 4 OpMemberDecorate %153 2 Offset 8 OpMemberDecorate %153 3 Offset 12 OpMemberDecorate %153 4 Offset 16 OpMemberDecorate %153 5 Offset 24 OpMemberDecorate %153 6 Offset 28 OpDecorate %154 ArrayStride 32 OpMemberDecorate %155 0 NonWritable OpMemberDecorate %155 0 Offset 0 OpDecorate %155 Block OpDecorate %157 DescriptorSet 0 OpDecorate %157 Binding 5 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 0 %7 = OpTypePointer Function %6 %8 = OpTypeBool %9 = OpTypeFunction %8 %7 %13 = OpTypeFloat 32 %14 = OpTypeVector %13 2 %15 = OpTypePointer Function %14 %16 = OpTypePointer Function %13 %17 = OpTypeVector %13 4 %18 = OpTypeFunction %17 %7 %15 %16 %27 = OpConstant %6 4294967295 %31 = OpTypeImage %13 2D 0 0 0 1 Unknown %32 = OpTypeRuntimeArray %31 %33 = OpTypePointer UniformConstant %32 %34 = OpVariable %33 UniformConstant %37 = OpTypePointer UniformConstant %31 %40 = OpTypeSampler %41 = OpTypePointer UniformConstant %40 %42 = OpVariable %41 UniformConstant %44 = OpTypeSampledImage %31 %51 = OpTypeVector %13 3 %52 = OpTypeStruct %14 %6 %51 %53 = OpTypePointer Input %52 %54 = OpVariable %53 Input %55 = OpTypeInt 32 1 %56 = OpConstant %55 1 %58 = OpTypePointer Input %6 %64 = OpConstant %55 0 %65 = OpConstant %13 0 %70 = OpTypePointer Input %14 %75 = OpConstant %6 0 %77 = OpConstant %13 0.5 %84 = OpConstant %13 0.800000012 %85 = OpConstantComposite %14 %77 %65 %86 = OpConstant %13 65504 %87 = OpConstant %13 3.40282347e+38 %88 = OpTypeVector %55 2 %89 = OpConstant %6 9 %90 = OpTypeArray %88 %89 %91 = OpConstantComposite %88 %64 %64 %92 = OpConstant %55 -1 %93 = OpConstantComposite %88 %92 %92 %94 = OpConstantComposite %88 %92 %56 %95 = OpConstantComposite %88 %56 %92 %96 = OpConstantComposite %88 %56 %56 %97 = OpConstantComposite %88 %56 %64 %98 = OpConstantComposite %88 %64 %56 %99 = OpConstantComposite %88 %92 %64 %100 = OpConstantComposite %88 %64 %92 %101 = OpConstantComposite %90 %91 %93 %94 %95 %96 %97 %98 %99 %100 %102 = OpTypeMatrix %51 3 %103 = OpConstant %6 6 %104 = OpTypeArray %102 %103 %105 = OpConstant %13 -1 %106 = OpConstantComposite %51 %65 %65 %105 %107 = OpConstantComposite %51 %65 %105 %65 %108 = OpConstant %13 1 %109 = OpConstantComposite %51 %108 %65 %65 %110 = OpConstantComposite %102 %106 %107 %109 %111 = OpConstantComposite %51 %65 %65 %108 %112 = OpConstantComposite %51 %105 %65 %65 %113 = OpConstantComposite %102 %111 %107 %112 %114 = OpConstantComposite %51 %65 %108 %65 %115 = OpConstantComposite %102 %109 %111 %114 %116 = OpConstantComposite %102 %109 %106 %107 %117 = OpConstantComposite %102 %109 %107 %111 %118 = OpConstantComposite %102 %112 %107 %106 %119 = OpConstantComposite %104 %110 %113 %115 %116 %117 %118 %120 = OpConstant %13 6 %121 = OpConstantComposite %17 %65 %65 %65 %65 %122 = OpConstant %13 0.25 %123 = OpConstant %13 0.379999995 %124 = OpConstant %13 0.479999989 %125 = OpConstant %13 0.00499999989 %126 = OpConstant %13 0.899999976 %127 = OpConstant %13 0.0399999991 %128 = OpConstant %13 1.5 %129 = OpConstant %13 0.0199999996 %130 = OpTypeRuntimeArray %13 %131 = OpTypeStruct %130 %132 = OpTypePointer StorageBuffer %131 %133 = OpVariable %132 StorageBuffer %134 = OpTypeStruct %6 %6 %6 %6 %135 = OpTypeRuntimeArray %134 %136 = OpTypeStruct %135 %137 = OpTypePointer StorageBuffer %136 %138 = OpVariable %137 StorageBuffer %139 = OpTypeMatrix %17 4 %140 = OpConstant %6 2 %141 = OpTypeArray %139 %140 %142 = OpTypeStruct %141 %139 %17 %17 %14 %13 %13 %13 %13 %55 %143 = OpTypeStruct %142 %144 = OpTypePointer Uniform %143 %145 = OpVariable %144 Uniform %146 = OpTypeMatrix %17 3 %147 = OpTypeStruct %146 %17 %146 %17 %148 = OpTypeRuntimeArray %147 %149 = OpTypeStruct %148 %150 = OpTypePointer StorageBuffer %149 %151 = OpVariable %150 StorageBuffer %152 = OpTypeVector %6 2 %153 = OpTypeStruct %6 %6 %6 %6 %152 %6 %13 %154 = OpTypeRuntimeArray %153 %155 = OpTypeStruct %154 %156 = OpTypePointer StorageBuffer %155 %157 = OpVariable %156 StorageBuffer %4 = OpFunction %2 None %3 %5 = OpLabel %83 = OpFunctionCall %2 %24 OpReturn OpFunctionEnd %11 = OpFunction %8 None %9 %10 = OpFunctionParameter %7 %12 = OpLabel %26 = OpLoad %6 %10 %28 = OpINotEqual %8 %26 %27 OpReturnValue %28 OpFunctionEnd %22 = OpFunction %17 None %18 %19 = OpFunctionParameter %7 %20 = OpFunctionParameter %15 %21 = OpFunctionParameter %16 %23 = OpLabel %35 = OpLoad %6 %19 %36 = OpCopyObject %6 %35 %38 = OpAccessChain %37 %34 %36 %39 = OpLoad %31 %38 %43 = OpLoad %40 %42 %45 = OpSampledImage %44 %39 %43 %46 = OpLoad %14 %20 %47 = OpLoad %13 %21 %48 = OpImageSampleExplicitLod %17 %45 %46 Lod %47 OpReturnValue %48 OpFunctionEnd %24 = OpFunction %2 None %3 %25 = OpLabel %57 = OpVariable %7 Function %66 = OpVariable %7 Function %69 = OpVariable %15 Function %73 = OpVariable %16 Function %59 = OpAccessChain %58 %54 %56 %60 = OpLoad %6 %59 OpStore %57 %60 %61 = OpFunctionCall %8 %11 %57 OpSelectionMerge %63 None OpBranchConditional %61 %62 %63 %62 = OpLabel %67 = OpAccessChain %58 %54 %56 %68 = OpLoad %6 %67 OpStore %66 %68 %71 = OpAccessChain %70 %54 %64 %72 = OpLoad %14 %71 OpStore %69 %72 OpStore %73 %65 %74 = OpFunctionCall %17 %22 %66 %69 %73 %76 = OpCompositeExtract %13 %74 0 %78 = OpFOrdLessThanEqual %8 %76 %77 OpBranch %63 %63 = OpLabel %79 = OpPhi %8 %61 %25 %78 %62 OpSelectionMerge %81 None OpBranchConditional %79 %80 %81 %80 = OpLabel OpKill %81 = OpLabel OpReturn OpFunctionEnd End SPIR-V Converted MSL: #pragma clang diagnostic ignored "-Wmissing-prototypes" #pragma clang diagnostic ignored "-Wmissing-braces" #include #include using namespace metal; template struct spvUnsafeArray { T elements[Num ? Num : 1]; thread T& operator [] (size_t pos) thread { return elements[pos]; } constexpr const thread T& operator [] (size_t pos) const thread { return elements[pos]; } device T& operator [] (size_t pos) device { return elements[pos]; } constexpr const device T& operator [] (size_t pos) const device { return elements[pos]; } constexpr const constant T& operator [] (size_t pos) const constant { return elements[pos]; } threadgroup T& operator [] (size_t pos) threadgroup { return elements[pos]; } constexpr const threadgroup T& operator [] (size_t pos) const threadgroup { return elements[pos]; } }; struct VertexData { float2 uv; uint maskSamplerId; float3 vViewPos; }; struct TextureTransformsBuffer { float TextureTransforms[1]; }; struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct TextureHandles { TextureSamplerIds samplerIds[1]; }; struct ShadowMapParameters { float4x4 CameraMatrices[2]; float4x4 ViewInverse; float4 WindScroll; float4 WindVec; float2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct ShadowMapParamsBuffer { ShadowMapParameters params; }; struct InstanceData { float3x4 transposeTransform; float4 customData0; float3x4 transposeInverseTransform; float4 customData1; }; struct InstanceDataBuffer { InstanceData instanceData[1]; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uint2 EmissiveColorPacked; uint avgColor; float padding; }; struct MaterialsBuffer { Material Materials[1]; }; struct spvDescriptorSetBuffer0 { const device TextureTransformsBuffer* m_133 [[id(0)]]; sampler SceneTextureSampler [[id(1)]]; const device TextureHandles* m_138 [[id(2)]]; constant ShadowMapParamsBuffer* m_145 [[id(3)]]; const device InstanceDataBuffer* m_151 [[id(4)]]; const device MaterialsBuffer* m_157 [[id(5)]]; }; struct spvDescriptorSetBuffer1 { array, 65536> SceneTextures [[id(0)]]; }; constant spvUnsafeArray _101 = spvUnsafeArray({ int2(0), int2(-1), int2(-1, 1), int2(1, -1), int2(1), int2(1, 0), int2(0, 1), int2(-1, 0), int2(0, -1) }); constant spvUnsafeArray _119 = spvUnsafeArray({ float3x3(float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0), float3(1.0, 0.0, 0.0)), float3x3(float3(0.0, 0.0, 1.0), float3(0.0, -1.0, 0.0), float3(-1.0, 0.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, 1.0), float3(0.0, 1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, 1.0)), float3x3(float3(-1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, -1.0)) }); struct main0_in { float2 inoutData_uv [[user(locn0)]]; uint inoutData_maskSamplerId [[user(locn1)]]; float3 inoutData_vViewPos [[user(locn2)]]; }; static inline __attribute__((always_inline)) bool isValidSamplerId(thread const uint& samplerId) { return samplerId != 4294967295u; } static inline __attribute__((always_inline)) float4 getTextureLodValue(thread const uint& samplerId, thread const float2& uv, thread const float& lod, constant array, 65536>& SceneTextures, sampler SceneTextureSampler) { uint _36 = samplerId; return SceneTextures[_36].sample(SceneTextureSampler, uv, level(lod)); } static inline __attribute__((always_inline)) void opaqueMaskedDoubleSided(constant array, 65536>& SceneTextures, sampler SceneTextureSampler, thread VertexData& inoutData) { uint param = inoutData.maskSamplerId; bool _61 = isValidSamplerId(param); bool _79; if (_61) { uint param_1 = inoutData.maskSamplerId; float2 param_2 = inoutData.uv; float param_3 = 0.0; _79 = getTextureLodValue(param_1, param_2, param_3, SceneTextures, SceneTextureSampler).x <= 0.5; } else { _79 = _61; } if (_79) { discard_fragment(); } } fragment void main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]]) { VertexData inoutData = {}; inoutData.uv = in.inoutData_uv; inoutData.maskSamplerId = in.inoutData_maskSamplerId; inoutData.vViewPos = in.inoutData_vViewPos; opaqueMaskedDoubleSided(spvDescriptorSet1.SceneTextures, spvDescriptorSet0.SceneTextureSampler, inoutData); } End MSL Estimated original GLSL: #version 460 #extension GL_EXT_nonuniform_qualifier : require struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct ShadowMapParameters { mat4 CameraMatrices[2]; mat4 ViewInverse; vec4 WindScroll; vec4 WindVec; vec2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct InstanceData { mat3x4 transposeTransform; vec4 customData0; mat3x4 transposeInverseTransform; vec4 customData1; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uvec2 EmissiveColorPacked; uint avgColor; float padding; }; layout(set = 0, binding = 0, std430) readonly buffer TextureTransformsBuffer { float TextureTransforms[]; } _133; layout(set = 0, binding = 2, std430) readonly buffer TextureHandles { TextureSamplerIds samplerIds[]; } _138; layout(set = 0, binding = 3, std140) uniform ShadowMapParamsBuffer { ShadowMapParameters params; } _145; layout(set = 0, binding = 4, std430) readonly buffer InstanceDataBuffer { InstanceData instanceData[]; } _151; layout(set = 0, binding = 5, std430) readonly buffer MaterialsBuffer { Material Materials[]; } _157; layout(set = 1, binding = 1) uniform texture2D SceneTextures[]; layout(set = 0, binding = 1) uniform sampler SceneTextureSampler; layout(location = 0) in VertexData { vec2 uv; flat uint maskSamplerId; vec3 vViewPos; } inoutData; bool isValidSamplerId(uint samplerId) { return samplerId != 4294967295u; } vec4 getTextureLodValue(uint samplerId, vec2 uv, float lod) { uint _36 = samplerId; return textureLod(sampler2D(SceneTextures[_36], SceneTextureSampler), uv, lod); } void opaqueMaskedDoubleSided() { uint param = inoutData.maskSamplerId; bool _61 = isValidSamplerId(param); bool _79; if (_61) { uint param_1 = inoutData.maskSamplerId; vec2 param_2 = inoutData.uv; float param_3 = 0.0; _79 = getTextureLodValue(param_1, param_2, param_3).x <= 0.5; } else { _79 = _61; } if (_79) { discard; } } void main() { opaqueMaskedDoubleSided(); } End GLSL ```
Broken version, MoltenVK 1.2.10. GLSL source is the same as in 1.2.6 ``` [mvk-info] Compiling Metal shader with FastMath enabled. [mvk-info] Converting SPIR-V: ; SPIR-V ; Version: 1.4 ; Generator: Google Shaderc over Glslang; 11 ; Bound: 158 ; Schema: 0 OpCapability Shader OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability SampledImageArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %34 %42 %54 %133 %138 %145 %151 %157 OpExecutionMode %4 OriginUpperLeft OpSource GLSL 460 OpSourceExtension "GL_ARB_enhanced_layouts" OpSourceExtension "GL_EXT_debug_printf" OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" OpSourceExtension "GL_GOOGLE_include_directive" OpName %4 "main" OpName %11 "isValidSamplerId(u1;" OpName %10 "samplerId" OpName %22 "getTextureLodValue(u1;vf2;f1;" OpName %19 "samplerId" OpName %20 "uv" OpName %21 "lod" OpName %24 "opaqueMaskedDoubleSided(" OpName %34 "SceneTextures" OpName %42 "SceneTextureSampler" OpName %52 "VertexData" OpMemberName %52 0 "uv" OpMemberName %52 1 "maskSamplerId" OpMemberName %52 2 "vViewPos" OpName %54 "inoutData" OpName %57 "param" OpName %66 "param" OpName %69 "param" OpName %73 "param" OpName %131 "TextureTransformsBuffer" OpMemberName %131 0 "TextureTransforms" OpName %133 "" OpName %134 "TextureSamplerIds" OpMemberName %134 0 "AlbedoId" OpMemberName %134 1 "NormalId" OpMemberName %134 2 "RoughnessId" OpMemberName %134 3 "MaskId" OpName %136 "TextureHandles" OpMemberName %136 0 "samplerIds" OpName %138 "" OpName %142 "ShadowMapParameters" OpMemberName %142 0 "CameraMatrices" OpMemberName %142 1 "ViewInverse" OpMemberName %142 2 "WindScroll" OpMemberName %142 3 "WindVec" OpMemberName %142 4 "NearFar" OpMemberName %142 5 "BacksideClipDistanceSq" OpMemberName %142 6 "ShadowOffsetFactor" OpMemberName %142 7 "ShadowOffsetUnits" OpMemberName %142 8 "ShadowOffsetDynamic" OpMemberName %142 9 "ProjectionType" OpName %143 "ShadowMapParamsBuffer" OpMemberName %143 0 "params" OpName %145 "" OpName %147 "InstanceData" OpMemberName %147 0 "transposeTransform" OpMemberName %147 1 "customData0" OpMemberName %147 2 "transposeInverseTransform" OpMemberName %147 3 "customData1" OpName %149 "InstanceDataBuffer" OpMemberName %149 0 "instanceData" OpName %151 "" OpName %153 "Material" OpMemberName %153 0 "BaseColorOpacityPacked" OpMemberName %153 1 "TintColorRoughnessPacked" OpMemberName %153 2 "MetallicSpecularImageFadeTypePacked" OpMemberName %153 3 "texTransformTypeIsVideoTexDispAmountTexClass" OpMemberName %153 4 "EmissiveColorPacked" OpMemberName %153 5 "avgColor" OpMemberName %153 6 "padding" OpName %155 "MaterialsBuffer" OpMemberName %155 0 "Materials" OpName %157 "" OpDecorate %34 DescriptorSet 1 OpDecorate %34 Binding 1 OpDecorate %36 NonUniform OpDecorate %38 NonUniform OpDecorate %39 NonUniform OpDecorate %42 DescriptorSet 0 OpDecorate %42 Binding 1 OpMemberDecorate %52 1 Flat OpDecorate %52 Block OpDecorate %54 Location 0 OpDecorate %130 ArrayStride 4 OpMemberDecorate %131 0 NonWritable OpMemberDecorate %131 0 Offset 0 OpDecorate %131 Block OpDecorate %133 DescriptorSet 0 OpDecorate %133 Binding 0 OpMemberDecorate %134 0 Offset 0 OpMemberDecorate %134 1 Offset 4 OpMemberDecorate %134 2 Offset 8 OpMemberDecorate %134 3 Offset 12 OpDecorate %135 ArrayStride 16 OpMemberDecorate %136 0 NonWritable OpMemberDecorate %136 0 Offset 0 OpDecorate %136 Block OpDecorate %138 DescriptorSet 0 OpDecorate %138 Binding 2 OpDecorate %141 ArrayStride 64 OpMemberDecorate %142 0 ColMajor OpMemberDecorate %142 0 Offset 0 OpMemberDecorate %142 0 MatrixStride 16 OpMemberDecorate %142 1 ColMajor OpMemberDecorate %142 1 Offset 128 OpMemberDecorate %142 1 MatrixStride 16 OpMemberDecorate %142 2 Offset 192 OpMemberDecorate %142 3 Offset 208 OpMemberDecorate %142 4 Offset 224 OpMemberDecorate %142 5 Offset 232 OpMemberDecorate %142 6 Offset 236 OpMemberDecorate %142 7 Offset 240 OpMemberDecorate %142 8 Offset 244 OpMemberDecorate %142 9 Offset 248 OpMemberDecorate %143 0 Offset 0 OpDecorate %143 Block OpDecorate %145 DescriptorSet 0 OpDecorate %145 Binding 3 OpMemberDecorate %147 0 ColMajor OpMemberDecorate %147 0 Offset 0 OpMemberDecorate %147 0 MatrixStride 16 OpMemberDecorate %147 1 Offset 48 OpMemberDecorate %147 2 ColMajor OpMemberDecorate %147 2 Offset 64 OpMemberDecorate %147 2 MatrixStride 16 OpMemberDecorate %147 3 Offset 112 OpDecorate %148 ArrayStride 128 OpMemberDecorate %149 0 NonWritable OpMemberDecorate %149 0 Offset 0 OpDecorate %149 Block OpDecorate %151 DescriptorSet 0 OpDecorate %151 Binding 4 OpMemberDecorate %153 0 Offset 0 OpMemberDecorate %153 1 Offset 4 OpMemberDecorate %153 2 Offset 8 OpMemberDecorate %153 3 Offset 12 OpMemberDecorate %153 4 Offset 16 OpMemberDecorate %153 5 Offset 24 OpMemberDecorate %153 6 Offset 28 OpDecorate %154 ArrayStride 32 OpMemberDecorate %155 0 NonWritable OpMemberDecorate %155 0 Offset 0 OpDecorate %155 Block OpDecorate %157 DescriptorSet 0 OpDecorate %157 Binding 5 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 0 %7 = OpTypePointer Function %6 %8 = OpTypeBool %9 = OpTypeFunction %8 %7 %13 = OpTypeFloat 32 %14 = OpTypeVector %13 2 %15 = OpTypePointer Function %14 %16 = OpTypePointer Function %13 %17 = OpTypeVector %13 4 %18 = OpTypeFunction %17 %7 %15 %16 %27 = OpConstant %6 4294967295 %31 = OpTypeImage %13 2D 0 0 0 1 Unknown %32 = OpTypeRuntimeArray %31 %33 = OpTypePointer UniformConstant %32 %34 = OpVariable %33 UniformConstant %37 = OpTypePointer UniformConstant %31 %40 = OpTypeSampler %41 = OpTypePointer UniformConstant %40 %42 = OpVariable %41 UniformConstant %44 = OpTypeSampledImage %31 %51 = OpTypeVector %13 3 %52 = OpTypeStruct %14 %6 %51 %53 = OpTypePointer Input %52 %54 = OpVariable %53 Input %55 = OpTypeInt 32 1 %56 = OpConstant %55 1 %58 = OpTypePointer Input %6 %64 = OpConstant %55 0 %65 = OpConstant %13 0 %70 = OpTypePointer Input %14 %75 = OpConstant %6 0 %77 = OpConstant %13 0.5 %84 = OpConstant %13 0.800000012 %85 = OpConstantComposite %14 %77 %65 %86 = OpConstant %13 65504 %87 = OpConstant %13 3.40282347e+38 %88 = OpTypeVector %55 2 %89 = OpConstant %6 9 %90 = OpTypeArray %88 %89 %91 = OpConstantComposite %88 %64 %64 %92 = OpConstant %55 -1 %93 = OpConstantComposite %88 %92 %92 %94 = OpConstantComposite %88 %92 %56 %95 = OpConstantComposite %88 %56 %92 %96 = OpConstantComposite %88 %56 %56 %97 = OpConstantComposite %88 %56 %64 %98 = OpConstantComposite %88 %64 %56 %99 = OpConstantComposite %88 %92 %64 %100 = OpConstantComposite %88 %64 %92 %101 = OpConstantComposite %90 %91 %93 %94 %95 %96 %97 %98 %99 %100 %102 = OpTypeMatrix %51 3 %103 = OpConstant %6 6 %104 = OpTypeArray %102 %103 %105 = OpConstant %13 -1 %106 = OpConstantComposite %51 %65 %65 %105 %107 = OpConstantComposite %51 %65 %105 %65 %108 = OpConstant %13 1 %109 = OpConstantComposite %51 %108 %65 %65 %110 = OpConstantComposite %102 %106 %107 %109 %111 = OpConstantComposite %51 %65 %65 %108 %112 = OpConstantComposite %51 %105 %65 %65 %113 = OpConstantComposite %102 %111 %107 %112 %114 = OpConstantComposite %51 %65 %108 %65 %115 = OpConstantComposite %102 %109 %111 %114 %116 = OpConstantComposite %102 %109 %106 %107 %117 = OpConstantComposite %102 %109 %107 %111 %118 = OpConstantComposite %102 %112 %107 %106 %119 = OpConstantComposite %104 %110 %113 %115 %116 %117 %118 %120 = OpConstant %13 6 %121 = OpConstantComposite %17 %65 %65 %65 %65 %122 = OpConstant %13 0.25 %123 = OpConstant %13 0.379999995 %124 = OpConstant %13 0.479999989 %125 = OpConstant %13 0.00499999989 %126 = OpConstant %13 0.899999976 %127 = OpConstant %13 0.0399999991 %128 = OpConstant %13 1.5 %129 = OpConstant %13 0.0199999996 %130 = OpTypeRuntimeArray %13 %131 = OpTypeStruct %130 %132 = OpTypePointer StorageBuffer %131 %133 = OpVariable %132 StorageBuffer %134 = OpTypeStruct %6 %6 %6 %6 %135 = OpTypeRuntimeArray %134 %136 = OpTypeStruct %135 %137 = OpTypePointer StorageBuffer %136 %138 = OpVariable %137 StorageBuffer %139 = OpTypeMatrix %17 4 %140 = OpConstant %6 2 %141 = OpTypeArray %139 %140 %142 = OpTypeStruct %141 %139 %17 %17 %14 %13 %13 %13 %13 %55 %143 = OpTypeStruct %142 %144 = OpTypePointer Uniform %143 %145 = OpVariable %144 Uniform %146 = OpTypeMatrix %17 3 %147 = OpTypeStruct %146 %17 %146 %17 %148 = OpTypeRuntimeArray %147 %149 = OpTypeStruct %148 %150 = OpTypePointer StorageBuffer %149 %151 = OpVariable %150 StorageBuffer %152 = OpTypeVector %6 2 %153 = OpTypeStruct %6 %6 %6 %6 %152 %6 %13 %154 = OpTypeRuntimeArray %153 %155 = OpTypeStruct %154 %156 = OpTypePointer StorageBuffer %155 %157 = OpVariable %156 StorageBuffer %4 = OpFunction %2 None %3 %5 = OpLabel %83 = OpFunctionCall %2 %24 OpReturn OpFunctionEnd %11 = OpFunction %8 None %9 %10 = OpFunctionParameter %7 %12 = OpLabel %26 = OpLoad %6 %10 %28 = OpINotEqual %8 %26 %27 OpReturnValue %28 OpFunctionEnd %22 = OpFunction %17 None %18 %19 = OpFunctionParameter %7 %20 = OpFunctionParameter %15 %21 = OpFunctionParameter %16 %23 = OpLabel %35 = OpLoad %6 %19 %36 = OpCopyObject %6 %35 %38 = OpAccessChain %37 %34 %36 %39 = OpLoad %31 %38 %43 = OpLoad %40 %42 %45 = OpSampledImage %44 %39 %43 %46 = OpLoad %14 %20 %47 = OpLoad %13 %21 %48 = OpImageSampleExplicitLod %17 %45 %46 Lod %47 OpReturnValue %48 OpFunctionEnd %24 = OpFunction %2 None %3 %25 = OpLabel %57 = OpVariable %7 Function %66 = OpVariable %7 Function %69 = OpVariable %15 Function %73 = OpVariable %16 Function %59 = OpAccessChain %58 %54 %56 %60 = OpLoad %6 %59 OpStore %57 %60 %61 = OpFunctionCall %8 %11 %57 OpSelectionMerge %63 None OpBranchConditional %61 %62 %63 %62 = OpLabel %67 = OpAccessChain %58 %54 %56 %68 = OpLoad %6 %67 OpStore %66 %68 %71 = OpAccessChain %70 %54 %64 %72 = OpLoad %14 %71 OpStore %69 %72 OpStore %73 %65 %74 = OpFunctionCall %17 %22 %66 %69 %73 %76 = OpCompositeExtract %13 %74 0 %78 = OpFOrdLessThanEqual %8 %76 %77 OpBranch %63 %63 = OpLabel %79 = OpPhi %8 %61 %25 %78 %62 OpSelectionMerge %81 None OpBranchConditional %79 %80 %81 %80 = OpLabel OpKill %81 = OpLabel OpReturn OpFunctionEnd End SPIR-V Converted MSL: #pragma clang diagnostic ignored "-Wmissing-prototypes" #pragma clang diagnostic ignored "-Wmissing-braces" #include #include using namespace metal; template struct spvUnsafeArray { T elements[Num ? Num : 1]; thread T& operator [] (size_t pos) thread { return elements[pos]; } constexpr const thread T& operator [] (size_t pos) const thread { return elements[pos]; } device T& operator [] (size_t pos) device { return elements[pos]; } constexpr const device T& operator [] (size_t pos) const device { return elements[pos]; } constexpr const constant T& operator [] (size_t pos) const constant { return elements[pos]; } threadgroup T& operator [] (size_t pos) threadgroup { return elements[pos]; } constexpr const threadgroup T& operator [] (size_t pos) const threadgroup { return elements[pos]; } }; struct VertexData { float2 uv; uint maskSamplerId; float3 vViewPos; }; struct TextureTransformsBuffer { float TextureTransforms[1]; }; struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct TextureHandles { TextureSamplerIds samplerIds[1]; }; struct ShadowMapParameters { float4x4 CameraMatrices[2]; float4x4 ViewInverse; float4 WindScroll; float4 WindVec; float2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct ShadowMapParamsBuffer { ShadowMapParameters params; }; struct InstanceData { float3x4 transposeTransform; float4 customData0; float3x4 transposeInverseTransform; float4 customData1; }; struct InstanceDataBuffer { InstanceData instanceData[1]; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uint2 EmissiveColorPacked; uint avgColor; float padding; }; struct MaterialsBuffer { Material Materials[1]; }; struct spvDescriptorSetBuffer0 { constant void* _m0_pad [[id(0)]]; constant void* _m1_pad [[id(1)]]; sampler SceneTextureSampler [[id(2)]]; }; struct spvDescriptorSetBuffer1 { array, 1> SceneTextures [[id(0)]]; }; constant spvUnsafeArray _101 = spvUnsafeArray({ int2(0), int2(-1), int2(-1, 1), int2(1, -1), int2(1), int2(1, 0), int2(0, 1), int2(-1, 0), int2(0, -1) }); constant spvUnsafeArray _119 = spvUnsafeArray({ float3x3(float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0), float3(1.0, 0.0, 0.0)), float3x3(float3(0.0, 0.0, 1.0), float3(0.0, -1.0, 0.0), float3(-1.0, 0.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, 1.0), float3(0.0, 1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, 1.0)), float3x3(float3(-1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, -1.0)) }); struct main0_in { float2 inoutData_uv [[user(locn0)]]; uint inoutData_maskSamplerId [[user(locn1)]]; float3 inoutData_vViewPos [[user(locn2)]]; }; static inline __attribute__((always_inline)) bool isValidSamplerId(thread const uint& samplerId) { return samplerId != 4294967295u; } static inline __attribute__((always_inline)) float4 getTextureLodValue(thread const uint& samplerId, thread const float2& uv, thread const float& lod, constant array, 1>& SceneTextures, sampler SceneTextureSampler) { uint _36 = samplerId; return SceneTextures[_36].sample(SceneTextureSampler, uv, level(lod)); } static inline __attribute__((always_inline)) void opaqueMaskedDoubleSided(constant array, 1>& SceneTextures, sampler SceneTextureSampler, thread VertexData& inoutData) { uint param = inoutData.maskSamplerId; bool _61 = isValidSamplerId(param); bool _79; if (_61) { uint param_1 = inoutData.maskSamplerId; float2 param_2 = inoutData.uv; float param_3 = 0.0; _79 = getTextureLodValue(param_1, param_2, param_3, SceneTextures, SceneTextureSampler).x <= 0.5; } else { _79 = _61; } if (_79) { discard_fragment(); } } fragment void main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]]) { VertexData inoutData = {}; inoutData.uv = in.inoutData_uv; inoutData.maskSamplerId = in.inoutData_maskSamplerId; inoutData.vViewPos = in.inoutData_vViewPos; opaqueMaskedDoubleSided(spvDescriptorSet1.SceneTextures, spvDescriptorSet0.SceneTextureSampler, inoutData); } End MSL Estimated original GLSL: #version 460 #extension GL_EXT_nonuniform_qualifier : require struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct ShadowMapParameters { mat4 CameraMatrices[2]; mat4 ViewInverse; vec4 WindScroll; vec4 WindVec; vec2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct InstanceData { mat3x4 transposeTransform; vec4 customData0; mat3x4 transposeInverseTransform; vec4 customData1; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uvec2 EmissiveColorPacked; uint avgColor; float padding; }; layout(set = 0, binding = 0, std430) readonly buffer TextureTransformsBuffer { float TextureTransforms[]; } _133; layout(set = 0, binding = 2, std430) readonly buffer TextureHandles { TextureSamplerIds samplerIds[]; } _138; layout(set = 0, binding = 3, std140) uniform ShadowMapParamsBuffer { ShadowMapParameters params; } _145; layout(set = 0, binding = 4, std430) readonly buffer InstanceDataBuffer { InstanceData instanceData[]; } _151; layout(set = 0, binding = 5, std430) readonly buffer MaterialsBuffer { Material Materials[]; } _157; layout(set = 1, binding = 1) uniform texture2D SceneTextures[]; layout(set = 0, binding = 1) uniform sampler SceneTextureSampler; layout(location = 0) in VertexData { vec2 uv; flat uint maskSamplerId; vec3 vViewPos; } inoutData; bool isValidSamplerId(uint samplerId) { return samplerId != 4294967295u; } vec4 getTextureLodValue(uint samplerId, vec2 uv, float lod) { uint _36 = samplerId; return textureLod(sampler2D(SceneTextures[_36], SceneTextureSampler), uv, lod); } void opaqueMaskedDoubleSided() { uint param = inoutData.maskSamplerId; bool _61 = isValidSamplerId(param); bool _79; if (_61) { uint param_1 = inoutData.maskSamplerId; vec2 param_2 = inoutData.uv; float param_3 = 0.0; _79 = getTextureLodValue(param_1, param_2, param_3).x <= 0.5; } else { _79 = _61; } if (_79) { discard; } } void main() { opaqueMaskedDoubleSided(); } End GLSL ```
Shader with workaround, working in MoltenVK 1.2.10 ``` [mvk-info] Compiling Metal shader with FastMath enabled. [mvk-info] Converting SPIR-V: ; SPIR-V ; Version: 1.4 ; Generator: Google Shaderc over Glslang; 11 ; Bound: 170 ; Schema: 0 OpCapability Shader OpCapability ShaderNonUniform OpCapability RuntimeDescriptorArray OpCapability SampledImageArrayNonUniformIndexing OpExtension "SPV_EXT_descriptor_indexing" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" %35 %43 %55 %89 %146 %151 %157 %163 %169 OpExecutionMode %4 OriginUpperLeft OpExecutionMode %4 DepthReplacing OpSource GLSL 460 OpSourceExtension "GL_ARB_enhanced_layouts" OpSourceExtension "GL_EXT_debug_printf" OpSourceExtension "GL_EXT_nonuniform_qualifier" OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" OpSourceExtension "GL_GOOGLE_include_directive" OpName %4 "main" OpName %11 "isValidSamplerId(u1;" OpName %10 "samplerId" OpName %22 "getTextureLodValue(u1;vf2;f1;" OpName %19 "samplerId" OpName %20 "uv" OpName %21 "lod" OpName %25 "opaqueMaskedDoubleSided(" OpName %35 "SceneTextures" OpName %43 "SceneTextureSampler" OpName %53 "VertexData" OpMemberName %53 0 "uv" OpMemberName %53 1 "maskSamplerId" OpMemberName %53 2 "vViewPos" OpName %55 "inoutData" OpName %58 "param" OpName %67 "param" OpName %70 "param" OpName %74 "param" OpName %89 "gl_FragDepth" OpName %144 "TextureTransformsBuffer" OpMemberName %144 0 "TextureTransforms" OpName %146 "" OpName %147 "TextureSamplerIds" OpMemberName %147 0 "AlbedoId" OpMemberName %147 1 "NormalId" OpMemberName %147 2 "RoughnessId" OpMemberName %147 3 "MaskId" OpName %149 "TextureHandles" OpMemberName %149 0 "samplerIds" OpName %151 "" OpName %154 "ShadowMapParameters" OpMemberName %154 0 "CameraMatrices" OpMemberName %154 1 "ViewInverse" OpMemberName %154 2 "WindScroll" OpMemberName %154 3 "WindVec" OpMemberName %154 4 "NearFar" OpMemberName %154 5 "BacksideClipDistanceSq" OpMemberName %154 6 "ShadowOffsetFactor" OpMemberName %154 7 "ShadowOffsetUnits" OpMemberName %154 8 "ShadowOffsetDynamic" OpMemberName %154 9 "ProjectionType" OpName %155 "ShadowMapParamsBuffer" OpMemberName %155 0 "params" OpName %157 "" OpName %159 "InstanceData" OpMemberName %159 0 "transposeTransform" OpMemberName %159 1 "customData0" OpMemberName %159 2 "transposeInverseTransform" OpMemberName %159 3 "customData1" OpName %161 "InstanceDataBuffer" OpMemberName %161 0 "instanceData" OpName %163 "" OpName %165 "Material" OpMemberName %165 0 "BaseColorOpacityPacked" OpMemberName %165 1 "TintColorRoughnessPacked" OpMemberName %165 2 "MetallicSpecularImageFadeTypePacked" OpMemberName %165 3 "texTransformTypeIsVideoTexDispAmountTexClass" OpMemberName %165 4 "EmissiveColorPacked" OpMemberName %165 5 "avgColor" OpMemberName %165 6 "padding" OpName %167 "MaterialsBuffer" OpMemberName %167 0 "Materials" OpName %169 "" OpDecorate %35 DescriptorSet 1 OpDecorate %35 Binding 1 OpDecorate %37 NonUniform OpDecorate %39 NonUniform OpDecorate %40 NonUniform OpDecorate %43 DescriptorSet 0 OpDecorate %43 Binding 1 OpMemberDecorate %53 1 Flat OpDecorate %53 Block OpDecorate %55 Location 0 OpDecorate %89 BuiltIn FragDepth OpDecorate %143 ArrayStride 4 OpMemberDecorate %144 0 NonWritable OpMemberDecorate %144 0 Offset 0 OpDecorate %144 Block OpDecorate %146 DescriptorSet 0 OpDecorate %146 Binding 0 OpMemberDecorate %147 0 Offset 0 OpMemberDecorate %147 1 Offset 4 OpMemberDecorate %147 2 Offset 8 OpMemberDecorate %147 3 Offset 12 OpDecorate %148 ArrayStride 16 OpMemberDecorate %149 0 NonWritable OpMemberDecorate %149 0 Offset 0 OpDecorate %149 Block OpDecorate %151 DescriptorSet 0 OpDecorate %151 Binding 2 OpDecorate %153 ArrayStride 64 OpMemberDecorate %154 0 ColMajor OpMemberDecorate %154 0 Offset 0 OpMemberDecorate %154 0 MatrixStride 16 OpMemberDecorate %154 1 ColMajor OpMemberDecorate %154 1 Offset 128 OpMemberDecorate %154 1 MatrixStride 16 OpMemberDecorate %154 2 Offset 192 OpMemberDecorate %154 3 Offset 208 OpMemberDecorate %154 4 Offset 224 OpMemberDecorate %154 5 Offset 232 OpMemberDecorate %154 6 Offset 236 OpMemberDecorate %154 7 Offset 240 OpMemberDecorate %154 8 Offset 244 OpMemberDecorate %154 9 Offset 248 OpMemberDecorate %155 0 Offset 0 OpDecorate %155 Block OpDecorate %157 DescriptorSet 0 OpDecorate %157 Binding 3 OpMemberDecorate %159 0 ColMajor OpMemberDecorate %159 0 Offset 0 OpMemberDecorate %159 0 MatrixStride 16 OpMemberDecorate %159 1 Offset 48 OpMemberDecorate %159 2 ColMajor OpMemberDecorate %159 2 Offset 64 OpMemberDecorate %159 2 MatrixStride 16 OpMemberDecorate %159 3 Offset 112 OpDecorate %160 ArrayStride 128 OpMemberDecorate %161 0 NonWritable OpMemberDecorate %161 0 Offset 0 OpDecorate %161 Block OpDecorate %163 DescriptorSet 0 OpDecorate %163 Binding 4 OpMemberDecorate %165 0 Offset 0 OpMemberDecorate %165 1 Offset 4 OpMemberDecorate %165 2 Offset 8 OpMemberDecorate %165 3 Offset 12 OpMemberDecorate %165 4 Offset 16 OpMemberDecorate %165 5 Offset 24 OpMemberDecorate %165 6 Offset 28 OpDecorate %166 ArrayStride 32 OpMemberDecorate %167 0 NonWritable OpMemberDecorate %167 0 Offset 0 OpDecorate %167 Block OpDecorate %169 DescriptorSet 0 OpDecorate %169 Binding 5 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 0 %7 = OpTypePointer Function %6 %8 = OpTypeBool %9 = OpTypeFunction %8 %7 %13 = OpTypeFloat 32 %14 = OpTypeVector %13 2 %15 = OpTypePointer Function %14 %16 = OpTypePointer Function %13 %17 = OpTypeVector %13 4 %18 = OpTypeFunction %17 %7 %15 %16 %24 = OpTypeFunction %13 %28 = OpConstant %6 4294967295 %32 = OpTypeImage %13 2D 0 0 0 1 Unknown %33 = OpTypeRuntimeArray %32 %34 = OpTypePointer UniformConstant %33 %35 = OpVariable %34 UniformConstant %38 = OpTypePointer UniformConstant %32 %41 = OpTypeSampler %42 = OpTypePointer UniformConstant %41 %43 = OpVariable %42 UniformConstant %45 = OpTypeSampledImage %32 %52 = OpTypeVector %13 3 %53 = OpTypeStruct %14 %6 %52 %54 = OpTypePointer Input %53 %55 = OpVariable %54 Input %56 = OpTypeInt 32 1 %57 = OpConstant %56 1 %59 = OpTypePointer Input %6 %65 = OpConstant %56 0 %66 = OpConstant %13 0 %71 = OpTypePointer Input %14 %76 = OpConstant %6 0 %78 = OpConstant %13 0.5 %85 = OpConstant %13 1 %88 = OpTypePointer Output %13 %89 = OpVariable %88 Output %90 = OpConstant %56 2 %91 = OpConstant %6 2 %92 = OpTypePointer Input %13 %98 = OpConstant %13 0.800000012 %99 = OpConstantComposite %14 %78 %66 %100 = OpConstant %13 65504 %101 = OpConstant %13 3.40282347e+38 %102 = OpTypeVector %56 2 %103 = OpConstant %6 9 %104 = OpTypeArray %102 %103 %105 = OpConstantComposite %102 %65 %65 %106 = OpConstant %56 -1 %107 = OpConstantComposite %102 %106 %106 %108 = OpConstantComposite %102 %106 %57 %109 = OpConstantComposite %102 %57 %106 %110 = OpConstantComposite %102 %57 %57 %111 = OpConstantComposite %102 %57 %65 %112 = OpConstantComposite %102 %65 %57 %113 = OpConstantComposite %102 %106 %65 %114 = OpConstantComposite %102 %65 %106 %115 = OpConstantComposite %104 %105 %107 %108 %109 %110 %111 %112 %113 %114 %116 = OpTypeMatrix %52 3 %117 = OpConstant %6 6 %118 = OpTypeArray %116 %117 %119 = OpConstant %13 -1 %120 = OpConstantComposite %52 %66 %66 %119 %121 = OpConstantComposite %52 %66 %119 %66 %122 = OpConstantComposite %52 %85 %66 %66 %123 = OpConstantComposite %116 %120 %121 %122 %124 = OpConstantComposite %52 %66 %66 %85 %125 = OpConstantComposite %52 %119 %66 %66 %126 = OpConstantComposite %116 %124 %121 %125 %127 = OpConstantComposite %52 %66 %85 %66 %128 = OpConstantComposite %116 %122 %124 %127 %129 = OpConstantComposite %116 %122 %120 %121 %130 = OpConstantComposite %116 %122 %121 %124 %131 = OpConstantComposite %116 %125 %121 %120 %132 = OpConstantComposite %118 %123 %126 %128 %129 %130 %131 %133 = OpConstant %13 6 %134 = OpConstantComposite %17 %66 %66 %66 %66 %135 = OpConstant %13 0.25 %136 = OpConstant %13 0.379999995 %137 = OpConstant %13 0.479999989 %138 = OpConstant %13 0.00499999989 %139 = OpConstant %13 0.899999976 %140 = OpConstant %13 0.0399999991 %141 = OpConstant %13 1.5 %142 = OpConstant %13 0.0199999996 %143 = OpTypeRuntimeArray %13 %144 = OpTypeStruct %143 %145 = OpTypePointer StorageBuffer %144 %146 = OpVariable %145 StorageBuffer %147 = OpTypeStruct %6 %6 %6 %6 %148 = OpTypeRuntimeArray %147 %149 = OpTypeStruct %148 %150 = OpTypePointer StorageBuffer %149 %151 = OpVariable %150 StorageBuffer %152 = OpTypeMatrix %17 4 %153 = OpTypeArray %152 %91 %154 = OpTypeStruct %153 %152 %17 %17 %14 %13 %13 %13 %13 %56 %155 = OpTypeStruct %154 %156 = OpTypePointer Uniform %155 %157 = OpVariable %156 Uniform %158 = OpTypeMatrix %17 3 %159 = OpTypeStruct %158 %17 %158 %17 %160 = OpTypeRuntimeArray %159 %161 = OpTypeStruct %160 %162 = OpTypePointer StorageBuffer %161 %163 = OpVariable %162 StorageBuffer %164 = OpTypeVector %6 2 %165 = OpTypeStruct %6 %6 %6 %6 %164 %6 %13 %166 = OpTypeRuntimeArray %165 %167 = OpTypeStruct %166 %168 = OpTypePointer StorageBuffer %167 %169 = OpVariable %168 StorageBuffer %4 = OpFunction %2 None %3 %5 = OpLabel %93 = OpAccessChain %92 %55 %90 %91 %94 = OpLoad %13 %93 %95 = OpFNegate %13 %94 %96 = OpFunctionCall %13 %25 %97 = OpFMul %13 %95 %96 OpStore %89 %97 OpReturn OpFunctionEnd %11 = OpFunction %8 None %9 %10 = OpFunctionParameter %7 %12 = OpLabel %27 = OpLoad %6 %10 %29 = OpINotEqual %8 %27 %28 OpReturnValue %29 OpFunctionEnd %22 = OpFunction %17 None %18 %19 = OpFunctionParameter %7 %20 = OpFunctionParameter %15 %21 = OpFunctionParameter %16 %23 = OpLabel %36 = OpLoad %6 %19 %37 = OpCopyObject %6 %36 %39 = OpAccessChain %38 %35 %37 %40 = OpLoad %32 %39 %44 = OpLoad %41 %43 %46 = OpSampledImage %45 %40 %44 %47 = OpLoad %14 %20 %48 = OpLoad %13 %21 %49 = OpImageSampleExplicitLod %17 %46 %47 Lod %48 OpReturnValue %49 OpFunctionEnd %25 = OpFunction %13 None %24 %26 = OpLabel %58 = OpVariable %7 Function %67 = OpVariable %7 Function %70 = OpVariable %15 Function %74 = OpVariable %16 Function %60 = OpAccessChain %59 %55 %57 %61 = OpLoad %6 %60 OpStore %58 %61 %62 = OpFunctionCall %8 %11 %58 OpSelectionMerge %64 None OpBranchConditional %62 %63 %64 %63 = OpLabel %68 = OpAccessChain %59 %55 %57 %69 = OpLoad %6 %68 OpStore %67 %69 %72 = OpAccessChain %71 %55 %65 %73 = OpLoad %14 %72 OpStore %70 %73 OpStore %74 %66 %75 = OpFunctionCall %17 %22 %67 %70 %74 %77 = OpCompositeExtract %13 %75 0 %79 = OpFOrdLessThanEqual %8 %77 %78 OpBranch %64 %64 = OpLabel %80 = OpPhi %8 %62 %26 %79 %63 OpSelectionMerge %82 None OpBranchConditional %80 %81 %84 %81 = OpLabel OpReturnValue %66 %84 = OpLabel OpReturnValue %85 %82 = OpLabel OpUnreachable OpFunctionEnd End SPIR-V Converted MSL: #pragma clang diagnostic ignored "-Wmissing-prototypes" #pragma clang diagnostic ignored "-Wmissing-braces" #include #include using namespace metal; template struct spvUnsafeArray { T elements[Num ? Num : 1]; thread T& operator [] (size_t pos) thread { return elements[pos]; } constexpr const thread T& operator [] (size_t pos) const thread { return elements[pos]; } device T& operator [] (size_t pos) device { return elements[pos]; } constexpr const device T& operator [] (size_t pos) const device { return elements[pos]; } constexpr const constant T& operator [] (size_t pos) const constant { return elements[pos]; } threadgroup T& operator [] (size_t pos) threadgroup { return elements[pos]; } constexpr const threadgroup T& operator [] (size_t pos) const threadgroup { return elements[pos]; } }; struct VertexData { float2 uv; uint maskSamplerId; float3 vViewPos; }; struct TextureTransformsBuffer { float TextureTransforms[1]; }; struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct TextureHandles { TextureSamplerIds samplerIds[1]; }; struct ShadowMapParameters { float4x4 CameraMatrices[2]; float4x4 ViewInverse; float4 WindScroll; float4 WindVec; float2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct ShadowMapParamsBuffer { ShadowMapParameters params; }; struct InstanceData { float3x4 transposeTransform; float4 customData0; float3x4 transposeInverseTransform; float4 customData1; }; struct InstanceDataBuffer { InstanceData instanceData[1]; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uint2 EmissiveColorPacked; uint avgColor; float padding; }; struct MaterialsBuffer { Material Materials[1]; }; struct spvDescriptorSetBuffer0 { constant void* _m0_pad [[id(0)]]; constant void* _m1_pad [[id(1)]]; sampler SceneTextureSampler [[id(2)]]; }; struct spvDescriptorSetBuffer1 { array, 1> SceneTextures [[id(0)]]; }; constant spvUnsafeArray _115 = spvUnsafeArray({ int2(0), int2(-1), int2(-1, 1), int2(1, -1), int2(1), int2(1, 0), int2(0, 1), int2(-1, 0), int2(0, -1) }); constant spvUnsafeArray _132 = spvUnsafeArray({ float3x3(float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0), float3(1.0, 0.0, 0.0)), float3x3(float3(0.0, 0.0, 1.0), float3(0.0, -1.0, 0.0), float3(-1.0, 0.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, 1.0), float3(0.0, 1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 0.0, -1.0), float3(0.0, -1.0, 0.0)), float3x3(float3(1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, 1.0)), float3x3(float3(-1.0, 0.0, 0.0), float3(0.0, -1.0, 0.0), float3(0.0, 0.0, -1.0)) }); struct main0_out { float gl_FragDepth [[depth(any)]]; }; struct main0_in { float2 inoutData_uv [[user(locn0)]]; uint inoutData_maskSamplerId [[user(locn1)]]; float3 inoutData_vViewPos [[user(locn2)]]; }; static inline __attribute__((always_inline)) bool isValidSamplerId(thread const uint& samplerId) { return samplerId != 4294967295u; } static inline __attribute__((always_inline)) float4 getTextureLodValue(thread const uint& samplerId, thread const float2& uv, thread const float& lod, constant array, 1>& SceneTextures, sampler SceneTextureSampler) { uint _37 = samplerId; return SceneTextures[_37].sample(SceneTextureSampler, uv, level(lod)); } static inline __attribute__((always_inline)) float opaqueMaskedDoubleSided(constant array, 1>& SceneTextures, sampler SceneTextureSampler, thread VertexData& inoutData) { uint param = inoutData.maskSamplerId; bool _62 = isValidSamplerId(param); bool _80; if (_62) { uint param_1 = inoutData.maskSamplerId; float2 param_2 = inoutData.uv; float param_3 = 0.0; _80 = getTextureLodValue(param_1, param_2, param_3, SceneTextures, SceneTextureSampler).x <= 0.5; } else { _80 = _62; } if (_80) { return 0.0; } else { return 1.0; } } fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]]) { main0_out out = {}; VertexData inoutData = {}; inoutData.uv = in.inoutData_uv; inoutData.maskSamplerId = in.inoutData_maskSamplerId; inoutData.vViewPos = in.inoutData_vViewPos; out.gl_FragDepth = (-inoutData.vViewPos.z) * opaqueMaskedDoubleSided(spvDescriptorSet1.SceneTextures, spvDescriptorSet0.SceneTextureSampler, inoutData); return out; } End MSL Estimated original GLSL: #version 460 #extension GL_EXT_nonuniform_qualifier : require struct TextureSamplerIds { uint AlbedoId; uint NormalId; uint RoughnessId; uint MaskId; }; struct ShadowMapParameters { mat4 CameraMatrices[2]; mat4 ViewInverse; vec4 WindScroll; vec4 WindVec; vec2 NearFar; float BacksideClipDistanceSq; float ShadowOffsetFactor; float ShadowOffsetUnits; float ShadowOffsetDynamic; int ProjectionType; }; struct InstanceData { mat3x4 transposeTransform; vec4 customData0; mat3x4 transposeInverseTransform; vec4 customData1; }; struct Material { uint BaseColorOpacityPacked; uint TintColorRoughnessPacked; uint MetallicSpecularImageFadeTypePacked; uint texTransformTypeIsVideoTexDispAmountTexClass; uvec2 EmissiveColorPacked; uint avgColor; float padding; }; layout(set = 0, binding = 0, std430) readonly buffer TextureTransformsBuffer { float TextureTransforms[]; } _146; layout(set = 0, binding = 2, std430) readonly buffer TextureHandles { TextureSamplerIds samplerIds[]; } _151; layout(set = 0, binding = 3, std140) uniform ShadowMapParamsBuffer { ShadowMapParameters params; } _157; layout(set = 0, binding = 4, std430) readonly buffer InstanceDataBuffer { InstanceData instanceData[]; } _163; layout(set = 0, binding = 5, std430) readonly buffer MaterialsBuffer { Material Materials[]; } _169; layout(set = 1, binding = 1) uniform texture2D SceneTextures[]; layout(set = 0, binding = 1) uniform sampler SceneTextureSampler; layout(location = 0) in VertexData { vec2 uv; flat uint maskSamplerId; vec3 vViewPos; } inoutData; bool isValidSamplerId(uint samplerId) { return samplerId != 4294967295u; } vec4 getTextureLodValue(uint samplerId, vec2 uv, float lod) { uint _37 = samplerId; return textureLod(sampler2D(SceneTextures[_37], SceneTextureSampler), uv, lod); } float opaqueMaskedDoubleSided() { uint param = inoutData.maskSamplerId; bool _62 = isValidSamplerId(param); bool _80; if (_62) { uint param_1 = inoutData.maskSamplerId; vec2 param_2 = inoutData.uv; float param_3 = 0.0; _80 = getTextureLodValue(param_1, param_2, param_3).x <= 0.5; } else { _80 = _62; } if (_80) { return 0.0; } else { return 1.0; } } void main() { gl_FragDepth = (-inoutData.vViewPos.z) * opaqueMaskedDoubleSided(); } End GLSL ```

Thank you for your help. I'll try to reproduce it with descriptor_indexing sample.

billhollings commented 2 months ago

Thanks for the more detailed info.

Fun fact: when I enable Shader Validation in Xcode, shadow map works in 1.2.10 = mask textures are sampled correctly.

workaround - do not do discard

Hmmm...I think we might be dealing with some kind of bizarreness around the discard_fragment() path. Possibly a Metal outlier bug.

What happens if you modify the shader to return a color value (maybe transparent black (0, 0, 0 0)), in an effort to try to get it to compile like a more traditional shader?

I'll try to reproduce it with descriptor_indexing sample.

If this is tied to discard_fragment() behaviour, you might not be able to replicate it there.

AlexanderDevaikinEnscape commented 1 month ago

discard + writing to RGBA_8 attachment still has the problem.

We have encountered another case of this issue - one compute shader samples mask textures and also gets zeroes for any texture index > 0. But that new one is a compute shader, so no attachments at all :/

AlexanderDevaikinEnscape commented 1 month ago

In compute shader case it even looks more like undefined behaviour. Some times it works but samples zeros, other times we crash with device lost as soon as mask texture has to be sampled. I'm experimenting with other texture formats and shader code shuffling, but without much luck yet. And similar to graphics pipeline case the problem is gone when I enable Metal shader validation.

AlexanderDevaikinEnscape commented 1 month ago

Hey @billhollings I have created a repro case with descriptor_indexing sample: https://github.com/KhronosGroup/Vulkan-Samples/compare/main...AlexanderDevaikinEnscape:Vulkan-Samples:Reprocase/MetalDeviceLost

Key points are: have discard and do not use sampled data in output. I don't have compute shader repro case, but as I mentioned above we observe similar issue in compute, plus there is no output in compute shader so we cannot workaround the problem.

P.S. issue is present on m1 and m2 chips + iPhones. m3 is not affected.