Merge pull request #1633 from KhronosGroup/fix-1626

GLSL: Handle complex load/store scenarios to gl_SampleMask.
This commit is contained in:
Hans-Kristian Arntzen 2021-03-09 15:17:21 +01:00 committed by GitHub
commit 60aa24566e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 174 additions and 12 deletions

View File

@ -0,0 +1,28 @@
#version 450
layout(binding = 0, std140) uniform uBuffer
{
vec4 color;
} x_12;
layout(location = 0) out vec4 fragColor;
const vec4 _2_init = vec4(0.0);
void main()
{
fragColor = _2_init;
gl_SampleMask[0] = 0;
fragColor = x_12.color;
gl_SampleMask[0] = int(uint(6));
gl_SampleMask[0] = int(uint(gl_SampleMask[0]));
uint _30_unrolled[1];
for (int i = 0; i < int(1); i++)
{
_30_unrolled[i] = int(gl_SampleMask[i]);
}
for (int i = 0; i < int(1); i++)
{
gl_SampleMask[i] = int(_30_unrolled[i]);
}
}

View File

@ -0,0 +1,57 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 29
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %fragColor %gl_SampleMask
OpExecutionMode %main OriginUpperLeft
OpName %fragColor "fragColor"
OpName %uBuffer "uBuffer"
OpMemberName %uBuffer 0 "color"
OpName %x_12 "x_12"
OpName %gl_SampleMask "gl_SampleMask"
OpName %main "main"
OpDecorate %fragColor Location 0
OpDecorate %uBuffer Block
OpMemberDecorate %uBuffer 0 Offset 0
OpDecorate %x_12 DescriptorSet 0
OpDecorate %x_12 Binding 0
OpDecorate %gl_SampleMask BuiltIn SampleMask
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%5 = OpConstantNull %v4float
%fragColor = OpVariable %_ptr_Output_v4float Output %5
%uBuffer = OpTypeStruct %v4float
%_ptr_Uniform_uBuffer = OpTypePointer Uniform %uBuffer
%x_12 = OpVariable %_ptr_Uniform_uBuffer Uniform
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%_arr_uint_uint_1 = OpTypeArray %uint %uint_1
%_ptr_Output__arr_uint_uint_1 = OpTypePointer Output %_arr_uint_uint_1
%14 = OpConstantNull %_arr_uint_uint_1
%gl_SampleMask = OpVariable %_ptr_Output__arr_uint_uint_1 Output %14
%void = OpTypeVoid
%15 = OpTypeFunction %void
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Output_uint = OpTypePointer Output %uint
%int_6 = OpConstant %int 6
%main = OpFunction %void None %15
%18 = OpLabel
%21 = OpAccessChain %_ptr_Uniform_v4float %x_12 %uint_0
%22 = OpLoad %v4float %21
OpStore %fragColor %22
%26 = OpAccessChain %_ptr_Output_uint %gl_SampleMask %int_0
%27 = OpBitcast %uint %int_6
OpStore %26 %27
%loaded_scalar = OpLoad %uint %26
OpStore %26 %loaded_scalar
%loaded = OpLoad %_arr_uint_uint_1 %gl_SampleMask
OpStore %gl_SampleMask %loaded
OpReturn
OpFunctionEnd

View File

@ -3608,6 +3608,21 @@ void CompilerGLSL::emit_output_variable_initializer(const SPIRVariable &var)
statement(to_expression(var.self), "[gl_InvocationID] = ", lut_name, "[gl_InvocationID];"); statement(to_expression(var.self), "[gl_InvocationID] = ", lut_name, "[gl_InvocationID];");
}); });
} }
else if (has_decoration(var.self, DecorationBuiltIn) &&
BuiltIn(get_decoration(var.self, DecorationBuiltIn)) == BuiltInSampleMask)
{
// We cannot copy the array since gl_SampleMask is unsized in GLSL. Unroll time! <_<
entry_func.fixup_hooks_in.push_back([&] {
auto &c = this->get<SPIRConstant>(var.initializer);
uint32_t num_constants = uint32_t(c.subconstants.size());
for (uint32_t i = 0; i < num_constants; i++)
{
// Don't use to_expression on constant since it might be uint, just fish out the raw int.
statement(to_expression(var.self), "[", i, "] = ",
convert_to_string(this->get<SPIRConstant>(c.subconstants[i]).scalar_i32()), ";");
}
});
}
else else
{ {
auto lut_name = join("_", var.self, "_init"); auto lut_name = join("_", var.self, "_init");
@ -9664,17 +9679,20 @@ void CompilerGLSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_ex
{ {
handle_store_to_invariant_variable(lhs_expression, rhs_expression); handle_store_to_invariant_variable(lhs_expression, rhs_expression);
auto lhs = to_dereferenced_expression(lhs_expression); if (!unroll_array_to_complex_store(lhs_expression, rhs_expression))
{
auto lhs = to_dereferenced_expression(lhs_expression);
// We might need to cast in order to store to a builtin. // We might need to cast in order to store to a builtin.
cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression)); cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression));
// Tries to optimize assignments like "<lhs> = <lhs> op expr". // Tries to optimize assignments like "<lhs> = <lhs> op expr".
// While this is purely cosmetic, this is important for legacy ESSL where loop // While this is purely cosmetic, this is important for legacy ESSL where loop
// variable increments must be in either i++ or i += const-expr. // variable increments must be in either i++ or i += const-expr.
// Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0. // Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0.
if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs)) if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs))
statement(lhs, " = ", rhs, ";"); statement(lhs, " = ", rhs, ";");
}
register_write(lhs_expression); register_write(lhs_expression);
} }
} }
@ -9836,6 +9854,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// We might be trying to load a gl_Position[N], where we should be // We might be trying to load a gl_Position[N], where we should be
// doing float4[](gl_in[i].gl_Position, ...) instead. // doing float4[](gl_in[i].gl_Position, ...) instead.
// Similar workarounds are required for input arrays in tessellation. // Similar workarounds are required for input arrays in tessellation.
// Also, loading from gl_SampleMask array needs special unroll.
unroll_array_from_complex_load(id, ptr, expr); unroll_array_from_complex_load(id, ptr, expr);
// Shouldn't need to check for ID, but current glslang codegen requires it in some cases // Shouldn't need to check for ID, but current glslang codegen requires it in some cases
@ -14966,6 +14985,43 @@ void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCl
statement(lhs, " = ", to_expression(rhs_id), ";"); statement(lhs, " = ", to_expression(rhs_id), ";");
} }
bool CompilerGLSL::unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id)
{
if (!backend.force_gl_in_out_block)
return false;
// This path is only relevant for GL backends.
auto *var = maybe_get<SPIRVariable>(target_id);
if (!var || var->storage != StorageClassOutput)
return false;
if (!is_builtin_variable(*var) || BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInSampleMask)
return false;
auto &type = expression_type(source_id);
string array_expr;
if (type.array_size_literal.back())
{
array_expr = convert_to_string(type.array.back());
if (type.array.back() == 0)
SPIRV_CROSS_THROW("Cannot unroll an array copy from unsized array.");
}
else
array_expr = to_expression(type.array.back());
SPIRType target_type;
target_type.basetype = SPIRType::Int;
statement("for (int i = 0; i < int(", array_expr, "); i++)");
begin_scope();
statement(to_expression(target_id), "[i] = ",
bitcast_expression(target_type, type.basetype, join(to_expression(source_id), "[i]")),
";");
end_scope();
return true;
}
void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr) void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr)
{ {
if (!backend.force_gl_in_out_block) if (!backend.force_gl_in_out_block)
@ -14976,7 +15032,7 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
if (!var) if (!var)
return; return;
if (var->storage != StorageClassInput) if (var->storage != StorageClassInput && var->storage != StorageClassOutput)
return; return;
auto &type = get_variable_data_type(*var); auto &type = get_variable_data_type(*var);
@ -14984,9 +15040,13 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
return; return;
auto builtin = BuiltIn(get_decoration(var->self, DecorationBuiltIn)); auto builtin = BuiltIn(get_decoration(var->self, DecorationBuiltIn));
bool is_builtin = is_builtin_variable(*var) && (builtin == BuiltInPointSize || builtin == BuiltInPosition); bool is_builtin = is_builtin_variable(*var) &&
(builtin == BuiltInPointSize ||
builtin == BuiltInPosition ||
builtin == BuiltInSampleMask);
bool is_tess = is_tessellation_shader(); bool is_tess = is_tessellation_shader();
bool is_patch = has_decoration(var->self, DecorationPatch); bool is_patch = has_decoration(var->self, DecorationPatch);
bool is_sample_mask = is_builtin && builtin == BuiltInSampleMask;
// Tessellation input arrays are special in that they are unsized, so we cannot directly copy from it. // Tessellation input arrays are special in that they are unsized, so we cannot directly copy from it.
// We must unroll the array load. // We must unroll the array load.
@ -15010,8 +15070,14 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
// The array size might be a specialization constant, so use a for-loop instead. // The array size might be a specialization constant, so use a for-loop instead.
statement("for (int i = 0; i < int(", array_expr, "); i++)"); statement("for (int i = 0; i < int(", array_expr, "); i++)");
begin_scope(); begin_scope();
if (is_builtin) if (is_builtin && !is_sample_mask)
statement(new_expr, "[i] = gl_in[i].", expr, ";"); statement(new_expr, "[i] = gl_in[i].", expr, ";");
else if (is_sample_mask)
{
SPIRType target_type;
target_type.basetype = SPIRType::Int;
statement(new_expr, "[i] = ", bitcast_expression(target_type, type.basetype, join(expr, "[i]")), ";");
}
else else
statement(new_expr, "[i] = ", expr, "[i];"); statement(new_expr, "[i] = ", expr, "[i];");
end_scope(); end_scope();
@ -15022,6 +15088,10 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type)
{ {
// We will handle array cases elsewhere.
if (!expr_type.array.empty())
return;
auto *var = maybe_get_backing_variable(source_id); auto *var = maybe_get_backing_variable(source_id);
if (var) if (var)
source_id = var->self; source_id = var->self;
@ -15049,6 +15119,7 @@ void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
case BuiltInDrawIndex: case BuiltInDrawIndex:
case BuiltInFragStencilRefEXT: case BuiltInFragStencilRefEXT:
case BuiltInInstanceCustomIndexNV: case BuiltInInstanceCustomIndexNV:
case BuiltInSampleMask:
expected_type = SPIRType::Int; expected_type = SPIRType::Int;
break; break;
@ -15074,6 +15145,10 @@ void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type)
{ {
auto *var = maybe_get_backing_variable(target_id);
if (var)
target_id = var->self;
// Only interested in standalone builtin variables. // Only interested in standalone builtin variables.
if (!has_decoration(target_id, DecorationBuiltIn)) if (!has_decoration(target_id, DecorationBuiltIn))
return; return;
@ -15088,6 +15163,7 @@ void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr,
case BuiltInPrimitiveId: case BuiltInPrimitiveId:
case BuiltInViewportIndex: case BuiltInViewportIndex:
case BuiltInFragStencilRefEXT: case BuiltInFragStencilRefEXT:
case BuiltInSampleMask:
expected_type = SPIRType::Int; expected_type = SPIRType::Int;
break; break;

View File

@ -880,6 +880,7 @@ protected:
virtual void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type); virtual void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type);
virtual void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type); virtual void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type);
void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr); void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr);
bool unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id);
void convert_non_uniform_expression(const SPIRType &type, std::string &expr); void convert_non_uniform_expression(const SPIRType &type, std::string &expr);
void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id); void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id);