Updated spirv-cross.

This commit is contained in:
Бранимир Караџић 2020-02-04 21:38:12 -08:00
parent 11137890f7
commit 4a27145184
26 changed files with 1638 additions and 389 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -521,6 +521,7 @@ struct CLIArguments
bool msl_view_index_from_device_index = false; bool msl_view_index_from_device_index = false;
bool msl_dispatch_base = false; bool msl_dispatch_base = false;
bool msl_decoration_binding = false; bool msl_decoration_binding = false;
bool msl_force_active_argument_buffer_resources = false;
bool glsl_emit_push_constant_as_ubo = false; bool glsl_emit_push_constant_as_ubo = false;
bool glsl_emit_ubo_as_plain_uniforms = false; bool glsl_emit_ubo_as_plain_uniforms = false;
bool vulkan_glsl_disable_ext_samplerless_texture_functions = false; bool vulkan_glsl_disable_ext_samplerless_texture_functions = false;
@ -528,6 +529,7 @@ struct CLIArguments
SmallVector<uint32_t> msl_discrete_descriptor_sets; SmallVector<uint32_t> msl_discrete_descriptor_sets;
SmallVector<uint32_t> msl_device_argument_buffers; SmallVector<uint32_t> msl_device_argument_buffers;
SmallVector<pair<uint32_t, uint32_t>> msl_dynamic_buffers; SmallVector<pair<uint32_t, uint32_t>> msl_dynamic_buffers;
SmallVector<pair<uint32_t, uint32_t>> msl_inline_uniform_blocks;
SmallVector<PLSArg> pls_in; SmallVector<PLSArg> pls_in;
SmallVector<PLSArg> pls_out; SmallVector<PLSArg> pls_out;
SmallVector<Remap> remaps; SmallVector<Remap> remaps;
@ -611,7 +613,9 @@ static void print_help()
"\t[--msl-view-index-from-device-index]\n" "\t[--msl-view-index-from-device-index]\n"
"\t[--msl-dispatch-base]\n" "\t[--msl-dispatch-base]\n"
"\t[--msl-dynamic-buffer <set index> <binding>]\n" "\t[--msl-dynamic-buffer <set index> <binding>]\n"
"\t[--msl-inline-uniform-block <set index> <binding>]\n"
"\t[--msl-decoration-binding]\n" "\t[--msl-decoration-binding]\n"
"\t[--msl-force-active-argument-buffer-resources]\n"
"\t[--hlsl]\n" "\t[--hlsl]\n"
"\t[--reflect]\n" "\t[--reflect]\n"
"\t[--shader-model]\n" "\t[--shader-model]\n"
@ -801,6 +805,7 @@ static string compile_iteration(const CLIArguments &args, std::vector<uint32_t>
msl_opts.view_index_from_device_index = args.msl_view_index_from_device_index; msl_opts.view_index_from_device_index = args.msl_view_index_from_device_index;
msl_opts.dispatch_base = args.msl_dispatch_base; msl_opts.dispatch_base = args.msl_dispatch_base;
msl_opts.enable_decoration_binding = args.msl_decoration_binding; msl_opts.enable_decoration_binding = args.msl_decoration_binding;
msl_opts.force_active_argument_buffer_resources = args.msl_force_active_argument_buffer_resources;
msl_comp->set_msl_options(msl_opts); msl_comp->set_msl_options(msl_opts);
for (auto &v : args.msl_discrete_descriptor_sets) for (auto &v : args.msl_discrete_descriptor_sets)
msl_comp->add_discrete_descriptor_set(v); msl_comp->add_discrete_descriptor_set(v);
@ -809,6 +814,8 @@ static string compile_iteration(const CLIArguments &args, std::vector<uint32_t>
uint32_t i = 0; uint32_t i = 0;
for (auto &v : args.msl_dynamic_buffers) for (auto &v : args.msl_dynamic_buffers)
msl_comp->add_dynamic_buffer(v.first, v.second, i++); msl_comp->add_dynamic_buffer(v.first, v.second, i++);
for (auto &v : args.msl_inline_uniform_blocks)
msl_comp->add_inline_uniform_block(v.first, v.second);
} }
else if (args.hlsl) else if (args.hlsl)
compiler.reset(new CompilerHLSL(move(spirv_parser.get_parsed_ir()))); compiler.reset(new CompilerHLSL(move(spirv_parser.get_parsed_ir())));
@ -1148,6 +1155,15 @@ static int main_inner(int argc, char *argv[])
args.msl_dynamic_buffers.push_back(make_pair(desc_set, binding)); args.msl_dynamic_buffers.push_back(make_pair(desc_set, binding));
}); });
cbs.add("--msl-decoration-binding", [&args](CLIParser &) { args.msl_decoration_binding = true; }); cbs.add("--msl-decoration-binding", [&args](CLIParser &) { args.msl_decoration_binding = true; });
cbs.add("--msl-force-active-argument-buffer-resources",
[&args](CLIParser &) { args.msl_force_active_argument_buffer_resources = true; });
cbs.add("--msl-inline-uniform-block", [&args](CLIParser &parser) {
args.msl_argument_buffers = true;
// Make sure next_uint() is called in-order.
uint32_t desc_set = parser.next_uint();
uint32_t binding = parser.next_uint();
args.msl_inline_uniform_blocks.push_back(make_pair(desc_set, binding));
});
cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); }); cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
cbs.add("--rename-entry-point", [&args](CLIParser &parser) { cbs.add("--rename-entry-point", [&args](CLIParser &parser) {
auto old_name = parser.next_string(); auto old_name = parser.next_string();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016-2019 Arm Limited * Copyright 2016-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016-2019 Arm Limited * Copyright 2016-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -638,6 +638,7 @@ struct SPIREntryPoint
uint32_t invocations = 0; uint32_t invocations = 0;
uint32_t output_vertices = 0; uint32_t output_vertices = 0;
spv::ExecutionModel model = spv::ExecutionModelMax; spv::ExecutionModel model = spv::ExecutionModelMax;
bool geometry_passthrough = false;
}; };
struct SPIRExpression : IVariant struct SPIRExpression : IVariant
@ -975,6 +976,7 @@ struct SPIRAccessChain : IVariant
VariableID loaded_from = 0; VariableID loaded_from = 0;
uint32_t matrix_stride = 0; uint32_t matrix_stride = 0;
uint32_t array_stride = 0;
bool row_major_matrix = false; bool row_major_matrix = false;
bool immutable = false; bool immutable = false;
@ -1051,8 +1053,7 @@ struct SPIRConstant : IVariant
type = TypeConstant type = TypeConstant
}; };
union Constant union Constant {
{
uint32_t u32; uint32_t u32;
int32_t i32; int32_t i32;
float f32; float f32;
@ -1090,8 +1091,7 @@ struct SPIRConstant : IVariant
int e = (u16_value >> 10) & 0x1f; int e = (u16_value >> 10) & 0x1f;
int m = (u16_value >> 0) & 0x3ff; int m = (u16_value >> 0) & 0x3ff;
union union {
{
float f32; float f32;
uint32_t u32; uint32_t u32;
} u; } u;
@ -1571,6 +1571,8 @@ struct Meta
uint32_t set = 0; uint32_t set = 0;
uint32_t binding = 0; uint32_t binding = 0;
uint32_t offset = 0; uint32_t offset = 0;
uint32_t xfb_buffer = 0;
uint32_t xfb_stride = 0;
uint32_t array_stride = 0; uint32_t array_stride = 0;
uint32_t matrix_stride = 0; uint32_t matrix_stride = 0;
uint32_t input_attachment = 0; uint32_t input_attachment = 0;
@ -1695,6 +1697,62 @@ static inline bool opcode_is_sign_invariant(spv::Op opcode)
return false; return false;
} }
} }
struct SetBindingPair
{
uint32_t desc_set;
uint32_t binding;
inline bool operator==(const SetBindingPair &other) const
{
return desc_set == other.desc_set && binding == other.binding;
}
inline bool operator<(const SetBindingPair &other) const
{
return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding);
}
};
struct StageSetBinding
{
spv::ExecutionModel model;
uint32_t desc_set;
uint32_t binding;
inline bool operator==(const StageSetBinding &other) const
{
return model == other.model && desc_set == other.desc_set && binding == other.binding;
}
};
struct InternalHasher
{
inline size_t operator()(const SetBindingPair &value) const
{
// Quality of hash doesn't really matter here.
auto hash_set = std::hash<uint32_t>()(value.desc_set);
auto hash_binding = std::hash<uint32_t>()(value.binding);
return (hash_set * 0x10001b31) ^ hash_binding;
}
inline size_t operator()(const StageSetBinding &value) const
{
// Quality of hash doesn't really matter here.
auto hash_model = std::hash<uint32_t>()(value.model);
auto hash_set = std::hash<uint32_t>()(value.desc_set);
auto tmp_hash = (hash_model * 0x10001b31) ^ hash_set;
return (tmp_hash * 0x10001b31) ^ value.binding;
}
};
// Special constant used in a {MSL,HLSL}ResourceBinding desc_set
// element to indicate the bindings for the push constants.
static const uint32_t ResourceBindingPushConstantDescriptorSet = ~(0u);
// Special constant used in a {MSL,HLSL}ResourceBinding binding
// element to indicate the bindings for the push constants.
static const uint32_t ResourceBindingPushConstantBinding = 0;
} // namespace SPIRV_CROSS_NAMESPACE } // namespace SPIRV_CROSS_NAMESPACE
namespace std namespace std

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -317,6 +317,8 @@ void Compiler::register_write(uint32_t chain)
var = maybe_get<SPIRVariable>(access_chain->loaded_from); var = maybe_get<SPIRVariable>(access_chain->loaded_from);
} }
auto &chain_type = expression_type(chain);
if (var) if (var)
{ {
bool check_argument_storage_qualifier = true; bool check_argument_storage_qualifier = true;
@ -359,7 +361,7 @@ void Compiler::register_write(uint32_t chain)
force_recompile(); force_recompile();
} }
} }
else else if (chain_type.pointer)
{ {
// If we stored through a variable pointer, then we don't know which // If we stored through a variable pointer, then we don't know which
// variable we stored to. So *all* expressions after this point need to // variable we stored to. So *all* expressions after this point need to
@ -368,6 +370,9 @@ void Compiler::register_write(uint32_t chain)
// only certain variables, we can invalidate only those. // only certain variables, we can invalidate only those.
flush_all_active_variables(); flush_all_active_variables();
} }
// If chain_type.pointer is false, we're not writing to memory backed variables, but temporaries instead.
// This can happen in copy_logical_type where we unroll complex reads and writes to temporaries.
} }
void Compiler::flush_dependees(SPIRVariable &var) void Compiler::flush_dependees(SPIRVariable &var)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019 Hans-Kristian Arntzen * Copyright 2019-2020 Hans-Kristian Arntzen
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -593,6 +593,10 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c
case SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING: case SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING:
options->msl.enable_decoration_binding = value != 0; options->msl.enable_decoration_binding = value != 0;
break; break;
case SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES:
options->msl.force_active_argument_buffer_resources = value != 0;
break;
#endif #endif
default: default:
@ -789,6 +793,60 @@ spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler
#endif #endif
} }
spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler,
const spvc_hlsl_resource_binding *binding)
{
#if SPIRV_CROSS_C_API_HLSL
if (compiler->backend != SPVC_BACKEND_HLSL)
{
compiler->context->report_error("HLSL function used on a non-HLSL backend.");
return SPVC_ERROR_INVALID_ARGUMENT;
}
auto &hlsl = *static_cast<CompilerHLSL *>(compiler->compiler.get());
HLSLResourceBinding bind;
bind.binding = binding->binding;
bind.desc_set = binding->desc_set;
bind.stage = static_cast<spv::ExecutionModel>(binding->stage);
bind.cbv.register_binding = binding->cbv.register_binding;
bind.cbv.register_space = binding->cbv.register_space;
bind.uav.register_binding = binding->uav.register_binding;
bind.uav.register_space = binding->uav.register_space;
bind.srv.register_binding = binding->srv.register_binding;
bind.srv.register_space = binding->srv.register_space;
bind.sampler.register_binding = binding->sampler.register_binding;
bind.sampler.register_space = binding->sampler.register_space;
hlsl.add_hlsl_resource_binding(bind);
return SPVC_SUCCESS;
#else
(void)binding;
compiler->context->report_error("HLSL function used on a non-HLSL backend.");
return SPVC_ERROR_INVALID_ARGUMENT;
#endif
}
spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, SpvExecutionModel model, unsigned set,
unsigned binding)
{
#if SPIRV_CROSS_C_API_HLSL
if (compiler->backend != SPVC_BACKEND_HLSL)
{
compiler->context->report_error("HLSL function used on a non-HLSL backend.");
return SPVC_FALSE;
}
auto &hlsl = *static_cast<CompilerHLSL *>(compiler->compiler.get());
return hlsl.is_hlsl_resource_binding_used(static_cast<spv::ExecutionModel>(model), set, binding) ? SPVC_TRUE :
SPVC_FALSE;
#else
(void)model;
(void)set;
(void)binding;
compiler->context->report_error("HLSL function used on a non-HLSL backend.");
return SPVC_FALSE;
#endif
}
spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler) spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler)
{ {
#if SPIRV_CROSS_C_API_MSL #if SPIRV_CROSS_C_API_MSL
@ -971,6 +1029,26 @@ spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigne
#endif #endif
} }
spvc_result spvc_compiler_msl_add_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding)
{
#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());
msl.add_inline_uniform_block(desc_set, binding);
return SPVC_SUCCESS;
#else
(void)binding;
(void)desc_set;
compiler->context->report_error("MSL function used on a non-MSL backend.");
return SPVC_ERROR_INVALID_ARGUMENT;
#endif
}
spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set) spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set)
{ {
#if SPIRV_CROSS_C_API_MSL #if SPIRV_CROSS_C_API_MSL
@ -2157,6 +2235,26 @@ void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding)
#endif #endif
} }
void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding)
{
#if SPIRV_CROSS_C_API_HLSL
HLSLResourceBinding binding_default;
binding->desc_set = binding_default.desc_set;
binding->binding = binding_default.binding;
binding->cbv.register_binding = binding_default.cbv.register_binding;
binding->cbv.register_space = binding_default.cbv.register_space;
binding->srv.register_binding = binding_default.srv.register_binding;
binding->srv.register_space = binding_default.srv.register_space;
binding->uav.register_binding = binding_default.uav.register_binding;
binding->uav.register_space = binding_default.uav.register_space;
binding->sampler.register_binding = binding_default.sampler.register_binding;
binding->sampler.register_space = binding_default.sampler.register_space;
binding->stage = static_cast<SpvExecutionModel>(binding_default.stage);
#else
memset(binding, 0, sizeof(*binding));
#endif
}
void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler) void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler)
{ {
#if SPIRV_CROSS_C_API_MSL #if SPIRV_CROSS_C_API_MSL

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019 Hans-Kristian Arntzen * Copyright 2019-2020 Hans-Kristian Arntzen
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -33,7 +33,7 @@ extern "C" {
/* Bumped if ABI or API breaks backwards compatibility. */ /* Bumped if ABI or API breaks backwards compatibility. */
#define SPVC_C_API_VERSION_MAJOR 0 #define SPVC_C_API_VERSION_MAJOR 0
/* Bumped if APIs or enumerations are added in a backwards compatible way. */ /* Bumped if APIs or enumerations are added in a backwards compatible way. */
#define SPVC_C_API_VERSION_MINOR 21 #define SPVC_C_API_VERSION_MINOR 24
/* Bumped if internal implementation details change. */ /* Bumped if internal implementation details change. */
#define SPVC_C_API_VERSION_PATCH 0 #define SPVC_C_API_VERSION_PATCH 0
@ -469,6 +469,7 @@ SPVC_PUBLIC_API void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycb
/* Maps to C++ API. */ /* Maps to C++ API. */
typedef enum spvc_hlsl_binding_flag_bits typedef enum spvc_hlsl_binding_flag_bits
{ {
SPVC_HLSL_BINDING_AUTO_NONE_BIT = 0,
SPVC_HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, SPVC_HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0,
SPVC_HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, SPVC_HLSL_BINDING_AUTO_CBV_BIT = 1 << 1,
SPVC_HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, SPVC_HLSL_BINDING_AUTO_SRV_BIT = 1 << 2,
@ -478,6 +479,31 @@ typedef enum spvc_hlsl_binding_flag_bits
} spvc_hlsl_binding_flag_bits; } spvc_hlsl_binding_flag_bits;
typedef unsigned spvc_hlsl_binding_flags; typedef unsigned spvc_hlsl_binding_flags;
#define SPVC_HLSL_PUSH_CONSTANT_DESC_SET (~(0u))
#define SPVC_HLSL_PUSH_CONSTANT_BINDING (0)
/* Maps to C++ API. */
typedef struct spvc_hlsl_resource_binding_mapping
{
unsigned register_space;
unsigned register_binding;
} spvc_hlsl_resource_binding_mapping;
typedef struct spvc_hlsl_resource_binding
{
SpvExecutionModel stage;
unsigned desc_set;
unsigned binding;
spvc_hlsl_resource_binding_mapping cbv, uav, srv, sampler;
} spvc_hlsl_resource_binding;
/*
* Initializes the resource binding struct.
* The defaults are non-zero.
*/
SPVC_PUBLIC_API void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding);
/* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */ /* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */
typedef enum spvc_compiler_option typedef enum spvc_compiler_option
{ {
@ -545,6 +571,7 @@ typedef enum spvc_compiler_option
SPVC_COMPILER_OPTION_MSL_INVARIANT_FP_MATH = 47 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_MSL_INVARIANT_FP_MATH = 47 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_EMULATE_CUBEMAP_ARRAY = 48 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_MSL_EMULATE_CUBEMAP_ARRAY = 48 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING = 49 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING = 49 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES = 50 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
} spvc_compiler_option; } spvc_compiler_option;
@ -621,6 +648,13 @@ SPVC_PUBLIC_API spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin
SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler, SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler,
spvc_hlsl_binding_flags flags); spvc_hlsl_binding_flags flags);
SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler,
const spvc_hlsl_resource_binding *binding);
SPVC_PUBLIC_API spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler,
SpvExecutionModel model,
unsigned set,
unsigned binding);
/* /*
* MSL specifics. * MSL specifics.
* Maps to C++ API. * Maps to C++ API.
@ -657,6 +691,8 @@ SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding_second
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigned desc_set, unsigned binding, unsigned index); SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigned desc_set, unsigned binding, unsigned index);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding);
/* /*
* Reflect resources. * Reflect resources.
* Maps almost 1:1 to C++ API. * Maps almost 1:1 to C++ API.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019 Hans-Kristian Arntzen * Copyright 2019-2020 Hans-Kristian Arntzen
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -61,8 +61,7 @@ public:
private: private:
#if defined(_MSC_VER) && _MSC_VER < 1900 #if defined(_MSC_VER) && _MSC_VER < 1900
// MSVC 2013 workarounds, sigh ... // MSVC 2013 workarounds, sigh ...
union union {
{
char aligned_char[sizeof(T) * N]; char aligned_char[sizeof(T) * N];
double dummy_aligner; double dummy_aligner;
} u; } u;
@ -86,72 +85,72 @@ template <typename T>
class VectorView class VectorView
{ {
public: public:
T &operator[](size_t i) T &operator[](size_t i) SPIRV_CROSS_NOEXCEPT
{ {
return ptr[i]; return ptr[i];
} }
const T &operator[](size_t i) const const T &operator[](size_t i) const SPIRV_CROSS_NOEXCEPT
{ {
return ptr[i]; return ptr[i];
} }
bool empty() const bool empty() const SPIRV_CROSS_NOEXCEPT
{ {
return buffer_size == 0; return buffer_size == 0;
} }
size_t size() const size_t size() const SPIRV_CROSS_NOEXCEPT
{ {
return buffer_size; return buffer_size;
} }
T *data() T *data() SPIRV_CROSS_NOEXCEPT
{ {
return ptr; return ptr;
} }
const T *data() const const T *data() const SPIRV_CROSS_NOEXCEPT
{ {
return ptr; return ptr;
} }
T *begin() T *begin() SPIRV_CROSS_NOEXCEPT
{ {
return ptr; return ptr;
} }
T *end() T *end() SPIRV_CROSS_NOEXCEPT
{ {
return ptr + buffer_size; return ptr + buffer_size;
} }
const T *begin() const const T *begin() const SPIRV_CROSS_NOEXCEPT
{ {
return ptr; return ptr;
} }
const T *end() const const T *end() const SPIRV_CROSS_NOEXCEPT
{ {
return ptr + buffer_size; return ptr + buffer_size;
} }
T &front() T &front() SPIRV_CROSS_NOEXCEPT
{ {
return ptr[0]; return ptr[0];
} }
const T &front() const const T &front() const SPIRV_CROSS_NOEXCEPT
{ {
return ptr[0]; return ptr[0];
} }
T &back() T &back() SPIRV_CROSS_NOEXCEPT
{ {
return ptr[buffer_size - 1]; return ptr[buffer_size - 1];
} }
const T &back() const const T &back() const SPIRV_CROSS_NOEXCEPT
{ {
return ptr[buffer_size - 1]; return ptr[buffer_size - 1];
} }
@ -195,13 +194,13 @@ template <typename T, size_t N = 8>
class SmallVector : public VectorView<T> class SmallVector : public VectorView<T>
{ {
public: public:
SmallVector() SmallVector() SPIRV_CROSS_NOEXCEPT
{ {
this->ptr = stack_storage.data(); this->ptr = stack_storage.data();
buffer_capacity = N; buffer_capacity = N;
} }
SmallVector(const T *arg_list_begin, const T *arg_list_end) SmallVector(const T *arg_list_begin, const T *arg_list_end) SPIRV_CROSS_NOEXCEPT
: SmallVector() : SmallVector()
{ {
auto count = size_t(arg_list_end - arg_list_begin); auto count = size_t(arg_list_end - arg_list_begin);
@ -246,14 +245,17 @@ public:
return *this; return *this;
} }
SmallVector(const SmallVector &other) SmallVector(const SmallVector &other) SPIRV_CROSS_NOEXCEPT
: SmallVector() : SmallVector()
{ {
*this = other; *this = other;
} }
SmallVector &operator=(const SmallVector &other) SmallVector &operator=(const SmallVector &other) SPIRV_CROSS_NOEXCEPT
{ {
if (this == &other)
return *this;
clear(); clear();
reserve(other.buffer_size); reserve(other.buffer_size);
for (size_t i = 0; i < other.buffer_size; i++) for (size_t i = 0; i < other.buffer_size; i++)
@ -262,7 +264,7 @@ public:
return *this; return *this;
} }
explicit SmallVector(size_t count) explicit SmallVector(size_t count) SPIRV_CROSS_NOEXCEPT
: SmallVector() : SmallVector()
{ {
resize(count); resize(count);
@ -275,28 +277,28 @@ public:
free(this->ptr); free(this->ptr);
} }
void clear() void clear() SPIRV_CROSS_NOEXCEPT
{ {
for (size_t i = 0; i < this->buffer_size; i++) for (size_t i = 0; i < this->buffer_size; i++)
this->ptr[i].~T(); this->ptr[i].~T();
this->buffer_size = 0; this->buffer_size = 0;
} }
void push_back(const T &t) void push_back(const T &t) SPIRV_CROSS_NOEXCEPT
{ {
reserve(this->buffer_size + 1); reserve(this->buffer_size + 1);
new (&this->ptr[this->buffer_size]) T(t); new (&this->ptr[this->buffer_size]) T(t);
this->buffer_size++; this->buffer_size++;
} }
void push_back(T &&t) void push_back(T &&t) SPIRV_CROSS_NOEXCEPT
{ {
reserve(this->buffer_size + 1); reserve(this->buffer_size + 1);
new (&this->ptr[this->buffer_size]) T(std::move(t)); new (&this->ptr[this->buffer_size]) T(std::move(t));
this->buffer_size++; this->buffer_size++;
} }
void pop_back() void pop_back() SPIRV_CROSS_NOEXCEPT
{ {
// Work around false positive warning on GCC 8.3. // Work around false positive warning on GCC 8.3.
// Calling pop_back on empty vector is undefined. // Calling pop_back on empty vector is undefined.
@ -305,14 +307,14 @@ public:
} }
template <typename... Ts> template <typename... Ts>
void emplace_back(Ts &&... ts) void emplace_back(Ts &&... ts) SPIRV_CROSS_NOEXCEPT
{ {
reserve(this->buffer_size + 1); reserve(this->buffer_size + 1);
new (&this->ptr[this->buffer_size]) T(std::forward<Ts>(ts)...); new (&this->ptr[this->buffer_size]) T(std::forward<Ts>(ts)...);
this->buffer_size++; this->buffer_size++;
} }
void reserve(size_t count) void reserve(size_t count) SPIRV_CROSS_NOEXCEPT
{ {
if (count > buffer_capacity) if (count > buffer_capacity)
{ {
@ -328,8 +330,9 @@ public:
T *new_buffer = T *new_buffer =
target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data(); target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data();
// If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery.
if (!new_buffer) if (!new_buffer)
SPIRV_CROSS_THROW("Out of memory."); std::terminate();
// In case for some reason two allocations both come from same stack. // In case for some reason two allocations both come from same stack.
if (new_buffer != this->ptr) if (new_buffer != this->ptr)
@ -349,7 +352,7 @@ public:
} }
} }
void insert(T *itr, const T *insert_begin, const T *insert_end) void insert(T *itr, const T *insert_begin, const T *insert_end) SPIRV_CROSS_NOEXCEPT
{ {
auto count = size_t(insert_end - insert_begin); auto count = size_t(insert_end - insert_begin);
if (itr == this->end()) if (itr == this->end())
@ -375,8 +378,10 @@ public:
// Need to allocate new buffer. Move everything to a new buffer. // Need to allocate new buffer. Move everything to a new buffer.
T *new_buffer = T *new_buffer =
target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data(); target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data();
// If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery.
if (!new_buffer) if (!new_buffer)
SPIRV_CROSS_THROW("Out of memory."); std::terminate();
// First, move elements from source buffer to new buffer. // First, move elements from source buffer to new buffer.
// We don't deal with types which can throw in move constructor. // We don't deal with types which can throw in move constructor.
@ -448,19 +453,19 @@ public:
} }
} }
void insert(T *itr, const T &value) void insert(T *itr, const T &value) SPIRV_CROSS_NOEXCEPT
{ {
insert(itr, &value, &value + 1); insert(itr, &value, &value + 1);
} }
T *erase(T *itr) T *erase(T *itr) SPIRV_CROSS_NOEXCEPT
{ {
std::move(itr + 1, this->end(), itr); std::move(itr + 1, this->end(), itr);
this->ptr[--this->buffer_size].~T(); this->ptr[--this->buffer_size].~T();
return itr; return itr;
} }
void erase(T *start_erase, T *end_erase) void erase(T *start_erase, T *end_erase) SPIRV_CROSS_NOEXCEPT
{ {
if (end_erase == this->end()) if (end_erase == this->end())
{ {
@ -474,7 +479,7 @@ public:
} }
} }
void resize(size_t new_size) void resize(size_t new_size) SPIRV_CROSS_NOEXCEPT
{ {
if (new_size < this->buffer_size) if (new_size < this->buffer_size)
{ {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2019 Arm Limited * Copyright 2018-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -128,6 +128,17 @@ void ParsedIR::set_id_bounds(uint32_t bounds)
block_meta.resize(bounds); block_meta.resize(bounds);
} }
// Roll our own versions of these functions to avoid potential locale shenanigans.
static bool is_alpha(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
static bool is_alphanumeric(char c)
{
return is_alpha(c) || (c >= '0' && c <= '9');
}
static string ensure_valid_identifier(const string &name, bool member) static string ensure_valid_identifier(const string &name, bool member)
{ {
// Functions in glslangValidator are mangled with name(<mangled> stuff. // Functions in glslangValidator are mangled with name(<mangled> stuff.
@ -143,20 +154,20 @@ static string ensure_valid_identifier(const string &name, bool member)
// _m<num> variables are reserved by the internal implementation, // _m<num> variables are reserved by the internal implementation,
// otherwise, make sure the name is a valid identifier. // otherwise, make sure the name is a valid identifier.
if (i == 0) if (i == 0)
c = isalpha(c) ? c : '_'; c = is_alpha(c) ? c : '_';
else if (i == 2 && str[0] == '_' && str[1] == 'm') else if (i == 2 && str[0] == '_' && str[1] == 'm')
c = isalpha(c) ? c : '_'; c = is_alpha(c) ? c : '_';
else else
c = isalnum(c) ? c : '_'; c = is_alphanumeric(c) ? c : '_';
} }
else else
{ {
// _<num> variables are reserved by the internal implementation, // _<num> variables are reserved by the internal implementation,
// otherwise, make sure the name is a valid identifier. // otherwise, make sure the name is a valid identifier.
if (i == 0 || (str[0] == '_' && i == 1)) if (i == 0 || (str[0] == '_' && i == 1))
c = isalpha(c) ? c : '_'; c = is_alpha(c) ? c : '_';
else else
c = isalnum(c) ? c : '_'; c = is_alphanumeric(c) ? c : '_';
} }
} }
return str; return str;
@ -255,6 +266,14 @@ void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument)
dec.offset = argument; dec.offset = argument;
break; break;
case DecorationXfbBuffer:
dec.xfb_buffer = argument;
break;
case DecorationXfbStride:
dec.xfb_stride = argument;
break;
case DecorationArrayStride: case DecorationArrayStride:
dec.array_stride = argument; dec.array_stride = argument;
break; break;
@ -326,6 +345,14 @@ void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decor
dec.offset = argument; dec.offset = argument;
break; break;
case DecorationXfbBuffer:
dec.xfb_buffer = argument;
break;
case DecorationXfbStride:
dec.xfb_stride = argument;
break;
case DecorationSpecId: case DecorationSpecId:
dec.spec_id = argument; dec.spec_id = argument;
break; break;
@ -439,6 +466,10 @@ uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const
return dec.component; return dec.component;
case DecorationOffset: case DecorationOffset:
return dec.offset; return dec.offset;
case DecorationXfbBuffer:
return dec.xfb_buffer;
case DecorationXfbStride:
return dec.xfb_stride;
case DecorationBinding: case DecorationBinding:
return dec.binding; return dec.binding;
case DecorationDescriptorSet: case DecorationDescriptorSet:
@ -503,6 +534,14 @@ void ParsedIR::unset_decoration(ID id, Decoration decoration)
dec.offset = 0; dec.offset = 0;
break; break;
case DecorationXfbBuffer:
dec.xfb_buffer = 0;
break;
case DecorationXfbStride:
dec.xfb_stride = 0;
break;
case DecorationBinding: case DecorationBinding:
dec.binding = 0; dec.binding = 0;
break; break;
@ -573,6 +612,10 @@ uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration d
return dec.binding; return dec.binding;
case DecorationOffset: case DecorationOffset:
return dec.offset; return dec.offset;
case DecorationXfbBuffer:
return dec.xfb_buffer;
case DecorationXfbStride:
return dec.xfb_stride;
case DecorationSpecId: case DecorationSpecId:
return dec.spec_id; return dec.spec_id;
case DecorationIndex: case DecorationIndex:
@ -661,6 +704,14 @@ void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration dec
dec.offset = 0; dec.offset = 0;
break; break;
case DecorationXfbBuffer:
dec.xfb_buffer = 0;
break;
case DecorationXfbStride:
dec.xfb_stride = 0;
break;
case DecorationSpecId: case DecorationSpecId:
dec.spec_id = 0; dec.spec_id = 0;
break; break;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2019 Arm Limited * Copyright 2018-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015-2019 Arm Limited * Copyright 2015-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -56,7 +56,8 @@ enum AccessChainFlagBits
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT = 1 << 0, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT = 1 << 0,
ACCESS_CHAIN_CHAIN_ONLY_BIT = 1 << 1, ACCESS_CHAIN_CHAIN_ONLY_BIT = 1 << 1,
ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2, ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2,
ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3 ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3,
ACCESS_CHAIN_LITERAL_MSB_FORCE_ID = 1 << 4
}; };
typedef uint32_t AccessChainFlags; typedef uint32_t AccessChainFlags;
@ -275,6 +276,9 @@ protected:
virtual bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const; virtual bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const;
void emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, uint32_t rhs_id, uint32_t rhs_type_id,
SmallVector<uint32_t> chain);
StringStream<> buffer; StringStream<> buffer;
template <typename T> template <typename T>
@ -427,6 +431,7 @@ protected:
void emit_buffer_block_legacy(const SPIRVariable &var); void emit_buffer_block_legacy(const SPIRVariable &var);
void emit_buffer_block_flattened(const SPIRVariable &type); void emit_buffer_block_flattened(const SPIRVariable &type);
void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model); void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model);
bool should_force_emit_builtin_block(spv::StorageClass storage);
void emit_push_constant_block_vulkan(const SPIRVariable &var); void emit_push_constant_block_vulkan(const SPIRVariable &var);
void emit_push_constant_block_glsl(const SPIRVariable &var); void emit_push_constant_block_glsl(const SPIRVariable &var);
void emit_interface_block(const SPIRVariable &type); void emit_interface_block(const SPIRVariable &type);
@ -463,6 +468,8 @@ protected:
SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type); SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type);
void emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, void emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op,
SPIRType::BaseType input_type, bool skip_cast_if_equal_type); SPIRType::BaseType input_type, bool skip_cast_if_equal_type);
void emit_binary_func_op_cast_clustered(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
const char *op, SPIRType::BaseType input_type);
void emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, void emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2,
const char *op, SPIRType::BaseType input_type); const char *op, SPIRType::BaseType input_type);
void emit_trinary_func_op_bitextract(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, void emit_trinary_func_op_bitextract(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
@ -503,7 +510,7 @@ protected:
std::string flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count, std::string flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count,
const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride,
bool need_transpose); uint32_t array_stride, bool need_transpose);
std::string flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count, std::string flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count,
const SPIRType &target_type, uint32_t offset); const SPIRType &target_type, uint32_t offset);
std::string flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count, std::string flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count,
@ -516,6 +523,7 @@ protected:
uint32_t count, uint32_t offset, uint32_t count, uint32_t offset,
uint32_t word_stride, bool *need_transpose = nullptr, uint32_t word_stride, bool *need_transpose = nullptr,
uint32_t *matrix_stride = nullptr, uint32_t *matrix_stride = nullptr,
uint32_t *array_stride = nullptr,
bool ptr_chain = false); bool ptr_chain = false);
const char *index_to_swizzle(uint32_t index); const char *index_to_swizzle(uint32_t index);
@ -583,6 +591,7 @@ protected:
bool check_atomic_image(uint32_t id); bool check_atomic_image(uint32_t id);
virtual void replace_illegal_names(); virtual void replace_illegal_names();
void replace_illegal_names(const std::unordered_set<std::string> &keywords);
virtual void emit_entry_point_declarations(); virtual void emit_entry_point_declarations();
void replace_fragment_output(SPIRVariable &var); void replace_fragment_output(SPIRVariable &var);
@ -705,6 +714,8 @@ protected:
void propagate_nonuniform_qualifier(uint32_t id); void propagate_nonuniform_qualifier(uint32_t id);
static const char *vector_swizzle(int vecsize, int index);
private: private:
void init(); void init();
}; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016-2019 Robert Konrad * Copyright 2016-2020 Robert Konrad
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -1113,15 +1113,7 @@ void CompilerHLSL::replace_illegal_names()
"line", "linear", "matrix", "point", "row_major", "sampler", "line", "linear", "matrix", "point", "row_major", "sampler",
}; };
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) { CompilerGLSL::replace_illegal_names(keywords);
if (!is_hidden_variable(var))
{
auto &m = ir.meta[var.self].decoration;
if (keywords.find(m.alias) != end(keywords))
m.alias = join("_", m.alias);
}
});
CompilerGLSL::replace_illegal_names(); CompilerGLSL::replace_illegal_names();
} }
@ -2934,16 +2926,15 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
string CompilerHLSL::to_resource_binding(const SPIRVariable &var) string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
{ {
// TODO: Basic implementation, might need special consideration for RW/RO structured buffers, const auto &type = get<SPIRType>(var.basetype);
// RW/RO images, and so on.
if (!has_decoration(var.self, DecorationBinding)) // We can remap push constant blocks, even if they don't have any binding decoration.
if (type.storage != StorageClassPushConstant && !has_decoration(var.self, DecorationBinding))
return ""; return "";
const auto &type = get<SPIRType>(var.basetype);
char space = '\0'; char space = '\0';
HLSLBindingFlags resource_flags = 0; HLSLBindingFlagBits resource_flags = HLSL_BINDING_AUTO_NONE_BIT;
switch (type.basetype) switch (type.basetype)
{ {
@ -3011,8 +3002,16 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
if (!space) if (!space)
return ""; return "";
return to_resource_register(resource_flags, space, get_decoration(var.self, DecorationBinding), uint32_t desc_set =
get_decoration(var.self, DecorationDescriptorSet)); resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantDescriptorSet : 0u;
uint32_t binding = resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantBinding : 0u;
if (has_decoration(var.self, DecorationBinding))
binding = get_decoration(var.self, DecorationBinding);
if (has_decoration(var.self, DecorationDescriptorSet))
desc_set = get_decoration(var.self, DecorationDescriptorSet);
return to_resource_register(resource_flags, space, binding, desc_set);
} }
string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var) string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var)
@ -3025,10 +3024,54 @@ string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var)
get_decoration(var.self, DecorationDescriptorSet)); get_decoration(var.self, DecorationDescriptorSet));
} }
string CompilerHLSL::to_resource_register(uint32_t flags, char space, uint32_t binding, uint32_t space_set) void CompilerHLSL::remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding)
{ {
if ((flags & resource_binding_flags) == 0) auto itr = resource_bindings.find({ get_execution_model(), desc_set, binding });
if (itr != end(resource_bindings))
{ {
auto &remap = itr->second;
remap.second = true;
switch (type)
{
case HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT:
case HLSL_BINDING_AUTO_CBV_BIT:
desc_set = remap.first.cbv.register_space;
binding = remap.first.cbv.register_binding;
break;
case HLSL_BINDING_AUTO_SRV_BIT:
desc_set = remap.first.srv.register_space;
binding = remap.first.srv.register_binding;
break;
case HLSL_BINDING_AUTO_SAMPLER_BIT:
desc_set = remap.first.sampler.register_space;
binding = remap.first.sampler.register_binding;
break;
case HLSL_BINDING_AUTO_UAV_BIT:
desc_set = remap.first.uav.register_space;
binding = remap.first.uav.register_binding;
break;
default:
break;
}
}
}
string CompilerHLSL::to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t space_set)
{
if ((flag & resource_binding_flags) == 0)
{
remap_hlsl_resource_binding(flag, space_set, binding);
// The push constant block did not have a binding, and there were no remap for it,
// so, declare without register binding.
if (flag == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT && space_set == ResourceBindingPushConstantDescriptorSet)
return "";
if (hlsl_options.shader_model >= 51) if (hlsl_options.shader_model >= 51)
return join(" : register(", space, binding, ", space", space_set, ")"); return join(" : register(", space, binding, ", space", space_set, ")");
else else
@ -3401,7 +3444,57 @@ void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
} }
} }
string CompilerHLSL::read_access_chain(const SPIRAccessChain &chain) void CompilerHLSL::read_access_chain_array(const string &lhs, const SPIRAccessChain &chain)
{
auto &type = get<SPIRType>(chain.basetype);
// Need to use a reserved identifier here since it might shadow an identifier in the access chain input or other loops.
auto ident = get_unique_identifier();
statement("[unroll]");
statement("for (int ", ident, " = 0; ", ident, " < ", to_array_size(type, uint32_t(type.array.size() - 1)), "; ",
ident, "++)");
begin_scope();
auto subchain = chain;
subchain.dynamic_index = join(ident, " * ", chain.array_stride, " + ", chain.dynamic_index);
subchain.basetype = type.parent_type;
if (!get<SPIRType>(subchain.basetype).array.empty())
subchain.array_stride = get_decoration(subchain.basetype, DecorationArrayStride);
read_access_chain(nullptr, join(lhs, "[", ident, "]"), subchain);
end_scope();
}
void CompilerHLSL::read_access_chain_struct(const string &lhs, const SPIRAccessChain &chain)
{
auto &type = get<SPIRType>(chain.basetype);
auto subchain = chain;
uint32_t member_count = uint32_t(type.member_types.size());
for (uint32_t i = 0; i < member_count; i++)
{
uint32_t offset = type_struct_member_offset(type, i);
subchain.static_index = chain.static_index + offset;
subchain.basetype = type.member_types[i];
subchain.matrix_stride = 0;
subchain.array_stride = 0;
subchain.row_major_matrix = false;
auto &member_type = get<SPIRType>(subchain.basetype);
if (member_type.columns > 1)
{
subchain.matrix_stride = type_struct_member_matrix_stride(type, i);
subchain.row_major_matrix = has_member_decoration(type.self, i, DecorationRowMajor);
}
if (!member_type.array.empty())
subchain.array_stride = type_struct_member_array_stride(type, i);
read_access_chain(nullptr, join(lhs, ".", to_member_name(type, i)), subchain);
}
}
void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIRAccessChain &chain)
{ {
auto &type = get<SPIRType>(chain.basetype); auto &type = get<SPIRType>(chain.basetype);
@ -3410,14 +3503,18 @@ string CompilerHLSL::read_access_chain(const SPIRAccessChain &chain)
target_type.vecsize = type.vecsize; target_type.vecsize = type.vecsize;
target_type.columns = type.columns; target_type.columns = type.columns;
if (type.basetype == SPIRType::Struct)
SPIRV_CROSS_THROW("Reading structs from ByteAddressBuffer not yet supported.");
if (type.width != 32)
SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported.");
if (!type.array.empty()) if (!type.array.empty())
SPIRV_CROSS_THROW("Reading arrays from ByteAddressBuffer not yet supported."); {
read_access_chain_array(lhs, chain);
return;
}
else if (type.basetype == SPIRType::Struct)
{
read_access_chain_struct(lhs, chain);
return;
}
else if (type.width != 32)
SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported.");
string load_expr; string load_expr;
@ -3525,7 +3622,13 @@ string CompilerHLSL::read_access_chain(const SPIRAccessChain &chain)
if (!bitcast_op.empty()) if (!bitcast_op.empty())
load_expr = join(bitcast_op, "(", load_expr, ")"); load_expr = join(bitcast_op, "(", load_expr, ")");
return load_expr; if (lhs.empty())
{
assert(expr);
*expr = move(load_expr);
}
else
statement(lhs, " = ", load_expr, ";");
} }
void CompilerHLSL::emit_load(const Instruction &instruction) void CompilerHLSL::emit_load(const Instruction &instruction)
@ -3542,33 +3645,138 @@ void CompilerHLSL::emit_load(const Instruction &instruction)
if (has_decoration(ptr, DecorationNonUniformEXT)) if (has_decoration(ptr, DecorationNonUniformEXT))
propagate_nonuniform_qualifier(ptr); propagate_nonuniform_qualifier(ptr);
auto load_expr = read_access_chain(*chain);
bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries);
// If we are forwarding this load,
// don't register the read to access chain here, defer that to when we actually use the expression,
// using the add_implied_read_expression mechanism.
if (!forward)
track_expression_read(chain->self);
// Do not forward complex load sequences like matrices, structs and arrays.
auto &type = get<SPIRType>(result_type); auto &type = get<SPIRType>(result_type);
if (type.columns > 1 || !type.array.empty() || type.basetype == SPIRType::Struct) bool composite_load = !type.array.empty() || type.basetype == SPIRType::Struct;
forward = false;
auto &e = emit_op(result_type, id, load_expr, forward, true); if (composite_load)
e.need_transpose = false; {
register_read(id, ptr, forward); // We cannot make this work in one single expression as we might have nested structures and arrays,
inherit_expression_dependencies(id, ptr); // so unroll the load to an uninitialized temporary.
if (forward) emit_uninitialized_temporary_expression(result_type, id);
add_implied_read_expression(e, chain->self); read_access_chain(nullptr, to_expression(id), *chain);
track_expression_read(chain->self);
}
else
{
string load_expr;
read_access_chain(&load_expr, "", *chain);
bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries);
// If we are forwarding this load,
// don't register the read to access chain here, defer that to when we actually use the expression,
// using the add_implied_read_expression mechanism.
if (!forward)
track_expression_read(chain->self);
// Do not forward complex load sequences like matrices, structs and arrays.
if (type.columns > 1)
forward = false;
auto &e = emit_op(result_type, id, load_expr, forward, true);
e.need_transpose = false;
register_read(id, ptr, forward);
inherit_expression_dependencies(id, ptr);
if (forward)
add_implied_read_expression(e, chain->self);
}
} }
else else
CompilerGLSL::emit_instruction(instruction); CompilerGLSL::emit_instruction(instruction);
} }
void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t value) void CompilerHLSL::write_access_chain_array(const SPIRAccessChain &chain, uint32_t value,
const SmallVector<uint32_t> &composite_chain)
{
auto &type = get<SPIRType>(chain.basetype);
// Need to use a reserved identifier here since it might shadow an identifier in the access chain input or other loops.
auto ident = get_unique_identifier();
uint32_t id = ir.increase_bound_by(2);
uint32_t int_type_id = id + 1;
SPIRType int_type;
int_type.basetype = SPIRType::Int;
int_type.width = 32;
set<SPIRType>(int_type_id, int_type);
set<SPIRExpression>(id, ident, int_type_id, true);
set_name(id, ident);
suppressed_usage_tracking.insert(id);
statement("[unroll]");
statement("for (int ", ident, " = 0; ", ident, " < ", to_array_size(type, uint32_t(type.array.size() - 1)), "; ",
ident, "++)");
begin_scope();
auto subchain = chain;
subchain.dynamic_index = join(ident, " * ", chain.array_stride, " + ", chain.dynamic_index);
subchain.basetype = type.parent_type;
// Forcefully allow us to use an ID here by setting MSB.
auto subcomposite_chain = composite_chain;
subcomposite_chain.push_back(0x80000000u | id);
if (!get<SPIRType>(subchain.basetype).array.empty())
subchain.array_stride = get_decoration(subchain.basetype, DecorationArrayStride);
write_access_chain(subchain, value, subcomposite_chain);
end_scope();
}
void CompilerHLSL::write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value,
const SmallVector<uint32_t> &composite_chain)
{
auto &type = get<SPIRType>(chain.basetype);
uint32_t member_count = uint32_t(type.member_types.size());
auto subchain = chain;
auto subcomposite_chain = composite_chain;
subcomposite_chain.push_back(0);
for (uint32_t i = 0; i < member_count; i++)
{
uint32_t offset = type_struct_member_offset(type, i);
subchain.static_index = chain.static_index + offset;
subchain.basetype = type.member_types[i];
subchain.matrix_stride = 0;
subchain.array_stride = 0;
subchain.row_major_matrix = false;
auto &member_type = get<SPIRType>(subchain.basetype);
if (member_type.columns > 1)
{
subchain.matrix_stride = type_struct_member_matrix_stride(type, i);
subchain.row_major_matrix = has_member_decoration(type.self, i, DecorationRowMajor);
}
if (!member_type.array.empty())
subchain.array_stride = type_struct_member_array_stride(type, i);
subcomposite_chain.back() = i;
write_access_chain(subchain, value, subcomposite_chain);
}
}
string CompilerHLSL::write_access_chain_value(uint32_t value, const SmallVector<uint32_t> &composite_chain,
bool enclose)
{
string ret;
if (composite_chain.empty())
ret = to_expression(value);
else
{
AccessChainMeta meta;
ret = access_chain_internal(value, composite_chain.data(), uint32_t(composite_chain.size()),
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_LITERAL_MSB_FORCE_ID, &meta);
}
if (enclose)
ret = enclose_expression(ret);
return ret;
}
void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t value,
const SmallVector<uint32_t> &composite_chain)
{ {
auto &type = get<SPIRType>(chain.basetype); auto &type = get<SPIRType>(chain.basetype);
@ -3583,12 +3791,20 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
target_type.vecsize = type.vecsize; target_type.vecsize = type.vecsize;
target_type.columns = type.columns; target_type.columns = type.columns;
if (type.basetype == SPIRType::Struct)
SPIRV_CROSS_THROW("Writing structs to RWByteAddressBuffer not yet supported.");
if (type.width != 32)
SPIRV_CROSS_THROW("Writing types other than 32-bit to RWByteAddressBuffer not yet supported.");
if (!type.array.empty()) if (!type.array.empty())
SPIRV_CROSS_THROW("Reading arrays from ByteAddressBuffer not yet supported."); {
write_access_chain_array(chain, value, composite_chain);
register_write(chain.self);
return;
}
else if (type.basetype == SPIRType::Struct)
{
write_access_chain_struct(chain, value, composite_chain);
register_write(chain.self);
return;
}
else if (type.width != 32)
SPIRV_CROSS_THROW("Writing types other than 32-bit to RWByteAddressBuffer not yet supported.");
if (type.columns == 1 && !chain.row_major_matrix) if (type.columns == 1 && !chain.row_major_matrix)
{ {
@ -3611,7 +3827,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
SPIRV_CROSS_THROW("Unknown vector size."); SPIRV_CROSS_THROW("Unknown vector size.");
} }
auto store_expr = to_expression(value); auto store_expr = write_access_chain_value(value, composite_chain, false);
auto bitcast_op = bitcast_glsl_op(target_type, type); auto bitcast_op = bitcast_glsl_op(target_type, type);
if (!bitcast_op.empty()) if (!bitcast_op.empty())
store_expr = join(bitcast_op, "(", store_expr, ")"); store_expr = join(bitcast_op, "(", store_expr, ")");
@ -3622,7 +3838,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
// Strided store. // Strided store.
for (uint32_t r = 0; r < type.vecsize; r++) for (uint32_t r = 0; r < type.vecsize; r++)
{ {
auto store_expr = to_enclosed_expression(value); auto store_expr = write_access_chain_value(value, composite_chain, true);
if (type.vecsize > 1) if (type.vecsize > 1)
{ {
store_expr += "."; store_expr += ".";
@ -3660,7 +3876,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
for (uint32_t c = 0; c < type.columns; c++) for (uint32_t c = 0; c < type.columns; c++)
{ {
auto store_expr = join(to_enclosed_expression(value), "[", c, "]"); auto store_expr = join(write_access_chain_value(value, composite_chain, true), "[", c, "]");
auto bitcast_op = bitcast_glsl_op(target_type, type); auto bitcast_op = bitcast_glsl_op(target_type, type);
if (!bitcast_op.empty()) if (!bitcast_op.empty())
store_expr = join(bitcast_op, "(", store_expr, ")"); store_expr = join(bitcast_op, "(", store_expr, ")");
@ -3674,7 +3890,8 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
{ {
for (uint32_t c = 0; c < type.columns; c++) for (uint32_t c = 0; c < type.columns; c++)
{ {
auto store_expr = join(to_enclosed_expression(value), "[", c, "].", index_to_swizzle(r)); auto store_expr =
join(write_access_chain_value(value, composite_chain, true), "[", c, "].", index_to_swizzle(r));
remove_duplicate_swizzle(store_expr); remove_duplicate_swizzle(store_expr);
auto bitcast_op = bitcast_glsl_op(target_type, type); auto bitcast_op = bitcast_glsl_op(target_type, type);
if (!bitcast_op.empty()) if (!bitcast_op.empty())
@ -3693,7 +3910,7 @@ void CompilerHLSL::emit_store(const Instruction &instruction)
auto ops = stream(instruction); auto ops = stream(instruction);
auto *chain = maybe_get<SPIRAccessChain>(ops[0]); auto *chain = maybe_get<SPIRAccessChain>(ops[0]);
if (chain) if (chain)
write_access_chain(*chain, ops[1]); write_access_chain(*chain, ops[1], {});
else else
CompilerGLSL::emit_instruction(instruction); CompilerGLSL::emit_instruction(instruction);
} }
@ -3723,7 +3940,10 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction)
if (need_byte_access_chain) if (need_byte_access_chain)
{ {
uint32_t to_plain_buffer_length = static_cast<uint32_t>(type.array.size()); // If we have a chain variable, we are already inside the SSBO, and any array type will refer to arrays within a block,
// and not array of SSBO.
uint32_t to_plain_buffer_length = chain ? 0u : static_cast<uint32_t>(type.array.size());
auto *backing_variable = maybe_get_backing_variable(ops[2]); auto *backing_variable = maybe_get_backing_variable(ops[2]);
string base; string base;
@ -3745,6 +3965,7 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction)
} }
uint32_t matrix_stride = 0; uint32_t matrix_stride = 0;
uint32_t array_stride = 0;
bool row_major_matrix = false; bool row_major_matrix = false;
// Inherit matrix information. // Inherit matrix information.
@ -3752,15 +3973,17 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction)
{ {
matrix_stride = chain->matrix_stride; matrix_stride = chain->matrix_stride;
row_major_matrix = chain->row_major_matrix; row_major_matrix = chain->row_major_matrix;
array_stride = chain->array_stride;
} }
auto offsets = auto offsets = flattened_access_chain_offset(*basetype, &ops[3 + to_plain_buffer_length],
flattened_access_chain_offset(*basetype, &ops[3 + to_plain_buffer_length], length - 3 - to_plain_buffer_length, 0, 1, &row_major_matrix,
length - 3 - to_plain_buffer_length, 0, 1, &row_major_matrix, &matrix_stride); &matrix_stride, &array_stride);
auto &e = set<SPIRAccessChain>(ops[1], ops[0], type.storage, base, offsets.first, offsets.second); auto &e = set<SPIRAccessChain>(ops[1], ops[0], type.storage, base, offsets.first, offsets.second);
e.row_major_matrix = row_major_matrix; e.row_major_matrix = row_major_matrix;
e.matrix_stride = matrix_stride; e.matrix_stride = matrix_stride;
e.array_stride = array_stride;
e.immutable = should_forward(ops[2]); e.immutable = should_forward(ops[2]);
e.loaded_from = backing_variable ? backing_variable->self : ID(0); e.loaded_from = backing_variable ? backing_variable->self : ID(0);
@ -3899,6 +4122,11 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i)
return join(expr, " * ", to_expression(ops[4])); return join(expr, " * ", to_expression(ops[4]));
}; };
// If we need to do implicit bitcasts, make sure we do it with the correct type.
uint32_t integer_width = get_integer_width_for_instruction(i);
auto int_type = to_signed_basetype(integer_width);
auto uint_type = to_unsigned_basetype(integer_width);
#define make_inclusive_BitAnd(expr) "" #define make_inclusive_BitAnd(expr) ""
#define make_inclusive_BitOr(expr) "" #define make_inclusive_BitOr(expr) ""
#define make_inclusive_BitXor(expr) "" #define make_inclusive_BitXor(expr) ""
@ -4007,20 +4235,34 @@ case OpGroupNonUniform##op: \
SPIRV_CROSS_THROW("Invalid group operation."); \ SPIRV_CROSS_THROW("Invalid group operation."); \
break; \ break; \
} }
#define HLSL_GROUP_OP_CAST(op, hlsl_op, type) \
case OpGroupNonUniform##op: \
{ \
auto operation = static_cast<GroupOperation>(ops[3]); \
if (operation == GroupOperationReduce) \
emit_unary_func_op_cast(result_type, id, ops[4], "WaveActive" #hlsl_op, type, type); \
else \
SPIRV_CROSS_THROW("Invalid group operation."); \
break; \
}
HLSL_GROUP_OP(FAdd, Sum, true) HLSL_GROUP_OP(FAdd, Sum, true)
HLSL_GROUP_OP(FMul, Product, true) HLSL_GROUP_OP(FMul, Product, true)
HLSL_GROUP_OP(FMin, Min, false) HLSL_GROUP_OP(FMin, Min, false)
HLSL_GROUP_OP(FMax, Max, false) HLSL_GROUP_OP(FMax, Max, false)
HLSL_GROUP_OP(IAdd, Sum, true) HLSL_GROUP_OP(IAdd, Sum, true)
HLSL_GROUP_OP(IMul, Product, true) HLSL_GROUP_OP(IMul, Product, true)
HLSL_GROUP_OP(SMin, Min, false) HLSL_GROUP_OP_CAST(SMin, Min, int_type)
HLSL_GROUP_OP(SMax, Max, false) HLSL_GROUP_OP_CAST(SMax, Max, int_type)
HLSL_GROUP_OP(UMin, Min, false) HLSL_GROUP_OP_CAST(UMin, Min, uint_type)
HLSL_GROUP_OP(UMax, Max, false) HLSL_GROUP_OP_CAST(UMax, Max, uint_type)
HLSL_GROUP_OP(BitwiseAnd, BitAnd, false) HLSL_GROUP_OP(BitwiseAnd, BitAnd, false)
HLSL_GROUP_OP(BitwiseOr, BitOr, false) HLSL_GROUP_OP(BitwiseOr, BitOr, false)
HLSL_GROUP_OP(BitwiseXor, BitXor, false) HLSL_GROUP_OP(BitwiseXor, BitXor, false)
#undef HLSL_GROUP_OP #undef HLSL_GROUP_OP
#undef HLSL_GROUP_OP_CAST
// clang-format on // clang-format on
case OpGroupNonUniformQuadSwap: case OpGroupNonUniformQuadSwap:
@ -5030,3 +5272,21 @@ void CompilerHLSL::emit_block_hints(const SPIRBlock &block)
break; break;
} }
} }
string CompilerHLSL::get_unique_identifier()
{
return join("_", unique_identifier_count++, "ident");
}
void CompilerHLSL::add_hlsl_resource_binding(const HLSLResourceBinding &binding)
{
StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding };
resource_bindings[tuple] = { binding, false };
}
bool CompilerHLSL::is_hlsl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
{
StageSetBinding tuple = { model, desc_set, binding };
auto itr = resource_bindings.find(tuple);
return itr != end(resource_bindings) && itr->second.second;
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016-2019 Robert Konrad * Copyright 2016-2020 Robert Konrad
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -44,6 +44,8 @@ struct RootConstants
// For finer control, decorations may be removed from specific resources instead with unset_decoration(). // For finer control, decorations may be removed from specific resources instead with unset_decoration().
enum HLSLBindingFlagBits enum HLSLBindingFlagBits
{ {
HLSL_BINDING_AUTO_NONE_BIT = 0,
// Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration. // Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration.
// A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land. // A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land.
// Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it. // Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it.
@ -67,6 +69,28 @@ enum HLSLBindingFlagBits
}; };
using HLSLBindingFlags = uint32_t; using HLSLBindingFlags = uint32_t;
// By matching stage, desc_set and binding for a SPIR-V resource,
// register bindings are set based on whether the HLSL resource is a
// CBV, UAV, SRV or Sampler. A single binding in SPIR-V might contain multiple
// resource types, e.g. COMBINED_IMAGE_SAMPLER, and SRV/Sampler bindings will be used respectively.
// On SM 5.0 and lower, register_space is ignored.
//
// To remap a push constant block which does not have any desc_set/binding associated with it,
// use ResourceBindingPushConstant{DescriptorSet,Binding} as values for desc_set/binding.
// For deeper control of push constants, set_root_constant_layouts() can be used instead.
struct HLSLResourceBinding
{
spv::ExecutionModel stage = spv::ExecutionModelMax;
uint32_t desc_set = 0;
uint32_t binding = 0;
struct Binding
{
uint32_t register_space = 0;
uint32_t register_binding = 0;
} cbv, uav, srv, sampler;
};
class CompilerHLSL : public CompilerGLSL class CompilerHLSL : public CompilerGLSL
{ {
public: public:
@ -145,6 +169,14 @@ public:
// Controls how resource bindings are declared in the output HLSL. // Controls how resource bindings are declared in the output HLSL.
void set_resource_binding_flags(HLSLBindingFlags flags); void set_resource_binding_flags(HLSLBindingFlags flags);
// resource is a resource binding to indicate the HLSL CBV, SRV, UAV or sampler binding
// to use for a particular SPIR-V description set
// and binding. If resource bindings are provided,
// is_hlsl_resource_binding_used() will return true after calling ::compile() if
// the set/binding combination was used by the HLSL code.
void add_hlsl_resource_binding(const HLSLResourceBinding &resource);
bool is_hlsl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const;
private: private:
std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
std::string image_type_hlsl(const SPIRType &type, uint32_t id); std::string image_type_hlsl(const SPIRType &type, uint32_t id);
@ -178,12 +210,19 @@ private:
std::string to_sampler_expression(uint32_t id); std::string to_sampler_expression(uint32_t id);
std::string to_resource_binding(const SPIRVariable &var); std::string to_resource_binding(const SPIRVariable &var);
std::string to_resource_binding_sampler(const SPIRVariable &var); std::string to_resource_binding_sampler(const SPIRVariable &var);
std::string to_resource_register(HLSLBindingFlags flags, char space, uint32_t binding, uint32_t set); std::string to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t set);
void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
void emit_access_chain(const Instruction &instruction); void emit_access_chain(const Instruction &instruction);
void emit_load(const Instruction &instruction); void emit_load(const Instruction &instruction);
std::string read_access_chain(const SPIRAccessChain &chain); void read_access_chain(std::string *expr, const std::string &lhs, const SPIRAccessChain &chain);
void write_access_chain(const SPIRAccessChain &chain, uint32_t value); void read_access_chain_struct(const std::string &lhs, const SPIRAccessChain &chain);
void read_access_chain_array(const std::string &lhs, const SPIRAccessChain &chain);
void write_access_chain(const SPIRAccessChain &chain, uint32_t value, const SmallVector<uint32_t> &composite_chain);
void write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value,
const SmallVector<uint32_t> &composite_chain);
void write_access_chain_array(const SPIRAccessChain &chain, uint32_t value,
const SmallVector<uint32_t> &composite_chain);
std::string write_access_chain_value(uint32_t value, const SmallVector<uint32_t> &composite_chain, bool enclose);
void emit_store(const Instruction &instruction); void emit_store(const Instruction &instruction);
void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op); void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op);
void emit_subgroup_op(const Instruction &i) override; void emit_subgroup_op(const Instruction &i) override;
@ -257,6 +296,12 @@ private:
std::vector<RootConstants> root_constants_layout; std::vector<RootConstants> root_constants_layout;
void validate_shader_model(); void validate_shader_model();
std::string get_unique_identifier();
uint32_t unique_identifier_count = 0;
std::unordered_map<StageSetBinding, std::pair<HLSLResourceBinding, bool>, InternalHasher> resource_bindings;
void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding);
}; };
} // namespace SPIRV_CROSS_NAMESPACE } // namespace SPIRV_CROSS_NAMESPACE

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016-2019 The Brenwill Workshop Ltd. * Copyright 2016-2020 The Brenwill Workshop Ltd.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -68,6 +68,12 @@ void CompilerMSL::add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32
buffers_requiring_dynamic_offset[pair] = { index, 0 }; buffers_requiring_dynamic_offset[pair] = { index, 0 };
} }
void CompilerMSL::add_inline_uniform_block(uint32_t desc_set, uint32_t binding)
{
SetBindingPair pair = { desc_set, binding };
inline_uniform_blocks.insert(pair);
}
void CompilerMSL::add_discrete_descriptor_set(uint32_t desc_set) void CompilerMSL::add_discrete_descriptor_set(uint32_t desc_set)
{ {
if (desc_set < kMaxArgumentBuffers) if (desc_set < kMaxArgumentBuffers)
@ -90,7 +96,7 @@ bool CompilerMSL::is_msl_vertex_attribute_used(uint32_t location)
return vtx_attrs_in_use.count(location) != 0; return vtx_attrs_in_use.count(location) != 0;
} }
bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
{ {
StageSetBinding tuple = { model, desc_set, binding }; StageSetBinding tuple = { model, desc_set, binding };
auto itr = resource_bindings.find(tuple); auto itr = resource_bindings.find(tuple);
@ -1003,6 +1009,9 @@ string CompilerMSL::compile()
fixup_image_load_store_access(); fixup_image_load_store_access();
set_enabled_interface_variables(get_active_interface_variables()); set_enabled_interface_variables(get_active_interface_variables());
if (msl_options.force_active_argument_buffer_resources)
activate_argument_buffer_resources();
if (swizzle_buffer_id) if (swizzle_buffer_id)
active_interface_variables.insert(swizzle_buffer_id); active_interface_variables.insert(swizzle_buffer_id);
if (buffer_size_buffer_id) if (buffer_size_buffer_id)
@ -1451,7 +1460,7 @@ uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t comp
} }
void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var, bool strip_array) SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta)
{ {
bool is_builtin = is_builtin_variable(var); bool is_builtin = is_builtin_variable(var);
BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
@ -1466,16 +1475,77 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
var.basetype = type_id; var.basetype = type_id;
type_id = get_pointee_type_id(var.basetype); type_id = get_pointee_type_id(var.basetype);
if (strip_array && is_array(get<SPIRType>(type_id))) if (meta.strip_array && is_array(get<SPIRType>(type_id)))
type_id = get<SPIRType>(type_id).parent_type; type_id = get<SPIRType>(type_id).parent_type;
auto &type = get<SPIRType>(type_id); auto &type = get<SPIRType>(type_id);
uint32_t target_components = 0; uint32_t target_components = 0;
uint32_t type_components = type.vecsize; uint32_t type_components = type.vecsize;
bool padded_output = false; bool padded_output = false;
bool padded_input = false;
uint32_t start_component = 0;
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
// Deal with Component decorations.
InterfaceBlockMeta::LocationMeta *location_meta = nullptr;
if (has_decoration(var.self, DecorationLocation))
{
auto location_meta_itr = meta.location_meta.find(get_decoration(var.self, DecorationLocation));
if (location_meta_itr != end(meta.location_meta))
location_meta = &location_meta_itr->second;
}
bool pad_fragment_output = has_decoration(var.self, DecorationLocation) &&
msl_options.pad_fragment_output_components &&
get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput;
// Check if we need to pad fragment output to match a certain number of components. // Check if we need to pad fragment output to match a certain number of components.
if (get_decoration_bitset(var.self).get(DecorationLocation) && msl_options.pad_fragment_output_components && if (location_meta)
get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput) {
start_component = get_decoration(var.self, DecorationComponent);
uint32_t num_components = location_meta->num_components;
if (pad_fragment_output)
{
uint32_t locn = get_decoration(var.self, DecorationLocation);
num_components = std::max(num_components, get_target_components_for_fragment_location(locn));
}
if (location_meta->ib_index != ~0u)
{
// We have already declared the variable. Just emit an early-declared variable and fixup as needed.
entry_func.add_local_variable(var.self);
vars_needing_early_declaration.push_back(var.self);
if (var.storage == StorageClassInput)
{
uint32_t ib_index = location_meta->ib_index;
entry_func.fixup_hooks_in.push_back([=, &var]() {
statement(to_name(var.self), " = ", ib_var_ref, ".", to_member_name(ib_type, ib_index),
vector_swizzle(type_components, start_component), ";");
});
}
else
{
uint32_t ib_index = location_meta->ib_index;
entry_func.fixup_hooks_out.push_back([=, &var]() {
statement(ib_var_ref, ".", to_member_name(ib_type, ib_index),
vector_swizzle(type_components, start_component), " = ", to_name(var.self), ";");
});
}
return;
}
else
{
location_meta->ib_index = uint32_t(ib_type.member_types.size());
type_id = build_extended_vector_type(type_id, num_components);
if (var.storage == StorageClassInput)
padded_input = true;
else
padded_output = true;
}
}
else if (pad_fragment_output)
{ {
uint32_t locn = get_decoration(var.self, DecorationLocation); uint32_t locn = get_decoration(var.self, DecorationLocation);
target_components = get_target_components_for_fragment_location(locn); target_components = get_target_components_for_fragment_location(locn);
@ -1495,25 +1565,42 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
// Update the original variable reference to include the structure reference // Update the original variable reference to include the structure reference
string qual_var_name = ib_var_ref + "." + mbr_name; string qual_var_name = ib_var_ref + "." + mbr_name;
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
if (padded_output) if (padded_output || padded_input)
{ {
entry_func.add_local_variable(var.self); entry_func.add_local_variable(var.self);
vars_needing_early_declaration.push_back(var.self); vars_needing_early_declaration.push_back(var.self);
entry_func.fixup_hooks_out.push_back([=, &var]() { if (padded_output)
SPIRType &padded_type = this->get<SPIRType>(type_id); {
statement(qual_var_name, " = ", remap_swizzle(padded_type, type_components, to_name(var.self)), ";"); entry_func.fixup_hooks_out.push_back([=, &var]() {
}); statement(qual_var_name, vector_swizzle(type_components, start_component), " = ", to_name(var.self),
";");
});
}
else
{
entry_func.fixup_hooks_in.push_back([=, &var]() {
statement(to_name(var.self), " = ", qual_var_name, vector_swizzle(type_components, start_component),
";");
});
}
} }
else if (!strip_array) else if (!meta.strip_array)
ir.meta[var.self].decoration.qualified_alias = qual_var_name; ir.meta[var.self].decoration.qualified_alias = qual_var_name;
if (var.storage == StorageClassOutput && var.initializer != ID(0)) if (var.storage == StorageClassOutput && var.initializer != ID(0))
{ {
entry_func.fixup_hooks_in.push_back( if (padded_output || padded_input)
[=, &var]() { statement(qual_var_name, " = ", to_expression(var.initializer), ";"); }); {
entry_func.fixup_hooks_in.push_back(
[=, &var]() { statement(to_name(var.self), " = ", to_expression(var.initializer), ";"); });
}
else
{
entry_func.fixup_hooks_in.push_back(
[=, &var]() { statement(qual_var_name, " = ", to_expression(var.initializer), ";"); });
}
} }
// Copy the variable location from the original variable to the member // Copy the variable location from the original variable to the member
@ -1522,10 +1609,14 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
uint32_t locn = get_decoration(var.self, DecorationLocation); uint32_t locn = get_decoration(var.self, DecorationLocation);
if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader())) if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
{ {
type_id = ensure_correct_attribute_type(var.basetype, locn); type_id = ensure_correct_attribute_type(var.basetype, locn,
var.basetype = type_id; location_meta ? location_meta->num_components : type.vecsize);
if (!location_meta)
var.basetype = type_id;
type_id = get_pointee_type_id(type_id); type_id = get_pointee_type_id(type_id);
if (strip_array && is_array(get<SPIRType>(type_id))) if (meta.strip_array && is_array(get<SPIRType>(type_id)))
type_id = get<SPIRType>(type_id).parent_type; type_id = get<SPIRType>(type_id).parent_type;
ib_type.member_types[ib_mbr_idx] = type_id; ib_type.member_types[ib_mbr_idx] = type_id;
} }
@ -1539,10 +1630,13 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
mark_location_as_used_by_shader(locn, storage); mark_location_as_used_by_shader(locn, storage);
} }
if (get_decoration_bitset(var.self).get(DecorationComponent)) if (!location_meta)
{ {
uint32_t comp = get_decoration(var.self, DecorationComponent); if (get_decoration_bitset(var.self).get(DecorationComponent))
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp); {
uint32_t component = get_decoration(var.self, DecorationComponent);
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, component);
}
} }
if (get_decoration_bitset(var.self).get(DecorationIndex)) if (get_decoration_bitset(var.self).get(DecorationIndex))
@ -1569,14 +1663,18 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
if (is_sample) if (is_sample)
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample);
set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); // If we have location meta, there is no unique OrigID. We won't need it, since we flatten/unflatten
// the variable to stack anyways here.
if (!location_meta)
set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self);
} }
void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var, bool strip_array) SPIRType &ib_type, SPIRVariable &var,
InterfaceBlockMeta &meta)
{ {
auto &entry_func = get<SPIRFunction>(ir.default_entry_point); auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
auto &var_type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var); auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
uint32_t elem_cnt = 0; uint32_t elem_cnt = 0;
if (is_matrix(var_type)) if (is_matrix(var_type))
@ -1612,6 +1710,7 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction)); set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction));
bool flatten_from_ib_var = false; bool flatten_from_ib_var = false;
string flatten_from_ib_mbr_name;
if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance) if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance)
{ {
@ -1619,13 +1718,15 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size());
ib_type.member_types.push_back(get_variable_data_type_id(var)); ib_type.member_types.push_back(get_variable_data_type_id(var));
set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance);
set_member_name(ib_type.self, clip_array_mbr_idx, builtin_to_glsl(BuiltInClipDistance, StorageClassOutput));
flatten_from_ib_mbr_name = builtin_to_glsl(BuiltInClipDistance, StorageClassOutput);
set_member_name(ib_type.self, clip_array_mbr_idx, flatten_from_ib_mbr_name);
// When we flatten, we flatten directly from the "out" struct, // When we flatten, we flatten directly from the "out" struct,
// not from a function variable. // not from a function variable.
flatten_from_ib_var = true; flatten_from_ib_var = true;
} }
else if (!strip_array) else if (!meta.strip_array)
{ {
// Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped. // Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped.
entry_func.add_local_variable(var.self); entry_func.add_local_variable(var.self);
@ -1708,7 +1809,7 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self);
// Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped. // Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped.
if (!strip_array) if (!meta.strip_array)
{ {
switch (storage) switch (storage)
{ {
@ -1728,7 +1829,8 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
";"); ";");
} }
else if (flatten_from_ib_var) else if (flatten_from_ib_var)
statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", to_name(var.self), "[", i, "];"); statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", flatten_from_ib_mbr_name, "[", i,
"];");
else else
statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];"); statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];");
}); });
@ -1771,10 +1873,10 @@ uint32_t CompilerMSL::get_accumulated_member_location(const SPIRVariable &var, u
void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var, SPIRType &ib_type, SPIRVariable &var,
uint32_t mbr_idx, bool strip_array) uint32_t mbr_idx, InterfaceBlockMeta &meta)
{ {
auto &entry_func = get<SPIRFunction>(ir.default_entry_point); auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
auto &var_type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var); auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
BuiltIn builtin = BuiltInMax; BuiltIn builtin = BuiltInMax;
bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin); bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin);
@ -1813,6 +1915,7 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
usable_type = &get<SPIRType>(usable_type->parent_type); usable_type = &get<SPIRType>(usable_type->parent_type);
bool flatten_from_ib_var = false; bool flatten_from_ib_var = false;
string flatten_from_ib_mbr_name;
if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance) if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance)
{ {
@ -1820,7 +1923,9 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size());
ib_type.member_types.push_back(mbr_type_id); ib_type.member_types.push_back(mbr_type_id);
set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance);
set_member_name(ib_type.self, clip_array_mbr_idx, builtin_to_glsl(BuiltInClipDistance, StorageClassOutput));
flatten_from_ib_mbr_name = builtin_to_glsl(BuiltInClipDistance, StorageClassOutput);
set_member_name(ib_type.self, clip_array_mbr_idx, flatten_from_ib_mbr_name);
// When we flatten, we flatten directly from the "out" struct, // When we flatten, we flatten directly from the "out" struct,
// not from a function variable. // not from a function variable.
@ -1845,7 +1950,7 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
} }
else if (has_decoration(var.self, DecorationLocation)) else if (has_decoration(var.self, DecorationLocation))
{ {
uint32_t locn = get_accumulated_member_location(var, mbr_idx, strip_array) + i; uint32_t locn = get_accumulated_member_location(var, mbr_idx, meta.strip_array) + i;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage); mark_location_as_used_by_shader(locn, storage);
} }
@ -1879,7 +1984,7 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, mbr_idx); set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, mbr_idx);
// Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate. // Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
if (!strip_array) if (!meta.strip_array)
{ {
switch (storage) switch (storage)
{ {
@ -1894,8 +1999,8 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() { entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() {
if (flatten_from_ib_var) if (flatten_from_ib_var)
{ {
statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", flatten_from_ib_mbr_name, "[", i,
to_member_name(var_type, mbr_idx), "[", i, "];"); "];");
} }
else else
{ {
@ -1914,9 +2019,9 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, 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, SPIRType &ib_type, SPIRVariable &var, uint32_t mbr_idx,
bool strip_array) InterfaceBlockMeta &meta)
{ {
auto &var_type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var); auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
auto &entry_func = get<SPIRFunction>(ir.default_entry_point); auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
BuiltIn builtin = BuiltInMax; BuiltIn builtin = BuiltInMax;
@ -1944,13 +2049,13 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
// Update the original variable reference to include the structure reference // Update the original variable reference to include the structure reference
string qual_var_name = ib_var_ref + "." + mbr_name; string qual_var_name = ib_var_ref + "." + mbr_name;
if (is_builtin && !strip_array) if (is_builtin && !meta.strip_array)
{ {
// For the builtin gl_PerVertex, we cannot treat it as a block anyways, // For the builtin gl_PerVertex, we cannot treat it as a block anyways,
// so redirect to qualified name. // so redirect to qualified name.
set_member_qualified_name(var_type.self, mbr_idx, qual_var_name); set_member_qualified_name(var_type.self, mbr_idx, qual_var_name);
} }
else if (!strip_array) else if (!meta.strip_array)
{ {
// Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate. // Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
switch (storage) switch (storage)
@ -1989,7 +2094,7 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
{ {
// The block itself might have a location and in this case, all members of the block // The block itself might have a location and in this case, all members of the block
// receive incrementing locations. // receive incrementing locations.
uint32_t locn = get_accumulated_member_location(var, mbr_idx, strip_array); uint32_t locn = get_accumulated_member_location(var, mbr_idx, meta.strip_array);
if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader())) if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
{ {
mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn); mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
@ -2155,19 +2260,20 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
} }
void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, SPIRType &ib_type, void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, SPIRType &ib_type,
SPIRVariable &var, bool strip_array) SPIRVariable &var, InterfaceBlockMeta &meta)
{ {
auto &entry_func = get<SPIRFunction>(ir.default_entry_point); auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
// Tessellation control I/O variables and tessellation evaluation per-point inputs are // Tessellation control I/O variables and tessellation evaluation per-point inputs are
// usually declared as arrays. In these cases, we want to add the element type to the // usually declared as arrays. In these cases, we want to add the element type to the
// interface block, since in Metal it's the interface block itself which is arrayed. // interface block, since in Metal it's the interface block itself which is arrayed.
auto &var_type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var); auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
bool is_builtin = is_builtin_variable(var); bool is_builtin = is_builtin_variable(var);
auto builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); auto builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
if (var_type.basetype == SPIRType::Struct) if (var_type.basetype == SPIRType::Struct)
{ {
if (!is_builtin_type(var_type) && (!capture_output_to_buffer || storage == StorageClassInput) && !strip_array) if (!is_builtin_type(var_type) && (!capture_output_to_buffer || storage == StorageClassInput) &&
!meta.strip_array)
{ {
// For I/O blocks or structs, we will need to pass the block itself around // 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. // to functions if they are used globally in leaf functions.
@ -2186,7 +2292,7 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
// Luckily, for stage-out when capturing output, we can avoid this and just add // Luckily, for stage-out when capturing output, we can avoid this and just add
// composite members directly, because the stage-out structure is stored to a buffer, // composite members directly, because the stage-out structure is stored to a buffer,
// not returned. // not returned.
add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, strip_array); add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, meta);
} }
else else
{ {
@ -2200,7 +2306,8 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
if (!is_builtin || has_active_builtin(builtin, storage)) if (!is_builtin || has_active_builtin(builtin, storage))
{ {
bool is_composite_type = is_matrix(mbr_type) || is_array(mbr_type); bool is_composite_type = is_matrix(mbr_type) || is_array(mbr_type);
bool attribute_load_store = storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; bool attribute_load_store =
storage == StorageClassInput && get_execution_model() != ExecutionModelFragment;
bool storage_is_stage_io = storage == StorageClassInput || storage == StorageClassOutput; bool storage_is_stage_io = storage == StorageClassInput || storage == StorageClassOutput;
// ClipDistance always needs to be declared as user attributes. // ClipDistance always needs to be declared as user attributes.
@ -2210,19 +2317,18 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type) if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type)
{ {
add_composite_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx, add_composite_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx,
strip_array); meta);
} }
else else
{ {
add_plain_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx, add_plain_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx, meta);
strip_array);
} }
} }
} }
} }
} }
else if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput && else if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput &&
!strip_array && is_builtin && (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner)) !meta.strip_array && is_builtin && (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner))
{ {
add_tess_level_input_to_interface_block(ib_var_ref, ib_type, var); add_tess_level_input_to_interface_block(ib_var_ref, ib_type, var);
} }
@ -2233,7 +2339,7 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
{ {
bool is_composite_type = is_matrix(var_type) || is_array(var_type); bool is_composite_type = is_matrix(var_type) || is_array(var_type);
bool storage_is_stage_io = bool storage_is_stage_io =
storage == StorageClassInput || (storage == StorageClassOutput && !capture_output_to_buffer); storage == StorageClassInput || (storage == StorageClassOutput && !capture_output_to_buffer);
bool attribute_load_store = storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; bool attribute_load_store = storage == StorageClassInput && get_execution_model() != ExecutionModelFragment;
// ClipDistance always needs to be declared as user attributes. // ClipDistance always needs to be declared as user attributes.
@ -2243,11 +2349,11 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
// MSL does not allow matrices or arrays in input or output variables, so need to handle it specially. // MSL does not allow matrices or arrays in input or output variables, so need to handle it specially.
if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type) if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type)
{ {
add_composite_variable_to_interface_block(storage, ib_var_ref, ib_type, var, strip_array); add_composite_variable_to_interface_block(storage, ib_var_ref, ib_type, var, meta);
} }
else else
{ {
add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, strip_array); add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, meta);
} }
} }
} }
@ -2301,6 +2407,16 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
bool incl_builtins = storage == StorageClassOutput || is_tessellation_shader(); bool incl_builtins = storage == StorageClassOutput || is_tessellation_shader();
bool has_seen_barycentric = false; bool has_seen_barycentric = false;
InterfaceBlockMeta meta;
// Varying interfaces between stages which use "user()" attribute can be dealt with
// without explicit packing and unpacking of components. For any variables which link against the runtime
// in some way (vertex attributes, fragment output, etc), we'll need to deal with it somehow.
bool pack_components =
(storage == StorageClassInput && get_execution_model() == ExecutionModelVertex) ||
(storage == StorageClassOutput && get_execution_model() == ExecutionModelFragment) ||
(storage == StorageClassOutput && get_execution_model() == ExecutionModelVertex && capture_output_to_buffer);
ir.for_each_typed_id<SPIRVariable>([&](uint32_t var_id, SPIRVariable &var) { ir.for_each_typed_id<SPIRVariable>([&](uint32_t var_id, SPIRVariable &var) {
if (var.storage != storage) if (var.storage != storage)
return; return;
@ -2347,6 +2463,28 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
(!is_builtin || is_interface_block_builtin)) (!is_builtin || is_interface_block_builtin))
{ {
vars.push_back(&var); vars.push_back(&var);
if (!is_builtin)
{
// Need to deal specially with DecorationComponent.
// Multiple variables can alias the same Location, and try to make sure each location is declared only once.
// We will swizzle data in and out to make this work.
// We only need to consider plain variables here, not composites.
// This is only relevant for vertex inputs and fragment outputs.
// Technically tessellation as well, but it is too complicated to support.
uint32_t component = get_decoration(var_id, DecorationComponent);
if (component != 0)
{
if (is_tessellation_shader())
SPIRV_CROSS_THROW("Component decoration is not supported in tessellation shaders.");
else if (pack_components)
{
uint32_t location = get_decoration(var_id, DecorationLocation);
auto &location_meta = meta.location_meta[location];
location_meta.num_components = std::max(location_meta.num_components, component + type.vecsize);
}
}
}
} }
}); });
@ -2480,7 +2618,9 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
(get_execution_model() == ExecutionModelTessellationControl || (get_execution_model() == ExecutionModelTessellationControl ||
(get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput)) && (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput)) &&
!patch; !patch;
add_variable_to_interface_block(storage, ib_var_ref, ib_type, *p_var, strip_array);
meta.strip_array = strip_array;
add_variable_to_interface_block(storage, ib_var_ref, ib_type, *p_var, meta);
} }
// Sort the members of the structure by their locations. // Sort the members of the structure by their locations.
@ -2599,13 +2739,18 @@ uint32_t CompilerMSL::ensure_correct_builtin_type(uint32_t type_id, BuiltIn buil
// Ensure that the type is compatible with the vertex attribute. // Ensure that the type is compatible with the vertex attribute.
// If it is, simply return the given type ID. // If it is, simply return the given type ID.
// Otherwise, create a new type, and return its ID. // Otherwise, create a new type, and return its ID.
uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t location) uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t location, uint32_t num_components)
{ {
auto &type = get<SPIRType>(type_id); auto &type = get<SPIRType>(type_id);
auto p_va = vtx_attrs_by_location.find(location); auto p_va = vtx_attrs_by_location.find(location);
if (p_va == end(vtx_attrs_by_location)) if (p_va == end(vtx_attrs_by_location))
return type_id; {
if (num_components != 0 && type.vecsize != num_components)
return build_extended_vector_type(type_id, num_components);
else
return type_id;
}
switch (p_va->second.format) switch (p_va->second.format)
{ {
@ -2616,19 +2761,27 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
case SPIRType::UByte: case SPIRType::UByte:
case SPIRType::UShort: case SPIRType::UShort:
case SPIRType::UInt: case SPIRType::UInt:
return type_id; if (num_components != 0 && type.vecsize != num_components)
return build_extended_vector_type(type_id, num_components);
else
return type_id;
case SPIRType::Short: case SPIRType::Short:
case SPIRType::Int: case SPIRType::Int:
break; break;
default: default:
SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader"); SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
} }
uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1); uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1);
uint32_t base_type_id = next_id++; uint32_t base_type_id = next_id++;
auto &base_type = set<SPIRType>(base_type_id); auto &base_type = set<SPIRType>(base_type_id);
base_type = type; base_type = type;
base_type.basetype = type.basetype == SPIRType::Short ? SPIRType::UShort : SPIRType::UInt; base_type.basetype = type.basetype == SPIRType::Short ? SPIRType::UShort : SPIRType::UInt;
base_type.pointer = false; base_type.pointer = false;
if (num_components != 0)
base_type.vecsize = num_components;
if (!type.pointer) if (!type.pointer)
return base_type_id; return base_type_id;
@ -2648,18 +2801,26 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
{ {
case SPIRType::UShort: case SPIRType::UShort:
case SPIRType::UInt: case SPIRType::UInt:
return type_id; if (num_components != 0 && type.vecsize != num_components)
return build_extended_vector_type(type_id, num_components);
else
return type_id;
case SPIRType::Int: case SPIRType::Int:
break; break;
default: default:
SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader"); SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
} }
uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1); uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1);
uint32_t base_type_id = next_id++; uint32_t base_type_id = next_id++;
auto &base_type = set<SPIRType>(base_type_id); auto &base_type = set<SPIRType>(base_type_id);
base_type = type; base_type = type;
base_type.basetype = SPIRType::UInt; base_type.basetype = SPIRType::UInt;
base_type.pointer = false; base_type.pointer = false;
if (num_components != 0)
base_type.vecsize = num_components;
if (!type.pointer) if (!type.pointer)
return base_type_id; return base_type_id;
@ -2674,7 +2835,8 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
} }
default: default:
case MSL_VERTEX_FORMAT_OTHER: if (num_components != 0 && type.vecsize != num_components)
type_id = build_extended_vector_type(type_id, num_components);
break; break;
} }
@ -3268,8 +3430,7 @@ string CompilerMSL::unpack_expression_type(string expr_str, const SPIRType &type
assert(type.vecsize >= 1 && type.vecsize <= 3); assert(type.vecsize >= 1 && type.vecsize <= 3);
return enclose_expression(expr_str) + swizzle_lut[type.vecsize - 1]; return enclose_expression(expr_str) + swizzle_lut[type.vecsize - 1];
} }
else if (physical_type && is_matrix(*physical_type) && is_vector(type) && else if (physical_type && is_matrix(*physical_type) && is_vector(type) && physical_type->vecsize > type.vecsize)
physical_type->vecsize > type.vecsize)
{ {
// Extract column from padded matrix. // Extract column from padded matrix.
assert(type.vecsize >= 1 && type.vecsize <= 3); assert(type.vecsize >= 1 && type.vecsize <= 3);
@ -8604,7 +8765,14 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
uint32_t locn = get_ordered_member_location(type.self, index, &comp); uint32_t locn = get_ordered_member_location(type.self, index, &comp);
if (locn != k_unknown_location) if (locn != k_unknown_location)
{ {
if (comp != k_unknown_component) // For user-defined attributes, this is fine. From Vulkan spec:
// A user-defined output variable is considered to match an input variable in the subsequent stage if
// the two variables are declared with the same Location and Component decoration and match in type
// and decoration, except that interpolation decorations are not required to match. For the purposes
// of interface matching, variables declared without a Component decoration are considered to have a
// Component decoration of zero.
if (comp != k_unknown_component && comp != 0)
quals = string("user(locn") + convert_to_string(locn) + "_" + convert_to_string(comp) + ")"; quals = string("user(locn") + convert_to_string(locn) + "_" + convert_to_string(comp) + ")";
else else
quals = string("user(locn") + convert_to_string(locn) + ")"; quals = string("user(locn") + convert_to_string(locn) + ")";
@ -9704,9 +9872,8 @@ uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::Base
bool use_secondary_binding = (var_type.basetype == SPIRType::SampledImage && basetype == SPIRType::Sampler) || bool use_secondary_binding = (var_type.basetype == SPIRType::SampledImage && basetype == SPIRType::Sampler) ||
basetype == SPIRType::AtomicCounter; basetype == SPIRType::AtomicCounter;
auto resource_decoration = use_secondary_binding ? auto resource_decoration =
SPIRVCrossDecorationResourceIndexSecondary : use_secondary_binding ? SPIRVCrossDecorationResourceIndexSecondary : SPIRVCrossDecorationResourceIndexPrimary;
SPIRVCrossDecorationResourceIndexPrimary;
if (plane == 1) if (plane == 1)
resource_decoration = SPIRVCrossDecorationResourceIndexTertiary; resource_decoration = SPIRVCrossDecorationResourceIndexTertiary;
@ -9751,6 +9918,11 @@ uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::Base
// If we did not explicitly remap, allocate bindings on demand. // If we did not explicitly remap, allocate bindings on demand.
// We cannot reliably use Binding decorations since SPIR-V and MSL's binding models are very different. // We cannot reliably use Binding decorations since SPIR-V and MSL's binding models are very different.
bool allocate_argument_buffer_ids = false;
if (var.storage != StorageClassPushConstant)
allocate_argument_buffer_ids = descriptor_set_is_argument_buffer(var_desc_set);
uint32_t binding_stride = 1; uint32_t binding_stride = 1;
auto &type = get<SPIRType>(var.basetype); auto &type = get<SPIRType>(var.basetype);
for (uint32_t i = 0; i < uint32_t(type.array.size()); i++) for (uint32_t i = 0; i < uint32_t(type.array.size()); i++)
@ -9761,20 +9933,11 @@ uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::Base
// If a binding has not been specified, revert to incrementing resource indices. // If a binding has not been specified, revert to incrementing resource indices.
uint32_t resource_index; uint32_t resource_index;
bool allocate_argument_buffer_ids = false;
uint32_t desc_set = 0;
if (var.storage != StorageClassPushConstant)
{
desc_set = get_decoration(var.self, DecorationDescriptorSet);
allocate_argument_buffer_ids = descriptor_set_is_argument_buffer(desc_set);
}
if (allocate_argument_buffer_ids) if (allocate_argument_buffer_ids)
{ {
// Allocate from a flat ID binding space. // Allocate from a flat ID binding space.
resource_index = next_metal_resource_ids[desc_set]; resource_index = next_metal_resource_ids[var_desc_set];
next_metal_resource_ids[desc_set] += binding_stride; next_metal_resource_ids[var_desc_set] += binding_stride;
} }
else else
{ {
@ -10244,19 +10407,31 @@ void CompilerMSL::replace_illegal_names()
}; };
ir.for_each_typed_id<SPIRVariable>([&](uint32_t self, SPIRVariable &) { ir.for_each_typed_id<SPIRVariable>([&](uint32_t self, SPIRVariable &) {
auto &dec = ir.meta[self].decoration; auto *meta = ir.find_meta(self);
if (!meta)
return;
auto &dec = meta->decoration;
if (keywords.find(dec.alias) != end(keywords)) if (keywords.find(dec.alias) != end(keywords))
dec.alias += "0"; dec.alias += "0";
}); });
ir.for_each_typed_id<SPIRFunction>([&](uint32_t self, SPIRFunction &) { ir.for_each_typed_id<SPIRFunction>([&](uint32_t self, SPIRFunction &) {
auto &dec = ir.meta[self].decoration; auto *meta = ir.find_meta(self);
if (!meta)
return;
auto &dec = meta->decoration;
if (illegal_func_names.find(dec.alias) != end(illegal_func_names)) if (illegal_func_names.find(dec.alias) != end(illegal_func_names))
dec.alias += "0"; dec.alias += "0";
}); });
ir.for_each_typed_id<SPIRType>([&](uint32_t self, SPIRType &) { ir.for_each_typed_id<SPIRType>([&](uint32_t self, SPIRType &) {
for (auto &mbr_dec : ir.meta[self].members) auto *meta = ir.find_meta(self);
if (!meta)
return;
for (auto &mbr_dec : meta->members)
if (keywords.find(mbr_dec.alias) != end(keywords)) if (keywords.find(mbr_dec.alias) != end(keywords))
mbr_dec.alias += "0"; mbr_dec.alias += "0";
}); });
@ -10709,6 +10884,11 @@ void CompilerMSL::emit_subgroup_op(const Instruction &i)
if (!msl_options.supports_msl_version(2)) if (!msl_options.supports_msl_version(2))
SPIRV_CROSS_THROW("Subgroups are only supported in Metal 2.0 and up."); SPIRV_CROSS_THROW("Subgroups are only supported in Metal 2.0 and up.");
// If we need to do implicit bitcasts, make sure we do it with the correct type.
uint32_t integer_width = get_integer_width_for_instruction(i);
auto int_type = to_signed_basetype(integer_width);
auto uint_type = to_unsigned_basetype(integer_width);
if (msl_options.is_ios()) if (msl_options.is_ios())
{ {
switch (op) switch (op)
@ -10731,7 +10911,7 @@ void CompilerMSL::emit_subgroup_op(const Instruction &i)
switch (op) switch (op)
{ {
default: default:
SPIRV_CROSS_THROW("Subgroup ops beyond broadcast and shuffle on macOS require Metal 2.0 and up."); SPIRV_CROSS_THROW("Subgroup ops beyond broadcast and shuffle on macOS require Metal 2.1 and up.");
case OpGroupNonUniformBroadcast: case OpGroupNonUniformBroadcast:
case OpGroupNonUniformShuffle: case OpGroupNonUniformShuffle:
case OpGroupNonUniformShuffleXor: case OpGroupNonUniformShuffleXor:
@ -10859,6 +11039,7 @@ case OpGroupNonUniform##op: \
MSL_GROUP_OP(IMul, product) MSL_GROUP_OP(IMul, product)
#undef MSL_GROUP_OP #undef MSL_GROUP_OP
// The others, unfortunately, don't support InclusiveScan or ExclusiveScan. // The others, unfortunately, don't support InclusiveScan or ExclusiveScan.
#define MSL_GROUP_OP(op, msl_op) \ #define MSL_GROUP_OP(op, msl_op) \
case OpGroupNonUniform##op: \ case OpGroupNonUniform##op: \
{ \ { \
@ -10881,12 +11062,36 @@ case OpGroupNonUniform##op: \
SPIRV_CROSS_THROW("Invalid group operation."); \ SPIRV_CROSS_THROW("Invalid group operation."); \
break; \ break; \
} }
#define MSL_GROUP_OP_CAST(op, msl_op, type) \
case OpGroupNonUniform##op: \
{ \
auto operation = static_cast<GroupOperation>(ops[3]); \
if (operation == GroupOperationReduce) \
emit_unary_func_op_cast(result_type, id, ops[4], "simd_" #msl_op, type, type); \
else if (operation == GroupOperationInclusiveScan) \
SPIRV_CROSS_THROW("Metal doesn't support InclusiveScan for OpGroupNonUniform" #op "."); \
else if (operation == GroupOperationExclusiveScan) \
SPIRV_CROSS_THROW("Metal doesn't support ExclusiveScan for OpGroupNonUniform" #op "."); \
else if (operation == GroupOperationClusteredReduce) \
{ \
/* Only cluster sizes of 4 are supported. */ \
uint32_t cluster_size = get<SPIRConstant>(ops[5]).scalar(); \
if (cluster_size != 4) \
SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \
emit_unary_func_op_cast(result_type, id, ops[4], "quad_" #msl_op, type, type); \
} \
else \
SPIRV_CROSS_THROW("Invalid group operation."); \
break; \
}
MSL_GROUP_OP(FMin, min) MSL_GROUP_OP(FMin, min)
MSL_GROUP_OP(FMax, max) MSL_GROUP_OP(FMax, max)
MSL_GROUP_OP(SMin, min) MSL_GROUP_OP_CAST(SMin, min, int_type)
MSL_GROUP_OP(SMax, max) MSL_GROUP_OP_CAST(SMax, max, int_type)
MSL_GROUP_OP(UMin, min) MSL_GROUP_OP_CAST(UMin, min, uint_type)
MSL_GROUP_OP(UMax, max) MSL_GROUP_OP_CAST(UMax, max, uint_type)
MSL_GROUP_OP(BitwiseAnd, and) MSL_GROUP_OP(BitwiseAnd, and)
MSL_GROUP_OP(BitwiseOr, or) MSL_GROUP_OP(BitwiseOr, or)
MSL_GROUP_OP(BitwiseXor, xor) MSL_GROUP_OP(BitwiseXor, xor)
@ -10894,6 +11099,8 @@ case OpGroupNonUniform##op: \
MSL_GROUP_OP(LogicalOr, or) MSL_GROUP_OP(LogicalOr, or)
MSL_GROUP_OP(LogicalXor, xor) MSL_GROUP_OP(LogicalXor, xor)
// clang-format on // clang-format on
#undef MSL_GROUP_OP
#undef MSL_GROUP_OP_CAST
case OpGroupNonUniformQuadSwap: case OpGroupNonUniformQuadSwap:
{ {
@ -12243,6 +12450,7 @@ void CompilerMSL::analyze_argument_buffers()
uint32_t plane; uint32_t plane;
}; };
SmallVector<Resource> resources_in_set[kMaxArgumentBuffers]; SmallVector<Resource> resources_in_set[kMaxArgumentBuffers];
SmallVector<uint32_t> inline_block_vars;
bool set_needs_swizzle_buffer[kMaxArgumentBuffers] = {}; bool set_needs_swizzle_buffer[kMaxArgumentBuffers] = {};
bool set_needs_buffer_sizes[kMaxArgumentBuffers] = {}; bool set_needs_buffer_sizes[kMaxArgumentBuffers] = {};
@ -12275,6 +12483,7 @@ void CompilerMSL::analyze_argument_buffers()
} }
} }
uint32_t binding = get_decoration(var_id, DecorationBinding);
if (type.basetype == SPIRType::SampledImage) if (type.basetype == SPIRType::SampledImage)
{ {
add_resource_name(var_id); add_resource_name(var_id);
@ -12297,9 +12506,14 @@ void CompilerMSL::analyze_argument_buffers()
{ &var, to_sampler_expression(var_id), SPIRType::Sampler, sampler_resource_index, 0 }); { &var, to_sampler_expression(var_id), SPIRType::Sampler, sampler_resource_index, 0 });
} }
} }
else if (inline_uniform_blocks.count(SetBindingPair{ desc_set, binding }))
{
inline_block_vars.push_back(var_id);
}
else if (!constexpr_sampler) else if (!constexpr_sampler)
{ {
// constexpr samplers are not declared as resources. // constexpr samplers are not declared as resources.
// Inline uniform blocks are always emitted at the end.
if (!msl_options.is_ios() || type.basetype != SPIRType::Image || type.image.sampled != 2) if (!msl_options.is_ios() || type.basetype != SPIRType::Image || type.image.sampled != 2)
{ {
add_resource_name(var_id); add_resource_name(var_id);
@ -12374,6 +12588,16 @@ void CompilerMSL::analyze_argument_buffers()
} }
} }
// Now add inline uniform blocks.
for (uint32_t var_id : inline_block_vars)
{
auto &var = get<SPIRVariable>(var_id);
uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet);
add_resource_name(var_id);
resources_in_set[desc_set].push_back(
{ &var, to_name(var_id), SPIRType::Struct, get_metal_resource_index(var, SPIRType::Struct), 0 });
}
for (uint32_t desc_set = 0; desc_set < kMaxArgumentBuffers; desc_set++) for (uint32_t desc_set = 0; desc_set < kMaxArgumentBuffers; desc_set++)
{ {
auto &resources = resources_in_set[desc_set]; auto &resources = resources_in_set[desc_set];
@ -12471,6 +12695,12 @@ void CompilerMSL::analyze_argument_buffers()
buffer_type.member_types.push_back(var.basetype); buffer_type.member_types.push_back(var.basetype);
buffers_requiring_dynamic_offset[pair].second = var.self; buffers_requiring_dynamic_offset[pair].second = var.self;
} }
else if (inline_uniform_blocks.count(pair))
{
// Put the buffer block itself into the argument buffer.
buffer_type.member_types.push_back(get_variable_data_type_id(var));
set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name));
}
else else
{ {
// Resources will be declared as pointers not references, so automatically dereference as appropriate. // Resources will be declared as pointers not references, so automatically dereference as appropriate.
@ -12491,34 +12721,15 @@ void CompilerMSL::analyze_argument_buffers()
} }
} }
bool CompilerMSL::SetBindingPair::operator==(const SetBindingPair &other) const void CompilerMSL::activate_argument_buffer_resources()
{ {
return desc_set == other.desc_set && binding == other.binding; // For ABI compatibility, force-enable all resources which are part of argument buffers.
} ir.for_each_typed_id<SPIRVariable>([&](uint32_t self, const SPIRVariable &) {
if (!has_decoration(self, DecorationDescriptorSet))
return;
bool CompilerMSL::SetBindingPair::operator<(const SetBindingPair &other) const uint32_t desc_set = get_decoration(self, DecorationDescriptorSet);
{ if (descriptor_set_is_argument_buffer(desc_set))
return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding); active_interface_variables.insert(self);
} });
bool CompilerMSL::StageSetBinding::operator==(const StageSetBinding &other) const
{
return model == other.model && desc_set == other.desc_set && binding == other.binding;
}
size_t CompilerMSL::InternalHasher::operator()(const SetBindingPair &value) const
{
// Quality of hash doesn't really matter here.
auto hash_set = std::hash<uint32_t>()(value.desc_set);
auto hash_binding = std::hash<uint32_t>()(value.binding);
return (hash_set * 0x10001b31) ^ hash_binding;
}
size_t CompilerMSL::InternalHasher::operator()(const StageSetBinding &value) const
{
// Quality of hash doesn't really matter here.
auto hash_model = std::hash<uint32_t>()(value.model);
auto hash_set = std::hash<uint32_t>()(value.desc_set);
auto tmp_hash = (hash_model * 0x10001b31) ^ hash_set;
return (tmp_hash * 0x10001b31) ^ value.binding;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2016-2019 The Brenwill Workshop Ltd. * Copyright 2016-2020 The Brenwill Workshop Ltd.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -218,11 +218,13 @@ struct MSLConstexprSampler
// Special constant used in a MSLResourceBinding desc_set // Special constant used in a MSLResourceBinding desc_set
// element to indicate the bindings for the push constants. // element to indicate the bindings for the push constants.
static const uint32_t kPushConstDescSet = ~(0u); // Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly.
static const uint32_t kPushConstDescSet = ResourceBindingPushConstantDescriptorSet;
// Special constant used in a MSLResourceBinding binding // Special constant used in a MSLResourceBinding binding
// element to indicate the bindings for the push constants. // element to indicate the bindings for the push constants.
static const uint32_t kPushConstBinding = 0; // Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly.
static const uint32_t kPushConstBinding = ResourceBindingPushConstantBinding;
// Special constant used in a MSLResourceBinding binding // Special constant used in a MSLResourceBinding binding
// element to indicate the buffer binding for swizzle buffers. // element to indicate the buffer binding for swizzle buffers.
@ -305,6 +307,11 @@ public:
// Requires MSL 2.1, use the native support for texel buffers. // Requires MSL 2.1, use the native support for texel buffers.
bool texture_buffer_native = false; bool texture_buffer_native = false;
// Forces all resources which are part of an argument buffer to be considered active.
// This ensures ABI compatibility between shaders where some resources might be unused,
// and would otherwise declare a different IAB.
bool force_active_argument_buffer_resources = false;
bool is_ios() bool is_ios()
{ {
return platform == iOS; return platform == iOS;
@ -425,6 +432,13 @@ public:
// an offset taken from the dynamic offset buffer. // an offset taken from the dynamic offset buffer.
void add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index); void add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index);
// desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource
// in this shader. This function marks that resource as an inline uniform block
// (VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT). This function only has any effect if argument buffers
// are enabled. If so, the buffer block will be directly embedded into the argument
// buffer, instead of being referenced indirectly via pointer.
void add_inline_uniform_block(uint32_t desc_set, uint32_t binding);
// When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets. // When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets.
// This corresponds to VK_KHR_push_descriptor in Vulkan. // This corresponds to VK_KHR_push_descriptor in Vulkan.
void add_discrete_descriptor_set(uint32_t desc_set); void add_discrete_descriptor_set(uint32_t desc_set);
@ -440,7 +454,7 @@ public:
// Constexpr samplers are always assumed to be emitted. // Constexpr samplers are always assumed to be emitted.
// No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped // No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped
// by remap_constexpr_sampler(_by_binding). // by remap_constexpr_sampler(_by_binding).
bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding); bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const;
// This must only be called after a successful call to CompilerMSL::compile(). // This must only be called after a successful call to CompilerMSL::compile().
// For a variable resource ID obtained through reflection API, report the automatically assigned resource index. // For a variable resource ID obtained through reflection API, report the automatically assigned resource index.
@ -637,18 +651,29 @@ protected:
uint32_t add_interface_block(spv::StorageClass storage, bool patch = false); uint32_t add_interface_block(spv::StorageClass storage, bool patch = false);
uint32_t add_interface_block_pointer(uint32_t ib_var_id, spv::StorageClass storage); uint32_t add_interface_block_pointer(uint32_t ib_var_id, spv::StorageClass storage);
struct InterfaceBlockMeta
{
struct LocationMeta
{
uint32_t num_components = 0;
uint32_t ib_index = ~0u;
};
std::unordered_map<uint32_t, LocationMeta> location_meta;
bool strip_array = false;
};
void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type, void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type,
SPIRVariable &var, bool strip_array); SPIRVariable &var, InterfaceBlockMeta &meta);
void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var, bool strip_array); SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta);
void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
SPIRType &ib_type, SPIRVariable &var, bool strip_array); SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta);
void add_plain_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, 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, SPIRType &ib_type, SPIRVariable &var, uint32_t index,
bool strip_array); InterfaceBlockMeta &meta);
void add_composite_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, 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, SPIRType &ib_type, SPIRVariable &var, uint32_t index,
bool strip_array); InterfaceBlockMeta &meta);
uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array); uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array);
void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var); void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var);
@ -656,7 +681,7 @@ protected:
void mark_location_as_used_by_shader(uint32_t location, spv::StorageClass storage); 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_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
uint32_t ensure_correct_attribute_type(uint32_t type_id, uint32_t location); uint32_t ensure_correct_attribute_type(uint32_t type_id, uint32_t location, uint32_t num_components = 0);
void emit_custom_templates(); void emit_custom_templates();
void emit_custom_functions(); void emit_custom_functions();
@ -775,28 +800,6 @@ protected:
std::set<std::string> typedef_lines; std::set<std::string> typedef_lines;
SmallVector<uint32_t> vars_needing_early_declaration; SmallVector<uint32_t> vars_needing_early_declaration;
struct SetBindingPair
{
uint32_t desc_set;
uint32_t binding;
bool operator==(const SetBindingPair &other) const;
bool operator<(const SetBindingPair &other) const;
};
struct StageSetBinding
{
spv::ExecutionModel model;
uint32_t desc_set;
uint32_t binding;
bool operator==(const StageSetBinding &other) const;
};
struct InternalHasher
{
size_t operator()(const SetBindingPair &value) const;
size_t operator()(const StageSetBinding &value) const;
};
std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings; std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings;
uint32_t next_metal_resource_index_buffer = 0; uint32_t next_metal_resource_index_buffer = 0;
@ -858,6 +861,8 @@ protected:
// Must be ordered since array is in a specific order. // Must be ordered since array is in a specific order.
std::map<SetBindingPair, std::pair<uint32_t, uint32_t>> buffers_requiring_dynamic_offset; std::map<SetBindingPair, std::pair<uint32_t, uint32_t>> buffers_requiring_dynamic_offset;
std::unordered_set<SetBindingPair, InternalHasher> inline_uniform_blocks;
uint32_t argument_buffer_ids[kMaxArgumentBuffers]; uint32_t argument_buffer_ids[kMaxArgumentBuffers];
uint32_t argument_buffer_discrete_mask = 0; uint32_t argument_buffer_discrete_mask = 0;
uint32_t argument_buffer_device_storage_mask = 0; uint32_t argument_buffer_device_storage_mask = 0;
@ -872,6 +877,8 @@ protected:
void add_spv_func_and_recompile(SPVFuncImpl spv_func); void add_spv_func_and_recompile(SPVFuncImpl spv_func);
void activate_argument_buffer_resources();
// OpcodeHandler that handles several MSL preprocessing operations. // OpcodeHandler that handles several MSL preprocessing operations.
struct OpCodePreprocessor : OpcodeHandler struct OpCodePreprocessor : OpcodeHandler
{ {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2019 Arm Limited * Copyright 2018-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2019 Arm Limited * Copyright 2018-2020 Arm Limited
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2019 Bradley Austin Davis * Copyright 2018-2020 Bradley Austin Davis
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -358,15 +358,16 @@ void CompilerReflection::emit_type_array(const SPIRType &type)
for (const auto &value : type.array) for (const auto &value : type.array)
json_stream->emit_json_array_value(value); json_stream->emit_json_array_value(value);
json_stream->end_json_array(); json_stream->end_json_array();
json_stream->emit_json_key_array("array_size_is_literal");
for (const auto &value : type.array_size_literal)
json_stream->emit_json_array_value(value);
json_stream->end_json_array();
} }
} }
void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index) void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index)
{ {
auto flags = combined_decoration_for_member(type, index);
if (flags.get(DecorationRowMajor))
json_stream->emit_json_key_value("row_major", true);
auto &membertype = get<SPIRType>(type.member_types[index]); auto &membertype = get<SPIRType>(type.member_types[index]);
emit_type_array(membertype); emit_type_array(membertype);
auto &memb = ir.meta[type.self].members; auto &memb = ir.meta[type.self].members;
@ -377,6 +378,16 @@ void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint3
json_stream->emit_json_key_value("location", dec.location); json_stream->emit_json_key_value("location", dec.location);
if (dec.decoration_flags.get(DecorationOffset)) if (dec.decoration_flags.get(DecorationOffset))
json_stream->emit_json_key_value("offset", dec.offset); json_stream->emit_json_key_value("offset", dec.offset);
// Array stride is a property of the array type, not the struct.
if (has_decoration(type.member_types[index], DecorationArrayStride))
json_stream->emit_json_key_value("array_stride",
get_decoration(type.member_types[index], DecorationArrayStride));
if (dec.decoration_flags.get(DecorationMatrixStride))
json_stream->emit_json_key_value("matrix_stride", dec.matrix_stride);
if (dec.decoration_flags.get(DecorationRowMajor))
json_stream->emit_json_key_value("row_major", true);
} }
} }
@ -592,6 +603,7 @@ void CompilerReflection::emit_specialization_constants()
json_stream->begin_json_object(); json_stream->begin_json_object();
json_stream->emit_json_key_value("id", spec_const.constant_id); json_stream->emit_json_key_value("id", spec_const.constant_id);
json_stream->emit_json_key_value("type", type_to_glsl(type)); json_stream->emit_json_key_value("type", type_to_glsl(type));
json_stream->emit_json_key_value("variable_id", spec_const.id);
switch (type.basetype) switch (type.basetype)
{ {
case SPIRType::UInt: case SPIRType::UInt:

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2018-2019 Bradley Austin Davis * Copyright 2018-2020 Bradley Austin Davis
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.