From 2e1cee5e1e9d9c2282bbad27cfa1693e4088459b Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Thu, 13 Jun 2019 11:33:40 +0200 Subject: [PATCH] MSL: Support PrimitiveID in fragment and barycentrics. --- .../frag/barycentric-nv-nopersp.msl22.frag | 28 +++++++ .../frag/barycentric-nv.msl22.frag | 28 +++++++ .../frag/barycentric-nv-nopersp.msl22.frag | 31 ++++++++ .../frag/barycentric-nv.msl22.frag | 31 ++++++++ .../frag/barycentric-nv-nopersp.msl22.frag | 17 +++++ shaders-msl/frag/barycentric-nv.msl22.frag | 17 +++++ spirv_msl.cpp | 75 +++++++++++++++++-- spirv_msl.hpp | 2 +- 8 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 reference/opt/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag create mode 100644 reference/opt/shaders-msl/frag/barycentric-nv.msl22.frag create mode 100644 reference/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag create mode 100644 reference/shaders-msl/frag/barycentric-nv.msl22.frag create mode 100644 shaders-msl/frag/barycentric-nv-nopersp.msl22.frag create mode 100644 shaders-msl/frag/barycentric-nv.msl22.frag diff --git a/reference/opt/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag b/reference/opt/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag new file mode 100644 index 00000000..53b8a743 --- /dev/null +++ b/reference/opt/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct Vertices +{ + float2 uvs[1]; +}; + +struct main0_out +{ + float2 value [[color(0)]]; +}; + +struct main0_in +{ + float3 gl_BaryCoordNoPerspNV [[barycentric_coord, center_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]]) +{ + main0_out out = {}; + int _23 = 3 * int(gl_PrimitiveID); + out.value = ((_19.uvs[_23] * in.gl_BaryCoordNoPerspNV.x) + (_19.uvs[_23 + 1] * in.gl_BaryCoordNoPerspNV.y)) + (_19.uvs[_23 + 2] * in.gl_BaryCoordNoPerspNV.z); + return out; +} + diff --git a/reference/opt/shaders-msl/frag/barycentric-nv.msl22.frag b/reference/opt/shaders-msl/frag/barycentric-nv.msl22.frag new file mode 100644 index 00000000..ae2c704d --- /dev/null +++ b/reference/opt/shaders-msl/frag/barycentric-nv.msl22.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct Vertices +{ + float2 uvs[1]; +}; + +struct main0_out +{ + float2 value [[color(0)]]; +}; + +struct main0_in +{ + float3 gl_BaryCoordNV [[barycentric_coord, center_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]]) +{ + main0_out out = {}; + int _23 = 3 * int(gl_PrimitiveID); + out.value = ((_19.uvs[_23] * in.gl_BaryCoordNV.x) + (_19.uvs[_23 + 1] * in.gl_BaryCoordNV.y)) + (_19.uvs[_23 + 2] * in.gl_BaryCoordNV.z); + return out; +} + diff --git a/reference/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag b/reference/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag new file mode 100644 index 00000000..ef19fbf8 --- /dev/null +++ b/reference/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct Vertices +{ + float2 uvs[1]; +}; + +struct main0_out +{ + float2 value [[color(0)]]; +}; + +struct main0_in +{ + float3 gl_BaryCoordNoPerspNV [[barycentric_coord, center_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]]) +{ + main0_out out = {}; + int prim = int(gl_PrimitiveID); + float2 uv0 = _19.uvs[(3 * prim) + 0]; + float2 uv1 = _19.uvs[(3 * prim) + 1]; + float2 uv2 = _19.uvs[(3 * prim) + 2]; + out.value = ((uv0 * in.gl_BaryCoordNoPerspNV.x) + (uv1 * in.gl_BaryCoordNoPerspNV.y)) + (uv2 * in.gl_BaryCoordNoPerspNV.z); + return out; +} + diff --git a/reference/shaders-msl/frag/barycentric-nv.msl22.frag b/reference/shaders-msl/frag/barycentric-nv.msl22.frag new file mode 100644 index 00000000..1d2e4c2f --- /dev/null +++ b/reference/shaders-msl/frag/barycentric-nv.msl22.frag @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct Vertices +{ + float2 uvs[1]; +}; + +struct main0_out +{ + float2 value [[color(0)]]; +}; + +struct main0_in +{ + float3 gl_BaryCoordNV [[barycentric_coord, center_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]]) +{ + main0_out out = {}; + int prim = int(gl_PrimitiveID); + float2 uv0 = _19.uvs[(3 * prim) + 0]; + float2 uv1 = _19.uvs[(3 * prim) + 1]; + float2 uv2 = _19.uvs[(3 * prim) + 2]; + out.value = ((uv0 * in.gl_BaryCoordNV.x) + (uv1 * in.gl_BaryCoordNV.y)) + (uv2 * in.gl_BaryCoordNV.z); + return out; +} + diff --git a/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag b/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag new file mode 100644 index 00000000..24edb1a2 --- /dev/null +++ b/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag @@ -0,0 +1,17 @@ +#version 450 +#extension GL_NV_fragment_shader_barycentric : require + +layout(location = 0) out vec2 value; + +layout(set = 0, binding = 0) readonly buffer Vertices +{ + vec2 uvs[]; +}; + +void main () { + int prim = gl_PrimitiveID; + vec2 uv0 = uvs[3 * prim + 0]; + vec2 uv1 = uvs[3 * prim + 1]; + vec2 uv2 = uvs[3 * prim + 2]; + value = gl_BaryCoordNoPerspNV.x * uv0 + gl_BaryCoordNoPerspNV.y * uv1 + gl_BaryCoordNoPerspNV.z * uv2; +} diff --git a/shaders-msl/frag/barycentric-nv.msl22.frag b/shaders-msl/frag/barycentric-nv.msl22.frag new file mode 100644 index 00000000..7aec19fb --- /dev/null +++ b/shaders-msl/frag/barycentric-nv.msl22.frag @@ -0,0 +1,17 @@ +#version 450 +#extension GL_NV_fragment_shader_barycentric : require + +layout(location = 0) out vec2 value; + +layout(set = 0, binding = 0) readonly buffer Vertices +{ + vec2 uvs[]; +}; + +void main () { + int prim = gl_PrimitiveID; + vec2 uv0 = uvs[3 * prim + 0]; + vec2 uv1 = uvs[3 * prim + 1]; + vec2 uv2 = uvs[3 * prim + 2]; + value = gl_BaryCoordNV.x * uv0 + gl_BaryCoordNV.y * uv1 + gl_BaryCoordNV.z * uv2; +} diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 3a827c65..02b30a63 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -1942,6 +1942,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) // Accumulate the variables that should appear in the interface struct. SmallVector vars; bool incl_builtins = storage == StorageClassOutput || is_tessellation_shader(); + bool has_seen_barycentric = false; ir.for_each_typed_id([&](uint32_t var_id, SPIRVariable &var) { if (var.storage != storage) @@ -1956,6 +1957,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) bool is_interface_block_builtin = (bi_type == BuiltInPosition || bi_type == BuiltInPointSize || bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance || bi_type == BuiltInLayer || bi_type == BuiltInViewportIndex || + bi_type == BuiltInBaryCoordNV || bi_type == BuiltInBaryCoordNoPerspNV || bi_type == BuiltInFragDepth || bi_type == BuiltInFragStencilRefEXT || bi_type == BuiltInSampleMask) || (get_execution_model() == ExecutionModelTessellationEvaluation && (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner)); @@ -1969,7 +1971,17 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) bool filter_patch_decoration = (has_decoration(var_id, DecorationPatch) || is_patch_block(type)) == patch; - if (is_active && !is_hidden_variable(var, incl_builtins) && type.pointer && filter_patch_decoration && + bool hidden = is_hidden_variable(var, incl_builtins); + // Barycentric inputs must be emitted in stage-in, because they can have interpolation arguments. + if (is_active && (bi_type == BuiltInBaryCoordNV || bi_type == BuiltInBaryCoordNoPerspNV)) + { + if (has_seen_barycentric) + SPIRV_CROSS_THROW("Cannot declare both BaryCoordNV and BaryCoordNoPerspNV in same shader in MSL."); + has_seen_barycentric = true; + hidden = false; + } + + if (is_active && !hidden && type.pointer && filter_patch_decoration && (!is_builtin || is_interface_block_builtin)) { vars.push_back(&var); @@ -5557,7 +5569,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in // Fragment function inputs if (execution.model == ExecutionModelFragment && type.storage == StorageClassInput) { - string quals = ""; + string quals; if (is_builtin) { switch (builtin) @@ -5568,7 +5580,10 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in case BuiltInSampleId: case BuiltInSampleMask: case BuiltInLayer: + case BuiltInBaryCoordNV: + case BuiltInBaryCoordNoPerspNV: quals = builtin_qualifier(builtin); + break; default: break; @@ -5586,6 +5601,19 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in quals = string("user(locn") + convert_to_string(locn) + ")"; } } + + if (builtin == BuiltInBaryCoordNV || builtin == BuiltInBaryCoordNoPerspNV) + { + if (has_member_decoration(type.self, index, DecorationFlat) || + has_member_decoration(type.self, index, DecorationCentroid) || + has_member_decoration(type.self, index, DecorationSample) || + has_member_decoration(type.self, index, DecorationNoPerspective)) + { + // NoPerspective is baked into the builtin type. + SPIRV_CROSS_THROW("Flat, Centroid, Sample, NoPerspective decorations are not supported for BaryCoord inputs."); + } + } + // Don't bother decorating integers with the 'flat' attribute; it's // the default (in fact, the only option). Also don't bother with the // FragCoord builtin; it's always noperspective on Metal. @@ -5622,6 +5650,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in quals += "center_no_perspective"; } } + if (!quals.empty()) return " [[" + quals + "]]"; } @@ -5929,13 +5958,14 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args) bi_type != BuiltInPatchVertices && bi_type != BuiltInTessLevelInner && bi_type != BuiltInTessLevelOuter && bi_type != BuiltInPosition && bi_type != BuiltInPointSize && bi_type != BuiltInClipDistance && bi_type != BuiltInCullDistance && bi_type != BuiltInSubgroupEqMask && + bi_type != BuiltInBaryCoordNV && bi_type != BuiltInBaryCoordNoPerspNV && bi_type != BuiltInSubgroupGeMask && bi_type != BuiltInSubgroupGtMask && bi_type != BuiltInSubgroupLeMask && bi_type != BuiltInSubgroupLtMask) { if (!ep_args.empty()) ep_args += ", "; - ep_args += builtin_type_decl(bi_type) + " " + to_expression(var_id); + ep_args += builtin_type_decl(bi_type, var_id) + " " + to_expression(var_id); ep_args += " [[" + builtin_qualifier(bi_type) + "]]"; } } @@ -6504,7 +6534,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) if (var.basevariable == stage_in_ptr_var_id || var.basevariable == stage_out_ptr_var_id) decl += type_to_glsl(type, arg.id); else if (builtin) - decl += builtin_type_decl(static_cast(get_decoration(arg.id, DecorationBuiltIn))); + decl += builtin_type_decl(static_cast(get_decoration(arg.id, DecorationBuiltIn)), arg.id); else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type)) decl += join(type_to_glsl(type, arg.id), "*"); else @@ -7541,6 +7571,12 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) break; + case BuiltInBaryCoordNV: + case BuiltInBaryCoordNoPerspNV: + if (storage == StorageClassInput && current_function && (current_function->self == ir.default_entry_point)) + return stage_in_var_name + "." + CompilerGLSL::builtin_to_glsl(builtin, storage); + break; + case BuiltInTessLevelOuter: if (get_execution_model() == ExecutionModelTessellationEvaluation) { @@ -7633,6 +7669,12 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin) return "threadgroup_position_in_grid"; case ExecutionModelTessellationEvaluation: return "patch_id"; + case ExecutionModelFragment: + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("PrimitiveId is not supported in fragment on iOS."); + else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("PrimitiveId on macOS requires MSL 2.2."); + return "primitive_id"; default: SPIRV_CROSS_THROW("PrimitiveId is not supported in this execution model."); } @@ -7716,13 +7758,29 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin) // Shouldn't be reached. SPIRV_CROSS_THROW("Subgroup ballot masks are handled specially in MSL."); + case BuiltInBaryCoordNV: + // TODO: AMD barycentrics as well? Seem to have different swizzle and 2 components rather than 3. + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("Barycentrics not supported on iOS."); + else if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Barycentrics are only supported in MSL 2.2 and above on macOS."); + return "barycentric_coord, center_perspective"; + + case BuiltInBaryCoordNoPerspNV: + // TODO: AMD barycentrics as well? Seem to have different swizzle and 2 components rather than 3. + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("Barycentrics not supported on iOS."); + else if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Barycentrics are only supported in MSL 2.2 and above on macOS."); + return "barycentric_coord, center_no_perspective"; + default: return "unsupported-built-in"; } } // Returns an MSL string type declaration for a SPIR-V builtin -string CompilerMSL::builtin_type_decl(BuiltIn builtin) +string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id) { const SPIREntryPoint &execution = get_entry_point(); switch (builtin) @@ -7822,6 +7880,11 @@ string CompilerMSL::builtin_type_decl(BuiltIn builtin) case BuiltInHelperInvocation: return "bool"; + case BuiltInBaryCoordNV: + case BuiltInBaryCoordNoPerspNV: + // Use the type as declared, can be 1, 2 or 3 components. + return type_to_glsl(get_variable_data_type(get(id))); + default: return "unsupported-built-in-type"; } @@ -8360,6 +8423,7 @@ void CompilerMSL::bitcast_from_builtin_load(uint32_t source_id, std::string &exp case BuiltInLayer: case BuiltInViewportIndex: case BuiltInFragStencilRefEXT: + case BuiltInPrimitiveId: expected_type = SPIRType::UInt; break; @@ -8401,6 +8465,7 @@ void CompilerMSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr case BuiltInLayer: case BuiltInViewportIndex: case BuiltInFragStencilRefEXT: + case BuiltInPrimitiveId: expected_type = SPIRType::UInt; break; diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 09782610..d0a090ac 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -471,7 +471,7 @@ protected: std::string to_swizzle_expression(uint32_t id); std::string to_buffer_size_expression(uint32_t id); std::string builtin_qualifier(spv::BuiltIn builtin); - std::string builtin_type_decl(spv::BuiltIn builtin); + std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0); std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma); std::string member_attribute_qualifier(const SPIRType &type, uint32_t index); std::string argument_decl(const SPIRFunction::Parameter &arg);