Merge pull request #809 from KhronosGroup/fix-797

MSL: Add support for emitting structs and arrays within I/O blocks.
This commit is contained in:
Hans-Kristian Arntzen 2019-01-09 10:35:11 +01:00 committed by GitHub
commit ff7c4838f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 770 additions and 307 deletions

View File

@ -0,0 +1,58 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Vert
{
float3x3 wMatrix;
float4 wTmp;
float arr[4];
};
struct main0_out
{
float4 FragColor [[color(0)]];
};
struct main0_in
{
float3 vMatrix_0 [[user(locn0)]];
float3 vMatrix_1 [[user(locn1)]];
float3 vMatrix_2 [[user(locn2)]];
float3 Vert_wMatrix_0 [[user(locn4)]];
float3 Vert_wMatrix_1 [[user(locn5)]];
float3 Vert_wMatrix_2 [[user(locn6)]];
float4 Vert_wTmp [[user(locn7)]];
float Vert_arr_0 [[user(locn8)]];
float Vert_arr_1 [[user(locn9)]];
float Vert_arr_2 [[user(locn10)]];
float Vert_arr_3 [[user(locn11)]];
};
fragment main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
Vert _17 = {};
float3x3 vMatrix = {};
_17.wMatrix[0] = in.Vert_wMatrix_0;
_17.wMatrix[1] = in.Vert_wMatrix_1;
_17.wMatrix[2] = in.Vert_wMatrix_2;
_17.wTmp = in.Vert_wTmp;
_17.arr[0] = in.Vert_arr_0;
_17.arr[1] = in.Vert_arr_1;
_17.arr[2] = in.Vert_arr_2;
_17.arr[3] = in.Vert_arr_3;
vMatrix[0] = in.vMatrix_0;
vMatrix[1] = in.vMatrix_1;
vMatrix[2] = in.vMatrix_2;
out.FragColor = (_17.wMatrix[0].xxyy + _17.wTmp) + vMatrix[1].yyzz;
for (int _56 = 0; _56 < 4; )
{
out.FragColor += float4(_17.arr[_56]);
_56++;
continue;
}
return out;
}

View File

@ -0,0 +1,64 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Vert
{
float arr[3];
float3x3 wMatrix;
float4 wTmp;
};
struct main0_out
{
float3 vMatrix_0 [[user(locn0)]];
float3 vMatrix_1 [[user(locn1)]];
float3 vMatrix_2 [[user(locn2)]];
float Vert_arr_0 [[user(locn4)]];
float Vert_arr_1 [[user(locn5)]];
float Vert_arr_2 [[user(locn6)]];
float3 Vert_wMatrix_0 [[user(locn7)]];
float3 Vert_wMatrix_1 [[user(locn8)]];
float3 Vert_wMatrix_2 [[user(locn9)]];
float4 Vert_wTmp [[user(locn10)]];
float4 gl_Position [[position]];
};
struct main0_in
{
float3 Matrix_0 [[attribute(0)]];
float3 Matrix_1 [[attribute(1)]];
float3 Matrix_2 [[attribute(2)]];
float4 Pos [[attribute(4)]];
};
vertex main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
float3x3 vMatrix = {};
Vert _20 = {};
float3x3 Matrix = {};
Matrix[0] = in.Matrix_0;
Matrix[1] = in.Matrix_1;
Matrix[2] = in.Matrix_2;
vMatrix = Matrix;
_20.wMatrix = Matrix;
_20.arr[0] = 1.0;
_20.arr[1] = 2.0;
_20.arr[2] = 3.0;
_20.wTmp = in.Pos;
out.gl_Position = in.Pos;
out.vMatrix_0 = vMatrix[0];
out.vMatrix_1 = vMatrix[1];
out.vMatrix_2 = vMatrix[2];
out.Vert_arr_0 = _20.arr[0];
out.Vert_arr_1 = _20.arr[1];
out.Vert_arr_2 = _20.arr[2];
out.Vert_wMatrix_0 = _20.wMatrix[0];
out.Vert_wMatrix_1 = _20.wMatrix[1];
out.Vert_wMatrix_2 = _20.wMatrix[2];
out.Vert_wTmp = _20.wTmp;
return out;
}

View File

@ -0,0 +1,56 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Vert
{
float3x3 wMatrix;
float4 wTmp;
float arr[4];
};
struct main0_out
{
float4 FragColor [[color(0)]];
};
struct main0_in
{
float3 vMatrix_0 [[user(locn0)]];
float3 vMatrix_1 [[user(locn1)]];
float3 vMatrix_2 [[user(locn2)]];
float3 Vert_wMatrix_0 [[user(locn4)]];
float3 Vert_wMatrix_1 [[user(locn5)]];
float3 Vert_wMatrix_2 [[user(locn6)]];
float4 Vert_wTmp [[user(locn7)]];
float Vert_arr_0 [[user(locn8)]];
float Vert_arr_1 [[user(locn9)]];
float Vert_arr_2 [[user(locn10)]];
float Vert_arr_3 [[user(locn11)]];
};
fragment main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
Vert _17 = {};
float3x3 vMatrix = {};
_17.wMatrix[0] = in.Vert_wMatrix_0;
_17.wMatrix[1] = in.Vert_wMatrix_1;
_17.wMatrix[2] = in.Vert_wMatrix_2;
_17.wTmp = in.Vert_wTmp;
_17.arr[0] = in.Vert_arr_0;
_17.arr[1] = in.Vert_arr_1;
_17.arr[2] = in.Vert_arr_2;
_17.arr[3] = in.Vert_arr_3;
vMatrix[0] = in.vMatrix_0;
vMatrix[1] = in.vMatrix_1;
vMatrix[2] = in.vMatrix_2;
out.FragColor = (_17.wMatrix[0].xxyy + _17.wTmp) + vMatrix[1].yyzz;
for (int i = 0; i < 4; i++)
{
out.FragColor += float4(_17.arr[i]);
}
return out;
}

View File

@ -0,0 +1,64 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Vert
{
float arr[3];
float3x3 wMatrix;
float4 wTmp;
};
struct main0_out
{
float3 vMatrix_0 [[user(locn0)]];
float3 vMatrix_1 [[user(locn1)]];
float3 vMatrix_2 [[user(locn2)]];
float Vert_arr_0 [[user(locn4)]];
float Vert_arr_1 [[user(locn5)]];
float Vert_arr_2 [[user(locn6)]];
float3 Vert_wMatrix_0 [[user(locn7)]];
float3 Vert_wMatrix_1 [[user(locn8)]];
float3 Vert_wMatrix_2 [[user(locn9)]];
float4 Vert_wTmp [[user(locn10)]];
float4 gl_Position [[position]];
};
struct main0_in
{
float3 Matrix_0 [[attribute(0)]];
float3 Matrix_1 [[attribute(1)]];
float3 Matrix_2 [[attribute(2)]];
float4 Pos [[attribute(4)]];
};
vertex main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
float3x3 vMatrix = {};
Vert _20 = {};
float3x3 Matrix = {};
Matrix[0] = in.Matrix_0;
Matrix[1] = in.Matrix_1;
Matrix[2] = in.Matrix_2;
vMatrix = Matrix;
_20.wMatrix = Matrix;
_20.arr[0] = 1.0;
_20.arr[1] = 2.0;
_20.arr[2] = 3.0;
_20.wTmp = in.Pos;
out.gl_Position = in.Pos;
out.vMatrix_0 = vMatrix[0];
out.vMatrix_1 = vMatrix[1];
out.vMatrix_2 = vMatrix[2];
out.Vert_arr_0 = _20.arr[0];
out.Vert_arr_1 = _20.arr[1];
out.Vert_arr_2 = _20.arr[2];
out.Vert_wMatrix_0 = _20.wMatrix[0];
out.Vert_wMatrix_1 = _20.wMatrix[1];
out.Vert_wMatrix_2 = _20.wMatrix[2];
out.Vert_wTmp = _20.wTmp;
return out;
}

View File

@ -0,0 +1,17 @@
#version 450
layout(location = 0) in mat3 vMatrix;
layout(location = 4) in Vert
{
mat3 wMatrix;
vec4 wTmp;
float arr[4];
};
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = wMatrix[0].xxyy + wTmp + vMatrix[1].yyzz;
for (int i = 0; i < 4; i++)
FragColor += arr[i];
}

View File

@ -0,0 +1,22 @@
#version 450
layout(location = 0) out mat3 vMatrix;
layout(location = 0) in mat3 Matrix;
layout(location = 4) in vec4 Pos;
layout(location = 4) out Vert
{
float arr[3];
mat3 wMatrix;
vec4 wTmp;
};
void main()
{
vMatrix = Matrix;
wMatrix = Matrix;
arr[0] = 1.0;
arr[1] = 2.0;
arr[2] = 3.0;
wTmp = Pos;
gl_Position = Pos;
}

View File

@ -1347,7 +1347,7 @@ struct Meta
std::string qualified_alias;
std::string hlsl_semantic;
Bitset decoration_flags;
spv::BuiltIn builtin_type;
spv::BuiltIn builtin_type = spv::BuiltInMax;
uint32_t location = 0;
uint32_t component = 0;
uint32_t set = 0;

View File

@ -697,7 +697,7 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::
uint32_t mbr_idx = 0;
for (auto &mbr_type_id : p_type->member_types)
{
BuiltIn builtin;
BuiltIn builtin = BuiltInMax;
bool is_builtin = is_member_builtin(*p_type, mbr_idx, &builtin);
if (is_builtin && has_active_builtin(builtin, var.storage))
{
@ -797,6 +797,475 @@ void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, StorageClas
p_va->used_by_shader = true;
}
void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var)
{
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);
bool is_sample = has_decoration(var.self, DecorationSample);
// Add a reference to the variable type to the interface struct.
uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
uint32_t type_id = ensure_correct_builtin_type(var.basetype, builtin);
var.basetype = type_id;
ib_type.member_types.push_back(get_pointee_type_id(type_id));
// Give the member a name
string mbr_name = ensure_valid_name(to_expression(var.self), "m");
set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
// Update the original variable reference to include the structure reference
string qual_var_name = ib_var_ref + "." + mbr_name;
ir.meta[var.self].decoration.qualified_alias = qual_var_name;
// Copy the variable location from the original variable to the member
if (get_decoration_bitset(var.self).get(DecorationLocation))
{
uint32_t locn = get_decoration(var.self, DecorationLocation);
if (storage == StorageClassInput && get_entry_point().model == ExecutionModelVertex)
{
type_id = ensure_correct_attribute_type(type_id, locn);
var.basetype = type_id;
ib_type.member_types[ib_mbr_idx] = get_pointee_type_id(type_id);
}
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))
{
uint32_t comp = get_decoration(var.self, DecorationComponent);
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp);
}
if (get_decoration_bitset(var.self).get(DecorationIndex))
{
uint32_t index = get_decoration(var.self, DecorationIndex);
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, index);
}
// Mark the member as builtin if needed
if (is_builtin)
{
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
if (builtin == BuiltInPosition)
qual_pos_var_name = qual_var_name;
}
// Copy interpolation decorations if needed
if (is_flat)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat);
if (is_noperspective)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective);
if (is_centroid)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid);
if (is_sample)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample);
}
void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var)
{
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
auto &var_type = get_variable_data_type(var);
uint32_t elem_cnt = 0;
if (is_matrix(var_type))
{
if (is_array(var_type))
SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables.");
elem_cnt = var_type.columns;
}
else if (is_array(var_type))
{
if (var_type.array.size() != 1)
SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables.");
elem_cnt = to_array_size_literal(var_type);
}
bool is_flat = has_decoration(var.self, DecorationFlat);
bool is_noperspective = has_decoration(var.self, DecorationNoPerspective);
bool is_centroid = has_decoration(var.self, DecorationCentroid);
bool is_sample = has_decoration(var.self, DecorationSample);
auto *usable_type = &var_type;
if (usable_type->pointer)
usable_type = &get<SPIRType>(usable_type->parent_type);
while (is_array(*usable_type) || is_matrix(*usable_type))
usable_type = &get<SPIRType>(usable_type->parent_type);
entry_func.add_local_variable(var.self);
// We need to declare the variable early and at entry-point scope.
vars_needing_early_declaration.push_back(var.self);
for (uint32_t i = 0; i < elem_cnt; i++)
{
// Add a reference to the variable type to the interface struct.
uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
ib_type.member_types.push_back(usable_type->self);
// Give the member a name
string mbr_name = ensure_valid_name(join(to_expression(var.self), "_", i), "m");
set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
// There is no qualified alias since we need to flatten the internal array on return.
if (get_decoration_bitset(var.self).get(DecorationLocation))
{
uint32_t locn = get_decoration(var.self, DecorationLocation) + i;
if (storage == StorageClassInput && get_entry_point().model == ExecutionModelVertex)
{
var.basetype = ensure_correct_attribute_type(var.basetype, locn);
uint32_t mbr_type_id = ensure_correct_attribute_type(usable_type->self, locn);
ib_type.member_types[ib_mbr_idx] = mbr_type_id;
}
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
if (get_decoration_bitset(var.self).get(DecorationIndex))
{
uint32_t index = get_decoration(var.self, DecorationIndex);
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, index);
}
// Copy interpolation decorations if needed
if (is_flat)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat);
if (is_noperspective)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective);
if (is_centroid)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid);
if (is_sample)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample);
switch (storage)
{
case StorageClassInput:
entry_func.fixup_hooks_in.push_back(
[=]() { statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, ";"); });
break;
case StorageClassOutput:
entry_func.fixup_hooks_out.push_back(
[=]() { statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];"); });
break;
default:
break;
}
}
}
uint32_t CompilerMSL::get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx)
{
auto &type = get<SPIRType>(var.basetype);
uint32_t location = get_decoration(var.self, DecorationLocation);
for (uint32_t i = 0; i < mbr_idx; i++)
{
auto &mbr_type = get<SPIRType>(type.member_types[i]);
// Start counting from any place we have a new location decoration.
if (has_member_decoration(type.self, mbr_idx, DecorationLocation))
location = get_member_decoration(type.self, mbr_idx, DecorationLocation);
uint32_t location_count = 1;
if (mbr_type.columns > 1)
location_count = mbr_type.columns;
if (!mbr_type.array.empty())
for (uint32_t j = 0; j < uint32_t(mbr_type.array.size()); j++)
location_count *= to_array_size_literal(mbr_type, j);
location += location_count;
}
return location;
}
void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var,
uint32_t mbr_idx)
{
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
auto &var_type = get_variable_data_type(var);
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) ||
has_decoration(var.self, DecorationNoPerspective);
bool is_centroid = has_member_decoration(var_type.self, mbr_idx, DecorationCentroid) ||
has_decoration(var.self, DecorationCentroid);
bool is_sample =
has_member_decoration(var_type.self, mbr_idx, DecorationSample) || has_decoration(var.self, DecorationSample);
uint32_t mbr_type_id = var_type.member_types[mbr_idx];
auto &mbr_type = get<SPIRType>(mbr_type_id);
uint32_t elem_cnt = 0;
if (is_matrix(mbr_type))
{
if (is_array(mbr_type))
SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables.");
elem_cnt = mbr_type.columns;
}
else if (is_array(mbr_type))
{
if (mbr_type.array.size() != 1)
SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables.");
elem_cnt = to_array_size_literal(mbr_type);
}
auto *usable_type = &mbr_type;
if (usable_type->pointer)
usable_type = &get<SPIRType>(usable_type->parent_type);
while (is_array(*usable_type) || is_matrix(*usable_type))
usable_type = &get<SPIRType>(usable_type->parent_type);
for (uint32_t i = 0; i < elem_cnt; i++)
{
// Add a reference to the variable type to the interface struct.
uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
ib_type.member_types.push_back(usable_type->self);
// Give the member a name
string mbr_name = ensure_valid_name(join(to_qualified_member_name(var_type, mbr_idx), "_", i), "m");
set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation))
{
uint32_t locn = get_member_decoration(var_type.self, mbr_idx, DecorationLocation) + i;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
else if (has_decoration(var.self, DecorationLocation))
{
uint32_t locn = get_accumulated_member_location(var, mbr_idx) + 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.");
// Copy interpolation decorations if needed
if (is_flat)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat);
if (is_noperspective)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective);
if (is_centroid)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid);
if (is_sample)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample);
// Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
switch (storage)
{
case StorageClassInput:
entry_func.fixup_hooks_in.push_back([=]() {
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([=]() {
statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), ".", to_member_name(var_type, mbr_idx),
"[", i, "];");
});
break;
default:
break;
}
}
}
void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var, uint32_t mbr_idx)
{
auto &var_type = get_variable_data_type(var);
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
BuiltIn builtin = BuiltInMax;
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) ||
has_decoration(var.self, DecorationNoPerspective);
bool is_centroid = has_member_decoration(var_type.self, mbr_idx, DecorationCentroid) ||
has_decoration(var.self, DecorationCentroid);
bool is_sample =
has_member_decoration(var_type.self, mbr_idx, DecorationSample) || has_decoration(var.self, DecorationSample);
// Add a reference to the member to the interface struct.
uint32_t mbr_type_id = var_type.member_types[mbr_idx];
uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
mbr_type_id = ensure_correct_builtin_type(mbr_type_id, builtin);
var_type.member_types[mbr_idx] = mbr_type_id;
ib_type.member_types.push_back(mbr_type_id);
// Give the member a name
string mbr_name = ensure_valid_name(to_qualified_member_name(var_type, mbr_idx), "m");
set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
// Update the original variable reference to include the structure reference
string qual_var_name = ib_var_ref + "." + mbr_name;
if (is_builtin)
{
// 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
{
// Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
switch (storage)
{
case StorageClassInput:
entry_func.fixup_hooks_in.push_back([=]() {
statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), " = ", qual_var_name, ";");
});
break;
case StorageClassOutput:
entry_func.fixup_hooks_out.push_back([=]() {
statement(qual_var_name, " = ", to_name(var.self), ".", to_member_name(var_type, mbr_idx), ";");
});
break;
default:
break;
}
}
// Copy the variable location from the original variable to the member
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_entry_point().model == ExecutionModelVertex)
{
mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
var_type.member_types[mbr_idx] = mbr_type_id;
ib_type.member_types[ib_mbr_idx] = mbr_type_id;
}
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
else if (has_decoration(var.self, DecorationLocation))
{
// 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);
if (storage == StorageClassInput && get_entry_point().model == ExecutionModelVertex)
{
mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
var_type.member_types[mbr_idx] = mbr_type_id;
ib_type.member_types[ib_mbr_idx] = mbr_type_id;
}
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
// Copy the component location, if present.
if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent))
{
uint32_t comp = get_member_decoration(var_type.self, mbr_idx, DecorationComponent);
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp);
}
// Mark the member as builtin if needed
if (is_builtin)
{
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
if (builtin == BuiltInPosition)
qual_pos_var_name = qual_var_name;
}
// Copy interpolation decorations if needed
if (is_flat)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat);
if (is_noperspective)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective);
if (is_centroid)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid);
if (is_sample)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample);
}
void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, SPIRType &ib_type,
SPIRVariable &var)
{
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
auto &var_type = get_variable_data_type(var);
if (var_type.basetype == SPIRType::Struct)
{
if (!is_builtin_type(var_type))
{
// For I/O blocks or structs, we will need to pass the block itself around
// to functions if they are used globally in leaf functions.
// Rather than passing down member by member,
// we unflatten I/O blocks while running the shader,
// and pass the actual struct type down to leaf functions.
// We then unflatten inputs, and flatten outputs in the "fixup" stages.
entry_func.add_local_variable(var.self);
vars_needing_early_declaration.push_back(var.self);
}
// Flatten the struct members into the interface struct
for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(var_type.member_types.size()); mbr_idx++)
{
BuiltIn builtin = BuiltInMax;
bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin);
auto &mbr_type = get<SPIRType>(var_type.member_types[mbr_idx]);
if (!is_builtin || has_active_builtin(builtin, storage))
{
if (!is_builtin && (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);
}
else
{
add_plain_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx);
}
}
}
}
else if (var_type.basetype == SPIRType::Boolean || var_type.basetype == SPIRType::Char ||
type_is_integral(var_type) || type_is_floating_point(var_type) || var_type.basetype == SPIRType::Boolean)
{
bool is_builtin = is_builtin_variable(var);
BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
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 && (storage == StorageClassInput || storage == StorageClassOutput) &&
(is_matrix(var_type) || is_array(var_type)))
{
add_composite_variable_to_interface_block(storage, ib_var_ref, ib_type, var);
}
else
{
add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var);
}
}
}
}
// Add an interface structure for the type of storage, which is either StorageClassInput or StorageClassOutput.
// Returns the ID of the newly added variable, or zero if no variable was added.
uint32_t CompilerMSL::add_interface_block(StorageClass storage)
@ -872,309 +1341,8 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage)
set_name(ib_type_id, to_name(ir.default_entry_point) + "_" + ib_var_ref);
set_name(ib_var_id, ib_var_ref);
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
for (auto p_var : vars)
{
uint32_t type_id = p_var->basetype;
auto &type = get_variable_data_type(*p_var);
if (type.basetype == SPIRType::Struct)
{
if (!is_builtin_type(type))
{
// For I/O blocks or structs, we will need to pass the block itself around
// to functions if they are used globally in leaf functions.
// Rather than passing down member by member,
// we unflatten I/O blocks while running the shader,
// and pass the actual struct type down to leaf functions.
// We then unflatten inputs, and flatten outputs in the "fixup" stages.
entry_func.add_local_variable(p_var->self);
vars_needing_early_declaration.push_back(p_var->self);
}
// Flatten the struct members into the interface struct
uint32_t mbr_idx = 0;
for (auto &mbr_type_id : type.member_types)
{
BuiltIn builtin;
bool is_builtin = is_member_builtin(type, mbr_idx, &builtin);
bool is_flat = has_member_decoration(type.self, mbr_idx, DecorationFlat) ||
has_decoration(p_var->self, DecorationFlat);
bool is_noperspective = has_member_decoration(type.self, mbr_idx, DecorationNoPerspective) ||
has_decoration(p_var->self, DecorationNoPerspective);
bool is_centroid = has_member_decoration(type.self, mbr_idx, DecorationCentroid) ||
has_decoration(p_var->self, DecorationCentroid);
bool is_sample = has_member_decoration(type.self, mbr_idx, DecorationSample) ||
has_decoration(p_var->self, DecorationSample);
if (!is_builtin || has_active_builtin(builtin, storage))
{
// Add a reference to the member to the interface struct.
uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
mbr_type_id = ensure_correct_builtin_type(mbr_type_id, builtin);
type.member_types[mbr_idx] = mbr_type_id;
ib_type.member_types.push_back(mbr_type_id);
// Give the member a name
string mbr_name = ensure_valid_name(to_qualified_member_name(type, mbr_idx), "m");
set_member_name(ib_type_id, ib_mbr_idx, mbr_name);
// Update the original variable reference to include the structure reference
string qual_var_name = ib_var_ref + "." + mbr_name;
if (is_builtin)
{
// For the builtin gl_PerVertex, we cannot treat it as a block anyways,
// so redirect to qualified name.
set_member_qualified_name(type_id, mbr_idx, qual_var_name);
}
else
{
// Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
switch (storage)
{
case StorageClassInput:
entry_func.fixup_hooks_in.push_back([=]() {
statement(to_name(p_var->self), ".", to_member_name(type, mbr_idx), " = ",
qual_var_name, ";");
});
break;
case StorageClassOutput:
entry_func.fixup_hooks_out.push_back([=]() {
statement(qual_var_name, " = ", to_name(p_var->self), ".",
to_member_name(type, mbr_idx), ";");
});
break;
default:
break;
}
}
// Copy the variable location from the original variable to the member
if (has_member_decoration(type_id, mbr_idx, DecorationLocation))
{
uint32_t locn = get_member_decoration(type_id, mbr_idx, DecorationLocation);
if (storage == StorageClassInput && get_entry_point().model == ExecutionModelVertex)
{
mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
type.member_types[mbr_idx] = mbr_type_id;
ib_type.member_types[ib_mbr_idx] = mbr_type_id;
}
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
else if (has_decoration(p_var->self, DecorationLocation))
{
// The block itself might have a location and in this case, all members of the block
// receive incrementing locations.
uint32_t locn = get_decoration(p_var->self, DecorationLocation) + mbr_idx;
if (storage == StorageClassInput && get_entry_point().model == ExecutionModelVertex)
{
mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
type.member_types[mbr_idx] = mbr_type_id;
ib_type.member_types[ib_mbr_idx] = mbr_type_id;
}
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
// Copy the component location, if present.
if (has_member_decoration(type_id, mbr_idx, DecorationComponent))
{
uint32_t comp = get_member_decoration(type_id, mbr_idx, DecorationComponent);
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationComponent, comp);
}
// Mark the member as builtin if needed
if (is_builtin)
{
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationBuiltIn, builtin);
if (builtin == BuiltInPosition)
qual_pos_var_name = qual_var_name;
}
// Copy interpolation decorations if needed
if (is_flat)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationFlat);
if (is_noperspective)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationNoPerspective);
if (is_centroid)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationCentroid);
if (is_sample)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationSample);
}
mbr_idx++;
}
}
else if (type.basetype == SPIRType::Boolean || type.basetype == SPIRType::Char || type_is_integral(type) ||
type_is_floating_point(type) || type.basetype == SPIRType::Boolean)
{
bool is_builtin = is_builtin_variable(*p_var);
BuiltIn builtin = BuiltIn(get_decoration(p_var->self, DecorationBuiltIn));
bool is_flat = has_decoration(p_var->self, DecorationFlat);
bool is_noperspective = has_decoration(p_var->self, DecorationNoPerspective);
bool is_centroid = has_decoration(p_var->self, DecorationCentroid);
bool is_sample = has_decoration(p_var->self, DecorationSample);
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 && (storage == StorageClassInput || storage == StorageClassOutput) &&
(is_matrix(type) || is_array(type)))
{
uint32_t elem_cnt = 0;
if (is_matrix(type))
{
if (is_array(type))
SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables.");
elem_cnt = type.columns;
}
else if (is_array(type))
{
if (type.array.size() != 1)
SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables.");
elem_cnt = to_array_size_literal(type);
}
auto *usable_type = &type;
if (usable_type->pointer)
usable_type = &get<SPIRType>(usable_type->parent_type);
while (is_array(*usable_type) || is_matrix(*usable_type))
usable_type = &get<SPIRType>(usable_type->parent_type);
entry_func.add_local_variable(p_var->self);
// We need to declare the variable early and at entry-point scope.
vars_needing_early_declaration.push_back(p_var->self);
for (uint32_t i = 0; i < elem_cnt; i++)
{
// Add a reference to the variable type to the interface struct.
uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
ib_type.member_types.push_back(usable_type->self);
// Give the member a name
string mbr_name = ensure_valid_name(join(to_expression(p_var->self), "_", i), "m");
set_member_name(ib_type_id, ib_mbr_idx, mbr_name);
// There is no qualified alias since we need to flatten the internal array on return.
if (get_decoration_bitset(p_var->self).get(DecorationLocation))
{
uint32_t locn = get_decoration(p_var->self, DecorationLocation) + i;
if (storage == StorageClassInput && get_entry_point().model == ExecutionModelVertex)
{
p_var->basetype = ensure_correct_attribute_type(p_var->basetype, locn);
uint32_t mbr_type_id = ensure_correct_attribute_type(usable_type->self, locn);
ib_type.member_types[ib_mbr_idx] = mbr_type_id;
}
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
if (get_decoration_bitset(p_var->self).get(DecorationIndex))
{
uint32_t index = get_decoration(p_var->self, DecorationIndex);
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationIndex, index);
}
// Copy interpolation decorations if needed
if (is_flat)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationFlat);
if (is_noperspective)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationNoPerspective);
if (is_centroid)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationCentroid);
if (is_sample)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationSample);
switch (storage)
{
case StorageClassInput:
entry_func.fixup_hooks_in.push_back([=]() {
statement(to_name(p_var->self), "[", i, "] = ", ib_var_ref, ".", mbr_name, ";");
});
break;
case StorageClassOutput:
entry_func.fixup_hooks_out.push_back([=]() {
statement(ib_var_ref, ".", mbr_name, " = ", to_name(p_var->self), "[", i, "];");
});
break;
default:
break;
}
}
}
else
{
// Add a reference to the variable type to the interface struct.
uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
type_id = ensure_correct_builtin_type(type_id, builtin);
p_var->basetype = type_id;
ib_type.member_types.push_back(get_pointee_type_id(type_id));
// Give the member a name
string mbr_name = ensure_valid_name(to_expression(p_var->self), "m");
set_member_name(ib_type_id, ib_mbr_idx, mbr_name);
// Update the original variable reference to include the structure reference
string qual_var_name = ib_var_ref + "." + mbr_name;
ir.meta[p_var->self].decoration.qualified_alias = qual_var_name;
// Copy the variable location from the original variable to the member
if (get_decoration_bitset(p_var->self).get(DecorationLocation))
{
uint32_t locn = get_decoration(p_var->self, DecorationLocation);
if (storage == StorageClassInput && get_entry_point().model == ExecutionModelVertex)
{
type_id = ensure_correct_attribute_type(type_id, locn);
p_var->basetype = type_id;
ib_type.member_types[ib_mbr_idx] = get_pointee_type_id(type_id);
}
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
if (get_decoration_bitset(p_var->self).get(DecorationComponent))
{
uint32_t comp = get_decoration(p_var->self, DecorationComponent);
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationComponent, comp);
}
if (get_decoration_bitset(p_var->self).get(DecorationIndex))
{
uint32_t index = get_decoration(p_var->self, DecorationIndex);
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationIndex, index);
}
// Mark the member as builtin if needed
if (is_builtin)
{
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationBuiltIn, builtin);
if (builtin == BuiltInPosition)
qual_pos_var_name = qual_var_name;
}
// Copy interpolation decorations if needed
if (is_flat)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationFlat);
if (is_noperspective)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationNoPerspective);
if (is_centroid)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationCentroid);
if (is_sample)
set_member_decoration(ib_type_id, ib_mbr_idx, DecorationSample);
}
}
}
}
add_variable_to_interface_block(storage, ib_var_ref, ib_type, *p_var);
// Sort the members of the structure by their locations.
MemberSorter member_sorter(ib_type, ir.meta[ib_type_id], MemberSorter::Location);
@ -1863,7 +2031,8 @@ void CompilerMSL::emit_custom_functions()
statement("if (!s)");
statement(" return x;");
statement("return vec<T, 4>(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), "
"spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), "
"spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) "
"& 0xFF)), "
"spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF)));");
end_scope();
statement("");
@ -3734,7 +3903,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
uint32_t mbr_type_id = type.member_types[index];
auto &mbr_type = get<SPIRType>(mbr_type_id);
BuiltIn builtin;
BuiltIn builtin = BuiltInMax;
bool is_builtin = is_member_builtin(type, index, &builtin);
// Vertex function inputs
@ -4409,7 +4578,7 @@ string CompilerMSL::to_name(uint32_t id, bool allow_alias) const
string CompilerMSL::to_qualified_member_name(const SPIRType &type, uint32_t index)
{
// Don't qualify Builtin names because they are unique and are treated as such when building expressions
BuiltIn builtin;
BuiltIn builtin = BuiltInMax;
if (is_member_builtin(type, index, &builtin))
return builtin_to_glsl(builtin, type.storage);

View File

@ -361,6 +361,19 @@ protected:
std::unordered_set<uint32_t> &global_var_ids,
std::unordered_set<uint32_t> &processed_func_ids);
uint32_t add_interface_block(spv::StorageClass storage);
void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type,
SPIRVariable &var);
void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var);
void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var);
void add_plain_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var, uint32_t index);
void add_composite_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var, uint32_t index);
uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx);
void mark_location_as_used_by_shader(uint32_t location, spv::StorageClass storage);
uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
uint32_t ensure_correct_attribute_type(uint32_t type_id, uint32_t location);