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");
* 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_dispatch_base = 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_ubo_as_plain_uniforms = 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_device_argument_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_out;
SmallVector<Remap> remaps;
@ -611,7 +613,9 @@ static void print_help()
"\t[--msl-view-index-from-device-index]\n"
"\t[--msl-dispatch-base]\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-force-active-argument-buffer-resources]\n"
"\t[--hlsl]\n"
"\t[--reflect]\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.dispatch_base = args.msl_dispatch_base;
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);
for (auto &v : args.msl_discrete_descriptor_sets)
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;
for (auto &v : args.msl_dynamic_buffers)
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)
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));
});
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("--rename-entry-point", [&args](CLIParser &parser) {
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");
* 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");
* 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");
* you may not use this file except in compliance with the License.
@ -638,6 +638,7 @@ struct SPIREntryPoint
uint32_t invocations = 0;
uint32_t output_vertices = 0;
spv::ExecutionModel model = spv::ExecutionModelMax;
bool geometry_passthrough = false;
};
struct SPIRExpression : IVariant
@ -975,6 +976,7 @@ struct SPIRAccessChain : IVariant
VariableID loaded_from = 0;
uint32_t matrix_stride = 0;
uint32_t array_stride = 0;
bool row_major_matrix = false;
bool immutable = false;
@ -1051,8 +1053,7 @@ struct SPIRConstant : IVariant
type = TypeConstant
};
union Constant
{
union Constant {
uint32_t u32;
int32_t i32;
float f32;
@ -1090,8 +1091,7 @@ struct SPIRConstant : IVariant
int e = (u16_value >> 10) & 0x1f;
int m = (u16_value >> 0) & 0x3ff;
union
{
union {
float f32;
uint32_t u32;
} u;
@ -1571,6 +1571,8 @@ struct Meta
uint32_t set = 0;
uint32_t binding = 0;
uint32_t offset = 0;
uint32_t xfb_buffer = 0;
uint32_t xfb_stride = 0;
uint32_t array_stride = 0;
uint32_t matrix_stride = 0;
uint32_t input_attachment = 0;
@ -1695,6 +1697,62 @@ static inline bool opcode_is_sign_invariant(spv::Op opcode)
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 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");
* 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");
* 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");
* 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);
}
auto &chain_type = expression_type(chain);
if (var)
{
bool check_argument_storage_qualifier = true;
@ -359,7 +361,7 @@ void Compiler::register_write(uint32_t chain)
force_recompile();
}
}
else
else if (chain_type.pointer)
{
// 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
@ -368,6 +370,9 @@ void Compiler::register_write(uint32_t chain)
// only certain variables, we can invalidate only those.
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)

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");
* 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");
* 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:
options->msl.enable_decoration_binding = value != 0;
break;
case SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES:
options->msl.force_active_argument_buffer_resources = value != 0;
break;
#endif
default:
@ -789,6 +793,60 @@ spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler
#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)
{
#if SPIRV_CROSS_C_API_MSL
@ -971,6 +1029,26 @@ spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigne
#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)
{
#if SPIRV_CROSS_C_API_MSL
@ -2157,6 +2235,26 @@ void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding)
#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)
{
#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");
* 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. */
#define SPVC_C_API_VERSION_MAJOR 0
/* 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. */
#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. */
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_CBV_BIT = 1 << 1,
SPVC_HLSL_BINDING_AUTO_SRV_BIT = 1 << 2,
@ -478,6 +479,31 @@ typedef enum spvc_hlsl_binding_flag_bits
} spvc_hlsl_binding_flag_bits;
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. */
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_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_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES = 50 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
} 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_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.
* 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_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding);
/*
* Reflect resources.
* 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");
* you may not use this file except in compliance with the License.
@ -61,8 +61,7 @@ public:
private:
#if defined(_MSC_VER) && _MSC_VER < 1900
// MSVC 2013 workarounds, sigh ...
union
{
union {
char aligned_char[sizeof(T) * N];
double dummy_aligner;
} u;
@ -86,72 +85,72 @@ template <typename T>
class VectorView
{
public:
T &operator[](size_t i)
T &operator[](size_t i) SPIRV_CROSS_NOEXCEPT
{
return ptr[i];
}
const T &operator[](size_t i) const
const T &operator[](size_t i) const SPIRV_CROSS_NOEXCEPT
{
return ptr[i];
}
bool empty() const
bool empty() const SPIRV_CROSS_NOEXCEPT
{
return buffer_size == 0;
}
size_t size() const
size_t size() const SPIRV_CROSS_NOEXCEPT
{
return buffer_size;
}
T *data()
T *data() SPIRV_CROSS_NOEXCEPT
{
return ptr;
}
const T *data() const
const T *data() const SPIRV_CROSS_NOEXCEPT
{
return ptr;
}
T *begin()
T *begin() SPIRV_CROSS_NOEXCEPT
{
return ptr;
}
T *end()
T *end() SPIRV_CROSS_NOEXCEPT
{
return ptr + buffer_size;
}
const T *begin() const
const T *begin() const SPIRV_CROSS_NOEXCEPT
{
return ptr;
}
const T *end() const
const T *end() const SPIRV_CROSS_NOEXCEPT
{
return ptr + buffer_size;
}
T &front()
T &front() SPIRV_CROSS_NOEXCEPT
{
return ptr[0];
}
const T &front() const
const T &front() const SPIRV_CROSS_NOEXCEPT
{
return ptr[0];
}
T &back()
T &back() SPIRV_CROSS_NOEXCEPT
{
return ptr[buffer_size - 1];
}
const T &back() const
const T &back() const SPIRV_CROSS_NOEXCEPT
{
return ptr[buffer_size - 1];
}
@ -195,13 +194,13 @@ template <typename T, size_t N = 8>
class SmallVector : public VectorView<T>
{
public:
SmallVector()
SmallVector() SPIRV_CROSS_NOEXCEPT
{
this->ptr = stack_storage.data();
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()
{
auto count = size_t(arg_list_end - arg_list_begin);
@ -246,14 +245,17 @@ public:
return *this;
}
SmallVector(const SmallVector &other)
SmallVector(const SmallVector &other) SPIRV_CROSS_NOEXCEPT
: SmallVector()
{
*this = other;
}
SmallVector &operator=(const SmallVector &other)
SmallVector &operator=(const SmallVector &other) SPIRV_CROSS_NOEXCEPT
{
if (this == &other)
return *this;
clear();
reserve(other.buffer_size);
for (size_t i = 0; i < other.buffer_size; i++)
@ -262,7 +264,7 @@ public:
return *this;
}
explicit SmallVector(size_t count)
explicit SmallVector(size_t count) SPIRV_CROSS_NOEXCEPT
: SmallVector()
{
resize(count);
@ -275,28 +277,28 @@ public:
free(this->ptr);
}
void clear()
void clear() SPIRV_CROSS_NOEXCEPT
{
for (size_t i = 0; i < this->buffer_size; i++)
this->ptr[i].~T();
this->buffer_size = 0;
}
void push_back(const T &t)
void push_back(const T &t) SPIRV_CROSS_NOEXCEPT
{
reserve(this->buffer_size + 1);
new (&this->ptr[this->buffer_size]) T(t);
this->buffer_size++;
}
void push_back(T &&t)
void push_back(T &&t) SPIRV_CROSS_NOEXCEPT
{
reserve(this->buffer_size + 1);
new (&this->ptr[this->buffer_size]) T(std::move(t));
this->buffer_size++;
}
void pop_back()
void pop_back() SPIRV_CROSS_NOEXCEPT
{
// Work around false positive warning on GCC 8.3.
// Calling pop_back on empty vector is undefined.
@ -305,14 +307,14 @@ public:
}
template <typename... Ts>
void emplace_back(Ts &&... ts)
void emplace_back(Ts &&... ts) SPIRV_CROSS_NOEXCEPT
{
reserve(this->buffer_size + 1);
new (&this->ptr[this->buffer_size]) T(std::forward<Ts>(ts)...);
this->buffer_size++;
}
void reserve(size_t count)
void reserve(size_t count) SPIRV_CROSS_NOEXCEPT
{
if (count > buffer_capacity)
{
@ -328,8 +330,9 @@ public:
T *new_buffer =
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)
SPIRV_CROSS_THROW("Out of memory.");
std::terminate();
// In case for some reason two allocations both come from same stack.
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);
if (itr == this->end())
@ -375,8 +378,10 @@ public:
// Need to allocate new buffer. Move everything to a new buffer.
T *new_buffer =
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)
SPIRV_CROSS_THROW("Out of memory.");
std::terminate();
// First, move elements from source buffer to new buffer.
// 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);
}
T *erase(T *itr)
T *erase(T *itr) SPIRV_CROSS_NOEXCEPT
{
std::move(itr + 1, this->end(), itr);
this->ptr[--this->buffer_size].~T();
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())
{
@ -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)
{

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");
* 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");
* 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);
}
// 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)
{
// 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,
// otherwise, make sure the name is a valid identifier.
if (i == 0)
c = isalpha(c) ? c : '_';
c = is_alpha(c) ? c : '_';
else if (i == 2 && str[0] == '_' && str[1] == 'm')
c = isalpha(c) ? c : '_';
c = is_alpha(c) ? c : '_';
else
c = isalnum(c) ? c : '_';
c = is_alphanumeric(c) ? c : '_';
}
else
{
// _<num> variables are reserved by the internal implementation,
// otherwise, make sure the name is a valid identifier.
if (i == 0 || (str[0] == '_' && i == 1))
c = isalpha(c) ? c : '_';
c = is_alpha(c) ? c : '_';
else
c = isalnum(c) ? c : '_';
c = is_alphanumeric(c) ? c : '_';
}
}
return str;
@ -255,6 +266,14 @@ void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument)
dec.offset = argument;
break;
case DecorationXfbBuffer:
dec.xfb_buffer = argument;
break;
case DecorationXfbStride:
dec.xfb_stride = argument;
break;
case DecorationArrayStride:
dec.array_stride = argument;
break;
@ -326,6 +345,14 @@ void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decor
dec.offset = argument;
break;
case DecorationXfbBuffer:
dec.xfb_buffer = argument;
break;
case DecorationXfbStride:
dec.xfb_stride = argument;
break;
case DecorationSpecId:
dec.spec_id = argument;
break;
@ -439,6 +466,10 @@ uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const
return dec.component;
case DecorationOffset:
return dec.offset;
case DecorationXfbBuffer:
return dec.xfb_buffer;
case DecorationXfbStride:
return dec.xfb_stride;
case DecorationBinding:
return dec.binding;
case DecorationDescriptorSet:
@ -503,6 +534,14 @@ void ParsedIR::unset_decoration(ID id, Decoration decoration)
dec.offset = 0;
break;
case DecorationXfbBuffer:
dec.xfb_buffer = 0;
break;
case DecorationXfbStride:
dec.xfb_stride = 0;
break;
case DecorationBinding:
dec.binding = 0;
break;
@ -573,6 +612,10 @@ uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration d
return dec.binding;
case DecorationOffset:
return dec.offset;
case DecorationXfbBuffer:
return dec.xfb_buffer;
case DecorationXfbStride:
return dec.xfb_stride;
case DecorationSpecId:
return dec.spec_id;
case DecorationIndex:
@ -661,6 +704,14 @@ void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration dec
dec.offset = 0;
break;
case DecorationXfbBuffer:
dec.xfb_buffer = 0;
break;
case DecorationXfbStride:
dec.xfb_stride = 0;
break;
case DecorationSpecId:
dec.spec_id = 0;
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");
* 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");
* 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");
* 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");
* 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_CHAIN_ONLY_BIT = 1 << 1,
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;
@ -275,6 +276,9 @@ protected:
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;
template <typename T>
@ -427,6 +431,7 @@ protected:
void emit_buffer_block_legacy(const SPIRVariable &var);
void emit_buffer_block_flattened(const SPIRVariable &type);
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_glsl(const SPIRVariable &var);
void emit_interface_block(const SPIRVariable &type);
@ -463,6 +468,8 @@ protected:
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,
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,
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,
@ -503,7 +510,7 @@ protected:
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,
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,
const SPIRType &target_type, uint32_t offset);
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 word_stride, bool *need_transpose = nullptr,
uint32_t *matrix_stride = nullptr,
uint32_t *array_stride = nullptr,
bool ptr_chain = false);
const char *index_to_swizzle(uint32_t index);
@ -583,6 +591,7 @@ protected:
bool check_atomic_image(uint32_t id);
virtual void replace_illegal_names();
void replace_illegal_names(const std::unordered_set<std::string> &keywords);
virtual void emit_entry_point_declarations();
void replace_fragment_output(SPIRVariable &var);
@ -705,6 +714,8 @@ protected:
void propagate_nonuniform_qualifier(uint32_t id);
static const char *vector_swizzle(int vecsize, int index);
private:
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");
* 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",
};
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
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(keywords);
CompilerGLSL::replace_illegal_names();
}
@ -2934,16 +2926,15 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
{
// TODO: Basic implementation, might need special consideration for RW/RO structured buffers,
// RW/RO images, and so on.
const auto &type = get<SPIRType>(var.basetype);
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 "";
const auto &type = get<SPIRType>(var.basetype);
char space = '\0';
HLSLBindingFlags resource_flags = 0;
HLSLBindingFlagBits resource_flags = HLSL_BINDING_AUTO_NONE_BIT;
switch (type.basetype)
{
@ -3011,8 +3002,16 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
if (!space)
return "";
return to_resource_register(resource_flags, space, get_decoration(var.self, DecorationBinding),
get_decoration(var.self, DecorationDescriptorSet));
uint32_t desc_set =
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)
@ -3025,10 +3024,54 @@ string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var)
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)
return join(" : register(", space, binding, ", space", space_set, ")");
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);
@ -3410,14 +3503,18 @@ string CompilerHLSL::read_access_chain(const SPIRAccessChain &chain)
target_type.vecsize = type.vecsize;
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())
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;
@ -3525,7 +3622,13 @@ string CompilerHLSL::read_access_chain(const SPIRAccessChain &chain)
if (!bitcast_op.empty())
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)
@ -3542,33 +3645,138 @@ void CompilerHLSL::emit_load(const Instruction &instruction)
if (has_decoration(ptr, DecorationNonUniformEXT))
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);
if (type.columns > 1 || !type.array.empty() || type.basetype == SPIRType::Struct)
forward = false;
bool composite_load = !type.array.empty() || type.basetype == SPIRType::Struct;
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);
if (composite_load)
{
// We cannot make this work in one single expression as we might have nested structures and arrays,
// so unroll the load to an uninitialized temporary.
emit_uninitialized_temporary_expression(result_type, id);
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
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);
@ -3583,12 +3791,20 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
target_type.vecsize = type.vecsize;
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())
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)
{
@ -3611,7 +3827,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
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);
if (!bitcast_op.empty())
store_expr = join(bitcast_op, "(", store_expr, ")");
@ -3622,7 +3838,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
// Strided store.
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)
{
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++)
{
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);
if (!bitcast_op.empty())
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++)
{
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);
auto bitcast_op = bitcast_glsl_op(target_type, type);
if (!bitcast_op.empty())
@ -3693,7 +3910,7 @@ void CompilerHLSL::emit_store(const Instruction &instruction)
auto ops = stream(instruction);
auto *chain = maybe_get<SPIRAccessChain>(ops[0]);
if (chain)
write_access_chain(*chain, ops[1]);
write_access_chain(*chain, ops[1], {});
else
CompilerGLSL::emit_instruction(instruction);
}
@ -3723,7 +3940,10 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction)
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]);
string base;
@ -3745,6 +3965,7 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction)
}
uint32_t matrix_stride = 0;
uint32_t array_stride = 0;
bool row_major_matrix = false;
// Inherit matrix information.
@ -3752,15 +3973,17 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction)
{
matrix_stride = chain->matrix_stride;
row_major_matrix = chain->row_major_matrix;
array_stride = chain->array_stride;
}
auto offsets =
flattened_access_chain_offset(*basetype, &ops[3 + to_plain_buffer_length],
length - 3 - to_plain_buffer_length, 0, 1, &row_major_matrix, &matrix_stride);
auto offsets = flattened_access_chain_offset(*basetype, &ops[3 + to_plain_buffer_length],
length - 3 - to_plain_buffer_length, 0, 1, &row_major_matrix,
&matrix_stride, &array_stride);
auto &e = set<SPIRAccessChain>(ops[1], ops[0], type.storage, base, offsets.first, offsets.second);
e.row_major_matrix = row_major_matrix;
e.matrix_stride = matrix_stride;
e.array_stride = array_stride;
e.immutable = should_forward(ops[2]);
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]));
};
// 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_BitOr(expr) ""
#define make_inclusive_BitXor(expr) ""
@ -4007,20 +4235,34 @@ case OpGroupNonUniform##op: \
SPIRV_CROSS_THROW("Invalid group operation."); \
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(FMul, Product, true)
HLSL_GROUP_OP(FMin, Min, false)
HLSL_GROUP_OP(FMax, Max, false)
HLSL_GROUP_OP(IAdd, Sum, true)
HLSL_GROUP_OP(IMul, Product, true)
HLSL_GROUP_OP(SMin, Min, false)
HLSL_GROUP_OP(SMax, Max, false)
HLSL_GROUP_OP(UMin, Min, false)
HLSL_GROUP_OP(UMax, Max, false)
HLSL_GROUP_OP_CAST(SMin, Min, int_type)
HLSL_GROUP_OP_CAST(SMax, Max, int_type)
HLSL_GROUP_OP_CAST(UMin, Min, uint_type)
HLSL_GROUP_OP_CAST(UMax, Max, uint_type)
HLSL_GROUP_OP(BitwiseAnd, BitAnd, false)
HLSL_GROUP_OP(BitwiseOr, BitOr, false)
HLSL_GROUP_OP(BitwiseXor, BitXor, false)
#undef HLSL_GROUP_OP
#undef HLSL_GROUP_OP_CAST
// clang-format on
case OpGroupNonUniformQuadSwap:
@ -5030,3 +5272,21 @@ void CompilerHLSL::emit_block_hints(const SPIRBlock &block)
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");
* 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().
enum HLSLBindingFlagBits
{
HLSL_BINDING_AUTO_NONE_BIT = 0,
// 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.
// 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;
// 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
{
public:
@ -145,6 +169,14 @@ public:
// Controls how resource bindings are declared in the output HLSL.
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:
std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
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_resource_binding(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_access_chain(const Instruction &instruction);
void emit_load(const Instruction &instruction);
std::string read_access_chain(const SPIRAccessChain &chain);
void write_access_chain(const SPIRAccessChain &chain, uint32_t value);
void read_access_chain(std::string *expr, const std::string &lhs, const SPIRAccessChain &chain);
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_atomic(const uint32_t *ops, uint32_t length, spv::Op op);
void emit_subgroup_op(const Instruction &i) override;
@ -257,6 +296,12 @@ private:
std::vector<RootConstants> root_constants_layout;
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

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");
* 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 };
}
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)
{
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;
}
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 };
auto itr = resource_bindings.find(tuple);
@ -1003,6 +1009,9 @@ string CompilerMSL::compile()
fixup_image_load_store_access();
set_enabled_interface_variables(get_active_interface_variables());
if (msl_options.force_active_argument_buffer_resources)
activate_argument_buffer_resources();
if (swizzle_buffer_id)
active_interface_variables.insert(swizzle_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,
SPIRType &ib_type, SPIRVariable &var, bool strip_array)
SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta)
{
bool is_builtin = is_builtin_variable(var);
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;
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;
auto &type = get<SPIRType>(type_id);
uint32_t target_components = 0;
uint32_t type_components = type.vecsize;
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.
if (get_decoration_bitset(var.self).get(DecorationLocation) && msl_options.pad_fragment_output_components &&
get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput)
if (location_meta)
{
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);
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
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);
vars_needing_early_declaration.push_back(var.self);
entry_func.fixup_hooks_out.push_back([=, &var]() {
SPIRType &padded_type = this->get<SPIRType>(type_id);
statement(qual_var_name, " = ", remap_swizzle(padded_type, type_components, to_name(var.self)), ";");
});
if (padded_output)
{
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;
if (var.storage == StorageClassOutput && var.initializer != ID(0))
{
entry_func.fixup_hooks_in.push_back(
[=, &var]() { statement(qual_var_name, " = ", to_expression(var.initializer), ";"); });
if (padded_output || padded_input)
{
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
@ -1522,10 +1609,14 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
uint32_t locn = get_decoration(var.self, DecorationLocation);
if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
{
type_id = ensure_correct_attribute_type(var.basetype, locn);
var.basetype = type_id;
type_id = ensure_correct_attribute_type(var.basetype, locn,
location_meta ? location_meta->num_components : type.vecsize);
if (!location_meta)
var.basetype = 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;
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);
}
if (get_decoration_bitset(var.self).get(DecorationComponent))
if (!location_meta)
{
uint32_t comp = get_decoration(var.self, DecorationComponent);
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp);
if (get_decoration_bitset(var.self).get(DecorationComponent))
{
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))
@ -1569,14 +1663,18 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
if (is_sample)
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,
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 &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;
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));
bool flatten_from_ib_var = false;
string flatten_from_ib_mbr_name;
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());
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_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,
// not from a function variable.
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.
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);
// Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped.
if (!strip_array)
if (!meta.strip_array)
{
switch (storage)
{
@ -1728,7 +1829,8 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
";");
}
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
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,
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 &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;
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);
bool flatten_from_ib_var = false;
string flatten_from_ib_mbr_name;
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());
ib_type.member_types.push_back(mbr_type_id);
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,
// 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))
{
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);
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);
// Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
if (!strip_array)
if (!meta.strip_array)
{
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]() {
if (flatten_from_ib_var)
{
statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".",
to_member_name(var_type, mbr_idx), "[", i, "];");
statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", flatten_from_ib_mbr_name, "[", i,
"];");
}
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,
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);
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
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,
// so redirect to qualified 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.
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
// 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()))
{
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,
SPIRVariable &var, bool strip_array)
SPIRVariable &var, InterfaceBlockMeta &meta)
{
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
// 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
// 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);
auto builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
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
// 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
// composite members directly, because the stage-out structure is stored to a buffer,
// 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
{
@ -2200,7 +2306,8 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
if (!is_builtin || has_active_builtin(builtin, storage))
{
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;
// 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)
{
add_composite_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx,
strip_array);
meta);
}
else
{
add_plain_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx,
strip_array);
add_plain_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx, meta);
}
}
}
}
}
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);
}
@ -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 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;
// 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.
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
{
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 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) {
if (var.storage != storage)
return;
@ -2347,6 +2463,28 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
(!is_builtin || is_interface_block_builtin))
{
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() == ExecutionModelTessellationEvaluation && storage == StorageClassInput)) &&
!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.
@ -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.
// If it is, simply return the given type 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 p_va = vtx_attrs_by_location.find(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)
{
@ -2616,19 +2761,27 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
case SPIRType::UByte:
case SPIRType::UShort:
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::Int:
break;
default:
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 base_type_id = next_id++;
auto &base_type = set<SPIRType>(base_type_id);
base_type = type;
base_type.basetype = type.basetype == SPIRType::Short ? SPIRType::UShort : SPIRType::UInt;
base_type.pointer = false;
if (num_components != 0)
base_type.vecsize = num_components;
if (!type.pointer)
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::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:
break;
default:
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 base_type_id = next_id++;
auto &base_type = set<SPIRType>(base_type_id);
base_type = type;
base_type.basetype = SPIRType::UInt;
base_type.pointer = false;
if (num_components != 0)
base_type.vecsize = num_components;
if (!type.pointer)
return base_type_id;
@ -2674,7 +2835,8 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
}
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;
}
@ -3268,8 +3430,7 @@ string CompilerMSL::unpack_expression_type(string expr_str, const SPIRType &type
assert(type.vecsize >= 1 && type.vecsize <= 3);
return enclose_expression(expr_str) + swizzle_lut[type.vecsize - 1];
}
else if (physical_type && is_matrix(*physical_type) && is_vector(type) &&
physical_type->vecsize > type.vecsize)
else if (physical_type && is_matrix(*physical_type) && is_vector(type) && physical_type->vecsize > type.vecsize)
{
// Extract column from padded matrix.
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);
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) + ")";
else
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) ||
basetype == SPIRType::AtomicCounter;
auto resource_decoration = use_secondary_binding ?
SPIRVCrossDecorationResourceIndexSecondary :
SPIRVCrossDecorationResourceIndexPrimary;
auto resource_decoration =
use_secondary_binding ? SPIRVCrossDecorationResourceIndexSecondary : SPIRVCrossDecorationResourceIndexPrimary;
if (plane == 1)
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.
// 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;
auto &type = get<SPIRType>(var.basetype);
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.
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)
{
// Allocate from a flat ID binding space.
resource_index = next_metal_resource_ids[desc_set];
next_metal_resource_ids[desc_set] += binding_stride;
resource_index = next_metal_resource_ids[var_desc_set];
next_metal_resource_ids[var_desc_set] += binding_stride;
}
else
{
@ -10244,19 +10407,31 @@ void CompilerMSL::replace_illegal_names()
};
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))
dec.alias += "0";
});
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))
dec.alias += "0";
});
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))
mbr_dec.alias += "0";
});
@ -10709,6 +10884,11 @@ void CompilerMSL::emit_subgroup_op(const Instruction &i)
if (!msl_options.supports_msl_version(2))
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())
{
switch (op)
@ -10731,7 +10911,7 @@ void CompilerMSL::emit_subgroup_op(const Instruction &i)
switch (op)
{
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 OpGroupNonUniformShuffle:
case OpGroupNonUniformShuffleXor:
@ -10859,6 +11039,7 @@ case OpGroupNonUniform##op: \
MSL_GROUP_OP(IMul, product)
#undef MSL_GROUP_OP
// The others, unfortunately, don't support InclusiveScan or ExclusiveScan.
#define MSL_GROUP_OP(op, msl_op) \
case OpGroupNonUniform##op: \
{ \
@ -10881,12 +11062,36 @@ case OpGroupNonUniform##op: \
SPIRV_CROSS_THROW("Invalid group operation."); \
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(FMax, max)
MSL_GROUP_OP(SMin, min)
MSL_GROUP_OP(SMax, max)
MSL_GROUP_OP(UMin, min)
MSL_GROUP_OP(UMax, max)
MSL_GROUP_OP_CAST(SMin, min, int_type)
MSL_GROUP_OP_CAST(SMax, max, int_type)
MSL_GROUP_OP_CAST(UMin, min, uint_type)
MSL_GROUP_OP_CAST(UMax, max, uint_type)
MSL_GROUP_OP(BitwiseAnd, and)
MSL_GROUP_OP(BitwiseOr, or)
MSL_GROUP_OP(BitwiseXor, xor)
@ -10894,6 +11099,8 @@ case OpGroupNonUniform##op: \
MSL_GROUP_OP(LogicalOr, or)
MSL_GROUP_OP(LogicalXor, xor)
// clang-format on
#undef MSL_GROUP_OP
#undef MSL_GROUP_OP_CAST
case OpGroupNonUniformQuadSwap:
{
@ -12243,6 +12450,7 @@ void CompilerMSL::analyze_argument_buffers()
uint32_t plane;
};
SmallVector<Resource> resources_in_set[kMaxArgumentBuffers];
SmallVector<uint32_t> inline_block_vars;
bool set_needs_swizzle_buffer[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)
{
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 });
}
}
else if (inline_uniform_blocks.count(SetBindingPair{ desc_set, binding }))
{
inline_block_vars.push_back(var_id);
}
else if (!constexpr_sampler)
{
// 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)
{
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++)
{
auto &resources = resources_in_set[desc_set];
@ -12471,6 +12695,12 @@ void CompilerMSL::analyze_argument_buffers()
buffer_type.member_types.push_back(var.basetype);
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
{
// 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
{
return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding);
}
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;
uint32_t desc_set = get_decoration(self, DecorationDescriptorSet);
if (descriptor_set_is_argument_buffer(desc_set))
active_interface_variables.insert(self);
});
}

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");
* 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
// 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
// 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
// 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.
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()
{
return platform == iOS;
@ -425,6 +432,13 @@ public:
// an offset taken from the dynamic offset buffer.
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.
// This corresponds to VK_KHR_push_descriptor in Vulkan.
void add_discrete_descriptor_set(uint32_t desc_set);
@ -440,7 +454,7 @@ public:
// Constexpr samplers are always assumed to be emitted.
// No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped
// 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().
// 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_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,
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,
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,
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,
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,
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);
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);
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_functions();
@ -775,28 +800,6 @@ protected:
std::set<std::string> typedef_lines;
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;
uint32_t next_metal_resource_index_buffer = 0;
@ -858,6 +861,8 @@ protected:
// Must be ordered since array is in a specific order.
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_discrete_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 activate_argument_buffer_resources();
// OpcodeHandler that handles several MSL preprocessing operations.
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");
* 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");
* 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");
* 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)
json_stream->emit_json_array_value(value);
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)
{
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]);
emit_type_array(membertype);
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);
if (dec.decoration_flags.get(DecorationOffset))
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->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("variable_id", spec_const.id);
switch (type.basetype)
{
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");
* you may not use this file except in compliance with the License.