From d096f5cafece717967bff576dcf8760fd9a31ebc Mon Sep 17 00:00:00 2001 From: msiglreith Date: Mon, 27 Nov 2017 16:00:56 +0100 Subject: [PATCH] hlsl: Support custom root constant layout --- spirv_glsl.cpp | 47 +++++++++++++--------- spirv_glsl.hpp | 5 ++- spirv_hlsl.cpp | 104 +++++++++++++++++++++++++++++++++++++------------ spirv_hlsl.hpp | 30 +++++++++++++- spirv_msl.cpp | 2 +- spirv_msl.hpp | 2 +- 6 files changed, 141 insertions(+), 49 deletions(-) diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 00f5b2ef..430f829b 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -1040,7 +1040,8 @@ uint32_t CompilerGLSL::type_to_packed_size(const SPIRType &type, uint64_t flags, return size; } -bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing) +bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, + uint32_t start_offset, uint32_t end_offset) { // This is very tricky and error prone, but try to be exhaustive and correct here. // SPIR-V doesn't directly say if we're using std430 or std140. @@ -1079,6 +1080,10 @@ bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackin uint32_t alignment = max(packed_alignment, pad_alignment); offset = (offset + alignment - 1) & ~(alignment - 1); + // Field is not in the specified range anymore and we can ignore any further fields. + if (offset >= end_offset) + break; + // The next member following a struct member is aligned to the base alignment of the struct that came before. // GL 4.5 spec, 7.6.2.2. if (memb_type.basetype == SPIRType::Struct) @@ -1086,27 +1091,31 @@ bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackin else pad_alignment = 1; - // We only care about offsets in std140, std430, etc ... - // For EnhancedLayout variants, we have the flexibility to choose our own offsets. - if (!packing_has_flexible_offset(packing)) + // Only care about packing if we are in the given range + if (offset >= start_offset) { - uint32_t actual_offset = type_struct_member_offset(type, i); - if (actual_offset != offset) // This cannot be the packing we're looking for. + // We only care about offsets in std140, std430, etc ... + // For EnhancedLayout variants, we have the flexibility to choose our own offsets. + if (!packing_has_flexible_offset(packing)) + { + uint32_t actual_offset = type_struct_member_offset(type, i); + if (actual_offset != offset) // This cannot be the packing we're looking for. + return false; + } + + // Verify array stride rules. + if (!memb_type.array.empty() && type_to_packed_array_stride(memb_type, member_flags, packing) != + type_struct_member_array_stride(type, i)) + return false; + + // Verify that sub-structs also follow packing rules. + // We cannot use enhanced layouts on substructs, so they better be up to spec. + auto substruct_packing = packing_to_substruct_packing(packing); + + if (!memb_type.member_types.empty() && !buffer_is_packing_standard(memb_type, substruct_packing)) return false; } - // Verify array stride rules. - if (!memb_type.array.empty() && - type_to_packed_array_stride(memb_type, member_flags, packing) != type_struct_member_array_stride(type, i)) - return false; - - // Verify that sub-structs also follow packing rules. - // We cannot use enhanced layouts on substructs, so they better be up to spec. - auto substruct_packing = packing_to_substruct_packing(packing); - - if (!memb_type.member_types.empty() && !buffer_is_packing_standard(memb_type, substruct_packing)) - return false; - // Bump size. offset += packed_size; } @@ -7066,7 +7075,7 @@ string CompilerGLSL::variable_decl(const SPIRType &type, const string &name, uin // Emit a structure member. Subclasses may override to modify output, // or to dynamically add a padding member if needed. void CompilerGLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, - const string &qualifier) + const string &qualifier, uint32_t) { auto &membertype = get(member_type_id); diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index 0cae70c0..eec33c9c 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -202,7 +202,7 @@ protected: virtual std::string type_to_glsl(const SPIRType &type, uint32_t id = 0); virtual std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage); virtual void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, - const std::string &qualifier = ""); + const std::string &qualifier = "", uint32_t base_offset = 0); virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0); virtual std::string constant_expression(const SPIRConstant &c); std::string constant_op_expression(const SPIRConstantOp &cop); @@ -424,7 +424,8 @@ protected: std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id); virtual bool skip_argument(uint32_t id) const; - bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing); + bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, uint32_t start_offset = 0, + uint32_t end_offset = std::numeric_limits::max()); uint32_t type_to_packed_base_size(const SPIRType &type, BufferPackingStandard packing); uint32_t type_to_packed_alignment(const SPIRType &type, uint64_t flags, BufferPackingStandard packing); uint32_t type_to_packed_array_stride(const SPIRType &type, uint64_t flags, BufferPackingStandard packing); diff --git a/spirv_hlsl.cpp b/spirv_hlsl.cpp index 794d7b74..c5cda519 100644 --- a/spirv_hlsl.cpp +++ b/spirv_hlsl.cpp @@ -779,7 +779,8 @@ std::string CompilerHLSL::builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClas case BuiltInNumWorkgroups: { if (!num_workgroups_builtin) - SPIRV_CROSS_THROW("NumWorkgroups builtin is used, but remap_num_workgroups_builtin() was not called. Cannot emit code for this builtin."); + SPIRV_CROSS_THROW("NumWorkgroups builtin is used, but remap_num_workgroups_builtin() was not called. " + "Cannot emit code for this builtin."); auto &var = get(num_workgroups_builtin); auto &type = get(var.basetype); @@ -1459,7 +1460,7 @@ string CompilerHLSL::layout_for_member(const SPIRType &type, uint32_t index) } void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, - const string &qualifier) + const string &qualifier, uint32_t base_offset) { auto &membertype = get(member_type_id); @@ -1475,9 +1476,12 @@ void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type qualifiers = to_interpolation_qualifiers(memberflags); string packing_offset; - if (has_decoration(type.self, DecorationCPacked) && has_member_decoration(type.self, index, DecorationOffset)) + bool is_push_constant = type.storage == StorageClassPushConstant; + + if ((has_decoration(type.self, DecorationCPacked) || is_push_constant) && + has_member_decoration(type.self, index, DecorationOffset)) { - uint32_t offset = memb[index].offset; + uint32_t offset = memb[index].offset - base_offset; if (offset & 3) SPIRV_CROSS_THROW("Cannot pack on tighter bounds than 4 bytes in HLSL."); @@ -1557,7 +1561,58 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var) void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var) { - emit_buffer_block(var); + if (root_constants_layout.empty()) + { + emit_buffer_block(var); + } + else + { + for (const auto &layout : root_constants_layout) + { + auto &type = get(var.basetype); + + if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset, layout.start, layout.end)) + set_decoration(type.self, DecorationCPacked); + else + SPIRV_CROSS_THROW( + "root constant cbuffer cannot be expressed with either HLSL packing layout or packoffset."); + + flattened_structs.insert(var.self); + type.member_name_cache.clear(); + add_resource_name(var.self); + auto &memb = meta[type.self].members; + + statement("cbuffer SPIRV_CROSS_RootConstant_", to_name(var.self), + to_resource_register('b', layout.binding, layout.space)); + begin_scope(); + + // Index of the next field in the generated root constant constant buffer + auto constant_index = 0u; + + // Iterate over all member of the push constant and check which of the fields + // fit into the given root constant layout. + for (auto i = 0u; i < memb.size(); i++) + { + const auto offset = memb[i].offset; + if (layout.start <= offset && offset < layout.end) + { + const auto &member = type.member_types[i]; + + add_member_name(type, constant_index); + auto backup_name = get_member_name(type.self, i); + auto member_name = to_member_name(type, i); + set_member_name(type.self, constant_index, + sanitize_underscores(join(to_name(type.self), "_", member_name))); + emit_struct_member(type, member, i, "", layout.start); + set_member_name(type.self, constant_index, backup_name); + + constant_index++; + } + } + + end_scope_decl(); + } + } } string CompilerHLSL::to_sampler_expression(uint32_t id) @@ -2316,24 +2371,24 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var) if (!has_decoration(var.self, DecorationBinding)) return ""; - auto &type = get(var.basetype); - const char *space = nullptr; + const auto &type = get(var.basetype); + char space = '\0'; switch (type.basetype) { case SPIRType::SampledImage: - space = "t"; // SRV + space = 't'; // SRV break; case SPIRType::Image: if (type.image.sampled == 2) - space = "u"; // UAV + space = 'u'; // UAV else - space = "t"; // SRV + space = 't'; // SRV break; case SPIRType::Sampler: - space = "s"; + space = 's'; break; case SPIRType::Struct: @@ -2345,15 +2400,15 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var) { uint64_t flags = get_buffer_block_flags(var); bool is_readonly = (flags & (1ull << DecorationNonWritable)) != 0; - space = is_readonly ? "t" : "u"; // UAV + space = is_readonly ? 't' : 'u'; // UAV } else if (has_decoration(type.self, DecorationBlock)) - space = "b"; // Constant buffers + space = 'b'; // Constant buffers } else if (storage == StorageClassPushConstant) - space = "b"; // Constant buffers + space = 'b'; // Constant buffers else if (storage == StorageClassStorageBuffer) - space = "u"; // UAV + space = 'u'; // UAV break; } @@ -2364,12 +2419,8 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var) if (!space) return ""; - // shader model 5.1 supports space - if (options.shader_model >= 51) - return join(" : register(", space, get_decoration(var.self, DecorationBinding), ", space", - get_decoration(var.self, DecorationDescriptorSet), ")"); - else - return join(" : register(", space, get_decoration(var.self, DecorationBinding), ")"); + return to_resource_register(space, get_decoration(var.self, DecorationBinding), + get_decoration(var.self, DecorationDescriptorSet)); } string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var) @@ -2378,11 +2429,16 @@ string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var) if (!has_decoration(var.self, DecorationBinding)) return ""; + return to_resource_register('s', get_decoration(var.self, DecorationBinding), + get_decoration(var.self, DecorationDescriptorSet)); +} + +string CompilerHLSL::to_resource_register(char space, uint32_t binding, uint32_t space_set) +{ if (options.shader_model >= 51) - return join(" : register(s", get_decoration(var.self, DecorationBinding), ", space", - get_decoration(var.self, DecorationDescriptorSet), ")"); + return join(" : register(", space, binding, ", space", space_set, ")"); else - return join(" : register(s", get_decoration(var.self, DecorationBinding), ")"); + return join(" : register(", space, binding, ")"); } void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var) diff --git a/spirv_hlsl.hpp b/spirv_hlsl.hpp index f759ab91..9efd403e 100644 --- a/spirv_hlsl.hpp +++ b/spirv_hlsl.hpp @@ -29,6 +29,18 @@ struct HLSLVertexAttributeRemap uint32_t location; std::string semantic; }; +// Specifying a root constant (d3d12) or push constant range (vulkan). +// +// `start` and `end` denotes the range of the root constant in bytes. +// Both values need to be multiple of 4. +struct RootConstants +{ + uint32_t start; + uint32_t end; + + uint32_t binding; + uint32_t space; +}; class CompilerHLSL : public CompilerGLSL { @@ -61,6 +73,15 @@ public: options = opts; } + // Optionally specify a custom root constant layout. + // + // Push constants ranges will be split up according to the + // layout specified. + void set_root_constant_layouts(std::vector layout) + { + root_constants_layout = std::move(layout); + } + // Compiles and remaps vertex attributes at specific locations to a fixed semantic. // The default is TEXCOORD# where # denotes location. // Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row. @@ -113,6 +134,7 @@ 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(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); @@ -121,8 +143,8 @@ private: void emit_store(const Instruction &instruction); void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op); - void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, - const std::string &qualifier) override; + void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier, + uint32_t base_offset = 0) override; const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override; @@ -173,6 +195,10 @@ private: std::string to_semantic(uint32_t vertex_location); uint32_t num_workgroups_builtin = 0; + + // Custom root constant layout, which should be emitted + // when translating push constant ranges. + std::vector root_constants_layout; }; } diff --git a/spirv_msl.cpp b/spirv_msl.cpp index a85b38c2..54965dbd 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -2394,7 +2394,7 @@ void CompilerMSL::emit_fixup() // Emit a structure member, padding and packing to maintain the correct memeber alignments. void CompilerMSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, - const string &qualifier) + const string &qualifier, uint32_t) { auto &membertype = get(member_type_id); diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 6f66f312..8ca4e67c 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -187,7 +187,7 @@ protected: void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; void emit_fixup() override; void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, - const std::string &qualifier = "") override; + const std::string &qualifier = "", uint32_t base_offset = 0) override; std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; std::string image_type_glsl(const SPIRType &type, uint32_t id = 0) override; std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;