Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 43f28d2

Browse files
authored
[Impeller] Start stroke tessellation in compute (#39543)
[Impeller] Start stroke tessellation in compute
1 parent 0f4acf3 commit 43f28d2

21 files changed

+1003
-15
lines changed

ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@
138138
../../../flutter/impeller/geometry/geometry_unittests.h
139139
../../../flutter/impeller/image/README.md
140140
../../../flutter/impeller/playground
141+
../../../flutter/impeller/renderer/compute_subgroup_unittests.cc
141142
../../../flutter/impeller/renderer/compute_unittests.cc
142143
../../../flutter/impeller/renderer/device_buffer_unittests.cc
143144
../../../flutter/impeller/renderer/host_buffer_unittests.cc

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,7 @@ ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/color.glsl + ../.
10881088
ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/constants.glsl + ../../../flutter/LICENSE
10891089
ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/gaussian.glsl + ../../../flutter/LICENSE
10901090
ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/gradient.glsl + ../../../flutter/LICENSE
1091+
ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/path.glsl + ../../../flutter/LICENSE
10911092
ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/texture.glsl + ../../../flutter/LICENSE
10921093
ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/transform.glsl + ../../../flutter/LICENSE
10931094
ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/types.glsl + ../../../flutter/LICENSE
@@ -3584,6 +3585,7 @@ FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/color.glsl
35843585
FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/constants.glsl
35853586
FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/gaussian.glsl
35863587
FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/gradient.glsl
3588+
FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/path.glsl
35873589
FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/texture.glsl
35883590
FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/transform.glsl
35893591
FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/types.glsl

impeller/compiler/compiler.cc

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
#include <array>
88
#include <filesystem>
99
#include <memory>
10+
#include <optional>
1011
#include <sstream>
12+
#include <string>
1113
#include <utility>
1214

1315
#include "flutter/fml/paths.h"
@@ -25,16 +27,36 @@ const uint32_t kFragBindingBase = 128;
2527
const size_t kNumUniformKinds =
2628
static_cast<int>(shaderc_uniform_kind::shaderc_uniform_kind_buffer) + 1;
2729

30+
static uint32_t ParseMSLVersion(const std::string& msl_version) {
31+
std::stringstream sstream(msl_version);
32+
std::string version_part;
33+
uint32_t major = 1;
34+
uint32_t minor = 2;
35+
uint32_t patch = 0;
36+
if (std::getline(sstream, version_part, '.')) {
37+
major = std::stoi(version_part);
38+
if (std::getline(sstream, version_part, '.')) {
39+
minor = std::stoi(version_part);
40+
if (std::getline(sstream, version_part, '.')) {
41+
patch = std::stoi(version_part);
42+
}
43+
}
44+
}
45+
if (major < 1 || minor < 2) {
46+
std::cerr << "--metal-version version must be at least 1.2. Have "
47+
<< msl_version << std::endl;
48+
}
49+
return spirv_cross::CompilerMSL::Options::make_msl_version(major, minor,
50+
patch);
51+
}
52+
2853
static CompilerBackend CreateMSLCompiler(const spirv_cross::ParsedIR& ir,
2954
const SourceOptions& source_options) {
3055
auto sl_compiler = std::make_shared<spirv_cross::CompilerMSL>(ir);
3156
spirv_cross::CompilerMSL::Options sl_options;
3257
sl_options.platform =
3358
TargetPlatformToMSLPlatform(source_options.target_platform);
34-
// If this version specification changes, the GN rules that process the
35-
// Metal to AIR must be updated as well.
36-
sl_options.msl_version =
37-
spirv_cross::CompilerMSL::Options::make_msl_version(1, 2);
59+
sl_options.msl_version = ParseMSLVersion(source_options.metal_version);
3860
sl_options.use_framebuffer_fetch_subpasses = true;
3961
sl_compiler->set_msl_options(sl_options);
4062

@@ -357,9 +379,9 @@ Compiler::Compiler(const fml::Mapping& source_mapping,
357379
shaderc_optimization_level::shaderc_optimization_level_performance);
358380
spirv_options.SetTargetEnvironment(
359381
shaderc_target_env::shaderc_target_env_vulkan,
360-
shaderc_env_version::shaderc_env_version_vulkan_1_0);
382+
shaderc_env_version::shaderc_env_version_vulkan_1_1);
361383
spirv_options.SetTargetSpirv(
362-
shaderc_spirv_version::shaderc_spirv_version_1_0);
384+
shaderc_spirv_version::shaderc_spirv_version_1_3);
363385
break;
364386
case TargetPlatform::kRuntimeStageMetal:
365387
case TargetPlatform::kRuntimeStageGLES:
@@ -437,7 +459,11 @@ Compiler::Compiler(const fml::Mapping& source_mapping,
437459
<< ShaderCErrorToString(spv_result_->GetCompilationStatus())
438460
<< ". " << spv_result_->GetNumErrors() << " error(s) and "
439461
<< spv_result_->GetNumWarnings() << " warning(s).";
440-
if (spv_result_->GetNumErrors() > 0 || spv_result_->GetNumWarnings() > 0) {
462+
// It should normally be enough to check that there are errors or warnings,
463+
// but some cases result in no errors or warnings and still have an error
464+
// message. If there's a message we should print it.
465+
if (spv_result_->GetNumErrors() > 0 || spv_result_->GetNumWarnings() > 0 ||
466+
!spv_result_->GetErrorMessage().empty()) {
441467
COMPILER_ERROR_NO_PREFIX << spv_result_->GetErrorMessage();
442468
}
443469
return;

impeller/compiler/impellerc_main.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ bool Main(const fml::CommandLine& command_line) {
7474
switches.entry_point);
7575
options.json_format = switches.json_format;
7676
options.gles_language_version = switches.gles_language_version;
77+
options.metal_version = switches.metal_version;
7778

7879
Reflector::Options reflector_options;
7980
reflector_options.target_platform = switches.target_platform;

impeller/compiler/shader_lib/impeller/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ copy("impeller") {
1010
"constants.glsl",
1111
"gaussian.glsl",
1212
"gradient.glsl",
13+
"path.glsl",
1314
"texture.glsl",
1415
"transform.glsl",
1516
"types.glsl",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef PATH_GLSL_
6+
#define PATH_GLSL_
7+
8+
#define MOVE 0
9+
#define LINE 1
10+
#define QUAD 2
11+
#define CUBIC 3
12+
13+
struct LineData {
14+
vec2 p1;
15+
vec2 p2;
16+
};
17+
18+
struct QuadData {
19+
vec2 p1;
20+
vec2 cp;
21+
vec2 p2;
22+
};
23+
24+
struct CubicData {
25+
vec2 p1;
26+
vec2 cp1;
27+
vec2 cp2;
28+
vec2 p2;
29+
};
30+
31+
struct Position {
32+
uint index;
33+
uint count;
34+
};
35+
36+
/// Solve for point on a quadratic Bezier curve defined by starting point `p1`,
37+
/// control point `cp`, and end point `p2` at time `t`.
38+
vec2 QuadraticSolve(QuadData quad, float t) {
39+
return (1.0 - t) * (1.0 - t) * quad.p1 + //
40+
2.0 * (1.0 - t) * t * quad.cp + //
41+
t * t * quad.p2;
42+
}
43+
44+
vec2 CubicSolve(CubicData cubic, float t) {
45+
return (1. - t) * (1. - t) * (1. - t) * cubic.p1 + //
46+
3 * (1. - t) * (1. - t) * t * cubic.cp1 + //
47+
3 * (1. - t) * t * t * cubic.cp2 + //
48+
t * t * t * cubic.p2;
49+
}
50+
51+
/// Used to approximate quadratic curves using parabola.
52+
///
53+
/// See
54+
/// https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html
55+
float ApproximateParabolaIntegral(float x) {
56+
float d = 0.67;
57+
return x / (1.0 - d + sqrt(sqrt(pow(d, 4) + 0.25 * x * x)));
58+
}
59+
60+
bool isfinite(float f) {
61+
return !isnan(f) && !isinf(f);
62+
}
63+
64+
float Cross(vec2 p1, vec2 p2) {
65+
return p1.x * p2.y - p1.y * p2.x;
66+
}
67+
68+
#endif

impeller/compiler/source_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct SourceOptions {
2727
uint32_t gles_language_version = 100;
2828
std::vector<std::string> defines;
2929
bool json_format = false;
30+
std::string metal_version;
3031

3132
SourceOptions();
3233

impeller/compiler/switches.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ Switches::Switches(const fml::CommandLine& command_line)
131131
gles_language_version(
132132
stoi(command_line.GetOptionValueWithDefault("gles-language-version",
133133
"0"))),
134+
metal_version(
135+
command_line.GetOptionValueWithDefault("metal-version", "1.2")),
134136
entry_point(
135137
command_line.GetOptionValueWithDefault("entry-point", "main")) {
136138
auto language =

impeller/compiler/switches.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct Switches {
3434
bool json_format;
3535
SourceLanguage source_language = SourceLanguage::kUnknown;
3636
uint32_t gles_language_version;
37+
std::string metal_version;
3738
std::string entry_point;
3839

3940
Switches();

impeller/fixtures/BUILD.gn

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@
55
import("//flutter/impeller/tools/impeller.gni")
66
import("//flutter/testing/testing.gni")
77

8+
if (impeller_enable_vulkan || impeller_enable_metal) {
9+
impeller_shaders("shader_subgroup_fixtures") {
10+
enable_opengles = false
11+
name = "subgroup_fixtures"
12+
13+
# Only need 2.1 for desktop. Running on iOS would require 2.4
14+
metal_version = "2.1"
15+
16+
shaders = [
17+
"cubic_to_quads.comp",
18+
"quad_polyline.comp",
19+
"stroke.comp",
20+
]
21+
}
22+
}
23+
824
impeller_shaders("shader_fixtures") {
925
name = "fixtures"
1026

@@ -70,10 +86,12 @@ test_fixtures("file_fixtures") {
7086
"bay_bridge.jpg",
7187
"blue_noise.png",
7288
"boston.jpg",
89+
"cubic_to_quads.comp",
7390
"embarcadero.jpg",
7491
"flutter_logo_baked.glb",
7592
"kalimba.jpg",
7693
"multiple_stages.hlsl",
94+
"quad_polyline.comp",
7795
"resources_limit.vert",
7896
"sample.comp",
7997
"sample.frag",
@@ -85,6 +103,7 @@ test_fixtures("file_fixtures") {
85103
"sa%m#ple.vert",
86104
"stage1.comp",
87105
"stage2.comp",
106+
"stroke.comp",
88107
"struct_def_bug.vert",
89108
"table_mountain_nx.png",
90109
"table_mountain_ny.png",
@@ -117,4 +136,8 @@ group("fixtures") {
117136
":scene_fixtures",
118137
":shader_fixtures",
119138
]
139+
140+
if (impeller_enable_vulkan || impeller_enable_metal) {
141+
public_deps += [ ":shader_subgroup_fixtures" ]
142+
}
120143
}

impeller/fixtures/cubic_to_quads.comp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
#extension GL_KHR_shader_subgroup_arithmetic : enable
5+
6+
layout(local_size_x = 512, local_size_y = 1) in;
7+
layout(std430) buffer;
8+
9+
#include <impeller/path.glsl>
10+
11+
layout(binding = 0) readonly buffer Cubics {
12+
uint count;
13+
CubicData data[];
14+
}
15+
cubics;
16+
17+
layout(binding = 1) buffer Quads {
18+
uint count;
19+
QuadData data[];
20+
}
21+
quads;
22+
23+
uniform Config {
24+
float accuracy;
25+
}
26+
config;
27+
28+
shared uint quad_counts[512];
29+
shared uint count_sums[512];
30+
31+
void main() {
32+
uint ident = gl_GlobalInvocationID.x;
33+
if (ident >= cubics.count) {
34+
return;
35+
}
36+
37+
// The maximum error, as a vector from the cubic to the best approximating
38+
// quadratic, is proportional to the third derivative, which is constant
39+
// across the segment. Thus, the error scales down as the third power of
40+
// the number of subdivisions. Our strategy then is to subdivide `t` evenly.
41+
//
42+
// This is an overestimate of the error because only the component
43+
// perpendicular to the first derivative is important. But the simplicity is
44+
// appealing.
45+
46+
// This magic number is the square of 36 / sqrt(3).
47+
// See: http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
48+
float max_hypot2 = 432.0 * config.accuracy * config.accuracy;
49+
50+
CubicData cubic = cubics.data[ident];
51+
52+
vec2 err_v = 3.0 * (cubic.cp2 - cubic.cp1) + cubic.p1 - cubic.p2;
53+
float err = dot(err_v, err_v);
54+
float quad_count = max(1., ceil(pow(err * (1.0 / max_hypot2), 1. / 6.0)));
55+
56+
quad_counts[ident] = uint(quad_count);
57+
58+
barrier();
59+
count_sums[ident] = subgroupInclusiveAdd(quad_counts[ident]);
60+
61+
quads.count = count_sums[cubics.count - 1];
62+
for (uint i = 0; i < quad_count; i++) {
63+
float t0 = i / quad_count;
64+
float t1 = (i + 1) / quad_count;
65+
66+
// calculate the subsegment
67+
vec2 sub_p1 = CubicSolve(cubic, t0);
68+
vec2 sub_p2 = CubicSolve(cubic, t1);
69+
QuadData quad = QuadData(3.0 * (cubic.cp1 - cubic.p1), //
70+
3.0 * (cubic.cp2 - cubic.cp1), //
71+
3.0 * (cubic.p2 - cubic.cp2));
72+
float sub_scale = (t1 - t0) * (1.0 / 3.0);
73+
vec2 sub_cp1 = sub_p1 + sub_scale * QuadraticSolve(quad, t0);
74+
vec2 sub_cp2 = sub_p2 - sub_scale * QuadraticSolve(quad, t1);
75+
76+
vec2 quad_p1x2 = 3.0 * sub_cp1 - sub_p1;
77+
vec2 quad_p2x2 = 3.0 * sub_cp2 - sub_p2;
78+
uint offset = count_sums[ident] - uint(quad_count);
79+
quads.data[offset + i] = QuadData(sub_p1, //
80+
((quad_p1x2 + quad_p2x2) / 4.0), //
81+
sub_p2);
82+
}
83+
}

0 commit comments

Comments
 (0)