diff --git a/3rdparty/spirv-cross/main.cpp b/3rdparty/spirv-cross/main.cpp index e8f14be27..e00a7f9bd 100644 --- a/3rdparty/spirv-cross/main.cpp +++ b/3rdparty/spirv-cross/main.cpp @@ -607,6 +607,7 @@ struct CLIArguments bool hlsl_force_storage_buffer_as_uav = false; bool hlsl_nonwritable_uav_texture_as_srv = false; bool hlsl_enable_16bit_types = false; + bool hlsl_flatten_matrix_vertex_input_semantics = false; HLSLBindingFlags hlsl_binding_flags = 0; bool vulkan_semantics = false; bool flatten_multidimensional_arrays = false; @@ -705,6 +706,7 @@ static void print_help_hlsl() "\t[--set-hlsl-vertex-input-semantic ]:\n\t\tEmits a specific vertex input semantic for a given location.\n" "\t\tOtherwise, TEXCOORD# is used as semantics, where # is location.\n" "\t[--hlsl-enable-16bit-types]:\n\t\tEnables native use of half/int16_t/uint16_t and ByteAddressBuffer interaction with these types. Requires SM 6.2.\n" + "\t[--hlsl-flatten-matrix-vertex-input-semantics]:\n\t\tEmits matrix vertex inputs with input semantics as if they were independent vectors, e.g. TEXCOORD{2,3,4} rather than matrix form TEXCOORD2_{0,1,2}.\n" ); // clang-format on } @@ -1195,6 +1197,7 @@ static string compile_iteration(const CLIArguments &args, std::vector hlsl_opts.force_storage_buffer_as_uav = args.hlsl_force_storage_buffer_as_uav; hlsl_opts.nonwritable_uav_texture_as_srv = args.hlsl_nonwritable_uav_texture_as_srv; hlsl_opts.enable_16bit_types = args.hlsl_enable_16bit_types; + hlsl_opts.flatten_matrix_vertex_input_semantics = args.hlsl_flatten_matrix_vertex_input_semantics; hlsl->set_hlsl_options(hlsl_opts); hlsl->set_resource_binding_flags(args.hlsl_binding_flags); } @@ -1367,6 +1370,7 @@ static int main_inner(int argc, char *argv[]) cbs.add("--hlsl-nonwritable-uav-texture-as-srv", [&args](CLIParser &) { args.hlsl_nonwritable_uav_texture_as_srv = true; }); cbs.add("--hlsl-enable-16bit-types", [&args](CLIParser &) { args.hlsl_enable_16bit_types = true; }); + cbs.add("--hlsl-flatten-matrix-vertex-input-semantics", [&args](CLIParser &) { args.hlsl_flatten_matrix_vertex_input_semantics = true; }); cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; }); cbs.add("-V", [&args](CLIParser &) { args.vulkan_semantics = true; }); cbs.add("--flatten-multidimensional-arrays", [&args](CLIParser &) { args.flatten_multidimensional_arrays = true; }); diff --git a/3rdparty/spirv-cross/spirv_common.hpp b/3rdparty/spirv-cross/spirv_common.hpp index c69c052b8..d13b55e50 100644 --- a/3rdparty/spirv-cross/spirv_common.hpp +++ b/3rdparty/spirv-cross/spirv_common.hpp @@ -516,6 +516,7 @@ struct SPIRType : IVariant // Keep internal types at the end. ControlPointArray, + Interpolant, Char }; @@ -1573,6 +1574,13 @@ enum ExtendedDecorations // addition of swizzles to keep the generated code compiling. SPIRVCrossDecorationTessIOOriginalInputTypeID, + // Apply to any access chain of an interface variable used with pull-model interpolation, where the variable is a + // vector but the resulting pointer is a scalar; stores the component index that is to be accessed by the chain. + // This is used when emitting calls to interpolation functions on the chain in MSL: in this case, the component + // must be applied to the result, since pull-model interpolants in MSL cannot be swizzled directly, but the + // results of interpolation can. + SPIRVCrossDecorationInterpolantComponentExpr, + SPIRVCrossDecorationCount }; diff --git a/3rdparty/spirv-cross/spirv_cross.cpp b/3rdparty/spirv-cross/spirv_cross.cpp index 77e7619fc..c2d8147ef 100644 --- a/3rdparty/spirv-cross/spirv_cross.cpp +++ b/3rdparty/spirv-cross/spirv_cross.cpp @@ -699,8 +699,31 @@ bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t { if (length < 5) return false; - uint32_t extension_set = args[2]; - if (compiler.get(extension_set).ext == SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter) + auto &extension_set = compiler.get(args[2]); + switch (extension_set.ext) + { + case SPIRExtension::GLSL: + { + auto op = static_cast(args[3]); + + switch (op) + { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + { + auto *var = compiler.maybe_get(args[4]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[4]); + break; + } + + default: + break; + } + break; + } + case SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter: { enum AMDShaderExplicitVertexParameter { @@ -722,6 +745,10 @@ bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t default: break; } + break; + } + default: + break; } break; } diff --git a/3rdparty/spirv-cross/spirv_cross_c.cpp b/3rdparty/spirv-cross/spirv_cross_c.cpp index 59a9c9186..5506d8d06 100644 --- a/3rdparty/spirv-cross/spirv_cross_c.cpp +++ b/3rdparty/spirv-cross/spirv_cross_c.cpp @@ -492,6 +492,10 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c case SPVC_COMPILER_OPTION_HLSL_ENABLE_16BIT_TYPES: options->hlsl.enable_16bit_types = value != 0; break; + + case SPVC_COMPILER_OPTION_HLSL_FLATTEN_MATRIX_VERTEX_INPUT_SEMANTICS: + options->hlsl.flatten_matrix_vertex_input_semantics = value != 0; + break; #endif #if SPIRV_CROSS_C_API_MSL diff --git a/3rdparty/spirv-cross/spirv_cross_c.h b/3rdparty/spirv-cross/spirv_cross_c.h index d6802a1fd..7ccec0aae 100644 --- a/3rdparty/spirv-cross/spirv_cross_c.h +++ b/3rdparty/spirv-cross/spirv_cross_c.h @@ -33,7 +33,7 @@ extern "C" { /* Bumped if ABI or API breaks backwards compatibility. */ #define SPVC_C_API_VERSION_MAJOR 0 /* Bumped if APIs or enumerations are added in a backwards compatible way. */ -#define SPVC_C_API_VERSION_MINOR 41 +#define SPVC_C_API_VERSION_MINOR 42 /* Bumped if internal implementation details change. */ #define SPVC_C_API_VERSION_PATCH 0 @@ -645,6 +645,8 @@ typedef enum spvc_compiler_option SPVC_COMPILER_OPTION_MSL_R32UI_LINEAR_TEXTURE_ALIGNMENT = 69 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_MSL_R32UI_ALIGNMENT_CONSTANT_ID = 70 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_HLSL_FLATTEN_MATRIX_VERTEX_INPUT_SEMANTICS = 71 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff } spvc_compiler_option; diff --git a/3rdparty/spirv-cross/spirv_glsl.cpp b/3rdparty/spirv-cross/spirv_glsl.cpp index 6a7d7b2ea..df14e5297 100644 --- a/3rdparty/spirv-cross/spirv_glsl.cpp +++ b/3rdparty/spirv-cross/spirv_glsl.cpp @@ -3706,8 +3706,12 @@ void CompilerGLSL::emit_extension_workarounds(spv::ExecutionModel model) { // Extensions we're using in place of GL_KHR_shader_subgroup_basic state // that subgroup execute in lockstep so this barrier is implicit. + // However the GL 4.6 spec also states that `barrier` implies a shared memory barrier, + // and a specific test of optimizing scans by leveraging lock-step invocation execution, + // has shown that a `memoryBarrierShared` is needed in place of a `subgroupBarrier`. + // https://github.com/buildaworldnet/IrrlichtBAW/commit/d8536857991b89a30a6b65d29441e51b64c2c7ad#diff-9f898d27be1ea6fc79b03d9b361e299334c1a347b6e4dc344ee66110c6aa596aR19 statement("#ifndef GL_KHR_shader_subgroup_basic"); - statement("void subgroupBarrier() { /*NOOP*/ }"); + statement("void subgroupBarrier() { memoryBarrierShared(); }"); statement("#endif"); statement(""); } @@ -3719,7 +3723,7 @@ void CompilerGLSL::emit_extension_workarounds(spv::ExecutionModel model) statement("#ifndef GL_KHR_shader_subgroup_basic"); statement("void subgroupMemoryBarrier() { groupMemoryBarrier(); }"); statement("void subgroupMemoryBarrierBuffer() { groupMemoryBarrier(); }"); - statement("void subgroupMemoryBarrierShared() { groupMemoryBarrier(); }"); + statement("void subgroupMemoryBarrierShared() { memoryBarrierShared(); }"); statement("void subgroupMemoryBarrierImage() { groupMemoryBarrier(); }"); statement("#endif"); } @@ -3810,6 +3814,33 @@ void CompilerGLSL::emit_extension_workarounds(spv::ExecutionModel model) } statement(""); } + + if (requires_transpose_2x2) + { + statement("mat2 SPIRV_Cross_Transpose(mat2 m)"); + begin_scope(); + statement("return mat2(m[0][0], m[1][0], m[0][1], m[1][1]);"); + end_scope(); + statement(""); + } + + if (requires_transpose_3x3) + { + statement("mat3 SPIRV_Cross_Transpose(mat3 m)"); + begin_scope(); + statement("return mat3(m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);"); + end_scope(); + statement(""); + } + + if (requires_transpose_4x4) + { + statement("mat4 SPIRV_Cross_Transpose(mat4 m)"); + begin_scope(); + statement("return mat4(m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], m[1][2], m[2][2], m[3][2], m[0][3], m[1][3], m[2][3], m[3][3]);"); + end_scope(); + statement(""); + } } // Returns a string representation of the ID, usable as a function arg. @@ -5637,26 +5668,7 @@ void CompilerGLSL::emit_bitfield_insert_op(uint32_t result_type, uint32_t result inherit_expression_dependencies(result_id, op3); } -// EXT_shader_texture_lod only concerns fragment shaders so lod tex functions -// are not allowed in ES 2 vertex shaders. But SPIR-V only supports lod tex -// functions in vertex shaders so we revert those back to plain calls when -// the lod is a constant value of zero. -bool CompilerGLSL::check_explicit_lod_allowed(uint32_t lod) -{ - auto &execution = get_entry_point(); - bool allowed = !is_legacy_es() || execution.model == ExecutionModelFragment; - if (!allowed && lod != 0) - { - auto *lod_constant = maybe_get(lod); - if (!lod_constant || lod_constant->scalar_f32() != 0.0f) - { - SPIRV_CROSS_THROW("Explicit lod not allowed in legacy ES non-fragment shaders."); - } - } - return allowed; -} - -string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod, uint32_t tex) +string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t tex) { const char *type; switch (imgtype.image.dim) @@ -5687,16 +5699,19 @@ string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtyp break; } - bool use_explicit_lod = check_explicit_lod_allowed(lod); - - if (op == "textureLod" || op == "textureProjLod" || op == "textureGrad" || op == "textureProjGrad") + // In legacy GLSL, an extension is required for textureLod in the fragment + // shader or textureGrad anywhere. + bool legacy_lod_ext = false; + auto &execution = get_entry_point(); + if (op == "textureGrad" || op == "textureProjGrad" || + ((op == "textureLod" || op == "textureProjLod") && execution.model != ExecutionModelVertex)) { if (is_legacy_es()) { - if (use_explicit_lod) - require_extension_internal("GL_EXT_shader_texture_lod"); + legacy_lod_ext = true; + require_extension_internal("GL_EXT_shader_texture_lod"); } - else if (is_legacy()) + else if (is_legacy_desktop()) require_extension_internal("GL_ARB_shader_texture_lod"); } @@ -5725,40 +5740,20 @@ string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtyp if (op == "texture") return is_es_and_depth ? join(type_prefix, type, "EXT") : join(type_prefix, type); else if (op == "textureLod") - { - if (use_explicit_lod) - return join(type_prefix, type, is_legacy_es() ? "LodEXT" : "Lod"); - else - return join(type_prefix, type); - } + return join(type_prefix, type, legacy_lod_ext ? "LodEXT" : "Lod"); else if (op == "textureProj") return join(type_prefix, type, is_es_and_depth ? "ProjEXT" : "Proj"); else if (op == "textureGrad") return join(type_prefix, type, is_legacy_es() ? "GradEXT" : is_legacy_desktop() ? "GradARB" : "Grad"); else if (op == "textureProjLod") - { - if (use_explicit_lod) - return join(type_prefix, type, is_legacy_es() ? "ProjLodEXT" : "ProjLod"); - else - return join(type_prefix, type, "Proj"); - } + return join(type_prefix, type, legacy_lod_ext ? "ProjLodEXT" : "ProjLod"); else if (op == "textureLodOffset") - { - if (use_explicit_lod) - return join(type_prefix, type, "LodOffset"); - else - return join(type_prefix, type); - } + return join(type_prefix, type, "LodOffset"); else if (op == "textureProjGrad") return join(type_prefix, type, is_legacy_es() ? "ProjGradEXT" : is_legacy_desktop() ? "ProjGradARB" : "ProjGrad"); else if (op == "textureProjLodOffset") - { - if (use_explicit_lod) - return join(type_prefix, type, "ProjLodOffset"); - else - return join(type_prefix, type, "ProjOffset"); - } + return join(type_prefix, type, "ProjLodOffset"); else { SPIRV_CROSS_THROW(join("Unsupported legacy texture op: ", op)); @@ -6443,7 +6438,7 @@ string CompilerGLSL::to_function_name(const TextureFunctionNameArguments &args) if (args.is_sparse_feedback || args.has_min_lod) fname += "ARB"; - return is_legacy() ? legacy_tex_op(fname, imgtype, args.lod, tex) : fname; + return is_legacy() ? legacy_tex_op(fname, imgtype, tex) : fname; } std::string CompilerGLSL::convert_separate_image_to_expression(uint32_t id) @@ -6628,23 +6623,20 @@ string CompilerGLSL::to_function_args(const TextureFunctionArguments &args, bool } else { - if (check_explicit_lod_allowed(args.lod)) + forward = forward && should_forward(args.lod); + farg_str += ", "; + + auto &lod_expr_type = expression_type(args.lod); + + // Lod expression for TexelFetch in GLSL must be int, and only int. + if (args.base.is_fetch && imgtype.image.dim != DimBuffer && !imgtype.image.ms && + lod_expr_type.basetype != SPIRType::Int) { - forward = forward && should_forward(args.lod); - farg_str += ", "; - - auto &lod_expr_type = expression_type(args.lod); - - // Lod expression for TexelFetch in GLSL must be int, and only int. - if (args.base.is_fetch && imgtype.image.dim != DimBuffer && !imgtype.image.ms && - lod_expr_type.basetype != SPIRType::Int) - { - farg_str += join("int(", to_expression(args.lod), ")"); - } - else - { - farg_str += to_expression(args.lod); - } + farg_str += join("int(", to_expression(args.lod), ")"); + } + else + { + farg_str += to_expression(args.lod); } } } @@ -6721,14 +6713,30 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, { // FP fiddling case GLSLstd450Round: - emit_unary_func_op(result_type, id, args[0], "round"); + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "round"); + else + { + auto op0 = to_enclosed_expression(args[0]); + auto &op0_type = expression_type(args[0]); + auto expr = join("floor(", op0, " + ", type_to_glsl_constructor(op0_type), "(0.5))"); + bool forward = should_forward(args[0]); + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, args[0]); + } break; case GLSLstd450RoundEven: - if ((options.es && options.version >= 300) || (!options.es && options.version >= 130)) + if (!is_legacy()) emit_unary_func_op(result_type, id, args[0], "roundEven"); + else if (!options.es) + { + // This extension provides round() with round-to-even semantics. + require_extension_internal("GL_EXT_gpu_shader4"); + emit_unary_func_op(result_type, id, args[0], "round"); + } else - SPIRV_CROSS_THROW("roundEven supported only in ESSL 300 and GLSL 130 and up."); + SPIRV_CROSS_THROW("roundEven supported only in ESSL 300."); break; case GLSLstd450Trunc: @@ -10020,12 +10028,22 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) bool pointer = get(result_type).pointer; auto *chain = maybe_get(rhs); + auto *imgsamp = maybe_get(rhs); if (chain) { // Cannot lower to a SPIRExpression, just copy the object. auto &e = set(id, *chain); e.self = id; } + else if (imgsamp) + { + // Cannot lower to a SPIRExpression, just copy the object. + // GLSL does not currently use this type and will never get here, but MSL does. + // Handled here instead of CompilerMSL for better integration and general handling, + // and in case GLSL or other subclasses require it in the future. + auto &e = set(id, *imgsamp); + e.self = id; + } else if (expression_is_lvalue(rhs) && !pointer) { // Need a copy. @@ -10251,7 +10269,32 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) break; case OpTranspose: - GLSL_UFOP(transpose); + if (options.version < 120) // Matches GLSL 1.10 / ESSL 1.00 + { + // transpose() is not available, so instead, flip need_transpose, + // which can later be turned into an emulated transpose op by + // convert_row_major_matrix(), if necessary. + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t input = ops[2]; + + // Force need_transpose to false temporarily to prevent + // to_expression() from doing the transpose. + bool need_transpose = false; + auto *input_e = maybe_get(input); + if (input_e) + swap(need_transpose, input_e->need_transpose); + + bool forward = should_forward(input); + auto &e = emit_op(result_type, result_id, to_expression(input), forward); + e.need_transpose = !need_transpose; + + // Restore the old need_transpose flag. + if (input_e) + input_e->need_transpose = need_transpose; + } + else + GLSL_UFOP(transpose); break; case OpSRem: @@ -12126,6 +12169,38 @@ string CompilerGLSL::convert_row_major_matrix(string exp_str, const SPIRType &ex transposed_expr += ")"; return transposed_expr; } + else if (options.version < 120) + { + // GLSL 110, ES 100 do not have transpose(), so emulate it. Note that + // these GLSL versions do not support non-square matrices. + if (exp_type.vecsize == 2 && exp_type.columns == 2) + { + if (!requires_transpose_2x2) + { + requires_transpose_2x2 = true; + force_recompile(); + } + } + else if (exp_type.vecsize == 3 && exp_type.columns == 3) + { + if (!requires_transpose_3x3) + { + requires_transpose_3x3 = true; + force_recompile(); + } + } + else if (exp_type.vecsize == 4 && exp_type.columns == 4) + { + if (!requires_transpose_4x4) + { + requires_transpose_4x4 = true; + force_recompile(); + } + } + else + SPIRV_CROSS_THROW("Non-square matrices are not supported in legacy GLSL, cannot transpose."); + return join("SPIRV_Cross_Transpose(", exp_str, ")"); + } else return join("transpose(", exp_str, ")"); } diff --git a/3rdparty/spirv-cross/spirv_glsl.hpp b/3rdparty/spirv-cross/spirv_glsl.hpp index d52f786e8..9b39cb09b 100644 --- a/3rdparty/spirv-cross/spirv_glsl.hpp +++ b/3rdparty/spirv-cross/spirv_glsl.hpp @@ -750,8 +750,7 @@ protected: void replace_fragment_output(SPIRVariable &var); void replace_fragment_outputs(); - bool check_explicit_lod_allowed(uint32_t lod); - std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod, uint32_t id); + std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t id); uint32_t indent = 0; @@ -806,6 +805,10 @@ protected: return !options.es && options.version < 130; } + bool requires_transpose_2x2 = false; + bool requires_transpose_3x3 = false; + bool requires_transpose_4x4 = false; + bool args_will_forward(uint32_t id, const uint32_t *args, uint32_t num_args, bool pure); void register_call_out_argument(uint32_t id); void register_impure_function_call(); diff --git a/3rdparty/spirv-cross/spirv_hlsl.cpp b/3rdparty/spirv-cross/spirv_hlsl.cpp index b7cdef739..9d465e5ab 100644 --- a/3rdparty/spirv-cross/spirv_hlsl.cpp +++ b/3rdparty/spirv-cross/spirv_hlsl.cpp @@ -310,7 +310,7 @@ string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t id) ">"); } -string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type, uint32_t id) +string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type, uint32_t /*id*/) { auto &imagetype = get(type.image.type); string res; @@ -373,8 +373,6 @@ string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type, uint32_t id) res += "MS"; if (type.image.arrayed) res += "Array"; - if (image_is_comparison(type, id)) - res += "Shadow"; return res; } @@ -935,8 +933,15 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unord { SPIRType newtype = type; newtype.columns = 1; + + string effective_semantic; + if (hlsl_options.flatten_matrix_vertex_input_semantics) + effective_semantic = to_semantic(location_number, execution.model, var.storage); + else + effective_semantic = join(semantic, "_", i); + statement(to_interpolation_qualifiers(get_decoration_bitset(var.self)), - variable_decl(newtype, join(name, "_", i)), " : ", semantic, "_", i, ";"); + variable_decl(newtype, join(name, "_", i)), " : ", effective_semantic, ";"); active_locations.insert(location_number++); } } @@ -2926,14 +2931,15 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse) SPIRV_CROSS_THROW("textureGather is not supported in HLSL shader model 2/3."); if (offset || coffset) SPIRV_CROSS_THROW("textureOffset is not supported in HLSL shader model 2/3."); - if (proj) - texop += "proj"; + if (grad_x || grad_y) texop += "grad"; - if (lod) + else if (lod) texop += "lod"; - if (bias) + else if (bias) texop += "bias"; + else if (proj || dref) + texop += "proj"; } } @@ -2987,35 +2993,47 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse) if (hlsl_options.shader_model < 40) { - string coord_filler; - uint32_t modifier_count = 0; + if (dref) + { + if (imgtype.image.dim != spv::Dim1D && imgtype.image.dim != spv::Dim2D) + SPIRV_CROSS_THROW("Depth comparison is only supported for 1D and 2D textures in HLSL shader model 2/3."); + + if (grad_x || grad_y) + SPIRV_CROSS_THROW("Depth comparison is not supported for grad sampling in HLSL shader model 2/3."); + + for (uint32_t size = coord_components; size < 2; ++size) + coord_expr += ", 0.0"; + + forward = forward && should_forward(dref); + coord_expr += ", " + to_expression(dref); + } + else if (lod || bias || proj) + { + for (uint32_t size = coord_components; size < 3; ++size) + coord_expr += ", 0.0"; + } if (lod) { - for (uint32_t size = coord_components; size < 3; ++size) - coord_filler += ", 0.0"; - coord_expr = "float4(" + coord_expr + coord_filler + ", " + to_expression(lod) + ")"; - modifier_count++; + coord_expr = "float4(" + coord_expr + ", " + to_expression(lod) + ")"; } - - if (bias) + else if (bias) { - for (uint32_t size = coord_components; size < 3; ++size) - coord_filler += ", 0.0"; - coord_expr = "float4(" + coord_expr + coord_filler + ", " + to_expression(bias) + ")"; - modifier_count++; + coord_expr = "float4(" + coord_expr + ", " + to_expression(bias) + ")"; } - - if (proj) + else if (proj) { - for (uint32_t size = coord_components; size < 3; ++size) - coord_filler += ", 0.0"; - coord_expr = "float4(" + coord_expr + coord_filler + ", " + + coord_expr = "float4(" + coord_expr + ", " + to_extract_component_expression(coord, coord_components) + ")"; - modifier_count++; + } + else if (dref) + { + // A "normal" sample gets fed into tex2Dproj as well, because the + // regular tex2D accepts only two coordinates. + coord_expr = "float4(" + coord_expr + ", 1.0)"; } - if (modifier_count > 1) + if (!!lod + !!bias + !!proj > 1) SPIRV_CROSS_THROW("Legacy HLSL can only use one of lod/bias/proj modifiers."); } @@ -3029,11 +3047,8 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse) expr += ", "; expr += coord_expr; - if (dref) + if (dref && hlsl_options.shader_model >= 40) { - if (hlsl_options.shader_model < 40) - SPIRV_CROSS_THROW("Legacy HLSL does not support comparison sampling."); - forward = forward && should_forward(dref); expr += ", "; @@ -3088,6 +3103,9 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse) expr += ")"; + if (dref && hlsl_options.shader_model < 40) + expr += ".x"; + if (op == OpImageQueryLod) { // This is rather awkward. @@ -3426,7 +3444,10 @@ void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, break; case GLSLstd450RoundEven: - SPIRV_CROSS_THROW("roundEven is not supported on HLSL."); + if (hlsl_options.shader_model < 40) + SPIRV_CROSS_THROW("roundEven is not supported in HLSL shader model 2/3."); + emit_unary_func_op(result_type, id, args[0], "round"); + break; case GLSLstd450Acosh: case GLSLstd450Asinh: diff --git a/3rdparty/spirv-cross/spirv_hlsl.hpp b/3rdparty/spirv-cross/spirv_hlsl.hpp index 11f7f2634..4abb203b2 100644 --- a/3rdparty/spirv-cross/spirv_hlsl.hpp +++ b/3rdparty/spirv-cross/spirv_hlsl.hpp @@ -124,6 +124,12 @@ public: // Uses half/int16_t/uint16_t instead of min16* types. // Also adds support for 16-bit load-store from (RW)ByteAddressBuffer. bool enable_16bit_types = false; + + // If matrices are used as IO variables, flatten the attribute declaration to use + // TEXCOORD{N,N+1,N+2,...} rather than TEXCOORDN_{0,1,2,3}. + // If add_vertex_attribute_remap is used and this feature is used, + // the semantic name will be queried once per active location. + bool flatten_matrix_vertex_input_semantics = false; }; explicit CompilerHLSL(std::vector spirv_) diff --git a/3rdparty/spirv-cross/spirv_msl.cpp b/3rdparty/spirv-cross/spirv_msl.cpp index e82637fc4..e6033d82a 100644 --- a/3rdparty/spirv-cross/spirv_msl.cpp +++ b/3rdparty/spirv-cross/spirv_msl.cpp @@ -103,6 +103,16 @@ bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t de return itr != end(resource_bindings) && itr->second.second; } +// Returns the size of the array of resources used by the variable with the specified id. +// The returned value is retrieved from the resource binding added using add_msl_resource_binding(). +uint32_t CompilerMSL::get_resource_array_size(uint32_t id) const +{ + StageSetBinding tuple = { get_entry_point().model, get_decoration(id, DecorationDescriptorSet), + get_decoration(id, DecorationBinding) }; + auto itr = resource_bindings.find(tuple); + return itr != end(resource_bindings) ? itr->second.first.count : 0; +} + uint32_t CompilerMSL::get_automatic_msl_resource_binding(uint32_t id) const { return get_extended_decoration(id, SPIRVCrossDecorationResourceIndexPrimary); @@ -159,7 +169,7 @@ void CompilerMSL::build_implicit_builtins() active_input_builtins.get(BuiltInInstanceIndex) || active_input_builtins.get(BuiltInBaseInstance)); bool need_sample_mask = msl_options.additional_fixed_sample_mask != 0xffffffff; if (need_subpass_input || need_sample_pos || need_subgroup_mask || need_vertex_params || need_tesc_params || - need_multiview || need_dispatch_base || need_vertex_base_params || need_grid_params || + need_multiview || need_dispatch_base || need_vertex_base_params || need_grid_params || needs_sample_id || needs_subgroup_invocation_id || needs_subgroup_size || need_sample_mask) { bool has_frag_coord = false; @@ -225,7 +235,7 @@ void CompilerMSL::build_implicit_builtins() } } - if (need_sample_pos && builtin == BuiltInSampleId) + if ((need_sample_pos || needs_sample_id) && builtin == BuiltInSampleId) { builtin_sample_id_id = var.self; mark_implicit_builtin(StorageClassInput, BuiltInSampleId, var.self); @@ -404,7 +414,7 @@ void CompilerMSL::build_implicit_builtins() } } - if (!has_sample_id && need_sample_pos) + if (!has_sample_id && (need_sample_pos || needs_sample_id)) { uint32_t offset = ir.increase_bound_by(2); uint32_t type_ptr_id = offset; @@ -1283,6 +1293,8 @@ void CompilerMSL::preprocess_op_codes() needs_subgroup_invocation_id = true; if (preproc.needs_subgroup_size) needs_subgroup_size = true; + if (preproc.needs_sample_id) + needs_sample_id = true; } // Move the Private and Workgroup global variables to the entry function. @@ -1454,6 +1466,31 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: break; } + case OpExtInst: + { + uint32_t extension_set = ops[2]; + if (get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(ops[3]); + switch (op_450) + { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + { + // For these, we really need the stage-in block. It is theoretically possible to pass the + // interpolant object, but a) doing so would require us to create an entirely new variable + // with Interpolant type, and b) if we have a struct or array, handling all the members and + // elements could get unwieldy fast. + added_arg_ids.insert(stage_in_var_id); + break; + } + default: + break; + } + } + } + default: break; } @@ -1685,6 +1722,19 @@ uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t comp return new_type_id; } +uint32_t CompilerMSL::build_msl_interpolant_type(uint32_t type_id, bool is_noperspective) +{ + uint32_t new_type_id = ir.increase_bound_by(1); + SPIRType &type = set(new_type_id, get(type_id)); + type.basetype = SPIRType::Interpolant; + type.parent_type = type_id; + // In Metal, the pull-model interpolant type encodes perspective-vs-no-perspective in the type itself. + // Add this decoration so we know which argument to pass to the template. + if (is_noperspective) + set_decoration(new_type_id, DecorationNoPerspective); + return new_type_id; +} + void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta) { @@ -1783,7 +1833,10 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co } } - ib_type.member_types.push_back(type_id); + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(type_id, is_noperspective)); + else + ib_type.member_types.push_back(type_id); // Give the member a name string mbr_name = ensure_valid_name(to_expression(var.self), "m"); @@ -1791,6 +1844,16 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co // Update the original variable reference to include the structure reference string qual_var_name = ib_var_ref + "." + mbr_name; + // If using pull-model interpolation, need to add a call to the correct interpolation method. + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + { + if (is_centroid) + qual_var_name += ".interpolate_at_centroid()"; + else if (is_sample) + qual_var_name += join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + qual_var_name += ".interpolate_at_center()"; + } if (padded_output || padded_input) { @@ -1842,7 +1905,10 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co type_id = get_pointee_type_id(type_id); if (meta.strip_array && is_array(get(type_id))) type_id = get(type_id).parent_type; - ib_type.member_types[ib_mbr_idx] = type_id; + if (pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = type_id; } set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); mark_location_as_used_by_shader(locn, get(type_id), storage); @@ -1878,14 +1944,17 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co } // Copy interpolation decorations if needed - if (is_flat) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); - if (is_noperspective) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); - if (is_centroid) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); - if (is_sample) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } // If we have location meta, there is no unique OrigID. We won't need it, since we flatten/unflatten // the variable to stack anyways here. @@ -1984,7 +2053,10 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage } } - ib_type.member_types.push_back(get_pointee_type_id(type_id)); + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(get_pointee_type_id(type_id), is_noperspective)); + else + ib_type.member_types.push_back(get_pointee_type_id(type_id)); // Give the member a name string mbr_name = ensure_valid_name(join(to_expression(var.self), "_", i), "m"); @@ -1998,7 +2070,10 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage { var.basetype = ensure_correct_input_type(var.basetype, locn); uint32_t mbr_type_id = ensure_correct_input_type(usable_type->self, locn); - ib_type.member_types[ib_mbr_idx] = mbr_type_id; + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(mbr_type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = mbr_type_id; } set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); mark_location_as_used_by_shader(locn, *usable_type, storage); @@ -2022,15 +2097,18 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, index); } - // Copy interpolation decorations if needed - if (is_flat) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); - if (is_noperspective) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); - if (is_centroid) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); - if (is_sample) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + // Copy interpolation decorations if needed + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); @@ -2040,8 +2118,23 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage switch (storage) { case StorageClassInput: - entry_func.fixup_hooks_in.push_back( - [=, &var]() { statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, ";"); }); + entry_func.fixup_hooks_in.push_back([=, &var]() { + if (pull_model_inputs.count(var.self)) + { + string lerp_call; + if (is_centroid) + lerp_call = ".interpolate_at_centroid()"; + else if (is_sample) + lerp_call = join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + lerp_call = ".interpolate_at_center()"; + statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, lerp_call, ";"); + } + else + { + statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, ";"); + } + }); break; case StorageClassOutput: @@ -2165,7 +2258,10 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass { // Add a reference to the variable type to the interface struct. uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); - ib_type.member_types.push_back(usable_type->self); + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(usable_type->self, is_noperspective)); + else + ib_type.member_types.push_back(usable_type->self); // Give the member a name string mbr_name = ensure_valid_name(join(to_qualified_member_name(var_type, mbr_idx), "_", i), "m"); @@ -2199,15 +2295,18 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent)) SPIRV_CROSS_THROW("DecorationComponent on matrices and arrays make little sense."); - // Copy interpolation decorations if needed - if (is_flat) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); - if (is_noperspective) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); - if (is_centroid) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); - if (is_sample) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + // Copy interpolation decorations if needed + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, mbr_idx); @@ -2219,8 +2318,23 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass { case StorageClassInput: entry_func.fixup_hooks_in.push_back([=, &var, &var_type]() { - statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), "[", i, "] = ", ib_var_ref, - ".", mbr_name, ";"); + if (pull_model_inputs.count(var.self)) + { + string lerp_call; + if (is_centroid) + lerp_call = ".interpolate_at_centroid()"; + else if (is_sample) + lerp_call = join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + lerp_call = ".interpolate_at_center()"; + statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), "[", i, "] = ", ib_var_ref, + ".", mbr_name, lerp_call, ";"); + } + else + { + statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), "[", i, "] = ", ib_var_ref, + ".", mbr_name, ";"); + } }); break; @@ -2269,7 +2383,10 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); mbr_type_id = ensure_correct_builtin_type(mbr_type_id, builtin); var_type.member_types[mbr_idx] = mbr_type_id; - ib_type.member_types.push_back(mbr_type_id); + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(mbr_type_id, is_noperspective)); + else + ib_type.member_types.push_back(mbr_type_id); // Give the member a name string mbr_name = ensure_valid_name(to_qualified_member_name(var_type, mbr_idx), "m"); @@ -2277,6 +2394,16 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor // Update the original variable reference to include the structure reference string qual_var_name = ib_var_ref + "." + mbr_name; + // If using pull-model interpolation, need to add a call to the correct interpolation method. + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + { + if (is_centroid) + qual_var_name += ".interpolate_at_centroid()"; + else if (is_sample) + qual_var_name += join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + qual_var_name += ".interpolate_at_center()"; + } if (is_builtin && !meta.strip_array) { @@ -2314,7 +2441,10 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor { mbr_type_id = ensure_correct_input_type(mbr_type_id, locn); var_type.member_types[mbr_idx] = mbr_type_id; - ib_type.member_types[ib_mbr_idx] = mbr_type_id; + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(mbr_type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = mbr_type_id; } set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); mark_location_as_used_by_shader(locn, get(mbr_type_id), storage); @@ -2328,7 +2458,10 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor { mbr_type_id = ensure_correct_input_type(mbr_type_id, locn); var_type.member_types[mbr_idx] = mbr_type_id; - ib_type.member_types[ib_mbr_idx] = mbr_type_id; + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(mbr_type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = mbr_type_id; } set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); mark_location_as_used_by_shader(locn, get(mbr_type_id), storage); @@ -2358,15 +2491,18 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor qual_pos_var_name = qual_var_name; } - // Copy interpolation decorations if needed - if (is_flat) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); - if (is_noperspective) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); - if (is_centroid) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); - if (is_sample) - set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + // Copy interpolation decorations if needed + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, mbr_idx); @@ -2597,11 +2733,13 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st // for per-vertex variables in a tessellation control shader. void CompilerMSL::fix_up_interface_member_indices(StorageClass storage, uint32_t ib_type_id) { - // Only needed for tessellation shaders. + // Only needed for tessellation shaders and pull-model interpolants. // Need to redirect interface indices back to variables themselves. // For structs, each member of the struct need a separate instance. if (get_execution_model() != ExecutionModelTessellationControl && - !(get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput)) + !(get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput) && + !(get_execution_model() == ExecutionModelFragment && storage == StorageClassInput && + !pull_model_inputs.empty())) return; auto mbr_cnt = uint32_t(ir.meta[ib_type_id].members.size()); @@ -6539,6 +6677,61 @@ void CompilerMSL::prepare_access_chain_for_scalar_access(std::string &expr, cons } } +// Sets the interface member index for an access chain to a pull-model interpolant. +void CompilerMSL::fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length) +{ + auto *var = maybe_get_backing_variable(ops[2]); + if (!var || !pull_model_inputs.count(var->self)) + return; + // Get the base index. + uint32_t interface_index; + auto &var_type = get_variable_data_type(*var); + auto &result_type = get(ops[0]); + auto *type = &var_type; + if (has_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex)) + { + interface_index = get_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex); + } + else + { + // Assume an access chain into a struct variable. + assert(var_type.basetype == SPIRType::Struct); + auto &c = get(ops[3 + var_type.array.size()]); + interface_index = get_extended_member_decoration(var->self, c.scalar(), + SPIRVCrossDecorationInterfaceMemberIndex); + } + // Accumulate indices. We'll have to skip over the one for the struct, if present, because we already accounted + // for that getting the base index. + for (uint32_t i = 3; i < length; ++i) + { + if (is_vector(*type) && is_scalar(result_type)) + { + // We don't want to combine the next index. Actually, we need to save it + // so we know to apply a swizzle to the result of the interpolation. + set_extended_decoration(ops[1], SPIRVCrossDecorationInterpolantComponentExpr, ops[i]); + break; + } + + auto *c = maybe_get(ops[i]); + if (!c || c->specialization) + SPIRV_CROSS_THROW("Trying to dynamically index into an array interface variable using pull-model " + "interpolation. This is currently unsupported."); + + if (type->parent_type) + type = &get(type->parent_type); + else if (type->basetype == SPIRType::Struct) + type = &get(type->member_types[c->scalar()]); + + if (!has_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex) && + i - 3 == var_type.array.size()) + continue; + + interface_index += c->scalar(); + } + // Save this to the access chain itself so we can recover it later when calling an interpolation function. + set_extended_decoration(ops[1], SPIRVCrossDecorationInterfaceMemberIndex, interface_index); +} + // Override for MSL-specific syntax instructions void CompilerMSL::emit_instruction(const Instruction &instruction) { @@ -7134,6 +7327,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction) } else CompilerGLSL::emit_instruction(instruction); + fix_up_interpolant_access_chain(ops, instruction.length); break; case OpStore: @@ -7893,10 +8087,70 @@ void CompilerMSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "precise::clamp"); break; - // TODO: - // GLSLstd450InterpolateAtCentroid (centroid_no_perspective qualifier) - // GLSLstd450InterpolateAtSample (sample_no_perspective qualifier) - // GLSLstd450InterpolateAtOffset + case GLSLstd450InterpolateAtCentroid: + { + // We can't just emit the expression normally, because the qualified name contains a call to the default + // interpolate method, or refers to a local variable. We saved the interface index we need; use it to construct + // the base for the method call. + uint32_t interface_index = get_extended_decoration(args[0], SPIRVCrossDecorationInterfaceMemberIndex); + string component; + if (has_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr)) + { + uint32_t index_expr = get_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr); + auto *c = maybe_get(index_expr); + if (!c || c->specialization) + component = join("[", to_expression(index_expr), "]"); + else + component = join(".", index_to_swizzle(c->scalar())); + } + emit_op(result_type, id, + join(to_name(stage_in_var_id), ".", to_member_name(get_stage_in_struct_type(), interface_index), + ".interpolate_at_centroid()", component), should_forward(args[0])); + break; + } + + case GLSLstd450InterpolateAtSample: + { + uint32_t interface_index = get_extended_decoration(args[0], SPIRVCrossDecorationInterfaceMemberIndex); + string component; + if (has_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr)) + { + uint32_t index_expr = get_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr); + auto *c = maybe_get(index_expr); + if (!c || c->specialization) + component = join("[", to_expression(index_expr), "]"); + else + component = join(".", index_to_swizzle(c->scalar())); + } + emit_op(result_type, id, + join(to_name(stage_in_var_id), ".", to_member_name(get_stage_in_struct_type(), interface_index), + ".interpolate_at_sample(", to_expression(args[1]), ")", component), + should_forward(args[0]) && should_forward(args[1])); + break; + } + + case GLSLstd450InterpolateAtOffset: + { + uint32_t interface_index = get_extended_decoration(args[0], SPIRVCrossDecorationInterfaceMemberIndex); + string component; + if (has_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr)) + { + uint32_t index_expr = get_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr); + auto *c = maybe_get(index_expr); + if (!c || c->specialization) + component = join("[", to_expression(index_expr), "]"); + else + component = join(".", index_to_swizzle(c->scalar())); + } + // Like Direct3D, Metal puts the (0, 0) at the upper-left corner, not the center as SPIR-V and GLSL do. + // Offset the offset by (1/2 - 1/16), or 0.4375, to compensate for this. + // It has to be (1/2 - 1/16) and not 1/2, or several CTS tests subtly break on Intel. + emit_op(result_type, id, + join(to_name(stage_in_var_id), ".", to_member_name(get_stage_in_struct_type(), interface_index), + ".interpolate_at_offset(", to_expression(args[1]), " + 0.4375)", component), + should_forward(args[0]) && should_forward(args[1])); + break; + } case GLSLstd450Distance: // MSL does not support scalar versions here. @@ -8136,7 +8390,7 @@ void CompilerMSL::emit_function_prototype(SPIRFunction &func, const Bitset &) // Manufacture automatic sampler arg for SampledImage texture if (arg_type.image.dim != DimBuffer) - decl += join(", thread const ", sampler_type(arg_type), " ", to_sampler_expression(arg.id)); + decl += join(", thread const ", sampler_type(arg_type, arg.id), " ", to_sampler_expression(arg.id)); } // Manufacture automatic swizzle arg. @@ -8642,10 +8896,10 @@ string CompilerMSL::to_function_args(const TextureFunctionArguments &args, bool grad_y = 0; farg_str += ", level(0)"; } - else + else if (!msl_options.supports_msl_version(2, 3)) { SPIRV_CROSS_THROW("Using non-constant 0.0 gradient() qualifier for sample_compare. This is not " - "supported in MSL macOS."); + "supported on macOS prior to MSL 2.3."); } } @@ -8657,10 +8911,10 @@ string CompilerMSL::to_function_args(const TextureFunctionArguments &args, bool { bias = 0; } - else + else if (!msl_options.supports_msl_version(2, 3)) { SPIRV_CROSS_THROW( - "Using non-constant 0.0 bias() qualifier for sample_compare. This is not supported in MSL macOS."); + "Using non-constant 0.0 bias() qualifier for sample_compare. This is not supported on macOS prior to MSL 2.3."); } } } @@ -8794,9 +9048,19 @@ string CompilerMSL::to_function_args(const TextureFunctionArguments &args, bool { forward = forward && should_forward(args.component); - if (const auto *var = maybe_get_backing_variable(img)) - if (!image_is_comparison(get(var->basetype), var->self)) - farg_str += ", " + to_component_argument(args.component); + uint32_t image_var = 0; + if (const auto *combined = maybe_get(img)) + { + if (const auto *img_var = maybe_get_backing_variable(combined->image)) + image_var = img_var->self; + } + else if (const auto *var = maybe_get_backing_variable(img)) + { + image_var = var->self; + } + + if (image_var == 0 || !image_is_comparison(expression_type(image_var), image_var)) + farg_str += ", " + to_component_argument(args.component); } } @@ -9941,6 +10205,8 @@ string CompilerMSL::get_type_address_space(const SPIRType &type, uint32_t id, bo if (get_execution_model() == ExecutionModelTessellationControl && var && var->basevariable == stage_in_ptr_var_id) addr_space = msl_options.multi_patch_workgroup ? "constant" : "threadgroup"; + if (get_execution_model() == ExecutionModelFragment && var && var->basevariable == stage_in_var_id) + addr_space = "thread"; break; case StorageClassOutput: @@ -10449,7 +10715,7 @@ void CompilerMSL::entry_point_args_discrete_descriptors(string &ep_args) case SPIRType::Sampler: if (!ep_args.empty()) ep_args += ", "; - ep_args += sampler_type(type) + " " + r.name; + ep_args += sampler_type(type, var_id) + " " + r.name; ep_args += " [[sampler(" + convert_to_string(r.index) + ")]]"; break; case SPIRType::Image: @@ -11245,7 +11511,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) decl += ")"; decl += type_to_array_glsl(type); } - else if (!opaque_handle) + else if (!opaque_handle && (!pull_model_inputs.count(var.basevariable) || type.basetype == SPIRType::Struct)) { // If this is going to be a reference to a variable pointer, the address space // for the reference has to go before the '&', but after the '*'. @@ -11709,7 +11975,7 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id) return image_type_glsl(type, id); case SPIRType::Sampler: - return sampler_type(type); + return sampler_type(type, id); case SPIRType::Void: return "void"; @@ -11719,6 +11985,10 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id) case SPIRType::ControlPointArray: return join("patch_control_point<", type_to_glsl(get(type.parent_type), id), ">"); + + case SPIRType::Interpolant: + return join("interpolant<", type_to_glsl(get(type.parent_type), id), ", interpolation::", + has_decoration(type.self, DecorationNoPerspective) ? "no_perspective" : "perspective", ">"); // Scalars case SPIRType::Boolean: @@ -11840,8 +12110,15 @@ std::string CompilerMSL::variable_decl(const SPIRType &type, const std::string & return CompilerGLSL::variable_decl(type, name, id); } -std::string CompilerMSL::sampler_type(const SPIRType &type) +std::string CompilerMSL::sampler_type(const SPIRType &type, uint32_t id) { + auto *var = maybe_get(id); + if (var && var->basevariable) + { + // Check against the base variable, and not a fake ID which might have been generated for this variable. + id = var->basevariable; + } + if (!type.array.empty()) { if (!msl_options.supports_msl_version(2)) @@ -11851,12 +12128,16 @@ std::string CompilerMSL::sampler_type(const SPIRType &type) SPIRV_CROSS_THROW("Arrays of arrays of samplers are not supported in MSL."); // Arrays of samplers in MSL must be declared with a special array syntax ala C++11 std::array. + // If we have a runtime array, it could be a variable-count descriptor set binding. uint32_t array_size = to_array_size_literal(type); + if (array_size == 0) + array_size = get_resource_array_size(id); + if (array_size == 0) SPIRV_CROSS_THROW("Unsized array of samplers is not supported in MSL."); auto &parent = get(get_pointee_type(type).parent_type); - return join("array<", sampler_type(parent), ", ", array_size, ">"); + return join("array<", sampler_type(parent, id), ", ", array_size, ">"); } else return "sampler"; @@ -11893,7 +12174,11 @@ string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id) SPIRV_CROSS_THROW("Arrays of arrays of textures are not supported in MSL."); // Arrays of images in MSL must be declared with a special array syntax ala C++11 std::array. + // If we have a runtime array, it could be a variable-count descriptor set binding. uint32_t array_size = to_array_size_literal(type); + if (array_size == 0) + array_size = get_resource_array_size(id); + if (array_size == 0) SPIRV_CROSS_THROW("Unsized array of images is not supported in MSL."); @@ -13285,6 +13570,55 @@ bool CompilerMSL::OpCodePreprocessor::handle(Op opcode, const uint32_t *args, ui break; } + case OpExtInst: + { + uint32_t extension_set = args[2]; + if (compiler.get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(args[3]); + switch (op_450) + { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + { + if (!compiler.msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("Pull-model interpolation requires MSL 2.3."); + // Fragment varyings used with pull-model interpolation need special handling, + // due to the way pull-model interpolation works in Metal. + auto *var = compiler.maybe_get_backing_variable(args[4]); + if (var) + { + compiler.pull_model_inputs.insert(var->self); + auto &var_type = compiler.get_variable_element_type(*var); + // In addition, if this variable has a 'Sample' decoration, we need the sample ID + // in order to do default interpolation. + if (compiler.has_decoration(var->self, DecorationSample)) + { + needs_sample_id = true; + } + else if (var_type.basetype == SPIRType::Struct) + { + // Now we need to check each member and see if it has this decoration. + for (uint32_t i = 0; i < var_type.member_types.size(); ++i) + { + if (compiler.has_member_decoration(var_type.self, i, DecorationSample)) + { + needs_sample_id = true; + break; + } + } + } + } + break; + } + default: + break; + } + } + break; + } + default: break; } diff --git a/3rdparty/spirv-cross/spirv_msl.hpp b/3rdparty/spirv-cross/spirv_msl.hpp index 13271f62a..a26047e04 100644 --- a/3rdparty/spirv-cross/spirv_msl.hpp +++ b/3rdparty/spirv-cross/spirv_msl.hpp @@ -60,7 +60,11 @@ struct MSLShaderInput // Matches the binding index of a MSL resource for a binding within a descriptor set. // Taken together, the stage, desc_set and binding combine to form a reference to a resource -// descriptor used in a particular shading stage. +// descriptor used in a particular shading stage. The count field indicates the number of +// resources consumed by this binding, if the binding represents an array of resources. +// If the resource array is a run-time-sized array, which are legal in GLSL or SPIR-V, this value +// will be used to declare the array size in MSL, which does not support run-time-sized arrays. +// For resources that are not held in a run-time-sized array, the count field does not need to be populated. // If using MSL 2.0 argument buffers, the descriptor set is not marked as a discrete descriptor set, // and (for iOS only) the resource is not a storage image (sampled != 2), the binding reference we // remap to will become an [[id(N)]] attribute within the "descriptor set" argument buffer structure. @@ -71,6 +75,7 @@ struct MSLResourceBinding spv::ExecutionModel stage = spv::ExecutionModelMax; uint32_t desc_set = 0; uint32_t binding = 0; + uint32_t count = 0; uint32_t msl_buffer = 0; uint32_t msl_texture = 0; uint32_t msl_sampler = 0; @@ -672,7 +677,7 @@ protected: std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0) override; std::string image_type_glsl(const SPIRType &type, uint32_t id = 0) override; - std::string sampler_type(const SPIRType &type); + std::string sampler_type(const SPIRType &type, uint32_t id); std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override; std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; std::string to_name(uint32_t id, bool allow_alias = true) const override; @@ -756,6 +761,7 @@ protected: void emit_specialization_constants_and_structs(); void emit_interface_block(uint32_t ib_var_id); bool maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs); + uint32_t get_resource_array_size(uint32_t id) const; void fix_up_shader_inputs_outputs(); @@ -863,6 +869,7 @@ protected: void prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, spv::StorageClass storage, bool &is_packed) override; + void fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length); bool emit_tessellation_access_chain(const uint32_t *ops, uint32_t length); bool emit_tessellation_io_load(uint32_t result_type, uint32_t id, uint32_t ptr); bool is_out_of_bounds_tessellation_level(uint32_t id_lhs); @@ -922,6 +929,7 @@ protected: bool added_builtin_tess_level = false; bool needs_subgroup_invocation_id = false; bool needs_subgroup_size = false; + bool needs_sample_id = false; std::string qual_pos_var_name; std::string stage_in_var_name = "in"; std::string stage_out_var_name = "out"; @@ -947,6 +955,7 @@ protected: std::unordered_set buffers_requiring_array_length; SmallVector buffer_arrays; std::unordered_set atomic_image_vars; // Emulate texture2D atomic operations + std::unordered_set pull_model_inputs; // Must be ordered since array is in a specific order. std::map> buffers_requiring_dynamic_offset; @@ -965,6 +974,7 @@ protected: uint32_t get_target_components_for_fragment_location(uint32_t location) const; uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components, SPIRType::BaseType basetype = SPIRType::Unknown); + uint32_t build_msl_interpolant_type(uint32_t type_id, bool is_noperspective); bool suppress_missing_prototypes = false; @@ -994,6 +1004,7 @@ protected: bool uses_resource_write = false; bool needs_subgroup_invocation_id = false; bool needs_subgroup_size = false; + bool needs_sample_id = false; }; // OpcodeHandler that scans for uses of sampled images diff --git a/3rdparty/spirv-cross/spirv_parser.cpp b/3rdparty/spirv-cross/spirv_parser.cpp index 49c429cd1..92db5287a 100644 --- a/3rdparty/spirv-cross/spirv_parser.cpp +++ b/3rdparty/spirv-cross/spirv_parser.cpp @@ -623,10 +623,15 @@ void Parser::parse(const Instruction &instruction) { uint32_t id = ops[0]; - auto &base = get(ops[2]); + // Very rarely, we might receive a FunctionPrototype here. + // We won't be able to compile it, but we shouldn't crash when parsing. + // We should be able to reflect. + auto *base = maybe_get(ops[2]); auto &ptrbase = set(id); - ptrbase = base; + if (base) + ptrbase = *base; + ptrbase.pointer = true; ptrbase.pointer_depth++; ptrbase.storage = static_cast(ops[1]); @@ -634,7 +639,7 @@ void Parser::parse(const Instruction &instruction) if (ptrbase.storage == StorageClassAtomicCounter) ptrbase.basetype = SPIRType::AtomicCounter; - if (base.forward_pointer) + if (base && base->forward_pointer) forward_pointer_fixups.push_back({ id, ops[2] }); ptrbase.parent_type = ops[2];