Merge pull request #1633 from KhronosGroup/fix-1626
GLSL: Handle complex load/store scenarios to gl_SampleMask.
This commit is contained in:
commit
60aa24566e
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
100
spirv_glsl.cpp
100
spirv_glsl.cpp
@ -3608,6 +3608,21 @@ void CompilerGLSL::emit_output_variable_initializer(const SPIRVariable &var)
|
||||
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
|
||||
{
|
||||
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);
|
||||
|
||||
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.
|
||||
cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression));
|
||||
// We might need to cast in order to store to a builtin.
|
||||
cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression));
|
||||
|
||||
// Tries to optimize assignments like "<lhs> = <lhs> op expr".
|
||||
// While this is purely cosmetic, this is important for legacy ESSL where loop
|
||||
// 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.
|
||||
if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs))
|
||||
statement(lhs, " = ", rhs, ";");
|
||||
// Tries to optimize assignments like "<lhs> = <lhs> op expr".
|
||||
// While this is purely cosmetic, this is important for legacy ESSL where loop
|
||||
// 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.
|
||||
if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs))
|
||||
statement(lhs, " = ", rhs, ";");
|
||||
}
|
||||
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
|
||||
// doing float4[](gl_in[i].gl_Position, ...) instead.
|
||||
// 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);
|
||||
|
||||
// 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), ";");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
return;
|
||||
|
||||
if (var->storage != StorageClassInput)
|
||||
if (var->storage != StorageClassInput && var->storage != StorageClassOutput)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
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_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.
|
||||
// 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.
|
||||
statement("for (int i = 0; i < int(", array_expr, "); i++)");
|
||||
begin_scope();
|
||||
if (is_builtin)
|
||||
if (is_builtin && !is_sample_mask)
|
||||
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
|
||||
statement(new_expr, "[i] = ", expr, "[i];");
|
||||
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)
|
||||
{
|
||||
// We will handle array cases elsewhere.
|
||||
if (!expr_type.array.empty())
|
||||
return;
|
||||
|
||||
auto *var = maybe_get_backing_variable(source_id);
|
||||
if (var)
|
||||
source_id = var->self;
|
||||
@ -15049,6 +15119,7 @@ void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
|
||||
case BuiltInDrawIndex:
|
||||
case BuiltInFragStencilRefEXT:
|
||||
case BuiltInInstanceCustomIndexNV:
|
||||
case BuiltInSampleMask:
|
||||
expected_type = SPIRType::Int;
|
||||
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)
|
||||
{
|
||||
auto *var = maybe_get_backing_variable(target_id);
|
||||
if (var)
|
||||
target_id = var->self;
|
||||
|
||||
// Only interested in standalone builtin variables.
|
||||
if (!has_decoration(target_id, DecorationBuiltIn))
|
||||
return;
|
||||
@ -15088,6 +15163,7 @@ void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr,
|
||||
case BuiltInPrimitiveId:
|
||||
case BuiltInViewportIndex:
|
||||
case BuiltInFragStencilRefEXT:
|
||||
case BuiltInSampleMask:
|
||||
expected_type = SPIRType::Int;
|
||||
break;
|
||||
|
||||
|
@ -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_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);
|
||||
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 handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id);
|
||||
|
Loading…
Reference in New Issue
Block a user