Skip to content

Commit

Permalink
Merge pull request #803 from KhronosGroup/fix-796
Browse files Browse the repository at this point in the history
MSL: Workaround missing gradient2d() on macOS for typical cascaded shadow mapping
  • Loading branch information
HansKristian-Work committed Jan 7, 2019
2 parents 169607c + 5b87622 commit e6cce63
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <metal_stdlib>
#include <simd/simd.h>

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<float> 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;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <metal_stdlib>
#include <simd/simd.h>

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<float> 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;
}

22 changes: 22 additions & 0 deletions reference/shaders-msl/frag/sampler-compare-cascade-gradient.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <metal_stdlib>
#include <simd/simd.h>

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<float> 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;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <metal_stdlib>
#include <simd/simd.h>

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<float> 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;
}

11 changes: 11 additions & 0 deletions shaders-msl/frag/sampler-compare-cascade-gradient.frag
Original file line number Diff line number Diff line change
@@ -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));
}
11 changes: 11 additions & 0 deletions shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag
Original file line number Diff line number Diff line change
@@ -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));
}
15 changes: 15 additions & 0 deletions spirv_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_)
{
Expand Down
8 changes: 3 additions & 5 deletions spirv_cross.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -901,8 +901,8 @@ void Compiler::flatten_interface_block(uint32_t id)
var.storage = storage;
}

void Compiler::update_name_cache(unordered_set<string> &cache_primary,
const unordered_set<string> &cache_secondary, string &name)
void Compiler::update_name_cache(unordered_set<string> &cache_primary, const unordered_set<string> &cache_secondary,
string &name)
{
if (name.empty())
return;
Expand All @@ -918,9 +918,7 @@ void Compiler::update_name_cache(unordered_set<string> &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))
{
Expand Down
3 changes: 1 addition & 2 deletions spirv_cross.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> &cache_primary,
const std::unordered_set<std::string> &cache_secondary,
std::string &name);
const std::unordered_set<std::string> &cache_secondary, std::string &name);

bool function_is_pure(const SPIRFunction &func);
bool block_is_pure(const SPIRBlock &block);
Expand Down
16 changes: 12 additions & 4 deletions spirv_glsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<SPIRConstant>(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,
Expand All @@ -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<SPIRConstant>(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;
}

Expand Down
5 changes: 3 additions & 2 deletions spirv_glsl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> &variables_primary,
const std::unordered_set<std::string> &variables_secondary,
std::string &name);
const std::unordered_set<std::string> &variables_secondary, std::string &name);

void check_function_call_constraints(const uint32_t *args, uint32_t length);
void handle_invalid_expression(uint32_t id);
Expand Down Expand Up @@ -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()
{
Expand Down
37 changes: 37 additions & 0 deletions spirv_msl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit e6cce63

Please sign in to comment.