From 61c31c60544a66f82030cf99cb46edc0fed022aa Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Tue, 7 Mar 2017 13:27:04 +0100 Subject: [PATCH] Make use of explicit locations in HLSL. --- reference/shaders-hlsl/frag/mrt.frag | 31 ++++ reference/shaders-hlsl/vert/basic.vert | 2 +- reference/shaders-hlsl/vert/locations.vert | 68 +++++++++ shaders-hlsl/frag/mrt.frag | 15 ++ shaders-hlsl/vert/locations.vert | 33 +++++ spirv_cross.cpp | 2 +- spirv_cross.hpp | 2 +- spirv_hlsl.cpp | 161 +++++++++++++-------- spirv_hlsl.hpp | 4 +- spirv_msl.cpp | 2 +- spirv_msl.hpp | 2 +- 11 files changed, 254 insertions(+), 68 deletions(-) create mode 100644 reference/shaders-hlsl/frag/mrt.frag create mode 100644 reference/shaders-hlsl/vert/locations.vert create mode 100644 shaders-hlsl/frag/mrt.frag create mode 100644 shaders-hlsl/vert/locations.vert diff --git a/reference/shaders-hlsl/frag/mrt.frag b/reference/shaders-hlsl/frag/mrt.frag new file mode 100644 index 00000000..b906e161 --- /dev/null +++ b/reference/shaders-hlsl/frag/mrt.frag @@ -0,0 +1,31 @@ +static float4 RT0; +static float4 RT1; +static float4 RT2; +static float4 RT3; + +struct SPIRV_Cross_Output +{ + float4 RT0 : COLOR0; + float4 RT1 : COLOR1; + float4 RT2 : COLOR2; + float4 RT3 : COLOR3; +}; + +void frag_main() +{ + RT0 = float4(1.0f, 1.0f, 1.0f, 1.0f); + RT1 = float4(2.0f, 2.0f, 2.0f, 2.0f); + RT2 = float4(3.0f, 3.0f, 3.0f, 3.0f); + RT3 = float4(4.0f, 4.0f, 4.0f, 4.0f); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.RT0 = RT0; + stage_output.RT1 = RT1; + stage_output.RT2 = RT2; + stage_output.RT3 = RT3; + return stage_output; +} diff --git a/reference/shaders-hlsl/vert/basic.vert b/reference/shaders-hlsl/vert/basic.vert index 1fa29908..4c4d8e40 100644 --- a/reference/shaders-hlsl/vert/basic.vert +++ b/reference/shaders-hlsl/vert/basic.vert @@ -23,7 +23,7 @@ struct SPIRV_Cross_Input struct SPIRV_Cross_Output { float4 gl_Position : POSITION; - float3 vNormal : TEXCOORD2; + float3 vNormal : TEXCOORD0; }; void vert_main() diff --git a/reference/shaders-hlsl/vert/locations.vert b/reference/shaders-hlsl/vert/locations.vert new file mode 100644 index 00000000..9a5c9da3 --- /dev/null +++ b/reference/shaders-hlsl/vert/locations.vert @@ -0,0 +1,68 @@ +struct Foo +{ + float3 a; + float3 b; + float3 c; +}; + +uniform float4 gl_HalfPixel; + +static float4 gl_Position; +static float4 Input2; +static float4 Input4; +static float4 Input0; +static float vLocation0; +static float vLocation1; +static float vLocation2[2]; +static Foo vLocation4; +static float vLocation7; + +struct SPIRV_Cross_Input +{ + float4 Input2 : TEXCOORD2; + float4 Input4 : TEXCOORD4; + float4 Input0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : POSITION; + float vLocation0 : TEXCOORD0; + float vLocation1 : TEXCOORD1; + float vLocation2[2] : TEXCOORD2; + Foo vLocation4 : TEXCOORD4; + float vLocation7 : TEXCOORD7; +}; + +void vert_main() +{ + gl_Position = ((float4(1.0f, 1.0f, 1.0f, 1.0f) + Input2) + Input4) + Input0; + vLocation0 = 0.0f; + vLocation1 = 1.0f; + vLocation2[0] = 2.0f; + vLocation2[1] = 2.0f; + Foo foo; + foo.a = float3(1.0f, 1.0f, 1.0f); + foo.b = float3(1.0f, 1.0f, 1.0f); + foo.c = float3(1.0f, 1.0f, 1.0f); + vLocation4 = foo; + vLocation7 = 7.0f; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + Input2 = stage_input.Input2; + Input4 = stage_input.Input4; + Input0 = stage_input.Input0; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vLocation0 = vLocation0; + stage_output.vLocation1 = vLocation1; + stage_output.vLocation2 = vLocation2; + stage_output.vLocation4 = vLocation4; + stage_output.vLocation7 = vLocation7; + stage_output.gl_Position.x = stage_output.gl_Position.x - gl_HalfPixel.x * stage_output.gl_Position.w; + stage_output.gl_Position.y = stage_output.gl_Position.y + gl_HalfPixel.y * stage_output.gl_Position.w; + return stage_output; +} diff --git a/shaders-hlsl/frag/mrt.frag b/shaders-hlsl/frag/mrt.frag new file mode 100644 index 00000000..77a2bb29 --- /dev/null +++ b/shaders-hlsl/frag/mrt.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 RT0; +layout(location = 1) out vec4 RT1; +layout(location = 2) out vec4 RT2; +layout(location = 3) out vec4 RT3; + +void main() +{ + RT0 = vec4(1.0); + RT1 = vec4(2.0); + RT2 = vec4(3.0); + RT3 = vec4(4.0); +} diff --git a/shaders-hlsl/vert/locations.vert b/shaders-hlsl/vert/locations.vert new file mode 100644 index 00000000..516f71b0 --- /dev/null +++ b/shaders-hlsl/vert/locations.vert @@ -0,0 +1,33 @@ +#version 310 es + +struct Foo +{ + vec3 a; + vec3 b; + vec3 c; +}; + +layout(location = 2) in vec4 Input2; +layout(location = 4) in vec4 Input4; +in vec4 Input0; + +layout(location = 0) out float vLocation0; +layout(location = 1) out float vLocation1; +out float vLocation2[2]; +out Foo vLocation4; +out float vLocation7; + +void main() +{ + gl_Position = vec4(1.0) + Input2 + Input4 + Input0; + vLocation0 = 0.0; + vLocation1 = 1.0; + vLocation2[0] = 2.0; + vLocation2[1] = 2.0; + Foo foo; + foo.a = vec3(1.0); + foo.b = vec3(1.0); + foo.c = vec3(1.0); + vLocation4 = foo; + vLocation7 = 7.0; +} diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 216f97cc..60958a13 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -136,7 +136,7 @@ bool Compiler::block_is_pure(const SPIRBlock &block) return true; } -string Compiler::to_name(uint32_t id, bool allow_alias) +string Compiler::to_name(uint32_t id, bool allow_alias) const { if (allow_alias && ids.at(id).get_type() == TypeType) { diff --git a/spirv_cross.hpp b/spirv_cross.hpp index 7b69510f..4be46ed4 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -411,7 +411,7 @@ protected: std::unordered_set selection_merge_targets; std::unordered_set multiselect_merge_targets; - virtual std::string to_name(uint32_t id, bool allow_alias = true); + virtual std::string to_name(uint32_t id, bool allow_alias = true) const; bool is_builtin_variable(const SPIRVariable &var) const; bool is_hidden_variable(const SPIRVariable &var, bool include_builtins = false) const; bool is_immutable(uint32_t id) const; diff --git a/spirv_hlsl.cpp b/spirv_hlsl.cpp index 94574af7..6f11cbe3 100644 --- a/spirv_hlsl.cpp +++ b/spirv_hlsl.cpp @@ -22,39 +22,6 @@ using namespace spv; using namespace spirv_cross; using namespace std; -namespace -{ -struct VariableComparator -{ - VariableComparator(const CompilerHLSL &compiler_) - : compiler(compiler_) - { - } - - bool operator()(SPIRVariable *var1, SPIRVariable *var2) - { - if (compiler.get_decoration_mask(var1->self) & compiler.get_decoration_mask(var2->self) & - (1ull << DecorationLocation)) - return compiler.get_decoration(var1->self, DecorationLocation) < - compiler.get_decoration(var2->self, DecorationLocation); - - auto &name1 = compiler.get_name(var1->self); - auto &name2 = compiler.get_name(var2->self); - - if (name1.empty() && name2.empty()) - return var1->self < var2->self; - if (name1.empty()) - return true; - else if (name2.empty()) - return false; - - return name1.compare(name2) < 0; - } - - const CompilerHLSL &compiler; -}; -} - string CompilerHLSL::type_to_glsl(const SPIRType &type) { // Ignore the pointer type since GLSL doesn't have pointers. @@ -163,21 +130,8 @@ void CompilerHLSL::emit_header() void CompilerHLSL::emit_interface_block_globally(const SPIRVariable &var) { - auto &execution = get_entry_point(); - add_resource_name(var.self); - - if (execution.model == ExecutionModelVertex && var.storage == StorageClassInput && is_builtin_variable(var)) - { - } - else if (execution.model == ExecutionModelVertex && var.storage == StorageClassOutput && is_builtin_variable(var)) - { - statement("static float4 gl_Position;"); - } - else - { - statement("static ", variable_decl(var), ";"); - } + statement("static ", variable_decl(var), ";"); } const char *CompilerHLSL::to_storage_qualifiers_glsl(const SPIRVariable &var) @@ -249,12 +203,37 @@ void CompilerHLSL::emit_builtin_inputs_in_struct() } } -void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, uint32_t &binding_number) +uint32_t CompilerHLSL::type_to_consumed_locations(const SPIRType &type) const +{ + // TODO: Need to verify correctness. + uint32_t elements = 0; + + if (type.basetype == SPIRType::Struct) + { + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + elements += type_to_consumed_locations(get(type.member_types[i])); + } + else + { + uint32_t array_multiplier = 1; + for (uint32_t i = 0; i < uint32_t(type.array.size()); i++) + { + if (type.array_size_literal[i]) + array_multiplier *= type.array[i]; + else + array_multiplier *= get(type.array[i]).scalar(); + } + elements += array_multiplier * type.columns; + } + return elements; +} + +void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unordered_set &active_locations) { auto &execution = get_entry_point(); auto &type = get(var.basetype); - string binding = "TEXCOORD"; + string binding; bool use_binding_number = true; bool legacy = options.shader_model <= 30; if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput) @@ -263,27 +242,52 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, uint3 use_binding_number = false; } + const auto get_vacant_location = [&]() -> uint32_t { + for (uint32_t i = 0; i < 64; i++) + if (!active_locations.count(i)) + return i; + SPIRV_CROSS_THROW("All locations from 0 to 63 are exhausted."); + }; + auto &m = meta[var.self].decoration; + auto name = to_name(var.self); if (use_binding_number) { + uint32_t binding_number; + + // If an explicit location exists, use it with TEXCOORD[N] semantic. + // Otherwise, pick a vacant location. + if (m.decoration_flags & (1ull << DecorationLocation)) + binding_number = m.location; + else + binding_number = get_vacant_location(); + if (type.columns > 1) { + if (!type.array.empty()) + SPIRV_CROSS_THROW("Arrays of matrices used as input/output. This is not supported."); + + // Unroll matrices. for (uint32_t i = 0; i < type.columns; i++) { SPIRType newtype = type; newtype.columns = 1; - statement(variable_decl(newtype, join(m.alias, "_", i)), " : ", binding, binding_number++, ";"); + statement(variable_decl(newtype, join(name, "_", i)), " : TEXCOORD", binding_number, ";"); + active_locations.insert(binding_number++); } - --binding_number; } else - statement(variable_decl(type, m.alias), " : ", binding, binding_number, ";"); + { + statement(variable_decl(type, name), " : TEXCOORD", binding_number, ";"); + + // Structs and arrays should consume more locations. + uint32_t consumed_locations = type_to_consumed_locations(type); + for (uint32_t i = 0; i < consumed_locations; i++) + active_locations.insert(binding_number + i); + } } else - statement(variable_decl(type, m.alias), " : ", binding, ";"); - - if (use_binding_number) - binding_number++; + statement(variable_decl(type, name), " : ", binding, ";"); } void CompilerHLSL::emit_builtin_variables() @@ -423,7 +427,8 @@ void CompilerHLSL::emit_resources() statement(""); emitted = false; - uint32_t binding_number = 0; + unordered_set active_inputs; + unordered_set active_outputs; vector input_variables; vector output_variables; for (auto &id : ids) @@ -445,17 +450,49 @@ void CompilerHLSL::emit_resources() } } + const auto variable_compare = [&](const SPIRVariable *a, const SPIRVariable *b) -> bool { + // Sort input and output variables based on, from more robust to less robust: + // - Location + // - Variable has a location + // - Name comparison + // - Variable has a name + // - Fallback: ID + bool has_location_a = (get_decoration_mask(a->self) & (1ull << DecorationLocation)) != 0; + bool has_location_b = (get_decoration_mask(b->self) & (1ull << DecorationLocation)) != 0; + + if (has_location_a && has_location_b) + { + return get_decoration(a->self, DecorationLocation) < + get_decoration(b->self, DecorationLocation); + } + else if (has_location_a && !has_location_b) + return true; + else if (!has_location_a && has_location_b) + return false; + + const auto &name1 = to_name(a->self); + const auto &name2 = to_name(b->self); + + if (name1.empty() && name2.empty()) + return a->self < b->self; + else if (name1.empty()) + return true; + else if (name2.empty()) + return false; + + return name1.compare(name2) < 0; + }; + if (!input_variables.empty() || active_input_builtins) { require_input = true; statement("struct SPIRV_Cross_Input"); begin_scope(); - // FIXME: Use locations properly if they exist. - sort(input_variables.begin(), input_variables.end(), VariableComparator(*this)); + sort(input_variables.begin(), input_variables.end(), variable_compare); emit_builtin_inputs_in_struct(); for (auto var : input_variables) - emit_interface_block_in_struct(*var, binding_number); + emit_interface_block_in_struct(*var, active_inputs); end_scope_decl(); statement(""); } @@ -469,10 +506,10 @@ void CompilerHLSL::emit_resources() begin_scope(); // FIXME: Use locations properly if they exist. - sort(output_variables.begin(), output_variables.end(), VariableComparator(*this)); + sort(output_variables.begin(), output_variables.end(), variable_compare); emit_builtin_outputs_in_struct(); for (auto var : output_variables) - emit_interface_block_in_struct(*var, binding_number); + emit_interface_block_in_struct(*var, active_outputs); end_scope_decl(); statement(""); } diff --git a/spirv_hlsl.hpp b/spirv_hlsl.hpp index 2a86d984..7a1224cf 100644 --- a/spirv_hlsl.hpp +++ b/spirv_hlsl.hpp @@ -57,7 +57,7 @@ private: void emit_header() override; void emit_resources(); void emit_interface_block_globally(const SPIRVariable &type); - void emit_interface_block_in_struct(const SPIRVariable &type, uint32_t &binding_number); + void emit_interface_block_in_struct(const SPIRVariable &type, std::unordered_set &active_locations); void emit_builtin_inputs_in_struct(); void emit_builtin_outputs_in_struct(); void emit_texture_op(const Instruction &i) override; @@ -76,6 +76,8 @@ private: void emit_builtin_variables(); bool require_output = false; bool require_input = false; + + uint32_t type_to_consumed_locations(const SPIRType &type) const; }; } diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 502141f1..20f542d1 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -1794,7 +1794,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) // If we're currently in the entry point function, and the object // has a qualified name, use it, otherwise use the standard name. -string CompilerMSL::to_name(uint32_t id, bool allow_alias) +string CompilerMSL::to_name(uint32_t id, bool allow_alias) const { if (current_function && (current_function->self == entry_point)) { diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 9416a343..e9366058 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -136,7 +136,7 @@ protected: std::string constant_expression(const SPIRConstant &c) override; size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const override; std::string to_func_call_arg(uint32_t id) override; - std::string to_name(uint32_t id, bool allow_alias = true) override; + std::string to_name(uint32_t id, bool allow_alias = true) const override; std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj, bool has_array_offsets, bool has_offset, bool has_grad, bool has_lod, bool has_dref) override;