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:
Hans-Kristian Arntzen 2016-05-23 10:57:22 +02:00
Родитель 78e7615af6
Коммит f05373bdd1
4 изменённых файлов: 81 добавлений и 14 удалений

Просмотреть файл

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