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

github.com/KhronosGroup/SPIRV-Cross.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans-Kristian Arntzen <post@arntzen-software.no>2022-09-12 13:05:48 +0300
committerGitHub <noreply@github.com>2022-09-12 13:05:48 +0300
commit85535888953fe2c9ba49e77d92f2c18a1204f76c (patch)
treef6ab0af7405cd54108d329c558a3ff1abe857805
parent210a80013067672b52847ec7aa70ff78b2f4d77e (diff)
parent064eaebe728f7c541face5ce865680b720fb4ff8 (diff)
Merge pull request #2014 from cdavis5e/msl-shader-output-fixup
MSL: Add a mechanism to fix up shader outputs.
-rw-r--r--main.cpp39
-rw-r--r--spirv_cross_c.cpp73
-rw-r--r--spirv_cross_c.h41
-rw-r--r--spirv_msl.cpp205
-rw-r--r--spirv_msl.hpp67
5 files changed, 358 insertions, 67 deletions
diff --git a/main.cpp b/main.cpp
index 8d015a6d..e83284a0 100644
--- a/main.cpp
+++ b/main.cpp
@@ -681,7 +681,8 @@ struct CLIArguments
SmallVector<uint32_t> msl_device_argument_buffers;
SmallVector<pair<uint32_t, uint32_t>> msl_dynamic_buffers;
SmallVector<pair<uint32_t, uint32_t>> msl_inline_uniform_blocks;
- SmallVector<MSLShaderInput> msl_shader_inputs;
+ SmallVector<MSLShaderInterfaceVariable> msl_shader_inputs;
+ SmallVector<MSLShaderInterfaceVariable> msl_shader_outputs;
SmallVector<PLSArg> pls_in;
SmallVector<PLSArg> pls_out;
SmallVector<Remap> remaps;
@@ -874,6 +875,10 @@ static void print_help_msl()
"\t\t<format> can be 'any32', 'any16', 'u16', 'u8', or 'other', to indicate a 32-bit opaque value, 16-bit opaque value, 16-bit unsigned integer, 8-bit unsigned integer, "
"or other-typed variable. <size> is the vector length of the variable, which must be greater than or equal to that declared in the shader.\n"
"\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n"
+ "\t[--msl-shader-output <index> <format> <size>]:\n\t\tSpecify the format of the shader output at <index>.\n"
+ "\t\t<format> can be 'any32', 'any16', 'u16', 'u8', or 'other', to indicate a 32-bit opaque value, 16-bit opaque value, 16-bit unsigned integer, 8-bit unsigned integer, "
+ "or other-typed variable. <size> is the vector length of the variable, which must be greater than or equal to that declared in the shader.\n"
+ "\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n"
"\t[--msl-multi-patch-workgroup]:\n\t\tUse the new style of tessellation control processing, where multiple patches are processed per workgroup.\n"
"\t\tThis should increase throughput by ensuring all the GPU's SIMD lanes are occupied, but it is not compatible with the old style.\n"
"\t\tIn addition, this style also passes input variables in buffers directly instead of using vertex attribute processing.\n"
@@ -1182,6 +1187,8 @@ static string compile_iteration(const CLIArguments &args, std::vector<uint32_t>
msl_comp->add_inline_uniform_block(v.first, v.second);
for (auto &v : args.msl_shader_inputs)
msl_comp->add_msl_shader_input(v);
+ for (auto &v : args.msl_shader_outputs)
+ msl_comp->add_msl_shader_output(v);
if (args.msl_combined_sampler_suffix)
msl_comp->set_combined_sampler_suffix(args.msl_combined_sampler_suffix);
}
@@ -1585,23 +1592,41 @@ static int main_inner(int argc, char *argv[])
cbs.add("--msl-no-clip-distance-user-varying",
[&args](CLIParser &) { args.msl_enable_clip_distance_user_varying = false; });
cbs.add("--msl-shader-input", [&args](CLIParser &parser) {
- MSLShaderInput input;
+ MSLShaderInterfaceVariable input;
// Make sure next_uint() is called in-order.
input.location = parser.next_uint();
const char *format = parser.next_value_string("other");
if (strcmp(format, "any32") == 0)
- input.format = MSL_SHADER_INPUT_FORMAT_ANY32;
+ input.format = MSL_SHADER_VARIABLE_FORMAT_ANY32;
else if (strcmp(format, "any16") == 0)
- input.format = MSL_SHADER_INPUT_FORMAT_ANY16;
+ input.format = MSL_SHADER_VARIABLE_FORMAT_ANY16;
else if (strcmp(format, "u16") == 0)
- input.format = MSL_SHADER_INPUT_FORMAT_UINT16;
+ input.format = MSL_SHADER_VARIABLE_FORMAT_UINT16;
else if (strcmp(format, "u8") == 0)
- input.format = MSL_SHADER_INPUT_FORMAT_UINT8;
+ input.format = MSL_SHADER_VARIABLE_FORMAT_UINT8;
else
- input.format = MSL_SHADER_INPUT_FORMAT_OTHER;
+ input.format = MSL_SHADER_VARIABLE_FORMAT_OTHER;
input.vecsize = parser.next_uint();
args.msl_shader_inputs.push_back(input);
});
+ cbs.add("--msl-shader-output", [&args](CLIParser &parser) {
+ MSLShaderInterfaceVariable output;
+ // Make sure next_uint() is called in-order.
+ output.location = parser.next_uint();
+ const char *format = parser.next_value_string("other");
+ if (strcmp(format, "any32") == 0)
+ output.format = MSL_SHADER_VARIABLE_FORMAT_ANY32;
+ else if (strcmp(format, "any16") == 0)
+ output.format = MSL_SHADER_VARIABLE_FORMAT_ANY16;
+ else if (strcmp(format, "u16") == 0)
+ output.format = MSL_SHADER_VARIABLE_FORMAT_UINT16;
+ else if (strcmp(format, "u8") == 0)
+ output.format = MSL_SHADER_VARIABLE_FORMAT_UINT8;
+ else
+ output.format = MSL_SHADER_VARIABLE_FORMAT_OTHER;
+ output.vecsize = parser.next_uint();
+ args.msl_shader_outputs.push_back(output);
+ });
cbs.add("--msl-multi-patch-workgroup", [&args](CLIParser &) { args.msl_multi_patch_workgroup = true; });
cbs.add("--msl-vertex-for-tessellation", [&args](CLIParser &) { args.msl_vertex_for_tessellation = true; });
cbs.add("--msl-additional-fixed-sample-mask",
diff --git a/spirv_cross_c.cpp b/spirv_cross_c.cpp
index 4a62b635..2d9401b8 100644
--- a/spirv_cross_c.cpp
+++ b/spirv_cross_c.cpp
@@ -1136,9 +1136,9 @@ spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, const
}
auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
- MSLShaderInput attr;
+ MSLShaderInterfaceVariable attr;
attr.location = va->location;
- attr.format = static_cast<MSLShaderInputFormat>(va->format);
+ attr.format = static_cast<MSLShaderVariableFormat>(va->format);
attr.builtin = static_cast<spv::BuiltIn>(va->builtin);
msl.add_msl_shader_input(attr);
return SPVC_SUCCESS;
@@ -1149,7 +1149,7 @@ spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, const
#endif
}
-spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, const spvc_msl_shader_input *si)
+spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, const spvc_msl_shader_interface_var *si)
{
#if SPIRV_CROSS_C_API_MSL
if (compiler->backend != SPVC_BACKEND_MSL)
@@ -1159,9 +1159,9 @@ spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, const spv
}
auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
- MSLShaderInput input;
+ MSLShaderInterfaceVariable input;
input.location = si->location;
- input.format = static_cast<MSLShaderInputFormat>(si->format);
+ input.format = static_cast<MSLShaderVariableFormat>(si->format);
input.builtin = static_cast<spv::BuiltIn>(si->builtin);
input.vecsize = si->vecsize;
msl.add_msl_shader_input(input);
@@ -1173,6 +1173,30 @@ spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, const spv
#endif
}
+spvc_result spvc_compiler_msl_add_shader_output(spvc_compiler compiler, const spvc_msl_shader_interface_var *so)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ MSLShaderInterfaceVariable output;
+ output.location = so->location;
+ output.format = static_cast<MSLShaderVariableFormat>(so->format);
+ output.builtin = static_cast<spv::BuiltIn>(so->builtin);
+ output.vecsize = so->vecsize;
+ msl.add_msl_shader_output(output);
+ return SPVC_SUCCESS;
+#else
+ (void)so;
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
const spvc_msl_resource_binding *binding)
{
@@ -1298,6 +1322,24 @@ spvc_bool spvc_compiler_msl_is_shader_input_used(spvc_compiler compiler, unsigne
#endif
}
+spvc_bool spvc_compiler_msl_is_shader_output_used(spvc_compiler compiler, unsigned location)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ return msl.is_msl_shader_output_used(location) ? SPVC_TRUE : SPVC_FALSE;
+#else
+ (void)location;
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+#endif
+}
+
spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location)
{
return spvc_compiler_msl_is_shader_input_used(compiler, location);
@@ -2511,7 +2553,7 @@ void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr)
{
#if SPIRV_CROSS_C_API_MSL
// Crude, but works.
- MSLShaderInput attr_default;
+ MSLShaderInterfaceVariable attr_default;
attr->location = attr_default.location;
attr->format = static_cast<spvc_msl_vertex_format>(attr_default.format);
attr->builtin = static_cast<SpvBuiltIn>(attr_default.builtin);
@@ -2520,19 +2562,24 @@ void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr)
#endif
}
-void spvc_msl_shader_input_init(spvc_msl_shader_input *input)
+void spvc_msl_shader_interface_var_init(spvc_msl_shader_interface_var *var)
{
#if SPIRV_CROSS_C_API_MSL
- MSLShaderInput input_default;
- input->location = input_default.location;
- input->format = static_cast<spvc_msl_shader_input_format>(input_default.format);
- input->builtin = static_cast<SpvBuiltIn>(input_default.builtin);
- input->vecsize = input_default.vecsize;
+ MSLShaderInterfaceVariable var_default;
+ var->location = var_default.location;
+ var->format = static_cast<spvc_msl_shader_variable_format>(var_default.format);
+ var->builtin = static_cast<SpvBuiltIn>(var_default.builtin);
+ var->vecsize = var_default.vecsize;
#else
- memset(input, 0, sizeof(*input));
+ memset(var, 0, sizeof(*var));
#endif
}
+void spvc_msl_shader_input_init(spvc_msl_shader_input *input)
+{
+ spvc_msl_shader_interface_var_init(input);
+}
+
void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding)
{
#if SPIRV_CROSS_C_API_MSL
diff --git a/spirv_cross_c.h b/spirv_cross_c.h
index a35a5d65..4e6c6397 100644
--- a/spirv_cross_c.h
+++ b/spirv_cross_c.h
@@ -290,23 +290,29 @@ typedef enum spvc_msl_index_type
} spvc_msl_index_type;
/* Maps to C++ API. */
-typedef enum spvc_msl_shader_input_format
+typedef enum spvc_msl_shader_variable_format
{
- SPVC_MSL_SHADER_INPUT_FORMAT_OTHER = 0,
- SPVC_MSL_SHADER_INPUT_FORMAT_UINT8 = 1,
- SPVC_MSL_SHADER_INPUT_FORMAT_UINT16 = 2,
- SPVC_MSL_SHADER_INPUT_FORMAT_ANY16 = 3,
- SPVC_MSL_SHADER_INPUT_FORMAT_ANY32 = 4,
+ SPVC_MSL_SHADER_VARIABLE_FORMAT_OTHER = 0,
+ SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT8 = 1,
+ SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT16 = 2,
+ SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY16 = 3,
+ SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY32 = 4,
/* Deprecated names. */
- SPVC_MSL_VERTEX_FORMAT_OTHER = SPVC_MSL_SHADER_INPUT_FORMAT_OTHER,
- SPVC_MSL_VERTEX_FORMAT_UINT8 = SPVC_MSL_SHADER_INPUT_FORMAT_UINT8,
- SPVC_MSL_VERTEX_FORMAT_UINT16 = SPVC_MSL_SHADER_INPUT_FORMAT_UINT16,
+ SPVC_MSL_VERTEX_FORMAT_OTHER = SPVC_MSL_SHADER_VARIABLE_FORMAT_OTHER,
+ SPVC_MSL_VERTEX_FORMAT_UINT8 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT8,
+ SPVC_MSL_VERTEX_FORMAT_UINT16 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT16,
+ SPVC_MSL_SHADER_INPUT_FORMAT_OTHER = SPVC_MSL_SHADER_VARIABLE_FORMAT_OTHER,
+ SPVC_MSL_SHADER_INPUT_FORMAT_UINT8 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT8,
+ SPVC_MSL_SHADER_INPUT_FORMAT_UINT16 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT16,
+ SPVC_MSL_SHADER_INPUT_FORMAT_ANY16 = SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY16,
+ SPVC_MSL_SHADER_INPUT_FORMAT_ANY32 = SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY32,
+
SPVC_MSL_SHADER_INPUT_FORMAT_INT_MAX = 0x7fffffff
-} spvc_msl_shader_input_format, spvc_msl_vertex_format;
+} spvc_msl_shader_variable_format, spvc_msl_shader_input_format, spvc_msl_vertex_format;
-/* Maps to C++ API. Deprecated; use spvc_msl_shader_input. */
+/* Maps to C++ API. Deprecated; use spvc_msl_shader_interface_var. */
typedef struct spvc_msl_vertex_attribute
{
unsigned location;
@@ -330,17 +336,21 @@ typedef struct spvc_msl_vertex_attribute
SPVC_PUBLIC_API void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr);
/* Maps to C++ API. */
-typedef struct spvc_msl_shader_input
+typedef struct spvc_msl_shader_interface_var
{
unsigned location;
spvc_msl_vertex_format format;
SpvBuiltIn builtin;
unsigned vecsize;
-} spvc_msl_shader_input;
+} spvc_msl_shader_interface_var, spvc_msl_shader_input;
/*
* Initializes the shader input struct.
*/
+SPVC_PUBLIC_API void spvc_msl_shader_interface_var_init(spvc_msl_shader_interface_var *var);
+/*
+ * Deprecated. Use spvc_msl_shader_interface_var_init().
+ */
SPVC_PUBLIC_API void spvc_msl_shader_input_init(spvc_msl_shader_input *input);
/* Maps to C++ API. */
@@ -786,13 +796,16 @@ SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
const spvc_msl_resource_binding *binding);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler,
- const spvc_msl_shader_input *input);
+ const spvc_msl_shader_interface_var *input);
+SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_output(spvc_compiler compiler,
+ const spvc_msl_shader_interface_var *output);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_argument_buffer_device_address_space(spvc_compiler compiler, unsigned desc_set, spvc_bool device_address);
/* Obsolete, use is_shader_input_used. */
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location);
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_shader_input_used(spvc_compiler compiler, unsigned location);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_shader_output_used(spvc_compiler compiler, unsigned location);
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compiler,
SpvExecutionModel model,
diff --git a/spirv_msl.cpp b/spirv_msl.cpp
index 583a9232..48e3e6a6 100644
--- a/spirv_msl.cpp
+++ b/spirv_msl.cpp
@@ -56,13 +56,20 @@ CompilerMSL::CompilerMSL(ParsedIR &&ir_)
{
}
-void CompilerMSL::add_msl_shader_input(const MSLShaderInput &si)
+void CompilerMSL::add_msl_shader_input(const MSLShaderInterfaceVariable &si)
{
inputs_by_location[{si.location, si.component}] = si;
if (si.builtin != BuiltInMax && !inputs_by_builtin.count(si.builtin))
inputs_by_builtin[si.builtin] = si;
}
+void CompilerMSL::add_msl_shader_output(const MSLShaderInterfaceVariable &so)
+{
+ outputs_by_location[{so.location, so.component}] = so;
+ if (so.builtin != BuiltInMax && !outputs_by_builtin.count(so.builtin))
+ outputs_by_builtin[so.builtin] = so;
+}
+
void CompilerMSL::add_msl_resource_binding(const MSLResourceBinding &binding)
{
StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding };
@@ -150,6 +157,13 @@ bool CompilerMSL::is_msl_shader_input_used(uint32_t location)
location_inputs_in_use_fallback.count(location) == 0;
}
+bool CompilerMSL::is_msl_shader_output_used(uint32_t location)
+{
+ // Don't report internal location allocations to app.
+ return location_outputs_in_use.count(location) != 0 &&
+ location_outputs_in_use_fallback.count(location) == 0;
+}
+
uint32_t CompilerMSL::get_automatic_builtin_input_location(spv::BuiltIn builtin) const
{
auto itr = builtin_to_automatic_input_location.find(builtin);
@@ -159,6 +173,15 @@ uint32_t CompilerMSL::get_automatic_builtin_input_location(spv::BuiltIn builtin)
return itr->second;
}
+uint32_t CompilerMSL::get_automatic_builtin_output_location(spv::BuiltIn builtin) const
+{
+ auto itr = builtin_to_automatic_output_location.find(builtin);
+ if (itr == builtin_to_automatic_output_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 };
@@ -2051,15 +2074,27 @@ void CompilerMSL::mark_as_workgroup_struct(SPIRType &type)
void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, const SPIRType &type,
StorageClass storage, bool fallback)
{
- if (storage != StorageClassInput)
- return;
-
uint32_t count = type_to_location_count(type);
- for (uint32_t i = 0; i < count; i++)
+ switch (storage)
{
- location_inputs_in_use.insert(location + i);
- if (fallback)
- location_inputs_in_use_fallback.insert(location + i);
+ case StorageClassInput:
+ for (uint32_t i = 0; i < count; i++)
+ {
+ location_inputs_in_use.insert(location + i);
+ if (fallback)
+ location_inputs_in_use_fallback.insert(location + i);
+ }
+ break;
+ case StorageClassOutput:
+ for (uint32_t i = 0; i < count; i++)
+ {
+ location_outputs_in_use.insert(location + i);
+ if (fallback)
+ location_outputs_in_use_fallback.insert(location + i);
+ }
+ break;
+ default:
+ return;
}
}
@@ -2360,6 +2395,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, type, storage);
}
+ else if (is_builtin && capture_output_to_buffer && storage == StorageClassOutput && outputs_by_builtin.count(builtin))
+ {
+ uint32_t locn = outputs_by_builtin[builtin].location;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, type, storage);
+ }
if (get_decoration_bitset(var.self).get(DecorationComponent))
{
@@ -2525,6 +2566,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, *usable_type, storage);
}
+ else if (is_builtin && capture_output_to_buffer && storage == StorageClassOutput && outputs_by_builtin.count(builtin))
+ {
+ uint32_t locn = outputs_by_builtin[builtin].location + i;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, *usable_type, storage);
+ }
else if (is_builtin && (builtin == BuiltInClipDistance || builtin == BuiltInCullDistance))
{
// Declare the Clip/CullDistance as [[user(clip/cullN)]].
@@ -2739,6 +2786,13 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
mark_location_as_used_by_shader(location, *usable_type, storage);
location++;
}
+ else if (is_builtin && capture_output_to_buffer && storage == StorageClassOutput && outputs_by_builtin.count(builtin))
+ {
+ location = outputs_by_builtin[builtin].location + i;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location);
+ mark_location_as_used_by_shader(location, *usable_type, storage);
+ location++;
+ }
else if (is_builtin && (builtin == BuiltInClipDistance || builtin == BuiltInCullDistance))
{
// Declare the Clip/CullDistance as [[user(clip/cullN)]].
@@ -2933,6 +2987,13 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
mark_location_as_used_by_shader(location, get<SPIRType>(mbr_type_id), storage);
location++;
}
+ else if (is_builtin && capture_output_to_buffer && storage == StorageClassOutput && outputs_by_builtin.count(builtin))
+ {
+ location = outputs_by_builtin[builtin].location;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location);
+ mark_location_as_used_by_shader(location, get<SPIRType>(mbr_type_id), storage);
+ location++;
+ }
// Copy the component location, if present.
if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent))
@@ -3825,12 +3886,12 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
SPIRType type;
switch (input.second.format)
{
- case MSL_SHADER_INPUT_FORMAT_UINT16:
- case MSL_SHADER_INPUT_FORMAT_ANY16:
+ case MSL_SHADER_VARIABLE_FORMAT_UINT16:
+ case MSL_SHADER_VARIABLE_FORMAT_ANY16:
type.basetype = SPIRType::UShort;
type.width = 16;
break;
- case MSL_SHADER_INPUT_FORMAT_ANY32:
+ case MSL_SHADER_VARIABLE_FORMAT_ANY32:
default:
type.basetype = SPIRType::UInt;
type.width = 32;
@@ -3862,6 +3923,67 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
}
}
+ if (capture_output_to_buffer && storage == StorageClassOutput)
+ {
+ // For captured output, add all inputs from the next stage to ensure
+ // the struct containing them is the correct size and layout. This is
+ // necessary for certain implicit builtins that may nonetheless be read,
+ // even when they aren't written.
+ for (auto &output : outputs_by_location)
+ {
+ if (location_outputs_in_use.count(output.first.location) != 0)
+ continue;
+
+ // Create a fake variable to put at the location.
+ uint32_t offset = ir.increase_bound_by(4);
+ uint32_t type_id = offset;
+ uint32_t array_type_id = offset + 1;
+ uint32_t ptr_type_id = offset + 2;
+ uint32_t var_id = offset + 3;
+
+ SPIRType type;
+ switch (output.second.format)
+ {
+ case MSL_SHADER_VARIABLE_FORMAT_UINT16:
+ case MSL_SHADER_VARIABLE_FORMAT_ANY16:
+ type.basetype = SPIRType::UShort;
+ type.width = 16;
+ break;
+ case MSL_SHADER_VARIABLE_FORMAT_ANY32:
+ default:
+ type.basetype = SPIRType::UInt;
+ type.width = 32;
+ break;
+ }
+ type.vecsize = output.second.vecsize;
+ set<SPIRType>(type_id, type);
+
+ if (get_execution_model() == ExecutionModelTessellationControl)
+ {
+ type.array.push_back(0);
+ type.array_size_literal.push_back(true);
+ type.parent_type = type_id;
+ set<SPIRType>(array_type_id, type);
+ }
+
+ type.pointer = true;
+ type.pointer_depth++;
+ type.parent_type = get_execution_model() == ExecutionModelTessellationControl ? array_type_id : type_id;
+ type.storage = storage;
+ auto &ptr_type = set<SPIRType>(ptr_type_id, type);
+ ptr_type.self = type.parent_type;
+
+ auto &fake_var = set<SPIRVariable>(var_id, ptr_type_id, storage);
+ set_decoration(var_id, DecorationLocation, output.first.location);
+ if (output.first.component)
+ set_decoration(var_id, DecorationComponent, output.first.component);
+
+ meta.strip_array = true;
+ meta.allow_local_declaration = false;
+ add_variable_to_interface_block(storage, ib_var_ref, ib_type, fake_var, meta);
+ }
+ }
+
// When multiple variables need to access same location,
// unroll locations one by one and we will flatten output or input as necessary.
for (auto &loc : meta.location_meta)
@@ -4031,7 +4153,7 @@ uint32_t CompilerMSL::ensure_correct_input_type(uint32_t type_id, uint32_t locat
switch (p_va->second.format)
{
- case MSL_SHADER_INPUT_FORMAT_UINT8:
+ case MSL_SHADER_VARIABLE_FORMAT_UINT8:
{
switch (type.basetype)
{
@@ -4055,7 +4177,7 @@ uint32_t CompilerMSL::ensure_correct_input_type(uint32_t type_id, uint32_t locat
}
}
- case MSL_SHADER_INPUT_FORMAT_UINT16:
+ case MSL_SHADER_VARIABLE_FORMAT_UINT16:
{
switch (type.basetype)
{
@@ -11241,6 +11363,16 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
return join(" [[", loc_qual, "]]");
}
+ if (execution.model == ExecutionModelVertex && msl_options.vertex_for_tessellation && type.storage == StorageClassOutput)
+ {
+ // For this type of shader, we always arrange for it to capture its
+ // output to a buffer. For this reason, qualifiers are irrelevant here.
+ if (is_builtin)
+ // We still have to assign a location so the output struct will sort correctly.
+ get_or_allocate_builtin_output_member_location(builtin, type.self, index);
+ return "";
+ }
+
// Tessellation control function inputs
if (execution.model == ExecutionModelTessellationControl && type.storage == StorageClassInput)
{
@@ -11283,6 +11415,9 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
{
// For this type of shader, we always arrange for it to capture its
// output to a buffer. For this reason, qualifiers are irrelevant here.
+ if (is_builtin)
+ // We still have to assign a location so the output struct will sort correctly.
+ get_or_allocate_builtin_output_member_location(builtin, type.self, index);
return "";
}
@@ -11573,6 +11708,50 @@ uint32_t CompilerMSL::get_or_allocate_builtin_input_member_location(spv::BuiltIn
return loc;
}
+uint32_t CompilerMSL::get_or_allocate_builtin_output_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;
+ loc = 0;
+
+ if (comp)
+ *comp = k_unknown_component;
+
+ // Late allocation. Find a location which is unused by the application.
+ // This can happen for built-in outputs 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);
+
+ const auto location_range_in_use = [this](uint32_t location, uint32_t location_count) -> bool {
+ for (uint32_t i = 0; i < location_count; i++)
+ if (location_outputs_in_use.count(location + i) != 0)
+ return true;
+ return false;
+ };
+
+ while (location_range_in_use(loc, count))
+ 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_output_location[BuiltInTessLevelInner] = loc;
+ builtin_to_automatic_output_location[BuiltInTessLevelOuter] = loc;
+ }
+ else
+ builtin_to_automatic_output_location[builtin] = loc;
+
+ mark_location_as_used_by_shader(loc, mbr_type, StorageClassOutput, true);
+ return loc;
+}
+
// Returns the type declaration for a function, including the
// entry type if the current function is the entry point function
string CompilerMSL::func_type_decl(SPIRType &type)
diff --git a/spirv_msl.hpp b/spirv_msl.hpp
index c15159cf..4b9d88da 100644
--- a/spirv_msl.hpp
+++ b/spirv_msl.hpp
@@ -34,34 +34,39 @@
namespace SPIRV_CROSS_NAMESPACE
{
-// Indicates the format of a shader input. Currently limited to specifying
+// Indicates the format of a shader interface variable. Currently limited to specifying
// if the input is an 8-bit unsigned integer, 16-bit unsigned integer, or
// some other format.
-enum MSLShaderInputFormat
+enum MSLShaderVariableFormat
{
- MSL_SHADER_INPUT_FORMAT_OTHER = 0,
- MSL_SHADER_INPUT_FORMAT_UINT8 = 1,
- MSL_SHADER_INPUT_FORMAT_UINT16 = 2,
- MSL_SHADER_INPUT_FORMAT_ANY16 = 3,
- MSL_SHADER_INPUT_FORMAT_ANY32 = 4,
+ MSL_SHADER_VARIABLE_FORMAT_OTHER = 0,
+ MSL_SHADER_VARIABLE_FORMAT_UINT8 = 1,
+ MSL_SHADER_VARIABLE_FORMAT_UINT16 = 2,
+ MSL_SHADER_VARIABLE_FORMAT_ANY16 = 3,
+ MSL_SHADER_VARIABLE_FORMAT_ANY32 = 4,
// Deprecated aliases.
- MSL_VERTEX_FORMAT_OTHER = MSL_SHADER_INPUT_FORMAT_OTHER,
- MSL_VERTEX_FORMAT_UINT8 = MSL_SHADER_INPUT_FORMAT_UINT8,
- MSL_VERTEX_FORMAT_UINT16 = MSL_SHADER_INPUT_FORMAT_UINT16,
-
- MSL_SHADER_INPUT_FORMAT_INT_MAX = 0x7fffffff
+ MSL_VERTEX_FORMAT_OTHER = MSL_SHADER_VARIABLE_FORMAT_OTHER,
+ MSL_VERTEX_FORMAT_UINT8 = MSL_SHADER_VARIABLE_FORMAT_UINT8,
+ MSL_VERTEX_FORMAT_UINT16 = MSL_SHADER_VARIABLE_FORMAT_UINT16,
+ MSL_SHADER_INPUT_FORMAT_OTHER = MSL_SHADER_VARIABLE_FORMAT_OTHER,
+ MSL_SHADER_INPUT_FORMAT_UINT8 = MSL_SHADER_VARIABLE_FORMAT_UINT8,
+ MSL_SHADER_INPUT_FORMAT_UINT16 = MSL_SHADER_VARIABLE_FORMAT_UINT16,
+ MSL_SHADER_INPUT_FORMAT_ANY16 = MSL_SHADER_VARIABLE_FORMAT_ANY16,
+ MSL_SHADER_INPUT_FORMAT_ANY32 = MSL_SHADER_VARIABLE_FORMAT_ANY32,
+
+ MSL_SHADER_VARIABLE_FORMAT_INT_MAX = 0x7fffffff
};
-// Defines MSL characteristics of an input variable at a particular location.
+// Defines MSL characteristics of a shader interface variable at a particular location.
// After compilation, it is possible to query whether or not this location was used.
// If vecsize is nonzero, it must be greater than or equal to the vecsize declared in the shader,
// or behavior is undefined.
-struct MSLShaderInput
+struct MSLShaderInterfaceVariable
{
uint32_t location = 0;
uint32_t component = 0;
- MSLShaderInputFormat format = MSL_SHADER_INPUT_FORMAT_OTHER;
+ MSLShaderVariableFormat format = MSL_SHADER_VARIABLE_FORMAT_OTHER;
spv::BuiltIn builtin = spv::BuiltInMax;
uint32_t vecsize = 0;
};
@@ -539,10 +544,15 @@ public:
explicit CompilerMSL(const ParsedIR &ir);
explicit CompilerMSL(ParsedIR &&ir);
- // input is a shader input description used to fix up shader input variables.
+ // input is a shader interface variable description used to fix up shader input variables.
// If shader inputs are provided, is_msl_shader_input_used() will return true after
- // calling ::compile() if the location was used by the MSL code.
- void add_msl_shader_input(const MSLShaderInput &input);
+ // calling ::compile() if the location were used by the MSL code.
+ void add_msl_shader_input(const MSLShaderInterfaceVariable &input);
+
+ // output is a shader interface variable description used to fix up shader output variables.
+ // If shader outputs are provided, is_msl_shader_output_used() will return true after
+ // calling ::compile() if the location were used by the MSL code.
+ void add_msl_shader_output(const MSLShaderInterfaceVariable &output);
// resource is a resource binding to indicate the MSL buffer,
// texture or sampler index to use for a particular SPIR-V description set
@@ -577,6 +587,9 @@ 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);
+ // Query after compilation is done. This allows you to check if an output location were used by the shader.
+ bool is_msl_shader_output_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.
@@ -584,6 +597,13 @@ public:
// 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;
+ // If not using add_msl_shader_output, it's possible
+ // that certain builtin attributes need to be automatically assigned locations.
+ // This is typical for tessellation builtin outputs such as tess levels, gl_Position, etc.
+ // This returns k_unknown_location if the location were explicitly assigned with
+ // add_msl_shader_output or the builtin were not used, otherwise returns N in [[attribute(N)]].
+ uint32_t get_automatic_builtin_output_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
@@ -894,6 +914,8 @@ protected:
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);
+ uint32_t get_or_allocate_builtin_output_member_location(spv::BuiltIn builtin,
+ uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
uint32_t get_physical_tess_level_array_size(spv::BuiltIn builtin) const;
@@ -1004,12 +1026,17 @@ protected:
Options msl_options;
std::set<SPVFuncImpl> spv_function_implementations;
// Must be ordered to ensure declarations are in a specific order.
- std::map<LocationComponentPair, MSLShaderInput> inputs_by_location;
- std::unordered_map<uint32_t, MSLShaderInput> inputs_by_builtin;
+ std::map<LocationComponentPair, MSLShaderInterfaceVariable> inputs_by_location;
+ std::unordered_map<uint32_t, MSLShaderInterfaceVariable> inputs_by_builtin;
+ std::map<LocationComponentPair, MSLShaderInterfaceVariable> outputs_by_location;
+ std::unordered_map<uint32_t, MSLShaderInterfaceVariable> outputs_by_builtin;
std::unordered_set<uint32_t> location_inputs_in_use;
std::unordered_set<uint32_t> location_inputs_in_use_fallback;
+ std::unordered_set<uint32_t> location_outputs_in_use;
+ std::unordered_set<uint32_t> location_outputs_in_use_fallback;
std::unordered_map<uint32_t, uint32_t> fragment_output_components;
std::unordered_map<uint32_t, uint32_t> builtin_to_automatic_input_location;
+ std::unordered_map<uint32_t, uint32_t> builtin_to_automatic_output_location;
std::set<std::string> pragma_lines;
std::set<std::string> typedef_lines;
SmallVector<uint32_t> vars_needing_early_declaration;