Do not rely on OpName when finding OpTypeStruct aliases.
SPIR-V can stamp out multiple variants of the same types to deal with different offsets, layouts, array strides and so on in buffer blocks.
This commit is contained in:
Родитель
78e7615af6
Коммит
f05373bdd1
|
@ -199,6 +199,11 @@ struct SPIRType : IVariant
|
|||
uint32_t sampled;
|
||||
spv::ImageFormat format;
|
||||
} image;
|
||||
|
||||
// Structs can be declared multiple times if they are used as part of interface blocks.
|
||||
// We want to detect this so that we only emit the struct definition once.
|
||||
// Since we cannot rely on OpName to be equal, we need to figure out aliases.
|
||||
uint32_t type_alias = 0;
|
||||
};
|
||||
|
||||
struct SPIRExtension : IVariant
|
||||
|
|
|
@ -127,8 +127,19 @@ bool Compiler::block_is_pure(const SPIRBlock &block)
|
|||
return true;
|
||||
}
|
||||
|
||||
string Compiler::to_name(uint32_t id)
|
||||
string Compiler::to_name(uint32_t id, bool allow_alias)
|
||||
{
|
||||
if (allow_alias && ids.at(id).get_type() == TypeType)
|
||||
{
|
||||
// If this type is a simple alias, emit the
|
||||
// name of the original type instead.
|
||||
// We don't want to override the meta alias
|
||||
// as that can be overridden by the reflection APIs after parse.
|
||||
auto &type = get<SPIRType>(id);
|
||||
if (type.type_alias)
|
||||
return to_name(type.type_alias);
|
||||
}
|
||||
|
||||
if (meta[id].decoration.alias.empty())
|
||||
return join("_", id);
|
||||
else
|
||||
|
@ -1152,6 +1163,20 @@ void Compiler::parse(const Instruction &instruction)
|
|||
type.basetype = SPIRType::Struct;
|
||||
for (uint32_t i = 1; i < length; i++)
|
||||
type.member_types.push_back(ops[i]);
|
||||
|
||||
// Check if we have seen this struct type before, with just different
|
||||
// decorations.
|
||||
for (auto &other : global_struct_cache)
|
||||
{
|
||||
if (types_are_logically_equivalent(type, get<SPIRType>(other)))
|
||||
{
|
||||
type.type_alias = other;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type.type_alias == 0)
|
||||
global_struct_cache.push_back(id);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1868,3 +1893,39 @@ uint32_t Compiler::increase_bound_by(uint32_t incr_amount)
|
|||
meta.resize(new_bound);
|
||||
return curr_bound;
|
||||
}
|
||||
|
||||
bool Compiler::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const
|
||||
{
|
||||
if (a.basetype != b.basetype)
|
||||
return false;
|
||||
if (a.width != b.width)
|
||||
return false;
|
||||
if (a.vecsize != b.vecsize)
|
||||
return false;
|
||||
if (a.columns != b.columns)
|
||||
return false;
|
||||
if (a.array.size() != b.array.size())
|
||||
return false;
|
||||
|
||||
unsigned array_count = a.array.size();
|
||||
if (array_count && memcmp(a.array.data(), b.array.data(), array_count * sizeof(uint32_t)) != 0)
|
||||
return false;
|
||||
|
||||
if (a.basetype == SPIRType::Image || a.basetype == SPIRType::SampledImage)
|
||||
{
|
||||
if (memcmp(&a.image, &b.image, sizeof(SPIRType::Image)) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.member_types.size() != b.member_types.size())
|
||||
return false;
|
||||
|
||||
unsigned member_types = a.member_types.size();
|
||||
for (unsigned i = 0; i < member_types; i++)
|
||||
{
|
||||
if (!types_are_logically_equivalent(get<SPIRType>(a.member_types[i]), get<SPIRType>(b.member_types[i])))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ protected:
|
|||
std::unordered_set<uint32_t> selection_merge_targets;
|
||||
std::unordered_set<uint32_t> multiselect_merge_targets;
|
||||
|
||||
std::string to_name(uint32_t id);
|
||||
std::string to_name(uint32_t id, bool allow_alias = true);
|
||||
bool is_builtin_variable(const SPIRVariable &var) const;
|
||||
bool is_immutable(uint32_t id) const;
|
||||
bool is_member_builtin(const SPIRType &type, uint32_t index, spv::BuiltIn *builtin) const;
|
||||
|
@ -307,7 +307,6 @@ protected:
|
|||
std::unordered_set<uint32_t> invalid_expressions;
|
||||
|
||||
void update_name_cache(std::unordered_set<std::string> &cache, std::string &name);
|
||||
std::unordered_set<std::string> global_struct_cache;
|
||||
|
||||
bool function_is_pure(const SPIRFunction &func);
|
||||
bool block_is_pure(const SPIRBlock &block);
|
||||
|
@ -326,6 +325,8 @@ protected:
|
|||
|
||||
uint32_t increase_bound_by(uint32_t incr_amount);
|
||||
|
||||
bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const;
|
||||
|
||||
private:
|
||||
void parse();
|
||||
void parse(const Instruction &i);
|
||||
|
@ -360,6 +361,8 @@ private:
|
|||
|
||||
bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const;
|
||||
bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const;
|
||||
// This must be an ordered data structure so we always pick the same type aliases.
|
||||
std::vector<uint32_t> global_struct_cache;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -123,9 +123,6 @@ void CompilerGLSL::reset()
|
|||
expression_usage_counts.clear();
|
||||
forwarded_temporaries.clear();
|
||||
|
||||
// Clear identifier caches
|
||||
global_struct_cache.clear();
|
||||
|
||||
for (auto &id : ids)
|
||||
{
|
||||
if (id.get_type() == TypeVariable)
|
||||
|
@ -355,17 +352,14 @@ void CompilerGLSL::emit_header()
|
|||
|
||||
void CompilerGLSL::emit_struct(const SPIRType &type)
|
||||
{
|
||||
auto name = type_to_glsl(type);
|
||||
|
||||
// Struct types can be stamped out multiple times
|
||||
// with just different offsets, matrix layouts, etc ...
|
||||
// Type-punning with these types is legal, which complicates things
|
||||
// when we are storing struct and array types in an SSBO for example.
|
||||
// For now, detect this duplication via OpName, but ideally we should
|
||||
// find proper aliases by inspecting the actual type.
|
||||
if (global_struct_cache.find(name) != end(global_struct_cache))
|
||||
if (type.type_alias != 0)
|
||||
return;
|
||||
update_name_cache(global_struct_cache, name);
|
||||
|
||||
auto name = type_to_glsl(type);
|
||||
|
||||
statement("struct ", name);
|
||||
begin_scope();
|
||||
|
@ -797,7 +791,10 @@ void CompilerGLSL::emit_buffer_block(const SPIRVariable &var)
|
|||
{
|
||||
auto &type = get<SPIRType>(var.basetype);
|
||||
auto ssbo = meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock);
|
||||
auto buffer_name = to_name(type.self);
|
||||
|
||||
// Block names should never alias.
|
||||
auto buffer_name = to_name(type.self, false);
|
||||
|
||||
statement(layout_for_variable(var) + (ssbo ? "buffer " : "uniform ") + buffer_name);
|
||||
begin_scope();
|
||||
|
||||
|
@ -830,7 +827,8 @@ void CompilerGLSL::emit_interface_block(const SPIRVariable &var)
|
|||
|
||||
if (block)
|
||||
{
|
||||
statement(layout_for_variable(var), qual, to_name(type.self));
|
||||
// Block names should never alias.
|
||||
statement(layout_for_variable(var), qual, to_name(type.self, false));
|
||||
begin_scope();
|
||||
|
||||
uint32_t i = 0;
|
||||
|
|
Загрузка…
Ссылка в новой задаче