diff options
Diffstat (limited to 'spirv_msl.cpp')
-rw-r--r-- | spirv_msl.cpp | 460 |
1 files changed, 291 insertions, 169 deletions
diff --git a/spirv_msl.cpp b/spirv_msl.cpp index b974b669..bc51447a 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -234,7 +234,8 @@ void CompilerMSL::build_implicit_builtins() bool need_sample_pos = active_input_builtins.get(BuiltInSamplePosition); bool need_vertex_params = capture_output_to_buffer && get_execution_model() == ExecutionModelVertex && !msl_options.vertex_for_tessellation; - bool need_tesc_params = get_execution_model() == ExecutionModelTessellationControl; + bool need_tesc_params = is_tesc_shader(); + bool need_tese_params = is_tese_shader() && msl_options.raw_buffer_tese_input; bool need_subgroup_mask = active_input_builtins.get(BuiltInSubgroupEqMask) || active_input_builtins.get(BuiltInSubgroupGeMask) || active_input_builtins.get(BuiltInSubgroupGtMask) || active_input_builtins.get(BuiltInSubgroupLeMask) || @@ -257,9 +258,9 @@ void CompilerMSL::build_implicit_builtins() bool need_workgroup_size = msl_options.emulate_subgroups && active_input_builtins.get(BuiltInNumSubgroups); 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 || needs_sample_id || - needs_subgroup_invocation_id || needs_subgroup_size || has_additional_fixed_sample_mask() || need_local_invocation_index || - need_workgroup_size) + need_tese_params || need_multiview || need_dispatch_base || need_vertex_base_params || need_grid_params || + needs_sample_id || needs_subgroup_invocation_id || needs_subgroup_size || has_additional_fixed_sample_mask() || + need_local_invocation_index || need_workgroup_size) { bool has_frag_coord = false; bool has_sample_id = false; @@ -365,23 +366,28 @@ void CompilerMSL::build_implicit_builtins() } } - if (need_tesc_params) + if (need_tesc_params && builtin == BuiltInInvocationId) { - switch (builtin) - { - case BuiltInInvocationId: - builtin_invocation_id_id = var.self; - mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var.self); - has_invocation_id = true; - break; - case BuiltInPrimitiveId: - builtin_primitive_id_id = var.self; - mark_implicit_builtin(StorageClassInput, BuiltInPrimitiveId, var.self); - has_primitive_id = true; - break; - default: - break; - } + builtin_invocation_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var.self); + has_invocation_id = true; + } + + if ((need_tesc_params || need_tese_params) && builtin == BuiltInPrimitiveId) + { + builtin_primitive_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInPrimitiveId, var.self); + has_primitive_id = true; + } + + if (need_tese_params && builtin == BuiltInTessLevelOuter) + { + tess_level_outer_var_id = var.self; + } + + if (need_tese_params && builtin == BuiltInTessLevelInner) + { + tess_level_inner_var_id = var.self; } if ((need_subgroup_mask || needs_subgroup_invocation_id) && builtin == BuiltInSubgroupLocalInvocationId) @@ -640,7 +646,7 @@ void CompilerMSL::build_implicit_builtins() } if ((need_tesc_params && (msl_options.multi_patch_workgroup || !has_invocation_id || !has_primitive_id)) || - need_grid_params) + (need_tese_params && !has_primitive_id) || need_grid_params) { uint32_t type_ptr_id = ir.increase_bound_by(1); @@ -653,7 +659,7 @@ void CompilerMSL::build_implicit_builtins() auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr); ptr_type.self = get_uint_type_id(); - if (msl_options.multi_patch_workgroup || need_grid_params) + if ((need_tesc_params && msl_options.multi_patch_workgroup) || need_grid_params) { uint32_t var_id = ir.increase_bound_by(1); @@ -674,7 +680,7 @@ void CompilerMSL::build_implicit_builtins() mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var_id); } - if (need_tesc_params && !has_primitive_id) + if ((need_tesc_params || need_tese_params) && !has_primitive_id) { uint32_t var_id = ir.increase_bound_by(1); @@ -891,11 +897,9 @@ void CompilerMSL::build_implicit_builtins() } // If we're returning a struct from a vertex-like entry point, we must return a position attribute. - bool need_position = - (get_execution_model() == ExecutionModelVertex || - get_execution_model() == ExecutionModelTessellationEvaluation) && - !capture_output_to_buffer && !get_is_rasterization_disabled() && - !active_output_builtins.get(BuiltInPosition); + bool need_position = (get_execution_model() == ExecutionModelVertex || is_tese_shader()) && + !capture_output_to_buffer && !get_is_rasterization_disabled() && + !active_output_builtins.get(BuiltInPosition); if (need_position) { @@ -1085,7 +1089,7 @@ SPIRType &CompilerMSL::get_patch_stage_out_struct_type() std::string CompilerMSL::get_tess_factor_struct_name() { - if (get_entry_point().flags.get(ExecutionModeTriangles)) + if (is_tessellating_triangles()) return "MTLTriangleTessellationFactorsHalf"; return "MTLQuadTessellationFactorsHalf"; } @@ -1483,10 +1487,10 @@ string CompilerMSL::compile() stage_out_var_id = add_interface_block(StorageClassOutput); patch_stage_out_var_id = add_interface_block(StorageClassOutput, true); stage_in_var_id = add_interface_block(StorageClassInput); - if (get_execution_model() == ExecutionModelTessellationEvaluation) + if (is_tese_shader()) patch_stage_in_var_id = add_interface_block(StorageClassInput, true); - if (get_execution_model() == ExecutionModelTessellationControl) + if (is_tesc_shader()) stage_out_ptr_var_id = add_interface_block_pointer(stage_out_var_id, StorageClassOutput); if (is_tessellation_shader()) stage_in_ptr_var_id = add_interface_block_pointer(stage_in_var_id, StorageClassInput); @@ -1565,8 +1569,7 @@ void CompilerMSL::preprocess_op_codes() // Tessellation control shaders are run as compute functions in Metal, and so // must capture their output to a buffer. - if (get_execution_model() == ExecutionModelTessellationControl || - (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation)) + if (is_tesc_shader() || (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation)) { is_rasterization_disabled = true; capture_output_to_buffer = true; @@ -1906,9 +1909,8 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: bool is_patch = has_decoration(arg_id, DecorationPatch) || is_patch_block(*p_type); bool is_block = has_decoration(p_type->self, DecorationBlock); bool is_control_point_storage = - !is_patch && - ((is_tessellation_shader() && var.storage == StorageClassInput) || - (get_execution_model() == ExecutionModelTessellationControl && var.storage == StorageClassOutput)); + !is_patch && ((is_tessellation_shader() && var.storage == StorageClassInput) || + (is_tesc_shader() && var.storage == StorageClassOutput)); bool is_patch_block_storage = is_patch && is_block && var.storage == StorageClassOutput; bool is_builtin = is_builtin_variable(var); bool variable_is_stage_io = @@ -1924,8 +1926,8 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: if (is_redirected_to_global_stage_io) { - // Tessellation control shaders see inputs and per-vertex outputs as arrays. - // Similarly, tessellation evaluation shaders see per-vertex inputs as arrays. + // Tessellation control shaders see inputs and per-point outputs as arrays. + // Similarly, tessellation evaluation shaders see per-point inputs as arrays. // We collected them into a structure; we must pass the array of this // structure to the function. std::string name; @@ -1948,10 +1950,6 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: } } - // Tessellation control shaders see inputs and per-vertex outputs as arrays. - // Similarly, tessellation evaluation shaders see per-vertex inputs as arrays. - // We collected them into a structure; we must pass the array of this - // structure to the function. if (var.storage == StorageClassInput) { auto &added_in = is_patch ? patch_added_in : control_point_added_in; @@ -1975,6 +1973,8 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: set<SPIRVariable>(next_id, type_id, StorageClassFunction, 0, arg_id); set_name(next_id, name); + if (is_tese_shader() && msl_options.raw_buffer_tese_input && var.storage == StorageClassInput) + set_decoration(next_id, DecorationNonWritable); } else if (is_builtin && has_decoration(p_type->self, DecorationBlock)) { @@ -2994,7 +2994,7 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor { set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location); mark_location_as_used_by_shader(location, get<SPIRType>(mbr_type_id), storage); - location++; + location += type_to_location_count(get<SPIRType>(mbr_type_id)); } else if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation)) { @@ -3011,7 +3011,7 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor } set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location); mark_location_as_used_by_shader(location, get<SPIRType>(mbr_type_id), storage); - location++; + location += type_to_location_count(get<SPIRType>(mbr_type_id)); } else if (has_decoration(var.self, DecorationLocation)) { @@ -3027,21 +3027,21 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor } set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location); mark_location_as_used_by_shader(location, get<SPIRType>(mbr_type_id), storage); - location++; + location += type_to_location_count(get<SPIRType>(mbr_type_id)); } else if (is_builtin && is_tessellation_shader() && storage == StorageClassInput && inputs_by_builtin.count(builtin)) { location = inputs_by_builtin[builtin].location; set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location); mark_location_as_used_by_shader(location, get<SPIRType>(mbr_type_id), storage); - location++; + location += type_to_location_count(get<SPIRType>(mbr_type_id)); } else if (is_builtin && capture_output_to_buffer && storage == StorageClassOutput && outputs_by_builtin.count(builtin)) { location = outputs_by_builtin[builtin].location; set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location); mark_location_as_used_by_shader(location, get<SPIRType>(mbr_type_id), storage); - location++; + location += type_to_location_count(get<SPIRType>(mbr_type_id)); } // Copy the component location, if present. @@ -3111,19 +3111,10 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var) { - auto &entry_func = get<SPIRFunction>(ir.default_entry_point); auto &var_type = get_variable_element_type(var); BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); - - // Force the variable to have the proper name. - string var_name = builtin_to_glsl(builtin, StorageClassFunction); - set_name(var.self, var_name); - - // We need to declare the variable early and at entry-point scope. - entry_func.add_local_variable(var.self); - vars_needing_early_declaration.push_back(var.self); - bool triangles = get_execution_mode_bitset().get(ExecutionModeTriangles); + bool triangles = is_tessellating_triangles(); string mbr_name; // Add a reference to the variable type to the interface struct. @@ -3169,7 +3160,7 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_ } else { - mbr_name = var_name; + mbr_name = builtin_to_glsl(builtin, StorageClassFunction); uint32_t type_id = build_extended_vector_type(var_type.self, builtin == BuiltInTessLevelOuter ? 4 : 2); @@ -3189,27 +3180,49 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_ mark_locations(new_var_type); } + add_tess_level_input(ib_var_ref, mbr_name, var); +} + +void CompilerMSL::add_tess_level_input(const std::string &base_ref, const std::string &mbr_name, SPIRVariable &var) +{ + auto &entry_func = get<SPIRFunction>(ir.default_entry_point); + BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + + // Force the variable to have the proper name. + string var_name = builtin_to_glsl(builtin, StorageClassFunction); + set_name(var.self, var_name); + + // We need to declare the variable early and at entry-point scope. + entry_func.add_local_variable(var.self); + vars_needing_early_declaration.push_back(var.self); + bool triangles = is_tessellating_triangles(); + if (builtin == BuiltInTessLevelOuter) { - entry_func.fixup_hooks_in.push_back([=]() { - statement(var_name, "[0] = ", ib_var_ref, ".", mbr_name, ".x;"); - statement(var_name, "[1] = ", ib_var_ref, ".", mbr_name, ".y;"); - statement(var_name, "[2] = ", ib_var_ref, ".", mbr_name, ".z;"); - if (!triangles) - statement(var_name, "[3] = ", ib_var_ref, ".", mbr_name, ".w;"); - }); + entry_func.fixup_hooks_in.push_back( + [=]() + { + statement(var_name, "[0] = ", base_ref, ".", mbr_name, "[0];"); + statement(var_name, "[1] = ", base_ref, ".", mbr_name, "[1];"); + statement(var_name, "[2] = ", base_ref, ".", mbr_name, "[2];"); + if (!triangles) + statement(var_name, "[3] = ", base_ref, ".", mbr_name, "[3];"); + }); } else { entry_func.fixup_hooks_in.push_back([=]() { if (triangles) { - statement(var_name, "[0] = ", ib_var_ref, ".", mbr_name, ".w;"); + if (msl_options.raw_buffer_tese_input) + statement(var_name, "[0] = ", base_ref, ".", mbr_name, ";"); + else + statement(var_name, "[0] = ", base_ref, ".", mbr_name, "[3];"); } else { - statement(var_name, "[0] = ", ib_var_ref, ".", mbr_name, ".x;"); - statement(var_name, "[1] = ", ib_var_ref, ".", mbr_name, ".y;"); + statement(var_name, "[0] = ", base_ref, ".", mbr_name, "[0];"); + statement(var_name, "[1] = ", base_ref, ".", mbr_name, "[1];"); } }); } @@ -3220,7 +3233,8 @@ bool CompilerMSL::variable_storage_requires_stage_io(spv::StorageClass storage) if (storage == StorageClassOutput) return !capture_output_to_buffer; else if (storage == StorageClassInput) - return !(get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup); + return !(is_tesc_shader() && msl_options.multi_patch_workgroup) && + !(is_tese_shader() && msl_options.raw_buffer_tese_input); else return false; } @@ -3345,7 +3359,8 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st if (var_type.basetype == SPIRType::Struct) { - bool block_requires_flattening = variable_storage_requires_stage_io(storage) || is_block; + bool block_requires_flattening = + variable_storage_requires_stage_io(storage) || (is_block && var_type.array.empty()); bool needs_local_declaration = !is_builtin && block_requires_flattening && meta.allow_local_declaration; if (needs_local_declaration) @@ -3474,8 +3489,7 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st // If we're redirecting a block, we might still need to access the original block // variable if we're masking some members. - if (masked_block && !needs_local_declaration && - (!is_builtin_variable(var) || get_execution_model() == ExecutionModelTessellationControl)) + if (masked_block && !needs_local_declaration && (!is_builtin_variable(var) || is_tesc_shader())) { if (is_builtin_variable(var)) { @@ -3496,8 +3510,8 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st } } } - else if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput && - !meta.strip_array && is_builtin && (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner)) + else if (is_tese_shader() && storage == StorageClassInput && !meta.strip_array && is_builtin && + (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner)) { add_tess_level_input_to_interface_block(ib_var_ref, ib_type, var); } @@ -3534,8 +3548,7 @@ void CompilerMSL::fix_up_interface_member_indices(StorageClass storage, uint32_t // 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) && + if (!is_tesc_shader() && !(is_tese_shader() && storage == StorageClassInput) && !(get_execution_model() == ExecutionModelFragment && storage == StorageClassInput && !pull_model_inputs.empty())) return; @@ -3624,9 +3637,8 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) // These builtins are part of the stage in/out structs. bool is_interface_block_builtin = - builtin_is_stage_in_out || - (get_execution_model() == ExecutionModelTessellationEvaluation && - (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner)); + builtin_is_stage_in_out || (is_tese_shader() && !msl_options.raw_buffer_tese_input && + (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner)); bool is_active = interface_variable_exists_in_entry_point(var.self); if (is_builtin && is_active) @@ -3722,12 +3734,29 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) } } } + + if (is_tese_shader() && msl_options.raw_buffer_tese_input && patch && storage == StorageClassInput && + (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner)) + { + // In this case, we won't add the builtin to the interface struct, + // but we still need the hook to run to populate the arrays. + string base_ref = join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id), "]"); + const char *mbr_name = + bi_type == BuiltInTessLevelOuter ? "edgeTessellationFactor" : "insideTessellationFactor"; + add_tess_level_input(base_ref, mbr_name, var); + if (inputs_by_builtin.count(bi_type)) + { + uint32_t locn = inputs_by_builtin[bi_type].location; + mark_location_as_used_by_shader(locn, type, StorageClassInput); + } + } }); // If no variables qualify, leave. // For patch input in a tessellation evaluation shader, the per-vertex stage inputs // are included in a special patch control point array. - if (vars.empty() && !(storage == StorageClassInput && patch && stage_in_var_id)) + if (vars.empty() && + !(!msl_options.raw_buffer_tese_input && storage == StorageClassInput && patch && stage_in_var_id)) return 0; // Add a new typed variable for this interface structure. @@ -3750,8 +3779,9 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) { case StorageClassInput: ib_var_ref = patch ? patch_stage_in_var_name : stage_in_var_name; - if (get_execution_model() == ExecutionModelTessellationControl) + switch (get_execution_model()) { + case ExecutionModelTessellationControl: // Add a hook to populate the shared workgroup memory containing the gl_in array. entry_func.fixup_hooks_in.push_back([=]() { // Can't use PatchVertices, PrimitiveId, or InvocationId yet; the hooks for those may not have run yet. @@ -3777,6 +3807,33 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) statement(" return;"); } }); + break; + case ExecutionModelTessellationEvaluation: + if (!msl_options.raw_buffer_tese_input) + break; + if (patch) + { + entry_func.fixup_hooks_in.push_back( + [=]() + { + statement("const device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", patch_input_buffer_var_name, "[", to_expression(builtin_primitive_id_id), + "];"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back( + [=]() + { + statement("const device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_in = &", + input_buffer_var_name, "[", to_expression(builtin_primitive_id_id), " * ", + get_entry_point().output_vertices, "];"); + }); + } + break; + default: + break; } break; @@ -3902,29 +3959,33 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) for (auto *p_var : vars) { - bool strip_array = - (get_execution_model() == ExecutionModelTessellationControl || - (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput)) && - !patch; + bool strip_array = (is_tesc_shader() || (is_tese_shader() && storage == StorageClassInput)) && !patch; // Fixing up flattened stores in TESC is impossible since the memory is group shared either via // device (not masked) or threadgroup (masked) storage classes and it's race condition city. meta.strip_array = strip_array; - meta.allow_local_declaration = !strip_array && !(get_execution_model() == ExecutionModelTessellationControl && - storage == StorageClassOutput); + meta.allow_local_declaration = !strip_array && !(is_tesc_shader() && storage == StorageClassOutput); add_variable_to_interface_block(storage, ib_var_ref, ib_type, *p_var, meta); } - if (get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup && + if (((is_tesc_shader() && msl_options.multi_patch_workgroup) || + (is_tese_shader() && msl_options.raw_buffer_tese_input)) && storage == StorageClassInput) { - // For tessellation control inputs, add all outputs from the vertex shader to ensure + // For tessellation inputs, add all outputs from the previous stage to ensure // the struct containing them is the correct size and layout. for (auto &input : inputs_by_location) { if (location_inputs_in_use.count(input.first.location) != 0) continue; + if (patch != (input.second.rate == MSL_SHADER_VARIABLE_RATE_PER_PATCH)) + continue; + + // Tessellation levels have their own struct, so there's no need to add them here. + if (input.second.builtin == BuiltInTessLevelOuter || input.second.builtin == BuiltInTessLevelInner) + continue; + // Create a fake variable to put at the location. uint32_t offset = ir.increase_bound_by(4); uint32_t type_id = offset; @@ -4007,7 +4068,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) type.vecsize = output.second.vecsize; set<SPIRType>(type_id, type); - if (get_execution_model() == ExecutionModelTessellationControl) + if (is_tesc_shader()) { type.array.push_back(0); type.array_size_literal.push_back(true); @@ -4017,7 +4078,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) type.pointer = true; type.pointer_depth++; - type.parent_type = get_execution_model() == ExecutionModelTessellationControl ? array_type_id : type_id; + type.parent_type = is_tesc_shader() ? array_type_id : type_id; type.storage = storage; auto &ptr_type = set<SPIRType>(ptr_type_id, type); ptr_type.self = type.parent_type; @@ -4067,7 +4128,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) fix_up_interface_member_indices(storage, ib_type_id); // For patch inputs, add one more member, holding the array of control point data. - if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput && patch && + if (is_tese_shader() && !msl_options.raw_buffer_tese_input && storage == StorageClassInput && patch && stage_in_var_id) { uint32_t pcp_type_id = ir.increase_bound_by(1); @@ -4081,6 +4142,9 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) set_member_name(ib_type.self, mbr_idx, "gl_in"); } + if (storage == StorageClassInput) + set_decoration(ib_var_id, DecorationNonWritable); + return ib_var_id; } @@ -4092,7 +4156,7 @@ uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageCla uint32_t ib_ptr_var_id; uint32_t next_id = ir.increase_bound_by(3); auto &ib_type = expression_type(ib_var_id); - if (get_execution_model() == ExecutionModelTessellationControl) + if (is_tesc_shader() || (is_tese_shader() && msl_options.raw_buffer_tese_input)) { // Tessellation control per-vertex I/O is presented as an array, so we must // do the same with our struct here. @@ -4101,10 +4165,12 @@ uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageCla ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self; ib_ptr_type.pointer = true; ib_ptr_type.pointer_depth++; - ib_ptr_type.storage = - storage == StorageClassInput ? - (msl_options.multi_patch_workgroup ? StorageClassStorageBuffer : StorageClassWorkgroup) : - StorageClassStorageBuffer; + ib_ptr_type.storage = storage == StorageClassInput ? + ((is_tesc_shader() && msl_options.multi_patch_workgroup) || + (is_tese_shader() && msl_options.raw_buffer_tese_input) ? + StorageClassStorageBuffer : + StorageClassWorkgroup) : + StorageClassStorageBuffer; ir.meta[ib_ptr_type_id] = ir.meta[ib_type.self]; // To ensure that get_variable_data_type() doesn't strip off the pointer, // which we need, use another pointer. @@ -4118,6 +4184,8 @@ uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageCla ib_ptr_var_id = next_id; set<SPIRVariable>(ib_ptr_var_id, ib_ptr_ptr_type_id, StorageClassFunction, 0); set_name(ib_ptr_var_id, storage == StorageClassInput ? "gl_in" : "gl_out"); + if (storage == StorageClassInput) + set_decoration(ib_ptr_var_id, DecorationNonWritable); } else { @@ -7330,7 +7398,7 @@ bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id auto &result_type = get<SPIRType>(result_type_id); if (ptr_type.storage != StorageClassInput && ptr_type.storage != StorageClassOutput) return false; - if (ptr_type.storage == StorageClassOutput && get_execution_model() == ExecutionModelTessellationEvaluation) + if (ptr_type.storage == StorageClassOutput && is_tese_shader()) return false; if (has_decoration(ptr, DecorationPatch)) @@ -7652,21 +7720,22 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l bool flatten_composites = false; bool is_block = false; - - if (var) - is_block = has_decoration(get_variable_data_type(*var).self, DecorationBlock); + bool is_arrayed = false; if (var) { + auto &type = get_variable_data_type(*var); + is_block = has_decoration(type.self, DecorationBlock); + is_arrayed = !type.array.empty(); + flatten_composites = variable_storage_requires_stage_io(var->storage); - patch = has_decoration(ops[2], DecorationPatch) || is_patch_block(get_variable_data_type(*var)); + patch = has_decoration(ops[2], DecorationPatch) || is_patch_block(type); // Should match strip_array in add_interface_block. - flat_data = var->storage == StorageClassInput || - (var->storage == StorageClassOutput && get_execution_model() == ExecutionModelTessellationControl); + flat_data = var->storage == StorageClassInput || (var->storage == StorageClassOutput && is_tesc_shader()); // Patch inputs are treated as normal block IO variables, so they don't deal with this path at all. - if (patch && (!is_block || var->storage == StorageClassInput)) + if (patch && (!is_block || is_arrayed || var->storage == StorageClassInput)) flat_data = false; // We might have a chained access chain, where @@ -7947,8 +8016,8 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l // Don't do this if the index is a constant 1, though. We need to drop stores // to that one. auto *m = ir.find_meta(var ? var->self : ID(0)); - if (get_execution_model() == ExecutionModelTessellationControl && var && m && - m->decoration.builtin_type == BuiltInTessLevelInner && get_entry_point().flags.get(ExecutionModeTriangles)) + if (is_tesc_shader() && var && m && m->decoration.builtin_type == BuiltInTessLevelInner && + is_tessellating_triangles()) { auto *c = maybe_get<SPIRConstant>(ops[3]); if (c && c->scalar() == 1) @@ -7965,7 +8034,7 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l bool CompilerMSL::is_out_of_bounds_tessellation_level(uint32_t id_lhs) { - if (!get_entry_point().flags.get(ExecutionModeTriangles)) + if (!is_tessellating_triangles()) return false; // In SPIR-V, TessLevelInner always has two elements and TessLevelOuter always has @@ -7975,7 +8044,7 @@ bool CompilerMSL::is_out_of_bounds_tessellation_level(uint32_t id_lhs) // In Metal, however, only the first element of TessLevelInner and the first three // of TessLevelOuter are accessible. This stems from how in Metal, the tessellation // levels must be stored to a dedicated buffer in a particular format that depends - // on the patch type. Therefore, in Triangles mode, any access to the second + // on the patch type. Therefore, in Triangles mode, any store to the second // inner level or the fourth outer level must be dropped. const auto *e = maybe_get<SPIRExpression>(id_lhs); if (!e || !e->access_chain) @@ -8017,8 +8086,7 @@ bool CompilerMSL::access_chain_needs_stage_io_builtin_translation(uint32_t base) // Avoid overriding it back to just gl_ClipDistance. // This can only happen in scenarios where we cannot flatten/unflatten access chains, so, the only case // where this triggers is evaluation shader inputs. - bool redirect_builtin = get_execution_model() == ExecutionModelTessellationEvaluation ? - var->storage == StorageClassOutput : false; + bool redirect_builtin = is_tese_shader() ? var->storage == StorageClassOutput : false; return redirect_builtin; } @@ -9127,7 +9195,7 @@ void CompilerMSL::emit_texture_op(const Instruction &i, bool sparse) void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem) { - if (get_execution_model() != ExecutionModelGLCompute && get_execution_model() != ExecutionModelTessellationControl) + if (get_execution_model() != ExecutionModelGLCompute && !is_tesc_shader()) return; uint32_t exe_scope = id_exe_scope ? evaluate_constant_u32(id_exe_scope) : uint32_t(ScopeInvocation); @@ -9154,13 +9222,12 @@ void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uin string mem_flags = ""; // For tesc shaders, this also affects objects in the Output storage class. // Since in Metal, these are placed in a device buffer, we have to sync device memory here. - if (get_execution_model() == ExecutionModelTessellationControl || + if (is_tesc_shader() || (mem_sem & (MemorySemanticsUniformMemoryMask | MemorySemanticsCrossWorkgroupMemoryMask))) mem_flags += "mem_flags::mem_device"; // Fix tessellation patch function processing - if (get_execution_model() == ExecutionModelTessellationControl || - (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask))) + if (is_tesc_shader() || (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask))) { if (!mem_flags.empty()) mem_flags += " | "; @@ -9338,7 +9405,7 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t lhs_id, uint32_t r uint32_t CompilerMSL::get_physical_tess_level_array_size(spv::BuiltIn builtin) const { - if (get_execution_mode_bitset().get(ExecutionModeTriangles)) + if (is_tessellating_triangles()) return builtin == BuiltInTessLevelInner ? 1 : 3; else return builtin == BuiltInTessLevelInner ? 2 : 4; @@ -9372,8 +9439,7 @@ bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs) return true; } - if (get_execution_model() == ExecutionModelTessellationControl && - has_decoration(id_lhs, DecorationBuiltIn)) + if (is_tesc_shader() && has_decoration(id_lhs, DecorationBuiltIn)) { auto builtin = BuiltIn(get_decoration(id_lhs, DecorationBuiltIn)); // Need to manually unroll the array store. @@ -11411,8 +11477,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in } // Vertex and tessellation evaluation function outputs - if (((execution.model == ExecutionModelVertex && !msl_options.vertex_for_tessellation) || - execution.model == ExecutionModelTessellationEvaluation) && + if (((execution.model == ExecutionModelVertex && !msl_options.vertex_for_tessellation) || is_tese_shader()) && type.storage == StorageClassOutput) { if (is_builtin) @@ -11466,7 +11531,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in } // Tessellation control function inputs - if (execution.model == ExecutionModelTessellationControl && type.storage == StorageClassInput) + if (is_tesc_shader() && type.storage == StorageClassInput) { if (is_builtin) { @@ -11503,7 +11568,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in } // Tessellation control function outputs - if (execution.model == ExecutionModelTessellationControl && type.storage == StorageClassOutput) + if (is_tesc_shader() && type.storage == StorageClassOutput) { // For this type of shader, we always arrange for it to capture its // output to a buffer. For this reason, qualifiers are irrelevant here. @@ -11514,7 +11579,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in } // Tessellation evaluation function inputs - if (execution.model == ExecutionModelTessellationEvaluation && type.storage == StorageClassInput) + if (is_tese_shader() && type.storage == StorageClassInput) { if (is_builtin) { @@ -11530,6 +11595,10 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in break; } } + + if (msl_options.raw_buffer_tese_input) + return ""; + // The special control point array must not be marked with an attribute. if (get_type(type.member_types[index]).basetype == SPIRType::ControlPointArray) return ""; @@ -11787,7 +11856,7 @@ uint32_t CompilerMSL::get_or_allocate_builtin_input_member_location(spv::BuiltIn // Triangle tess level inputs are shared in one packed float4, // mark both builtins as sharing one location. - if (get_execution_mode_bitset().get(ExecutionModeTriangles) && + if (!msl_options.raw_buffer_tese_input && is_tessellating_triangles() && (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter)) { builtin_to_automatic_input_location[BuiltInTessLevelInner] = loc; @@ -11831,8 +11900,7 @@ uint32_t CompilerMSL::get_or_allocate_builtin_output_member_location(spv::BuiltI // Triangle tess level inputs are shared in one packed float4; // mark both builtins as sharing one location. - if (get_execution_mode_bitset().get(ExecutionModeTriangles) && - (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter)) + if (is_tessellating_triangles() && (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter)) { builtin_to_automatic_output_location[BuiltInTessLevelInner] = loc; builtin_to_automatic_output_location[BuiltInTessLevelOuter] = loc; @@ -11874,10 +11942,9 @@ string CompilerMSL::func_type_decl(SPIRType &type) if (execution.flags.get(ExecutionModeIsolines)) SPIRV_CROSS_THROW("Metal does not support isoline tessellation."); if (msl_options.is_ios()) - entry_type = - join("[[ patch(", execution.flags.get(ExecutionModeTriangles) ? "triangle" : "quad", ") ]] vertex"); + entry_type = join("[[ patch(", is_tessellating_triangles() ? "triangle" : "quad", ") ]] vertex"); else - entry_type = join("[[ patch(", execution.flags.get(ExecutionModeTriangles) ? "triangle" : "quad", ", ", + entry_type = join("[[ patch(", is_tessellating_triangles() ? "triangle" : "quad", ", ", execution.output_vertices, ") ]] vertex"); break; case ExecutionModelFragment: @@ -11901,6 +11968,16 @@ string CompilerMSL::func_type_decl(SPIRType &type) return entry_type + " " + return_type; } +bool CompilerMSL::is_tesc_shader() const +{ + return get_execution_model() == ExecutionModelTessellationControl; +} + +bool CompilerMSL::is_tese_shader() const +{ + return get_execution_model() == ExecutionModelTessellationEvaluation; +} + bool CompilerMSL::uses_explicit_early_fragment_test() { auto &ep_flags = get_entry_point().flags; @@ -11972,9 +12049,20 @@ string CompilerMSL::get_type_address_space(const SPIRType &type, uint32_t id, bo break; case StorageClassInput: - if (get_execution_model() == ExecutionModelTessellationControl && var && - var->basevariable == stage_in_ptr_var_id) - addr_space = msl_options.multi_patch_workgroup ? "constant" : "threadgroup"; + if (is_tesc_shader() && var && var->basevariable == stage_in_ptr_var_id) + addr_space = msl_options.multi_patch_workgroup ? "const device" : "threadgroup"; + // Don't pass tessellation levels in the device AS; we load and convert them + // to float manually. + if (is_tese_shader() && msl_options.raw_buffer_tese_input && var) + { + bool is_stage_in = var->basevariable == stage_in_ptr_var_id; + bool is_patch_stage_in = has_decoration(var->self, DecorationPatch); + bool is_builtin = has_decoration(var->self, DecorationBuiltIn); + BuiltIn builtin = (BuiltIn)get_decoration(var->self, DecorationBuiltIn); + bool is_tess_level = is_builtin && (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner); + if (is_stage_in || (is_patch_stage_in && !is_tess_level)) + addr_space = "const device"; + } if (get_execution_model() == ExecutionModelFragment && var && var->basevariable == stage_in_var_id) addr_space = "thread"; break; @@ -12039,12 +12127,13 @@ string CompilerMSL::entry_point_arg_stage_in() { string decl; - if (get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup) + if ((is_tesc_shader() && msl_options.multi_patch_workgroup) || + (is_tese_shader() && msl_options.raw_buffer_tese_input)) return decl; // Stage-in structure uint32_t stage_in_id; - if (get_execution_model() == ExecutionModelTessellationEvaluation) + if (is_tese_shader()) stage_in_id = patch_stage_in_var_id; else stage_in_id = stage_in_var_id; @@ -12084,7 +12173,7 @@ bool CompilerMSL::is_direct_input_builtin(BuiltIn bi_type) return false; case BuiltInInvocationId: case BuiltInPrimitiveId: - return get_execution_model() != ExecutionModelTessellationControl || !msl_options.multi_patch_workgroup; + return !is_tesc_shader() || !msl_options.multi_patch_workgroup; // Tess. evaluation function in case BuiltInTessLevelInner: case BuiltInTessLevelOuter: @@ -12239,7 +12328,7 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args) " [[buffer(", msl_options.shader_output_buffer_index, ")]]"); } - if (get_execution_model() == ExecutionModelTessellationControl) + if (is_tesc_shader()) { if (!ep_args.empty()) ep_args += ", "; @@ -12281,7 +12370,7 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args) // a buffer to hold the per-patch data, a buffer to hold the per-patch // tessellation levels, and a block of workgroup memory to hold the // input control point data. - if (get_execution_model() == ExecutionModelTessellationControl) + if (is_tesc_shader()) { if (patch_stage_out_var_id) { @@ -12315,20 +12404,22 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args) if (outer_factor_initializer_id && (c = maybe_get<SPIRConstant>(outer_factor_initializer_id))) { auto &entry_func = get<SPIRFunction>(ir.default_entry_point); - entry_func.fixup_hooks_in.push_back([=]() { - uint32_t components = get_execution_mode_bitset().get(ExecutionModeTriangles) ? 3 : 4; - for (uint32_t i = 0; i < components; i++) - { - statement(builtin_to_glsl(BuiltInTessLevelOuter, StorageClassOutput), "[", i, "] = ", - "half(", to_expression(c->subconstants[i]), ");"); - } - }); + entry_func.fixup_hooks_in.push_back( + [=]() + { + uint32_t components = is_tessellating_triangles() ? 3 : 4; + for (uint32_t i = 0; i < components; i++) + { + statement(builtin_to_glsl(BuiltInTessLevelOuter, StorageClassOutput), "[", i, + "] = ", "half(", to_expression(c->subconstants[i]), ");"); + } + }); } if (inner_factor_initializer_id && (c = maybe_get<SPIRConstant>(inner_factor_initializer_id))) { auto &entry_func = get<SPIRFunction>(ir.default_entry_point); - if (get_execution_mode_bitset().get(ExecutionModeTriangles)) + if (is_tessellating_triangles()) { entry_func.fixup_hooks_in.push_back([=]() { statement(builtin_to_glsl(BuiltInTessLevelInner, StorageClassOutput), " = ", "half(", @@ -12364,6 +12455,36 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args) } } } + // Tessellation evaluation shaders get three additional parameters: + // a buffer for the per-patch data, a buffer for the per-patch + // tessellation levels, and a buffer for the control point data. + if (is_tese_shader() && msl_options.raw_buffer_tese_input) + { + if (patch_stage_in_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += + join("const device ", type_to_glsl(get_patch_stage_in_struct_type()), "* ", patch_input_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_patch_input_buffer_index), ")]]"); + } + + if (tess_level_inner_var_id || tess_level_outer_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("const device ", get_tess_factor_struct_name(), "* ", tess_factor_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_tess_factor_buffer_index), ")]]"); + } + + if (stage_in_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("const device ", type_to_glsl(get_stage_in_struct_type()), "* ", input_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_input_buffer_index), ")]]"); + } + } } string CompilerMSL::entry_point_args_argument_buffer(bool append_comma) @@ -12823,7 +12944,7 @@ void CompilerMSL::fix_up_shader_inputs_outputs() break; case BuiltInInvocationId: // This is direct-mapped without multi-patch workgroups. - if (get_execution_model() != ExecutionModelTessellationControl || !msl_options.multi_patch_workgroup) + if (!is_tesc_shader() || !msl_options.multi_patch_workgroup) break; entry_func.fixup_hooks_in.push_back([=]() { @@ -12835,7 +12956,7 @@ void CompilerMSL::fix_up_shader_inputs_outputs() case BuiltInPrimitiveId: // This is natively supported by fragment and tessellation evaluation shaders. // In tessellation control shaders, this is direct-mapped without multi-patch workgroups. - if (get_execution_model() != ExecutionModelTessellationControl || !msl_options.multi_patch_workgroup) + if (!is_tesc_shader() || !msl_options.multi_patch_workgroup) break; entry_func.fixup_hooks_in.push_back([=]() { @@ -12845,7 +12966,7 @@ void CompilerMSL::fix_up_shader_inputs_outputs() }); break; case BuiltInPatchVertices: - if (get_execution_model() == ExecutionModelTessellationEvaluation) + if (is_tese_shader()) entry_func.fixup_hooks_in.push_back([=]() { statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", to_expression(patch_stage_in_var_id), ".gl_in.size();"); @@ -12868,7 +12989,7 @@ void CompilerMSL::fix_up_shader_inputs_outputs() // Emit a fixup to account for the shifted domain. Don't do this for triangles; // MoltenVK will just reverse the winding order instead. - if (msl_options.tess_domain_origin_lower_left && !get_entry_point().flags.get(ExecutionModeTriangles)) + if (msl_options.tess_domain_origin_lower_left && !is_tessellating_triangles()) { string tc = to_expression(var_id); entry_func.fixup_hooks_in.push_back([=]() { statement(tc, ".y = 1.0 - ", tc, ".y;"); }); @@ -13598,7 +13719,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) } // Special case, need to override the array size here if we're using tess level as an argument. - if (get_execution_model() == ExecutionModelTessellationControl && builtin && + if (is_tesc_shader() && builtin && (builtin_type == BuiltInTessLevelInner || builtin_type == BuiltInTessLevelOuter)) { uint32_t array_size = get_physical_tess_level_array_size(builtin_type); @@ -14355,23 +14476,25 @@ bool CompilerMSL::variable_decl_is_remapped_storage(const SPIRVariable &variable if (storage == StorageClassWorkgroup) { - auto model = get_execution_model(); - // Specially masked IO block variable. // Normally, we will never access IO blocks directly here. // The only scenario which that should occur is with a masked IO block. - if (model == ExecutionModelTessellationControl && variable.storage == StorageClassOutput && + if (is_tesc_shader() && variable.storage == StorageClassOutput && has_decoration(get<SPIRType>(variable.basetype).self, DecorationBlock)) { return true; } - return variable.storage == StorageClassOutput && - model == ExecutionModelTessellationControl && - is_stage_output_variable_masked(variable); + return variable.storage == StorageClassOutput && is_tesc_shader() && is_stage_output_variable_masked(variable); } else if (storage == StorageClassStorageBuffer) { + // These builtins are passed directly; we don't want to use remapping + // for them. + auto builtin = (BuiltIn)get_decoration(variable.self, DecorationBuiltIn); + if (is_tese_shader() && is_builtin_variable(variable) && (builtin == BuiltInTessCoord || builtin == BuiltInPrimitiveId)) + return false; + // We won't be able to catch writes to control point outputs here since variable // refers to a function local pointer. // This is fine, as there cannot be concurrent writers to that memory anyways, @@ -15091,7 +15214,7 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) case BuiltInClipDistance: case BuiltInCullDistance: case BuiltInLayer: - if (get_execution_model() == ExecutionModelTessellationControl) + if (is_tesc_shader()) break; if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point) && !is_stage_output_builtin_masked(builtin)) @@ -15123,8 +15246,8 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) break; case BuiltInTessLevelOuter: - if (get_execution_model() == ExecutionModelTessellationControl && - storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point)) + if (is_tesc_shader() && storage != StorageClassInput && current_function && + (current_function->self == ir.default_entry_point)) { return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id), "].edgeTessellationFactor"); @@ -15132,8 +15255,8 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) break; case BuiltInTessLevelInner: - if (get_execution_model() == ExecutionModelTessellationControl && - storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point)) + if (is_tesc_shader() && storage != StorageClassInput && current_function && + (current_function->self == ir.default_entry_point)) { return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id), "].insideTessellationFactor"); @@ -15376,7 +15499,6 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin) // Returns an MSL string type declaration for a SPIR-V builtin string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id) { - const SPIREntryPoint &execution = get_entry_point(); switch (builtin) { // Vertex function in @@ -15420,12 +15542,12 @@ string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id) // Tess. control function out case BuiltInTessLevelInner: - if (execution.model == ExecutionModelTessellationEvaluation) - return !execution.flags.get(ExecutionModeTriangles) ? "float2" : "float"; + if (is_tese_shader()) + return (msl_options.raw_buffer_tese_input || is_tessellating_triangles()) ? "float" : "float2"; return "half"; case BuiltInTessLevelOuter: - if (execution.model == ExecutionModelTessellationEvaluation) - return !execution.flags.get(ExecutionModeTriangles) ? "float4" : "float"; + if (is_tese_shader()) + return (msl_options.raw_buffer_tese_input || is_tessellating_triangles()) ? "float" : "float4"; return "half"; // Tess. evaluation function in @@ -16397,7 +16519,7 @@ void CompilerMSL::cast_from_variable_load(uint32_t source_id, std::string &expr, case BuiltInTessLevelInner: case BuiltInTessLevelOuter: - if (get_execution_model() == ExecutionModelTessellationControl) + if (is_tesc_shader()) { expected_type = SPIRType::Half; expected_width = 16; @@ -16427,7 +16549,7 @@ void CompilerMSL::cast_from_variable_load(uint32_t source_id, std::string &expr, wrap_expr += ", "; } - if (get_execution_mode_bitset().get(ExecutionModeTriangles)) + if (is_tessellating_triangles()) wrap_expr += ", 0.0"; wrap_expr += " })"; |