Merge pull request #1648 from billhollings/msl-pad-arg-buff-structs
MSL: Support padding Metal argument buffer entries based on argument index.
This commit is contained in:
commit
45818c14e4
222
spirv_msl.cpp
222
spirv_msl.cpp
@ -67,6 +67,52 @@ void CompilerMSL::add_msl_resource_binding(const MSLResourceBinding &binding)
|
||||
{
|
||||
StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding };
|
||||
resource_bindings[tuple] = { binding, false };
|
||||
|
||||
// If we might need to pad argument buffer members to positionally align
|
||||
// arg buffer indexes, also maintain a lookup by argument buffer index.
|
||||
if (msl_options.pad_argument_buffer_resources)
|
||||
{
|
||||
StageSetBinding arg_idx_tuple = { binding.stage, binding.desc_set, k_unknown_component };
|
||||
|
||||
#define ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(rez) \
|
||||
arg_idx_tuple.binding = binding.msl_##rez; \
|
||||
resource_arg_buff_idx_to_binding_number[arg_idx_tuple] = binding.binding
|
||||
|
||||
switch (binding.basetype)
|
||||
{
|
||||
case SPIRType::Void:
|
||||
case SPIRType::Boolean:
|
||||
case SPIRType::SByte:
|
||||
case SPIRType::UByte:
|
||||
case SPIRType::Short:
|
||||
case SPIRType::UShort:
|
||||
case SPIRType::Int:
|
||||
case SPIRType::UInt:
|
||||
case SPIRType::Int64:
|
||||
case SPIRType::UInt64:
|
||||
case SPIRType::AtomicCounter:
|
||||
case SPIRType::Half:
|
||||
case SPIRType::Float:
|
||||
case SPIRType::Double:
|
||||
ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(buffer);
|
||||
break;
|
||||
case SPIRType::Image:
|
||||
ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(texture);
|
||||
break;
|
||||
case SPIRType::Sampler:
|
||||
ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(sampler);
|
||||
break;
|
||||
case SPIRType::SampledImage:
|
||||
ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(texture);
|
||||
ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(sampler);
|
||||
break;
|
||||
default:
|
||||
SPIRV_CROSS_THROW("Unexpected argument buffer resource base type. When padding argument buffer elements, "
|
||||
"all descriptor set resources must be supplied with a base type by the app.");
|
||||
break;
|
||||
}
|
||||
#undef ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP
|
||||
}
|
||||
}
|
||||
|
||||
void CompilerMSL::add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index)
|
||||
@ -15094,10 +15140,67 @@ void CompilerMSL::analyze_argument_buffers()
|
||||
});
|
||||
|
||||
uint32_t member_index = 0;
|
||||
uint32_t next_arg_buff_index = 0;
|
||||
for (auto &resource : resources)
|
||||
{
|
||||
auto &var = *resource.var;
|
||||
auto &type = get_variable_data_type(var);
|
||||
|
||||
// If needed, synthesize and add padding members.
|
||||
// member_index and next_arg_buff_index are incremented when padding members are added.
|
||||
if (msl_options.pad_argument_buffer_resources)
|
||||
{
|
||||
while (resource.index > next_arg_buff_index)
|
||||
{
|
||||
auto &rez_bind = get_argument_buffer_resource(desc_set, next_arg_buff_index);
|
||||
switch (rez_bind.basetype)
|
||||
{
|
||||
case SPIRType::Void:
|
||||
case SPIRType::Boolean:
|
||||
case SPIRType::SByte:
|
||||
case SPIRType::UByte:
|
||||
case SPIRType::Short:
|
||||
case SPIRType::UShort:
|
||||
case SPIRType::Int:
|
||||
case SPIRType::UInt:
|
||||
case SPIRType::Int64:
|
||||
case SPIRType::UInt64:
|
||||
case SPIRType::AtomicCounter:
|
||||
case SPIRType::Half:
|
||||
case SPIRType::Float:
|
||||
case SPIRType::Double:
|
||||
add_argument_buffer_padding_buffer_type(buffer_type, member_index, next_arg_buff_index, rez_bind);
|
||||
break;
|
||||
case SPIRType::Image:
|
||||
add_argument_buffer_padding_image_type(buffer_type, member_index, next_arg_buff_index, rez_bind);
|
||||
break;
|
||||
case SPIRType::Sampler:
|
||||
add_argument_buffer_padding_sampler_type(buffer_type, member_index, next_arg_buff_index, rez_bind);
|
||||
break;
|
||||
case SPIRType::SampledImage:
|
||||
if (next_arg_buff_index == rez_bind.msl_sampler)
|
||||
add_argument_buffer_padding_sampler_type(buffer_type, member_index, next_arg_buff_index, rez_bind);
|
||||
else
|
||||
add_argument_buffer_padding_image_type(buffer_type, member_index, next_arg_buff_index, rez_bind);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust the number of slots consumed by current member itself.
|
||||
// If actual member is an array, allow runtime array resolution as well.
|
||||
uint32_t elem_cnt = type.array.empty() ? 1 : to_array_size_literal(type);
|
||||
if (elem_cnt == 0)
|
||||
elem_cnt = get_resource_array_size(var.self);
|
||||
|
||||
// And if the member is a combined image sampler, it takes double the slots
|
||||
if (type.basetype == SPIRType::SampledImage)
|
||||
elem_cnt *= 2;
|
||||
|
||||
next_arg_buff_index += elem_cnt;
|
||||
}
|
||||
|
||||
string mbr_name = ensure_valid_name(resource.name, "m");
|
||||
if (resource.plane > 0)
|
||||
mbr_name += join(plane_name_suffix, resource.plane);
|
||||
@ -15196,6 +15299,125 @@ void CompilerMSL::analyze_argument_buffers()
|
||||
}
|
||||
}
|
||||
|
||||
// Return the resource type of the app-provided resources for the descriptor set,
|
||||
// that matches the resource index of the argument buffer index.
|
||||
// This is a two-step lookup, first lookup the resource binding number from the argument buffer index,
|
||||
// then lookup the resource binding using the binding number.
|
||||
MSLResourceBinding &CompilerMSL::get_argument_buffer_resource(uint32_t desc_set, uint32_t arg_idx)
|
||||
{
|
||||
auto stage = get_entry_point().model;
|
||||
StageSetBinding arg_idx_tuple = { stage, desc_set, arg_idx };
|
||||
auto arg_itr = resource_arg_buff_idx_to_binding_number.find(arg_idx_tuple);
|
||||
if (arg_itr != end(resource_arg_buff_idx_to_binding_number))
|
||||
{
|
||||
StageSetBinding bind_tuple = { stage, desc_set, arg_itr->second };
|
||||
auto bind_itr = resource_bindings.find(bind_tuple);
|
||||
if (bind_itr != end(resource_bindings))
|
||||
return bind_itr->second.first;
|
||||
}
|
||||
SPIRV_CROSS_THROW("Argument buffer resource base type could not be determined. When padding argument buffer "
|
||||
"elements, all descriptor set resources must be supplied with a base type by the app.");
|
||||
}
|
||||
|
||||
// Adds an argument buffer padding argument buffer type as one or more members of the struct type at the member index.
|
||||
// Metal does not support arrays of buffers, so these are emitted as multiple struct members.
|
||||
void CompilerMSL::add_argument_buffer_padding_buffer_type(SPIRType &struct_type, uint32_t &mbr_idx,
|
||||
uint32_t &arg_buff_index, MSLResourceBinding &rez_bind)
|
||||
{
|
||||
if (!argument_buffer_padding_buffer_type_id)
|
||||
{
|
||||
uint32_t buff_type_id = ir.increase_bound_by(2);
|
||||
auto &buff_type = set<SPIRType>(buff_type_id);
|
||||
buff_type.basetype = rez_bind.basetype;
|
||||
buff_type.storage = StorageClassUniformConstant;
|
||||
|
||||
uint32_t ptr_type_id = buff_type_id + 1;
|
||||
auto &ptr_type = set<SPIRType>(ptr_type_id);
|
||||
ptr_type = buff_type;
|
||||
ptr_type.pointer = true;
|
||||
ptr_type.pointer_depth++;
|
||||
ptr_type.parent_type = buff_type_id;
|
||||
|
||||
argument_buffer_padding_buffer_type_id = ptr_type_id;
|
||||
}
|
||||
|
||||
for (uint32_t rez_idx = 0; rez_idx < rez_bind.count; rez_idx++)
|
||||
add_argument_buffer_padding_type(argument_buffer_padding_buffer_type_id, struct_type, mbr_idx, arg_buff_index, 1);
|
||||
}
|
||||
|
||||
// Adds an argument buffer padding argument image type as a member of the struct type at the member index.
|
||||
void CompilerMSL::add_argument_buffer_padding_image_type(SPIRType &struct_type, uint32_t &mbr_idx,
|
||||
uint32_t &arg_buff_index, MSLResourceBinding &rez_bind)
|
||||
{
|
||||
if (!argument_buffer_padding_image_type_id)
|
||||
{
|
||||
uint32_t base_type_id = ir.increase_bound_by(2);
|
||||
auto &base_type = set<SPIRType>(base_type_id);
|
||||
base_type.basetype = SPIRType::Float;
|
||||
base_type.width = 32;
|
||||
|
||||
uint32_t img_type_id = base_type_id + 1;
|
||||
auto &img_type = set<SPIRType>(img_type_id);
|
||||
img_type.basetype = SPIRType::Image;
|
||||
img_type.storage = StorageClassUniformConstant;
|
||||
|
||||
img_type.image.type = base_type_id;
|
||||
img_type.image.dim = Dim2D;
|
||||
img_type.image.depth = false;
|
||||
img_type.image.arrayed = false;
|
||||
img_type.image.ms = false;
|
||||
img_type.image.sampled = 1;
|
||||
img_type.image.format = ImageFormatUnknown;
|
||||
img_type.image.access = AccessQualifierMax;
|
||||
|
||||
argument_buffer_padding_image_type_id = img_type_id;
|
||||
}
|
||||
|
||||
add_argument_buffer_padding_type(argument_buffer_padding_image_type_id, struct_type, mbr_idx, arg_buff_index, rez_bind.count);
|
||||
}
|
||||
|
||||
// Adds an argument buffer padding argument sampler type as a member of the struct type at the member index.
|
||||
void CompilerMSL::add_argument_buffer_padding_sampler_type(SPIRType &struct_type, uint32_t &mbr_idx,
|
||||
uint32_t &arg_buff_index, MSLResourceBinding &rez_bind)
|
||||
{
|
||||
if (!argument_buffer_padding_sampler_type_id)
|
||||
{
|
||||
uint32_t samp_type_id = ir.increase_bound_by(1);
|
||||
auto &samp_type = set<SPIRType>(samp_type_id);
|
||||
samp_type.basetype = SPIRType::Sampler;
|
||||
samp_type.storage = StorageClassUniformConstant;
|
||||
|
||||
argument_buffer_padding_sampler_type_id = samp_type_id;
|
||||
}
|
||||
|
||||
add_argument_buffer_padding_type(argument_buffer_padding_sampler_type_id, struct_type, mbr_idx, arg_buff_index, rez_bind.count);
|
||||
}
|
||||
|
||||
// Adds the argument buffer padding argument type as a member of the struct type at the member index.
|
||||
// Advances both arg_buff_index and mbr_idx to next argument slots.
|
||||
void CompilerMSL::add_argument_buffer_padding_type(uint32_t mbr_type_id, SPIRType &struct_type, uint32_t &mbr_idx,
|
||||
uint32_t &arg_buff_index, uint32_t count)
|
||||
{
|
||||
uint32_t type_id = mbr_type_id;
|
||||
if (count > 1)
|
||||
{
|
||||
uint32_t ary_type_id = ir.increase_bound_by(1);
|
||||
auto &ary_type = set<SPIRType>(ary_type_id);
|
||||
ary_type = get<SPIRType>(type_id);
|
||||
ary_type.array.push_back(count);
|
||||
ary_type.array_size_literal.push_back(true);
|
||||
ary_type.parent_type = type_id;
|
||||
type_id = ary_type_id;
|
||||
}
|
||||
|
||||
set_member_name(struct_type.self, mbr_idx, join("_m", arg_buff_index, "_pad"));
|
||||
set_extended_member_decoration(struct_type.self, mbr_idx, SPIRVCrossDecorationResourceIndexPrimary, arg_buff_index);
|
||||
struct_type.member_types.push_back(type_id);
|
||||
|
||||
arg_buff_index += count;
|
||||
mbr_idx++;
|
||||
}
|
||||
|
||||
void CompilerMSL::activate_argument_buffer_resources()
|
||||
{
|
||||
// For ABI compatibility, force-enable all resources which are part of argument buffers.
|
||||
|
@ -71,15 +71,23 @@ struct MSLShaderInput
|
||||
// resources consumed by this binding, if the binding represents an array of resources.
|
||||
// If the resource array is a run-time-sized array, which are legal in GLSL or SPIR-V, this value
|
||||
// will be used to declare the array size in MSL, which does not support run-time-sized arrays.
|
||||
// For resources that are not held in a run-time-sized array, the count field does not need to be populated.
|
||||
// If pad_argument_buffer_resources is enabled, the base_type and count values are used to
|
||||
// specify the base type and array size of the resource in the argument buffer, if that resource
|
||||
// is not defined and used by the shader. With pad_argument_buffer_resources enabled, this
|
||||
// information will be used to pad the argument buffer structure, in order to align that
|
||||
// structure consistently for all uses, across all shaders, of the descriptor set represented
|
||||
// by the arugment buffer. If pad_argument_buffer_resources is disabled, base_type does not
|
||||
// need to be populated, and if the resource is also not a run-time sized array, the count
|
||||
// field does not need to be populated.
|
||||
// If using MSL 2.0 argument buffers, the descriptor set is not marked as a discrete descriptor set,
|
||||
// and (for iOS only) the resource is not a storage image (sampled != 2), the binding reference we
|
||||
// remap to will become an [[id(N)]] attribute within the "descriptor set" argument buffer structure.
|
||||
// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will become a
|
||||
// [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used.
|
||||
// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will
|
||||
// become a [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used.
|
||||
struct MSLResourceBinding
|
||||
{
|
||||
spv::ExecutionModel stage = spv::ExecutionModelMax;
|
||||
SPIRType::BaseType basetype = SPIRType::Unknown;
|
||||
uint32_t desc_set = 0;
|
||||
uint32_t binding = 0;
|
||||
uint32_t count = 0;
|
||||
@ -346,6 +354,19 @@ public:
|
||||
// and would otherwise declare a different IAB.
|
||||
bool force_active_argument_buffer_resources = false;
|
||||
|
||||
// Aligns each resource in an argument buffer to its assigned index value, id(N),
|
||||
// by adding synthetic padding members in the argument buffer struct for any resources
|
||||
// in the argument buffer that are not defined and used by the shader. This allows
|
||||
// the shader to index into the correct argument in a descriptor set argument buffer
|
||||
// that is shared across shaders, where not all resources in the argument buffer are
|
||||
// defined in each shader. For this to work, an MSLResourceBinding must be provided for
|
||||
// all descriptors in any descriptor set held in an argument buffer in the shader, and
|
||||
// that MSLResourceBinding must have the basetype and count members populated correctly.
|
||||
// The implementation here assumes any inline blocks in the argument buffer is provided
|
||||
// in a Metal buffer, and doesn't take into consideration inline blocks that are
|
||||
// optionally embedded directly into the argument buffer via add_inline_uniform_block().
|
||||
bool pad_argument_buffer_resources = false;
|
||||
|
||||
// Forces the use of plain arrays, which works around certain driver bugs on certain versions
|
||||
// of Intel Macbooks. See https://github.com/KhronosGroup/SPIRV-Cross/issues/1210.
|
||||
// May reduce performance in scenarios where arrays are copied around as value-types.
|
||||
@ -913,6 +934,9 @@ protected:
|
||||
uint32_t view_mask_buffer_id = 0;
|
||||
uint32_t dynamic_offsets_buffer_id = 0;
|
||||
uint32_t uint_type_id = 0;
|
||||
uint32_t argument_buffer_padding_buffer_type_id = 0;
|
||||
uint32_t argument_buffer_padding_image_type_id = 0;
|
||||
uint32_t argument_buffer_padding_sampler_type_id = 0;
|
||||
|
||||
bool does_shader_write_sample_mask = false;
|
||||
|
||||
@ -948,6 +972,7 @@ protected:
|
||||
SmallVector<uint32_t> vars_needing_early_declaration;
|
||||
|
||||
std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings;
|
||||
std::unordered_map<StageSetBinding, uint32_t, InternalHasher> resource_arg_buff_idx_to_binding_number;
|
||||
uint32_t type_to_location_count(const SPIRType &type) const;
|
||||
|
||||
uint32_t next_metal_resource_index_buffer = 0;
|
||||
@ -1027,6 +1052,11 @@ protected:
|
||||
|
||||
void analyze_argument_buffers();
|
||||
bool descriptor_set_is_argument_buffer(uint32_t desc_set) const;
|
||||
MSLResourceBinding &get_argument_buffer_resource(uint32_t desc_set, uint32_t arg_idx);
|
||||
void add_argument_buffer_padding_buffer_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind);
|
||||
void add_argument_buffer_padding_image_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind);
|
||||
void add_argument_buffer_padding_sampler_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind);
|
||||
void add_argument_buffer_padding_type(uint32_t mbr_type_id, SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, uint32_t count);
|
||||
|
||||
uint32_t get_target_components_for_fragment_location(uint32_t location) const;
|
||||
uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components,
|
||||
|
Loading…
Reference in New Issue
Block a user