|
|
@@ -36,7 +36,11 @@ CompilerMSL::CompilerMSL(vector<uint32_t> spirv_, vector<MSLVertexAttr> *p_vtx_a
|
|
|
{
|
|
|
if (p_vtx_attrs)
|
|
|
for (auto &va : *p_vtx_attrs)
|
|
|
+ {
|
|
|
vtx_attrs_by_location[va.location] = &va;
|
|
|
+ if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
|
|
|
+ vtx_attrs_by_builtin[va.builtin] = &va;
|
|
|
+ }
|
|
|
|
|
|
if (p_res_bindings)
|
|
|
for (auto &rb : *p_res_bindings)
|
|
|
@@ -49,7 +53,12 @@ CompilerMSL::CompilerMSL(const uint32_t *ir_, size_t word_count, MSLVertexAttr *
|
|
|
{
|
|
|
if (p_vtx_attrs)
|
|
|
for (size_t i = 0; i < vtx_attrs_count; i++)
|
|
|
- vtx_attrs_by_location[p_vtx_attrs[i].location] = &p_vtx_attrs[i];
|
|
|
+ {
|
|
|
+ auto &va = p_vtx_attrs[i];
|
|
|
+ vtx_attrs_by_location[va.location] = &va;
|
|
|
+ if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
|
|
|
+ vtx_attrs_by_builtin[va.builtin] = &va;
|
|
|
+ }
|
|
|
|
|
|
if (p_res_bindings)
|
|
|
for (size_t i = 0; i < res_bindings_count; i++)
|
|
|
@@ -62,7 +71,12 @@ CompilerMSL::CompilerMSL(const ParsedIR &ir_, MSLVertexAttr *p_vtx_attrs, size_t
|
|
|
{
|
|
|
if (p_vtx_attrs)
|
|
|
for (size_t i = 0; i < vtx_attrs_count; i++)
|
|
|
- vtx_attrs_by_location[p_vtx_attrs[i].location] = &p_vtx_attrs[i];
|
|
|
+ {
|
|
|
+ auto &va = p_vtx_attrs[i];
|
|
|
+ vtx_attrs_by_location[va.location] = &va;
|
|
|
+ if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
|
|
|
+ vtx_attrs_by_builtin[va.builtin] = &va;
|
|
|
+ }
|
|
|
|
|
|
if (p_res_bindings)
|
|
|
for (size_t i = 0; i < res_bindings_count; i++)
|
|
|
@@ -75,7 +89,12 @@ CompilerMSL::CompilerMSL(ParsedIR &&ir_, MSLVertexAttr *p_vtx_attrs, size_t vtx_
|
|
|
{
|
|
|
if (p_vtx_attrs)
|
|
|
for (size_t i = 0; i < vtx_attrs_count; i++)
|
|
|
- vtx_attrs_by_location[p_vtx_attrs[i].location] = &p_vtx_attrs[i];
|
|
|
+ {
|
|
|
+ auto &va = p_vtx_attrs[i];
|
|
|
+ vtx_attrs_by_location[va.location] = &va;
|
|
|
+ if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
|
|
|
+ vtx_attrs_by_builtin[va.builtin] = &va;
|
|
|
+ }
|
|
|
|
|
|
if (p_res_bindings)
|
|
|
for (size_t i = 0; i < res_bindings_count; i++)
|
|
|
@@ -391,6 +410,12 @@ SPIRType &CompilerMSL::get_stage_out_struct_type()
|
|
|
return get_variable_data_type(so_var);
|
|
|
}
|
|
|
|
|
|
+SPIRType &CompilerMSL::get_patch_stage_in_struct_type()
|
|
|
+{
|
|
|
+ auto &si_var = get<SPIRVariable>(patch_stage_in_var_id);
|
|
|
+ return get_variable_data_type(si_var);
|
|
|
+}
|
|
|
+
|
|
|
SPIRType &CompilerMSL::get_patch_stage_out_struct_type()
|
|
|
{
|
|
|
auto &so_var = get<SPIRVariable>(patch_stage_out_var_id);
|
|
|
@@ -558,7 +583,6 @@ string CompilerMSL::compile()
|
|
|
options.version = 450;
|
|
|
backend.null_pointer_literal = "nullptr";
|
|
|
backend.float_literal_suffix = false;
|
|
|
- backend.half_literal_suffix = "h";
|
|
|
backend.uint32_t_literal_suffix = true;
|
|
|
backend.int16_t_literal_suffix = nullptr;
|
|
|
backend.uint16_t_literal_suffix = "u";
|
|
|
@@ -580,6 +604,7 @@ string CompilerMSL::compile()
|
|
|
backend.boolean_mix_support = false;
|
|
|
backend.allow_truncated_access_chain = true;
|
|
|
backend.array_is_value_type = false;
|
|
|
+ backend.comparison_image_samples_scalar = true;
|
|
|
|
|
|
capture_output_to_buffer = msl_options.capture_output_to_buffer;
|
|
|
is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer;
|
|
|
@@ -609,12 +634,13 @@ 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)
|
|
|
+ patch_stage_in_var_id = add_interface_block(StorageClassInput, true);
|
|
|
|
|
|
if (get_execution_model() == ExecutionModelTessellationControl)
|
|
|
- {
|
|
|
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);
|
|
|
- }
|
|
|
|
|
|
// Metal vertex functions that define no output must disable rasterization and return void.
|
|
|
if (!stage_out_var_id)
|
|
|
@@ -662,7 +688,11 @@ string CompilerMSL::compile(vector<MSLVertexAttr> *p_vtx_attrs, vector<MSLResour
|
|
|
{
|
|
|
vtx_attrs_by_location.clear();
|
|
|
for (auto &va : *p_vtx_attrs)
|
|
|
+ {
|
|
|
vtx_attrs_by_location[va.location] = &va;
|
|
|
+ if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
|
|
|
+ vtx_attrs_by_builtin[va.builtin] = &va;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (p_res_bindings)
|
|
|
@@ -869,14 +899,15 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::
|
|
|
auto *p_type = &get<SPIRType>(type_id);
|
|
|
BuiltIn bi_type = BuiltIn(get_decoration(arg_id, DecorationBuiltIn));
|
|
|
|
|
|
- if (get_execution_model() == ExecutionModelTessellationControl &&
|
|
|
- (var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
|
|
|
- !has_decoration(arg_id, DecorationPatch) &&
|
|
|
+ if (((is_tessellation_shader() && var.storage == StorageClassInput) ||
|
|
|
+ (get_execution_model() == ExecutionModelTessellationControl && var.storage == StorageClassOutput)) &&
|
|
|
+ !(has_decoration(arg_id, DecorationPatch) || is_patch_block(*p_type)) &&
|
|
|
(!is_builtin_variable(var) || bi_type == BuiltInPosition || bi_type == BuiltInPointSize ||
|
|
|
bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance ||
|
|
|
p_type->basetype == SPIRType::Struct))
|
|
|
{
|
|
|
// 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.
|
|
|
std::string name;
|
|
|
@@ -1003,8 +1034,8 @@ void CompilerMSL::mark_as_packable(SPIRType &type)
|
|
|
void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, StorageClass storage)
|
|
|
{
|
|
|
MSLVertexAttr *p_va;
|
|
|
- if ((get_execution_model() == ExecutionModelVertex || get_execution_model() == ExecutionModelTessellationControl) &&
|
|
|
- (storage == StorageClassInput) && (p_va = vtx_attrs_by_location[location]))
|
|
|
+ if ((get_execution_model() == ExecutionModelVertex || is_tessellation_shader()) && (storage == StorageClassInput) &&
|
|
|
+ (p_va = vtx_attrs_by_location[location]))
|
|
|
p_va->used_by_shader = true;
|
|
|
}
|
|
|
|
|
|
@@ -1086,7 +1117,7 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
|
|
|
statement(qual_var_name, " = ", remap_swizzle(padded_type, type_components, to_name(var.self)), ";");
|
|
|
});
|
|
|
}
|
|
|
- else
|
|
|
+ else if (!strip_array)
|
|
|
ir.meta[var.self].decoration.qualified_alias = qual_var_name;
|
|
|
|
|
|
if (var.storage == StorageClassOutput && var.initializer != 0)
|
|
|
@@ -1099,8 +1130,7 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
|
|
|
if (get_decoration_bitset(var.self).get(DecorationLocation))
|
|
|
{
|
|
|
uint32_t locn = get_decoration(var.self, DecorationLocation);
|
|
|
- if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex ||
|
|
|
- get_execution_model() == ExecutionModelTessellationControl))
|
|
|
+ if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
|
|
|
{
|
|
|
type_id = ensure_correct_attribute_type(var.basetype, locn);
|
|
|
var.basetype = type_id;
|
|
|
@@ -1112,6 +1142,12 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
|
|
|
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
|
|
|
mark_location_as_used_by_shader(locn, storage);
|
|
|
}
|
|
|
+ else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
|
|
|
+ {
|
|
|
+ uint32_t locn = vtx_attrs_by_builtin[builtin]->location;
|
|
|
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
|
|
|
+ mark_location_as_used_by_shader(locn, storage);
|
|
|
+ }
|
|
|
|
|
|
if (get_decoration_bitset(var.self).get(DecorationComponent))
|
|
|
{
|
|
|
@@ -1168,6 +1204,8 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
|
|
|
elem_cnt = to_array_size_literal(var_type);
|
|
|
}
|
|
|
|
|
|
+ bool is_builtin = is_builtin_variable(var);
|
|
|
+ BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
|
|
|
bool is_flat = has_decoration(var.self, DecorationFlat);
|
|
|
bool is_noperspective = has_decoration(var.self, DecorationNoPerspective);
|
|
|
bool is_centroid = has_decoration(var.self, DecorationCentroid);
|
|
|
@@ -1179,6 +1217,10 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
|
|
|
while (is_array(*usable_type) || is_matrix(*usable_type))
|
|
|
usable_type = &get<SPIRType>(usable_type->parent_type);
|
|
|
|
|
|
+ // If a builtin, force it to have the proper name.
|
|
|
+ if (is_builtin)
|
|
|
+ set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction));
|
|
|
+
|
|
|
entry_func.add_local_variable(var.self);
|
|
|
|
|
|
// We need to declare the variable early and at entry-point scope.
|
|
|
@@ -1217,8 +1259,8 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
|
|
|
if (get_decoration_bitset(var.self).get(DecorationLocation))
|
|
|
{
|
|
|
uint32_t locn = get_decoration(var.self, DecorationLocation) + i;
|
|
|
- if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex ||
|
|
|
- get_execution_model() == ExecutionModelTessellationControl))
|
|
|
+ if (storage == StorageClassInput &&
|
|
|
+ (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
|
|
|
{
|
|
|
var.basetype = ensure_correct_attribute_type(var.basetype, locn);
|
|
|
uint32_t mbr_type_id = ensure_correct_attribute_type(usable_type->self, locn);
|
|
|
@@ -1227,6 +1269,12 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
|
|
|
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
|
|
|
mark_location_as_used_by_shader(locn, storage);
|
|
|
}
|
|
|
+ else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
|
|
|
+ {
|
|
|
+ uint32_t locn = vtx_attrs_by_builtin[builtin]->location + i;
|
|
|
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
|
|
|
+ mark_location_as_used_by_shader(locn, storage);
|
|
|
+ }
|
|
|
|
|
|
if (get_decoration_bitset(var.self).get(DecorationIndex))
|
|
|
{
|
|
|
@@ -1246,29 +1294,33 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
|
|
|
|
|
|
set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self);
|
|
|
|
|
|
- switch (storage)
|
|
|
+ if (!strip_array)
|
|
|
{
|
|
|
- case StorageClassInput:
|
|
|
- entry_func.fixup_hooks_in.push_back(
|
|
|
- [=, &var]() { statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, ";"); });
|
|
|
- break;
|
|
|
+ switch (storage)
|
|
|
+ {
|
|
|
+ case StorageClassInput:
|
|
|
+ entry_func.fixup_hooks_in.push_back(
|
|
|
+ [=, &var]() { statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, ";"); });
|
|
|
+ break;
|
|
|
|
|
|
- case StorageClassOutput:
|
|
|
- entry_func.fixup_hooks_out.push_back([=, &var]() {
|
|
|
- if (padded_output)
|
|
|
- {
|
|
|
- auto &padded_type = this->get<SPIRType>(type_id);
|
|
|
- statement(ib_var_ref, ".", mbr_name, " = ",
|
|
|
- remap_swizzle(padded_type, usable_type->vecsize, join(to_name(var.self), "[", i, "]")),
|
|
|
- ";");
|
|
|
- }
|
|
|
- else
|
|
|
- statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];");
|
|
|
- });
|
|
|
- break;
|
|
|
+ case StorageClassOutput:
|
|
|
+ entry_func.fixup_hooks_out.push_back([=, &var]() {
|
|
|
+ if (padded_output)
|
|
|
+ {
|
|
|
+ auto &padded_type = this->get<SPIRType>(type_id);
|
|
|
+ statement(
|
|
|
+ ib_var_ref, ".", mbr_name, " = ",
|
|
|
+ remap_swizzle(padded_type, usable_type->vecsize, join(to_name(var.self), "[", i, "]")),
|
|
|
+ ";");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];");
|
|
|
+ });
|
|
|
+ break;
|
|
|
|
|
|
- default:
|
|
|
- break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1308,6 +1360,8 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
|
|
|
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
|
|
|
auto &var_type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
|
|
|
|
|
|
+ BuiltIn builtin;
|
|
|
+ bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin);
|
|
|
bool is_flat =
|
|
|
has_member_decoration(var_type.self, mbr_idx, DecorationFlat) || has_decoration(var.self, DecorationFlat);
|
|
|
bool is_noperspective = has_member_decoration(var_type.self, mbr_idx, DecorationNoPerspective) ||
|
|
|
@@ -1364,6 +1418,12 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
|
|
|
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
|
|
|
mark_location_as_used_by_shader(locn, storage);
|
|
|
}
|
|
|
+ else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
|
|
|
+ {
|
|
|
+ uint32_t locn = vtx_attrs_by_builtin[builtin]->location + i;
|
|
|
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
|
|
|
+ mark_location_as_used_by_shader(locn, storage);
|
|
|
+ }
|
|
|
|
|
|
if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent))
|
|
|
SPIRV_CROSS_THROW("DecorationComponent on matrices and arrays make little sense.");
|
|
|
@@ -1382,24 +1442,27 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
|
|
|
set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, mbr_idx);
|
|
|
|
|
|
// Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
|
|
|
- switch (storage)
|
|
|
+ if (!strip_array)
|
|
|
{
|
|
|
- case StorageClassInput:
|
|
|
- entry_func.fixup_hooks_in.push_back([=, &var, &var_type]() {
|
|
|
- statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), "[", i, "] = ", ib_var_ref, ".",
|
|
|
- mbr_name, ";");
|
|
|
- });
|
|
|
- break;
|
|
|
+ switch (storage)
|
|
|
+ {
|
|
|
+ case StorageClassInput:
|
|
|
+ entry_func.fixup_hooks_in.push_back([=, &var, &var_type]() {
|
|
|
+ statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), "[", i, "] = ", ib_var_ref,
|
|
|
+ ".", mbr_name, ";");
|
|
|
+ });
|
|
|
+ break;
|
|
|
|
|
|
- case StorageClassOutput:
|
|
|
- entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() {
|
|
|
- statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), ".", to_member_name(var_type, mbr_idx),
|
|
|
- "[", i, "];");
|
|
|
- });
|
|
|
- break;
|
|
|
+ case StorageClassOutput:
|
|
|
+ entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() {
|
|
|
+ statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), ".",
|
|
|
+ to_member_name(var_type, mbr_idx), "[", i, "];");
|
|
|
+ });
|
|
|
+ break;
|
|
|
|
|
|
- default:
|
|
|
- break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1436,13 +1499,13 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
|
|
|
// Update the original variable reference to include the structure reference
|
|
|
string qual_var_name = ib_var_ref + "." + mbr_name;
|
|
|
|
|
|
- if (is_builtin || get_execution_model() == ExecutionModelTessellationControl)
|
|
|
+ if (is_builtin && !strip_array)
|
|
|
{
|
|
|
// For the builtin gl_PerVertex, we cannot treat it as a block anyways,
|
|
|
// so redirect to qualified name.
|
|
|
set_member_qualified_name(var_type.self, mbr_idx, qual_var_name);
|
|
|
}
|
|
|
- else
|
|
|
+ else if (!strip_array)
|
|
|
{
|
|
|
// Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
|
|
|
switch (storage)
|
|
|
@@ -1468,8 +1531,7 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
|
|
|
if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation))
|
|
|
{
|
|
|
uint32_t locn = get_member_decoration(var_type.self, mbr_idx, DecorationLocation);
|
|
|
- if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex ||
|
|
|
- get_execution_model() == ExecutionModelTessellationControl))
|
|
|
+ if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
|
|
|
{
|
|
|
mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
|
|
|
var_type.member_types[mbr_idx] = mbr_type_id;
|
|
|
@@ -1483,8 +1545,7 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
|
|
|
// The block itself might have a location and in this case, all members of the block
|
|
|
// receive incrementing locations.
|
|
|
uint32_t locn = get_accumulated_member_location(var, mbr_idx, strip_array);
|
|
|
- if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex ||
|
|
|
- get_execution_model() == ExecutionModelTessellationControl))
|
|
|
+ if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
|
|
|
{
|
|
|
mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
|
|
|
var_type.member_types[mbr_idx] = mbr_type_id;
|
|
|
@@ -1493,6 +1554,12 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
|
|
|
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
|
|
|
mark_location_as_used_by_shader(locn, storage);
|
|
|
}
|
|
|
+ else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
|
|
|
+ {
|
|
|
+ uint32_t locn = vtx_attrs_by_builtin[builtin]->location;
|
|
|
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
|
|
|
+ mark_location_as_used_by_shader(locn, storage);
|
|
|
+ }
|
|
|
|
|
|
// Copy the component location, if present.
|
|
|
if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent))
|
|
|
@@ -1566,7 +1633,9 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
|
|
|
|
|
|
if (!is_builtin || has_active_builtin(builtin, storage))
|
|
|
{
|
|
|
- if (!is_builtin && (storage == StorageClassInput || storage == StorageClassOutput) &&
|
|
|
+ if ((!is_builtin ||
|
|
|
+ (storage == StorageClassInput && get_execution_model() != ExecutionModelFragment)) &&
|
|
|
+ (storage == StorageClassInput || storage == StorageClassOutput) &&
|
|
|
(is_matrix(mbr_type) || is_array(mbr_type)))
|
|
|
{
|
|
|
add_composite_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx,
|
|
|
@@ -1590,7 +1659,7 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
|
|
|
if (!is_builtin || has_active_builtin(builtin, storage))
|
|
|
{
|
|
|
// MSL does not allow matrices or arrays in input or output variables, so need to handle it specially.
|
|
|
- if (!is_builtin &&
|
|
|
+ if ((!is_builtin || (storage == StorageClassInput && get_execution_model() != ExecutionModelFragment)) &&
|
|
|
(storage == StorageClassInput || (storage == StorageClassOutput && !capture_output_to_buffer)) &&
|
|
|
(is_matrix(var_type) || is_array(var_type)))
|
|
|
{
|
|
|
@@ -1608,8 +1677,9 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
|
|
|
// for per-vertex variables in a tessellation control shader.
|
|
|
void CompilerMSL::fix_up_interface_member_indices(StorageClass storage, uint32_t ib_type_id)
|
|
|
{
|
|
|
- // Only needed for tessellation control shaders.
|
|
|
- if (get_execution_model() != ExecutionModelTessellationControl)
|
|
|
+ // Only needed for tessellation shaders.
|
|
|
+ if (get_execution_model() != ExecutionModelTessellationControl &&
|
|
|
+ !(get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput))
|
|
|
return;
|
|
|
|
|
|
bool in_array = false;
|
|
|
@@ -1672,24 +1742,28 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
|
|
|
{
|
|
|
// Accumulate the variables that should appear in the interface struct
|
|
|
vector<SPIRVariable *> vars;
|
|
|
- bool incl_builtins = (storage == StorageClassOutput || get_execution_model() == ExecutionModelTessellationControl);
|
|
|
+ bool incl_builtins = (storage == StorageClassOutput || is_tessellation_shader());
|
|
|
|
|
|
ir.for_each_typed_id<SPIRVariable>([&](uint32_t var_id, SPIRVariable &var) {
|
|
|
auto &type = this->get<SPIRType>(var.basetype);
|
|
|
BuiltIn bi_type = BuiltIn(get_decoration(var_id, DecorationBuiltIn));
|
|
|
if (var.storage == storage && interface_variable_exists_in_entry_point(var.self) &&
|
|
|
!is_hidden_variable(var, incl_builtins) && type.pointer &&
|
|
|
- has_decoration(var_id, DecorationPatch) == patch &&
|
|
|
+ (has_decoration(var_id, DecorationPatch) || is_patch_block(type)) == patch &&
|
|
|
(!is_builtin_variable(var) || bi_type == BuiltInPosition || bi_type == BuiltInPointSize ||
|
|
|
bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance || bi_type == BuiltInLayer ||
|
|
|
- bi_type == BuiltInViewportIndex || bi_type == BuiltInFragDepth || bi_type == BuiltInSampleMask))
|
|
|
+ bi_type == BuiltInViewportIndex || bi_type == BuiltInFragDepth || bi_type == BuiltInSampleMask ||
|
|
|
+ (get_execution_model() == ExecutionModelTessellationEvaluation &&
|
|
|
+ (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner))))
|
|
|
{
|
|
|
vars.push_back(&var);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // If no variables qualify, leave
|
|
|
- if (vars.empty())
|
|
|
+ // 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))
|
|
|
return 0;
|
|
|
|
|
|
// Add a new typed variable for this interface structure.
|
|
|
@@ -1711,7 +1785,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
|
|
|
switch (storage)
|
|
|
{
|
|
|
case StorageClassInput:
|
|
|
- ib_var_ref = stage_in_var_name;
|
|
|
+ ib_var_ref = patch ? patch_stage_in_var_name : stage_in_var_name;
|
|
|
if (get_execution_model() == ExecutionModelTessellationControl)
|
|
|
{
|
|
|
// Add a hook to populate the shared workgroup memory containing
|
|
|
@@ -1752,6 +1826,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
|
|
|
switch (get_execution_model())
|
|
|
{
|
|
|
case ExecutionModelVertex:
|
|
|
+ case ExecutionModelTessellationEvaluation:
|
|
|
// Instead of declaring a struct variable to hold the output and then
|
|
|
// copying that to the output buffer, we'll declare the output variable
|
|
|
// as a reference to the final output element in the buffer. Then we can
|
|
|
@@ -1798,7 +1873,10 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
|
|
|
|
|
|
for (auto p_var : vars)
|
|
|
{
|
|
|
- bool strip_array = get_execution_model() == ExecutionModelTessellationControl && !patch;
|
|
|
+ bool strip_array =
|
|
|
+ (get_execution_model() == ExecutionModelTessellationControl ||
|
|
|
+ (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput)) &&
|
|
|
+ !patch;
|
|
|
add_variable_to_interface_block(storage, ib_var_ref, ib_type, *p_var, strip_array);
|
|
|
}
|
|
|
|
|
|
@@ -1811,6 +1889,21 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
|
|
|
if (!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 &&
|
|
|
+ stage_in_var_id)
|
|
|
+ {
|
|
|
+ uint32_t pcp_type_id = ir.increase_bound_by(1);
|
|
|
+ auto &pcp_type = set<SPIRType>(pcp_type_id, ib_type);
|
|
|
+ pcp_type.basetype = SPIRType::ControlPointArray;
|
|
|
+ pcp_type.parent_type = pcp_type.type_alias = get_stage_in_struct_type().self;
|
|
|
+ pcp_type.storage = storage;
|
|
|
+ ir.meta[pcp_type_id] = ir.meta[ib_type.self];
|
|
|
+ uint32_t mbr_idx = uint32_t(ib_type.member_types.size());
|
|
|
+ ib_type.member_types.push_back(pcp_type_id);
|
|
|
+ set_member_name(ib_type.self, mbr_idx, "gl_in");
|
|
|
+ }
|
|
|
+
|
|
|
return ib_var_id;
|
|
|
}
|
|
|
|
|
|
@@ -1819,28 +1912,52 @@ uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageCla
|
|
|
if (!ib_var_id)
|
|
|
return 0;
|
|
|
|
|
|
- // Tessellation control per-vertex I/O is presented as an array, so we must
|
|
|
- // do the same with our struct here.
|
|
|
+ uint32_t ib_ptr_var_id;
|
|
|
uint32_t next_id = ir.increase_bound_by(3);
|
|
|
- uint32_t ib_ptr_type_id = next_id++;
|
|
|
auto &ib_type = expression_type(ib_var_id);
|
|
|
- auto &ib_ptr_type = set<SPIRType>(ib_ptr_type_id, ib_type);
|
|
|
- ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self;
|
|
|
- ib_ptr_type.pointer = true;
|
|
|
- ib_ptr_type.storage = storage == StorageClassInput ? 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.
|
|
|
- uint32_t ib_ptr_ptr_type_id = next_id++;
|
|
|
- auto &ib_ptr_ptr_type = set<SPIRType>(ib_ptr_ptr_type_id, ib_ptr_type);
|
|
|
- ib_ptr_ptr_type.parent_type = ib_ptr_type_id;
|
|
|
- ib_ptr_ptr_type.type_alias = ib_type.self;
|
|
|
- ib_ptr_ptr_type.storage = StorageClassFunction;
|
|
|
- ir.meta[ib_ptr_ptr_type_id] = ir.meta[ib_type.self];
|
|
|
-
|
|
|
- uint32_t 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 ? input_wg_var_name : "gl_out");
|
|
|
+ if (get_execution_model() == ExecutionModelTessellationControl)
|
|
|
+ {
|
|
|
+ // Tessellation control per-vertex I/O is presented as an array, so we must
|
|
|
+ // do the same with our struct here.
|
|
|
+ uint32_t ib_ptr_type_id = next_id++;
|
|
|
+ auto &ib_ptr_type = set<SPIRType>(ib_ptr_type_id, ib_type);
|
|
|
+ ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self;
|
|
|
+ ib_ptr_type.pointer = true;
|
|
|
+ ib_ptr_type.storage = storage == StorageClassInput ? 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.
|
|
|
+ uint32_t ib_ptr_ptr_type_id = next_id++;
|
|
|
+ auto &ib_ptr_ptr_type = set<SPIRType>(ib_ptr_ptr_type_id, ib_ptr_type);
|
|
|
+ ib_ptr_ptr_type.parent_type = ib_ptr_type_id;
|
|
|
+ ib_ptr_ptr_type.type_alias = ib_type.self;
|
|
|
+ ib_ptr_ptr_type.storage = StorageClassFunction;
|
|
|
+ ir.meta[ib_ptr_ptr_type_id] = ir.meta[ib_type.self];
|
|
|
+
|
|
|
+ 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 ? input_wg_var_name : "gl_out");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Tessellation evaluation per-vertex inputs are also presented as arrays.
|
|
|
+ // But, in Metal, this array uses a very special type, 'patch_control_point<T>',
|
|
|
+ // which is a container that can be used to access the control point data.
|
|
|
+ // To represent this, a special 'ControlPointArray' type has been added to the
|
|
|
+ // SPIRV-Cross type system. It should only be generated by and seen in the MSL
|
|
|
+ // backend (i.e. this one).
|
|
|
+ uint32_t pcp_type_id = next_id++;
|
|
|
+ auto &pcp_type = set<SPIRType>(pcp_type_id, ib_type);
|
|
|
+ pcp_type.basetype = SPIRType::ControlPointArray;
|
|
|
+ pcp_type.parent_type = pcp_type.type_alias = ib_type.self;
|
|
|
+ pcp_type.storage = storage;
|
|
|
+ ir.meta[pcp_type_id] = ir.meta[ib_type.self];
|
|
|
+
|
|
|
+ ib_ptr_var_id = next_id;
|
|
|
+ set<SPIRVariable>(ib_ptr_var_id, pcp_type_id, storage, 0);
|
|
|
+ set_name(ib_ptr_var_id, "gl_in");
|
|
|
+ ir.meta[ib_ptr_var_id].decoration.qualified_alias = join(patch_stage_in_var_name, ".gl_in");
|
|
|
+ }
|
|
|
return ib_ptr_var_id;
|
|
|
}
|
|
|
|
|
|
@@ -2723,6 +2840,7 @@ void CompilerMSL::emit_resources()
|
|
|
emit_interface_block(stage_out_var_id);
|
|
|
emit_interface_block(patch_stage_out_var_id);
|
|
|
emit_interface_block(stage_in_var_id);
|
|
|
+ emit_interface_block(patch_stage_in_var_id);
|
|
|
}
|
|
|
|
|
|
// Emit declarations for the specialization Metal function constants
|
|
|
@@ -2822,6 +2940,8 @@ void CompilerMSL::emit_specialization_constants_and_structs()
|
|
|
is_declarable_struct = false;
|
|
|
if (stage_in_var_id && get_stage_in_struct_type().self == type_id)
|
|
|
is_declarable_struct = false;
|
|
|
+ if (patch_stage_in_var_id && get_patch_stage_in_struct_type().self == type_id)
|
|
|
+ is_declarable_struct = false;
|
|
|
|
|
|
// Align and emit declarable structs...but avoid declaring each more than once.
|
|
|
if (is_declarable_struct && declared_structs.count(type_id) == 0)
|
|
|
@@ -2859,13 +2979,15 @@ void CompilerMSL::emit_binary_unord_op(uint32_t result_type, uint32_t result_id,
|
|
|
inherit_expression_dependencies(result_id, op1);
|
|
|
}
|
|
|
|
|
|
-bool CompilerMSL::emit_tessellation_control_access_chain(const uint32_t *ops, uint32_t length)
|
|
|
+bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t length)
|
|
|
{
|
|
|
// If this is a per-vertex output, remap it to the I/O array buffer.
|
|
|
auto *var = maybe_get<SPIRVariable>(ops[2]);
|
|
|
BuiltIn bi_type = BuiltIn(get_decoration(ops[2], DecorationBuiltIn));
|
|
|
- if (var && (var->storage == StorageClassInput || var->storage == StorageClassOutput) &&
|
|
|
- !has_decoration(ops[2], DecorationPatch) &&
|
|
|
+ if (var &&
|
|
|
+ (var->storage == StorageClassInput ||
|
|
|
+ (get_execution_model() == ExecutionModelTessellationControl && var->storage == StorageClassOutput)) &&
|
|
|
+ !(has_decoration(ops[2], DecorationPatch) || is_patch_block(get_variable_data_type(*var))) &&
|
|
|
(!is_builtin_variable(*var) || bi_type == BuiltInPosition || bi_type == BuiltInPointSize ||
|
|
|
bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance ||
|
|
|
get_variable_data_type(*var).basetype == SPIRType::Struct))
|
|
|
@@ -2934,7 +3056,8 @@ bool CompilerMSL::emit_tessellation_control_access_chain(const uint32_t *ops, ui
|
|
|
for (uint32_t k = 0; k < mbr_type.columns; k++, index++)
|
|
|
{
|
|
|
set<SPIRConstant>(const_mbr_id, type_id, index, false);
|
|
|
- auto e = access_chain(ptr, indices.data(), indices.size(), mbr_type, nullptr, true);
|
|
|
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), mbr_type, nullptr,
|
|
|
+ true);
|
|
|
statement(temp_name, ".", to_member_name(*type, j), "[", k, "] = ", e, ";");
|
|
|
}
|
|
|
}
|
|
|
@@ -2943,14 +3066,16 @@ bool CompilerMSL::emit_tessellation_control_access_chain(const uint32_t *ops, ui
|
|
|
for (uint32_t k = 0; k < mbr_type.array[0]; k++, index++)
|
|
|
{
|
|
|
set<SPIRConstant>(const_mbr_id, type_id, index, false);
|
|
|
- auto e = access_chain(ptr, indices.data(), indices.size(), mbr_type, nullptr, true);
|
|
|
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), mbr_type, nullptr,
|
|
|
+ true);
|
|
|
statement(temp_name, ".", to_member_name(*type, j), "[", k, "] = ", e, ";");
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
set<SPIRConstant>(const_mbr_id, type_id, index, false);
|
|
|
- auto e = access_chain(ptr, indices.data(), indices.size(), mbr_type, nullptr, true);
|
|
|
+ auto e =
|
|
|
+ access_chain(ptr, indices.data(), uint32_t(indices.size()), mbr_type, nullptr, true);
|
|
|
statement(temp_name, ".", to_member_name(*type, j), " = ", e, ";");
|
|
|
}
|
|
|
}
|
|
|
@@ -2960,7 +3085,7 @@ bool CompilerMSL::emit_tessellation_control_access_chain(const uint32_t *ops, ui
|
|
|
for (uint32_t j = 0; j < type->columns; j++, index++)
|
|
|
{
|
|
|
set<SPIRConstant>(const_mbr_id, type_id, index, false);
|
|
|
- auto e = access_chain(ptr, indices.data(), indices.size(), *type, nullptr, true);
|
|
|
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), *type, nullptr, true);
|
|
|
statement(temp_name, "[", j, "] = ", e, ";");
|
|
|
}
|
|
|
}
|
|
|
@@ -2970,7 +3095,7 @@ bool CompilerMSL::emit_tessellation_control_access_chain(const uint32_t *ops, ui
|
|
|
for (uint32_t j = 0; j < type->array[0]; j++, index++)
|
|
|
{
|
|
|
set<SPIRConstant>(const_mbr_id, type_id, index, false);
|
|
|
- auto e = access_chain(ptr, indices.data(), indices.size(), *type, nullptr, true);
|
|
|
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), *type, nullptr, true);
|
|
|
statement(temp_name, "[", j, "] = ", e, ";");
|
|
|
}
|
|
|
}
|
|
|
@@ -3006,7 +3131,7 @@ bool CompilerMSL::emit_tessellation_control_access_chain(const uint32_t *ops, ui
|
|
|
|
|
|
// We use the pointer to the base of the input/output array here,
|
|
|
// so this is always a pointer chain.
|
|
|
- auto e = access_chain(ptr, indices.data(), indices.size(), get<SPIRType>(ops[0]), &meta, true);
|
|
|
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), get<SPIRType>(ops[0]), &meta, true);
|
|
|
auto &expr = set<SPIRExpression>(ops[1], move(e), ops[0], should_forward(ops[2]));
|
|
|
expr.loaded_from = var->self;
|
|
|
expr.need_transpose = meta.need_transpose;
|
|
|
@@ -3033,10 +3158,15 @@ bool CompilerMSL::emit_tessellation_control_access_chain(const uint32_t *ops, ui
|
|
|
// drop the last index. It isn't an array in this case, so we can't have an
|
|
|
// array reference here. We need to make this ID a variable instead of an
|
|
|
// expression so we don't try to dereference it as a variable pointer.
|
|
|
+ // 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 : 0);
|
|
|
- if (var && m && m->decoration.builtin_type == BuiltInTessLevelInner &&
|
|
|
- get_entry_point().flags.get(ExecutionModeTriangles))
|
|
|
+ if (get_execution_model() == ExecutionModelTessellationControl && var && m &&
|
|
|
+ m->decoration.builtin_type == BuiltInTessLevelInner && get_entry_point().flags.get(ExecutionModeTriangles))
|
|
|
{
|
|
|
+ auto *c = maybe_get<SPIRConstant>(ops[3]);
|
|
|
+ if (c && c->scalar() == 1)
|
|
|
+ return false;
|
|
|
auto &dest_var = set<SPIRVariable>(ops[1], *var);
|
|
|
dest_var.basetype = ops[0];
|
|
|
ir.meta[ops[1]] = ir.meta[ops[2]];
|
|
|
@@ -3047,6 +3177,33 @@ bool CompilerMSL::emit_tessellation_control_access_chain(const uint32_t *ops, ui
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+bool CompilerMSL::is_out_of_bounds_tessellation_level(uint32_t id_lhs)
|
|
|
+{
|
|
|
+ if (!get_entry_point().flags.get(ExecutionModeTriangles))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // In SPIR-V, TessLevelInner always has two elements and TessLevelOuter always has
|
|
|
+ // four. This is true even if we are tessellating triangles. This allows clients
|
|
|
+ // to use a single tessellation control shader with multiple tessellation evaluation
|
|
|
+ // shaders.
|
|
|
+ // 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
|
|
|
+ // inner level or the fourth outer level must be dropped.
|
|
|
+ const auto *e = maybe_get<SPIRExpression>(id_lhs);
|
|
|
+ if (!e || !e->access_chain)
|
|
|
+ return false;
|
|
|
+ BuiltIn builtin = BuiltIn(get_decoration(e->loaded_from, DecorationBuiltIn));
|
|
|
+ if (builtin != BuiltInTessLevelInner && builtin != BuiltInTessLevelOuter)
|
|
|
+ return false;
|
|
|
+ auto *c = maybe_get<SPIRConstant>(e->implied_read_expressions[1]);
|
|
|
+ if (!c)
|
|
|
+ return false;
|
|
|
+ return (builtin == BuiltInTessLevelInner && c->scalar() == 1) ||
|
|
|
+ (builtin == BuiltInTessLevelOuter && c->scalar() == 3);
|
|
|
+}
|
|
|
+
|
|
|
// Override for MSL-specific syntax instructions
|
|
|
void CompilerMSL::emit_instruction(const Instruction &instruction)
|
|
|
{
|
|
|
@@ -3518,9 +3675,9 @@ void CompilerMSL::emit_instruction(const Instruction &instruction)
|
|
|
case OpInBoundsAccessChain:
|
|
|
case OpAccessChain:
|
|
|
case OpPtrAccessChain:
|
|
|
- if (get_execution_model() == ExecutionModelTessellationControl)
|
|
|
+ if (is_tessellation_shader())
|
|
|
{
|
|
|
- if (!emit_tessellation_control_access_chain(ops, instruction.length))
|
|
|
+ if (!emit_tessellation_access_chain(ops, instruction.length))
|
|
|
CompilerGLSL::emit_instruction(instruction);
|
|
|
}
|
|
|
else
|
|
|
@@ -3528,6 +3685,9 @@ void CompilerMSL::emit_instruction(const Instruction &instruction)
|
|
|
break;
|
|
|
|
|
|
case OpStore:
|
|
|
+ if (is_out_of_bounds_tessellation_level(ops[0]))
|
|
|
+ break;
|
|
|
+
|
|
|
if (maybe_emit_array_assignment(ops[0], ops[1]))
|
|
|
break;
|
|
|
|
|
|
@@ -4592,6 +4752,21 @@ string CompilerMSL::to_swizzle_expression(uint32_t id)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// Checks whether the type is a Block all of whose members have DecorationPatch.
|
|
|
+bool CompilerMSL::is_patch_block(const SPIRType &type)
|
|
|
+{
|
|
|
+ if (!has_decoration(type.self, DecorationBlock))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ for (uint32_t i = 0; i < type.member_types.size(); i++)
|
|
|
+ {
|
|
|
+ if (!has_member_decoration(type.self, i, DecorationPatch))
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
// Checks whether the ID is a row_major matrix that requires conversion before use
|
|
|
bool CompilerMSL::is_non_native_row_major_matrix(uint32_t id)
|
|
|
{
|
|
|
@@ -4600,7 +4775,7 @@ bool CompilerMSL::is_non_native_row_major_matrix(uint32_t id)
|
|
|
return false;
|
|
|
|
|
|
// Non-matrix or column-major matrix types do not need to be converted.
|
|
|
- if (!ir.meta[id].decoration.decoration_flags.get(DecorationRowMajor))
|
|
|
+ if (!has_decoration(id, DecorationRowMajor))
|
|
|
return false;
|
|
|
|
|
|
// Generate a function that will swap matrix elements from row-major to column-major.
|
|
|
@@ -4622,7 +4797,7 @@ bool CompilerMSL::member_is_non_native_row_major_matrix(const SPIRType &type, ui
|
|
|
return false;
|
|
|
|
|
|
// Non-matrix or column-major matrix types do not need to be converted.
|
|
|
- if (!combined_decoration_for_member(type, index).get(DecorationRowMajor))
|
|
|
+ if (!has_member_decoration(type.self, index, DecorationRowMajor))
|
|
|
return false;
|
|
|
|
|
|
// Generate a function that will swap matrix elements from row-major to column-major.
|
|
|
@@ -4685,7 +4860,9 @@ string CompilerMSL::convert_row_major_matrix(string exp_str, const SPIRType &exp
|
|
|
// Called automatically at the end of the entry point function
|
|
|
void CompilerMSL::emit_fixup()
|
|
|
{
|
|
|
- if ((get_entry_point().model == ExecutionModelVertex) && stage_out_var_id && !qual_pos_var_name.empty())
|
|
|
+ if ((get_execution_model() == ExecutionModelVertex ||
|
|
|
+ get_execution_model() == ExecutionModelTessellationEvaluation) &&
|
|
|
+ stage_out_var_id && !qual_pos_var_name.empty() && !capture_output_to_buffer)
|
|
|
{
|
|
|
if (options.vertex.fixup_clipspace)
|
|
|
statement(qual_pos_var_name, ".z = (", qual_pos_var_name, ".z + ", qual_pos_var_name,
|
|
|
@@ -4792,8 +4969,9 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
|
|
|
return string(" [[attribute(") + convert_to_string(locn) + ")]]";
|
|
|
}
|
|
|
|
|
|
- // Vertex function outputs
|
|
|
- if (execution.model == ExecutionModelVertex && type.storage == StorageClassOutput)
|
|
|
+ // Vertex and tessellation evaluation function outputs
|
|
|
+ if ((execution.model == ExecutionModelVertex || execution.model == ExecutionModelTessellationEvaluation) &&
|
|
|
+ type.storage == StorageClassOutput)
|
|
|
{
|
|
|
if (is_builtin)
|
|
|
{
|
|
|
@@ -4860,6 +5038,33 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
+ // Tessellation evaluation function inputs
|
|
|
+ if (execution.model == ExecutionModelTessellationEvaluation && type.storage == StorageClassInput)
|
|
|
+ {
|
|
|
+ if (is_builtin)
|
|
|
+ {
|
|
|
+ switch (builtin)
|
|
|
+ {
|
|
|
+ case BuiltInPrimitiveId:
|
|
|
+ case BuiltInTessCoord:
|
|
|
+ return string(" [[") + builtin_qualifier(builtin) + "]]";
|
|
|
+ case BuiltInPatchVertices:
|
|
|
+ return "";
|
|
|
+ // Others come from stage input.
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // The special control point array must not be marked with an attribute.
|
|
|
+ if (get_type(type.member_types[index]).basetype == SPIRType::ControlPointArray)
|
|
|
+ return "";
|
|
|
+ uint32_t locn = get_ordered_member_location(type.self, index);
|
|
|
+ if (locn != k_unknown_location)
|
|
|
+ return string(" [[attribute(") + convert_to_string(locn) + ")]]";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Tessellation evaluation function outputs were handled above.
|
|
|
+
|
|
|
// Fragment function inputs
|
|
|
if (execution.model == ExecutionModelFragment && type.storage == StorageClassInput)
|
|
|
{
|
|
|
@@ -5028,6 +5233,18 @@ string CompilerMSL::func_type_decl(SPIRType &type)
|
|
|
case ExecutionModelVertex:
|
|
|
entry_type = "vertex";
|
|
|
break;
|
|
|
+ case ExecutionModelTessellationEvaluation:
|
|
|
+ if (!msl_options.supports_msl_version(1, 2))
|
|
|
+ SPIRV_CROSS_THROW("Tessellation requires Metal 1.2.");
|
|
|
+ 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");
|
|
|
+ else
|
|
|
+ entry_type = join("[[ patch(", execution.flags.get(ExecutionModeTriangles) ? "triangle" : "quad", ", ",
|
|
|
+ execution.output_vertices, ") ]] vertex");
|
|
|
+ break;
|
|
|
case ExecutionModelFragment:
|
|
|
entry_type =
|
|
|
execution.flags.get(ExecutionModeEarlyFragmentTests) ? "[[ early_fragment_tests ]] fragment" : "fragment";
|
|
|
@@ -5152,9 +5369,15 @@ string CompilerMSL::entry_point_args(bool append_comma)
|
|
|
string ep_args;
|
|
|
|
|
|
// Stage-in structure
|
|
|
- if (stage_in_var_id)
|
|
|
+ uint32_t stage_in_id;
|
|
|
+ if (get_execution_model() == ExecutionModelTessellationEvaluation)
|
|
|
+ stage_in_id = patch_stage_in_var_id;
|
|
|
+ else
|
|
|
+ stage_in_id = stage_in_var_id;
|
|
|
+
|
|
|
+ if (stage_in_id)
|
|
|
{
|
|
|
- auto &var = get<SPIRVariable>(stage_in_var_id);
|
|
|
+ auto &var = get<SPIRVariable>(stage_in_id);
|
|
|
auto &type = get_variable_data_type(var);
|
|
|
|
|
|
if (!ep_args.empty())
|
|
|
@@ -5284,7 +5507,8 @@ string CompilerMSL::entry_point_args(bool append_comma)
|
|
|
// Don't emit SamplePosition as a separate parameter. In the entry
|
|
|
// point, we get that by calling get_sample_position() on the sample ID.
|
|
|
if (var.storage == StorageClassInput && is_builtin_variable(var) &&
|
|
|
- get_variable_data_type(var).basetype != SPIRType::Struct)
|
|
|
+ get_variable_data_type(var).basetype != SPIRType::Struct &&
|
|
|
+ get_variable_data_type(var).basetype != SPIRType::ControlPointArray)
|
|
|
{
|
|
|
if (bi_type != BuiltInSamplePosition && bi_type != BuiltInHelperInvocation &&
|
|
|
bi_type != BuiltInPatchVertices && bi_type != BuiltInTessLevelInner &&
|
|
|
@@ -5413,9 +5637,24 @@ void CompilerMSL::fix_up_shader_inputs_outputs()
|
|
|
});
|
|
|
break;
|
|
|
case BuiltInPatchVertices:
|
|
|
- entry_func.fixup_hooks_in.push_back([=]() {
|
|
|
- statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = spvIndirectParams[0];");
|
|
|
- });
|
|
|
+ if (get_execution_model() == ExecutionModelTessellationEvaluation)
|
|
|
+ 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();");
|
|
|
+ });
|
|
|
+ else
|
|
|
+ entry_func.fixup_hooks_in.push_back([=]() {
|
|
|
+ statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = spvIndirectParams[0];");
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case BuiltInTessCoord:
|
|
|
+ // 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))
|
|
|
+ {
|
|
|
+ string tc = to_expression(var_id);
|
|
|
+ entry_func.fixup_hooks_in.push_back([=]() { statement(tc, ".y = 1.0 - ", tc, ".y;"); });
|
|
|
+ }
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
|
@@ -5982,6 +6221,9 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
|
|
|
case SPIRType::AtomicCounter:
|
|
|
return "atomic_uint";
|
|
|
|
|
|
+ case SPIRType::ControlPointArray:
|
|
|
+ return join("patch_control_point<", type_to_glsl(get<SPIRType>(type.parent_type), id), ">");
|
|
|
+
|
|
|
// Scalars
|
|
|
case SPIRType::Boolean:
|
|
|
type_name = "bool";
|
|
|
@@ -6263,7 +6505,7 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
|
|
|
|
|
|
// When used in the entry function, output builtins are qualified with output struct name.
|
|
|
// Test storage class as NOT Input, as output builtins might be part of generic type.
|
|
|
- // Also don't do this for tessellation shaders.
|
|
|
+ // Also don't do this for tessellation control shaders.
|
|
|
case BuiltInViewportIndex:
|
|
|
if (!msl_options.supports_msl_version(2, 0))
|
|
|
SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0.");
|
|
|
@@ -6283,12 +6525,16 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
|
|
|
break;
|
|
|
|
|
|
case BuiltInTessLevelOuter:
|
|
|
+ if (get_execution_model() == ExecutionModelTessellationEvaluation)
|
|
|
+ break;
|
|
|
if (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");
|
|
|
break;
|
|
|
|
|
|
case BuiltInTessLevelInner:
|
|
|
+ if (get_execution_model() == ExecutionModelTessellationEvaluation)
|
|
|
+ break;
|
|
|
if (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");
|
|
|
@@ -6345,7 +6591,15 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin)
|
|
|
// Shouldn't be reached.
|
|
|
SPIRV_CROSS_THROW("PatchVertices is derived from the auxiliary buffer in MSL.");
|
|
|
case BuiltInPrimitiveId:
|
|
|
- return "threadgroup_position_in_grid";
|
|
|
+ switch (execution.model)
|
|
|
+ {
|
|
|
+ case ExecutionModelTessellationControl:
|
|
|
+ return "threadgroup_position_in_grid";
|
|
|
+ case ExecutionModelTessellationEvaluation:
|
|
|
+ return "patch_id";
|
|
|
+ default:
|
|
|
+ SPIRV_CROSS_THROW("PrimitiveId is not supported in this execution model.");
|
|
|
+ }
|
|
|
|
|
|
// Tess. control function out
|
|
|
case BuiltInTessLevelOuter:
|
|
|
@@ -6353,6 +6607,10 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin)
|
|
|
// Shouldn't be reached.
|
|
|
SPIRV_CROSS_THROW("Tessellation levels are handled specially in MSL.");
|
|
|
|
|
|
+ // Tess. evaluation function in
|
|
|
+ case BuiltInTessCoord:
|
|
|
+ return "position_in_patch";
|
|
|
+
|
|
|
// Fragment function in
|
|
|
case BuiltInFrontFacing:
|
|
|
return "front_facing";
|
|
|
@@ -6447,6 +6705,10 @@ string CompilerMSL::builtin_type_decl(BuiltIn builtin)
|
|
|
case BuiltInTessLevelOuter:
|
|
|
return "half";
|
|
|
|
|
|
+ // Tess. evaluation function in
|
|
|
+ case BuiltInTessCoord:
|
|
|
+ return get_entry_point().flags.get(ExecutionModeTriangles) ? "float3" : "float2";
|
|
|
+
|
|
|
// Fragment function in
|
|
|
case BuiltInFrontFacing:
|
|
|
return "bool";
|
|
|
@@ -6973,6 +7235,13 @@ void CompilerMSL::bitcast_from_builtin_load(uint32_t source_id, std::string &exp
|
|
|
|
|
|
if (expected_type != expr_type.basetype)
|
|
|
expr = bitcast_expression(expr_type, expected_type, expr);
|
|
|
+
|
|
|
+ if (builtin == BuiltInTessCoord && get_entry_point().flags.get(ExecutionModeQuads) && expr_type.vecsize == 3)
|
|
|
+ {
|
|
|
+ // In SPIR-V, this is always a vec3, even for quads. In Metal, though, it's a float2 for quads.
|
|
|
+ // The code is expecting a float3, so we need to widen this.
|
|
|
+ expr = join("float3(", expr, ", 0)");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void CompilerMSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type)
|