From ad2b7c05e7a6fe26099b3bea3939844610f922c0 Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Fri, 23 Jun 2017 09:44:41 +0200 Subject: [PATCH] Implement workaround for textureLod on 2D array shadow. This does not exist in GLSL, but it exists in HLSL if LOD == 0.0. --- .../frag/hlsl-sample-cmp-level-zero.asm.frag | 27 +++++ .../shaders/asm/frag/invalidation.asm.frag | 6 +- .../frag/hlsl-sample-cmp-level-zero.asm.frag | 113 ++++++++++++++++++ shaders/asm/frag/invalidation.asm.frag | 3 + spirv_glsl.cpp | 49 ++++++-- test_shaders.py | 2 +- 6 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 reference/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag create mode 100644 shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag diff --git a/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag b/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag new file mode 100644 index 00000000..63856ddd --- /dev/null +++ b/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag @@ -0,0 +1,27 @@ +#version 450 + +uniform sampler2DArrayShadow SPIRV_Cross_CombinedShadowMapShadowSamplerPCF; + +layout(location = 0) in vec2 texCoords; +layout(location = 1) in float cascadeIndex; +layout(location = 2) in float fragDepth; +layout(location = 0) out vec4 _entryPointOutput; + +vec4 _main(vec2 texCoords_1, float cascadeIndex_1, float fragDepth_1) +{ + vec4 _60 = vec4(vec3(texCoords_1, cascadeIndex_1), fragDepth_1); + float c = textureGrad(SPIRV_Cross_CombinedShadowMapShadowSamplerPCF, vec4(_60.xyz, _60.w), vec2(0.0), vec2(0.0)); + return vec4(c, c, c, c); +} + +void main() +{ + vec2 texCoords_1 = texCoords; + float cascadeIndex_1 = cascadeIndex; + float fragDepth_1 = fragDepth; + vec2 param = texCoords_1; + float param_1 = cascadeIndex_1; + float param_2 = fragDepth_1; + _entryPointOutput = _main(param, param_1, param_2); +} + diff --git a/reference/shaders/asm/frag/invalidation.asm.frag b/reference/shaders/asm/frag/invalidation.asm.frag index 1cdc9761..db118180 100644 --- a/reference/shaders/asm/frag/invalidation.asm.frag +++ b/reference/shaders/asm/frag/invalidation.asm.frag @@ -1,8 +1,8 @@ #version 450 -in float v0; -in float v1; -out float FragColor; +layout(location = 0) in float v0; +layout(location = 1) in float v1; +layout(location = 0) out float FragColor; void main() { diff --git a/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag b/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag new file mode 100644 index 00000000..34fb6e83 --- /dev/null +++ b/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag @@ -0,0 +1,113 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 70 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %texCoords_1 %cascadeIndex_1 %fragDepth_1 %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %_main_vf2_f1_f1_ "@main(vf2;f1;f1;" + OpName %texCoords "texCoords" + OpName %cascadeIndex "cascadeIndex" + OpName %fragDepth "fragDepth" + OpName %c "c" + OpName %ShadowMap "ShadowMap" + OpName %ShadowSamplerPCF "ShadowSamplerPCF" + OpName %texCoords_0 "texCoords" + OpName %texCoords_1 "texCoords" + OpName %cascadeIndex_0 "cascadeIndex" + OpName %cascadeIndex_1 "cascadeIndex" + OpName %fragDepth_0 "fragDepth" + OpName %fragDepth_1 "fragDepth" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpName %param_1 "param" + OpDecorate %ShadowMap DescriptorSet 0 + OpDecorate %ShadowSamplerPCF DescriptorSet 0 + OpDecorate %texCoords_1 Location 0 + OpDecorate %cascadeIndex_1 Location 1 + OpDecorate %fragDepth_1 Location 2 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Function_float = OpTypePointer Function %float + %v4float = OpTypeVector %float 4 + %11 = OpTypeFunction %v4float %_ptr_Function_v2float %_ptr_Function_float %_ptr_Function_float + %18 = OpTypeImage %float 2D 0 1 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %ShadowMap = OpVariable %_ptr_UniformConstant_18 UniformConstant + %22 = OpTypeSampler +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 +%ShadowSamplerPCF = OpVariable %_ptr_UniformConstant_22 UniformConstant + %26 = OpTypeImage %float 2D 1 1 0 1 Unknown + %27 = OpTypeSampledImage %26 + %v3float = OpTypeVector %float 3 + %float_0 = OpConstant %float 0 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords_1 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_float = OpTypePointer Input %float +%cascadeIndex_1 = OpVariable %_ptr_Input_float Input +%fragDepth_1 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel +%texCoords_0 = OpVariable %_ptr_Function_v2float Function +%cascadeIndex_0 = OpVariable %_ptr_Function_float Function +%fragDepth_0 = OpVariable %_ptr_Function_float Function + %param = OpVariable %_ptr_Function_v2float Function + %param_0 = OpVariable %_ptr_Function_float Function + %param_1 = OpVariable %_ptr_Function_float Function + %53 = OpLoad %v2float %texCoords_1 + OpStore %texCoords_0 %53 + %57 = OpLoad %float %cascadeIndex_1 + OpStore %cascadeIndex_0 %57 + %60 = OpLoad %float %fragDepth_1 + OpStore %fragDepth_0 %60 + %64 = OpLoad %v2float %texCoords_0 + OpStore %param %64 + %66 = OpLoad %float %cascadeIndex_0 + OpStore %param_0 %66 + %68 = OpLoad %float %fragDepth_0 + OpStore %param_1 %68 + %69 = OpFunctionCall %v4float %_main_vf2_f1_f1_ %param %param_0 %param_1 + OpStore %_entryPointOutput %69 + OpReturn + OpFunctionEnd +%_main_vf2_f1_f1_ = OpFunction %v4float None %11 + %texCoords = OpFunctionParameter %_ptr_Function_v2float +%cascadeIndex = OpFunctionParameter %_ptr_Function_float + %fragDepth = OpFunctionParameter %_ptr_Function_float + %16 = OpLabel + %c = OpVariable %_ptr_Function_float Function + %21 = OpLoad %18 %ShadowMap + %25 = OpLoad %22 %ShadowSamplerPCF + %28 = OpSampledImage %27 %21 %25 + %29 = OpLoad %v2float %texCoords + %30 = OpLoad %float %cascadeIndex + %32 = OpCompositeExtract %float %29 0 + %33 = OpCompositeExtract %float %29 1 + %34 = OpCompositeConstruct %v3float %32 %33 %30 + %35 = OpLoad %float %fragDepth + %36 = OpCompositeExtract %float %34 0 + %37 = OpCompositeExtract %float %34 1 + %38 = OpCompositeExtract %float %34 2 + %39 = OpCompositeConstruct %v4float %36 %37 %38 %35 + %41 = OpCompositeExtract %float %39 3 + %42 = OpImageSampleDrefExplicitLod %float %28 %39 %41 Lod %float_0 + OpStore %c %42 + %43 = OpLoad %float %c + %44 = OpLoad %float %c + %45 = OpLoad %float %c + %46 = OpLoad %float %c + %47 = OpCompositeConstruct %v4float %43 %44 %45 %46 + OpReturnValue %47 + OpFunctionEnd diff --git a/shaders/asm/frag/invalidation.asm.frag b/shaders/asm/frag/invalidation.asm.frag index 1c171b6d..8e753d50 100644 --- a/shaders/asm/frag/invalidation.asm.frag +++ b/shaders/asm/frag/invalidation.asm.frag @@ -15,6 +15,9 @@ OpName %b "b" OpName %v1 "v1" OpName %FragColor "FragColor" + OpDecorate %v0 Location 0 + OpDecorate %v1 Location 1 + OpDecorate %FragColor Location 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %float = OpTypeFloat 32 diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 8ff91632..570575e1 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -2973,6 +2973,19 @@ string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is { string fname; + // textureLod on sampler2DArrayShadow does not exist in GLSL for some reason. + // To emulate this, we will have to use textureGrad with a constant gradient of 0. + // The workaround will assert that the LOD is in fact constant 0, or we cannot emit correct code. + // This happens for HLSL SampleCmpLevelZero on Texture2DArray. + bool workaround_lod_array_shadow_as_grad = false; + if (imgtype.image.arrayed && imgtype.image.dim == Dim2D && imgtype.image.depth && lod) + { + auto *constant_lod = maybe_get(lod); + if (!constant_lod || constant_lod->scalar_f32() != 0.0f) + SPIRV_CROSS_THROW("textureLod on sampler2DArraySahdow is not constant 0.0. This cannot be expressed in GLSL."); + workaround_lod_array_shadow_as_grad = true; + } + if (is_fetch) fname += "texelFetch"; else @@ -2985,9 +2998,9 @@ string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is fname += "Offsets"; if (is_proj) fname += "Proj"; - if (has_grad) + if (has_grad || workaround_lod_array_shadow_as_grad) fname += "Grad"; - if (!!lod) + if (!!lod && !workaround_lod_array_shadow_as_grad) fname += "Lod"; } @@ -2998,7 +3011,7 @@ string CompilerGLSL::to_function_name(uint32_t, const SPIRType &imgtype, bool is } // Returns the function args for a texture sampling function for the specified image and sampling characteristics. -string CompilerGLSL::to_function_args(uint32_t img, const SPIRType &, bool, bool, bool, uint32_t coord, +string CompilerGLSL::to_function_args(uint32_t img, const SPIRType &imgtype, bool, bool, bool, uint32_t coord, uint32_t coord_components, uint32_t dref, uint32_t grad_x, uint32_t grad_y, uint32_t lod, uint32_t coffset, uint32_t offset, uint32_t bias, uint32_t comp, uint32_t sample, bool *p_forward) @@ -3030,7 +3043,18 @@ string CompilerGLSL::to_function_args(uint32_t img, const SPIRType &, bool, bool // Only enclose the UV expression if needed. auto coord_expr = (*swizzle_expr == '\0') ? to_expression(coord) : (to_enclosed_expression(coord) + swizzle_expr); - // TODO: implement rest ... A bit intensive. + // textureLod on sampler2DArrayShadow does not exist in GLSL for some reason. + // To emulate this, we will have to use textureGrad with a constant gradient of 0. + // The workaround will assert that the LOD is in fact constant 0, or we cannot emit correct code. + // This happens for HLSL SampleCmpLevelZero on Texture2DArray. + bool workaround_lod_array_shadow_as_grad = false; + if (imgtype.image.arrayed && imgtype.image.dim == Dim2D && imgtype.image.depth && lod) + { + auto *constant_lod = maybe_get(lod); + if (!constant_lod || constant_lod->scalar_f32() != 0.0f) + SPIRV_CROSS_THROW("textureLod on sampler2DArraySahdow is not constant 0.0. This cannot be expressed in GLSL."); + workaround_lod_array_shadow_as_grad = true; + } if (dref) { @@ -3076,11 +3100,20 @@ string CompilerGLSL::to_function_args(uint32_t img, const SPIRType &, bool, bool if (lod) { - if (check_explicit_lod_allowed(lod)) + if (workaround_lod_array_shadow_as_grad) { - forward = forward && should_forward(lod); - farg_str += ", "; - farg_str += to_expression(lod); + // Implement textureGrad() instead. LOD == 0.0 is implemented as gradient of 0.0. + // Implementing this as plain texture() is not safe on some implementations. + farg_str += ", vec2(0.0), vec2(0.0)"; + } + else + { + if (check_explicit_lod_allowed(lod)) + { + forward = forward && should_forward(lod); + farg_str += ", "; + farg_str += to_expression(lod); + } } } diff --git a/test_shaders.py b/test_shaders.py index 74aa6e0e..ce7a7e4c 100755 --- a/test_shaders.py +++ b/test_shaders.py @@ -161,7 +161,7 @@ def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, fl if vulkan or spirv: subprocess.check_call([spirv_cross_path, '--entry', 'main', '--vulkan-semantics', '--output', vulkan_glsl_path, spirv_path] + extra_args) - validate_shader(vulkan_glsl_path, vulkan) + validate_shader(vulkan_glsl_path, True) return (spirv_path, glsl_path, vulkan_glsl_path if vulkan else None)