|
|
@@ -559,18 +559,19 @@ string CompilerGLSL::compile()
|
|
|
{
|
|
|
ir.fixup_reserved_names();
|
|
|
|
|
|
- if (options.vulkan_semantics)
|
|
|
- backend.allow_precision_qualifiers = true;
|
|
|
- else
|
|
|
+ if (!options.vulkan_semantics)
|
|
|
{
|
|
|
// only NV_gpu_shader5 supports divergent indexing on OpenGL, and it does so without extra qualifiers
|
|
|
backend.nonuniform_qualifier = "";
|
|
|
backend.needs_row_major_load_workaround = true;
|
|
|
}
|
|
|
+ backend.allow_precision_qualifiers = options.vulkan_semantics || options.es;
|
|
|
backend.force_gl_in_out_block = true;
|
|
|
backend.supports_extensions = true;
|
|
|
backend.use_array_constructor = true;
|
|
|
|
|
|
+ backend.support_precise_qualifier = (!options.es && options.version >= 400) || (options.es && options.version >= 320);
|
|
|
+
|
|
|
if (is_legacy_es())
|
|
|
backend.support_case_fallthrough = false;
|
|
|
|
|
|
@@ -764,6 +765,8 @@ void CompilerGLSL::emit_header()
|
|
|
{
|
|
|
statement("#if defined(GL_AMD_gpu_shader_int16)");
|
|
|
statement("#extension GL_AMD_gpu_shader_int16 : require");
|
|
|
+ statement("#elif defined(GL_NV_gpu_shader5)");
|
|
|
+ statement("#extension GL_NV_gpu_shader5 : require");
|
|
|
statement("#else");
|
|
|
statement("#error No extension available for Int16.");
|
|
|
statement("#endif");
|
|
|
@@ -4395,6 +4398,16 @@ string CompilerGLSL::to_composite_constructor_expression(uint32_t id, bool uses_
|
|
|
return to_unpacked_expression(id);
|
|
|
}
|
|
|
|
|
|
+string CompilerGLSL::to_non_uniform_aware_expression(uint32_t id)
|
|
|
+{
|
|
|
+ string expr = to_expression(id);
|
|
|
+
|
|
|
+ if (has_decoration(id, DecorationNonUniform))
|
|
|
+ convert_non_uniform_expression(expr, id);
|
|
|
+
|
|
|
+ return expr;
|
|
|
+}
|
|
|
+
|
|
|
string CompilerGLSL::to_expression(uint32_t id, bool register_expression_read)
|
|
|
{
|
|
|
auto itr = invalid_expressions.find(id);
|
|
|
@@ -5533,7 +5546,12 @@ void CompilerGLSL::emit_unary_op(uint32_t result_type, uint32_t result_id, uint3
|
|
|
|
|
|
void CompilerGLSL::emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op)
|
|
|
{
|
|
|
- bool forward = should_forward(op0) && should_forward(op1);
|
|
|
+ // Various FP arithmetic opcodes such as add, sub, mul will hit this.
|
|
|
+ bool force_temporary_precise = backend.support_precise_qualifier &&
|
|
|
+ has_decoration(result_id, DecorationNoContraction) &&
|
|
|
+ type_is_floating_point(get<SPIRType>(result_type));
|
|
|
+ bool forward = should_forward(op0) && should_forward(op1) && !force_temporary_precise;
|
|
|
+
|
|
|
emit_op(result_type, result_id,
|
|
|
join(to_enclosed_unpacked_expression(op0), " ", op, " ", to_enclosed_unpacked_expression(op1)), forward);
|
|
|
|
|
|
@@ -5712,6 +5730,27 @@ void CompilerGLSL::emit_binary_func_op(uint32_t result_type, uint32_t result_id,
|
|
|
inherit_expression_dependencies(result_id, op1);
|
|
|
}
|
|
|
|
|
|
+void CompilerGLSL::emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
|
|
|
+ const char *op)
|
|
|
+{
|
|
|
+ forced_temporaries.insert(result_id);
|
|
|
+ emit_op(result_type, result_id,
|
|
|
+ join(op, "(", to_non_uniform_aware_expression(op0), ", ",
|
|
|
+ to_unpacked_expression(op1), ")"), false);
|
|
|
+ flush_all_atomic_capable_variables();
|
|
|
+}
|
|
|
+
|
|
|
+void CompilerGLSL::emit_atomic_func_op(uint32_t result_type, uint32_t result_id,
|
|
|
+ uint32_t op0, uint32_t op1, uint32_t op2,
|
|
|
+ const char *op)
|
|
|
+{
|
|
|
+ forced_temporaries.insert(result_id);
|
|
|
+ emit_op(result_type, result_id,
|
|
|
+ join(op, "(", to_non_uniform_aware_expression(op0), ", ",
|
|
|
+ to_unpacked_expression(op1), ", ", to_unpacked_expression(op2), ")"), false);
|
|
|
+ flush_all_atomic_capable_variables();
|
|
|
+}
|
|
|
+
|
|
|
void CompilerGLSL::emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op,
|
|
|
SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type)
|
|
|
{
|
|
|
@@ -6214,7 +6253,7 @@ string CompilerGLSL::to_combined_image_sampler(VariableID image_id, VariableID s
|
|
|
{
|
|
|
// Keep track of the array indices we have used to load the image.
|
|
|
// We'll need to use the same array index into the combined image sampler array.
|
|
|
- auto image_expr = to_expression(image_id);
|
|
|
+ auto image_expr = to_non_uniform_aware_expression(image_id);
|
|
|
string array_expr;
|
|
|
auto array_index = image_expr.find_first_of('[');
|
|
|
if (array_index != string::npos)
|
|
|
@@ -6442,20 +6481,8 @@ std::string CompilerGLSL::to_texture_op(const Instruction &i, bool sparse, bool
|
|
|
auto &result_type = get<SPIRType>(result_type_id);
|
|
|
|
|
|
inherited_expressions.push_back(coord);
|
|
|
-
|
|
|
- // Make sure non-uniform decoration is back-propagated to where it needs to be.
|
|
|
- if (has_decoration(img, DecorationNonUniformEXT))
|
|
|
- {
|
|
|
- // In Vulkan GLSL, we cannot back-propgate nonuniform qualifiers if we
|
|
|
- // use a combined image sampler constructor.
|
|
|
- // We're only interested in back-propagating if we can trace back through access chains.
|
|
|
- // If not, we will apply nonuniform to the sampled image expression itself.
|
|
|
- auto *backing = maybe_get_backing_variable(img);
|
|
|
- if (backing)
|
|
|
- propagate_nonuniform_qualifier(img);
|
|
|
- else
|
|
|
- nonuniform_expression = true;
|
|
|
- }
|
|
|
+ if (has_decoration(img, DecorationNonUniform) && !maybe_get_backing_variable(img))
|
|
|
+ nonuniform_expression = true;
|
|
|
|
|
|
switch (op)
|
|
|
{
|
|
|
@@ -6794,7 +6821,7 @@ std::string CompilerGLSL::convert_separate_image_to_expression(uint32_t id)
|
|
|
// Don't need to consider Shadow state since the dummy sampler is always non-shadow.
|
|
|
auto sampled_type = type;
|
|
|
sampled_type.basetype = SPIRType::SampledImage;
|
|
|
- return join(type_to_glsl(sampled_type), "(", to_expression(id), ", ",
|
|
|
+ return join(type_to_glsl(sampled_type), "(", to_non_uniform_aware_expression(id), ", ",
|
|
|
to_expression(dummy_sampler_id), ")");
|
|
|
}
|
|
|
else
|
|
|
@@ -6814,7 +6841,7 @@ std::string CompilerGLSL::convert_separate_image_to_expression(uint32_t id)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return to_expression(id);
|
|
|
+ return to_non_uniform_aware_expression(id);
|
|
|
}
|
|
|
|
|
|
// Returns the function args for a texture sampling function for the specified image and sampling characteristics.
|
|
|
@@ -6827,7 +6854,7 @@ string CompilerGLSL::to_function_args(const TextureFunctionArguments &args, bool
|
|
|
if (args.base.is_fetch)
|
|
|
farg_str = convert_separate_image_to_expression(img);
|
|
|
else
|
|
|
- farg_str = to_expression(img);
|
|
|
+ farg_str = to_non_uniform_aware_expression(img);
|
|
|
|
|
|
if (args.nonuniform_expression && farg_str.find_first_of('[') != string::npos)
|
|
|
{
|
|
|
@@ -8317,12 +8344,35 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
|
|
|
SPIRV_CROSS_THROW("Stencil export not supported in GLES.");
|
|
|
}
|
|
|
|
|
|
+ case BuiltInPrimitiveShadingRateKHR:
|
|
|
+ {
|
|
|
+ if (!options.vulkan_semantics)
|
|
|
+ SPIRV_CROSS_THROW("Can only use PrimitiveShadingRateKHR in Vulkan GLSL.");
|
|
|
+ require_extension_internal("GL_EXT_fragment_shading_rate");
|
|
|
+ return "gl_PrimitiveShadingRateEXT";
|
|
|
+ }
|
|
|
+
|
|
|
+ case BuiltInShadingRateKHR:
|
|
|
+ {
|
|
|
+ if (!options.vulkan_semantics)
|
|
|
+ SPIRV_CROSS_THROW("Can only use ShadingRateKHR in Vulkan GLSL.");
|
|
|
+ require_extension_internal("GL_EXT_fragment_shading_rate");
|
|
|
+ return "gl_ShadingRateEXT";
|
|
|
+ }
|
|
|
+
|
|
|
case BuiltInDeviceIndex:
|
|
|
if (!options.vulkan_semantics)
|
|
|
SPIRV_CROSS_THROW("Need Vulkan semantics for device group support.");
|
|
|
require_extension_internal("GL_EXT_device_group");
|
|
|
return "gl_DeviceIndex";
|
|
|
|
|
|
+ case BuiltInFullyCoveredEXT:
|
|
|
+ if (!options.es)
|
|
|
+ require_extension_internal("GL_NV_conservative_raster_underestimation");
|
|
|
+ else
|
|
|
+ SPIRV_CROSS_THROW("Need desktop GL to use GL_NV_conservative_raster_underestimation.");
|
|
|
+ return "gl_FragFullyCoveredNV";
|
|
|
+
|
|
|
default:
|
|
|
return join("gl_BuiltIn_", convert_to_string(builtin));
|
|
|
}
|
|
|
@@ -8345,7 +8395,7 @@ const char *CompilerGLSL::index_to_swizzle(uint32_t index)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void CompilerGLSL::access_chain_internal_append_index(std::string &expr, uint32_t /*base*/, const SPIRType *type,
|
|
|
+void CompilerGLSL::access_chain_internal_append_index(std::string &expr, uint32_t /*base*/, const SPIRType * /*type*/,
|
|
|
AccessChainFlags flags, bool & /*access_chain_is_arrayed*/,
|
|
|
uint32_t index)
|
|
|
{
|
|
|
@@ -8354,27 +8404,19 @@ void CompilerGLSL::access_chain_internal_append_index(std::string &expr, uint32_
|
|
|
|
|
|
expr += "[";
|
|
|
|
|
|
- // If we are indexing into an array of SSBOs or UBOs, we need to index it with a non-uniform qualifier.
|
|
|
- bool nonuniform_index =
|
|
|
- has_decoration(index, DecorationNonUniformEXT) &&
|
|
|
- (has_decoration(type->self, DecorationBlock) || has_decoration(type->self, DecorationBufferBlock));
|
|
|
- if (nonuniform_index)
|
|
|
- {
|
|
|
- expr += backend.nonuniform_qualifier;
|
|
|
- expr += "(";
|
|
|
- }
|
|
|
-
|
|
|
if (index_is_literal)
|
|
|
expr += convert_to_string(index);
|
|
|
else
|
|
|
expr += to_expression(index, register_expression_read);
|
|
|
|
|
|
- if (nonuniform_index)
|
|
|
- expr += ")";
|
|
|
-
|
|
|
expr += "]";
|
|
|
}
|
|
|
|
|
|
+bool CompilerGLSL::access_chain_needs_stage_io_builtin_translation(uint32_t)
|
|
|
+{
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count,
|
|
|
AccessChainFlags flags, AccessChainMeta *meta)
|
|
|
{
|
|
|
@@ -8584,7 +8626,7 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
|
|
|
SPIRV_CROSS_THROW("Member index is out of bounds!");
|
|
|
|
|
|
BuiltIn builtin;
|
|
|
- if (is_member_builtin(*type, index, &builtin))
|
|
|
+ if (is_member_builtin(*type, index, &builtin) && access_chain_needs_stage_io_builtin_translation(base))
|
|
|
{
|
|
|
if (access_chain_is_arrayed)
|
|
|
{
|
|
|
@@ -8652,13 +8694,30 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
|
|
|
|
|
|
// Internally, access chain implementation can also be used on composites,
|
|
|
// ignore scalar access workarounds in this case.
|
|
|
- StorageClass effective_storage;
|
|
|
- if (expression_type(base).pointer)
|
|
|
- effective_storage = get_expression_effective_storage_class(base);
|
|
|
+ StorageClass effective_storage = StorageClassGeneric;
|
|
|
+ bool ignore_potential_sliced_writes = false;
|
|
|
+ if ((flags & ACCESS_CHAIN_FORCE_COMPOSITE_BIT) == 0)
|
|
|
+ {
|
|
|
+ if (expression_type(base).pointer)
|
|
|
+ effective_storage = get_expression_effective_storage_class(base);
|
|
|
+
|
|
|
+ // Special consideration for control points.
|
|
|
+ // Control points can only be written by InvocationID, so there is no need
|
|
|
+ // to consider scalar access chains here.
|
|
|
+ // Cleans up some cases where it's very painful to determine the accurate storage class
|
|
|
+ // since blocks can be partially masked ...
|
|
|
+ auto *var = maybe_get_backing_variable(base);
|
|
|
+ if (var && var->storage == StorageClassOutput &&
|
|
|
+ get_execution_model() == ExecutionModelTessellationControl &&
|
|
|
+ !has_decoration(var->self, DecorationPatch))
|
|
|
+ {
|
|
|
+ ignore_potential_sliced_writes = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
else
|
|
|
- effective_storage = StorageClassGeneric;
|
|
|
+ ignore_potential_sliced_writes = true;
|
|
|
|
|
|
- if (!row_major_matrix_needs_conversion)
|
|
|
+ if (!row_major_matrix_needs_conversion && !ignore_potential_sliced_writes)
|
|
|
{
|
|
|
// On some backends, we might not be able to safely access individual scalars in a vector.
|
|
|
// To work around this, we might have to cast the access chain reference to something which can,
|
|
|
@@ -8698,7 +8757,7 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
|
|
|
expr += "]";
|
|
|
}
|
|
|
|
|
|
- if (row_major_matrix_needs_conversion)
|
|
|
+ if (row_major_matrix_needs_conversion && !ignore_potential_sliced_writes)
|
|
|
{
|
|
|
prepare_access_chain_for_scalar_access(expr, get<SPIRType>(type->parent_type), effective_storage,
|
|
|
is_packed);
|
|
|
@@ -9682,6 +9741,8 @@ void CompilerGLSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_ex
|
|
|
if (!unroll_array_to_complex_store(lhs_expression, rhs_expression))
|
|
|
{
|
|
|
auto lhs = to_dereferenced_expression(lhs_expression);
|
|
|
+ if (has_decoration(lhs_expression, DecorationNonUniform))
|
|
|
+ convert_non_uniform_expression(lhs, lhs_expression);
|
|
|
|
|
|
// We might need to cast in order to store to a builtin.
|
|
|
cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression));
|
|
|
@@ -9857,12 +9918,10 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
// Also, loading from gl_SampleMask array needs special unroll.
|
|
|
unroll_array_from_complex_load(id, ptr, expr);
|
|
|
|
|
|
- // Shouldn't need to check for ID, but current glslang codegen requires it in some cases
|
|
|
- // when loading Image/Sampler descriptors. It does not hurt to check ID as well.
|
|
|
- if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ptr, DecorationNonUniformEXT))
|
|
|
+ if (!type_is_opaque_value(type) && has_decoration(ptr, DecorationNonUniform))
|
|
|
{
|
|
|
- propagate_nonuniform_qualifier(ptr);
|
|
|
- convert_non_uniform_expression(type, expr);
|
|
|
+ // If we're loading something non-opaque, we need to handle non-uniform descriptor access.
|
|
|
+ convert_non_uniform_expression(expr, ptr);
|
|
|
}
|
|
|
|
|
|
if (forward && ptr_expression)
|
|
|
@@ -9885,7 +9944,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
// it is an array, and our backend does not support arrays as value types.
|
|
|
// Emit the temporary, and copy it explicitly.
|
|
|
e = &emit_uninitialized_temporary_expression(result_type, id);
|
|
|
- emit_array_copy(to_expression(id), ptr, StorageClassFunction, get_expression_effective_storage_class(ptr));
|
|
|
+ emit_array_copy(to_expression(id), id, ptr, StorageClassFunction, get_expression_effective_storage_class(ptr));
|
|
|
}
|
|
|
else
|
|
|
e = &emit_op(result_type, id, expr, forward, !usage_tracking);
|
|
|
@@ -9966,9 +10025,6 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
if (expr.expression_dependencies.empty())
|
|
|
forwarded_temporaries.erase(ops[1]);
|
|
|
|
|
|
- if (has_decoration(ops[1], DecorationNonUniformEXT))
|
|
|
- propagate_nonuniform_qualifier(ops[1]);
|
|
|
-
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
@@ -10006,6 +10062,8 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
uint32_t result_type = ops[0];
|
|
|
uint32_t id = ops[1];
|
|
|
auto e = access_chain_internal(ops[2], &ops[3], length - 3, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, nullptr);
|
|
|
+ if (has_decoration(ops[2], DecorationNonUniform))
|
|
|
+ convert_non_uniform_expression(e, ops[2]);
|
|
|
set<SPIRExpression>(id, join(type_to_glsl(get<SPIRType>(result_type)), "(", e, ".length())"), result_type,
|
|
|
true);
|
|
|
break;
|
|
|
@@ -10307,14 +10365,16 @@ 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_internal(ops[2], &ops[3], length,
|
|
|
- ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_CHAIN_ONLY_BIT, &meta);
|
|
|
+ ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_CHAIN_ONLY_BIT |
|
|
|
+ ACCESS_CHAIN_FORCE_COMPOSITE_BIT, &meta);
|
|
|
e = &emit_op(result_type, id, expr, true, should_suppress_usage_tracking(ops[2]));
|
|
|
inherit_expression_dependencies(id, ops[2]);
|
|
|
e->base_expression = ops[2];
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- auto expr = access_chain_internal(ops[2], &ops[3], length, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, &meta);
|
|
|
+ auto expr = access_chain_internal(ops[2], &ops[3], length,
|
|
|
+ ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_FORCE_COMPOSITE_BIT, &meta);
|
|
|
e = &emit_op(result_type, id, expr, should_forward(ops[2]), should_suppress_usage_tracking(ops[2]));
|
|
|
inherit_expression_dependencies(id, ops[2]);
|
|
|
}
|
|
|
@@ -11221,9 +11281,8 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
// Ignore semantics for now, probably only relevant to CL.
|
|
|
uint32_t val = ops[5];
|
|
|
const char *op = check_atomic_image(ptr) ? "imageAtomicExchange" : "atomicExchange";
|
|
|
- forced_temporaries.insert(id);
|
|
|
- emit_binary_func_op(result_type, id, ptr, val, op);
|
|
|
- flush_all_atomic_capable_variables();
|
|
|
+
|
|
|
+ emit_atomic_func_op(result_type, id, ptr, val, op);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
@@ -11236,9 +11295,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
uint32_t comp = ops[7];
|
|
|
const char *op = check_atomic_image(ptr) ? "imageAtomicCompSwap" : "atomicCompSwap";
|
|
|
|
|
|
- forced_temporaries.insert(id);
|
|
|
- emit_trinary_func_op(result_type, id, ptr, comp, val, op);
|
|
|
- flush_all_atomic_capable_variables();
|
|
|
+ emit_atomic_func_op(result_type, id, ptr, comp, val, op);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
@@ -11253,7 +11310,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
(atomic_image && get<SPIRType>(type.image.type).basetype == SPIRType::UInt);
|
|
|
const char *op = atomic_image ? "imageAtomicAdd" : "atomicAdd";
|
|
|
const char *increment = unsigned_type ? "0u" : "0";
|
|
|
- emit_op(ops[0], ops[1], join(op, "(", to_expression(ops[2]), ", ", increment, ")"), false);
|
|
|
+ emit_op(ops[0], ops[1],
|
|
|
+ join(op, "(",
|
|
|
+ to_non_uniform_aware_expression(ops[2]), ", ", increment, ")"), false);
|
|
|
flush_all_atomic_capable_variables();
|
|
|
break;
|
|
|
}
|
|
|
@@ -11266,7 +11325,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
// Ignore semantics for now, probably only relevant to CL.
|
|
|
uint32_t val = ops[3];
|
|
|
const char *op = check_atomic_image(ptr) ? "imageAtomicExchange" : "atomicExchange";
|
|
|
- statement(op, "(", to_expression(ptr), ", ", to_expression(val), ");");
|
|
|
+ statement(op, "(", to_non_uniform_aware_expression(ptr), ", ", to_expression(val), ");");
|
|
|
flush_all_atomic_capable_variables();
|
|
|
break;
|
|
|
}
|
|
|
@@ -11301,7 +11360,8 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
else
|
|
|
increment = "-1";
|
|
|
|
|
|
- emit_op(ops[0], ops[1], join(op, "(", to_expression(ops[2]), ", ", increment, ")"), false);
|
|
|
+ emit_op(ops[0], ops[1],
|
|
|
+ join(op, "(", to_non_uniform_aware_expression(ops[2]), ", ", increment, ")"), false);
|
|
|
}
|
|
|
|
|
|
flush_all_atomic_capable_variables();
|
|
|
@@ -11311,9 +11371,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
case OpAtomicIAdd:
|
|
|
{
|
|
|
const char *op = check_atomic_image(ops[2]) ? "imageAtomicAdd" : "atomicAdd";
|
|
|
- forced_temporaries.insert(ops[1]);
|
|
|
- emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
- flush_all_atomic_capable_variables();
|
|
|
+ emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
@@ -11321,7 +11379,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
{
|
|
|
const char *op = check_atomic_image(ops[2]) ? "imageAtomicAdd" : "atomicAdd";
|
|
|
forced_temporaries.insert(ops[1]);
|
|
|
- auto expr = join(op, "(", to_expression(ops[2]), ", -", to_enclosed_expression(ops[5]), ")");
|
|
|
+ auto expr = join(op, "(", to_non_uniform_aware_expression(ops[2]), ", -", to_enclosed_expression(ops[5]), ")");
|
|
|
emit_op(ops[0], ops[1], expr, should_forward(ops[2]) && should_forward(ops[5]));
|
|
|
flush_all_atomic_capable_variables();
|
|
|
break;
|
|
|
@@ -11331,9 +11389,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
case OpAtomicUMin:
|
|
|
{
|
|
|
const char *op = check_atomic_image(ops[2]) ? "imageAtomicMin" : "atomicMin";
|
|
|
- forced_temporaries.insert(ops[1]);
|
|
|
- emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
- flush_all_atomic_capable_variables();
|
|
|
+ emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
@@ -11341,36 +11397,28 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
case OpAtomicUMax:
|
|
|
{
|
|
|
const char *op = check_atomic_image(ops[2]) ? "imageAtomicMax" : "atomicMax";
|
|
|
- forced_temporaries.insert(ops[1]);
|
|
|
- emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
- flush_all_atomic_capable_variables();
|
|
|
+ emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case OpAtomicAnd:
|
|
|
{
|
|
|
const char *op = check_atomic_image(ops[2]) ? "imageAtomicAnd" : "atomicAnd";
|
|
|
- forced_temporaries.insert(ops[1]);
|
|
|
- emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
- flush_all_atomic_capable_variables();
|
|
|
+ emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case OpAtomicOr:
|
|
|
{
|
|
|
const char *op = check_atomic_image(ops[2]) ? "imageAtomicOr" : "atomicOr";
|
|
|
- forced_temporaries.insert(ops[1]);
|
|
|
- emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
- flush_all_atomic_capable_variables();
|
|
|
+ emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case OpAtomicXor:
|
|
|
{
|
|
|
const char *op = check_atomic_image(ops[2]) ? "imageAtomicXor" : "atomicXor";
|
|
|
- forced_temporaries.insert(ops[1]);
|
|
|
- emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
- flush_all_atomic_capable_variables();
|
|
|
+ emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
@@ -11465,16 +11513,33 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
|
|
|
case OpImageQueryLod:
|
|
|
{
|
|
|
+ const char *op = nullptr;
|
|
|
if (!options.es && options.version < 400)
|
|
|
{
|
|
|
require_extension_internal("GL_ARB_texture_query_lod");
|
|
|
// For some reason, the ARB spec is all-caps.
|
|
|
- GLSL_BFOP(textureQueryLOD);
|
|
|
+ op = "textureQueryLOD";
|
|
|
}
|
|
|
else if (options.es)
|
|
|
SPIRV_CROSS_THROW("textureQueryLod not supported in ES profile.");
|
|
|
else
|
|
|
- GLSL_BFOP(textureQueryLod);
|
|
|
+ op = "textureQueryLod";
|
|
|
+
|
|
|
+ auto sampler_expr = to_expression(ops[2]);
|
|
|
+ if (has_decoration(ops[2], DecorationNonUniform))
|
|
|
+ {
|
|
|
+ if (maybe_get_backing_variable(ops[2]))
|
|
|
+ convert_non_uniform_expression(sampler_expr, ops[2]);
|
|
|
+ else if (*backend.nonuniform_qualifier != '\0')
|
|
|
+ sampler_expr = join(backend.nonuniform_qualifier, "(", sampler_expr, ")");
|
|
|
+ }
|
|
|
+
|
|
|
+ bool forward = should_forward(ops[3]);
|
|
|
+ emit_op(ops[0], ops[1],
|
|
|
+ join(op, "(", sampler_expr, ", ", to_unpacked_expression(ops[3]), ")"),
|
|
|
+ forward);
|
|
|
+ inherit_expression_dependencies(ops[1], ops[2]);
|
|
|
+ inherit_expression_dependencies(ops[1], ops[3]);
|
|
|
register_control_dependent_expression(ops[1]);
|
|
|
break;
|
|
|
}
|
|
|
@@ -11504,7 +11569,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
|
|
|
string expr;
|
|
|
if (type.image.sampled == 2)
|
|
|
- expr = join("imageSamples(", to_expression(ops[2]), ")");
|
|
|
+ expr = join("imageSamples(", to_non_uniform_aware_expression(ops[2]), ")");
|
|
|
else
|
|
|
expr = join("textureSamples(", convert_separate_image_to_expression(ops[2]), ")");
|
|
|
|
|
|
@@ -11615,10 +11680,10 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
"operand mask was used.");
|
|
|
|
|
|
uint32_t samples = ops[5];
|
|
|
- imgexpr = join("subpassLoad(", to_expression(ops[2]), ", ", to_expression(samples), ")");
|
|
|
+ imgexpr = join("subpassLoad(", to_non_uniform_aware_expression(ops[2]), ", ", to_expression(samples), ")");
|
|
|
}
|
|
|
else
|
|
|
- imgexpr = join("subpassLoad(", to_expression(ops[2]), ")");
|
|
|
+ imgexpr = join("subpassLoad(", to_non_uniform_aware_expression(ops[2]), ")");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -11630,13 +11695,13 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
"operand mask was used.");
|
|
|
|
|
|
uint32_t samples = ops[5];
|
|
|
- imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), ",
|
|
|
+ imgexpr = join("texelFetch(", to_non_uniform_aware_expression(ops[2]), ", ivec2(gl_FragCoord.xy), ",
|
|
|
to_expression(samples), ")");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
// Implement subpass loads via texture barrier style sampling.
|
|
|
- imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)");
|
|
|
+ imgexpr = join("texelFetch(", to_non_uniform_aware_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)");
|
|
|
}
|
|
|
}
|
|
|
imgexpr = remap_swizzle(get<SPIRType>(result_type), 4, imgexpr);
|
|
|
@@ -11667,12 +11732,12 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
"operand mask was used.");
|
|
|
|
|
|
uint32_t samples = ops[5];
|
|
|
- statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_expression(ops[2]), ", ",
|
|
|
+ statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_non_uniform_aware_expression(ops[2]), ", ",
|
|
|
coord_expr, ", ", to_expression(samples), ", ", to_expression(sparse_texel_id), ");");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_expression(ops[2]), ", ",
|
|
|
+ statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_non_uniform_aware_expression(ops[2]), ", ",
|
|
|
coord_expr, ", ", to_expression(sparse_texel_id), ");");
|
|
|
}
|
|
|
imgexpr = join(type_to_glsl(get<SPIRType>(result_type)), "(", to_expression(sparse_code_id), ", ",
|
|
|
@@ -11689,10 +11754,10 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
|
|
|
uint32_t samples = ops[5];
|
|
|
imgexpr =
|
|
|
- join("imageLoad(", to_expression(ops[2]), ", ", coord_expr, ", ", to_expression(samples), ")");
|
|
|
+ join("imageLoad(", to_non_uniform_aware_expression(ops[2]), ", ", coord_expr, ", ", to_expression(samples), ")");
|
|
|
}
|
|
|
else
|
|
|
- imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", coord_expr, ")");
|
|
|
+ imgexpr = join("imageLoad(", to_non_uniform_aware_expression(ops[2]), ", ", coord_expr, ")");
|
|
|
}
|
|
|
|
|
|
if (!sparse)
|
|
|
@@ -11733,9 +11798,6 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
coord_expr = bitcast_expression(target_coord_type, expression_type(ops[3]).basetype, coord_expr);
|
|
|
|
|
|
auto expr = join(to_expression(ops[2]), ", ", coord_expr);
|
|
|
- if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ops[2], DecorationNonUniformEXT))
|
|
|
- convert_non_uniform_expression(expression_type(ops[2]), expr);
|
|
|
-
|
|
|
auto &e = set<SPIRExpression>(id, expr, result_type, true);
|
|
|
|
|
|
// When using the pointer, we need to know which variable it is actually loaded from.
|
|
|
@@ -11778,11 +11840,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
if (operands != ImageOperandsSampleMask || length != 5)
|
|
|
SPIRV_CROSS_THROW("Multisampled image used in OpImageWrite, but unexpected operand mask was used.");
|
|
|
uint32_t samples = ops[4];
|
|
|
- statement("imageStore(", to_expression(ops[0]), ", ", coord_expr, ", ", to_expression(samples), ", ",
|
|
|
+ statement("imageStore(", to_non_uniform_aware_expression(ops[0]), ", ", coord_expr, ", ", to_expression(samples), ", ",
|
|
|
remap_swizzle(store_type, value_type.vecsize, to_expression(ops[2])), ");");
|
|
|
}
|
|
|
else
|
|
|
- statement("imageStore(", to_expression(ops[0]), ", ", coord_expr, ", ",
|
|
|
+ statement("imageStore(", to_non_uniform_aware_expression(ops[0]), ", ", coord_expr, ", ",
|
|
|
remap_swizzle(store_type, value_type.vecsize, to_expression(ops[2])), ");");
|
|
|
|
|
|
if (var && variable_storage_is_aliased(*var))
|
|
|
@@ -11807,7 +11869,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
SPIRV_CROSS_THROW("At least ESSL 3.10 required for imageSize.");
|
|
|
|
|
|
// The size of an image is always constant.
|
|
|
- expr = join("imageSize(", to_expression(ops[2]), ")");
|
|
|
+ expr = join("imageSize(", to_non_uniform_aware_expression(ops[2]), ")");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -12332,9 +12394,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
flush_control_dependent_expressions(current_emitting_block->self);
|
|
|
break;
|
|
|
case OpTraceNV:
|
|
|
- if (has_decoration(ops[0], DecorationNonUniformEXT))
|
|
|
- propagate_nonuniform_qualifier(ops[0]);
|
|
|
- statement("traceNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ", ",
|
|
|
+ statement("traceNV(", to_non_uniform_aware_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ", ",
|
|
|
to_expression(ops[3]), ", ", to_expression(ops[4]), ", ", to_expression(ops[5]), ", ",
|
|
|
to_expression(ops[6]), ", ", to_expression(ops[7]), ", ", to_expression(ops[8]), ", ",
|
|
|
to_expression(ops[9]), ", ", to_expression(ops[10]), ");");
|
|
|
@@ -12343,9 +12403,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
|
|
case OpTraceRayKHR:
|
|
|
if (!has_decoration(ops[10], DecorationLocation))
|
|
|
SPIRV_CROSS_THROW("A memory declaration object must be used in TraceRayKHR.");
|
|
|
- if (has_decoration(ops[0], DecorationNonUniformEXT))
|
|
|
- propagate_nonuniform_qualifier(ops[0]);
|
|
|
- statement("traceRayEXT(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ", ",
|
|
|
+ statement("traceRayEXT(", to_non_uniform_aware_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ", ",
|
|
|
to_expression(ops[3]), ", ", to_expression(ops[4]), ", ", to_expression(ops[5]), ", ",
|
|
|
to_expression(ops[6]), ", ", to_expression(ops[7]), ", ", to_expression(ops[8]), ", ",
|
|
|
to_expression(ops[9]), ", ", get_decoration(ops[10], DecorationLocation), ");");
|
|
|
@@ -12644,6 +12702,11 @@ string CompilerGLSL::variable_decl(const SPIRType &type, const string &name, uin
|
|
|
return join(type_name, " ", name, type_to_array_glsl(type));
|
|
|
}
|
|
|
|
|
|
+bool CompilerGLSL::variable_decl_is_remapped_storage(const SPIRVariable &var, StorageClass storage) const
|
|
|
+{
|
|
|
+ return var.storage == storage;
|
|
|
+}
|
|
|
+
|
|
|
// Emit a structure member. Subclasses may override to modify output,
|
|
|
// or to dynamically add a padding member if needed.
|
|
|
void CompilerGLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
|
|
|
@@ -12671,7 +12734,7 @@ void CompilerGLSL::emit_struct_padding_target(const SPIRType &)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
-const char *CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags)
|
|
|
+string CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags)
|
|
|
{
|
|
|
// GL_EXT_buffer_reference variables can be marked as restrict.
|
|
|
if (flags.get(DecorationRestrictPointerEXT))
|
|
|
@@ -12683,6 +12746,11 @@ const char *CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const B
|
|
|
type.basetype != SPIRType::Sampler)
|
|
|
return "";
|
|
|
|
|
|
+ string qual;
|
|
|
+
|
|
|
+ if (flags.get(DecorationNoContraction) && backend.support_precise_qualifier)
|
|
|
+ qual = "precise ";
|
|
|
+
|
|
|
if (options.es)
|
|
|
{
|
|
|
auto &execution = get_entry_point();
|
|
|
@@ -12697,7 +12765,7 @@ const char *CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const B
|
|
|
options.fragment.default_int_precision == Options::Mediump &&
|
|
|
execution.model == ExecutionModelFragment;
|
|
|
|
|
|
- return implied_fmediump || implied_imediump ? "" : "mediump ";
|
|
|
+ qual += (implied_fmediump || implied_imediump) ? "" : "mediump ";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -12711,7 +12779,7 @@ const char *CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const B
|
|
|
execution.model == ExecutionModelFragment) ||
|
|
|
(execution.model != ExecutionModelFragment));
|
|
|
|
|
|
- return implied_fhighp || implied_ihighp ? "" : "highp ";
|
|
|
+ qual += (implied_fhighp || implied_ihighp) ? "" : "highp ";
|
|
|
}
|
|
|
}
|
|
|
else if (backend.allow_precision_qualifiers)
|
|
|
@@ -12719,18 +12787,16 @@ const char *CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const B
|
|
|
// Vulkan GLSL supports precision qualifiers, even in desktop profiles, which is convenient.
|
|
|
// The default is highp however, so only emit mediump in the rare case that a shader has these.
|
|
|
if (flags.get(DecorationRelaxedPrecision))
|
|
|
- return "mediump ";
|
|
|
- else
|
|
|
- return "";
|
|
|
+ qual += "mediump ";
|
|
|
}
|
|
|
- else
|
|
|
- return "";
|
|
|
+
|
|
|
+ return qual;
|
|
|
}
|
|
|
|
|
|
-const char *CompilerGLSL::to_precision_qualifiers_glsl(uint32_t id)
|
|
|
+string CompilerGLSL::to_precision_qualifiers_glsl(uint32_t id)
|
|
|
{
|
|
|
auto &type = expression_type(id);
|
|
|
- bool use_precision_qualifiers = backend.allow_precision_qualifiers || options.es;
|
|
|
+ bool use_precision_qualifiers = backend.allow_precision_qualifiers;
|
|
|
if (use_precision_qualifiers && (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage))
|
|
|
{
|
|
|
// Force mediump for the sampler type. We cannot declare 16-bit or smaller image types.
|
|
|
@@ -12787,10 +12853,24 @@ string CompilerGLSL::to_qualifiers_glsl(uint32_t id)
|
|
|
res += "coherent ";
|
|
|
if (flags.get(DecorationRestrict))
|
|
|
res += "restrict ";
|
|
|
+
|
|
|
if (flags.get(DecorationNonWritable))
|
|
|
res += "readonly ";
|
|
|
+
|
|
|
+ bool formatted_load = type.image.format == ImageFormatUnknown;
|
|
|
if (flags.get(DecorationNonReadable))
|
|
|
+ {
|
|
|
res += "writeonly ";
|
|
|
+ formatted_load = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (formatted_load)
|
|
|
+ {
|
|
|
+ if (!options.es)
|
|
|
+ require_extension_internal("GL_EXT_shader_image_load_formatted");
|
|
|
+ else
|
|
|
+ SPIRV_CROSS_THROW("Cannot use GL_EXT_shader_image_load_formatted in ESSL.");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
res += to_precision_qualifiers_glsl(id);
|
|
|
@@ -12869,7 +12949,7 @@ string CompilerGLSL::variable_decl(const SPIRVariable &variable)
|
|
|
else if (options.force_zero_initialized_variables && type_can_zero_initialize(type))
|
|
|
res += join(" = ", to_zero_initialized_expression(get_variable_data_type_id(variable)));
|
|
|
}
|
|
|
- else if (variable.initializer)
|
|
|
+ else if (variable.initializer && !variable_decl_is_remapped_storage(variable, StorageClassWorkgroup))
|
|
|
{
|
|
|
uint32_t expr = variable.initializer;
|
|
|
if (ir.ids[expr].get_type() != TypeUndef)
|
|
|
@@ -13559,7 +13639,7 @@ void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags)
|
|
|
auto &var = get<SPIRVariable>(v);
|
|
|
var.deferred_declaration = false;
|
|
|
|
|
|
- if (var.storage == StorageClassWorkgroup)
|
|
|
+ if (variable_decl_is_remapped_storage(var, StorageClassWorkgroup))
|
|
|
{
|
|
|
// Special variable type which cannot have initializer,
|
|
|
// need to be declared as standalone variables.
|
|
|
@@ -14761,7 +14841,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
|
|
// The backend is responsible for setting this up, and redirection the return values as appropriate.
|
|
|
if (ir.ids[block.return_value].get_type() != TypeUndef)
|
|
|
{
|
|
|
- emit_array_copy("spvReturnValue", block.return_value, StorageClassFunction,
|
|
|
+ emit_array_copy("spvReturnValue", 0, block.return_value, StorageClassFunction,
|
|
|
get_expression_effective_storage_class(block.return_value));
|
|
|
}
|
|
|
|
|
|
@@ -14980,7 +15060,7 @@ uint32_t CompilerGLSL::mask_relevant_memory_semantics(uint32_t semantics)
|
|
|
MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask);
|
|
|
}
|
|
|
|
|
|
-void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageClass, StorageClass)
|
|
|
+void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t, uint32_t rhs_id, StorageClass, StorageClass)
|
|
|
{
|
|
|
statement(lhs, " = ", to_expression(rhs_id), ";");
|
|
|
}
|
|
|
@@ -15120,6 +15200,8 @@ void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
|
|
|
case BuiltInFragStencilRefEXT:
|
|
|
case BuiltInInstanceCustomIndexNV:
|
|
|
case BuiltInSampleMask:
|
|
|
+ case BuiltInPrimitiveShadingRateKHR:
|
|
|
+ case BuiltInShadingRateKHR:
|
|
|
expected_type = SPIRType::Int;
|
|
|
break;
|
|
|
|
|
|
@@ -15164,6 +15246,8 @@ void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr,
|
|
|
case BuiltInViewportIndex:
|
|
|
case BuiltInFragStencilRefEXT:
|
|
|
case BuiltInSampleMask:
|
|
|
+ case BuiltInPrimitiveShadingRateKHR:
|
|
|
+ case BuiltInShadingRateKHR:
|
|
|
expected_type = SPIRType::Int;
|
|
|
break;
|
|
|
|
|
|
@@ -15179,64 +15263,62 @@ void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void CompilerGLSL::convert_non_uniform_expression(const SPIRType &type, std::string &expr)
|
|
|
+void CompilerGLSL::convert_non_uniform_expression(string &expr, uint32_t ptr_id)
|
|
|
{
|
|
|
if (*backend.nonuniform_qualifier == '\0')
|
|
|
return;
|
|
|
|
|
|
- // Handle SPV_EXT_descriptor_indexing.
|
|
|
- if (type.basetype == SPIRType::Sampler || type.basetype == SPIRType::SampledImage ||
|
|
|
- type.basetype == SPIRType::Image || type.basetype == SPIRType::AccelerationStructure)
|
|
|
- {
|
|
|
- // The image/sampler ID must be declared as non-uniform.
|
|
|
- // However, it is not legal GLSL to have
|
|
|
- // nonuniformEXT(samplers[index]), so we must move the nonuniform qualifier
|
|
|
- // to the array indexing, like
|
|
|
- // samplers[nonuniformEXT(index)].
|
|
|
- // While the access chain will generally be nonuniformEXT, it's not necessarily so,
|
|
|
- // so we might have to fixup the OpLoad-ed expression late.
|
|
|
+ auto *var = maybe_get_backing_variable(ptr_id);
|
|
|
+ if (!var)
|
|
|
+ return;
|
|
|
|
|
|
- auto start_array_index = expr.find_first_of('[');
|
|
|
+ if (var->storage != StorageClassUniformConstant &&
|
|
|
+ var->storage != StorageClassStorageBuffer &&
|
|
|
+ var->storage != StorageClassUniform)
|
|
|
+ return;
|
|
|
|
|
|
- if (start_array_index == string::npos)
|
|
|
- return;
|
|
|
+ auto &backing_type = get<SPIRType>(var->basetype);
|
|
|
+ if (backing_type.array.empty())
|
|
|
+ return;
|
|
|
|
|
|
- // Check for the edge case that a non-arrayed resource was marked to be nonuniform,
|
|
|
- // and the bracket we found is actually part of non-resource related data.
|
|
|
- if (expr.find_first_of(',') < start_array_index)
|
|
|
- return;
|
|
|
+ // If we get here, we know we're accessing an arrayed resource which
|
|
|
+ // might require nonuniform qualifier.
|
|
|
|
|
|
- // We've opened a bracket, track expressions until we can close the bracket.
|
|
|
- // This must be our image index.
|
|
|
- size_t end_array_index = string::npos;
|
|
|
- unsigned bracket_count = 1;
|
|
|
- for (size_t index = start_array_index + 1; index < expr.size(); index++)
|
|
|
+ auto start_array_index = expr.find_first_of('[');
|
|
|
+
|
|
|
+ if (start_array_index == string::npos)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // We've opened a bracket, track expressions until we can close the bracket.
|
|
|
+ // This must be our resource index.
|
|
|
+ size_t end_array_index = string::npos;
|
|
|
+ unsigned bracket_count = 1;
|
|
|
+ for (size_t index = start_array_index + 1; index < expr.size(); index++)
|
|
|
+ {
|
|
|
+ if (expr[index] == ']')
|
|
|
{
|
|
|
- if (expr[index] == ']')
|
|
|
+ if (--bracket_count == 0)
|
|
|
{
|
|
|
- if (--bracket_count == 0)
|
|
|
- {
|
|
|
- end_array_index = index;
|
|
|
- break;
|
|
|
- }
|
|
|
+ end_array_index = index;
|
|
|
+ break;
|
|
|
}
|
|
|
- else if (expr[index] == '[')
|
|
|
- bracket_count++;
|
|
|
}
|
|
|
+ else if (expr[index] == '[')
|
|
|
+ bracket_count++;
|
|
|
+ }
|
|
|
|
|
|
- assert(bracket_count == 0);
|
|
|
+ assert(bracket_count == 0);
|
|
|
|
|
|
- // Doesn't really make sense to declare a non-arrayed image with nonuniformEXT, but there's
|
|
|
- // nothing we can do here to express that.
|
|
|
- if (start_array_index == string::npos || end_array_index == string::npos || end_array_index < start_array_index)
|
|
|
- return;
|
|
|
+ // Doesn't really make sense to declare a non-arrayed image with nonuniformEXT, but there's
|
|
|
+ // nothing we can do here to express that.
|
|
|
+ if (start_array_index == string::npos || end_array_index == string::npos || end_array_index < start_array_index)
|
|
|
+ return;
|
|
|
|
|
|
- start_array_index++;
|
|
|
+ start_array_index++;
|
|
|
|
|
|
- expr = join(expr.substr(0, start_array_index), backend.nonuniform_qualifier, "(",
|
|
|
- expr.substr(start_array_index, end_array_index - start_array_index), ")",
|
|
|
- expr.substr(end_array_index, string::npos));
|
|
|
- }
|
|
|
+ expr = join(expr.substr(0, start_array_index), backend.nonuniform_qualifier, "(",
|
|
|
+ expr.substr(start_array_index, end_array_index - start_array_index), ")",
|
|
|
+ expr.substr(end_array_index, string::npos));
|
|
|
}
|
|
|
|
|
|
void CompilerGLSL::emit_block_hints(const SPIRBlock &)
|
|
|
@@ -15342,40 +15424,6 @@ void CompilerGLSL::emit_line_directive(uint32_t file_id, uint32_t line_literal)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void CompilerGLSL::propagate_nonuniform_qualifier(uint32_t id)
|
|
|
-{
|
|
|
- // SPIR-V might only tag the very last ID with NonUniformEXT, but for codegen,
|
|
|
- // we need to know NonUniformEXT a little earlier, when the resource is actually loaded.
|
|
|
- // Back-propagate the qualifier based on the expression dependency chain.
|
|
|
-
|
|
|
- if (!has_decoration(id, DecorationNonUniformEXT))
|
|
|
- {
|
|
|
- set_decoration(id, DecorationNonUniformEXT);
|
|
|
- force_recompile();
|
|
|
- }
|
|
|
-
|
|
|
- auto *e = maybe_get<SPIRExpression>(id);
|
|
|
- auto *combined = maybe_get<SPIRCombinedImageSampler>(id);
|
|
|
- auto *chain = maybe_get<SPIRAccessChain>(id);
|
|
|
- if (e)
|
|
|
- {
|
|
|
- for (auto &expr : e->expression_dependencies)
|
|
|
- propagate_nonuniform_qualifier(expr);
|
|
|
- for (auto &expr : e->implied_read_expressions)
|
|
|
- propagate_nonuniform_qualifier(expr);
|
|
|
- }
|
|
|
- else if (combined)
|
|
|
- {
|
|
|
- propagate_nonuniform_qualifier(combined->image);
|
|
|
- propagate_nonuniform_qualifier(combined->sampler);
|
|
|
- }
|
|
|
- else if (chain)
|
|
|
- {
|
|
|
- for (auto &expr : chain->implied_read_expressions)
|
|
|
- propagate_nonuniform_qualifier(expr);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
void CompilerGLSL::emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, uint32_t rhs_id, uint32_t rhs_type_id,
|
|
|
SmallVector<uint32_t> chain)
|
|
|
{
|
|
|
@@ -15788,3 +15836,149 @@ void CompilerGLSL::rewrite_load_for_wrapped_row_major(std::string &expr, TypeID
|
|
|
expr = join("spvWorkaroundRowMajor(", expr, ")");
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+void CompilerGLSL::mask_stage_output_by_location(uint32_t location, uint32_t component)
|
|
|
+{
|
|
|
+ masked_output_locations.insert({ location, component });
|
|
|
+}
|
|
|
+
|
|
|
+void CompilerGLSL::mask_stage_output_by_builtin(BuiltIn builtin)
|
|
|
+{
|
|
|
+ masked_output_builtins.insert(builtin);
|
|
|
+}
|
|
|
+
|
|
|
+bool CompilerGLSL::is_stage_output_variable_masked(const SPIRVariable &var) const
|
|
|
+{
|
|
|
+ auto &type = get<SPIRType>(var.basetype);
|
|
|
+ bool is_block = has_decoration(type.self, DecorationBlock);
|
|
|
+ // Blocks by themselves are never masked. Must be masked per-member.
|
|
|
+ if (is_block)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ bool is_builtin = has_decoration(var.self, DecorationBuiltIn);
|
|
|
+
|
|
|
+ if (is_builtin)
|
|
|
+ {
|
|
|
+ return is_stage_output_builtin_masked(BuiltIn(get_decoration(var.self, DecorationBuiltIn)));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (!has_decoration(var.self, DecorationLocation))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return is_stage_output_location_masked(
|
|
|
+ get_decoration(var.self, DecorationLocation),
|
|
|
+ get_decoration(var.self, DecorationComponent));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool CompilerGLSL::is_stage_output_block_member_masked(const SPIRVariable &var, uint32_t index, bool strip_array) const
|
|
|
+{
|
|
|
+ auto &type = get<SPIRType>(var.basetype);
|
|
|
+ bool is_block = has_decoration(type.self, DecorationBlock);
|
|
|
+ if (!is_block)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ BuiltIn builtin = BuiltInMax;
|
|
|
+ if (is_member_builtin(type, index, &builtin))
|
|
|
+ {
|
|
|
+ return is_stage_output_builtin_masked(builtin);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ uint32_t location = get_declared_member_location(var, index, strip_array);
|
|
|
+ uint32_t component = get_member_decoration(type.self, index, DecorationComponent);
|
|
|
+ return is_stage_output_location_masked(location, component);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool CompilerGLSL::is_stage_output_location_masked(uint32_t location, uint32_t component) const
|
|
|
+{
|
|
|
+ return masked_output_locations.count({ location, component }) != 0;
|
|
|
+}
|
|
|
+
|
|
|
+bool CompilerGLSL::is_stage_output_builtin_masked(spv::BuiltIn builtin) const
|
|
|
+{
|
|
|
+ return masked_output_builtins.count(builtin) != 0;
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t CompilerGLSL::get_declared_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const
|
|
|
+{
|
|
|
+ auto &block_type = get<SPIRType>(var.basetype);
|
|
|
+ if (has_member_decoration(block_type.self, mbr_idx, DecorationLocation))
|
|
|
+ return get_member_decoration(block_type.self, mbr_idx, DecorationLocation);
|
|
|
+ else
|
|
|
+ return get_accumulated_member_location(var, mbr_idx, strip_array);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t CompilerGLSL::get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const
|
|
|
+{
|
|
|
+ auto &type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
|
|
|
+ uint32_t location = get_decoration(var.self, DecorationLocation);
|
|
|
+
|
|
|
+ for (uint32_t i = 0; i < mbr_idx; i++)
|
|
|
+ {
|
|
|
+ auto &mbr_type = get<SPIRType>(type.member_types[i]);
|
|
|
+
|
|
|
+ // Start counting from any place we have a new location decoration.
|
|
|
+ if (has_member_decoration(type.self, mbr_idx, DecorationLocation))
|
|
|
+ location = get_member_decoration(type.self, mbr_idx, DecorationLocation);
|
|
|
+
|
|
|
+ uint32_t location_count = type_to_location_count(mbr_type);
|
|
|
+ location += location_count;
|
|
|
+ }
|
|
|
+
|
|
|
+ return location;
|
|
|
+}
|
|
|
+
|
|
|
+StorageClass CompilerGLSL::get_expression_effective_storage_class(uint32_t ptr)
|
|
|
+{
|
|
|
+ auto *var = maybe_get_backing_variable(ptr);
|
|
|
+
|
|
|
+ // If the expression has been lowered to a temporary, we need to use the Generic storage class.
|
|
|
+ // We're looking for the effective storage class of a given expression.
|
|
|
+ // An access chain or forwarded OpLoads from such access chains
|
|
|
+ // will generally have the storage class of the underlying variable, but if the load was not forwarded
|
|
|
+ // we have lost any address space qualifiers.
|
|
|
+ bool forced_temporary = ir.ids[ptr].get_type() == TypeExpression && !get<SPIRExpression>(ptr).access_chain &&
|
|
|
+ (forced_temporaries.count(ptr) != 0 || forwarded_temporaries.count(ptr) == 0);
|
|
|
+
|
|
|
+ if (var && !forced_temporary)
|
|
|
+ {
|
|
|
+ if (variable_decl_is_remapped_storage(*var, StorageClassWorkgroup))
|
|
|
+ return StorageClassWorkgroup;
|
|
|
+ if (variable_decl_is_remapped_storage(*var, StorageClassStorageBuffer))
|
|
|
+ return StorageClassStorageBuffer;
|
|
|
+
|
|
|
+ // Normalize SSBOs to StorageBuffer here.
|
|
|
+ if (var->storage == StorageClassUniform &&
|
|
|
+ has_decoration(get<SPIRType>(var->basetype).self, DecorationBufferBlock))
|
|
|
+ return StorageClassStorageBuffer;
|
|
|
+ else
|
|
|
+ return var->storage;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ return expression_type(ptr).storage;
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t CompilerGLSL::type_to_location_count(const SPIRType &type) const
|
|
|
+{
|
|
|
+ uint32_t count;
|
|
|
+ if (type.basetype == SPIRType::Struct)
|
|
|
+ {
|
|
|
+ uint32_t mbr_count = uint32_t(type.member_types.size());
|
|
|
+ count = 0;
|
|
|
+ for (uint32_t i = 0; i < mbr_count; i++)
|
|
|
+ count += type_to_location_count(get<SPIRType>(type.member_types[i]));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ count = type.columns > 1 ? type.columns : 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t dim_count = uint32_t(type.array.size());
|
|
|
+ for (uint32_t i = 0; i < dim_count; i++)
|
|
|
+ count *= to_array_size_literal(type, i);
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|