From f591bc0d4a9829fa404f2316dad8676289d39bcb Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Fri, 30 Jun 2017 19:10:46 -0400 Subject: [PATCH] CompilerMSL enhancements. Support BuiltInFragDepth. Emit interface block for StorageClassUniformConstant. Throw exception when output or fragment input structs contain matrix or array. Dynamically created interface structs sorted by location number instead of alphabetically. Add Compiler::is_array() function. --- .../shaders-msl/flatten/swizzle.flatten.vert | 10 +- reference/shaders-msl/frag/builtins.frag | 24 +++++ reference/shaders-msl/vert/functions.vert | 6 +- reference/shaders-msl/vert/out_block.vert | 2 +- reference/shaders-msl/vert/ubo.alignment.vert | 2 +- shaders-msl/frag/builtins.frag | 11 +++ spirv_cross.cpp | 5 + spirv_cross.hpp | 1 + spirv_glsl.cpp | 2 +- spirv_msl.cpp | 96 ++++++++++++------- spirv_msl.hpp | 5 +- 11 files changed, 115 insertions(+), 49 deletions(-) create mode 100644 reference/shaders-msl/frag/builtins.frag create mode 100644 shaders-msl/frag/builtins.frag diff --git a/reference/shaders-msl/flatten/swizzle.flatten.vert b/reference/shaders-msl/flatten/swizzle.flatten.vert index 8903ea77..e6935777 100644 --- a/reference/shaders-msl/flatten/swizzle.flatten.vert +++ b/reference/shaders-msl/flatten/swizzle.flatten.vert @@ -23,12 +23,12 @@ struct UBO struct main0_out { - float4 oF [[user(locn5)]]; - float4 oE [[user(locn4)]]; - float4 oD [[user(locn3)]]; - float4 oC [[user(locn2)]]; - float4 oB [[user(locn1)]]; float4 oA [[user(locn0)]]; + float4 oB [[user(locn1)]]; + float4 oC [[user(locn2)]]; + float4 oD [[user(locn3)]]; + float4 oE [[user(locn4)]]; + float4 oF [[user(locn5)]]; float4 gl_Position [[position]]; }; diff --git a/reference/shaders-msl/frag/builtins.frag b/reference/shaders-msl/frag/builtins.frag new file mode 100644 index 00000000..9283d1a6 --- /dev/null +++ b/reference/shaders-msl/frag/builtins.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + float gl_FragDepth [[depth(any)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = gl_FragCoord + in.vColor; + out.gl_FragDepth = 0.5; + return out; +} + diff --git a/reference/shaders-msl/vert/functions.vert b/reference/shaders-msl/vert/functions.vert index 73e4da33..d0aaa7d2 100644 --- a/reference/shaders-msl/vert/functions.vert +++ b/reference/shaders-msl/vert/functions.vert @@ -21,11 +21,11 @@ struct main0_in struct main0_out { - float3 vRotRad [[user(locn2)]]; - float3 vRotDeg [[user(locn1)]]; float3 vNormal [[user(locn0)]]; - int2 vMSB [[user(locn4)]]; + float3 vRotDeg [[user(locn1)]]; + float3 vRotRad [[user(locn2)]]; int2 vLSB [[user(locn3)]]; + int2 vMSB [[user(locn4)]]; float4 gl_Position [[position]]; }; diff --git a/reference/shaders-msl/vert/out_block.vert b/reference/shaders-msl/vert/out_block.vert index ea73aad8..3ae18387 100644 --- a/reference/shaders-msl/vert/out_block.vert +++ b/reference/shaders-msl/vert/out_block.vert @@ -16,8 +16,8 @@ struct main0_in struct main0_out { - float4 VertexOut_color2 [[user(locn3)]]; float4 VertexOut_color [[user(locn2)]]; + float4 VertexOut_color2 [[user(locn3)]]; float4 gl_Position [[position]]; }; diff --git a/reference/shaders-msl/vert/ubo.alignment.vert b/reference/shaders-msl/vert/ubo.alignment.vert index 98a6988f..bc076def 100644 --- a/reference/shaders-msl/vert/ubo.alignment.vert +++ b/reference/shaders-msl/vert/ubo.alignment.vert @@ -20,9 +20,9 @@ struct main0_in struct main0_out { - float2 vSize [[user(locn2)]]; float3 vNormal [[user(locn0)]]; float3 vColor [[user(locn1)]]; + float2 vSize [[user(locn2)]]; float4 gl_Position [[position]]; }; diff --git a/shaders-msl/frag/builtins.frag b/shaders-msl/frag/builtins.frag new file mode 100644 index 00000000..99e6e2df --- /dev/null +++ b/shaders-msl/frag/builtins.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = gl_FragCoord + vColor; + gl_FragDepth = 0.5; +} diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 767f69ee..59550e80 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -475,6 +475,11 @@ bool Compiler::is_matrix(const SPIRType &type) const return type.vecsize > 1 && type.columns > 1; } +bool Compiler::is_array(const SPIRType &type) const +{ + return !type.array.empty(); +} + ShaderResources Compiler::get_shader_resources() const { return get_shader_resources(nullptr); diff --git a/spirv_cross.hpp b/spirv_cross.hpp index c2123077..6bf4fbcd 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -455,6 +455,7 @@ protected: bool is_scalar(const SPIRType &type) const; bool is_vector(const SPIRType &type) const; bool is_matrix(const SPIRType &type) const; + bool is_array(const SPIRType &type) const; const SPIRType &expression_type(uint32_t id) const; bool expression_is_lvalue(uint32_t id) const; bool variable_storage_is_aliased(const SPIRVariable &var); diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 2f75aed8..2d034a15 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -340,7 +340,7 @@ string CompilerGLSL::compile() std::string CompilerGLSL::get_partial_source() { - return buffer->str(); + return buffer ? buffer->str() : "No compiled source available yet."; } void CompilerGLSL::emit_header() diff --git a/spirv_msl.cpp b/spirv_msl.cpp index b7c68752..eaa7baab 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -87,6 +87,8 @@ string CompilerMSL::compile() update_active_builtins(); fixup_image_load_store_access(); + set_enabled_interface_variables(get_active_interface_variables()); + // Preprocess OpCodes to extract the need to output additional header content preprocess_op_codes(); @@ -168,7 +170,6 @@ string CompilerMSL::compile(MSLConfiguration &msl_cfg, vector *p_ // Register the need to output any custom functions. void CompilerMSL::preprocess_op_codes() { - set_enabled_interface_variables(get_active_interface_variables()); spv_function_implementations.clear(); OpCodePreprocessor preproc(*this); @@ -389,6 +390,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage) case StorageClassUniformConstant: { ib_var_ref = stage_uniform_var_name; + active_interface_variables.insert(ib_var_id); // Ensure will be emitted break; } @@ -413,10 +415,9 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage) bool is_builtin = is_member_builtin(type, mbr_idx, &builtin); auto &mbr_type = get(mbr_type_id); - if (is_matrix(mbr_type)) - { - exclude_member_from_stage_in(type, mbr_idx); - } + if (should_move_to_input_buffer(mbr_type, is_builtin, storage)) + move_member_to_input_buffer(type, mbr_idx); + else if (!is_builtin || has_active_builtin(builtin, storage)) { // Add a reference to the member to the interface struct. @@ -467,8 +468,8 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage) bool is_builtin = is_builtin_variable(*p_var); BuiltIn builtin = BuiltIn(get_decoration(p_var->self, DecorationBuiltIn)); - if (is_matrix(type)) - exclude_from_stage_in(*p_var); + if (should_move_to_input_buffer(type, is_builtin, storage)) + move_to_input_buffer(*p_var); else if (!is_builtin || has_active_builtin(builtin, storage)) { @@ -503,34 +504,58 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage) } } - // Sort the members of the interface structure by their attribute numbers. - // Oddly, Metal handles inputs better if they are sorted in reverse order, - // particularly if the offsets are all equal. + // Sort the members of the structure by their locations. + // Oddly, Metal handles inputs better if they are sorted in reverse order. MemberSorter::SortAspect sort_aspect = (storage == StorageClassInput) ? MemberSorter::LocationReverse : MemberSorter::Location; MemberSorter member_sorter(ib_type, meta[ib_type_id], sort_aspect); member_sorter.sort(); - // Sort input or output variables alphabetical - auto &execution = get_entry_point(); - if ((execution.model == ExecutionModelFragment && storage == StorageClassInput) || - (execution.model == ExecutionModelVertex && storage == StorageClassOutput)) - { - MemberSorter member_sorter_io(ib_type, meta[ib_type.self], MemberSorter::Alphabetical); - member_sorter_io.sort(); - } - return ib_var_id; } -// Excludes the specified input variable from the stage_in block structure. -// Instead, the variable is added to a block variable corresponding to a secondary MSL buffer. -// The main use case for this is when a stage_in variable contains a matrix, which is a rare occurrence. -void CompilerMSL::exclude_from_stage_in(SPIRVariable &var) +// Returns whether a variable of type and storage class should be moved from an interface +// block to a secondary input buffer block. +// This is the case for matrixes and arrays that appear in the stage_in interface block +// of a vertex function, and true is returned. +// Other types do not need to move, and false is returned. +// Matrices and arrays are not permitted in the output of a vertex function or the input +// or output of a fragment function, and in those cases, an exception is thrown. +bool CompilerMSL::should_move_to_input_buffer(SPIRType &type, bool is_builtin, StorageClass storage) +{ + if ((is_matrix(type) || is_array(type)) && !is_builtin) + { + auto &execution = get_entry_point(); + + if (execution.model == ExecutionModelVertex) + { + if (storage == StorageClassInput) + return true; + + if (storage == StorageClassOutput) + SPIRV_CROSS_THROW("The vertex function output structure may not include a matrix or array."); + } + else if (execution.model == ExecutionModelFragment) + { + if (storage == StorageClassInput) + SPIRV_CROSS_THROW("The fragment function stage_in structure may not include a matrix or array."); + + if (storage == StorageClassOutput) + SPIRV_CROSS_THROW("The fragment function output structure may not include a matrix or array."); + } + } + + return false; +} + +// Excludes the specified variable from an interface block structure. +// Instead, for the variable is added to a block variable corresponding to a secondary MSL buffer. +// The use case for this is when a vertex stage_in variable contains a matrix or array. +void CompilerMSL::move_to_input_buffer(SPIRVariable &var) { uint32_t var_id = var.self; - if (!(get_decoration_mask(var_id) & (1ull << DecorationLocation))) + if (!has_decoration(var_id, DecorationLocation)) return; uint32_t mbr_type_id = var.basetype; @@ -540,9 +565,9 @@ void CompilerMSL::exclude_from_stage_in(SPIRVariable &var) } // Excludes the specified type member from the stage_in block structure. -// Instead, the member is added to a block variable corresponding to a secondary MSL buffer. -// The main use case for this is when a stage_in variable contains a matrix, which is a rare occurrence. -void CompilerMSL::exclude_member_from_stage_in(const SPIRType &type, uint32_t index) +// Instead, for the variable is added to a block variable corresponding to a secondary MSL buffer. +// The use case for this is when a vertex stage_in variable contains a matrix or array. +void CompilerMSL::move_member_to_input_buffer(const SPIRType &type, uint32_t index) { uint32_t type_id = type.self; @@ -2607,19 +2632,22 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin) case BuiltInInstanceIndex: return "gl_InstanceIndex"; - // Output builtins qualified with output struct when used in the entry function + // When used in the entry function, output builtins are qualified with output struct name. case BuiltInPosition: case BuiltInPointSize: case BuiltInClipDistance: case BuiltInLayer: + case BuiltInFragDepth: if (current_function && (current_function->self == entry_point)) return stage_out_var_name + "." + CompilerGLSL::builtin_to_glsl(builtin); - else - return CompilerGLSL::builtin_to_glsl(builtin); + + break; default: - return CompilerGLSL::builtin_to_glsl(builtin); + break; } + + return CompilerGLSL::builtin_to_glsl(builtin); } // Returns an MSL string attribute qualifer for a SPIR-V builtin @@ -2663,16 +2691,12 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin) // Fragment function out case BuiltInFragDepth: - { if (execution.flags & (1ull << ExecutionModeDepthGreater)) return "depth(greater)"; else if (execution.flags & (1ull << ExecutionModeDepthLess)) return "depth(less)"; - else if (execution.flags & (1ull << ExecutionModeDepthUnchanged)) - return "depth(any)"; else return "depth(any)"; - } // Compute function in case BuiltInGlobalInvocationId: @@ -2987,7 +3011,7 @@ bool CompilerMSL::MemberSorter::operator()(uint32_t mbr_idx1, uint32_t mbr_idx2) return (mbr_meta1.offset < mbr_meta2.offset) || ((mbr_meta1.offset == mbr_meta2.offset) && (mbr_meta1.location > mbr_meta2.location)); case Alphabetical: - return mbr_meta1.alias > mbr_meta2.alias; + return mbr_meta1.alias < mbr_meta2.alias; default: return false; } diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 25f1a37b..3e9e49ca 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -202,8 +202,9 @@ protected: uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index); size_t get_declared_struct_member_alignment(const SPIRType &struct_type, uint32_t index) const; std::string to_component_argument(uint32_t id); - void exclude_from_stage_in(SPIRVariable &var); - void exclude_member_from_stage_in(const SPIRType &type, uint32_t index); + bool should_move_to_input_buffer(SPIRType &type, bool is_builtin, spv::StorageClass storage); + void move_to_input_buffer(SPIRVariable &var); + void move_member_to_input_buffer(const SPIRType &type, uint32_t index); std::string add_input_buffer_block_member(uint32_t mbr_type_id, std::string mbr_name, uint32_t mbr_locn); uint32_t get_input_buffer_block_var_id(uint32_t msl_buffer); void align_struct(SPIRType &ib_type);