Merge pull request #127 from KhronosGroup/flatten-io

Flatten varying structs in legacy targets.
This commit is contained in:
Hans-Kristian Arntzen 2017-02-24 14:30:47 +01:00 committed by GitHub
commit f33220e45a
18 changed files with 436 additions and 51 deletions

View File

@ -122,9 +122,15 @@ Please see `samples/cpp` where some GLSL shaders are compiled to SPIR-V, decompi
Reading through the samples should explain how to use the C++ interface.
A simple Makefile is included to build all shaders in the directory.
### Using SPIRV-Cross to output GLSL shaders from glslang HLSL
### Implementation notes
#### Entry point
When using SPIR-V and SPIRV-Cross as an intermediate step for cross-compiling between high level languages there are some considerations to take into account,
as not all features used by one high-level language are necessarily supported natively by the target shader language.
SPIRV-Cross aims to provide the tools needed to handle these scenarios in a clean and robust way, but some manual action is required to maintain compatibility.
#### HLSL source to GLSL
##### HLSL entry points
When using SPIR-V shaders compiled from HLSL, there are some extra things you need to take care of.
First make sure that the entry point is used correctly.
@ -136,7 +142,67 @@ Cannot end a function before ending the current block.
Likely cause: If this SPIR-V was created from glslang HLSL, make sure the entry point is valid.
```
#### Separate image samplers
##### Vertex/Fragment interface linking
HLSL relies on semantics in order to effectively link together shader stages. In the SPIR-V generated by glslang, the transformation from HLSL to GLSL ends up looking like
```
struct VSOutput {
// SV_Position is rerouted to gl_Position
float4 position : SV_Position;
float4 coord : TEXCOORD0;
};
VSOutput main(...) {}
```
```
struct VSOutput {
float4 coord;
}
layout(location = 0) out VSOutput _magicNameGeneratedByGlslang;
```
While this works, be aware of the type of the struct which is used in the vertex stage and the fragment stage.
There may be issues if the structure type name differs in vertex stage and fragment stage.
You can make use of the reflection interface to force the name of the struct type.
```
// Something like this for both vertex outputs and fragment inputs.
compiler.set_name(varying_resource.base_type_id, "VertexFragmentLinkage");
```
#### HLSL source to legacy GLSL/ESSL
HLSL tends to emit varying struct types to pass data between vertex and fragment.
This is not supported in legacy GL/GLES targets, so to support this, varying structs are flattened.
This is done automatically, but the API user might need to be aware that this is happening in order to support all cases.
Modern GLES code like this:
```
struct Output {
vec4 a;
vec2 b;
};
out Output vout;
```
Is transformed into:
```
struct Output {
vec4 a;
vec2 b;
};
varying vec4 Output_a;
varying vec2 Output_b;
```
Note that now, both the struct name and the member names will participate in the linking interface between vertex and fragment, so
API users might want to ensure that both the struct names and member names match so that vertex outputs and fragment inputs can link properly.
#### Separate image samplers (HLSL/Vulkan) for backends which do not support it (GLSL)
Another thing you need to remember is when using samplers and textures in HLSL these are separable, and not directly compatible with GLSL. If you need to use this with desktop GL/GLES, you need to call `Compiler::build_combined_image_samplers` first before calling `Compiler::compile`, or you will get an exception.
@ -155,7 +221,12 @@ for (auto &remap : compiler->get_combined_image_samplers())
```
If your target is Vulkan GLSL, `--vulkan-semantics` will emit separate image samplers as you'd expect.
The command line client does this automatically, but if you're calling the library, you'll need to do this yourself.
The command line client calls `Compiler::build_combined_image_samplers` automatically, but if you're calling the library, you'll need to do this yourself.
#### Descriptor sets (Vulkan GLSL) for backends which do not support them (HLSL/GLSL/Metal)
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)`.
## Contributing

View File

@ -444,7 +444,8 @@ static void print_help()
fprintf(stderr, "Usage: spirv-cross [--output <output path>] [SPIR-V file] [--es] [--no-es] [--no-cfg-analysis] "
"[--version <GLSL "
"version>] [--dump-resources] [--help] [--force-temporary] [--cpp] [--cpp-interface-name <name>] "
"[--metal] [--hlsl] [--shader-model] [--vulkan-semantics] [--flatten-ubo] [--fixup-clipspace] [--iterations iter] "
"[--metal] [--hlsl] [--shader-model] [--vulkan-semantics] [--flatten-ubo] [--fixup-clipspace] "
"[--iterations iter] "
"[--pls-in format input-name] [--pls-out format output-name] [--remap source_name target_name "
"components] [--extension ext] [--entry name] [--remove-unused-variables] "
"[--remap-variable-type <variable_name> <new_variable_type>]\n");

View File

@ -9,10 +9,10 @@ in VertexData
layout(location = 3) float i;
} vin;
layout(location = 4) in flat float f;
layout(location = 5) in centroid vec4 g;
layout(location = 6) in flat int h;
layout(location = 7) in sample float i;
layout(location = 4) flat in float f;
layout(location = 5) centroid in vec4 g;
layout(location = 6) flat in int h;
layout(location = 7) sample in float i;
void main()
{

View File

@ -8,9 +8,9 @@ out VertexData
layout(location = 3) float i;
} vout;
layout(location = 4) out flat float f;
layout(location = 5) out centroid vec4 g;
layout(location = 6) out flat int h;
layout(location = 4) flat out float f;
layout(location = 5) centroid out vec4 g;
layout(location = 6) flat out int h;
layout(location = 7) out float i;
void main()

View File

@ -9,7 +9,7 @@ struct Foobar
};
layout(location = 0) out vec4 FragColor;
layout(location = 0) in mediump flat int index;
layout(location = 0) flat in mediump int index;
vec4 resolve(Foobar f)
{

View File

@ -0,0 +1,22 @@
#version 100
precision mediump float;
precision highp int;
struct Inputs
{
highp vec4 a;
highp vec2 b;
};
varying highp vec4 Inputs_a;
varying highp vec2 Inputs_b;
void main()
{
Inputs v0 = Inputs(Inputs_a, Inputs_b);
Inputs v1 = Inputs(Inputs_a, Inputs_b);
highp vec4 a = Inputs_a;
highp vec4 b = Inputs_b.xxyy;
gl_FragData[0] = ((((v0.a + v0.b.xxyy) + v1.a) + v1.b.yyxx) + a) + b;
}

View File

@ -0,0 +1,32 @@
#version 100
struct Output
{
vec4 a;
vec2 b;
};
varying vec4 Output_a;
varying vec2 Output_b;
void main()
{
Output s = Output(vec4(0.5), vec2(0.25));
{
Output vout = s;
Output_a = vout.a;
Output_b = vout.b;
}
{
Output vout = s;
Output_a = vout.a;
Output_b = vout.b;
}
Output tmp = Output(Output_a, Output_b);
Output_a = tmp.a;
Output_b = tmp.b;
Output_a.x = 1.0;
Output_b.y = 1.0;
float c = Output_a.x;
}

View File

@ -2,7 +2,7 @@
#extension GL_EXT_tessellation_shader : require
layout(vertices = 1) out;
out patch vec3 vFoo;
patch out vec3 vFoo;
void main()
{

View File

@ -12,8 +12,8 @@ layout(std140) uniform UBO
vec4 uFrustum[6];
} _41;
out patch vec2 vOutPatchPosBase;
out patch vec4 vPatchLods;
patch out vec2 vOutPatchPosBase;
patch out vec4 vPatchLods;
in vec2 vPatchPosBase[32];
bool frustum_cull(vec2 p0)

View File

@ -14,8 +14,8 @@ layout(binding = 1, std140) uniform UBO
layout(binding = 0) uniform mediump sampler2D uHeightmapDisplacement;
in patch vec2 vOutPatchPosBase;
in patch vec4 vPatchLods;
patch in vec2 vOutPatchPosBase;
patch in vec4 vPatchLods;
out vec4 vGradNormalTex;
out vec3 vWorld;

View File

@ -0,0 +1,25 @@
#version 310 es
precision highp float;
struct Inputs
{
vec4 a;
vec2 b;
};
layout(location = 0) in Inputs vin;
layout(location = 0) out vec4 FragColor;
void main()
{
// Read struct once.
Inputs v0 = vin;
// Read struct again.
Inputs v1 = vin;
// Read members individually.
vec4 a = vin.a;
vec4 b = vin.b.xxyy;
FragColor = v0.a + v0.b.xxyy + v1.a + v1.b.yyxx + a + b;
}

View File

@ -0,0 +1,33 @@
#version 310 es
struct Output
{
vec4 a;
vec2 b;
};
layout(location = 0) out Output vout;
void main()
{
Output s = Output(vec4(0.5), vec2(0.25));
// Write whole struct.
vout = s;
// Write whole struct again, checks for scoping.
vout = s;
// Read it back.
Output tmp = vout;
// Write elements individually.
vout.a = tmp.a;
vout.b = tmp.b;
// Write individual elements.
vout.a.x = 1.0;
vout.b.y = 1.0;
// Read individual elements.
float c = vout.a.x;
}

View File

@ -24,6 +24,32 @@ using namespace spv;
using namespace spirv_cross;
using namespace std;
// Sanitizes underscores for GLSL where multiple underscores in a row are not allowed.
static string sanitize_underscores(const string &str)
{
string res;
res.reserve(str.size());
bool last_underscore = false;
for (auto c : str)
{
if (c == '_')
{
if (last_underscore)
continue;
res += c;
last_underscore = true;
}
else
{
res += c;
last_underscore = false;
}
}
return res;
}
// Returns true if an arithmetic operation does not change behavior depending on signedness.
static bool opcode_is_sign_invariant(Op opcode)
{
@ -1112,21 +1138,36 @@ void CompilerGLSL::emit_buffer_block_flattened(const SPIRVariable &var)
SPIRV_CROSS_THROW("All basic types in a flattened block must be the same.");
}
void CompilerGLSL::emit_interface_block(const SPIRVariable &var)
const char *CompilerGLSL::to_storage_qualifiers_glsl(const SPIRVariable &var)
{
auto &execution = get_entry_point();
if (var.storage == StorageClassInput || var.storage == StorageClassOutput)
{
if (is_legacy() && execution.model == ExecutionModelVertex)
return var.storage == StorageClassInput ? "attribute " : "varying ";
else if (is_legacy() && execution.model == ExecutionModelFragment)
return "varying "; // Fragment outputs are renamed so they never hit this case.
else
return var.storage == StorageClassInput ? "in " : "out ";
}
else if (var.storage == StorageClassUniformConstant || var.storage == StorageClassUniform ||
var.storage == StorageClassPushConstant)
{
return "uniform ";
}
return "";
}
void CompilerGLSL::emit_interface_block(const SPIRVariable &var)
{
auto &type = get<SPIRType>(var.basetype);
// 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;
const char *qual = nullptr;
if (is_legacy() && execution.model == ExecutionModelVertex)
qual = var.storage == StorageClassInput ? "attribute " : "varying ";
else if (is_legacy() && execution.model == ExecutionModelFragment)
qual = "varying "; // Fragment outputs are renamed so they never hit this case.
else
qual = var.storage == StorageClassInput ? "in " : "out ";
const char *qual = to_storage_qualifiers_glsl(var);
if (block)
{
@ -1164,9 +1205,60 @@ void CompilerGLSL::emit_interface_block(const SPIRVariable &var)
statement("");
}
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)))
{
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);
// 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 &= ~(1ull << DecorationBlock);
// Treat this variable as flattened from now on.
flattened_structs.insert(var.self);
}
else
{
add_resource_name(var.self);
statement(layout_for_variable(var), qual, variable_decl(var), ";");
statement(layout_for_variable(var), variable_decl(var), ";");
}
}
}
@ -1182,7 +1274,7 @@ void CompilerGLSL::emit_uniform(const SPIRVariable &var)
}
add_resource_name(var.self);
statement(layout_for_variable(var), "uniform ", variable_decl(var), ";");
statement(layout_for_variable(var), variable_decl(var), ";");
}
void CompilerGLSL::emit_specialization_constant(const SPIRConstant &constant)
@ -1682,6 +1774,10 @@ string CompilerGLSL::to_expression(uint32_t id)
var.deferred_declaration = false;
return variable_decl(var);
}
else if (flattened_structs.count(id))
{
return load_flattened_struct(var);
}
else
{
auto &dec = meta[var.self].decoration;
@ -3181,8 +3277,8 @@ const char *CompilerGLSL::index_to_swizzle(uint32_t index)
}
}
string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32_t count, bool index_is_literal,
bool chain_only, bool *need_transpose)
string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count,
bool index_is_literal, bool chain_only, bool *need_transpose)
{
string expr;
if (!chain_only)
@ -3308,6 +3404,11 @@ string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32
return expr;
}
string CompilerGLSL::to_flattened_struct_member(const SPIRType &type, uint32_t index)
{
return sanitize_underscores(join(to_name(type.self), "_", to_member_name(type, index)));
}
string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type,
bool *out_need_transpose)
{
@ -3322,12 +3423,64 @@ string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32
return flattened_access_chain(base, indices, count, target_type, 0, matrix_stride, need_transpose);
}
else if (flattened_structs.count(base) && count > 0)
{
auto chain = access_chain_internal(base, indices, count, false, true).substr(1);
auto &type = get<SPIRType>(get<SPIRVariable>(base).basetype);
if (out_need_transpose)
*out_need_transpose = false;
return sanitize_underscores(join(to_name(type.self), "_", chain));
}
else
{
return access_chain(base, indices, count, false, false, out_need_transpose);
return access_chain_internal(base, indices, count, false, false, out_need_transpose);
}
}
string CompilerGLSL::load_flattened_struct(SPIRVariable &var)
{
auto expr = type_to_glsl_constructor(get<SPIRType>(var.basetype));
expr += '(';
auto &type = get<SPIRType>(var.basetype);
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
{
if (i)
expr += ", ";
// Flatten the varyings.
// Apply name transformation for flattened I/O blocks.
expr += to_flattened_struct_member(type, i);
}
expr += ')';
return expr;
}
void CompilerGLSL::store_flattened_struct(SPIRVariable &var, uint32_t value)
{
// We're trying to store a structure which has been flattened.
// Need to copy members one by one.
auto rhs = to_expression(value);
// Store result locally.
// Since we're declaring a variable potentially multiple times here,
// store the variable in an isolated scope.
begin_scope();
statement(variable_decl_function_local(var), " = ", rhs, ";");
auto &type = get<SPIRType>(var.basetype);
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
{
// Flatten the varyings.
// Apply name transformation for flattened I/O blocks.
auto lhs = sanitize_underscores(join(to_name(type.self), "_", to_member_name(type, i)));
rhs = join(to_name(var.self), ".", to_member_name(type, i));
statement(lhs, " = ", rhs, ";");
}
end_scope();
}
std::string CompilerGLSL::flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count,
const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride,
bool need_transpose)
@ -3670,12 +3823,25 @@ void CompilerGLSL::register_call_out_argument(uint32_t id)
flush_variable_declaration(var->self);
}
string CompilerGLSL::variable_decl_function_local(SPIRVariable &var)
{
// These variables are always function local,
// so make sure we emit the variable without storage qualifiers.
// Some backends will inject custom variables locally in a function
// with a storage qualifier which is not function-local.
auto old_storage = var.storage;
var.storage = StorageClassFunction;
auto expr = variable_decl(var);
var.storage = old_storage;
return expr;
}
void CompilerGLSL::flush_variable_declaration(uint32_t id)
{
auto *var = maybe_get<SPIRVariable>(id);
if (var && var->deferred_declaration)
{
statement(variable_decl(*var), ";");
statement(variable_decl_function_local(*var), ";");
var->deferred_declaration = false;
}
}
@ -3983,6 +4149,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
var->static_expression = ops[1];
else if (var && var->loop_variable && !var->loop_variable_enable)
var->static_expression = ops[1];
else if (var && flattened_structs.count(ops[0]))
{
store_flattened_struct(*var, ops[1]);
register_write(ops[0]);
}
else
{
auto lhs = to_expression(ops[0]);
@ -4008,7 +4179,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
{
uint32_t result_type = ops[0];
uint32_t id = ops[1];
auto e = access_chain(ops[2], &ops[3], length - 3, true);
auto e = access_chain_internal(ops[2], &ops[3], length - 3, true);
set<SPIRExpression>(id, e + ".length()", result_type, true);
break;
}
@ -4179,7 +4350,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// Make a copy, then use access chain to store the variable.
statement(declare_temporary(result_type, id), to_expression(vec), ";");
set<SPIRExpression>(id, to_name(id), result_type, true);
auto chain = access_chain(id, &index, 1, false);
auto chain = access_chain_internal(id, &index, 1, false);
statement(chain, " = ", to_expression(comp), ";");
break;
}
@ -4189,7 +4360,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
uint32_t result_type = ops[0];
uint32_t id = ops[1];
auto expr = access_chain(ops[2], &ops[3], 1, false);
auto expr = access_chain_internal(ops[2], &ops[3], 1, false);
emit_op(result_type, id, expr, should_forward(ops[2]));
break;
}
@ -4220,13 +4391,13 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
//
// Including the base will prevent this and would trigger multiple reads
// from expression causing it to be forced to an actual temporary in GLSL.
auto expr = access_chain(ops[2], &ops[3], length, true, true);
auto expr = access_chain_internal(ops[2], &ops[3], length, true, true);
auto &e = emit_op(result_type, id, expr, true, !expression_is_forwarded(ops[2]));
e.base_expression = ops[2];
}
else
{
auto expr = access_chain(ops[2], &ops[3], length, true);
auto expr = access_chain_internal(ops[2], &ops[3], length, true);
emit_op(result_type, id, expr, should_forward(ops[2]), !expression_is_forwarded(ops[2]));
}
break;
@ -4249,12 +4420,12 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// Make a copy, then use access chain to store the variable.
statement(declare_temporary(result_type, id), to_expression(composite), ";");
set<SPIRExpression>(id, to_name(id), result_type, true);
auto chain = access_chain(id, elems, length, true);
auto chain = access_chain_internal(id, elems, length, true);
statement(chain, " = ", to_expression(obj), ";");
}
else
{
auto chain = access_chain(composite, elems, length, true);
auto chain = access_chain_internal(composite, elems, length, true);
statement(chain, " = ", to_expression(obj), ";");
set<SPIRExpression>(id, to_expression(composite), result_type, true);
@ -5323,7 +5494,8 @@ string CompilerGLSL::variable_decl(const SPIRType &type, const string &name)
return join(type_name, " ", name, type_to_array_glsl(type));
}
string CompilerGLSL::member_decl(const SPIRType &type, const SPIRType &membertype, uint32_t index)
string CompilerGLSL::member_decl(const SPIRType &type, const SPIRType &membertype, uint32_t index,
const string &qualifier)
{
uint64_t memberflags = 0;
auto &memb = meta[type.self].members;
@ -5336,7 +5508,8 @@ string CompilerGLSL::member_decl(const SPIRType &type, const SPIRType &membertyp
if (is_block)
qualifiers = to_interpolation_qualifiers(memberflags);
return join(layout_for_member(type, index), flags_to_precision_qualifiers_glsl(membertype, memberflags), qualifiers,
return join(layout_for_member(type, index), qualifiers, qualifier,
flags_to_precision_qualifiers_glsl(membertype, memberflags),
variable_decl(membertype, to_member_name(type, index)));
}
@ -5398,9 +5571,12 @@ string CompilerGLSL::to_qualifiers_glsl(uint32_t id)
if (var && var->storage == StorageClassWorkgroup && !backend.shared_is_implied)
res += "shared ";
res += to_precision_qualifiers_glsl(id);
res += to_interpolation_qualifiers(flags);
if (var)
res += to_storage_qualifiers_glsl(*var);
res += to_precision_qualifiers_glsl(id);
auto &type = expression_type(id);
if (type.image.dim != DimSubpassData && type.image.sampled == 2)
{
if (flags & (1ull << DecorationNonWritable))
@ -5931,7 +6107,7 @@ void CompilerGLSL::emit_function(SPIRFunction &func, uint64_t return_flags)
add_local_variable_name(var.self);
if (var.initializer)
statement(variable_decl(var), ";");
statement(variable_decl_function_local(var), ";");
else
{
// Don't declare variable until first use to declutter the GLSL output quite a lot.

View File

@ -159,7 +159,8 @@ protected:
virtual void emit_texture_op(const Instruction &i);
virtual std::string type_to_glsl(const SPIRType &type);
virtual std::string builtin_to_glsl(spv::BuiltIn builtin);
virtual std::string member_decl(const SPIRType &type, const SPIRType &member_type, uint32_t member);
virtual std::string member_decl(const SPIRType &type, const SPIRType &member_type, uint32_t member,
const std::string &qualifier = "");
virtual std::string image_type_glsl(const SPIRType &type);
virtual std::string constant_expression(const SPIRConstant &c);
std::string constant_op_expression(const SPIRConstantOp &cop);
@ -238,6 +239,7 @@ protected:
std::string to_array_size(const SPIRType &type, uint32_t index);
uint32_t to_array_size_literal(const SPIRType &type, uint32_t index) const;
std::string variable_decl(const SPIRVariable &variable);
std::string variable_decl_function_local(SPIRVariable &variable);
void add_local_variable_name(uint32_t id);
void add_resource_name(uint32_t id);
@ -315,7 +317,7 @@ protected:
bool expression_is_forwarded(uint32_t id);
SPIRExpression &emit_op(uint32_t result_type, uint32_t result_id, const std::string &rhs, bool forward_rhs,
bool suppress_usage_tracking = false);
std::string access_chain(uint32_t base, const uint32_t *indices, uint32_t count, bool index_is_literal,
std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, bool index_is_literal,
bool chain_only = false, bool *need_transpose = nullptr);
std::string access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type,
bool *need_transpose = nullptr);
@ -348,6 +350,7 @@ protected:
std::string argument_decl(const SPIRFunction::Parameter &arg);
std::string to_qualifiers_glsl(uint32_t id);
const char *to_precision_qualifiers_glsl(uint32_t id);
virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var);
const char *flags_to_precision_qualifiers_glsl(const SPIRType &type, uint64_t flags);
const char *format_to_glsl(spv::ImageFormat format);
std::string layout_for_member(const SPIRType &type, uint32_t index);
@ -384,6 +387,11 @@ protected:
std::unordered_set<uint32_t> emitted_functions;
std::unordered_set<uint32_t> flattened_buffer_blocks;
std::unordered_set<uint32_t> flattened_structs;
std::string load_flattened_struct(SPIRVariable &var);
std::string to_flattened_struct_member(const SPIRType &type, uint32_t index);
void store_flattened_struct(SPIRVariable &var, uint32_t value);
// Usage tracking. If a temporary is used more than once, use the temporary instead to
// avoid AST explosion when SPIRV is generated with pure SSA and doesn't write stuff to variables.

View File

@ -165,6 +165,19 @@ void CompilerHLSL::emit_interface_block_globally(const SPIRVariable &var)
}
}
const char *CompilerHLSL::to_storage_qualifiers_glsl(const SPIRVariable &var)
{
// Input and output variables are handled specially in HLSL backend.
// The variables are declared as global, private variables, and do not need any qualifiers.
if (var.storage == StorageClassUniformConstant || var.storage == StorageClassUniform ||
var.storage == StorageClassPushConstant)
{
return "uniform ";
}
return "";
}
void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, uint32_t &binding_number, bool builtins)
{
auto &execution = get_entry_point();
@ -897,7 +910,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
void CompilerHLSL::emit_uniform(const SPIRVariable &var)
{
add_resource_name(var.self);
statement("uniform ", variable_decl(var), ";");
statement(variable_decl(var), ";");
}
void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t count)

View File

@ -66,6 +66,8 @@ private:
void emit_push_constant_block(const SPIRVariable &var) override;
void emit_uniform(const SPIRVariable &var) override;
const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override;
Options options;
bool requires_op_fmod = false;
};

View File

@ -1305,9 +1305,10 @@ void CompilerMSL::emit_fixup()
}
// Returns a declaration for a structure member.
string CompilerMSL::member_decl(const SPIRType &type, const SPIRType &membertype, uint32_t index)
string CompilerMSL::member_decl(const SPIRType &type, const SPIRType &membertype, uint32_t index,
const string &qualifier)
{
return join(type_to_glsl(membertype), " ", to_member_name(type, index), type_to_array_glsl(membertype),
return join(type_to_glsl(membertype), " ", qualifier, to_member_name(type, index), type_to_array_glsl(membertype),
member_attribute_qualifier(type, index));
}

View File

@ -109,7 +109,8 @@ protected:
std::string type_to_glsl(const SPIRType &type) override;
std::string image_type_glsl(const SPIRType &type) override;
std::string builtin_to_glsl(spv::BuiltIn builtin) override;
std::string member_decl(const SPIRType &type, const SPIRType &member_type, uint32_t member) override;
std::string member_decl(const SPIRType &type, const SPIRType &member_type, uint32_t member,
const std::string &qualifier) override;
std::string constant_expression(const SPIRConstant &c) override;
size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const override;
std::string to_func_call_arg(uint32_t id) override;