[impellerc] Sort uniforms by location in SkSL backend (#34684)
diff --git a/impeller/compiler/spirv_sksl.cc b/impeller/compiler/spirv_sksl.cc
index cbad7d9..977f593 100644
--- a/impeller/compiler/spirv_sksl.cc
+++ b/impeller/compiler/spirv_sksl.cc
@@ -19,7 +19,7 @@
}
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_uniform(const SPIRVariable& var) {
auto& type = get<SPIRType>(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 @@
}
}
+ // 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<SPIRVariable>(id);
emit_uniform(var);
diff --git a/lib/spirv/test/general_shaders/BUILD.gn b/lib/spirv/test/general_shaders/BUILD.gn
index e12f315..2c53b4e 100644
--- a/lib/spirv/test/general_shaders/BUILD.gn
+++ b/lib/spirv/test/general_shaders/BUILD.gn
@@ -13,6 +13,7 @@
"functions.frag",
"simple.frag",
"uniforms.frag",
+ "uniforms_sorted.frag",
]
group("general_shaders") {
diff --git a/lib/spirv/test/general_shaders/uniforms_sorted.frag b/lib/spirv/test/general_shaders/uniforms_sorted.frag
new file mode 100644
index 0000000..e7e191f
--- /dev/null
+++ b/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/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart
index 8bc4927..602440f 100644
--- a/testing/dart/fragment_shader_test.dart
+++ b/testing/dart/fragment_shader_test.dart
@@ -223,6 +223,27 @@
// 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<double>.generate(32, (int i) => i.toDouble()),
+ ),
+ );
+
+ await _expectShaderRendersGreen(shader);
+ });
+
// Test all supported GLSL ops. See lib/spirv/lib/src/constants.dart
final Map<String, ByteBuffer> supportedGLSLOpShaders = _loadShaders(
path.join('supported_glsl_op_shaders', 'spirv'),