diff --git a/reference/shaders-no-opt/asm/frag/array-builtin-bitcast-load-store.asm.frag b/reference/shaders-no-opt/asm/frag/array-builtin-bitcast-load-store.asm.frag new file mode 100644 index 00000000..40f6ee71 --- /dev/null +++ b/reference/shaders-no-opt/asm/frag/array-builtin-bitcast-load-store.asm.frag @@ -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]); + } +} + diff --git a/shaders-no-opt/asm/frag/array-builtin-bitcast-load-store.asm.frag b/shaders-no-opt/asm/frag/array-builtin-bitcast-load-store.asm.frag new file mode 100644 index 00000000..387764c9 --- /dev/null +++ b/shaders-no-opt/asm/frag/array-builtin-bitcast-load-store.asm.frag @@ -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 diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index aa34b498..31ff8504 100644 --- a/spirv_glsl.cpp +++ b/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(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(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 " = 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 " = 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(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; diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index 37ec6a7a..0e030382 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -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);