diff --git a/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.frag b/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.frag new file mode 100644 index 000000000..18c2d4237 --- /dev/null +++ b/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, level(0)); + return out; +} + diff --git a/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag b/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag new file mode 100644 index 000000000..f01965c9e --- /dev/null +++ b/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, gradient2d(float2(0.0), float2(0.0))); + return out; +} + diff --git a/reference/shaders-msl/frag/sampler-compare-cascade-gradient.frag b/reference/shaders-msl/frag/sampler-compare-cascade-gradient.frag new file mode 100644 index 000000000..18c2d4237 --- /dev/null +++ b/reference/shaders-msl/frag/sampler-compare-cascade-gradient.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, level(0)); + return out; +} + diff --git a/reference/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag b/reference/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag new file mode 100644 index 000000000..f01965c9e --- /dev/null +++ b/reference/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, gradient2d(float2(0.0), float2(0.0))); + return out; +} + diff --git a/shaders-msl/frag/sampler-compare-cascade-gradient.frag b/shaders-msl/frag/sampler-compare-cascade-gradient.frag new file mode 100644 index 000000000..9fd9e3ca0 --- /dev/null +++ b/shaders-msl/frag/sampler-compare-cascade-gradient.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform texture2DArray uTex; +layout(binding = 1) uniform samplerShadow uShadow; +layout(location = 0) in vec4 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = textureGrad(sampler2DArrayShadow(uTex, uShadow), vUV, vec2(0.0), vec2(0.0)); +} diff --git a/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag b/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag new file mode 100644 index 000000000..9fd9e3ca0 --- /dev/null +++ b/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform texture2DArray uTex; +layout(binding = 1) uniform samplerShadow uShadow; +layout(location = 0) in vec4 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = textureGrad(sampler2DArrayShadow(uTex, uShadow), vUV, vec2(0.0), vec2(0.0)); +} diff --git a/spirv_common.hpp b/spirv_common.hpp index 78a8d80d7..9c3f31ec7 100644 --- a/spirv_common.hpp +++ b/spirv_common.hpp @@ -1088,6 +1088,21 @@ struct SPIRConstant : IVariant c.vecsize = constant_type_.vecsize; } + inline bool constant_is_null() const + { + if (specialization) + return false; + if (!subconstants.empty()) + return false; + + for (uint32_t col = 0; col < columns(); col++) + for (uint32_t row = 0; row < vector_size(); row++) + if (scalar_u64(col, row) != 0) + return false; + + return true; + } + explicit SPIRConstant(uint32_t constant_type_) : constant_type(constant_type_) { diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 0d09d6220..56933f180 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -901,8 +901,8 @@ void Compiler::flatten_interface_block(uint32_t id) var.storage = storage; } -void Compiler::update_name_cache(unordered_set &cache_primary, - const unordered_set &cache_secondary, string &name) +void Compiler::update_name_cache(unordered_set &cache_primary, const unordered_set &cache_secondary, + string &name) { if (name.empty()) return; @@ -918,9 +918,7 @@ void Compiler::update_name_cache(unordered_set &cache_primary, return false; }; - const auto insert_name = [&](const string &n) { - cache_primary.insert(n); - }; + const auto insert_name = [&](const string &n) { cache_primary.insert(n); }; if (!find_name(name)) { diff --git a/spirv_cross.hpp b/spirv_cross.hpp index bf3b47720..2829ef088 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -653,8 +653,7 @@ class Compiler // but the set is not updated when we have found a new name. // Used primarily when adding block interface names. void update_name_cache(std::unordered_set &cache_primary, - const std::unordered_set &cache_secondary, - std::string &name); + const std::unordered_set &cache_secondary, std::string &name); bool function_is_pure(const SPIRFunction &func); bool block_is_pure(const SPIRBlock &block); diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 0f6b3e3a4..86289cd60 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -1546,8 +1546,7 @@ void CompilerGLSL::emit_buffer_block_native(const SPIRVariable &var) // Shaders never use the block by interface name, so we don't // have to track this other than updating name caches. // If we have a collision for any reason, just fallback immediately. - if (ir.meta[type.self].decoration.alias.empty() || - block_namespace.find(buffer_name) != end(block_namespace) || + if (ir.meta[type.self].decoration.alias.empty() || block_namespace.find(buffer_name) != end(block_namespace) || resource_names.find(buffer_name) != end(resource_names)) { buffer_name = get_block_fallback_name(var.self); @@ -4231,6 +4230,14 @@ void CompilerGLSL::emit_texture_op(const Instruction &i) } } +bool CompilerGLSL::expression_is_constant_null(uint32_t id) const +{ + auto *c = maybe_get(id); + if (!c) + return false; + return c->constant_is_null(); +} + // Returns the function name for a texture sampling function for the specified image and sampling characteristics. // For some subclasses, the function is a method on the specified image. string CompilerGLSL::to_function_name(uint32_t tex, const SPIRType &imgtype, bool is_fetch, bool is_gather, @@ -4247,10 +4254,11 @@ string CompilerGLSL::to_function_name(uint32_t tex, const SPIRType &imgtype, boo if (((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) && image_is_comparison(imgtype, tex) && lod) { - auto *constant_lod = maybe_get(lod); - if (!constant_lod || constant_lod->scalar_f32() != 0.0f) + if (!expression_is_constant_null(lod)) + { SPIRV_CROSS_THROW( "textureLod on sampler2DArrayShadow is not constant 0.0. This cannot be expressed in GLSL."); + } workaround_lod_array_shadow_as_grad = true; } diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index a77e72fee..150588bcb 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -577,8 +577,7 @@ class CompilerGLSL : public Compiler // but the set is not updated when we have found a new name. // Used primarily when adding block interface names. void add_variable(std::unordered_set &variables_primary, - const std::unordered_set &variables_secondary, - std::string &name); + const std::unordered_set &variables_secondary, std::string &name); void check_function_call_constraints(const uint32_t *args, uint32_t length); void handle_invalid_expression(uint32_t id); @@ -615,6 +614,8 @@ class CompilerGLSL : public Compiler void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id); void disallow_forwarding_in_expression_chain(const SPIRExpression &expr); + bool expression_is_constant_null(uint32_t id) const; + private: void init() { diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 4ddf4934e..bd252725b 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -3321,6 +3321,43 @@ string CompilerMSL::to_function_args(uint32_t img, const SPIRType &imgtype, bool forward = forward && should_forward(dref); farg_str += ", "; farg_str += to_expression(dref); + + if (msl_options.is_macos() && (grad_x || grad_y)) + { + // For sample compare, MSL does not support gradient2d for all targets (only iOS apparently according to docs). + // However, the most common case here is to have a constant gradient of 0, as that is the only way to express + // LOD == 0 in GLSL with sampler2DArrayShadow (cascaded shadow mapping). + // We will detect a compile-time constant 0 value for gradient and promote that to level(0) on MSL. + bool constant_zero_x = !grad_x || expression_is_constant_null(grad_x); + bool constant_zero_y = !grad_y || expression_is_constant_null(grad_y); + if (constant_zero_x && constant_zero_y) + { + lod = 0; + grad_x = 0; + grad_y = 0; + farg_str += ", level(0)"; + } + else + { + SPIRV_CROSS_THROW("Using non-constant 0.0 gradient() qualifier for sample_compare. This is not " + "supported in MSL macOS."); + } + } + + if (msl_options.is_macos() && bias) + { + // Bias is not supported either on macOS with sample_compare. + // Verify it is compile-time zero, and drop the argument. + if (expression_is_constant_null(bias)) + { + bias = 0; + } + else + { + SPIRV_CROSS_THROW( + "Using non-constant 0.0 bias() qualifier for sample_compare. This is not supported in MSL macOS."); + } + } } // LOD Options