hlsl: Support custom root constant layout

This commit is contained in:
msiglreith 2017-11-27 16:00:56 +01:00
parent 4b58f65af7
commit d096f5cafe
6 changed files with 141 additions and 49 deletions

View File

@ -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,6 +1091,9 @@ bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackin
else
pad_alignment = 1;
// Only care about packing if we are in the given range
if (offset >= start_offset)
{
// 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))
@ -1096,8 +1104,8 @@ bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackin
}
// 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))
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.
@ -1106,6 +1114,7 @@ bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackin
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<SPIRType>(member_type_id);

View File

@ -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<uint32_t>::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);

View File

@ -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<SPIRVariable>(num_workgroups_builtin);
auto &type = get<SPIRType>(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<SPIRType>(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.");
@ -1556,9 +1560,60 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
}
void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var)
{
if (root_constants_layout.empty())
{
emit_buffer_block(var);
}
else
{
for (const auto &layout : root_constants_layout)
{
auto &type = get<SPIRType>(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<SPIRType>(var.basetype);
const char *space = nullptr;
const auto &type = get<SPIRType>(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)

View File

@ -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<RootConstants> 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<RootConstants> root_constants_layout;
};
}

View File

@ -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<SPIRType>(member_type_id);

View File

@ -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;