|
|
@@ -341,6 +341,7 @@ void CompilerGLSL::reset()
|
|
|
|
|
|
statement_count = 0;
|
|
|
indent = 0;
|
|
|
+ current_loop_level = 0;
|
|
|
}
|
|
|
|
|
|
void CompilerGLSL::remap_pls_variables()
|
|
|
@@ -539,6 +540,9 @@ string CompilerGLSL::compile()
|
|
|
backend.supports_extensions = true;
|
|
|
backend.use_array_constructor = true;
|
|
|
|
|
|
+ if (is_legacy_es())
|
|
|
+ backend.support_case_fallthrough = false;
|
|
|
+
|
|
|
// Scan the SPIR-V to find trivial uses of extensions.
|
|
|
fixup_type_alias();
|
|
|
reorder_type_alias();
|
|
|
@@ -2952,12 +2956,13 @@ void CompilerGLSL::emit_resources()
|
|
|
{
|
|
|
auto *type = &id.get<SPIRType>();
|
|
|
|
|
|
- bool is_natural_struct =
|
|
|
- type->basetype == SPIRType::Struct && type->array.empty() && !type->pointer &&
|
|
|
- (!has_decoration(type->self, DecorationBlock) && !has_decoration(type->self, DecorationBufferBlock));
|
|
|
+ bool is_natural_struct = type->basetype == SPIRType::Struct && type->array.empty() && !type->pointer &&
|
|
|
+ (!has_decoration(type->self, DecorationBlock) &&
|
|
|
+ !has_decoration(type->self, DecorationBufferBlock));
|
|
|
|
|
|
// Special case, ray payload and hit attribute blocks are not really blocks, just regular structs.
|
|
|
- if (type->basetype == SPIRType::Struct && type->pointer && has_decoration(type->self, DecorationBlock) &&
|
|
|
+ if (type->basetype == SPIRType::Struct && type->pointer &&
|
|
|
+ has_decoration(type->self, DecorationBlock) &&
|
|
|
(type->storage == StorageClassRayPayloadNV || type->storage == StorageClassIncomingRayPayloadNV ||
|
|
|
type->storage == StorageClassHitAttributeNV))
|
|
|
{
|
|
|
@@ -3432,9 +3437,8 @@ string CompilerGLSL::to_composite_constructor_expression(uint32_t id, bool uses_
|
|
|
{
|
|
|
auto &type = expression_type(id);
|
|
|
|
|
|
- bool reroll_array = !type.array.empty() &&
|
|
|
- (!backend.array_is_value_type ||
|
|
|
- (uses_buffer_offset && !backend.buffer_offset_array_is_value_type));
|
|
|
+ bool reroll_array = !type.array.empty() && (!backend.array_is_value_type ||
|
|
|
+ (uses_buffer_offset && !backend.buffer_offset_array_is_value_type));
|
|
|
|
|
|
if (reroll_array)
|
|
|
{
|
|
|
@@ -4547,6 +4551,17 @@ bool CompilerGLSL::expression_suppresses_usage_tracking(uint32_t id) const
|
|
|
return suppressed_usage_tracking.count(id) != 0;
|
|
|
}
|
|
|
|
|
|
+bool CompilerGLSL::expression_read_implies_multiple_reads(uint32_t id) const
|
|
|
+{
|
|
|
+ auto *expr = maybe_get<SPIRExpression>(id);
|
|
|
+ if (!expr)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // If we're emitting code at a deeper loop level than when we emitted the expression,
|
|
|
+ // we're probably reading the same expression over and over.
|
|
|
+ return current_loop_level > expr->emitted_loop_level;
|
|
|
+}
|
|
|
+
|
|
|
SPIRExpression &CompilerGLSL::emit_op(uint32_t result_type, uint32_t result_id, const string &rhs, bool forwarding,
|
|
|
bool suppress_usage_tracking)
|
|
|
{
|
|
|
@@ -4702,7 +4717,8 @@ bool CompilerGLSL::emit_complex_bitcast(uint32_t result_type, uint32_t id, uint3
|
|
|
|
|
|
if (output_type.basetype == SPIRType::Half && input_type.basetype == SPIRType::Float && input_type.vecsize == 1)
|
|
|
expr = join("unpackFloat2x16(floatBitsToUint(", to_unpacked_expression(op0), "))");
|
|
|
- else if (output_type.basetype == SPIRType::Float && input_type.basetype == SPIRType::Half && input_type.vecsize == 2)
|
|
|
+ else if (output_type.basetype == SPIRType::Float && input_type.basetype == SPIRType::Half &&
|
|
|
+ input_type.vecsize == 2)
|
|
|
expr = join("uintBitsToFloat(packFloat2x16(", to_unpacked_expression(op0), "))");
|
|
|
else
|
|
|
return false;
|
|
|
@@ -5368,8 +5384,8 @@ static inline bool image_opcode_is_sample_no_dref(Op op)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void CompilerGLSL::emit_sparse_feedback_temporaries(uint32_t result_type_id, uint32_t id,
|
|
|
- uint32_t &feedback_id, uint32_t &texel_id)
|
|
|
+void CompilerGLSL::emit_sparse_feedback_temporaries(uint32_t result_type_id, uint32_t id, uint32_t &feedback_id,
|
|
|
+ uint32_t &texel_id)
|
|
|
{
|
|
|
// Need to allocate two temporaries.
|
|
|
if (options.es)
|
|
|
@@ -5421,7 +5437,8 @@ void CompilerGLSL::emit_texture_op(const Instruction &i, bool sparse)
|
|
|
if (sparse)
|
|
|
{
|
|
|
statement(to_expression(sparse_code_id), " = ", expr, ";");
|
|
|
- expr = join(type_to_glsl(return_type), "(", to_expression(sparse_code_id), ", ", to_expression(sparse_texel_id), ")");
|
|
|
+ expr = join(type_to_glsl(return_type), "(", to_expression(sparse_code_id), ", ", to_expression(sparse_texel_id),
|
|
|
+ ")");
|
|
|
forward = true;
|
|
|
inherited_expressions.clear();
|
|
|
}
|
|
|
@@ -5708,8 +5725,7 @@ bool CompilerGLSL::expression_is_non_value_type_array(uint32_t ptr)
|
|
|
return false;
|
|
|
|
|
|
auto &backed_type = get<SPIRType>(var->basetype);
|
|
|
- return !backend.buffer_offset_array_is_value_type &&
|
|
|
- backed_type.basetype == SPIRType::Struct &&
|
|
|
+ return !backend.buffer_offset_array_is_value_type && backed_type.basetype == SPIRType::Struct &&
|
|
|
has_member_decoration(backed_type.self, 0, DecorationOffset);
|
|
|
}
|
|
|
|
|
|
@@ -5853,7 +5869,8 @@ string CompilerGLSL::to_function_args(const TextureFunctionArguments &args, bool
|
|
|
// The IR can give us more components than we need, so chop them off as needed.
|
|
|
auto swizzle_expr = swizzle(args.coord_components, expression_type(args.coord).vecsize);
|
|
|
// Only enclose the UV expression if needed.
|
|
|
- auto coord_expr = (*swizzle_expr == '\0') ? to_expression(args.coord) : (to_enclosed_expression(args.coord) + swizzle_expr);
|
|
|
+ auto coord_expr =
|
|
|
+ (*swizzle_expr == '\0') ? to_expression(args.coord) : (to_enclosed_expression(args.coord) + swizzle_expr);
|
|
|
|
|
|
// texelFetch only takes int, not uint.
|
|
|
auto &coord_type = expression_type(args.coord);
|
|
|
@@ -5878,7 +5895,8 @@ string CompilerGLSL::to_function_args(const TextureFunctionArguments &args, bool
|
|
|
forward = forward && should_forward(args.dref);
|
|
|
|
|
|
// SPIR-V splits dref and coordinate.
|
|
|
- if (args.base.is_gather || args.coord_components == 4) // GLSL also splits the arguments in two. Same for textureGather.
|
|
|
+ if (args.base.is_gather ||
|
|
|
+ args.coord_components == 4) // GLSL also splits the arguments in two. Same for textureGather.
|
|
|
{
|
|
|
farg_str += ", ";
|
|
|
farg_str += to_expression(args.coord);
|
|
|
@@ -6994,7 +7012,7 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
|
|
|
|
|
|
default:
|
|
|
SPIRV_CROSS_THROW(
|
|
|
- "Cannot implement gl_InstanceID in Vulkan GLSL. This shader was created with GL semantics.");
|
|
|
+ "Cannot implement gl_InstanceID in Vulkan GLSL. This shader was created with GL semantics.");
|
|
|
}
|
|
|
}
|
|
|
if (!options.es && options.version < 140)
|
|
|
@@ -8169,6 +8187,13 @@ void CompilerGLSL::track_expression_read(uint32_t id)
|
|
|
auto &v = expression_usage_counts[id];
|
|
|
v++;
|
|
|
|
|
|
+ // If we create an expression outside a loop,
|
|
|
+ // but access it inside a loop, we're implicitly reading it multiple times.
|
|
|
+ // If the expression in question is expensive, we should hoist it out to avoid relying on loop-invariant code motion
|
|
|
+ // working inside the backend compiler.
|
|
|
+ if (expression_read_implies_multiple_reads(id))
|
|
|
+ v++;
|
|
|
+
|
|
|
if (v >= 2)
|
|
|
{
|
|
|
//if (v == 2)
|
|
|
@@ -8435,8 +8460,8 @@ string CompilerGLSL::build_composite_combiner(uint32_t return_type, const uint32
|
|
|
if (i)
|
|
|
op += ", ";
|
|
|
|
|
|
- bool uses_buffer_offset = type.basetype == SPIRType::Struct &&
|
|
|
- has_member_decoration(type.self, i, DecorationOffset);
|
|
|
+ bool uses_buffer_offset =
|
|
|
+ type.basetype == SPIRType::Struct && has_member_decoration(type.self, i, DecorationOffset);
|
|
|
subop = to_composite_constructor_expression(elems[i], uses_buffer_offset);
|
|
|
}
|
|
|
|
|
|
@@ -9683,7 +9708,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
{
|
|
|
auto &type = get<SPIRType>(ops[0]);
|
|
|
if (type.vecsize > 1)
|
|
|
- GLSL_UFOP(not);
|
|
|
+ GLSL_UFOP(not );
|
|
|
else
|
|
|
GLSL_UOP(!);
|
|
|
break;
|
|
|
@@ -10471,7 +10496,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
uint32_t operands = ops[4];
|
|
|
if (operands != ImageOperandsSampleMask || length != 6)
|
|
|
SPIRV_CROSS_THROW(
|
|
|
- "Multisampled image used in OpImageRead, but unexpected operand mask was used.");
|
|
|
+ "Multisampled image used in OpImageRead, but unexpected operand mask was used.");
|
|
|
|
|
|
uint32_t samples = ops[5];
|
|
|
statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_expression(ops[2]), ", ",
|
|
|
@@ -10482,8 +10507,8 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_expression(ops[2]), ", ",
|
|
|
coord_expr, ", ", to_expression(sparse_texel_id), ");");
|
|
|
}
|
|
|
- imgexpr = join(type_to_glsl(get<SPIRType>(result_type)), "(",
|
|
|
- to_expression(sparse_code_id), ", ", to_expression(sparse_texel_id), ")");
|
|
|
+ imgexpr = join(type_to_glsl(get<SPIRType>(result_type)), "(", to_expression(sparse_code_id), ", ",
|
|
|
+ to_expression(sparse_texel_id), ")");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -13000,6 +13025,10 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
bool skip_direct_branch = false;
|
|
|
bool emitted_loop_header_variables = false;
|
|
|
bool force_complex_continue_block = false;
|
|
|
+ ValueSaver<uint32_t> loop_level_saver(current_loop_level);
|
|
|
+
|
|
|
+ if (block.merge == SPIRBlock::MergeLoop)
|
|
|
+ add_loop_level();
|
|
|
|
|
|
emit_hoisted_temporaries(block.declare_temporary);
|
|
|
|
|
|
@@ -13272,6 +13301,8 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
// Order does not matter.
|
|
|
if (!injected_block)
|
|
|
block_declaration_order.push_back(block.default_block);
|
|
|
+ else if (is_legacy_es())
|
|
|
+ SPIRV_CROSS_THROW("Default case label fallthrough to other case label is not supported in ESSL 1.0.");
|
|
|
|
|
|
case_constructs[block.default_block] = {};
|
|
|
}
|
|
|
@@ -13282,12 +13313,26 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
return is_unsigned_case ? convert_to_string(literal) : convert_to_string(int32_t(literal));
|
|
|
};
|
|
|
|
|
|
+ const auto to_legacy_case_label = [&](uint32_t condition, const SmallVector<uint32_t> &labels,
|
|
|
+ const char *suffix) -> string {
|
|
|
+ string ret;
|
|
|
+ size_t count = labels.size();
|
|
|
+ for (size_t i = 0; i < count; i++)
|
|
|
+ {
|
|
|
+ if (i)
|
|
|
+ ret += " || ";
|
|
|
+ ret += join(count > 1 ? "(" : "", to_enclosed_expression(condition), " == ", labels[i], suffix,
|
|
|
+ count > 1 ? ")" : "");
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+ };
|
|
|
+
|
|
|
// We need to deal with a complex scenario for OpPhi. If we have case-fallthrough and Phi in the picture,
|
|
|
// we need to flush phi nodes outside the switch block in a branch,
|
|
|
// and skip any Phi handling inside the case label to make fall-through work as expected.
|
|
|
// This kind of code-gen is super awkward and it's a last resort. Normally we would want to handle this
|
|
|
// inside the case label if at all possible.
|
|
|
- for (size_t i = 1; i < num_blocks; i++)
|
|
|
+ for (size_t i = 1; backend.support_case_fallthrough && i < num_blocks; i++)
|
|
|
{
|
|
|
if (flush_phi_required(block.self, block_declaration_order[i]) &&
|
|
|
flush_phi_required(block_declaration_order[i - 1], block_declaration_order[i]))
|
|
|
@@ -13341,8 +13386,18 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
// This is buggy on FXC, so just emit the logical equivalent of a do { } while(false), which is more idiomatic.
|
|
|
bool degenerate_switch = block.default_block != block.merge_block && block.cases.empty();
|
|
|
|
|
|
- if (degenerate_switch)
|
|
|
- statement("do");
|
|
|
+ if (degenerate_switch || is_legacy_es())
|
|
|
+ {
|
|
|
+ // ESSL 1.0 is not guaranteed to support do/while.
|
|
|
+ if (is_legacy_es())
|
|
|
+ {
|
|
|
+ uint32_t counter = statement_count;
|
|
|
+ statement("for (int SPIRV_Cross_Dummy", counter, " = 0; SPIRV_Cross_Dummy", counter,
|
|
|
+ " < 1; SPIRV_Cross_Dummy", counter, "++)");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ statement("do");
|
|
|
+ }
|
|
|
else
|
|
|
{
|
|
|
emit_block_hints(block);
|
|
|
@@ -13359,14 +13414,27 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
{
|
|
|
// Default case.
|
|
|
if (!degenerate_switch)
|
|
|
- statement("default:");
|
|
|
+ {
|
|
|
+ if (is_legacy_es())
|
|
|
+ statement("else");
|
|
|
+ else
|
|
|
+ statement("default:");
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- for (auto &case_literal : literals)
|
|
|
+ if (is_legacy_es())
|
|
|
{
|
|
|
- // The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here.
|
|
|
- statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":");
|
|
|
+ statement((i ? "else " : ""), "if (", to_legacy_case_label(block.condition, literals, label_suffix),
|
|
|
+ ")");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ for (auto &case_literal : literals)
|
|
|
+ {
|
|
|
+ // The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here.
|
|
|
+ statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -13401,7 +13469,12 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":");
|
|
|
|
|
|
if (block.default_block == block.next_block)
|
|
|
- statement("default:");
|
|
|
+ {
|
|
|
+ if (is_legacy_es())
|
|
|
+ statement("else");
|
|
|
+ else
|
|
|
+ statement("default:");
|
|
|
+ }
|
|
|
|
|
|
begin_scope();
|
|
|
flush_phi(block.self, block.next_block);
|
|
|
@@ -13410,7 +13483,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (degenerate_switch)
|
|
|
+ if (degenerate_switch && !is_legacy_es())
|
|
|
end_scope_decl("while(false)");
|
|
|
else
|
|
|
end_scope();
|
|
|
@@ -13492,7 +13565,11 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
// If we hit this case, we're dealing with an unconditional branch, which means we will output
|
|
|
// that block after this. If we had selection merge, we already flushed phi variables.
|
|
|
if (block.merge != SPIRBlock::MergeSelection)
|
|
|
+ {
|
|
|
flush_phi(block.self, block.next_block);
|
|
|
+ // For a direct branch, need to remember to invalidate expressions in the next linear block instead.
|
|
|
+ get<SPIRBlock>(block.next_block).invalidate_expressions = block.invalidate_expressions;
|
|
|
+ }
|
|
|
|
|
|
// For switch fallthrough cases, we terminate the chain here, but we still need to handle Phi.
|
|
|
if (!current_emitting_switch_fallthrough)
|
|
|
@@ -13546,6 +13623,8 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
else
|
|
|
end_scope();
|
|
|
|
|
|
+ loop_level_saver.release();
|
|
|
+
|
|
|
// We cannot break out of two loops at once, so don't check for break; here.
|
|
|
// Using block.self as the "from" block isn't quite right, but it has the same scope
|
|
|
// and dominance structure, so it's fine.
|