Skip to content

Commit

Permalink
HLSL: Add support to preserve (RW)StructuredBuffer resources.
Browse files Browse the repository at this point in the history
  • Loading branch information
LukasBanana authored and Laura Hermanns committed May 12, 2023
1 parent 4faeb81 commit bcb6243
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 24 deletions.
1 change: 1 addition & 0 deletions spirv_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1663,6 +1663,7 @@ struct Meta
std::string alias;
std::string qualified_alias;
std::string hlsl_semantic;
std::string user_type;
Bitset decoration_flags;
spv::BuiltIn builtin_type = spv::BuiltInMax;
uint32_t location = 0;
Expand Down
7 changes: 7 additions & 0 deletions spirv_cross_parsed_ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,10 @@ void ParsedIR::set_decoration_string(ID id, Decoration decoration, const string
dec.hlsl_semantic = argument;
break;

case DecorationUserTypeGOOGLE:
dec.user_type = argument;
break;

default:
break;
}
Expand Down Expand Up @@ -659,6 +663,9 @@ const string &ParsedIR::get_decoration_string(ID id, Decoration decoration) cons
case DecorationHlslSemanticGOOGLE:
return dec.hlsl_semantic;

case DecorationUserTypeGOOGLE:
return dec.user_type;

default:
return empty_string;
}
Expand Down
56 changes: 36 additions & 20 deletions spirv_glsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9606,6 +9606,9 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
bool pending_array_enclose = false;
bool dimension_flatten = false;

// If we are translating access to a structured buffer, the first subscript '._m0' must be hidden
bool hide_first_subscript = count > 1 && is_user_type_structured(base);

const auto append_index = [&](uint32_t index, bool is_literal, bool is_ptr_chain = false) {
AccessChainFlags mod_flags = flags;
if (!is_literal)
Expand Down Expand Up @@ -9792,32 +9795,40 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
if (index >= type->member_types.size())
SPIRV_CROSS_THROW("Member index is out of bounds!");

BuiltIn builtin = BuiltInMax;
if (is_member_builtin(*type, index, &builtin) && access_chain_needs_stage_io_builtin_translation(base))
if (hide_first_subscript)
{
if (access_chain_is_arrayed)
{
expr += ".";
expr += builtin_to_glsl(builtin, type->storage);
}
else
expr = builtin_to_glsl(builtin, type->storage);
// First "._m0" subscript has been hidden, subsequent fields must be emitted even for structured buffers
hide_first_subscript = false;
}
else
{
// If the member has a qualified name, use it as the entire chain
string qual_mbr_name = get_member_qualified_name(type_id, index);
if (!qual_mbr_name.empty())
expr = qual_mbr_name;
else if (flatten_member_reference)
expr += join("_", to_member_name(*type, index));
BuiltIn builtin = BuiltInMax;
if (is_member_builtin(*type, index, &builtin) && access_chain_needs_stage_io_builtin_translation(base))
{
if (access_chain_is_arrayed)
{
expr += ".";
expr += builtin_to_glsl(builtin, type->storage);
}
else
expr = builtin_to_glsl(builtin, type->storage);
}
else
{
// Any pointer de-refences for values are handled in the first access chain.
// For pointer chains, the pointer-ness is resolved through an array access.
// The only time this is not true is when accessing array of SSBO/UBO.
// This case is explicitly handled.
expr += to_member_reference(base, *type, index, ptr_chain || i != 0);
// If the member has a qualified name, use it as the entire chain
string qual_mbr_name = get_member_qualified_name(type_id, index);
if (!qual_mbr_name.empty())
expr = qual_mbr_name;
else if (flatten_member_reference)
expr += join("_", to_member_name(*type, index));
else
{
// Any pointer de-refences for values are handled in the first access chain.
// For pointer chains, the pointer-ness is resolved through an array access.
// The only time this is not true is when accessing array of SSBO/UBO.
// This case is explicitly handled.
expr += to_member_reference(base, *type, index, ptr_chain || i != 0);
}
}
}

Expand Down Expand Up @@ -15335,6 +15346,11 @@ bool CompilerGLSL::builtin_translates_to_nonarray(spv::BuiltIn /*builtin*/) cons
return false; // GLSL itself does not need to translate array builtin types to non-array builtin types
}

bool CompilerGLSL::is_user_type_structured(uint32_t /*id*/) const
{
return false; // GLSL itself does not have structured user type, but HLSL does with StructuredBuffer and RWStructuredBuffer resources.
}

bool CompilerGLSL::check_atomic_image(uint32_t id)
{
auto &type = expression_type(id);
Expand Down
2 changes: 2 additions & 0 deletions spirv_glsl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ class CompilerGLSL : public Compiler

virtual bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const;

virtual bool is_user_type_structured(uint32_t id) 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);

Expand Down
47 changes: 43 additions & 4 deletions spirv_hlsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2603,11 +2603,30 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self);
bool is_coherent = flags.get(DecorationCoherent) && !is_readonly;
bool is_interlocked = interlocked_resources.count(var.self) > 0;
const char *type_name = "ByteAddressBuffer ";
if (!is_readonly)
type_name = is_interlocked ? "RasterizerOrderedByteAddressBuffer " : "RWByteAddressBuffer ";

auto to_structuredbuffer_subtype_name = [this](const SPIRType &parent_type) -> std::string
{
if (parent_type.basetype == SPIRType::Struct && parent_type.member_types.size() == 1)
{
// Use type of first struct member as a StructuredBuffer will have only one '._m0' field in SPIR-V
const auto &member0_type = this->get<SPIRType>(parent_type.member_types.front());
return this->type_to_glsl(member0_type);
}
else
{
// Otherwise, this StructuredBuffer only has a basic subtype, e.g. StructuredBuffer<int>
return this->type_to_glsl(parent_type);
}
};

std::string type_name;
if (is_user_type_structured(var.self))
type_name = join(is_readonly ? "" : is_interlocked ? "RasterizerOrdered" : "RW", "StructuredBuffer<", to_structuredbuffer_subtype_name(type), ">");
else
type_name = is_readonly ? "ByteAddressBuffer" : is_interlocked ? "RasterizerOrderedByteAddressBuffer" : "RWByteAddressBuffer";

add_resource_name(var.self);
statement(is_coherent ? "globallycoherent " : "", type_name, to_name(var.self), type_to_array_glsl(type),
statement(is_coherent ? "globallycoherent " : "", type_name, " ", to_name(var.self), type_to_array_glsl(type),
to_resource_binding(var), ";");
}
else
Expand Down Expand Up @@ -4969,6 +4988,12 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction)

auto *backing_variable = maybe_get_backing_variable(ops[2]);

if (backing_variable != nullptr && is_user_type_structured(backing_variable->self))
{
CompilerGLSL::emit_instruction(instruction);
return;
}

string base;
if (to_plain_buffer_length != 0)
base = access_chain(ops[2], &ops[3], to_plain_buffer_length, get<SPIRType>(ops[0]));
Expand Down Expand Up @@ -6693,3 +6718,17 @@ bool CompilerHLSL::builtin_translates_to_nonarray(spv::BuiltIn builtin) const
{
return (builtin == BuiltInSampleMask);
}

bool CompilerHLSL::is_user_type_structured(uint32_t id) const
{
if (hlsl_options.preserve_structured_buffers)
{
// Compare left hand side of string only as these user types can contain more meta data such as their subtypes,
// e.g. "structuredbuffer:int"
const std::string &user_type = get_decoration_string(id, DecorationUserTypeGOOGLE);
return user_type.compare(0, 16, "structuredbuffer") == 0 ||
user_type.compare(0, 18, "rwstructuredbuffer") == 0 ||
user_type.compare(0, 33, "rasterizerorderedstructuredbuffer") == 0;
}
return false;
}
8 changes: 8 additions & 0 deletions spirv_hlsl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ class CompilerHLSL : public CompilerGLSL

// Rather than emitting main() for the entry point, use the name in SPIR-V.
bool use_entry_point_name = false;

// Preserve (RW)StructuredBuffer types if the input source was HLSL.
// This relies on UserTypeGOOGLE to encode the buffer type either as "structuredbuffer" or "rwstructuredbuffer"
// whereas the type can be extended with an optional subtype, e.g. "structuredbuffer:int".
bool preserve_structured_buffers = false;
};

explicit CompilerHLSL(std::vector<uint32_t> spirv_)
Expand Down Expand Up @@ -398,6 +403,9 @@ class CompilerHLSL : public CompilerGLSL
// Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but SV_Coverage is a scalar in HLSL.
bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override;

// Returns true if the specified ID has a UserTypeGOOGLE decoration for StructuredBuffer or RWStructuredBuffer resources.
bool is_user_type_structured(uint32_t id) const override;

std::vector<TypeID> composite_selection_workaround_types;

std::string get_inner_entry_point_name() const;
Expand Down

0 comments on commit bcb6243

Please sign in to comment.