diff --git a/engine/src/flutter/impeller/compiler/spirv_sksl.cc b/engine/src/flutter/impeller/compiler/spirv_sksl.cc index cbad7d96e40..977f593ccbd 100644 --- a/engine/src/flutter/impeller/compiler/spirv_sksl.cc +++ b/engine/src/flutter/impeller/compiler/spirv_sksl.cc @@ -19,7 +19,7 @@ std::string CompilerSkSL::compile() { } options.es = false; - options.version = 300; + options.version = 100; options.vulkan_semantics = false; options.enable_420pack_extension = false; @@ -82,7 +82,7 @@ void CompilerSkSL::emit_header() { void CompilerSkSL::emit_uniform(const SPIRVariable& var) { auto& type = get(var.basetype); add_resource_name(var.self); - statement(layout_for_variable(var), variable_decl(var), ";"); + statement(variable_decl(var), ";"); // The Flutter FragmentProgram implementation passes additional unifroms along // with shader uniforms that encode the shader width and height. @@ -198,6 +198,25 @@ bool CompilerSkSL::emit_uniform_resources() { } } + // Sort uniforms by location. + auto compare_locations = [this](ID id1, ID id2) { + auto& flags1 = get_decoration_bitset(id1); + auto& flags2 = get_decoration_bitset(id2); + // Put the uniforms with no location after the ones that have a location. + if (!flags1.get(DecorationLocation)) { + return false; + } + if (!flags2.get(DecorationLocation)) { + return true; + } + // Sort in increasing order of location. + return get_decoration(id1, DecorationLocation) < + get_decoration(id2, DecorationLocation); + }; + std::sort(regular_uniforms.begin(), regular_uniforms.end(), + compare_locations); + std::sort(shader_uniforms.begin(), shader_uniforms.end(), compare_locations); + for (const auto& id : regular_uniforms) { auto& var = get(id); emit_uniform(var); diff --git a/engine/src/flutter/lib/spirv/test/general_shaders/BUILD.gn b/engine/src/flutter/lib/spirv/test/general_shaders/BUILD.gn index e12f315b8da..2c53b4ee716 100644 --- a/engine/src/flutter/lib/spirv/test/general_shaders/BUILD.gn +++ b/engine/src/flutter/lib/spirv/test/general_shaders/BUILD.gn @@ -13,6 +13,7 @@ if (enable_unittests) { "functions.frag", "simple.frag", "uniforms.frag", + "uniforms_sorted.frag", ] group("general_shaders") { diff --git a/engine/src/flutter/lib/spirv/test/general_shaders/uniforms_sorted.frag b/engine/src/flutter/lib/spirv/test/general_shaders/uniforms_sorted.frag new file mode 100644 index 00000000000..e7e191fe49e --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/general_shaders/uniforms_sorted.frag @@ -0,0 +1,135 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This shader is a fixture for a test that ensures that the order of the +// unifroms is retained. The values of the uniforms in the test are strictly +// increasing. If two uniforms are out of order, the output color is red. If +// the uniforms are in the right order, the output color is green. + +precision highp float; + +layout(location = 0) uniform vec4 u_color; +layout(location = 1) uniform float u_alpha; +layout(location = 2) uniform vec4 u_sparkle_color; +layout(location = 3) uniform float u_sparkle_alpha; +layout(location = 4) uniform float u_blur; +layout(location = 5) uniform vec2 u_center; +layout(location = 6) uniform float u_radius_scale; +layout(location = 7) uniform float u_max_radius; +layout(location = 8) uniform vec2 u_resolution_scale; +layout(location = 9) uniform vec2 u_noise_scale; +layout(location = 10) uniform float u_noise_phase; +layout(location = 11) uniform vec2 u_circle1; +layout(location = 12) uniform vec2 u_circle2; +layout(location = 13) uniform vec2 u_circle3; +layout(location = 14) uniform vec2 u_rotation1; +layout(location = 15) uniform vec2 u_rotation2; +layout(location = 16) uniform vec2 u_rotation3; + +layout(location = 0) out vec4 fragColor; + +const float PI = 3.1415926535897932384626; +const float PI_ROTATE_RIGHT = PI * 0.0078125; +const float PI_ROTATE_LEFT = PI * -0.0078125; +const float ONE_THIRD = 1./3.; +const vec2 TURBULENCE_SCALE = vec2(0.8); + +float saturate(float x) { + return clamp(x, 0.0, 1.0); +} + +float triangle_noise(highp vec2 n) { + n = fract(n * vec2(5.3987, 5.4421)); + n += dot(n.yx, n.xy + vec2(21.5351, 14.3137)); + float xy = n.x * n.y; + return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0; +} + +float threshold(float v, float l, float h) { + return step(l, v) * (1.0 - step(h, v)); +} + +mat2 rotate2d(vec2 rad){ + return mat2(rad.x, -rad.y, rad.y, rad.x); +} + +float soft_circle(vec2 uv, vec2 xy, float radius, float blur) { + float blur_half = blur * 0.5; + float d = distance(uv, xy); + return 1.0 - smoothstep(1.0 - blur_half, 1.0 + blur_half, d / radius); +} + +float soft_ring(vec2 uv, vec2 xy, float radius, float thickness, float blur) { + float circle_outer = soft_circle(uv, xy, radius + thickness, blur); + float circle_inner = soft_circle(uv, xy, max(radius - thickness, 0.0), blur); + return saturate(circle_outer - circle_inner); +} + +float circle_grid(vec2 resolution, vec2 p, vec2 xy, vec2 rotation, float cell_diameter) { + p = rotate2d(rotation) * (xy - p) + xy; + p = mod(p, cell_diameter) / resolution; + float cell_uv = cell_diameter / resolution.y * 0.5; + float r = 0.65 * cell_uv; + return soft_circle(p, vec2(cell_uv), r, r * 50.0); +} + +float sparkle(vec2 uv, float t) { + float n = triangle_noise(uv); + float s = threshold(n, 0.0, 0.05); + s += threshold(n + sin(PI * (t + 0.35)), 0.1, 0.15); + s += threshold(n + sin(PI * (t + 0.7)), 0.2, 0.25); + s += threshold(n + sin(PI * (t + 1.05)), 0.3, 0.35); + return saturate(s) * 0.55; +} + +float turbulence(vec2 uv) { + vec2 uv_scale = uv * TURBULENCE_SCALE; + float g1 = circle_grid(TURBULENCE_SCALE, uv_scale, u_circle1, u_rotation1, 0.17); + float g2 = circle_grid(TURBULENCE_SCALE, uv_scale, u_circle2, u_rotation2, 0.2); + float g3 = circle_grid(TURBULENCE_SCALE, uv_scale, u_circle3, u_rotation3, 0.275); + float v = (g1 * g1 + g2 - g3) * 0.5; + return saturate(0.45 + 0.8 * v); +} + +void main() { + // This block of code triggers the compiler to emit the uniforms out of order + // if they are not explicitly sorted. + vec2 p = gl_FragCoord.xy; + vec2 uv = p * u_resolution_scale; + vec2 density_uv = uv - mod(p, u_noise_scale); + float radius = u_max_radius * u_radius_scale; + float turbulence = turbulence(uv); + float ring = soft_ring(p, u_center, radius, 0.05 * u_max_radius, u_blur); + float sparkle = sparkle(density_uv, u_noise_phase) * ring * turbulence * u_sparkle_alpha; + float wave_alpha = soft_circle(p, u_center, radius, u_blur) * u_alpha * u_color.a; + vec4 wave_color = vec4(u_color.rgb * wave_alpha, wave_alpha); + vec4 sparkle_color = vec4(u_sparkle_color.rgb * u_sparkle_color.a, u_sparkle_color.a); + fragColor = mix(wave_color, sparkle_color, sparkle); + + vec4 badColor = vec4(1.0, 0, 0, 1.0); + vec4 goodColor = vec4(0, 1.0, 0, 1.0); + if (u_color.x > u_alpha || + u_alpha > u_sparkle_color.x || + u_sparkle_color.x > u_sparkle_alpha || + u_sparkle_alpha > u_blur || + u_blur > u_center.x || + u_center.x > u_radius_scale || + u_radius_scale > u_max_radius || + u_max_radius > u_resolution_scale.x || + u_resolution_scale.x > u_noise_scale.x || + u_noise_scale.x > u_noise_phase || + u_noise_phase > u_circle1.x || + u_circle1.x > u_circle2.x || + u_circle2.x > u_circle3.x || + u_circle3.x > u_rotation1.x || + u_rotation1.x > u_rotation2.x || + u_rotation2.x > u_rotation3.x) { + fragColor = badColor; + } else { + fragColor = goodColor; + } +} + diff --git a/engine/src/flutter/testing/dart/fragment_shader_test.dart b/engine/src/flutter/testing/dart/fragment_shader_test.dart index 8bc4927cb2c..602440f6074 100644 --- a/engine/src/flutter/testing/dart/fragment_shader_test.dart +++ b/engine/src/flutter/testing/dart/fragment_shader_test.dart @@ -223,6 +223,27 @@ void main() { // produces the correct pixels are in the framework. }); + test('sksl uniforms are sorted correctly', () async { + final Uint8List sksl = await shaderFile( + path.join('general_shaders', 'sksl'), + 'uniforms_sorted.frag.sksl', + ).readAsBytes(); + final FragmentProgram program = await FragmentProgram.compile( + raw: sksl.buffer, + uniformFloatCount: 32, + ); + + // The shader will not render green if the compiler doesn't keep the + // uniforms in the right order. + final Shader shader = program.shader( + floatUniforms: Float32List.fromList( + List.generate(32, (int i) => i.toDouble()), + ), + ); + + await _expectShaderRendersGreen(shader); + }); + // Test all supported GLSL ops. See lib/spirv/lib/src/constants.dart final Map supportedGLSLOpShaders = _loadShaders( path.join('supported_glsl_op_shaders', 'spirv'),