MSL: Gracefully assign automatic input locations to builtin attributes.

This commit is contained in:
Hans-Kristian Arntzen 2021-02-17 12:21:21 +01:00
parent aa271c1460
commit ce552f4f91
4 changed files with 143 additions and 22 deletions

View File

@ -0,0 +1,31 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 gl_Position [[position]];
};
struct main0_in
{
float4 FragColors [[attribute(2)]];
float4 gl_Position [[attribute(1)]];
};
struct main0_patchIn
{
float4 FragColor [[attribute(0)]];
float2 gl_TessLevelInner [[attribute(3)]];
float4 gl_TessLevelOuter [[attribute(4)]];
patch_control_point<main0_in> gl_in;
};
[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], uint gl_PrimitiveID [[patch_id]])
{
main0_out out = {};
out.gl_Position = (((((float4(1.0) + patchIn.FragColor) + patchIn.gl_in[0].FragColors) + patchIn.gl_in[1].FragColors) + float4(patchIn.gl_TessLevelInner.x)) + float4(patchIn.gl_TessLevelOuter[int(gl_PrimitiveID) & 1])) + patchIn.gl_in[0].gl_Position;
return out;
}

View File

@ -0,0 +1,10 @@
#version 450
layout(quads) in;
layout(location = 0) patch in vec4 FragColor;
layout(location = 2) in vec4 FragColors[];
void main()
{
gl_Position = vec4(1.0) + FragColor + FragColors[0] + FragColors[1] + gl_TessLevelInner[0] + gl_TessLevelOuter[gl_PrimitiveID & 1] + gl_in[0].gl_Position;
}

View File

@ -103,6 +103,15 @@ bool CompilerMSL::is_msl_shader_input_used(uint32_t location)
return location_inputs_in_use.count(location) != 0;
}
uint32_t CompilerMSL::get_automatic_builtin_input_location(spv::BuiltIn builtin) const
{
auto itr = builtin_to_automatic_input_location.find(builtin);
if (itr == builtin_to_automatic_input_location.end())
return k_unknown_location;
else
return itr->second;
}
bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
{
StageSetBinding tuple = { model, desc_set, binding };
@ -2720,7 +2729,7 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
// Force the variable to have the proper name.
set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction));
if (get_entry_point().flags.get(ExecutionModeTriangles))
if (get_execution_mode_bitset().get(ExecutionModeTriangles))
{
// Triangles are tricky, because we want only one member in the struct.
@ -2743,6 +2752,10 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
// Give the member a name
set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
// We cannot decorate both, but the important part is that
// it's marked as builtin so we can get automatic attribute assignment if needed.
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
// There is no qualified alias since we need to flatten the internal array on return.
if (get_decoration_bitset(var.self).get(DecorationLocation))
{
@ -2805,6 +2818,8 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
string qual_var_name = ib_var_ref + "." + mbr_name;
ir.meta[var.self].decoration.qualified_alias = qual_var_name;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
if (get_decoration_bitset(var.self).get(DecorationLocation))
{
uint32_t locn = get_decoration(var.self, DecorationLocation);
@ -10041,7 +10056,13 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
return "";
}
}
uint32_t locn = get_ordered_member_location(type.self, index);
uint32_t locn;
if (is_builtin)
locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index);
else
locn = get_member_location(type.self, index);
if (locn != k_unknown_location)
return string(" [[attribute(") + convert_to_string(locn) + ")]]";
}
@ -10081,7 +10102,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
}
}
uint32_t comp;
uint32_t locn = get_ordered_member_location(type.self, index, &comp);
uint32_t locn = get_member_location(type.self, index, &comp);
if (locn != k_unknown_location)
{
if (comp != k_unknown_component)
@ -10117,7 +10138,13 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
}
if (msl_options.multi_patch_workgroup)
return "";
uint32_t locn = get_ordered_member_location(type.self, index);
uint32_t locn;
if (is_builtin)
locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index);
else
locn = get_member_location(type.self, index);
if (locn != k_unknown_location)
return string(" [[attribute(") + convert_to_string(locn) + ")]]";
}
@ -10150,7 +10177,13 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
// 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);
uint32_t locn;
if (is_builtin)
locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index);
else
locn = get_member_location(type.self, index);
if (locn != k_unknown_location)
return string(" [[attribute(") + convert_to_string(locn) + ")]]";
}
@ -10190,7 +10223,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
else
{
uint32_t comp;
uint32_t locn = get_ordered_member_location(type.self, index, &comp);
uint32_t locn = get_member_location(type.self, index, &comp);
if (locn != k_unknown_location)
{
// For user-defined attributes, this is fine. From Vulkan spec:
@ -10292,7 +10325,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
return "";
}
}
uint32_t locn = get_ordered_member_location(type.self, index);
uint32_t locn = get_member_location(type.self, index);
// Metal will likely complain about missing color attachments, too.
if (locn != k_unknown_location && !(msl_options.enable_frag_output_mask & (1 << locn)))
return "";
@ -10341,24 +10374,61 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
// If the location of the member has been explicitly set, that location is used. If not, this
// function assumes the members are ordered in their location order, and simply returns the
// index as the location.
uint32_t CompilerMSL::get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp)
uint32_t CompilerMSL::get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp) const
{
auto &m = ir.meta[type_id];
if (index < m.members.size())
if (comp)
{
auto &dec = m.members[index];
if (comp)
{
if (dec.decoration_flags.get(DecorationComponent))
*comp = dec.component;
else
*comp = k_unknown_component;
}
if (dec.decoration_flags.get(DecorationLocation))
return dec.location;
if (has_member_decoration(type_id, index, DecorationComponent))
*comp = get_member_decoration(type_id, index, DecorationComponent);
else
*comp = k_unknown_component;
}
return index;
if (has_member_decoration(type_id, index, DecorationLocation))
return get_member_decoration(type_id, index, DecorationLocation);
else
return k_unknown_location;
}
uint32_t CompilerMSL::get_or_allocate_builtin_input_member_location(spv::BuiltIn builtin,
uint32_t type_id, uint32_t index,
uint32_t *comp)
{
uint32_t loc = get_member_location(type_id, index, comp);
if (loc != k_unknown_location)
return loc;
if (comp)
*comp = k_unknown_component;
// Late allocation. Find a location which is unused by the application.
// This can happen for built-in inputs in tessellation which are mixed and matched with user inputs.
auto &mbr_type = get<SPIRType>(get<SPIRType>(type_id).member_types[index]);
uint32_t count = type_to_location_count(mbr_type);
// This should always be 1.
if (count != 1)
return k_unknown_location;
loc = 0;
while (location_inputs_in_use.count(loc) != 0)
loc++;
set_member_decoration(type_id, index, DecorationLocation, loc);
// 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))
{
builtin_to_automatic_input_location[BuiltInTessLevelInner] = loc;
builtin_to_automatic_input_location[BuiltInTessLevelOuter] = loc;
}
else
builtin_to_automatic_input_location[builtin] = loc;
mark_location_as_used_by_shader(loc, mbr_type, StorageClassInput);
return loc;
}
// Returns the type declaration for a function, including the

View File

@ -550,6 +550,13 @@ public:
// Query after compilation is done. This allows you to check if an input location was used by the shader.
bool is_msl_shader_input_used(uint32_t location);
// If not using add_msl_shader_input, it's possible
// that certain builtin attributes need to be automatically assigned locations.
// This is typical for tessellation builtin inputs such as tess levels, gl_Position, etc.
// This returns k_unknown_location if the location was explicitly assigned with
// add_msl_shader_input or the builtin is not used, otherwise returns N in [[attribute(N)]].
uint32_t get_automatic_builtin_input_location(spv::BuiltIn builtin) const;
// NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here.
// Constexpr samplers are always assumed to be emitted.
// No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped
@ -826,7 +833,9 @@ protected:
std::string argument_decl(const SPIRFunction::Parameter &arg);
std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0);
uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
uint32_t get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr) const;
uint32_t get_or_allocate_builtin_input_member_location(spv::BuiltIn builtin,
uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
// MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output.
// These values can change depending on various extended decorations which control packing rules.
@ -931,6 +940,7 @@ protected:
std::unordered_map<uint32_t, MSLShaderInput> inputs_by_builtin;
std::unordered_set<uint32_t> location_inputs_in_use;
std::unordered_map<uint32_t, uint32_t> fragment_output_components;
std::unordered_map<uint32_t, uint32_t> builtin_to_automatic_input_location;
std::set<std::string> pragma_lines;
std::set<std::string> typedef_lines;
SmallVector<uint32_t> vars_needing_early_declaration;