Add support for flattening I/O blocks.
Fix issue where layout() qualifiers are emitted for members in legacy targets.
This commit is contained in:
Родитель
7f2e17969b
Коммит
c5de1cfa90
|
@ -228,6 +228,13 @@ The command line client calls `Compiler::build_combined_image_samplers` automati
|
||||||
Descriptor sets are unique to Vulkan, so make sure that descriptor set + binding is remapped to a flat binding scheme (set always 0), so that other APIs can make sense of the bindings.
|
Descriptor sets are unique to Vulkan, so make sure that descriptor set + binding is remapped to a flat binding scheme (set always 0), so that other APIs can make sense of the bindings.
|
||||||
This can be done with `Compiler::set_decoration(id, spv::DecorationDescriptorSet)`.
|
This can be done with `Compiler::set_decoration(id, spv::DecorationDescriptorSet)`.
|
||||||
|
|
||||||
|
#### Linking by name for targets which do not support explicit locations (legacy GLSL/ESSL)
|
||||||
|
|
||||||
|
Modern GLSL and HLSL sources will rely on explicit layout(location) qualifiers to guide the linking process,
|
||||||
|
but legacy GLSL relies on symbol names to perform the linking. When emitting legacy shaders, these layout statements will be dropped,
|
||||||
|
so it is important that the API user ensures that the names of I/O variables are sanitized to ensure that linking will work properly.
|
||||||
|
The reflection API can rename variables, struct types and struct members to deal with these scenarios using `Compiler::set_name` and friends.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Contributions to SPIRV-Cross are welcome. See Testing and Licensing sections for details.
|
Contributions to SPIRV-Cross are welcome. See Testing and Licensing sections for details.
|
||||||
|
|
144
spirv_glsl.cpp
144
spirv_glsl.cpp
|
@ -555,6 +555,9 @@ string CompilerGLSL::to_interpolation_qualifiers(uint64_t flags)
|
||||||
|
|
||||||
string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index)
|
string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index)
|
||||||
{
|
{
|
||||||
|
if (is_legacy())
|
||||||
|
return "";
|
||||||
|
|
||||||
bool is_block = (meta[type.self].decoration.decoration_flags &
|
bool is_block = (meta[type.self].decoration.decoration_flags &
|
||||||
((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))) != 0;
|
((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))) != 0;
|
||||||
if (!is_block)
|
if (!is_block)
|
||||||
|
@ -1160,60 +1163,83 @@ const char *CompilerGLSL::to_storage_qualifiers_glsl(const SPIRVariable &var)
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerGLSL::emit_flattened_io_block(const SPIRVariable &var, const char *qual)
|
||||||
|
{
|
||||||
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
if (!type.array.empty())
|
||||||
|
SPIRV_CROSS_THROW("Array of varying structs cannot be flattened to legacy-compatible varyings.");
|
||||||
|
|
||||||
|
// Block names should never alias.
|
||||||
|
auto block_name = to_name(type.self, false);
|
||||||
|
|
||||||
|
// Shaders never use the block by interface name, so we don't
|
||||||
|
// have to track this other than updating name caches.
|
||||||
|
if (resource_names.find(block_name) != end(resource_names))
|
||||||
|
block_name = get_fallback_name(type.self);
|
||||||
|
else
|
||||||
|
resource_names.insert(block_name);
|
||||||
|
|
||||||
|
auto old_flags = meta[type.self].decoration.decoration_flags;
|
||||||
|
// Emit the members as if they are part of a block to get all qualifiers.
|
||||||
|
meta[type.self].decoration.decoration_flags |= 1ull << DecorationBlock;
|
||||||
|
|
||||||
|
uint32_t i = 0;
|
||||||
|
for (auto &member : type.member_types)
|
||||||
|
{
|
||||||
|
add_member_name(type, i);
|
||||||
|
auto &membertype = get<SPIRType>(member);
|
||||||
|
|
||||||
|
if (membertype.basetype == SPIRType::Struct)
|
||||||
|
SPIRV_CROSS_THROW("Cannot flatten struct inside structs in I/O variables.");
|
||||||
|
|
||||||
|
// Pass in the varying qualifier here so it will appear in the correct declaration order.
|
||||||
|
// Replace member name while emitting it so it encodes both struct name and member name.
|
||||||
|
// Sanitize underscores because joining the two identifiers might create more than 1 underscore in a row,
|
||||||
|
// which is not allowed.
|
||||||
|
auto member_name = get_member_name(type.self, i);
|
||||||
|
set_member_name(type.self, i, sanitize_underscores(join(to_name(type.self), "_", member_name)));
|
||||||
|
statement(member_decl(type, membertype, i, qual), ";");
|
||||||
|
// Restore member name.
|
||||||
|
set_member_name(type.self, i, member_name);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta[type.self].decoration.decoration_flags = old_flags;
|
||||||
|
|
||||||
|
// Treat this variable as flattened from now on.
|
||||||
|
flattened_structs.insert(var.self);
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerGLSL::emit_interface_block(const SPIRVariable &var)
|
void CompilerGLSL::emit_interface_block(const SPIRVariable &var)
|
||||||
{
|
{
|
||||||
auto &type = get<SPIRType>(var.basetype);
|
auto &type = get<SPIRType>(var.basetype);
|
||||||
|
|
||||||
// Either make it plain in/out or in/out blocks depending on what shader is doing ...
|
// Either make it plain in/out or in/out blocks depending on what shader is doing ...
|
||||||
bool block = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) != 0;
|
bool block = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)) != 0;
|
||||||
|
|
||||||
const char *qual = to_storage_qualifiers_glsl(var);
|
const char *qual = to_storage_qualifiers_glsl(var);
|
||||||
|
|
||||||
if (block)
|
if (block)
|
||||||
{
|
|
||||||
if (is_legacy())
|
|
||||||
SPIRV_CROSS_THROW("IO blocks are not supported in legacy targets.");
|
|
||||||
|
|
||||||
add_resource_name(var.self);
|
|
||||||
|
|
||||||
// Block names should never alias.
|
|
||||||
auto block_name = to_name(type.self, false);
|
|
||||||
|
|
||||||
// Shaders never use the block by interface name, so we don't
|
|
||||||
// have to track this other than updating name caches.
|
|
||||||
if (resource_names.find(block_name) != end(resource_names))
|
|
||||||
block_name = get_fallback_name(type.self);
|
|
||||||
else
|
|
||||||
resource_names.insert(block_name);
|
|
||||||
|
|
||||||
statement(layout_for_variable(var), qual, block_name);
|
|
||||||
begin_scope();
|
|
||||||
|
|
||||||
type.member_name_cache.clear();
|
|
||||||
|
|
||||||
uint32_t i = 0;
|
|
||||||
for (auto &member : type.member_types)
|
|
||||||
{
|
|
||||||
add_member_name(type, i);
|
|
||||||
|
|
||||||
auto &membertype = get<SPIRType>(member);
|
|
||||||
statement(member_decl(type, membertype, i), ";");
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
end_scope_decl(join(to_name(var.self), type_to_array_glsl(type)));
|
|
||||||
statement("");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// ESSL earlier than 310 and GLSL earlier than 150 did not support
|
// ESSL earlier than 310 and GLSL earlier than 150 did not support
|
||||||
// I/O variables which are struct types.
|
// I/O variables which are struct types.
|
||||||
// To support this, flatten the struct into separate varyings instead.
|
// To support this, flatten the struct into separate varyings instead.
|
||||||
if (type.basetype == SPIRType::Struct &&
|
if ((options.es && options.version < 310) || (!options.es && options.version < 150))
|
||||||
((options.es && options.version < 310) || (!options.es && options.version < 150)))
|
|
||||||
{
|
{
|
||||||
if (!type.array.empty())
|
// I/O blocks on ES require version 310 with Android Extension Pack extensions, or core version 320.
|
||||||
SPIRV_CROSS_THROW("Array of varying structs cannot be flattened to legacy-compatible varyings.");
|
// On desktop, I/O blocks were introduced with geometry shaders in GL 3.2 (GLSL 150).
|
||||||
|
emit_flattened_io_block(var, qual);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (options.es && options.version < 320)
|
||||||
|
{
|
||||||
|
// Geometry and tessellation extensions imply this extension.
|
||||||
|
if (!forced_extensions.count("GL_EXT_geometry_shader") &&
|
||||||
|
!forced_extensions.count("GL_EXT_tessellation_shader"))
|
||||||
|
require_extension("GL_EXT_shader_io_blocks");
|
||||||
|
}
|
||||||
|
|
||||||
|
add_resource_name(var.self);
|
||||||
|
|
||||||
// Block names should never alias.
|
// Block names should never alias.
|
||||||
auto block_name = to_name(type.self, false);
|
auto block_name = to_name(type.self, false);
|
||||||
|
@ -1225,34 +1251,34 @@ void CompilerGLSL::emit_interface_block(const SPIRVariable &var)
|
||||||
else
|
else
|
||||||
resource_names.insert(block_name);
|
resource_names.insert(block_name);
|
||||||
|
|
||||||
// Emit the members as if they are part of a block to get all qualifiers.
|
statement(layout_for_variable(var), qual, block_name);
|
||||||
meta[type.self].decoration.decoration_flags |= 1ull << DecorationBlock;
|
begin_scope();
|
||||||
|
|
||||||
|
type.member_name_cache.clear();
|
||||||
|
|
||||||
uint32_t i = 0;
|
uint32_t i = 0;
|
||||||
for (auto &member : type.member_types)
|
for (auto &member : type.member_types)
|
||||||
{
|
{
|
||||||
add_member_name(type, i);
|
add_member_name(type, i);
|
||||||
|
|
||||||
auto &membertype = get<SPIRType>(member);
|
auto &membertype = get<SPIRType>(member);
|
||||||
|
statement(member_decl(type, membertype, i), ";");
|
||||||
if (membertype.basetype == SPIRType::Struct)
|
|
||||||
SPIRV_CROSS_THROW("Cannot flatten struct inside structs in I/O variables.");
|
|
||||||
|
|
||||||
// Pass in the varying qualifier here so it will appear in the correct declaration order.
|
|
||||||
// Replace member name while emitting it so it encodes both struct name and member name.
|
|
||||||
// Sanitize underscores because joining the two identifiers might create more than 1 underscore in a row,
|
|
||||||
// which is not allowed.
|
|
||||||
auto member_name = get_member_name(type.self, i);
|
|
||||||
set_member_name(type.self, i, sanitize_underscores(join(to_name(type.self), "_", member_name)));
|
|
||||||
statement(member_decl(type, membertype, i, qual), ";");
|
|
||||||
// Restore member name.
|
|
||||||
set_member_name(type.self, i, member_name);
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
meta[type.self].decoration.decoration_flags &= ~(1ull << DecorationBlock);
|
end_scope_decl(join(to_name(var.self), type_to_array_glsl(type)));
|
||||||
|
statement("");
|
||||||
// Treat this variable as flattened from now on.
|
}
|
||||||
flattened_structs.insert(var.self);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ESSL earlier than 310 and GLSL earlier than 150 did not support
|
||||||
|
// I/O variables which are struct types.
|
||||||
|
// To support this, flatten the struct into separate varyings instead.
|
||||||
|
if (type.basetype == SPIRType::Struct &&
|
||||||
|
((options.es && options.version < 310) || (!options.es && options.version < 150)))
|
||||||
|
{
|
||||||
|
emit_flattened_io_block(var, qual);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -282,6 +282,7 @@ protected:
|
||||||
void emit_push_constant_block_vulkan(const SPIRVariable &var);
|
void emit_push_constant_block_vulkan(const SPIRVariable &var);
|
||||||
void emit_push_constant_block_glsl(const SPIRVariable &var);
|
void emit_push_constant_block_glsl(const SPIRVariable &var);
|
||||||
void emit_interface_block(const SPIRVariable &type);
|
void emit_interface_block(const SPIRVariable &type);
|
||||||
|
void emit_flattened_io_block(const SPIRVariable &var, const char *qual);
|
||||||
void emit_block_chain(SPIRBlock &block);
|
void emit_block_chain(SPIRBlock &block);
|
||||||
void emit_specialization_constant(const SPIRConstant &constant);
|
void emit_specialization_constant(const SPIRConstant &constant);
|
||||||
std::string emit_continue_block(uint32_t continue_block);
|
std::string emit_continue_block(uint32_t continue_block);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче