Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/KhronosGroup/SPIRV-Cross.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spirv_msl.cpp')
-rw-r--r--spirv_msl.cpp460
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 += " })";