diff --git a/spirv_hlsl.cpp b/spirv_hlsl.cpp index a6e0ce500..cf460d453 100644 --- a/spirv_hlsl.cpp +++ b/spirv_hlsl.cpp @@ -4721,16 +4721,12 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) case OpFMod: { -#ifndef SPIRV_CROSS_WEBMIN if (!requires_op_fmod) { requires_op_fmod = true; force_recompile(); } CompilerGLSL_emit_instruction(instruction); -#else - SPIRV_CROSS_INVALID_CALL(); -#endif break; } @@ -4814,12 +4810,8 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) case OpFwidth: case OpFwidthCoarse: case OpFwidthFine: -#ifndef SPIRV_CROSS_WEBMIN HLSL_UFOP(fwidth); register_control_dependent_expression(ops[1]); -#else - SPIRV_CROSS_INVALID_CALL(); -#endif break; case OpLogicalNot: @@ -12955,7 +12947,6 @@ void CompilerHLSL::CompilerGLSL_emit_instruction(const Instruction &instruction) // ALU case OpIsNan: -#ifndef SPIRV_CROSS_WEBMIN if (!is_legacy()) GLSL_UFOP(isnan); else @@ -12967,13 +12958,9 @@ void CompilerHLSL::CompilerGLSL_emit_instruction(const Instruction &instruction) else emit_binary_op(ops[0], ops[1], ops[2], ops[2], "!="); } -#else - SPIRV_CROSS_INVALID_CALL(); -#endif break; case OpIsInf: -#ifndef SPIRV_CROSS_WEBMIN if (!is_legacy()) GLSL_UFOP(isinf); else @@ -13011,9 +12998,6 @@ void CompilerHLSL::CompilerGLSL_emit_instruction(const Instruction &instruction) inherit_expression_dependencies(result_id, operand); } -#else - SPIRV_CROSS_INVALID_CALL(); -#endif break; case OpSNegate: @@ -13412,7 +13396,6 @@ void CompilerHLSL::CompilerGLSL_emit_instruction(const Instruction &instruction) case OpLogicalOr: { -#ifndef SPIRV_CROSS_WEBMIN // No vector variant in GLSL for logical OR. auto result_type = ops[0]; auto id = ops[1]; @@ -13422,15 +13405,11 @@ void CompilerHLSL::CompilerGLSL_emit_instruction(const Instruction &instruction) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "||", false, SPIRType::Unknown); else GLSL_BOP(||); -#else - SPIRV_CROSS_INVALID_CALL(); -#endif break; } case OpLogicalAnd: { -#ifndef SPIRV_CROSS_WEBMIN // No vector variant in GLSL for logical AND. auto result_type = ops[0]; auto id = ops[1]; @@ -13440,9 +13419,6 @@ void CompilerHLSL::CompilerGLSL_emit_instruction(const Instruction &instruction) emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "&&", false, SPIRType::Unknown); else GLSL_BOP(&&); -#else - SPIRV_CROSS_INVALID_CALL(); -#endif break; } @@ -16313,6 +16289,189 @@ std::string CompilerHLSL::bitcast_expression(const SPIRType &target_type, SPIRTy return join(bitcast_glsl_op(target_type, src_type), "(", expr, ")"); } +void CompilerHLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type) +{ + string cast_op0, cast_op1; + auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type); + auto &out_type = get(result_type); + + // Special case boolean outputs since relational opcodes output booleans instead of int/uint. + string expr; + if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean) + { + expected_type.basetype = input_type; + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +void CompilerHLSL::emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op, SPIRType::BaseType input_type) +{ + auto &out_type = get(result_type); + auto expected_type = out_type; + expected_type.basetype = input_type; + string cast_op0 = + expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + string cast_op1 = + expression_type(op1).basetype != input_type ? bitcast_glsl(expected_type, op1) : to_unpacked_expression(op1); + string cast_op2 = + expression_type(op2).basetype != input_type ? bitcast_glsl(expected_type, op2) : to_unpacked_expression(op2); + + string expr; + if (out_type.basetype != input_type) + { + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1) && should_forward(op2)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); +} + +void CompilerHLSL::emit_emulated_ahyper_op(uint32_t result_type, uint32_t id, uint32_t op0, GLSLstd450 op) +{ + const char *one = backend.float_literal_suffix ? "1.0f" : "1.0"; + std::string expr; + bool forward = should_forward(op0); + + switch (op) + { + case GLSLstd450Asinh: + expr = join("log(", to_enclosed_expression(op0), " + sqrt(", to_enclosed_expression(op0), " * ", + to_enclosed_expression(op0), " + ", one, "))"); + emit_op(result_type, id, expr, forward); + break; + + case GLSLstd450Acosh: + expr = join("log(", to_enclosed_expression(op0), " + sqrt(", to_enclosed_expression(op0), " * ", + to_enclosed_expression(op0), " - ", one, "))"); + break; + + case GLSLstd450Atanh: + expr = join("log((", one, " + ", to_enclosed_expression(op0), + ") / " + "(", + one, " - ", to_enclosed_expression(op0), ")) * 0.5", backend.float_literal_suffix ? "f" : ""); + break; + + default: + SPIRV_CROSS_THROW("Invalid op."); + } + + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, op0); +} + +void CompilerHLSL::emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + 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(result_type); + auto expr = type_to_glsl_constructor(type); + expr += '('; + for (uint32_t i = 0; i < type.vecsize; i++) + { + // Make sure to call to_expression multiple times to ensure + // that these expressions are properly flushed to temporaries if needed. + 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 += ' '; + + 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 += ", "; + } + expr += ')'; + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +string CompilerHLSL::to_extract_component_expression(uint32_t id, uint32_t index) +{ + auto expr = to_enclosed_expression(id); + if (has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked)) + return join(expr, "[", index, "]"); + else + return join(expr, ".", index_to_swizzle(index)); +} + +void CompilerHLSL::emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op) +{ + auto &type = get(result_type); + auto expr = type_to_glsl_constructor(type); + expr += '('; + for (uint32_t i = 0; i < type.vecsize; i++) + { + // Make sure to call to_expression multiple times to ensure + // that these expressions are properly flushed to temporaries if needed. + expr += op; + expr += to_extract_component_expression(operand, i); + + if (i + 1 < type.vecsize) + expr += ", "; + } + expr += ')'; + emit_op(result_type, result_id, expr, should_forward(operand)); + + inherit_expression_dependencies(result_id, operand); +} + +void CompilerHLSL::emit_while_loop_initializers(const SPIRBlock &block) +{ + // While loops do not take initializers, so declare all of them outside. + for (auto &loop_var : block.loop_variables) + { + auto &var = get(loop_var); + statement(variable_decl(var), ";"); + } +} + #ifndef SPIRV_CROSS_WEBMIN CompilerHLSL::ShaderSubgroupSupportHelper::Result::Result() { @@ -18160,48 +18319,6 @@ string CompilerHLSL::to_combined_image_sampler(VariableID image_id, VariableID s } } -string CompilerHLSL::to_extract_component_expression(uint32_t id, uint32_t index) -{ - auto expr = to_enclosed_expression(id); - if (has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked)) - return join(expr, "[", index, "]"); - else - return join(expr, ".", index_to_swizzle(index)); -} - -void CompilerHLSL::emit_emulated_ahyper_op(uint32_t result_type, uint32_t id, uint32_t op0, GLSLstd450 op) -{ - const char *one = backend.float_literal_suffix ? "1.0f" : "1.0"; - std::string expr; - bool forward = should_forward(op0); - - switch (op) - { - case GLSLstd450Asinh: - expr = join("log(", to_enclosed_expression(op0), " + sqrt(", - to_enclosed_expression(op0), " * ", to_enclosed_expression(op0), " + ", one, "))"); - emit_op(result_type, id, expr, forward); - break; - - case GLSLstd450Acosh: - expr = join("log(", to_enclosed_expression(op0), " + sqrt(", - to_enclosed_expression(op0), " * ", to_enclosed_expression(op0), " - ", one, "))"); - break; - - case GLSLstd450Atanh: - expr = join("log((", one, " + ", to_enclosed_expression(op0), ") / " - "(", one, " - ", to_enclosed_expression(op0), ")) * 0.5", - backend.float_literal_suffix ? "f" : ""); - break; - - default: - SPIRV_CROSS_THROW("Invalid op."); - } - - emit_op(result_type, id, expr, forward); - inherit_expression_dependencies(id, op0); -} - void CompilerHLSL::convert_non_uniform_expression(string &expr, uint32_t ptr_id) { if (*backend.nonuniform_qualifier == '\0') @@ -18448,77 +18565,6 @@ std::pair CompilerHLSL::flattened_access_chain_offset( return std::make_pair(expr, offset); } -void CompilerHLSL::emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op) -{ - auto &type = get(result_type); - auto expr = type_to_glsl_constructor(type); - expr += '('; - for (uint32_t i = 0; i < type.vecsize; i++) - { - // Make sure to call to_expression multiple times to ensure - // that these expressions are properly flushed to temporaries if needed. - expr += op; - expr += to_extract_component_expression(operand, i); - - if (i + 1 < type.vecsize) - expr += ", "; - } - expr += ')'; - emit_op(result_type, result_id, expr, should_forward(operand)); - - inherit_expression_dependencies(result_id, operand); -} - -void CompilerHLSL::emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, - 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(result_type); - auto expr = type_to_glsl_constructor(type); - expr += '('; - for (uint32_t i = 0; i < type.vecsize; i++) - { - // Make sure to call to_expression multiple times to ensure - // that these expressions are properly flushed to temporaries if needed. - 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 += ' '; - - 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 += ", "; - } - expr += ')'; - emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); - - inherit_expression_dependencies(result_id, op0); - inherit_expression_dependencies(result_id, op1); -} - uint32_t CompilerHLSL::mask_relevant_memory_semantics(uint32_t semantics) { return semantics & (MemorySemanticsAtomicCounterMemoryMask | MemorySemanticsImageMemoryMask | @@ -18555,17 +18601,6 @@ void CompilerHLSL::emit_line_directive(uint32_t file_id, uint32_t line_literal) } } -void CompilerHLSL::emit_while_loop_initializers(const SPIRBlock &block) -{ - // While loops do not take initializers, so declare all of them outside. - for (auto &loop_var : block.loop_variables) - { - auto &var = get(loop_var); - statement(variable_decl(var), ";"); - } -} - - string CompilerHLSL::address_of_expression(const std::string &expr) { if (expr.size() > 3 && expr[0] == '(' && expr[1] == '*' && expr.back() == ')') @@ -19426,65 +19461,6 @@ void CompilerHLSL::request_workaround_wrapper_overload(TypeID id) } } -void CompilerHLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, - const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type) -{ - string cast_op0, cast_op1; - auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type); - auto &out_type = get(result_type); - - // Special case boolean outputs since relational opcodes output booleans instead of int/uint. - string expr; - if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean) - { - expected_type.basetype = input_type; - expr = bitcast_glsl_op(out_type, expected_type); - expr += '('; - expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); - expr += ')'; - } - else - { - expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); - } - - emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); - inherit_expression_dependencies(result_id, op0); - inherit_expression_dependencies(result_id, op1); -} - -void CompilerHLSL::emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, - uint32_t op2, const char *op, SPIRType::BaseType input_type) -{ - auto &out_type = get(result_type); - auto expected_type = out_type; - expected_type.basetype = input_type; - string cast_op0 = - expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); - string cast_op1 = - expression_type(op1).basetype != input_type ? bitcast_glsl(expected_type, op1) : to_unpacked_expression(op1); - string cast_op2 = - expression_type(op2).basetype != input_type ? bitcast_glsl(expected_type, op2) : to_unpacked_expression(op2); - - string expr; - if (out_type.basetype != input_type) - { - expr = bitcast_glsl_op(out_type, expected_type); - expr += '('; - expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); - expr += ')'; - } - else - { - expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); - } - - emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1) && should_forward(op2)); - inherit_expression_dependencies(result_id, op0); - inherit_expression_dependencies(result_id, op1); - inherit_expression_dependencies(result_id, op2); -} - void CompilerHLSL::emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op) { // Need to emulate this call. @@ -20485,18 +20461,6 @@ string CompilerHLSL::to_combined_image_sampler(VariableID , VariableID) SPIRV_CROSS_THROW("Invalid call."); } -string CompilerHLSL::to_extract_component_expression(uint32_t, uint32_t) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} - -void CompilerHLSL::emit_emulated_ahyper_op(uint32_t, uint32_t, uint32_t, GLSLstd450) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} - void CompilerHLSL::convert_non_uniform_expression(string &, uint32_t) { SPIRV_CROSS_INVALID_CALL(); @@ -20517,19 +20481,6 @@ std::pair CompilerHLSL::flattened_access_chain_offset( SPIRV_CROSS_THROW("Invalid call."); } -void CompilerHLSL::emit_unrolled_unary_op(uint32_t, uint32_t, uint32_t, const char *) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} - -void CompilerHLSL::emit_unrolled_binary_op(uint32_t, uint32_t, uint32_t, uint32_t, - const char *, bool, SPIRType::BaseType) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} - uint32_t CompilerHLSL::mask_relevant_memory_semantics(uint32_t) { SPIRV_CROSS_INVALID_CALL(); @@ -20548,12 +20499,6 @@ void CompilerHLSL::emit_line_directive(uint32_t, uint32_t) SPIRV_CROSS_THROW("Invalid call."); } -void CompilerHLSL::emit_while_loop_initializers(const SPIRBlock &) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} - string CompilerHLSL::address_of_expression(const std::string &) { SPIRV_CROSS_INVALID_CALL(); @@ -20761,20 +20706,6 @@ void CompilerHLSL::request_workaround_wrapper_overload(TypeID) SPIRV_CROSS_THROW("Invalid call."); } -void CompilerHLSL::emit_binary_func_op_cast(uint32_t, uint32_t, uint32_t, uint32_t, - const char *, SPIRType::BaseType, bool) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} - -void CompilerHLSL::emit_trinary_func_op_cast(uint32_t, uint32_t, uint32_t, uint32_t, - uint32_t, const char *, SPIRType::BaseType) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} - void CompilerHLSL::emit_nminmax_op(uint32_t, uint32_t, uint32_t, uint32_t, GLSLstd450) { SPIRV_CROSS_INVALID_CALL(); diff --git a/spirv_msl.cpp b/spirv_msl.cpp index dd6029187..c887633ae 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -24275,55 +24275,274 @@ bool CompilerMSL::optimize_read_modify_write(const SPIRType &type, const string return true; } -#ifndef SPIRV_CROSS_WEBMIN +bool CompilerMSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t) +{ + // This is handled from the outside where we deal with PtrToU/UToPtr and friends. + return false; +} -void CompilerMSL::store_flattened_struct(const string &basename, uint32_t rhs_id, const SPIRType &type, - const SmallVector &indices) +string CompilerMSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type) { - SmallVector sub_indices = indices; - sub_indices.push_back(0); + if (out_type.basetype == in_type.basetype) + return ""; - auto *member_type = &type; - for (auto &index : indices) - member_type = &get(member_type->member_types[index]); + assert(out_type.basetype != SPIRType::Boolean); + assert(in_type.basetype != SPIRType::Boolean); - for (uint32_t i = 0; i < uint32_t(member_type->member_types.size()); i++) + bool integral_cast = type_is_integral(out_type) && type_is_integral(in_type) && (out_type.vecsize == in_type.vecsize); + bool same_size_cast = (out_type.width * out_type.vecsize) == (in_type.width * in_type.vecsize); + + // Bitcasting can only be used between types of the same overall size. + // And always formally cast between integers, because it's trivial, and also + // because Metal can internally cast the results of some integer ops to a larger + // size (eg. short shift right becomes int), which means chaining integer ops + // together may introduce size variations that SPIR-V doesn't know about. + if (same_size_cast && !integral_cast) + return "as_type<" + type_to_glsl(out_type) + ">"; + else + return type_to_glsl(out_type); +} + +const char *CompilerMSL::descriptor_address_space(uint32_t id, StorageClass storage, const char *plain_address_space) const +{ + if (msl_options.argument_buffers) { - sub_indices.back() = i; - auto lhs = join(basename, "_", to_member_name(*member_type, i)); - ParsedIR::sanitize_underscores(lhs); + bool storage_class_is_descriptor = storage == StorageClassUniform || + storage == StorageClassStorageBuffer || + storage == StorageClassUniformConstant; - if (get(member_type->member_types[i]).basetype == SPIRType::Struct) + uint32_t desc_set = get_decoration(id, DecorationDescriptorSet); + if (storage_class_is_descriptor && descriptor_set_is_argument_buffer(desc_set)) { - store_flattened_struct(lhs, rhs_id, type, sub_indices); + // An awkward case where we need to emit *more* address space declarations (yay!). + // An example is where we pass down an array of buffer pointers to leaf functions. + // It's a constant array containing pointers to constants. + // The pointer array is always constant however. E.g. + // device SSBO * constant (&array)[N]. + // const device SSBO * constant (&array)[N]. + // constant SSBO * constant (&array)[N]. + // However, this only matters for argument buffers, since for MSL 1.0 style codegen, + // we emit the buffer array on stack instead, and that seems to work just fine apparently. + + // If the argument was marked as being in device address space, any pointer to member would + // be const device, not constant. + if (argument_buffer_device_storage_mask & (1u << desc_set)) + return "const device"; + else + return "constant"; } + } + + return plain_address_space; +} + +bool CompilerMSL::emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, + StorageClass lhs_storage, StorageClass rhs_storage) +{ + // Allow Metal to use the array template to make arrays a value type. + // This, however, cannot be used for threadgroup address specifiers, so consider the custom array copy as fallback. + bool lhs_is_thread_storage = storage_class_array_is_thread(lhs_storage); + bool rhs_is_thread_storage = storage_class_array_is_thread(rhs_storage); + + bool lhs_is_array_template = lhs_is_thread_storage; + bool rhs_is_array_template = rhs_is_thread_storage; + + // Special considerations for stage IO variables. + // If the variable is actually backed by non-user visible device storage, we use array templates for those. + // + // Another special consideration is given to thread local variables which happen to have Offset decorations + // applied to them. Block-like types do not use array templates, so we need to force POD path if we detect + // these scenarios. This check isn't perfect since it would be technically possible to mix and match these things, + // and for a fully correct solution we might have to track array template state through access chains as well, + // but for all reasonable use cases, this should suffice. + // This special case should also only apply to Function/Private storage classes. + // We should not check backing variable for temporaries. + auto *lhs_var = maybe_get_backing_variable(lhs_id); + if (lhs_var && lhs_storage == StorageClassStorageBuffer && storage_class_array_is_thread(lhs_var->storage)) + lhs_is_array_template = true; + else if (lhs_var && (lhs_storage == StorageClassFunction || lhs_storage == StorageClassPrivate) && + type_is_block_like(get(lhs_var->basetype))) + lhs_is_array_template = false; + + auto *rhs_var = maybe_get_backing_variable(rhs_id); + if (rhs_var && rhs_storage == StorageClassStorageBuffer && storage_class_array_is_thread(rhs_var->storage)) + rhs_is_array_template = true; + else if (rhs_var && (rhs_storage == StorageClassFunction || rhs_storage == StorageClassPrivate) && + type_is_block_like(get(rhs_var->basetype))) + rhs_is_array_template = false; + + // If threadgroup storage qualifiers are *not* used: + // Avoid spvCopy* wrapper functions; Otherwise, spvUnsafeArray<> template cannot be used with that storage qualifier. + if (lhs_is_array_template && rhs_is_array_template && !using_builtin_array()) + { + // Fall back to normal copy path. + return false; + } + else + { + // Ensure the LHS variable has been declared + if (lhs_var) + flush_variable_declaration(lhs_var->self); + + string lhs; + if (expr) + lhs = expr; else + lhs = to_expression(lhs_id); + + // Assignment from an array initializer is fine. + auto &type = expression_type(rhs_id); + auto *var = maybe_get_backing_variable(rhs_id); + + // Unfortunately, we cannot template on address space in MSL, + // so explicit address space redirection it is ... + bool is_constant = false; + if (ir.ids[rhs_id].get_type() == TypeConstant) { - auto rhs = to_expression(rhs_id) + to_multi_member_reference(type, sub_indices); - statement(lhs, " = ", rhs, ";"); + is_constant = true; + } + else if (var && var->remapped_variable && var->statically_assigned && + ir.ids[var->static_expression].get_type() == TypeConstant) + { + is_constant = true; + } + else if (rhs_storage == StorageClassUniform || rhs_storage == StorageClassUniformConstant) + { + is_constant = true; + } + + // For the case where we have OpLoad triggering an array copy, + // we cannot easily detect this case ahead of time since it's + // context dependent. We might have to force a recompile here + // if this is the only use of array copies in our shader. + if (type.array.size() > 1) + { + if (type.array.size() > kArrayCopyMultidimMax) + SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays."); + auto func = static_cast(SPVFuncImplArrayCopyMultidimBase + type.array.size()); + add_spv_func_and_recompile(func); } + else + add_spv_func_and_recompile(SPVFuncImplArrayCopy); + + const char *tag = nullptr; + if (lhs_is_thread_storage && is_constant) + tag = "FromConstantToStack"; + else if (lhs_storage == StorageClassWorkgroup && is_constant) + tag = "FromConstantToThreadGroup"; + else if (lhs_is_thread_storage && rhs_is_thread_storage) + tag = "FromStackToStack"; + else if (lhs_storage == StorageClassWorkgroup && rhs_is_thread_storage) + tag = "FromStackToThreadGroup"; + else if (lhs_is_thread_storage && rhs_storage == StorageClassWorkgroup) + tag = "FromThreadGroupToStack"; + else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassWorkgroup) + tag = "FromThreadGroupToThreadGroup"; + else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassStorageBuffer) + tag = "FromDeviceToDevice"; + else if (lhs_storage == StorageClassStorageBuffer && is_constant) + tag = "FromConstantToDevice"; + else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassWorkgroup) + tag = "FromThreadGroupToDevice"; + else if (lhs_storage == StorageClassStorageBuffer && rhs_is_thread_storage) + tag = "FromStackToDevice"; + else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassStorageBuffer) + tag = "FromDeviceToThreadGroup"; + else if (lhs_is_thread_storage && rhs_storage == StorageClassStorageBuffer) + tag = "FromDeviceToStack"; + else + SPIRV_CROSS_THROW("Unknown storage class used for copying arrays."); + + // Pass internal array of spvUnsafeArray<> into wrapper functions + if (lhs_is_array_template && rhs_is_array_template && !msl_options.force_native_arrays) + statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ".elements, ", to_expression(rhs_id), ".elements);"); + if (lhs_is_array_template && !msl_options.force_native_arrays) + statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ".elements, ", to_expression(rhs_id), ");"); + else if (rhs_is_array_template && !msl_options.force_native_arrays) + statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ".elements);"); + else + statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");"); } + + return true; } -string CompilerMSL::to_multi_member_reference(const SPIRType &type, const SmallVector &indices) +void CompilerMSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type) { - string ret; - auto *member_type = &type; - for (auto &index : indices) + string cast_op0, cast_op1; + auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type); + auto &out_type = get(result_type); + + // Special case boolean outputs since relational opcodes output booleans instead of int/uint. + string expr; + if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean) { - ret += join(".", to_member_name(*member_type, index)); - member_type = &get(member_type->member_types[index]); + expected_type.basetype = input_type; + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); + expr += ')'; } - return ret; + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); } -void CompilerMSL::store_flattened_struct(uint32_t lhs_id, uint32_t value) +void CompilerMSL::emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op, SPIRType::BaseType input_type) { - auto &type = expression_type(lhs_id); - auto basename = to_flattened_access_chain_expression(lhs_id); - store_flattened_struct(basename, value, type, {}); + auto &out_type = get(result_type); + auto expected_type = out_type; + expected_type.basetype = input_type; + string cast_op0 = + expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + string cast_op1 = + expression_type(op1).basetype != input_type ? bitcast_glsl(expected_type, op1) : to_unpacked_expression(op1); + string cast_op2 = + expression_type(op2).basetype != input_type ? bitcast_glsl(expected_type, op2) : to_unpacked_expression(op2); + + string expr; + if (out_type.basetype != input_type) + { + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1) && should_forward(op2)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); +} + +void CompilerMSL::emit_while_loop_initializers(const SPIRBlock &block) +{ + // While loops do not take initializers, so declare all of them outside. + for (auto &loop_var : block.loop_variables) + { + auto &var = get(loop_var); + statement(variable_decl(var), ";"); + } } +void CompilerMSL::end_scope_decl(const string &decl) +{ + if (!indent) + SPIRV_CROSS_THROW("Popping empty indent stack."); + indent--; + statement("} ", decl, ";"); +} string CompilerMSL::to_extract_constant_composite_expression(uint32_t result_type, const SPIRConstant &c, const uint32_t *chain, uint32_t length) @@ -24363,6 +24582,57 @@ string CompilerMSL::to_extract_constant_composite_expression(uint32_t result_typ return constant_expression(tmp); } +#ifndef SPIRV_CROSS_WEBMIN + +void CompilerMSL::store_flattened_struct(const string &basename, uint32_t rhs_id, const SPIRType &type, + const SmallVector &indices) +{ + SmallVector sub_indices = indices; + sub_indices.push_back(0); + + auto *member_type = &type; + for (auto &index : indices) + member_type = &get(member_type->member_types[index]); + + for (uint32_t i = 0; i < uint32_t(member_type->member_types.size()); i++) + { + sub_indices.back() = i; + auto lhs = join(basename, "_", to_member_name(*member_type, i)); + ParsedIR::sanitize_underscores(lhs); + + if (get(member_type->member_types[i]).basetype == SPIRType::Struct) + { + store_flattened_struct(lhs, rhs_id, type, sub_indices); + } + else + { + auto rhs = to_expression(rhs_id) + to_multi_member_reference(type, sub_indices); + statement(lhs, " = ", rhs, ";"); + } + } +} + +string CompilerMSL::to_multi_member_reference(const SPIRType &type, const SmallVector &indices) +{ + string ret; + auto *member_type = &type; + for (auto &index : indices) + { + ret += join(".", to_member_name(*member_type, index)); + member_type = &get(member_type->member_types[index]); + } + return ret; +} + +void CompilerMSL::store_flattened_struct(uint32_t lhs_id, uint32_t value) +{ + auto &type = expression_type(lhs_id); + auto basename = to_flattened_access_chain_expression(lhs_id); + store_flattened_struct(basename, value, type, {}); +} + + + void CompilerMSL::emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, uint32_t rhs_id, uint32_t rhs_type_id, SmallVector chain) { @@ -25734,79 +26004,22 @@ void CompilerMSL::emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op else if (expression_type(op0).vecsize > 1) { // If the number doesn't equal itself, it must be NaN - emit_binary_func_op(btype_id, left_nan_id, op0, op0, "notEqual"); - emit_binary_func_op(btype_id, right_nan_id, op1, op1, "notEqual"); - } - else - { - emit_binary_op(btype_id, left_nan_id, op0, op0, "!="); - emit_binary_op(btype_id, right_nan_id, op1, op1, "!="); - } - emit_binary_func_op(result_type, tmp_id, op0, op1, op == GLSLstd450NMin ? "min" : "max"); - emit_mix_op(result_type, mixed_first_id, tmp_id, op1, left_nan_id); - emit_mix_op(result_type, id, mixed_first_id, op0, right_nan_id); -} - - -void CompilerMSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, - const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type) -{ - string cast_op0, cast_op1; - auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type); - auto &out_type = get(result_type); - - // Special case boolean outputs since relational opcodes output booleans instead of int/uint. - string expr; - if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean) - { - expected_type.basetype = input_type; - expr = bitcast_glsl_op(out_type, expected_type); - expr += '('; - expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); - expr += ')'; - } - else - { - expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); - } - - emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); - inherit_expression_dependencies(result_id, op0); - inherit_expression_dependencies(result_id, op1); -} - -void CompilerMSL::emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, - uint32_t op2, const char *op, SPIRType::BaseType input_type) -{ - auto &out_type = get(result_type); - auto expected_type = out_type; - expected_type.basetype = input_type; - string cast_op0 = - expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); - string cast_op1 = - expression_type(op1).basetype != input_type ? bitcast_glsl(expected_type, op1) : to_unpacked_expression(op1); - string cast_op2 = - expression_type(op2).basetype != input_type ? bitcast_glsl(expected_type, op2) : to_unpacked_expression(op2); - - string expr; - if (out_type.basetype != input_type) - { - expr = bitcast_glsl_op(out_type, expected_type); - expr += '('; - expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); - expr += ')'; + emit_binary_func_op(btype_id, left_nan_id, op0, op0, "notEqual"); + emit_binary_func_op(btype_id, right_nan_id, op1, op1, "notEqual"); } else { - expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + emit_binary_op(btype_id, left_nan_id, op0, op0, "!="); + emit_binary_op(btype_id, right_nan_id, op1, op1, "!="); } - - emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1) && should_forward(op2)); - inherit_expression_dependencies(result_id, op0); - inherit_expression_dependencies(result_id, op1); - inherit_expression_dependencies(result_id, op2); + emit_binary_func_op(result_type, tmp_id, op0, op1, op == GLSLstd450NMin ? "min" : "max"); + emit_mix_op(result_type, mixed_first_id, tmp_id, op1, left_nan_id); + emit_mix_op(result_type, id, mixed_first_id, op0, right_nan_id); } + + + void CompilerMSL::emit_emulated_ahyper_op(uint32_t result_type, uint32_t id, uint32_t op0, GLSLstd450 op) { const char *one = backend.float_literal_suffix ? "1.0f" : "1.0"; @@ -26105,15 +26318,6 @@ void CompilerMSL::forward_relaxed_precision(uint32_t dst_id, const uint32_t *arg set_decoration(dst_id, DecorationRelaxedPrecision); } -void CompilerMSL::emit_while_loop_initializers(const SPIRBlock &block) -{ - // While loops do not take initializers, so declare all of them outside. - for (auto &loop_var : block.loop_variables) - { - auto &var = get(loop_var); - statement(variable_decl(var), ";"); - } -} void CompilerMSL::emit_line_directive(uint32_t file_id, uint32_t line_literal) @@ -27062,12 +27266,6 @@ uint32_t CompilerMSL::get_declared_member_location(const SPIRVariable &var, uint } -void CompilerMSL::end_scope_decl(const string &decl) -{ - if (!indent) - SPIRV_CROSS_THROW("Popping empty indent stack."); - indent--; - statement("} ", decl, ";"); } void CompilerMSL::end_scope(const string &trailer) @@ -28120,33 +28318,7 @@ case OpGroupNonUniform##op: \ register_control_dependent_expression(id); } -string CompilerMSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type) -{ - if (out_type.basetype == in_type.basetype) - return ""; - - assert(out_type.basetype != SPIRType::Boolean); - assert(in_type.basetype != SPIRType::Boolean); - - bool integral_cast = type_is_integral(out_type) && type_is_integral(in_type) && (out_type.vecsize == in_type.vecsize); - bool same_size_cast = (out_type.width * out_type.vecsize) == (in_type.width * in_type.vecsize); - - // Bitcasting can only be used between types of the same overall size. - // And always formally cast between integers, because it's trivial, and also - // because Metal can internally cast the results of some integer ops to a larger - // size (eg. short shift right becomes int), which means chaining integer ops - // together may introduce size variations that SPIR-V doesn't know about. - if (same_size_cast && !integral_cast) - return "as_type<" + type_to_glsl(out_type) + ">"; - else - return type_to_glsl(out_type); -} -bool CompilerMSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t) -{ - // This is handled from the outside where we deal with PtrToU/UToPtr and friends. - return false; -} string CompilerMSL::constant_op_expression(const SPIRConstantOp &cop) { @@ -28168,38 +28340,6 @@ bool CompilerMSL::type_is_pointer_to_pointer(const SPIRType &type) const return type.pointer_depth > parent_type.pointer_depth && type_is_pointer(parent_type); } -const char *CompilerMSL::descriptor_address_space(uint32_t id, StorageClass storage, const char *plain_address_space) const -{ - if (msl_options.argument_buffers) - { - bool storage_class_is_descriptor = storage == StorageClassUniform || - storage == StorageClassStorageBuffer || - storage == StorageClassUniformConstant; - - uint32_t desc_set = get_decoration(id, DecorationDescriptorSet); - if (storage_class_is_descriptor && descriptor_set_is_argument_buffer(desc_set)) - { - // An awkward case where we need to emit *more* address space declarations (yay!). - // An example is where we pass down an array of buffer pointers to leaf functions. - // It's a constant array containing pointers to constants. - // The pointer array is always constant however. E.g. - // device SSBO * constant (&array)[N]. - // const device SSBO * constant (&array)[N]. - // constant SSBO * constant (&array)[N]. - // However, this only matters for argument buffers, since for MSL 1.0 style codegen, - // we emit the buffer array on stack instead, and that seems to work just fine apparently. - - // If the argument was marked as being in device address space, any pointer to member would - // be const device, not constant. - if (argument_buffer_device_storage_mask & (1u << desc_set)) - return "const device"; - else - return "constant"; - } - } - - return plain_address_space; -} string CompilerMSL::entry_point_args_argument_buffer(bool append_comma) { @@ -28717,136 +28857,6 @@ void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uin flush_all_active_variables(); } -bool CompilerMSL::emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, - StorageClass lhs_storage, StorageClass rhs_storage) -{ - // Allow Metal to use the array template to make arrays a value type. - // This, however, cannot be used for threadgroup address specifiers, so consider the custom array copy as fallback. - bool lhs_is_thread_storage = storage_class_array_is_thread(lhs_storage); - bool rhs_is_thread_storage = storage_class_array_is_thread(rhs_storage); - - bool lhs_is_array_template = lhs_is_thread_storage; - bool rhs_is_array_template = rhs_is_thread_storage; - - // Special considerations for stage IO variables. - // If the variable is actually backed by non-user visible device storage, we use array templates for those. - // - // Another special consideration is given to thread local variables which happen to have Offset decorations - // applied to them. Block-like types do not use array templates, so we need to force POD path if we detect - // these scenarios. This check isn't perfect since it would be technically possible to mix and match these things, - // and for a fully correct solution we might have to track array template state through access chains as well, - // but for all reasonable use cases, this should suffice. - // This special case should also only apply to Function/Private storage classes. - // We should not check backing variable for temporaries. - auto *lhs_var = maybe_get_backing_variable(lhs_id); - if (lhs_var && lhs_storage == StorageClassStorageBuffer && storage_class_array_is_thread(lhs_var->storage)) - lhs_is_array_template = true; - else if (lhs_var && (lhs_storage == StorageClassFunction || lhs_storage == StorageClassPrivate) && - type_is_block_like(get(lhs_var->basetype))) - lhs_is_array_template = false; - - auto *rhs_var = maybe_get_backing_variable(rhs_id); - if (rhs_var && rhs_storage == StorageClassStorageBuffer && storage_class_array_is_thread(rhs_var->storage)) - rhs_is_array_template = true; - else if (rhs_var && (rhs_storage == StorageClassFunction || rhs_storage == StorageClassPrivate) && - type_is_block_like(get(rhs_var->basetype))) - rhs_is_array_template = false; - - // If threadgroup storage qualifiers are *not* used: - // Avoid spvCopy* wrapper functions; Otherwise, spvUnsafeArray<> template cannot be used with that storage qualifier. - if (lhs_is_array_template && rhs_is_array_template && !using_builtin_array()) - { - // Fall back to normal copy path. - return false; - } - else - { - // Ensure the LHS variable has been declared - if (lhs_var) - flush_variable_declaration(lhs_var->self); - - string lhs; - if (expr) - lhs = expr; - else - lhs = to_expression(lhs_id); - - // Assignment from an array initializer is fine. - auto &type = expression_type(rhs_id); - auto *var = maybe_get_backing_variable(rhs_id); - - // Unfortunately, we cannot template on address space in MSL, - // so explicit address space redirection it is ... - bool is_constant = false; - if (ir.ids[rhs_id].get_type() == TypeConstant) - { - is_constant = true; - } - else if (var && var->remapped_variable && var->statically_assigned && - ir.ids[var->static_expression].get_type() == TypeConstant) - { - is_constant = true; - } - else if (rhs_storage == StorageClassUniform || rhs_storage == StorageClassUniformConstant) - { - is_constant = true; - } - - // For the case where we have OpLoad triggering an array copy, - // we cannot easily detect this case ahead of time since it's - // context dependent. We might have to force a recompile here - // if this is the only use of array copies in our shader. - if (type.array.size() > 1) - { - if (type.array.size() > kArrayCopyMultidimMax) - SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays."); - auto func = static_cast(SPVFuncImplArrayCopyMultidimBase + type.array.size()); - add_spv_func_and_recompile(func); - } - else - add_spv_func_and_recompile(SPVFuncImplArrayCopy); - - const char *tag = nullptr; - if (lhs_is_thread_storage && is_constant) - tag = "FromConstantToStack"; - else if (lhs_storage == StorageClassWorkgroup && is_constant) - tag = "FromConstantToThreadGroup"; - else if (lhs_is_thread_storage && rhs_is_thread_storage) - tag = "FromStackToStack"; - else if (lhs_storage == StorageClassWorkgroup && rhs_is_thread_storage) - tag = "FromStackToThreadGroup"; - else if (lhs_is_thread_storage && rhs_storage == StorageClassWorkgroup) - tag = "FromThreadGroupToStack"; - else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassWorkgroup) - tag = "FromThreadGroupToThreadGroup"; - else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassStorageBuffer) - tag = "FromDeviceToDevice"; - else if (lhs_storage == StorageClassStorageBuffer && is_constant) - tag = "FromConstantToDevice"; - else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassWorkgroup) - tag = "FromThreadGroupToDevice"; - else if (lhs_storage == StorageClassStorageBuffer && rhs_is_thread_storage) - tag = "FromStackToDevice"; - else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassStorageBuffer) - tag = "FromDeviceToThreadGroup"; - else if (lhs_is_thread_storage && rhs_storage == StorageClassStorageBuffer) - tag = "FromDeviceToStack"; - else - SPIRV_CROSS_THROW("Unknown storage class used for copying arrays."); - - // Pass internal array of spvUnsafeArray<> into wrapper functions - if (lhs_is_array_template && rhs_is_array_template && !msl_options.force_native_arrays) - statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ".elements, ", to_expression(rhs_id), ".elements);"); - if (lhs_is_array_template && !msl_options.force_native_arrays) - statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ".elements, ", to_expression(rhs_id), ");"); - else if (rhs_is_array_template && !msl_options.force_native_arrays) - statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ".elements);"); - else - statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");"); - } - - return true; -} uint32_t CompilerMSL::get_physical_tess_level_array_size(spv::BuiltIn builtin) const { @@ -30437,12 +30447,6 @@ void CompilerMSL::store_flattened_struct(uint32_t, uint32_t) } -string CompilerMSL::to_extract_constant_composite_expression(uint32_t, const SPIRConstant &, - const uint32_t *, uint32_t ) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} void CompilerMSL::emit_copy_logical_type(uint32_t, uint32_t, uint32_t, uint32_t, SmallVector) @@ -30731,19 +30735,7 @@ void CompilerMSL::emit_nminmax_op(uint32_t, uint32_t, uint32_t, uint32_t, GLSLst } -void CompilerMSL::emit_binary_func_op_cast(uint32_t, uint32_t, uint32_t, uint32_t, - const char *, SPIRType::BaseType, bool) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} -void CompilerMSL::emit_trinary_func_op_cast(uint32_t, uint32_t, uint32_t, uint32_t, - uint32_t, const char *, SPIRType::BaseType) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} void CompilerMSL::emit_emulated_ahyper_op(uint32_t, uint32_t, uint32_t, GLSLstd450) { @@ -30803,11 +30795,6 @@ void CompilerMSL::forward_relaxed_precision(uint32_t, const uint32_t *, uint32_t SPIRV_CROSS_THROW("Invalid call."); } -void CompilerMSL::emit_while_loop_initializers(const SPIRBlock &) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} void CompilerMSL::emit_line_directive(uint32_t, uint32_t) @@ -31052,11 +31039,6 @@ uint32_t CompilerMSL::get_declared_member_location(const SPIRVariable &, uint32_ } -void CompilerMSL::end_scope_decl(const string &) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} void CompilerMSL::end_scope(const string &) { @@ -31219,17 +31201,7 @@ void CompilerMSL::emit_subgroup_op(const Instruction &) SPIRV_CROSS_THROW("Invalid call."); } -string CompilerMSL::bitcast_glsl_op(const SPIRType &, const SPIRType &) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} -bool CompilerMSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} string CompilerMSL::constant_op_expression(const SPIRConstantOp &) { @@ -31243,11 +31215,6 @@ bool CompilerMSL::type_is_pointer_to_pointer(const SPIRType &) const SPIRV_CROSS_THROW("Invalid call."); } -const char *CompilerMSL::descriptor_address_space(uint32_t, StorageClass, const char *) const -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} string CompilerMSL::entry_point_args_argument_buffer(bool) { @@ -31331,12 +31298,6 @@ void CompilerMSL::emit_barrier(uint32_t, uint32_t, uint32_t) SPIRV_CROSS_THROW("Invalid call."); } -bool CompilerMSL::emit_array_copy(const char *, uint32_t, uint32_t, - StorageClass, StorageClass) -{ - SPIRV_CROSS_INVALID_CALL(); - SPIRV_CROSS_THROW("Invalid call."); -} uint32_t CompilerMSL::get_physical_tess_level_array_size(spv::BuiltIn) const {