diff options
Diffstat (limited to '3rdparty/bgfx/3rdparty/spirv-cross/spirv_glsl.cpp')
-rw-r--r-- | 3rdparty/bgfx/3rdparty/spirv-cross/spirv_glsl.cpp | 330 |
1 files changed, 257 insertions, 73 deletions
diff --git a/3rdparty/bgfx/3rdparty/spirv-cross/spirv_glsl.cpp b/3rdparty/bgfx/3rdparty/spirv-cross/spirv_glsl.cpp index fccc8d36a97..8af7e76e7c9 100644 --- a/3rdparty/bgfx/3rdparty/spirv-cross/spirv_glsl.cpp +++ b/3rdparty/bgfx/3rdparty/spirv-cross/spirv_glsl.cpp @@ -324,6 +324,9 @@ void CompilerGLSL::reset() forwarded_temporaries.clear(); suppressed_usage_tracking.clear(); + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + flushed_phi_variables.clear(); + reset_name_caches(); ir.for_each_typed_id<SPIRFunction>([&](uint32_t, SPIRFunction &func) { @@ -502,6 +505,7 @@ string CompilerGLSL::compile() backend.allow_precision_qualifiers = true; backend.force_gl_in_out_block = true; backend.supports_extensions = true; + backend.use_array_constructor = true; // Scan the SPIR-V to find trivial uses of extensions. fixup_type_alias(); @@ -3032,7 +3036,7 @@ string CompilerGLSL::to_composite_constructor_expression(uint32_t id) return to_rerolled_array_expression(to_enclosed_expression(id), type); } else - return to_expression(id); + return to_unpacked_expression(id); } string CompilerGLSL::to_expression(uint32_t id, bool register_expression_read) @@ -3394,11 +3398,19 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c) { // Handles Arrays and structures. string res; + + // Allow Metal to use the array<T> template to make arrays a value type + bool needs_trailing_tracket = false; if (backend.use_initializer_list && backend.use_typed_initializer_list && type.basetype == SPIRType::Struct && type.array.empty()) { res = type_to_glsl_constructor(type) + "{ "; } + else if (backend.use_initializer_list && backend.use_typed_initializer_list && !type.array.empty()) + { + res = type_to_glsl_constructor(type) + "({ "; + needs_trailing_tracket = true; + } else if (backend.use_initializer_list) { res = "{ "; @@ -3421,8 +3433,23 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c) } res += backend.use_initializer_list ? " }" : ")"; + if (needs_trailing_tracket) + res += ")"; + return res; } + else if (type.basetype == SPIRType::Struct && type.member_types.size() == 0) + { + // Metal tessellation likes empty structs which are then constant expressions. + if (backend.supports_empty_struct) + return "{ }"; + else if (backend.use_typed_initializer_list) + return join(type_to_glsl(get<SPIRType>(c.constant_type)), "{ 0 }"); + else if (backend.use_initializer_list) + return "{ 0 }"; + else + return join(type_to_glsl(get<SPIRType>(c.constant_type)), "(0)"); + } else if (c.columns() == 1) { return constant_expression_vector(c, 0); @@ -4159,8 +4186,18 @@ void CompilerGLSL::emit_unrolled_unary_op(uint32_t result_type, uint32_t result_ } void CompilerGLSL::emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, - const char *op) + const char *op, bool negate, SPIRType::BaseType expected_type) { + auto &type0 = expression_type(op0); + auto &type1 = expression_type(op1); + + SPIRType target_type0 = type0; + SPIRType target_type1 = type1; + target_type0.basetype = expected_type; + target_type1.basetype = expected_type; + target_type0.vecsize = 1; + target_type1.vecsize = 1; + auto &type = get<SPIRType>(result_type); auto expr = type_to_glsl_constructor(type); expr += '('; @@ -4168,11 +4205,25 @@ void CompilerGLSL::emit_unrolled_binary_op(uint32_t result_type, uint32_t result { // Make sure to call to_expression multiple times to ensure // that these expressions are properly flushed to temporaries if needed. - expr += to_extract_component_expression(op0, i); + if (negate) + expr += "!("; + + if (expected_type != SPIRType::Unknown && type0.basetype != expected_type) + expr += bitcast_expression(target_type0, type0.basetype, to_extract_component_expression(op0, i)); + else + expr += to_extract_component_expression(op0, i); + expr += ' '; expr += op; expr += ' '; - expr += to_extract_component_expression(op1, i); + + if (expected_type != SPIRType::Unknown && type1.basetype != expected_type) + expr += bitcast_expression(target_type1, type1.basetype, to_extract_component_expression(op1, i)); + else + expr += to_extract_component_expression(op1, i); + + if (negate) + expr += ")"; if (i + 1 < type.vecsize) expr += ", "; @@ -6562,6 +6613,36 @@ const char *CompilerGLSL::index_to_swizzle(uint32_t index) } } +void CompilerGLSL::access_chain_internal_append_index(std::string &expr, uint32_t /*base*/, const SPIRType *type, + AccessChainFlags flags, bool & /*access_chain_is_arrayed*/, + uint32_t index) +{ + bool index_is_literal = (flags & ACCESS_CHAIN_INDEX_IS_LITERAL_BIT) != 0; + bool register_expression_read = (flags & ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT) == 0; + + expr += "["; + + // If we are indexing into an array of SSBOs or UBOs, we need to index it with a non-uniform qualifier. + bool nonuniform_index = + has_decoration(index, DecorationNonUniformEXT) && + (has_decoration(type->self, DecorationBlock) || has_decoration(type->self, DecorationBufferBlock)); + if (nonuniform_index) + { + expr += backend.nonuniform_qualifier; + expr += "("; + } + + if (index_is_literal) + expr += convert_to_string(index); + else + expr += to_expression(index, register_expression_read); + + if (nonuniform_index) + expr += ")"; + + expr += "]"; +} + string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags, AccessChainMeta *meta) { @@ -6613,27 +6694,7 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice bool dimension_flatten = false; const auto append_index = [&](uint32_t index) { - expr += "["; - - // If we are indexing into an array of SSBOs or UBOs, we need to index it with a non-uniform qualifier. - bool nonuniform_index = - has_decoration(index, DecorationNonUniformEXT) && - (has_decoration(type->self, DecorationBlock) || has_decoration(type->self, DecorationBufferBlock)); - if (nonuniform_index) - { - expr += backend.nonuniform_qualifier; - expr += "("; - } - - if (index_is_literal) - expr += convert_to_string(index); - else - expr += to_expression(index, register_expression_read); - - if (nonuniform_index) - expr += ")"; - - expr += "]"; + access_chain_internal_append_index(expr, base, type, flags, access_chain_is_arrayed, index); }; for (uint32_t i = 0; i < count; i++) @@ -6756,7 +6817,9 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice if (!pending_array_enclose) expr += "]"; } - else + // Some builtins are arrays in SPIR-V but not in other languages, e.g. gl_SampleMask[] is an array in SPIR-V but not in Metal. + // By throwing away the index, we imply the index was 0, which it must be for gl_SampleMask. + else if (!builtin_translates_to_nonarray(BuiltIn(get_decoration(base, DecorationBuiltIn)))) { append_index(index); } @@ -6849,8 +6912,16 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice else if (ir.ids[index].get_type() == TypeConstant && !is_packed && !row_major_matrix_needs_conversion) { auto &c = get<SPIRConstant>(index); - expr += "."; - expr += index_to_swizzle(c.scalar()); + if (c.specialization) + { + // If the index is a spec constant, we cannot turn extract into a swizzle. + expr += join("[", to_expression(index), "]"); + } + else + { + expr += "."; + expr += index_to_swizzle(c.scalar()); + } } else if (index_is_literal) { @@ -7470,23 +7541,29 @@ string CompilerGLSL::variable_decl_function_local(SPIRVariable &var) void CompilerGLSL::emit_variable_temporary_copies(const SPIRVariable &var) { - if (var.allocate_temporary_copy) + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + if (var.allocate_temporary_copy && !flushed_phi_variables.count(var.self)) { auto &type = get<SPIRType>(var.basetype); auto &flags = get_decoration_bitset(var.self); statement(flags_to_qualifiers_glsl(type, flags), variable_decl(type, join("_", var.self, "_copy")), ";"); + flushed_phi_variables.insert(var.self); } } void CompilerGLSL::flush_variable_declaration(uint32_t id) { + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred auto *var = maybe_get<SPIRVariable>(id); if (var && var->deferred_declaration) { statement(variable_decl_function_local(*var), ";"); - emit_variable_temporary_copies(*var); var->deferred_declaration = false; } + if (var) + { + emit_variable_temporary_copies(*var); + } } bool CompilerGLSL::remove_duplicate_swizzle(string &op) @@ -7816,6 +7893,10 @@ uint32_t CompilerGLSL::get_integer_width_for_instruction(const Instruction &inst case OpSLessThanEqual: case OpSGreaterThan: case OpSGreaterThanEqual: + case OpULessThan: + case OpULessThanEqual: + case OpUGreaterThan: + case OpUGreaterThanEqual: return expression_type(ops[2]).width; default: @@ -8257,22 +8338,32 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) string constructor_op; if (backend.use_initializer_list && composite) { + bool needs_trailing_tracket = false; // Only use this path if we are building composites. // This path cannot be used for arithmetic. if (backend.use_typed_initializer_list && out_type.basetype == SPIRType::Struct && out_type.array.empty()) constructor_op += type_to_glsl_constructor(get<SPIRType>(result_type)); + else if (backend.use_typed_initializer_list && !out_type.array.empty()) + { + // MSL path. Array constructor is baked into type here, do not use _constructor variant. + constructor_op += type_to_glsl_constructor(get<SPIRType>(result_type)) + "("; + needs_trailing_tracket = true; + } constructor_op += "{ "; + if (type_is_empty(out_type) && !backend.supports_empty_struct) constructor_op += "0"; else if (splat) - constructor_op += to_expression(elems[0]); + constructor_op += to_unpacked_expression(elems[0]); else constructor_op += build_composite_combiner(result_type, elems, length); constructor_op += " }"; + if (needs_trailing_tracket) + constructor_op += ")"; } else if (swizzle_splat && !composite) { - constructor_op = remap_swizzle(get<SPIRType>(result_type), 1, to_expression(elems[0])); + constructor_op = remap_swizzle(get<SPIRType>(result_type), 1, to_unpacked_expression(elems[0])); } else { @@ -8280,7 +8371,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (type_is_empty(out_type) && !backend.supports_empty_struct) constructor_op += "0"; else if (splat) - constructor_op += to_expression(elems[0]); + constructor_op += to_unpacked_expression(elems[0]); else constructor_op += build_composite_combiner(result_type, elems, length); constructor_op += ")"; @@ -8838,7 +8929,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) auto &type = get<SPIRType>(result_type); if (type.vecsize > 1) - emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "||"); + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "||", false, SPIRType::Unknown); else GLSL_BOP(||); break; @@ -8852,7 +8943,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) auto &type = get<SPIRType>(result_type); if (type.vecsize > 1) - emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "&&"); + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "&&", false, SPIRType::Unknown); else GLSL_BOP(&&); break; @@ -8909,7 +9000,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpUGreaterThan: case OpSGreaterThan: { - auto type = opcode == OpUGreaterThan ? SPIRType::UInt : SPIRType::Int; + auto type = opcode == OpUGreaterThan ? uint_type : int_type; if (expression_type(ops[2]).vecsize > 1) GLSL_BFOP_CAST(greaterThan, type); else @@ -8929,7 +9020,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpUGreaterThanEqual: case OpSGreaterThanEqual: { - auto type = opcode == OpUGreaterThanEqual ? SPIRType::UInt : SPIRType::Int; + auto type = opcode == OpUGreaterThanEqual ? uint_type : int_type; if (expression_type(ops[2]).vecsize > 1) GLSL_BFOP_CAST(greaterThanEqual, type); else @@ -8949,7 +9040,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpULessThan: case OpSLessThan: { - auto type = opcode == OpULessThan ? SPIRType::UInt : SPIRType::Int; + auto type = opcode == OpULessThan ? uint_type : int_type; if (expression_type(ops[2]).vecsize > 1) GLSL_BFOP_CAST(lessThan, type); else @@ -8969,7 +9060,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) case OpULessThanEqual: case OpSLessThanEqual: { - auto type = opcode == OpULessThanEqual ? SPIRType::UInt : SPIRType::Int; + auto type = opcode == OpULessThanEqual ? uint_type : int_type; if (expression_type(ops[2]).vecsize > 1) GLSL_BFOP_CAST(lessThanEqual, type); else @@ -9614,11 +9705,18 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) { uint32_t result_type = ops[0]; uint32_t id = ops[1]; - auto &e = set<SPIRExpression>(id, join(to_expression(ops[2]), ", ", to_expression(ops[3])), result_type, true); + + auto coord_expr = to_expression(ops[3]); + auto target_coord_type = expression_type(ops[3]); + target_coord_type.basetype = SPIRType::Int; + coord_expr = bitcast_expression(target_coord_type, expression_type(ops[3]).basetype, coord_expr); + + auto &e = set<SPIRExpression>(id, join(to_expression(ops[2]), ", ", coord_expr), result_type, true); // When using the pointer, we need to know which variable it is actually loaded from. auto *var = maybe_get_backing_variable(ops[2]); e.loaded_from = var ? var->self : ID(0); + inherit_expression_dependencies(id, ops[3]); break; } @@ -10073,28 +10171,98 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; case OpFUnordEqual: - GLSL_BFOP(unsupported_FUnordEqual); - break; - case OpFUnordNotEqual: - GLSL_BFOP(unsupported_FUnordNotEqual); - break; - case OpFUnordLessThan: - GLSL_BFOP(unsupported_FUnordLessThan); - break; - case OpFUnordGreaterThan: - GLSL_BFOP(unsupported_FUnordGreaterThan); - break; - case OpFUnordLessThanEqual: - GLSL_BFOP(unsupported_FUnordLessThanEqual); - break; - case OpFUnordGreaterThanEqual: - GLSL_BFOP(unsupported_FUnordGreaterThanEqual); + { + // GLSL doesn't specify if floating point comparisons are ordered or unordered, + // but glslang always emits ordered floating point compares for GLSL. + // To get unordered compares, we can test the opposite thing and invert the result. + // This way, we force true when there is any NaN present. + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + + string expr; + if (expression_type(op0).vecsize > 1) + { + const char *comp_op = nullptr; + switch (opcode) + { + case OpFUnordEqual: + comp_op = "notEqual"; + break; + + case OpFUnordNotEqual: + comp_op = "equal"; + break; + + case OpFUnordLessThan: + comp_op = "greaterThanEqual"; + break; + + case OpFUnordLessThanEqual: + comp_op = "greaterThan"; + break; + + case OpFUnordGreaterThan: + comp_op = "lessThanEqual"; + break; + + case OpFUnordGreaterThanEqual: + comp_op = "lessThan"; + break; + + default: + assert(0); + break; + } + + expr = join("not(", comp_op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), "))"); + } + else + { + const char *comp_op = nullptr; + switch (opcode) + { + case OpFUnordEqual: + comp_op = " != "; + break; + + case OpFUnordNotEqual: + comp_op = " == "; + break; + + case OpFUnordLessThan: + comp_op = " >= "; + break; + + case OpFUnordLessThanEqual: + comp_op = " > "; + break; + + case OpFUnordGreaterThan: + comp_op = " <= "; + break; + + case OpFUnordGreaterThanEqual: + comp_op = " < "; + break; + + default: + assert(0); + break; + } + + expr = join("!(", to_enclosed_unpacked_expression(op0), comp_op, to_enclosed_unpacked_expression(op1), ")"); + } + + emit_op(ops[0], ops[1], expr, should_forward(op0) && should_forward(op1)); + inherit_expression_dependencies(ops[1], op0); + inherit_expression_dependencies(ops[1], op1); break; + } case OpReportIntersectionNV: statement("reportIntersectionNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ");"); @@ -10228,6 +10396,12 @@ void CompilerGLSL::append_global_func_args(const SPIRFunction &func, uint32_t in string CompilerGLSL::to_member_name(const SPIRType &type, uint32_t index) { + if (type.type_alias != TypeID(0) && + !has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked)) + { + return to_member_name(get<SPIRType>(type.type_alias), index); + } + auto &memb = ir.meta[type.self].members; if (index < memb.size() && !memb[index].alias.empty()) return memb[index].alias; @@ -10588,8 +10762,10 @@ string CompilerGLSL::to_array_size(const SPIRType &type, uint32_t index) // Tessellation control and evaluation shaders must have either gl_MaxPatchVertices or unsized arrays for input arrays. // Opt for unsized as it's the more "correct" variant to use. - if (type.storage == StorageClassInput && (get_entry_point().model == ExecutionModelTessellationControl || - get_entry_point().model == ExecutionModelTessellationEvaluation)) + if (type.storage == StorageClassInput && + (get_entry_point().model == ExecutionModelTessellationControl || + get_entry_point().model == ExecutionModelTessellationEvaluation) && + index == uint32_t(type.array.size() - 1)) return ""; auto &size = type.array[index]; @@ -10758,7 +10934,7 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t id) string CompilerGLSL::type_to_glsl_constructor(const SPIRType &type) { - if (type.array.size() > 1) + if (backend.use_array_constructor && type.array.size() > 1) { if (options.flatten_multidimensional_arrays) SPIRV_CROSS_THROW("Cannot flatten constructors of multidimensional array constructors, e.g. float[][]()."); @@ -10769,8 +10945,11 @@ string CompilerGLSL::type_to_glsl_constructor(const SPIRType &type) } auto e = type_to_glsl(type); - for (uint32_t i = 0; i < type.array.size(); i++) - e += "[]"; + if (backend.use_array_constructor) + { + for (uint32_t i = 0; i < type.array.size(); i++) + e += "[]"; + } return e; } @@ -11009,6 +11188,11 @@ void CompilerGLSL::flatten_buffer_block(VariableID id) flattened_buffer_blocks.insert(id); } +bool CompilerGLSL::builtin_translates_to_nonarray(spv::BuiltIn /*builtin*/) const +{ + return false; // GLSL itself does not need to translate array builtin types to non-array builtin types +} + bool CompilerGLSL::check_atomic_image(uint32_t id) { auto &type = expression_type(id); @@ -11198,14 +11382,6 @@ void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags) current_function = &func; auto &entry_block = get<SPIRBlock>(func.entry_block); - sort(begin(func.constant_arrays_needed_on_stack), end(func.constant_arrays_needed_on_stack)); - for (auto &array : func.constant_arrays_needed_on_stack) - { - auto &c = get<SPIRConstant>(array); - auto &type = get<SPIRType>(c.constant_type); - statement(variable_decl(type, join("_", array, "_array_copy")), " = ", constant_expression(c), ";"); - } - for (auto &v : func.local_variables) { auto &var = get<SPIRVariable>(v); @@ -12591,14 +12767,14 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s auto new_expr = join("_", target_id, "_unrolled"); statement(variable_decl(type, new_expr, target_id), ";"); string array_expr; - if (type.array_size_literal.front()) + if (type.array_size_literal.back()) { - array_expr = convert_to_string(type.array.front()); - if (type.array.front() == 0) + array_expr = convert_to_string(type.array.back()); + if (type.array.back() == 0) SPIRV_CROSS_THROW("Cannot unroll an array copy from unsized array."); } else - array_expr = to_expression(type.array.front()); + array_expr = to_expression(type.array.back()); // The array size might be a specialization constant, so use a for-loop instead. statement("for (int i = 0; i < int(", array_expr, "); i++)"); @@ -12776,6 +12952,14 @@ void CompilerGLSL::fixup_type_alias() // This is not allowed, drop the type_alias. type.type_alias = 0; } + else if (type.type_alias && !type_is_block_like(this->get<SPIRType>(type.type_alias))) + { + // If the alias master is not a block-like type, there is no reason to use type aliasing. + // This case can happen if two structs are declared with the same name, but they are unrelated. + // Aliases are only used to deal with aliased types for structs which are used in different buffer types + // which all create a variant of the same struct with different DecorationOffset values. + type.type_alias = 0; + } }); } |