mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-10-29 12:15:30 +00:00
Updated spirv-cross.
This commit is contained in:
committed by
Branimir Karadžić
parent
1473b05e5e
commit
affc050e21
Vendored
+12
@@ -694,6 +694,8 @@ struct CLIArguments
|
||||
uint32_t glsl_ovr_multiview_view_count = 0;
|
||||
SmallVector<pair<uint32_t, uint32_t>> glsl_ext_framebuffer_fetch;
|
||||
bool glsl_ext_framebuffer_fetch_noncoherent = false;
|
||||
uint32_t glsl_descriptor_heap_set = UINT32_MAX;
|
||||
uint32_t glsl_descriptor_heap_binding = UINT32_MAX;
|
||||
bool vulkan_glsl_disable_ext_samplerless_texture_functions = false;
|
||||
bool emit_line_directives = false;
|
||||
bool enable_storage_image_qualifier_deduction = true;
|
||||
@@ -821,6 +823,7 @@ static void print_help_glsl()
|
||||
"\t\tPrimary use case is supporting external samplers in ESSL for video rendering on Android where you could remap a texture to a YUV one.\n"
|
||||
"\t[--glsl-force-flattened-io-blocks]:\n\t\tAlways flatten I/O blocks and structs.\n"
|
||||
"\t[--glsl-ovr-multiview-view-count count]:\n\t\tIn GL_OVR_multiview2, specify layout(num_views).\n"
|
||||
"\t[--glsl-descriptor-heap-set-binding desc_set binding]:\n\t\tInstead of layout(descriptor_heap), emit layout(set = desc_set, binding = binding) instead for compatibility with mapping API.\n"
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
@@ -1458,6 +1461,10 @@ static string compile_iteration(const CLIArguments &args, std::vector<uint32_t>
|
||||
opts.force_recompile_max_debug_iterations = args.force_recompile_max_debug_iterations;
|
||||
compiler->set_common_options(opts);
|
||||
|
||||
// This is enough for Vulkan mapping API.
|
||||
if (args.glsl_descriptor_heap_set != UINT32_MAX)
|
||||
compiler->remap_descriptor_heap(ResourceTypeUnknown, args.glsl_descriptor_heap_set, args.glsl_descriptor_heap_binding);
|
||||
|
||||
for (auto &fetch : args.glsl_ext_framebuffer_fetch)
|
||||
compiler->remap_ext_framebuffer_fetch(fetch.first, fetch.second, !args.glsl_ext_framebuffer_fetch_noncoherent);
|
||||
|
||||
@@ -1704,6 +1711,11 @@ static int main_inner(int argc, char *argv[])
|
||||
cbs.add("--glsl-ext-framebuffer-fetch-noncoherent", [&args](CLIParser &) {
|
||||
args.glsl_ext_framebuffer_fetch_noncoherent = true;
|
||||
});
|
||||
cbs.add("--glsl-descriptor-heap-set-binding", [&args](CLIParser &parser)
|
||||
{
|
||||
args.glsl_descriptor_heap_set = parser.next_uint();
|
||||
args.glsl_descriptor_heap_binding = parser.next_uint();
|
||||
});
|
||||
cbs.add("--vulkan-glsl-disable-ext-samplerless-texture-functions",
|
||||
[&args](CLIParser &) { args.vulkan_glsl_disable_ext_samplerless_texture_functions = true; });
|
||||
cbs.add("--disable-storage-image-qualifier-deduction",
|
||||
|
||||
Vendored
+477
-152
File diff suppressed because it is too large
Load Diff
Vendored
+477
-152
File diff suppressed because it is too large
Load Diff
+25
-1
@@ -608,7 +608,8 @@ struct SPIRType : IVariant
|
||||
FloatE4M3,
|
||||
FloatE5M2,
|
||||
|
||||
Tensor
|
||||
Tensor,
|
||||
DescriptorHeapBuffer
|
||||
};
|
||||
|
||||
// Scalar/vector/matrix support.
|
||||
@@ -655,6 +656,11 @@ struct SPIRType : IVariant
|
||||
uint32_t rank;
|
||||
uint32_t shape;
|
||||
} tensor;
|
||||
|
||||
struct
|
||||
{
|
||||
spv::StorageClass storage;
|
||||
} descriptor_heap_buffer;
|
||||
} ext;
|
||||
|
||||
spv::StorageClass storage = spv::StorageClassGeneric;
|
||||
@@ -808,6 +814,13 @@ struct SPIRExpression : IVariant
|
||||
// Whether or not gl_MeshVerticesEXT[].gl_Position (as a whole or .y) is referenced
|
||||
bool access_meshlet_position_y = false;
|
||||
|
||||
// If this expression represents a OpBufferPointerEXT cast.
|
||||
bool buffer_pointer = false;
|
||||
|
||||
// Temporaries which can remain forwarded as long as this variable is not modified.
|
||||
// Only used for buffer pointers.
|
||||
SmallVector<ID> buffer_pointer_dependees;
|
||||
|
||||
// A list of expressions which this expression depends on.
|
||||
SmallVector<ID> expression_dependencies;
|
||||
|
||||
@@ -1200,6 +1213,12 @@ struct SPIRVariable : IVariant
|
||||
// Used to find global LUTs
|
||||
bool is_written_to = false;
|
||||
|
||||
// Untyped pointer. The pointer of the variable is effectively void.
|
||||
// The underlying payload for allocation is in alloca_type, but may be 0 too.
|
||||
// This is mostly here to support descriptor heap proxy.
|
||||
bool untyped = false;
|
||||
ID untyped_alloca_type = 0;
|
||||
|
||||
SPIRFunction::Parameter *parameter = nullptr;
|
||||
|
||||
SPIRV_CROSS_DECLARE_CLONE(SPIRVariable)
|
||||
@@ -1535,6 +1554,9 @@ struct SPIRConstant : IVariant
|
||||
// preprocessor directives before compiling the shader.
|
||||
std::string specialization_constant_macro_name;
|
||||
|
||||
// ConstantSizeOfEXT.
|
||||
ID size_of_type = 0;
|
||||
|
||||
SPIRV_CROSS_DECLARE_CLONE(SPIRConstant)
|
||||
};
|
||||
|
||||
@@ -1815,10 +1837,12 @@ struct Meta
|
||||
uint32_t set = 0;
|
||||
uint32_t binding = 0;
|
||||
uint32_t offset = 0;
|
||||
uint32_t offset_id = 0;
|
||||
uint32_t xfb_buffer = 0;
|
||||
uint32_t xfb_stride = 0;
|
||||
uint32_t stream = 0;
|
||||
uint32_t array_stride = 0;
|
||||
uint32_t array_stride_id = 0;
|
||||
uint32_t matrix_stride = 0;
|
||||
uint32_t input_attachment = 0;
|
||||
uint32_t spec_id = 0;
|
||||
|
||||
Vendored
+266
-6
@@ -78,6 +78,11 @@ string Compiler::compile()
|
||||
bool Compiler::variable_storage_is_aliased(const SPIRVariable &v)
|
||||
{
|
||||
auto &type = get<SPIRType>(v.basetype);
|
||||
|
||||
// Untyped pointer, assume full aliasing.
|
||||
if (type.basetype == SPIRType::Void)
|
||||
return true;
|
||||
|
||||
bool ssbo = v.storage == StorageClassStorageBuffer ||
|
||||
ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
|
||||
bool image = type.basetype == SPIRType::Image;
|
||||
@@ -429,10 +434,19 @@ SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain)
|
||||
return var;
|
||||
}
|
||||
|
||||
SPIRExpression *Compiler::maybe_get_backing_buffer_pointer(uint32_t chain)
|
||||
{
|
||||
auto *expr = maybe_get<SPIRExpression>(chain);
|
||||
while (expr && !expr->buffer_pointer && expr->loaded_from)
|
||||
expr = maybe_get<SPIRExpression>(expr->loaded_from);
|
||||
return expr && expr->buffer_pointer ? expr : nullptr;
|
||||
}
|
||||
|
||||
void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded)
|
||||
{
|
||||
auto &e = get<SPIRExpression>(expr);
|
||||
auto *var = maybe_get_backing_variable(chain);
|
||||
auto *buffer_pointer = maybe_get_backing_buffer_pointer(chain);
|
||||
|
||||
if (var)
|
||||
{
|
||||
@@ -447,6 +461,13 @@ void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded)
|
||||
if (var && var->parameter)
|
||||
var->parameter->read_count++;
|
||||
}
|
||||
else if (buffer_pointer)
|
||||
{
|
||||
e.loaded_from = buffer_pointer->self;
|
||||
// If the backing variable is immutable, we do not need to depend on the variable.
|
||||
if (forwarded && !is_immutable(buffer_pointer->self))
|
||||
buffer_pointer->buffer_pointer_dependees.push_back(e.self);
|
||||
}
|
||||
}
|
||||
|
||||
void Compiler::register_write(uint32_t chain)
|
||||
@@ -464,6 +485,8 @@ void Compiler::register_write(uint32_t chain)
|
||||
var = maybe_get<SPIRVariable>(access_chain->loaded_from);
|
||||
}
|
||||
|
||||
auto *buffer_pointer = maybe_get_backing_buffer_pointer(chain);
|
||||
|
||||
auto &chain_type = expression_type(chain);
|
||||
|
||||
if (var)
|
||||
@@ -508,6 +531,10 @@ void Compiler::register_write(uint32_t chain)
|
||||
force_recompile();
|
||||
}
|
||||
}
|
||||
else if (buffer_pointer)
|
||||
{
|
||||
flush_dependees(*buffer_pointer);
|
||||
}
|
||||
else if (chain_type.pointer)
|
||||
{
|
||||
// If we stored through a variable pointer, then we don't know which
|
||||
@@ -529,6 +556,16 @@ void Compiler::flush_dependees(SPIRVariable &var)
|
||||
var.dependees.clear();
|
||||
}
|
||||
|
||||
void Compiler::flush_dependees(SPIRExpression &expr)
|
||||
{
|
||||
// A little ugly to split things up like this since BufferPointerEXT is a weird case
|
||||
// where it's both an expression (chain into global heap) and a memory declaration at the same time ...
|
||||
assert(expr.buffer_pointer);
|
||||
for (auto dep : expr.buffer_pointer_dependees)
|
||||
invalid_expressions.insert(dep);
|
||||
expr.buffer_pointer_dependees.clear();
|
||||
}
|
||||
|
||||
void Compiler::flush_all_aliased_variables()
|
||||
{
|
||||
for (auto aliased : aliased_variables)
|
||||
@@ -539,6 +576,8 @@ void Compiler::flush_all_atomic_capable_variables()
|
||||
{
|
||||
for (auto global : global_variables)
|
||||
flush_dependees(get<SPIRVariable>(global));
|
||||
for (auto global : buffer_pointer_variables)
|
||||
flush_dependees(get<SPIRExpression>(global));
|
||||
flush_all_aliased_variables();
|
||||
}
|
||||
|
||||
@@ -560,6 +599,8 @@ void Compiler::flush_all_active_variables()
|
||||
flush_dependees(get<SPIRVariable>(arg.id));
|
||||
for (auto global : global_variables)
|
||||
flush_dependees(get<SPIRVariable>(global));
|
||||
for (auto global : buffer_pointer_variables)
|
||||
flush_dependees(get<SPIRExpression>(global));
|
||||
|
||||
flush_all_aliased_variables();
|
||||
}
|
||||
@@ -668,8 +709,9 @@ bool Compiler::is_hidden_variable(const SPIRVariable &var, bool include_builtins
|
||||
}
|
||||
|
||||
// In SPIR-V 1.4 and up we must also use the active variable interface to disable global variables
|
||||
// which are not part of the entry point.
|
||||
if (ir.get_spirv_version() >= 0x10400 && var.storage != StorageClassGeneric &&
|
||||
// which are not part of the entry point. Library modules have no real entry point so the filter
|
||||
// would hide every global so skip it in that case.
|
||||
if (ir.get_spirv_version() >= 0x10400 && !ir.is_library_module && var.storage != StorageClassGeneric &&
|
||||
var.storage != StorageClassFunction && !interface_variable_exists_in_entry_point(var.self))
|
||||
{
|
||||
return true;
|
||||
@@ -742,17 +784,17 @@ bool Compiler::is_array(const SPIRType &type) const
|
||||
|
||||
bool Compiler::is_pointer(const SPIRType &type) const
|
||||
{
|
||||
return type.op == OpTypePointer && type.basetype != SPIRType::Unknown; // Ignore function pointers.
|
||||
return (type.op == OpTypePointer || type.op == OpTypeUntypedPointerKHR) && type.basetype != SPIRType::Unknown; // Ignore function pointers.
|
||||
}
|
||||
|
||||
bool Compiler::is_physical_pointer(const SPIRType &type) const
|
||||
{
|
||||
return type.op == OpTypePointer && type.storage == StorageClassPhysicalStorageBuffer;
|
||||
return (type.op == OpTypePointer || type.op == OpTypeUntypedPointerKHR) && type.storage == StorageClassPhysicalStorageBuffer;
|
||||
}
|
||||
|
||||
bool Compiler::is_physical_or_buffer_pointer(const SPIRType &type) const
|
||||
{
|
||||
return type.op == OpTypePointer &&
|
||||
return (type.op == OpTypePointer || type.op == OpTypeUntypedPointerKHR) &&
|
||||
(type.storage == StorageClassPhysicalStorageBuffer || type.storage == StorageClassUniform ||
|
||||
type.storage == StorageClassStorageBuffer || type.storage == StorageClassWorkgroup ||
|
||||
type.storage == StorageClassPushConstant);
|
||||
@@ -2068,7 +2110,7 @@ size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, si
|
||||
|
||||
size_t size = get_declared_struct_size(type);
|
||||
auto &last_type = get<SPIRType>(type.member_types.back());
|
||||
if (!last_type.array.empty() && last_type.array_size_literal[0] && last_type.array[0] == 0) // Runtime array
|
||||
if (!last_type.array.empty() && last_type.array_size_literal.back() && last_type.array.back() == 0) // Runtime array
|
||||
size += array_size * type_struct_member_array_stride(type, uint32_t(type.member_types.size() - 1));
|
||||
|
||||
return size;
|
||||
@@ -4799,6 +4841,18 @@ void Compiler::build_function_control_flow_graphs_and_analyze()
|
||||
CFGBuilder handler(*this);
|
||||
handler.function_cfgs[ir.default_entry_point].reset(new CFG(*this, get<SPIRFunction>(ir.default_entry_point)));
|
||||
traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
|
||||
if (ir.is_library_module)
|
||||
{
|
||||
// In library mode, default_entry_point is just the first exported
|
||||
// function. Build a CFG for every other exported function (and its
|
||||
// callees) so per-function analyses below cover all of them.
|
||||
for (auto export_id : ir.library_exported_functions)
|
||||
{
|
||||
auto &func = get<SPIRFunction>(export_id);
|
||||
if (handler.follow_function_call(func))
|
||||
traverse_all_reachable_opcodes(func, handler);
|
||||
}
|
||||
}
|
||||
function_cfgs = std::move(handler.function_cfgs);
|
||||
bool single_function = function_cfgs.size() <= 1;
|
||||
|
||||
@@ -5429,6 +5483,212 @@ void Compiler::analyze_non_block_pointer_types()
|
||||
physical_storage_type_to_alignment = std::move(handler.physical_block_type_meta);
|
||||
}
|
||||
|
||||
void Compiler::analyze_descriptor_heap_types()
|
||||
{
|
||||
struct HeapHandler : OpcodeHandler
|
||||
{
|
||||
bool handle(Op opcode, const uint32_t *args, uint32_t) override
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case OpBufferPointerEXT:
|
||||
{
|
||||
auto &ptr_type = compiler.get<SPIRType>(args[0]);
|
||||
// BufferPointerEXT can return untyped or typed pointers.
|
||||
// If it's typed, we resolve it here.
|
||||
if (ptr_type.basetype == SPIRType::Struct)
|
||||
{
|
||||
DescriptorHeapMeta meta = {};
|
||||
meta.type = ptr_type.self;
|
||||
meta.hlsl_style_stride = hlsl_style_stride_access_chains.count(args[2]);
|
||||
meta.buffer_pointer_id = args[1];
|
||||
meta.storage = ptr_type.storage;
|
||||
meta.nonreadable = compiler.has_decoration(args[1], DecorationNonReadable);
|
||||
meta.nonwritable = compiler.has_decoration(args[1], DecorationNonWritable);
|
||||
meta.coherent = compiler.has_decoration(args[1], DecorationCoherent);
|
||||
meta.is_restrict = compiler.has_decoration(args[1], DecorationRestrict);
|
||||
meta.is_volatile = compiler.has_decoration(args[1], DecorationVolatile);
|
||||
add_unique_type(meta);
|
||||
}
|
||||
buffer_pointers[args[1]] = { args[0], hlsl_style_stride_access_chains.count(args[2]) != 0 };
|
||||
break;
|
||||
}
|
||||
|
||||
case OpUntypedAccessChainKHR:
|
||||
case OpUntypedInBoundsAccessChainKHR:
|
||||
case OpUntypedArrayLengthKHR:
|
||||
{
|
||||
auto &data_type = compiler.get<SPIRType>(args[2]);
|
||||
|
||||
if (compiler.is_pointer(data_type))
|
||||
SPIRV_CROSS_THROW("pointer type not allowed.");
|
||||
|
||||
bool hlsl_style_stride = false;
|
||||
|
||||
// Need to validate the array stride and types. HLLs are not flexible enough to support the full flexibility of SPIR-V.
|
||||
if (BuiltIn(compiler.get_decoration(args[3], DecorationBuiltIn)) == BuiltInResourceHeapEXT)
|
||||
{
|
||||
if (!compiler.is_runtime_size_array(data_type))
|
||||
SPIRV_CROSS_THROW("Descriptor heap must be accessed as a runtime array.");
|
||||
|
||||
// The only meaningful use of this is ArrayStride equal to sizeof(type) right now.
|
||||
uint32_t array_stride_id = compiler.get_decoration(args[2], DecorationArrayStrideIdEXT);
|
||||
if (!array_stride_id)
|
||||
SPIRV_CROSS_THROW("Expected ArrayStrideIdEXT to be set for resource heap.");
|
||||
|
||||
auto *spec_c = compiler.maybe_get<SPIRConstantOp>(array_stride_id);
|
||||
auto *c = compiler.maybe_get<SPIRConstant>(array_stride_id);
|
||||
|
||||
if (!spec_c && !c)
|
||||
SPIRV_CROSS_THROW("Array stride must be some constant expression.");
|
||||
|
||||
if (spec_c)
|
||||
{
|
||||
// This gets potentially infinitely weird, but if we get HLSL-style shaders
|
||||
// we expect the array stride to be max(buffer, image) since all descriptors have equal size in D3D12.
|
||||
// We just have to be a bit loose here since it's impossible to anticipate every theoretical formulation.
|
||||
// Anything non-conforming to strict GLSL is flagged in the codegen output.
|
||||
if (spec_c->opcode == OpSelect)
|
||||
{
|
||||
auto *true_value = compiler.maybe_get<SPIRConstant>(spec_c->arguments[1]);
|
||||
auto *false_value = compiler.maybe_get<SPIRConstant>(spec_c->arguments[2]);
|
||||
hlsl_style_stride = true_value && true_value->size_of_type &&
|
||||
false_value && false_value->size_of_type;
|
||||
}
|
||||
|
||||
if (!hlsl_style_stride)
|
||||
SPIRV_CROSS_THROW("Unusual pattern of descriptor stride detected. This probably cannot be expressed in current GLSL.");
|
||||
}
|
||||
|
||||
if (c && !c->size_of_type)
|
||||
SPIRV_CROSS_THROW("Resource heap array stride must be ConstantSizeOfEXT for high level languages.");
|
||||
|
||||
auto &element_type = compiler.get<SPIRType>(data_type.parent_type);
|
||||
|
||||
if (element_type.basetype == SPIRType::DescriptorHeapBuffer)
|
||||
{
|
||||
if (c && compiler.get<SPIRType>(c->size_of_type).basetype != SPIRType::DescriptorHeapBuffer)
|
||||
SPIRV_CROSS_THROW("Buffer descriptors in heap must be ConstantSizeOfEXT(OpTypeBufferEXT) for GLSL.");
|
||||
}
|
||||
else if (data_type.basetype == SPIRType::Image)
|
||||
{
|
||||
if (c && compiler.get<SPIRType>(c->size_of_type).basetype != SPIRType::Image)
|
||||
SPIRV_CROSS_THROW("Image descriptors in heap must be ConstantSizeOfEXT(OpTypeImage) for GLSL.");
|
||||
}
|
||||
else if (data_type.basetype == SPIRType::AccelerationStructure)
|
||||
{
|
||||
if (c && compiler.get<SPIRType>(c->size_of_type).basetype != SPIRType::AccelerationStructure)
|
||||
SPIRV_CROSS_THROW("RTAS descriptors in heap must be ConstantSizeOfEXT(OpTypeAccelerationStructure) for GLSL.");
|
||||
}
|
||||
}
|
||||
else if (BuiltIn(compiler.get_decoration(args[3], DecorationBuiltIn)) == BuiltInSamplerHeapEXT)
|
||||
{
|
||||
if (!compiler.is_runtime_size_array(data_type))
|
||||
SPIRV_CROSS_THROW("Descriptor heap must be accessed as a runtime array.");
|
||||
|
||||
// The only meaningful use of this is ArrayStride equal to sizeof(sampler) right now.
|
||||
uint32_t array_stride_id = compiler.get_decoration(args[2], DecorationArrayStrideIdEXT);
|
||||
if (!array_stride_id)
|
||||
SPIRV_CROSS_THROW("Expected ArrayStrideIdEXT to be set for sampler heap.");
|
||||
|
||||
auto *c = compiler.maybe_get<SPIRConstant>(array_stride_id);
|
||||
if (!c || !c->size_of_type || compiler.get<SPIRType>(c->size_of_type).basetype != SPIRType::Sampler)
|
||||
SPIRV_CROSS_THROW("Sampler heap array stride must be ConstantSizeOfEXT(OpTypeSampler) for high level languages.");
|
||||
}
|
||||
|
||||
// Remember this for OpBufferPointerEXT.
|
||||
if (hlsl_style_stride)
|
||||
hlsl_style_stride_access_chains.insert(args[1]);
|
||||
|
||||
if (data_type.basetype == SPIRType::SampledImage)
|
||||
{
|
||||
SPIRV_CROSS_THROW("Attempting to access heap as combined sampler image. This does not make sense.");
|
||||
}
|
||||
else if (data_type.basetype == SPIRType::Image ||
|
||||
data_type.basetype == SPIRType::AccelerationStructure ||
|
||||
data_type.basetype == SPIRType::Sampler)
|
||||
{
|
||||
DescriptorHeapMeta meta = {};
|
||||
meta.type = data_type.self;
|
||||
meta.hlsl_style_stride = hlsl_style_stride;
|
||||
add_unique_type(meta);
|
||||
}
|
||||
else if (buffer_pointers.count(args[3]) != 0)
|
||||
{
|
||||
if (!compiler.has_decoration(data_type.self, DecorationBlock) &&
|
||||
!compiler.has_decoration(data_type.self, DecorationBufferBlock))
|
||||
{
|
||||
SPIRV_CROSS_THROW("BufferPointerEXT must reference a block type.");
|
||||
}
|
||||
|
||||
auto &pointer_meta = buffer_pointers[args[3]];
|
||||
auto &buffer_type = compiler.get<SPIRType>(pointer_meta.type);
|
||||
if (buffer_type.basetype == SPIRType::Void)
|
||||
{
|
||||
// This is where the pointer becomes typed, so register it here.
|
||||
DescriptorHeapMeta meta = {};
|
||||
meta.type = data_type.self;
|
||||
meta.hlsl_style_stride = pointer_meta.hlsl_style_stride;
|
||||
meta.buffer_pointer_id = args[3];
|
||||
meta.storage = buffer_type.storage;
|
||||
meta.nonreadable = compiler.has_decoration(args[3], DecorationNonReadable);
|
||||
meta.nonwritable = compiler.has_decoration(args[3], DecorationNonWritable);
|
||||
meta.coherent = compiler.has_decoration(args[3], DecorationCoherent);
|
||||
meta.is_volatile = compiler.has_decoration(args[3], DecorationVolatile);
|
||||
meta.is_restrict = compiler.has_decoration(args[3], DecorationRestrict);
|
||||
add_unique_type(meta);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
explicit HeapHandler(Compiler &compiler_) : OpcodeHandler(compiler_) {}
|
||||
|
||||
std::vector<DescriptorHeapMeta> heap_types;
|
||||
|
||||
struct BufferPointerMeta
|
||||
{
|
||||
TypeID type;
|
||||
bool hlsl_style_stride;
|
||||
};
|
||||
std::unordered_map<uint32_t, BufferPointerMeta> buffer_pointers;
|
||||
std::unordered_set<uint32_t> hlsl_style_stride_access_chains;
|
||||
|
||||
void add_unique_type(const DescriptorHeapMeta &meta)
|
||||
{
|
||||
assert(meta.type != 0);
|
||||
|
||||
for (auto &type : heap_types)
|
||||
{
|
||||
if (type.type == meta.type && type.storage == meta.storage &&
|
||||
type.buffer_pointer_id == meta.buffer_pointer_id &&
|
||||
type.nonreadable == meta.nonreadable &&
|
||||
type.nonwritable == meta.nonwritable &&
|
||||
type.coherent == meta.coherent &&
|
||||
type.is_restrict == meta.is_restrict &&
|
||||
type.hlsl_style_stride == meta.hlsl_style_stride &&
|
||||
type.is_volatile == meta.is_volatile)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
heap_types.push_back(meta);
|
||||
}
|
||||
};
|
||||
|
||||
HeapHandler handler(*this);
|
||||
traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
|
||||
descriptor_heap_types = std::move(handler.heap_types);
|
||||
}
|
||||
|
||||
bool Compiler::InterlockedResourceAccessPrepassHandler::handle(Op op, const uint32_t *, uint32_t)
|
||||
{
|
||||
if (op == OpBeginInvocationInterlockEXT || op == OpEndInvocationInterlockEXT)
|
||||
|
||||
Vendored
+42
@@ -91,6 +91,28 @@ struct BuiltInResource
|
||||
Resource resource;
|
||||
};
|
||||
|
||||
// Needs to stay in sync 1:1 with C API.
|
||||
enum ResourceType
|
||||
{
|
||||
ResourceTypeUnknown = 0,
|
||||
ResourceTypeUniformBuffer = 1,
|
||||
ResourceTypeStorageBuffer = 2,
|
||||
ResourceTypeStageInput = 3,
|
||||
ResourceTypeStageOutput = 4,
|
||||
ResourceTypeSubpassInput = 5,
|
||||
ResourceTypeStorageImage = 6,
|
||||
ResourceTypeSampledImage = 7,
|
||||
ResourceTypeAtomicCounter = 8,
|
||||
ResourceTypePushConstant = 9,
|
||||
ResourceTypeSeparateImage = 10,
|
||||
ResourceTypeSeparateSamplers = 11,
|
||||
ResourceTypeAccelerationStructure = 12,
|
||||
ResourceTypeRayQuery = 13,
|
||||
ResourceTypeShaderRecordBuffer = 14,
|
||||
ResourceTypeGLPlainUniform = 15,
|
||||
ResourceTypeTensor = 16
|
||||
};
|
||||
|
||||
struct ShaderResources
|
||||
{
|
||||
SmallVector<Resource> uniform_buffers;
|
||||
@@ -585,6 +607,7 @@ protected:
|
||||
// (SSBO, image load store, etc)
|
||||
SmallVector<uint32_t> global_variables;
|
||||
SmallVector<uint32_t> aliased_variables;
|
||||
SmallVector<uint32_t> buffer_pointer_variables;
|
||||
|
||||
SPIRFunction *current_function = nullptr;
|
||||
SPIRBlock *current_block = nullptr;
|
||||
@@ -705,6 +728,7 @@ protected:
|
||||
bool expression_is_lvalue(uint32_t id) const;
|
||||
bool variable_storage_is_aliased(const SPIRVariable &var);
|
||||
SPIRVariable *maybe_get_backing_variable(uint32_t chain);
|
||||
SPIRExpression *maybe_get_backing_buffer_pointer(uint32_t chain);
|
||||
|
||||
void register_read(uint32_t expr, uint32_t chain, bool forwarded);
|
||||
void register_write(uint32_t chain);
|
||||
@@ -739,6 +763,7 @@ protected:
|
||||
|
||||
// Dependency tracking for temporaries read from variables.
|
||||
void flush_dependees(SPIRVariable &var);
|
||||
void flush_dependees(SPIRExpression &expr);
|
||||
void flush_all_active_variables();
|
||||
void flush_control_dependent_expressions(uint32_t block);
|
||||
void flush_all_atomic_capable_variables();
|
||||
@@ -1090,6 +1115,23 @@ protected:
|
||||
SmallVector<uint32_t> physical_storage_non_block_pointer_types;
|
||||
std::unordered_map<uint32_t, PhysicalBlockMeta> physical_storage_type_to_alignment;
|
||||
|
||||
struct DescriptorHeapMeta
|
||||
{
|
||||
TypeID type;
|
||||
bool hlsl_style_stride;
|
||||
|
||||
// For buffers
|
||||
ID buffer_pointer_id;
|
||||
StorageClass storage;
|
||||
bool nonwritable;
|
||||
bool nonreadable;
|
||||
bool coherent;
|
||||
bool is_volatile;
|
||||
bool is_restrict;
|
||||
};
|
||||
std::vector<DescriptorHeapMeta> descriptor_heap_types;
|
||||
void analyze_descriptor_heap_types();
|
||||
|
||||
void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler);
|
||||
void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler,
|
||||
bool single_function);
|
||||
|
||||
+32
@@ -79,6 +79,9 @@ ParsedIR &ParsedIR::operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT
|
||||
memory_model = other.memory_model;
|
||||
|
||||
default_entry_point = other.default_entry_point;
|
||||
is_library_module = other.is_library_module;
|
||||
library_exports = std::move(other.library_exports);
|
||||
library_exported_functions = std::move(other.library_exported_functions);
|
||||
sources = std::move(other.sources);
|
||||
loop_iteration_depth_hard = other.loop_iteration_depth_hard;
|
||||
loop_iteration_depth_soft = other.loop_iteration_depth_soft;
|
||||
@@ -111,6 +114,9 @@ ParsedIR &ParsedIR::operator=(const ParsedIR &other)
|
||||
continue_block_to_loop_header = other.continue_block_to_loop_header;
|
||||
entry_points = other.entry_points;
|
||||
default_entry_point = other.default_entry_point;
|
||||
is_library_module = other.is_library_module;
|
||||
library_exports = other.library_exports;
|
||||
library_exported_functions = other.library_exported_functions;
|
||||
sources = other.sources;
|
||||
loop_iteration_depth_hard = other.loop_iteration_depth_hard;
|
||||
loop_iteration_depth_soft = other.loop_iteration_depth_soft;
|
||||
@@ -404,6 +410,10 @@ void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument)
|
||||
dec.offset = argument;
|
||||
break;
|
||||
|
||||
case DecorationOffsetIdEXT:
|
||||
dec.offset_id = argument;
|
||||
break;
|
||||
|
||||
case DecorationXfbBuffer:
|
||||
dec.xfb_buffer = argument;
|
||||
break;
|
||||
@@ -420,6 +430,10 @@ void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument)
|
||||
dec.array_stride = argument;
|
||||
break;
|
||||
|
||||
case DecorationArrayStrideIdEXT:
|
||||
dec.array_stride_id = argument;
|
||||
break;
|
||||
|
||||
case DecorationMatrixStride:
|
||||
dec.matrix_stride = argument;
|
||||
break;
|
||||
@@ -492,6 +506,10 @@ void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decor
|
||||
dec.offset = argument;
|
||||
break;
|
||||
|
||||
case DecorationOffsetIdEXT:
|
||||
dec.offset_id = argument;
|
||||
break;
|
||||
|
||||
case DecorationXfbBuffer:
|
||||
dec.xfb_buffer = argument;
|
||||
break;
|
||||
@@ -645,6 +663,8 @@ uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const
|
||||
return dec.component;
|
||||
case DecorationOffset:
|
||||
return dec.offset;
|
||||
case DecorationOffsetIdEXT:
|
||||
return dec.offset_id;
|
||||
case DecorationXfbBuffer:
|
||||
return dec.xfb_buffer;
|
||||
case DecorationXfbStride:
|
||||
@@ -661,6 +681,8 @@ uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const
|
||||
return dec.spec_id;
|
||||
case DecorationArrayStride:
|
||||
return dec.array_stride;
|
||||
case DecorationArrayStrideIdEXT:
|
||||
return dec.array_stride_id;
|
||||
case DecorationMatrixStride:
|
||||
return dec.matrix_stride;
|
||||
case DecorationIndex:
|
||||
@@ -720,6 +742,10 @@ void ParsedIR::unset_decoration(ID id, Decoration decoration)
|
||||
dec.offset = 0;
|
||||
break;
|
||||
|
||||
case DecorationOffsetIdEXT:
|
||||
dec.offset_id = 0;
|
||||
break;
|
||||
|
||||
case DecorationXfbBuffer:
|
||||
dec.xfb_buffer = 0;
|
||||
break;
|
||||
@@ -806,6 +832,8 @@ uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration d
|
||||
return dec.binding;
|
||||
case DecorationOffset:
|
||||
return dec.offset;
|
||||
case DecorationOffsetIdEXT:
|
||||
return dec.offset_id;
|
||||
case DecorationXfbBuffer:
|
||||
return dec.xfb_buffer;
|
||||
case DecorationXfbStride:
|
||||
@@ -903,6 +931,10 @@ void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration dec
|
||||
dec.offset = 0;
|
||||
break;
|
||||
|
||||
case DecorationOffsetIdEXT:
|
||||
dec.offset_id = 0;
|
||||
break;
|
||||
|
||||
case DecorationXfbBuffer:
|
||||
dec.xfb_buffer = 0;
|
||||
break;
|
||||
|
||||
@@ -110,6 +110,13 @@ public:
|
||||
std::unordered_map<FunctionID, SPIREntryPoint> entry_points;
|
||||
FunctionID default_entry_point = 0;
|
||||
|
||||
// A "library" module has no OpEntryPoint and instead exports symbols via
|
||||
// OpDecorate ... LinkageAttributes ... Export. These vectors keep track
|
||||
// of all these exports and specifically the function exports.
|
||||
bool is_library_module = false;
|
||||
SmallVector<uint32_t> library_exports;
|
||||
SmallVector<FunctionID> library_exported_functions;
|
||||
|
||||
struct Source
|
||||
{
|
||||
SourceLanguage lang = SourceLanguageUnknown;
|
||||
|
||||
Vendored
+690
-117
File diff suppressed because it is too large
Load Diff
Vendored
+33
-4
@@ -306,6 +306,19 @@ public:
|
||||
// Returns the macro name corresponding to constant id
|
||||
std::string constant_value_macro_name(uint32_t id) const;
|
||||
|
||||
// Rather than using layout(descriptor_heap), emit layout(set, binding).
|
||||
// This intended to be compatible with descriptor buffers, legacy descriptor indexing,
|
||||
// or when the heap descriptors require unusual kinds of mapping in the Vulkan API
|
||||
// which is not expressible by GLSL directly.
|
||||
//
|
||||
// ResourceTypeUnknown can be used as a default catch-all mapping.
|
||||
// dim can be used to disambiguate between texel buffers and images since they are both image types,
|
||||
// but use different descriptor types in the Vulkan API.
|
||||
// No distinction is made between 1D/2D/3D/Cube textures.
|
||||
// The default argument of DimMax maps to both texel buffers and images.
|
||||
// dim is ignored for ResourceTypeUnknown.
|
||||
void remap_descriptor_heap(ResourceType type, uint32_t desc_set, uint32_t binding, Dim dim = DimMax);
|
||||
|
||||
protected:
|
||||
struct ShaderSubgroupSupportHelper
|
||||
{
|
||||
@@ -670,6 +683,7 @@ protected:
|
||||
bool requires_relaxed_precision_analysis = false;
|
||||
bool implicit_c_integer_promotion_rules = false;
|
||||
bool supports_spec_constant_array_size = true;
|
||||
bool requires_phi_undef_zero_init = false;
|
||||
} backend;
|
||||
|
||||
void emit_struct(SPIRType &type);
|
||||
@@ -677,7 +691,9 @@ protected:
|
||||
void emit_extension_workarounds(ExecutionModel model);
|
||||
void emit_subgroup_arithmetic_workaround(const std::string &func, Op op, GroupOperation group_op);
|
||||
void emit_polyfills(uint32_t polyfills, bool relaxed);
|
||||
void emit_buffer_block_native(const SPIRVariable &var);
|
||||
void emit_buffer_block_native(const SPIRVariable *var, const DescriptorHeapMeta *heap_meta = nullptr);
|
||||
std::string to_buffer_pointer_name_prefix(uint32_t ptr_id) const;
|
||||
static std::string heap_meta_to_prefix(const DescriptorHeapMeta &meta);
|
||||
void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration);
|
||||
void emit_buffer_block_legacy(const SPIRVariable &var);
|
||||
void emit_buffer_block_flattened(const SPIRVariable &type);
|
||||
@@ -771,7 +787,7 @@ protected:
|
||||
AccessChainFlags flags, bool &access_chain_is_arrayed, uint32_t index);
|
||||
|
||||
std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags,
|
||||
AccessChainMeta *meta);
|
||||
AccessChainMeta *meta, const SPIRType *untyped_data_type);
|
||||
|
||||
// Only meaningful on backends with physical pointer support ala MSL.
|
||||
// Relevant for PtrAccessChain / BDA.
|
||||
@@ -785,7 +801,8 @@ protected:
|
||||
StorageClass storage, bool &is_packed);
|
||||
|
||||
std::string access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type,
|
||||
AccessChainMeta *meta = nullptr, bool ptr_chain = false);
|
||||
AccessChainMeta *meta = nullptr, bool ptr_chain = false,
|
||||
const SPIRType *untyped_data_type = nullptr);
|
||||
|
||||
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,
|
||||
@@ -1023,6 +1040,7 @@ protected:
|
||||
|
||||
std::string emit_for_loop_initializers(const SPIRBlock &block);
|
||||
void emit_while_loop_initializers(const SPIRBlock &block);
|
||||
std::string undef_loop_variable_initializer_suffix(const SPIRVariable &var);
|
||||
bool for_loop_initializers_are_same_type(const SPIRBlock &block);
|
||||
bool optimize_read_modify_write(const SPIRType &type, const std::string &lhs, const std::string &rhs);
|
||||
void fixup_image_load_store_access();
|
||||
@@ -1055,7 +1073,7 @@ protected:
|
||||
void disallow_forwarding_in_expression_chain(const SPIRExpression &expr);
|
||||
|
||||
bool expression_is_constant_null(uint32_t id) const;
|
||||
bool expression_is_non_value_type_array(uint32_t ptr);
|
||||
bool expression_is_non_value_type_array(uint32_t value_type_id, uint32_t ptr);
|
||||
virtual void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression);
|
||||
|
||||
uint32_t get_integer_width_for_instruction(const Instruction &instr) const;
|
||||
@@ -1089,6 +1107,17 @@ protected:
|
||||
uint32_t get_fp_fast_math_flags_for_op(uint32_t result_type, uint32_t id) const;
|
||||
bool has_legacy_nocontract(uint32_t result_type, uint32_t id) const;
|
||||
|
||||
struct DescriptorHeapMapping
|
||||
{
|
||||
ResourceType type;
|
||||
uint32_t desc_set;
|
||||
uint32_t binding;
|
||||
Dim dim;
|
||||
};
|
||||
SmallVector<DescriptorHeapMapping> descriptor_heap_mappings;
|
||||
bool is_descriptor_non_uniform(uint32_t id) const;
|
||||
std::string to_descriptor_heap_layout(const SPIRType &type, StorageClass storage = StorageClassUniformConstant) const;
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
|
||||
Vendored
+26
-6
@@ -3117,7 +3117,12 @@ uint32_t CompilerHLSL::input_vertices_from_execution_mode(SPIREntryPoint &execut
|
||||
|
||||
void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &return_flags)
|
||||
{
|
||||
if (func.self != ir.default_entry_point)
|
||||
// In library mode default_entry_point points at the first exported
|
||||
// function; treat every export as a normal function rather than as the
|
||||
// shader's entry point.
|
||||
const bool is_entry_point = !ir.is_library_module && func.self == ir.default_entry_point;
|
||||
|
||||
if (!is_entry_point)
|
||||
add_function_overload(func);
|
||||
|
||||
// Avoid shadow declarations.
|
||||
@@ -3138,7 +3143,7 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &ret
|
||||
decl = "void ";
|
||||
}
|
||||
|
||||
if (func.self == ir.default_entry_point)
|
||||
if (is_entry_point)
|
||||
{
|
||||
decl += get_inner_entry_point_name();
|
||||
processing_entry_point = true;
|
||||
@@ -5237,7 +5242,8 @@ string CompilerHLSL::write_access_chain_value(uint32_t value, const SmallVector<
|
||||
{
|
||||
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);
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_LITERAL_MSB_FORCE_ID, &meta,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
if (enclose)
|
||||
@@ -7101,6 +7107,7 @@ string CompilerHLSL::compile()
|
||||
backend.can_return_array = false;
|
||||
backend.nonuniform_qualifier = "NonUniformResourceIndex";
|
||||
backend.support_case_fallthrough = false;
|
||||
backend.requires_phi_undef_zero_init = true;
|
||||
backend.force_merged_mesh_block = get_execution_model() == ExecutionModelMeshEXT;
|
||||
backend.force_gl_in_out_block = backend.force_merged_mesh_block;
|
||||
backend.supports_empty_struct = hlsl_options.shader_model <= 30;
|
||||
@@ -7146,14 +7153,27 @@ string CompilerHLSL::compile()
|
||||
emit_header();
|
||||
emit_resources();
|
||||
|
||||
emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
|
||||
emit_hlsl_entry_point();
|
||||
if (ir.is_library_module)
|
||||
{
|
||||
// Emit each exported function as a normal free function.
|
||||
// emit_function recursively emits callees, so internal helpers
|
||||
// are picked up too.
|
||||
for (auto export_id : ir.library_exported_functions)
|
||||
emit_function(get<SPIRFunction>(export_id), Bitset());
|
||||
}
|
||||
else
|
||||
{
|
||||
emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
|
||||
emit_hlsl_entry_point();
|
||||
}
|
||||
|
||||
pass_count++;
|
||||
} while (is_forcing_recompilation());
|
||||
|
||||
// Entry point in HLSL is always main() for the time being.
|
||||
get_entry_point().name = "main";
|
||||
// Skip the rename for library modules; their exports keep their declared names.
|
||||
if (!ir.is_library_module)
|
||||
get_entry_point().name = "main";
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
Vendored
+95
-76
@@ -1087,6 +1087,18 @@ void CompilerMSL::build_implicit_builtins()
|
||||
dynamic_offsets_buffer_id = var_id;
|
||||
}
|
||||
|
||||
if (active_input_builtins.get(BuiltInDrawIndex))
|
||||
{
|
||||
// This is always emulated.
|
||||
uint32_t var_id = build_constant_uint_array_pointer();
|
||||
set_name(var_id, "spvDrawIndex");
|
||||
// This should never match anything.
|
||||
set_decoration(var_id, DecorationDescriptorSet, ~(6u));
|
||||
set_decoration(var_id, DecorationBinding, msl_options.draw_id_buffer_index);
|
||||
set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, msl_options.draw_id_buffer_index);
|
||||
draw_index_buffer_id = var_id;
|
||||
}
|
||||
|
||||
// If we're returning a struct from a vertex-like entry point, we must return a position attribute.
|
||||
bool need_position = (get_execution_model() == ExecutionModelVertex || is_tese_shader()) &&
|
||||
!capture_output_to_buffer && !get_is_rasterization_disabled() &&
|
||||
@@ -1766,6 +1778,8 @@ string CompilerMSL::compile()
|
||||
add_active_interface_variable(view_mask_buffer_id);
|
||||
if (dynamic_offsets_buffer_id)
|
||||
add_active_interface_variable(dynamic_offsets_buffer_id);
|
||||
if (draw_index_buffer_id)
|
||||
add_active_interface_variable(draw_index_buffer_id);
|
||||
if (builtin_layer_id)
|
||||
add_active_interface_variable(builtin_layer_id);
|
||||
if (builtin_dispatch_base_id && !msl_options.supports_msl_version(1, 2))
|
||||
@@ -8727,7 +8741,7 @@ bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id
|
||||
const uint32_t indices[2] = { i, interface_index };
|
||||
AccessChainMeta meta;
|
||||
expr += access_chain_internal(stage_in_ptr_var_id, indices, 2,
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta);
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta, nullptr);
|
||||
if (i + 1 < num_control_points)
|
||||
expr += ", ";
|
||||
}
|
||||
@@ -8763,7 +8777,8 @@ bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id
|
||||
|
||||
AccessChainMeta meta;
|
||||
expr += access_chain_internal(stage_in_ptr_var_id, indices, 2,
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta);
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT,
|
||||
&meta, nullptr);
|
||||
if (!is_matrix(sub_type) && sub_type.basetype != SPIRType::Struct &&
|
||||
expr_type.vecsize > sub_type.vecsize)
|
||||
expr += vector_swizzle(sub_type.vecsize, 0);
|
||||
@@ -8821,7 +8836,8 @@ bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id
|
||||
AccessChainMeta meta;
|
||||
expr += access_chain_internal(
|
||||
stage_in_ptr_var_id, indices, 2,
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta);
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT,
|
||||
&meta, nullptr);
|
||||
}
|
||||
else
|
||||
expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index);
|
||||
@@ -8845,7 +8861,8 @@ bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id
|
||||
AccessChainMeta meta;
|
||||
expr += access_chain_internal(
|
||||
stage_in_ptr_var_id, indices, 2,
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta);
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT,
|
||||
&meta, nullptr);
|
||||
}
|
||||
else
|
||||
expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index);
|
||||
@@ -8865,7 +8882,7 @@ bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id
|
||||
AccessChainMeta meta;
|
||||
expr += access_chain_internal(stage_in_ptr_var_id, indices, 2,
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT,
|
||||
&meta);
|
||||
&meta, nullptr);
|
||||
}
|
||||
else
|
||||
expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index);
|
||||
@@ -8910,7 +8927,8 @@ bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id
|
||||
|
||||
AccessChainMeta meta;
|
||||
expr += access_chain_internal(stage_in_ptr_var_id, indices, 2,
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta);
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT,
|
||||
&meta, nullptr);
|
||||
if (expr_type.vecsize > result_type.vecsize)
|
||||
expr += vector_swizzle(result_type.vecsize, 0);
|
||||
if (j + 1 < result_type.columns)
|
||||
@@ -8955,7 +8973,8 @@ bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id
|
||||
|
||||
AccessChainMeta meta;
|
||||
expr += access_chain_internal(stage_in_ptr_var_id, indices, 2,
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta);
|
||||
ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT,
|
||||
&meta, nullptr);
|
||||
if (expr_type.vecsize > result_type.vecsize)
|
||||
expr += vector_swizzle(result_type.vecsize, 0);
|
||||
|
||||
@@ -9000,7 +9019,6 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
|
||||
auto *var = maybe_get_backing_variable(ops[2]);
|
||||
bool patch = false;
|
||||
bool flat_data = false;
|
||||
bool ptr_is_chain = false;
|
||||
bool flatten_composites = false;
|
||||
|
||||
bool is_block = false;
|
||||
@@ -9021,12 +9039,6 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
|
||||
// Patch inputs are treated as normal block IO variables, so they don't deal with this path at all.
|
||||
if (patch && (!is_block || is_arrayed || var->storage == StorageClassInput))
|
||||
flat_data = false;
|
||||
|
||||
// We might have a chained access chain, where
|
||||
// we first take the access chain to the control point, and then we chain into a member or something similar.
|
||||
// In this case, we need to skip gl_in/gl_out remapping.
|
||||
// Also, skip ptr chain for patches.
|
||||
ptr_is_chain = var->self != ID(ops[2]);
|
||||
}
|
||||
|
||||
bool builtin_variable = false;
|
||||
@@ -9047,10 +9059,27 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
|
||||
|
||||
if (variable_is_flat)
|
||||
{
|
||||
if (auto *ptr_expr = maybe_get<SPIRExpression>(ops[2]))
|
||||
{
|
||||
// Too many edge cases in incrementally resolving tessellation access chains.
|
||||
// Only reasonable option is to completely rematerialize the chain from the start.
|
||||
SmallVector<uint32_t> rematerialize_ops;
|
||||
rematerialize_ops.push_back(ops[0]);
|
||||
rematerialize_ops.push_back(ops[1]);
|
||||
|
||||
for (auto expr : ptr_expr->implied_read_expressions)
|
||||
rematerialize_ops.push_back(expr);
|
||||
|
||||
for (uint32_t i = 3; i < length; i++)
|
||||
rematerialize_ops.push_back(ops[i]);
|
||||
|
||||
return emit_tessellation_access_chain(rematerialize_ops.data(), uint32_t(rematerialize_ops.size()));
|
||||
}
|
||||
|
||||
// If output is masked, it is emitted as a "normal" variable, just go through normal code paths.
|
||||
// Only check this for the first level of access chain.
|
||||
// Dealing with this for partial access chains should be possible, but awkward.
|
||||
if (var->storage == StorageClassOutput && !ptr_is_chain)
|
||||
if (var->storage == StorageClassOutput)
|
||||
{
|
||||
bool masked = false;
|
||||
if (is_block)
|
||||
@@ -9077,7 +9106,7 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
|
||||
|
||||
indices.reserve(length - 3 + 1);
|
||||
|
||||
uint32_t first_non_array_index = (ptr_is_chain ? 3 : 4) - (patch ? 1 : 0);
|
||||
uint32_t first_non_array_index = 4 - (patch ? 1 : 0);
|
||||
|
||||
VariableID stage_var_id;
|
||||
if (patch)
|
||||
@@ -9085,8 +9114,9 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
|
||||
else
|
||||
stage_var_id = var->storage == StorageClassInput ? stage_in_ptr_var_id : stage_out_ptr_var_id;
|
||||
|
||||
VariableID ptr = ptr_is_chain ? VariableID(ops[2]) : stage_var_id;
|
||||
if (!ptr_is_chain && !patch)
|
||||
VariableID ptr = stage_var_id;
|
||||
|
||||
if (!patch)
|
||||
{
|
||||
// Index into gl_in/gl_out with first array index.
|
||||
indices.push_back(ops[first_non_array_index - 1]);
|
||||
@@ -9097,17 +9127,7 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
|
||||
uint32_t const_mbr_id = next_id++;
|
||||
uint32_t index = get_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex);
|
||||
|
||||
// If we have a pointer chain expression, and we are no longer pointing to a composite
|
||||
// object, we are in the clear. There is no longer a need to flatten anything.
|
||||
bool further_access_chain_is_trivial = false;
|
||||
if (ptr_is_chain && flatten_composites)
|
||||
{
|
||||
auto &ptr_type = expression_type(ptr);
|
||||
if (!is_array(ptr_type) && !is_matrix(ptr_type) && ptr_type.basetype != SPIRType::Struct)
|
||||
further_access_chain_is_trivial = true;
|
||||
}
|
||||
|
||||
if (!further_access_chain_is_trivial && (flatten_composites || is_block))
|
||||
if (flatten_composites || is_block)
|
||||
{
|
||||
uint32_t i = first_non_array_index;
|
||||
auto *type = &get_variable_element_type(*var);
|
||||
@@ -9203,42 +9223,8 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
|
||||
|
||||
// We use the pointer to the base of the input/output array here,
|
||||
// so this is always a pointer chain.
|
||||
string e;
|
||||
|
||||
if (!ptr_is_chain)
|
||||
{
|
||||
// This is the start of an access chain, use ptr_chain to index into control point array.
|
||||
e = access_chain(ptr, indices.data(), uint32_t(indices.size()), result_ptr_type, &meta, !patch);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're accessing a struct, we need to use member indices which are based on the IO block,
|
||||
// not actual struct type, so we have to use a split access chain here where
|
||||
// first path resolves the control point index, i.e. gl_in[index], and second half deals with
|
||||
// looking up flattened member name.
|
||||
|
||||
// However, it is possible that we partially accessed a struct,
|
||||
// by taking pointer to member inside the control-point array.
|
||||
// For this case, we fall back to a natural access chain since we have already dealt with remapping struct members.
|
||||
// One way to check this here is if we have 2 implied read expressions.
|
||||
// First one is the gl_in/gl_out struct itself, then an index into that array.
|
||||
// If we have traversed further, we use a normal access chain formulation.
|
||||
auto *ptr_expr = maybe_get<SPIRExpression>(ptr);
|
||||
bool split_access_chain_formulation = flatten_composites && ptr_expr &&
|
||||
ptr_expr->implied_read_expressions.size() == 2 &&
|
||||
!further_access_chain_is_trivial;
|
||||
|
||||
if (split_access_chain_formulation)
|
||||
{
|
||||
e = join(to_expression(ptr),
|
||||
access_chain_internal(stage_var_id, indices.data(), uint32_t(indices.size()),
|
||||
ACCESS_CHAIN_CHAIN_ONLY_BIT, &meta));
|
||||
}
|
||||
else
|
||||
{
|
||||
e = access_chain_internal(ptr, indices.data(), uint32_t(indices.size()), 0, &meta);
|
||||
}
|
||||
}
|
||||
// This is the start of an access chain, use ptr_chain to index into control point array.
|
||||
auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), result_ptr_type, &meta, !patch);
|
||||
|
||||
// Get the actual type of the object that was accessed. If it's a vector type and we changed it,
|
||||
// then we'll need to add a swizzle.
|
||||
@@ -9498,10 +9484,31 @@ void CompilerMSL::emit_instruction(const Instruction &instruction)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sample mask input for Metal is not an array
|
||||
if (BuiltIn(get_decoration(ptr, DecorationBuiltIn)) == BuiltInSampleMask)
|
||||
set_decoration(id, DecorationBuiltIn, BuiltInSampleMask);
|
||||
CompilerGLSL::emit_instruction(instruction);
|
||||
auto is_sample_mask = BuiltIn(get_decoration(ptr, DecorationBuiltIn)) == BuiltInSampleMask;
|
||||
auto ptr_storage = get_expression_effective_storage_class(ptr);
|
||||
auto *ptr_var = maybe_get_backing_variable(ptr);
|
||||
|
||||
// More edge cases ... Normally composite outputs are lowered at the end,
|
||||
// but that's not the case for clip-cull arrays.
|
||||
if (ptr_var && ptr_storage == StorageClassOutput && is_builtin_variable(*ptr_var) &&
|
||||
!is_sample_mask && is_array(get<SPIRType>(ops[0])))
|
||||
{
|
||||
emit_uninitialized_temporary_expression(ops[0], id);
|
||||
auto &type = get<SPIRType>(ops[0]);
|
||||
if (type.array.size() != 1)
|
||||
SPIRV_CROSS_THROW("Cannot load array of clip-cull distances from array of array.");
|
||||
if (!type.array_size_literal.front())
|
||||
SPIRV_CROSS_THROW("Cannot load array of clip-cull distances from spec constant array size.");
|
||||
for (uint32_t i = 0; i < type.array[0]; i++)
|
||||
statement(to_expression(id), "[", i, "] = ", to_expression(ptr), "[", i, "];");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sample mask input for Metal is not an array
|
||||
if (is_sample_mask)
|
||||
set_decoration(id, DecorationBuiltIn, BuiltInSampleMask);
|
||||
CompilerGLSL::emit_instruction(instruction);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -11174,6 +11181,12 @@ bool CompilerMSL::emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rh
|
||||
else if (rhs_var && rhs_storage != StorageClassGeneric && type_is_explicit_layout(get<SPIRType>(rhs_var->basetype)))
|
||||
rhs_is_array_template = false;
|
||||
|
||||
// Special consideration for clip/culldistance. Normally composites are lowered, but clip/cull is special for reasons ...
|
||||
if (lhs_var && lhs_storage == StorageClassOutput && is_builtin_variable(*lhs_var))
|
||||
lhs_is_array_template = false;
|
||||
if (rhs_var && rhs_storage == StorageClassOutput && is_builtin_variable(*rhs_var))
|
||||
rhs_is_array_template = false;
|
||||
|
||||
// If threadgroup storage qualifiers are *not* used:
|
||||
// Avoid spvCopy* wrapper functions; Otherwise, spvUnsafeArray<> template cannot be used with that storage qualifier.
|
||||
if (lhs_is_array_template && rhs_is_array_template && !using_builtin_array())
|
||||
@@ -13821,9 +13834,6 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
|
||||
return "";
|
||||
return string(" [[") + builtin_qualifier(builtin) + "]]";
|
||||
|
||||
case BuiltInDrawIndex:
|
||||
SPIRV_CROSS_THROW("DrawIndex is not supported in MSL.");
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@@ -14672,6 +14682,9 @@ bool CompilerMSL::is_direct_input_builtin(BuiltIn bi_type)
|
||||
/* fallthrough */
|
||||
case BuiltInSubgroupLocalInvocationId:
|
||||
return !msl_options.emulate_subgroups;
|
||||
case BuiltInDrawIndex:
|
||||
// Emulated
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@@ -15970,6 +15983,12 @@ void CompilerMSL::fix_up_shader_inputs_outputs()
|
||||
to_expression(builtin_dispatch_base_id), ".y;");
|
||||
});
|
||||
break;
|
||||
case BuiltInDrawIndex:
|
||||
entry_func.fixup_hooks_in.push_back([=]() {
|
||||
statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = *",
|
||||
to_expression(draw_index_buffer_id), ";");
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -18120,8 +18139,9 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
|
||||
{
|
||||
SPIRV_CROSS_THROW("BaseInstance requires Metal 1.1 and Mac or Apple A9+ hardware.");
|
||||
}
|
||||
|
||||
case BuiltInDrawIndex:
|
||||
SPIRV_CROSS_THROW("DrawIndex is not supported in MSL.");
|
||||
return "gl_DrawID";
|
||||
|
||||
// When used in the entry function, output builtins are qualified with output struct name.
|
||||
// Test storage class as NOT Input, as output builtins might be part of generic type.
|
||||
@@ -18232,8 +18252,6 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin)
|
||||
return "instance_id";
|
||||
case BuiltInBaseInstance:
|
||||
return "base_instance";
|
||||
case BuiltInDrawIndex:
|
||||
SPIRV_CROSS_THROW("DrawIndex is not supported in MSL.");
|
||||
|
||||
// Vertex function out
|
||||
case BuiltInClipDistance:
|
||||
@@ -18457,7 +18475,7 @@ string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id)
|
||||
case BuiltInBaseInstance:
|
||||
return "uint";
|
||||
case BuiltInDrawIndex:
|
||||
SPIRV_CROSS_THROW("DrawIndex is not supported in MSL.");
|
||||
return "uint";
|
||||
|
||||
// Vertex function out
|
||||
case BuiltInClipDistance:
|
||||
@@ -19671,6 +19689,7 @@ void CompilerMSL::cast_from_variable_load(uint32_t source_id, std::string &expr,
|
||||
case BuiltInSubgroupSize:
|
||||
case BuiltInSubgroupLocalInvocationId:
|
||||
case BuiltInViewIndex:
|
||||
case BuiltInDrawIndex:
|
||||
case BuiltInVertexIndex:
|
||||
case BuiltInInstanceIndex:
|
||||
case BuiltInBaseInstance:
|
||||
|
||||
Vendored
+2
@@ -317,6 +317,7 @@ public:
|
||||
uint32_t shader_input_buffer_index = 22;
|
||||
uint32_t shader_index_buffer_index = 21;
|
||||
uint32_t shader_patch_input_buffer_index = 20;
|
||||
uint32_t draw_id_buffer_index = 19;
|
||||
uint32_t shader_input_wg_index = 0;
|
||||
uint32_t device_index = 0;
|
||||
uint32_t enable_frag_output_mask = 0xffffffff;
|
||||
@@ -1180,6 +1181,7 @@ protected:
|
||||
uint32_t swizzle_buffer_id = 0;
|
||||
uint32_t buffer_size_buffer_id = 0;
|
||||
uint32_t view_mask_buffer_id = 0;
|
||||
uint32_t draw_index_buffer_id = 0;
|
||||
uint32_t dynamic_offsets_buffer_id = 0;
|
||||
uint32_t uint_type_id = 0;
|
||||
uint32_t shared_uint_type_id = 0;
|
||||
|
||||
+86
-3
@@ -151,8 +151,29 @@ void Parser::parse()
|
||||
SPIRV_CROSS_THROW("Function was not terminated.");
|
||||
if (current_block)
|
||||
SPIRV_CROSS_THROW("Block was not terminated.");
|
||||
|
||||
// Now that all definitions are bound to a kind, we can filter the library
|
||||
// exports and populate the exported functions.
|
||||
for (uint32_t id : ir.library_exports)
|
||||
{
|
||||
if (ir.ids[id].get_type() == TypeFunction)
|
||||
ir.library_exported_functions.push_back(id);
|
||||
}
|
||||
|
||||
if (ir.default_entry_point == 0)
|
||||
SPIRV_CROSS_THROW("There is no entry point in the SPIR-V module.");
|
||||
{
|
||||
if (ir.library_exported_functions.empty())
|
||||
SPIRV_CROSS_THROW("There is no entry point in the SPIR-V module.");
|
||||
|
||||
// No OpEntryPoint, but the module exports functions. Treat as a library
|
||||
// module: designate the first exported function as the default entry
|
||||
// point so analyses keyed on default_entry_point can run.
|
||||
ir.is_library_module = true;
|
||||
ir.default_entry_point = ir.library_exported_functions.front();
|
||||
auto &name = ir.get_name(ir.default_entry_point);
|
||||
ir.entry_points.insert(std::make_pair(ir.default_entry_point,
|
||||
SPIREntryPoint(ir.default_entry_point, ExecutionModelGLCompute, name)));
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t *Parser::stream(const Instruction &instr) const
|
||||
@@ -604,6 +625,19 @@ void Parser::parse(const Instruction &instruction)
|
||||
else
|
||||
ir.set_decoration(id, decoration);
|
||||
|
||||
// Track exported functions so we can compile library modules that have no OpEntryPoint.
|
||||
// LinkageAttributes layout: literal-string (variable words) followed by LinkageType.
|
||||
if (decoration == DecorationLinkageAttributes && length >= 4 &&
|
||||
static_cast<LinkageType>(ops[length - 1]) == LinkageTypeExport)
|
||||
{
|
||||
ir.library_exports.push_back(id);
|
||||
|
||||
// If OpName was stripped (e.g. by spirv-opt --strip-debug), fall back
|
||||
// to the linkage name so the emitted function keeps its export name.
|
||||
if (ir.get_name(id).empty())
|
||||
ir.set_name(id, extract_string(ir.spirv, instruction.offset + 2));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -627,6 +661,12 @@ void Parser::parse(const Instruction &instruction)
|
||||
break;
|
||||
}
|
||||
|
||||
// MemberDecorateIdEXT only applies to OffsetIdEXT when descriptors are packed in structs.
|
||||
// This is currently unsupported and will fail in compilation.
|
||||
// Pass it through in case someone just needs reflection.
|
||||
case OpMemberDecorateIdEXT:
|
||||
break;
|
||||
|
||||
case OpMemberDecorateStringGOOGLE:
|
||||
{
|
||||
uint32_t id = ops[0];
|
||||
@@ -859,6 +899,7 @@ void Parser::parse(const Instruction &instruction)
|
||||
break;
|
||||
}
|
||||
|
||||
case OpTypeUntypedPointerKHR:
|
||||
case OpTypePointer:
|
||||
{
|
||||
uint32_t id = ops[0];
|
||||
@@ -866,7 +907,7 @@ void Parser::parse(const Instruction &instruction)
|
||||
// Very rarely, we might receive a FunctionPrototype here.
|
||||
// We won't be able to compile it, but we shouldn't crash when parsing.
|
||||
// We should be able to reflect.
|
||||
auto *base = maybe_get<SPIRType>(ops[2]);
|
||||
auto *base = op == OpTypePointer ? maybe_get<SPIRType>(ops[2]) : nullptr;
|
||||
auto &ptrbase = set<SPIRType>(id, op);
|
||||
|
||||
if (base)
|
||||
@@ -885,7 +926,10 @@ void Parser::parse(const Instruction &instruction)
|
||||
if (base && base->forward_pointer)
|
||||
forward_pointer_fixups.push_back({ id, ops[2] });
|
||||
|
||||
ptrbase.parent_type = ops[2];
|
||||
if (op == OpTypePointer)
|
||||
ptrbase.parent_type = ops[2];
|
||||
else
|
||||
ptrbase.basetype = SPIRType::Void;
|
||||
|
||||
// Do NOT set ptrbase.self!
|
||||
break;
|
||||
@@ -1005,6 +1049,27 @@ void Parser::parse(const Instruction &instruction)
|
||||
break;
|
||||
}
|
||||
|
||||
case OpUntypedVariableKHR:
|
||||
{
|
||||
uint32_t type = ops[0];
|
||||
uint32_t id = ops[1];
|
||||
auto storage = static_cast<StorageClass>(ops[2]);
|
||||
uint32_t data_type = length >= 4 ? ops[3] : 0;
|
||||
uint32_t initializer = length >= 5 ? ops[4] : 0;
|
||||
|
||||
if (storage == StorageClassFunction)
|
||||
{
|
||||
if (!current_function)
|
||||
SPIRV_CROSS_THROW("No function currently in scope");
|
||||
current_function->add_local_variable(id);
|
||||
}
|
||||
|
||||
auto &v = set<SPIRVariable>(id, type, storage, initializer);
|
||||
v.untyped = true;
|
||||
v.untyped_alloca_type = data_type;
|
||||
break;
|
||||
}
|
||||
|
||||
// OpPhi
|
||||
// OpPhi is a fairly magical opcode.
|
||||
// It selects temporary variables based on which parent block we *came from*.
|
||||
@@ -1135,6 +1200,24 @@ void Parser::parse(const Instruction &instruction)
|
||||
break;
|
||||
}
|
||||
|
||||
case OpConstantSizeOfEXT:
|
||||
{
|
||||
uint32_t id = ops[1];
|
||||
uint32_t type = ops[0];
|
||||
auto &c = set<SPIRConstant>(id, type);
|
||||
c.size_of_type = ops[2];
|
||||
break;
|
||||
}
|
||||
|
||||
case OpTypeBufferEXT:
|
||||
{
|
||||
uint32_t type = ops[0];
|
||||
auto &t = set<SPIRType>(type, OpTypeBufferEXT);
|
||||
t.basetype = SPIRType::DescriptorHeapBuffer;
|
||||
t.ext.descriptor_heap_buffer.storage = static_cast<StorageClass>(ops[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
// Functions
|
||||
case OpFunction:
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user