diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 68b361cc90bd3..4c2ead5262dad 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -138,6 +138,7 @@ ../../../flutter/impeller/geometry/geometry_unittests.h ../../../flutter/impeller/image/README.md ../../../flutter/impeller/playground +../../../flutter/impeller/renderer/compute_subgroup_unittests.cc ../../../flutter/impeller/renderer/compute_unittests.cc ../../../flutter/impeller/renderer/device_buffer_unittests.cc ../../../flutter/impeller/renderer/host_buffer_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 208cc69479c01..dda3662bd99e1 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1088,6 +1088,7 @@ ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/color.glsl + ../. ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/constants.glsl + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/gaussian.glsl + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/gradient.glsl + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/path.glsl + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/texture.glsl + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/transform.glsl + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/types.glsl + ../../../flutter/LICENSE @@ -3584,6 +3585,7 @@ FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/color.glsl FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/constants.glsl FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/gaussian.glsl FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/gradient.glsl +FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/path.glsl FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/texture.glsl FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/transform.glsl FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/types.glsl diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index bbcdca367d5df..05be9d20133b3 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include "flutter/fml/paths.h" @@ -25,16 +27,36 @@ const uint32_t kFragBindingBase = 128; const size_t kNumUniformKinds = static_cast(shaderc_uniform_kind::shaderc_uniform_kind_buffer) + 1; +static uint32_t ParseMSLVersion(const std::string& msl_version) { + std::stringstream sstream(msl_version); + std::string version_part; + uint32_t major = 1; + uint32_t minor = 2; + uint32_t patch = 0; + if (std::getline(sstream, version_part, '.')) { + major = std::stoi(version_part); + if (std::getline(sstream, version_part, '.')) { + minor = std::stoi(version_part); + if (std::getline(sstream, version_part, '.')) { + patch = std::stoi(version_part); + } + } + } + if (major < 1 || minor < 2) { + std::cerr << "--metal-version version must be at least 1.2. Have " + << msl_version << std::endl; + } + return spirv_cross::CompilerMSL::Options::make_msl_version(major, minor, + patch); +} + static CompilerBackend CreateMSLCompiler(const spirv_cross::ParsedIR& ir, const SourceOptions& source_options) { auto sl_compiler = std::make_shared(ir); spirv_cross::CompilerMSL::Options sl_options; sl_options.platform = TargetPlatformToMSLPlatform(source_options.target_platform); - // If this version specification changes, the GN rules that process the - // Metal to AIR must be updated as well. - sl_options.msl_version = - spirv_cross::CompilerMSL::Options::make_msl_version(1, 2); + sl_options.msl_version = ParseMSLVersion(source_options.metal_version); sl_options.use_framebuffer_fetch_subpasses = true; sl_compiler->set_msl_options(sl_options); @@ -357,9 +379,9 @@ Compiler::Compiler(const fml::Mapping& source_mapping, shaderc_optimization_level::shaderc_optimization_level_performance); spirv_options.SetTargetEnvironment( shaderc_target_env::shaderc_target_env_vulkan, - shaderc_env_version::shaderc_env_version_vulkan_1_0); + shaderc_env_version::shaderc_env_version_vulkan_1_1); spirv_options.SetTargetSpirv( - shaderc_spirv_version::shaderc_spirv_version_1_0); + shaderc_spirv_version::shaderc_spirv_version_1_3); break; case TargetPlatform::kRuntimeStageMetal: case TargetPlatform::kRuntimeStageGLES: @@ -437,7 +459,11 @@ Compiler::Compiler(const fml::Mapping& source_mapping, << ShaderCErrorToString(spv_result_->GetCompilationStatus()) << ". " << spv_result_->GetNumErrors() << " error(s) and " << spv_result_->GetNumWarnings() << " warning(s)."; - if (spv_result_->GetNumErrors() > 0 || spv_result_->GetNumWarnings() > 0) { + // It should normally be enough to check that there are errors or warnings, + // but some cases result in no errors or warnings and still have an error + // message. If there's a message we should print it. + if (spv_result_->GetNumErrors() > 0 || spv_result_->GetNumWarnings() > 0 || + !spv_result_->GetErrorMessage().empty()) { COMPILER_ERROR_NO_PREFIX << spv_result_->GetErrorMessage(); } return; diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index f25ecd3f86509..e50a5d76db94d 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -74,6 +74,7 @@ bool Main(const fml::CommandLine& command_line) { switches.entry_point); options.json_format = switches.json_format; options.gles_language_version = switches.gles_language_version; + options.metal_version = switches.metal_version; Reflector::Options reflector_options; reflector_options.target_platform = switches.target_platform; diff --git a/impeller/compiler/shader_lib/impeller/BUILD.gn b/impeller/compiler/shader_lib/impeller/BUILD.gn index 99ca69f472f7c..857c272d60324 100644 --- a/impeller/compiler/shader_lib/impeller/BUILD.gn +++ b/impeller/compiler/shader_lib/impeller/BUILD.gn @@ -10,6 +10,7 @@ copy("impeller") { "constants.glsl", "gaussian.glsl", "gradient.glsl", + "path.glsl", "texture.glsl", "transform.glsl", "types.glsl", diff --git a/impeller/compiler/shader_lib/impeller/path.glsl b/impeller/compiler/shader_lib/impeller/path.glsl new file mode 100644 index 0000000000000..c34081de2c9df --- /dev/null +++ b/impeller/compiler/shader_lib/impeller/path.glsl @@ -0,0 +1,68 @@ +// 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. + +#ifndef PATH_GLSL_ +#define PATH_GLSL_ + +#define MOVE 0 +#define LINE 1 +#define QUAD 2 +#define CUBIC 3 + +struct LineData { + vec2 p1; + vec2 p2; +}; + +struct QuadData { + vec2 p1; + vec2 cp; + vec2 p2; +}; + +struct CubicData { + vec2 p1; + vec2 cp1; + vec2 cp2; + vec2 p2; +}; + +struct Position { + uint index; + uint count; +}; + +/// Solve for point on a quadratic Bezier curve defined by starting point `p1`, +/// control point `cp`, and end point `p2` at time `t`. +vec2 QuadraticSolve(QuadData quad, float t) { + return (1.0 - t) * (1.0 - t) * quad.p1 + // + 2.0 * (1.0 - t) * t * quad.cp + // + t * t * quad.p2; +} + +vec2 CubicSolve(CubicData cubic, float t) { + return (1. - t) * (1. - t) * (1. - t) * cubic.p1 + // + 3 * (1. - t) * (1. - t) * t * cubic.cp1 + // + 3 * (1. - t) * t * t * cubic.cp2 + // + t * t * t * cubic.p2; +} + +/// Used to approximate quadratic curves using parabola. +/// +/// See +/// https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html +float ApproximateParabolaIntegral(float x) { + float d = 0.67; + return x / (1.0 - d + sqrt(sqrt(pow(d, 4) + 0.25 * x * x))); +} + +bool isfinite(float f) { + return !isnan(f) && !isinf(f); +} + +float Cross(vec2 p1, vec2 p2) { + return p1.x * p2.y - p1.y * p2.x; +} + +#endif diff --git a/impeller/compiler/source_options.h b/impeller/compiler/source_options.h index ccd264562a3d0..c10e97f1f178b 100644 --- a/impeller/compiler/source_options.h +++ b/impeller/compiler/source_options.h @@ -27,6 +27,7 @@ struct SourceOptions { uint32_t gles_language_version = 100; std::vector defines; bool json_format = false; + std::string metal_version; SourceOptions(); diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index 8fbb9e65e8465..27fe2a6bf368f 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -131,6 +131,8 @@ Switches::Switches(const fml::CommandLine& command_line) gles_language_version( stoi(command_line.GetOptionValueWithDefault("gles-language-version", "0"))), + metal_version( + command_line.GetOptionValueWithDefault("metal-version", "1.2")), entry_point( command_line.GetOptionValueWithDefault("entry-point", "main")) { auto language = diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index 01774285e7f27..f9703f2e766b8 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -34,6 +34,7 @@ struct Switches { bool json_format; SourceLanguage source_language = SourceLanguage::kUnknown; uint32_t gles_language_version; + std::string metal_version; std::string entry_point; Switches(); diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index a2a73ccc843a5..8dee5288196f8 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -5,6 +5,22 @@ import("//flutter/impeller/tools/impeller.gni") import("//flutter/testing/testing.gni") +if (impeller_enable_vulkan || impeller_enable_metal) { + impeller_shaders("shader_subgroup_fixtures") { + enable_opengles = false + name = "subgroup_fixtures" + + # Only need 2.1 for desktop. Running on iOS would require 2.4 + metal_version = "2.1" + + shaders = [ + "cubic_to_quads.comp", + "quad_polyline.comp", + "stroke.comp", + ] + } +} + impeller_shaders("shader_fixtures") { name = "fixtures" @@ -70,10 +86,12 @@ test_fixtures("file_fixtures") { "bay_bridge.jpg", "blue_noise.png", "boston.jpg", + "cubic_to_quads.comp", "embarcadero.jpg", "flutter_logo_baked.glb", "kalimba.jpg", "multiple_stages.hlsl", + "quad_polyline.comp", "resources_limit.vert", "sample.comp", "sample.frag", @@ -85,6 +103,7 @@ test_fixtures("file_fixtures") { "sa%m#ple.vert", "stage1.comp", "stage2.comp", + "stroke.comp", "struct_def_bug.vert", "table_mountain_nx.png", "table_mountain_ny.png", @@ -117,4 +136,8 @@ group("fixtures") { ":scene_fixtures", ":shader_fixtures", ] + + if (impeller_enable_vulkan || impeller_enable_metal) { + public_deps += [ ":shader_subgroup_fixtures" ] + } } diff --git a/impeller/fixtures/cubic_to_quads.comp b/impeller/fixtures/cubic_to_quads.comp new file mode 100644 index 0000000000000..723e2e3a280bd --- /dev/null +++ b/impeller/fixtures/cubic_to_quads.comp @@ -0,0 +1,83 @@ +// 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. +#extension GL_KHR_shader_subgroup_arithmetic : enable + +layout(local_size_x = 512, local_size_y = 1) in; +layout(std430) buffer; + +#include + +layout(binding = 0) readonly buffer Cubics { + uint count; + CubicData data[]; +} +cubics; + +layout(binding = 1) buffer Quads { + uint count; + QuadData data[]; +} +quads; + +uniform Config { + float accuracy; +} +config; + +shared uint quad_counts[512]; +shared uint count_sums[512]; + +void main() { + uint ident = gl_GlobalInvocationID.x; + if (ident >= cubics.count) { + return; + } + + // The maximum error, as a vector from the cubic to the best approximating + // quadratic, is proportional to the third derivative, which is constant + // across the segment. Thus, the error scales down as the third power of + // the number of subdivisions. Our strategy then is to subdivide `t` evenly. + // + // This is an overestimate of the error because only the component + // perpendicular to the first derivative is important. But the simplicity is + // appealing. + + // This magic number is the square of 36 / sqrt(3). + // See: http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html + float max_hypot2 = 432.0 * config.accuracy * config.accuracy; + + CubicData cubic = cubics.data[ident]; + + vec2 err_v = 3.0 * (cubic.cp2 - cubic.cp1) + cubic.p1 - cubic.p2; + float err = dot(err_v, err_v); + float quad_count = max(1., ceil(pow(err * (1.0 / max_hypot2), 1. / 6.0))); + + quad_counts[ident] = uint(quad_count); + + barrier(); + count_sums[ident] = subgroupInclusiveAdd(quad_counts[ident]); + + quads.count = count_sums[cubics.count - 1]; + for (uint i = 0; i < quad_count; i++) { + float t0 = i / quad_count; + float t1 = (i + 1) / quad_count; + + // calculate the subsegment + vec2 sub_p1 = CubicSolve(cubic, t0); + vec2 sub_p2 = CubicSolve(cubic, t1); + QuadData quad = QuadData(3.0 * (cubic.cp1 - cubic.p1), // + 3.0 * (cubic.cp2 - cubic.cp1), // + 3.0 * (cubic.p2 - cubic.cp2)); + float sub_scale = (t1 - t0) * (1.0 / 3.0); + vec2 sub_cp1 = sub_p1 + sub_scale * QuadraticSolve(quad, t0); + vec2 sub_cp2 = sub_p2 - sub_scale * QuadraticSolve(quad, t1); + + vec2 quad_p1x2 = 3.0 * sub_cp1 - sub_p1; + vec2 quad_p2x2 = 3.0 * sub_cp2 - sub_p2; + uint offset = count_sums[ident] - uint(quad_count); + quads.data[offset + i] = QuadData(sub_p1, // + ((quad_p1x2 + quad_p2x2) / 4.0), // + sub_p2); + } +} diff --git a/impeller/fixtures/golden_heart.h b/impeller/fixtures/golden_heart.h new file mode 100644 index 0000000000000..def3f465d4593 --- /dev/null +++ b/impeller/fixtures/golden_heart.h @@ -0,0 +1,321 @@ +// 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. + +#include + +#include "impeller/geometry/path_component.h" +#include "impeller/geometry/point.h" + +namespace impeller { +namespace testing { + +// A heart shape with cubics. +// M140 20 C73 20 20 74 20 140 C20 275 156 310 248 443C336 311 477 270 477 +// 140C477 74 423 20 357 20C309 20 267 48 248 89C229 48 188 20 140 20Z + +std::vector golden_heart_cubics = { + {Point(140, 20), Point(73, 20), Point(20, 74), Point(20, 140)}, + {Point(20, 140), Point(20, 275), Point(156, 310), Point(248, 443)}, + {Point(248, 443), Point(336, 311), Point(477, 270), Point(477, 140)}, + {Point(477, 140), Point(477, 74), Point(423, 20), Point(357, 20)}, + {Point(357, 20), Point(309, 20), Point(267, 48), Point(248, 89)}, + {Point(248, 89), Point(229, 48), Point(188, 20), Point(140, 20)}}; + +// A heart shape with quads, created from the cubics here: +// M140 20C73 20 20 74 20 140C20 275 156 310 248 443C336 311 477 270 477 +// 140C477 74 423 20 357 20C309 20 267 48 248 89C229 48 188 20 140 20Z +// The values here are slightly less precise than what the polyline code +// actually generates for easier storage and reading, but still precise enough +// to work visually. +std::vector golden_heart_quads = { + {{140, 20}, {114.723, 20.1641}, {92.9844, 29.4688}}, + {{92.9844, 29.4688}, {71.2461, 38.7734}, {54.875, 55.25}}, + {{54.875, 55.25}, {38.5039, 71.7266}, {29.3281, 93.4062}}, + {{29.3281, 93.4062}, {20.1523, 115.086}, {20, 140}}, + {{20, 140}, {20.2083, 173.521}, {30.5, 200.083}}, + {{30.5, 200.083}, {40.7917, 226.646}, {58.6667, 249}}, + {{58.6667, 249}, {76.5417, 271.354}, {99.5, 292.25}}, + {{99.5, 292.25}, {122.458, 313.146}, {148, 335.333}}, + {{148, 335.333}, {173.542, 357.521}, {199.167, 383.75}}, + {{199.167, 383.75}, {224.792, 409.979}, {248, 443}}, + {{248, 443}, {270.225, 410.208}, {295.519, 383.75}}, + {{295.519, 383.75}, {320.812, 357.292}, {346.481, 334.667}}, + {{346.481, 334.667}, {372.15, 312.042}, {395.5, 290.75}}, + {{395.5, 290.75}, {418.85, 269.458}, {437.185, 247}}, + {{437.185, 247}, {455.521, 224.542}, {466.148, 198.417}}, + {{466.148, 198.417}, {476.775, 172.292}, {477, 140}}, + {{477, 140}, {476.836, 115.086}, {467.531, 93.4062}}, + {{467.531, 93.4062}, {458.227, 71.7266}, {441.75, 55.25}}, + {{441.75, 55.25}, {425.273, 38.7734}, {403.594, 29.4688}}, + {{403.594, 29.4688}, {381.914, 20.1641}, {357, 20}}, + {{357, 20}, {332.843, 20.1389}, {311.63, 28.7778}}, + {{311.63, 28.7778}, {290.417, 37.4167}, {274.037, 52.8889}}, + {{274.037, 52.8889}, {257.657, 68.3611}, {248, 89}}, + {{248, 89}, {238.361, 68.3611}, {222.222, 52.8889}}, + {{222.222, 52.8889}, {206.083, 37.4167}, {185.111, 28.7778}}, + {{185.111, 28.7778}, {164.139, 20.1389}, {140, 20}}}; + +// In local testing, CPU and GPU versions of the polyline algorithm +// disagree on number of points, but both produce visually acceptable +// results. +// +// These points can be verified in an SVG like the following: +// +// +// +// +// +// Where points looks like 140, 20 130.231, 20.415 etc. The values here are +// slightly less precise than what the polyline code actually generates for +// easier storage and reading, but still precise enough to work visually. +std::vector golden_heart_points = { + {140, 20}, {131.765, 20.3023}, {123.672, 21.1078}, + {115.734, 22.4239}, {107.965, 24.2554}, {100.378, 26.6041}, + {92.9844, 29.4688}, {85.9188, 32.7537}, {79.1253, 36.4472}, + {72.615, 40.5481}, {66.3979, 45.0528}, {60.4823, 49.956}, + {54.875, 55.25}, {49.6196, 60.8847}, {44.7551, 66.8194}, + {40.2891, 73.0478}, {36.2272, 79.5616}, {32.5731, 86.3513}, + {29.3281, 93.4062}, {26.5067, 100.762}, {24.1924, 108.297}, + {22.387, 116.001}, {21.0895, 123.861}, {20.296, 131.865}, + {20, 140}, {20.2534, 149.197}, {20.906, 158.214}, + {21.9689, 167.034}, {23.4519, 175.646}, {25.3637, 184.033}, + {27.7113, 192.183}, {30.5, 200.083}, {34.1384, 208.814}, + {38.2033, 217.323}, {42.6928, 225.603}, {47.6033, 233.646}, + {52.9299, 241.447}, {58.6667, 249}, {66.101, 257.978}, + {73.9106, 266.793}, {82.0865, 275.443}, {90.6194, 283.929}, + {99.5, 292.25}, {115.262, 306.476}, {131.433, 320.838}, + {148, 335.333}, {160.921, 346.811}, {173.757, 358.715}, + {186.506, 371.032}, {199.167, 383.75}, {207.755, 392.805}, + {216.163, 402.198}, {224.391, 411.923}, {232.439, 421.971}, + {240.309, 432.333}, {248, 443}, {255.407, 432.366}, + {263.018, 422.027}, {270.835, 411.988}, {278.857, 402.258}, + {287.085, 392.843}, {295.519, 383.75}, {308.072, 370.878}, + {320.75, 358.4}, {333.554, 346.326}, {346.481, 334.667}, + {363.181, 319.87}, {379.524, 305.229}, {395.5, 290.75}, + {404.531, 282.291}, {413.224, 273.686}, {421.57, 264.936}, + {429.56, 256.04}, {437.185, 247}, {443.055, 239.45}, + {448.516, 231.675}, {453.562, 223.678}, {458.186, 215.466}, + {462.382, 207.043}, {466.148, 198.417}, {469.016, 190.697}, + {471.436, 182.748}, {473.412, 174.582}, {474.949, 166.212}, + {476.053, 157.649}, {476.734, 148.908}, {477, 140}, + {476.697, 131.863}, {475.889, 123.857}, {474.571, 115.997}, + {472.739, 108.293}, {470.392, 100.759}, {467.531, 93.4062}, + {464.244, 86.3502}, {460.548, 79.5599}, {456.446, 73.0461}, + {451.942, 66.8181}, {447.041, 60.884}, {441.75, 55.25}, + {436.116, 49.9595}, {430.182, 45.0581}, {423.954, 40.5537}, + {417.44, 36.452}, {410.65, 32.7565}, {403.594, 29.4688}, + {396.241, 26.6084}, {388.707, 24.2609}, {381.004, 22.4286}, + {373.143, 21.1106}, {365.137, 20.3032}, {357, 20}, + {349.102, 20.2777}, {341.323, 21.0241}, {333.676, 22.2452}, + {326.171, 23.9443}, {318.819, 26.1224}, {311.63, 28.7778}, + {304.722, 31.8337}, {298.058, 35.2778}, {291.65, 39.1093}, + {285.505, 43.3251}, {279.632, 47.9205}, {274.037, 52.8889}, + {268.767, 58.1866}, {263.861, 63.7819}, {259.326, 69.6689}, + {255.169, 75.8403}, {251.393, 82.2873}, {248, 89}, + {244.622, 82.2933}, {240.874, 75.8498}, {236.755, 69.6796}, + {232.27, 63.7913}, {227.423, 58.1924}, {222.222, 52.8889}, + {216.709, 47.922}, {210.917, 43.3271}, {204.854, 39.1112}, + {198.525, 35.2793}, {191.941, 31.8344}, {185.111, 28.7778}, + {177.988, 26.1195}, {170.693, 23.9406}, {163.234, 22.242}, + {155.625, 21.0222}, {147.876, 20.277}, {140, 20}}; + +std::vector golden_heart_vertices = { + {139.95, 19.5025}, {140.05, 20.4975}, {131.715, 19.8048}, + {131.815, 20.7998}, {131.683, 19.809}, {131.847, 20.7956}, + {123.59, 20.6145}, {123.753, 21.601}, {123.557, 20.6211}, + {123.786, 21.5944}, {115.619, 21.9372}, {115.848, 22.9105}, + {115.586, 21.9462}, {115.882, 22.9015}, {107.817, 23.7777}, + {108.113, 24.733}, {107.784, 23.7891}, {108.146, 24.7215}, + {100.197, 26.1378}, {100.558, 27.0703}, {100.167, 26.1506}, + {100.589, 27.0574}, {92.7736, 29.0154}, {93.1952, 29.9221}, + {92.7456, 29.0295}, {93.2232, 29.908}, {85.68, 32.3144}, + {86.1576, 33.193}, {85.6523, 32.3306}, {86.1853, 33.1768}, + {78.8588, 36.0242}, {79.3918, 36.8703}, {78.8319, 36.0423}, + {79.4187, 36.8521}, {72.3216, 40.1432}, {72.9084, 40.9529}, + {72.2959, 40.1631}, {72.9341, 40.933}, {66.0788, 44.6679}, + {66.717, 45.4378}, {66.0546, 44.6893}, {66.7411, 45.4164}, + {60.139, 49.5924}, {60.8255, 50.3196}, {60.1166, 49.615}, + {60.8479, 50.297}, {54.5094, 54.909}, {55.2406, 55.591}, + {54.4883, 54.933}, {55.2617, 55.567}, {49.2329, 60.5677}, + {50.0063, 61.2016}, {49.2133, 60.5933}, {50.026, 61.176}, + {44.3488, 66.5281}, {45.1614, 67.1108}, {44.3308, 66.5549}, + {45.1794, 67.084}, {39.8648, 72.7832}, {40.7133, 73.3123}, + {39.8488, 72.8108}, {40.7293, 73.2847}, {35.7869, 79.3246}, + {36.6675, 79.7985}, {35.7729, 79.3526}, {36.6815, 79.7705}, + {32.1188, 86.1424}, {33.0273, 86.5603}, {32.1062, 86.1723}, + {33.0399, 86.5304}, {28.8613, 93.2272}, {29.795, 93.5853}, + {28.8502, 93.2595}, {29.8061, 93.553}, {26.0288, 100.615}, + {26.9847, 100.908}, {26.0199, 100.648}, {26.9935, 100.876}, + {23.7056, 108.183}, {24.6792, 108.411}, {23.6991, 108.215}, + {24.6857, 108.378}, {21.8937, 115.919}, {22.8804, 116.082}, + {21.8895, 115.951}, {22.8846, 116.05}, {20.592, 123.812}, + {21.5871, 123.91}, {20.5899, 123.843}, {21.5892, 123.879}, + {19.7964, 131.847}, {20.7957, 131.883}, {19.7962, 131.879}, + {20.7958, 131.851}, {19.5002, 140.014}, {20.4998, 139.986}, + {19.5013, 140.036}, {20.4987, 139.964}, {19.7547, 149.234}, + {20.7521, 149.161}, {19.757, 149.257}, {20.7498, 149.138}, + {20.4096, 158.273}, {21.4024, 158.154}, {20.4133, 158.299}, + {21.3987, 158.129}, {21.4761, 167.119}, {22.4616, 166.95}, + {21.4814, 167.146}, {22.4564, 166.923}, {22.9644, 175.757}, + {23.9394, 175.535}, {22.9715, 175.784}, {23.9324, 175.507}, + {24.8833, 184.172}, {25.8442, 183.895}, {24.8923, 184.2}, + {25.8352, 183.867}, {27.2398, 192.35}, {28.1828, 192.017}, + {27.2498, 192.376}, {28.1728, 191.991}, {30.0385, 200.276}, + {30.9615, 199.891}, {30.0488, 200.299}, {30.9512, 199.868}, + {33.6872, 209.03}, {34.5896, 208.599}, {33.6989, 209.053}, + {34.5779, 208.576}, {37.7638, 217.562}, {38.6429, 217.085}, + {37.7766, 217.584}, {38.6301, 217.063}, {42.266, 225.863}, + {43.1195, 225.342}, {42.2799, 225.885}, {43.1057, 225.321}, + {47.1903, 233.928}, {48.0162, 233.364}, {47.2051, 233.949}, + {48.0014, 233.344}, {52.5317, 241.749}, {53.3281, 241.144}, + {52.5448, 241.766}, {53.315, 241.128}, {58.2816, 249.319}, + {59.0518, 248.681}, {58.2924, 249.332}, {59.0409, 248.668}, + {65.7267, 258.309}, {66.4752, 257.646}, {65.7376, 258.321}, + {66.4644, 257.634}, {73.5471, 267.136}, {74.2739, 266.449}, + {73.558, 267.147}, {74.2631, 266.438}, {81.7338, 275.798}, + {82.439, 275.089}, {81.7445, 275.808}, {82.4283, 275.078}, + {90.2775, 284.294}, {90.9613, 283.564}, {90.2844, 284.3}, + {90.9544, 283.558}, {99.165, 292.621}, {99.835, 291.879}, + {99.168, 292.624}, {99.832, 291.876}, {114.93, 306.849}, + {115.594, 306.102}, {114.933, 306.852}, {115.591, 306.099}, + {131.104, 321.215}, {131.762, 320.462}, {131.101, 321.212}, + {131.765, 320.464}, {147.668, 335.707}, {148.332, 334.96}, + {147.66, 335.7}, {148.34, 334.967}, {160.581, 347.178}, + {161.261, 346.445}, {160.573, 347.171}, {161.268, 346.452}, + {173.409, 359.074}, {174.104, 358.355}, {173.402, 359.068}, + {174.111, 358.362}, {186.151, 371.385}, {186.86, 370.679}, + {186.143, 371.376}, {186.868, 370.688}, {198.804, 384.094}, + {199.529, 383.406}, {198.794, 384.084}, {199.539, 383.417}, + {207.382, 393.138}, {208.127, 392.471}, {207.373, 393.128}, + {208.136, 392.482}, {215.781, 402.522}, {216.544, 401.876}, + {215.772, 402.511}, {216.553, 401.886}, {224, 412.236}, + {224.781, 411.611}, {223.992, 412.226}, {224.789, 411.621}, + {232.041, 422.273}, {232.837, 421.668}, {232.033, 422.263}, + {232.844, 421.678}, {239.903, 432.625}, {240.714, 432.04}, + {240.719, 432.618}, {239.898, 432.047}, {248.41, 443.286}, + {247.59, 442.714}, {248.403, 443.296}, {247.597, 442.704}, + {255.809, 432.663}, {255.004, 432.07}, {255.801, 432.674}, + {255.012, 432.059}, {263.412, 422.334}, {262.623, 421.72}, + {263.404, 422.345}, {262.632, 421.709}, {271.22, 412.306}, + {270.448, 411.67}, {271.211, 412.317}, {270.458, 411.659}, + {279.233, 402.587}, {278.48, 401.929}, {279.223, 402.598}, + {278.49, 401.918}, {287.451, 393.183}, {286.718, 392.503}, + {287.442, 393.192}, {286.727, 392.494}, {295.876, 384.099}, + {295.161, 383.401}, {295.869, 384.106}, {295.168, 383.394}, + {308.423, 371.234}, {307.721, 370.522}, {308.415, 371.242}, + {307.729, 370.514}, {321.094, 358.764}, {320.408, 358.036}, + {321.086, 358.771}, {320.416, 358.029}, {333.889, 346.697}, + {333.219, 345.955}, {333.886, 346.7}, {333.222, 345.952}, + {346.813, 335.041}, {346.15, 334.292}, {346.815, 335.039}, + {346.148, 334.294}, {363.515, 320.242}, {362.848, 319.497}, + {363.517, 320.24}, {362.846, 319.499}, {379.86, 305.6}, + {379.189, 304.859}, {379.866, 305.594}, {379.183, 304.864}, + {395.842, 291.115}, {395.158, 290.385}, {395.852, 291.105}, + {395.148, 290.395}, {404.882, 282.647}, {404.179, 281.936}, + {404.892, 282.636}, {404.169, 281.946}, {413.586, 274.032}, + {412.862, 273.341}, {413.596, 274.021}, {412.852, 273.352}, + {421.942, 265.27}, {421.198, 264.602}, {421.952, 265.258}, + {421.188, 264.613}, {429.943, 256.362}, {429.178, 255.718}, + {429.955, 256.347}, {429.166, 255.733}, {437.58, 247.307}, + {436.79, 246.693}, {437.594, 247.287}, {436.776, 246.713}, + {443.464, 239.737}, {442.646, 239.163}, {443.478, 239.717}, + {442.632, 239.183}, {448.939, 231.941}, {448.094, 231.408}, + {448.952, 231.92}, {448.081, 231.429}, {453.998, 223.923}, + {453.126, 223.433}, {454.01, 223.901}, {453.115, 223.455}, + {458.633, 215.689}, {457.738, 215.243}, {458.644, 215.666}, + {457.727, 215.266}, {462.841, 207.243}, {461.924, 206.843}, + {462.851, 207.217}, {461.914, 206.869}, {466.617, 198.591}, + {465.679, 198.243}, {466.626, 198.562}, {465.67, 198.271}, + {469.494, 190.842}, {468.538, 190.551}, {469.502, 190.814}, + {468.53, 190.579}, {471.922, 182.865}, {470.951, 182.63}, + {471.928, 182.838}, {470.945, 182.657}, {473.904, 174.672}, + {472.921, 174.492}, {473.908, 174.646}, {472.917, 174.518}, + {475.445, 166.275}, {474.453, 166.147}, {475.448, 166.25}, + {474.451, 166.173}, {476.552, 157.688}, {475.555, 157.61}, + {476.553, 157.664}, {475.554, 157.634}, {477.234, 148.922}, + {476.234, 148.893}, {477.234, 148.889}, {476.234, 148.926}, + {477.5, 139.981}, {476.5, 140.019}, {477.497, 139.95}, + {476.503, 140.05}, {477.194, 131.813}, {476.199, 131.913}, + {477.19, 131.78}, {476.204, 131.946}, {476.383, 123.775}, + {475.396, 123.94}, {476.376, 123.742}, {475.403, 123.973}, + {475.058, 115.881}, {474.085, 116.112}, {475.049, 115.848}, + {474.094, 116.145}, {473.216, 108.144}, {472.262, 108.442}, + {473.205, 108.112}, {472.273, 108.474}, {470.858, 100.578}, + {469.926, 100.94}, {470.845, 100.548}, {469.938, 100.97}, + {467.984, 93.1951}, {467.078, 93.6174}, {467.97, 93.1672}, + {467.092, 93.6453}, {464.683, 86.1112}, {463.804, 86.5892}, + {464.667, 86.0838}, {463.821, 86.6167}, {460.971, 79.2935}, + {460.125, 79.8264}, {460.953, 79.267}, {460.143, 79.853}, + {456.851, 72.7531}, {456.041, 73.3392}, {456.832, 72.7278}, + {456.061, 73.3646}, {452.327, 66.4998}, {451.556, 67.1366}, + {452.306, 66.4759}, {451.577, 67.1604}, {447.405, 60.5417}, + {446.676, 61.2262}, {447.383, 60.5195}, {446.698, 61.2485}, + {442.092, 54.8855}, {441.408, 55.6145}, {442.068, 54.8645}, + {441.432, 55.6355}, {436.434, 49.5739}, {435.798, 50.345}, + {436.409, 49.5543}, {435.823, 50.3646}, {430.475, 44.653}, + {429.889, 45.4633}, {430.448, 44.635}, {429.915, 45.4812}, + {424.22, 40.1306}, {423.687, 40.9768}, {424.193, 40.1145}, + {423.715, 40.9929}, {417.679, 36.0127}, {417.201, 36.8911}, + {417.651, 35.9987}, {417.229, 36.9051}, {410.861, 32.3032}, + {410.439, 33.2096}, {410.831, 32.2904}, {410.469, 33.2224}, + {403.775, 29.0028}, {403.412, 29.9347}, {403.742, 28.9914}, + {403.445, 29.9461}, {396.39, 26.131}, {396.092, 27.0857}, + {396.357, 26.1219}, {396.125, 27.0948}, {388.823, 23.7744}, + {388.591, 24.7473}, {388.79, 23.7677}, {388.624, 24.754}, + {381.086, 21.9354}, {380.921, 22.9217}, {381.054, 21.9311}, + {380.953, 22.926}, {373.193, 20.6131}, {373.092, 21.608}, + {373.161, 20.6109}, {373.124, 21.6102}, {365.156, 19.8036}, + {365.118, 20.8029}, {365.119, 19.8035}, {365.154, 20.8029}, + {356.982, 19.5003}, {357.018, 20.4997}, {356.952, 19.5023}, + {357.048, 20.4977}, {349.054, 19.7799}, {349.15, 20.7754}, + {349.023, 19.7839}, {349.181, 20.7714}, {341.244, 20.5304}, + {341.402, 21.5179}, {341.213, 20.5365}, {341.434, 21.5118}, + {333.565, 21.7575}, {333.786, 22.7329}, {333.534, 21.7658}, + {333.818, 22.7246}, {326.028, 23.4649}, {326.312, 24.4237}, + {325.997, 23.4753}, {326.343, 24.4133}, {318.645, 25.6533}, + {318.991, 26.5914}, {318.616, 25.6651}, {319.02, 26.5796}, + {311.427, 28.3205}, {311.832, 29.235}, {311.4, 28.3336}, + {311.859, 29.2219}, {304.492, 31.3895}, {304.951, 32.2778}, + {304.465, 31.4045}, {304.978, 32.2628}, {297.801, 34.8487}, + {298.314, 35.707}, {297.775, 34.8655}, {298.341, 35.6901}, + {291.367, 38.697}, {291.932, 39.5216}, {291.341, 38.7155}, + {291.958, 39.5031}, {285.197, 42.9313}, {285.813, 43.7189}, + {285.173, 42.9512}, {285.837, 43.699}, {279.3, 47.5467}, + {279.964, 48.2944}, {279.278, 47.5679}, {279.987, 48.2732}, + {273.683, 52.5363}, {274.392, 53.2415}, {273.661, 52.5592}, + {274.413, 53.2185}, {268.391, 57.857}, {269.143, 58.5163}, + {268.371, 57.8815}, {269.163, 58.4917}, {263.465, 63.4768}, + {264.257, 64.087}, {263.446, 63.5026}, {264.276, 64.0613}, + {258.912, 69.3896}, {259.741, 69.9483}, {258.895, 69.4162}, + {259.758, 69.9216}, {254.738, 75.5876}, {255.601, 76.093}, + {254.723, 75.6147}, {255.615, 76.0659}, {250.947, 82.0618}, + {251.839, 82.5129}, {251.84, 82.0624}, {250.947, 82.5122}, + {248.447, 88.7751}, {247.553, 89.2249}, {248.432, 88.7486}, + {247.568, 89.2514}, {245.055, 82.0419}, {244.19, 82.5447}, + {245.038, 82.0157}, {244.207, 82.5709}, {241.29, 75.5723}, + {240.458, 76.1274}, {241.271, 75.5469}, {240.476, 76.1528}, + {237.153, 69.3766}, {236.357, 69.9825}, {237.133, 69.3523}, + {236.377, 70.0068}, {232.648, 63.464}, {231.892, 64.1185}, + {232.627, 63.4412}, {231.913, 64.1414}, {227.781, 57.8422}, + {227.067, 58.5424}, {227.758, 57.8209}, {227.089, 58.5638}, + {222.557, 52.5174}, {221.888, 53.2604}, {222.533, 52.4972}, + {221.911, 53.2806}, {217.02, 47.5303}, {216.398, 48.3137}, + {216.995, 47.5114}, {216.424, 48.3325}, {211.203, 42.9166}, + {210.632, 43.7376}, {211.176, 42.8994}, {210.659, 43.7548}, + {205.113, 38.6835}, {204.595, 39.5389}, {205.086, 38.6682}, + {204.622, 39.5542}, {198.757, 34.8362}, {198.294, 35.7223}, + {198.73, 34.8229}, {198.321, 35.7356}, {192.145, 31.378}, + {191.737, 32.2908}, {192.116, 31.3659}, {191.766, 32.3028}, + {185.286, 28.3093}, {184.936, 29.2462}, {185.254, 28.2987}, + {184.968, 29.2569}, {178.132, 25.6404}, {177.845, 26.5986}, + {178.1, 25.632}, {177.877, 26.607}, {170.804, 23.4531}, + {170.582, 24.4281}, {170.772, 23.4469}, {170.614, 24.4343}, + {163.313, 21.7483}, {163.155, 22.7357}, {163.282, 21.7443}, + {163.186, 22.7397}, {155.673, 20.5245}, {155.577, 21.5199}, + {155.642, 20.5225}, {155.607, 21.5219}, {147.893, 19.7773}, + {147.858, 20.7767}, {147.947, 19.782}, {147.805, 20.772}, + {140.071, 19.505}, {139.929, 20.495}, +}; +} // namespace testing +} // namespace impeller diff --git a/impeller/fixtures/quad_polyline.comp b/impeller/fixtures/quad_polyline.comp new file mode 100644 index 0000000000000..3f18813ce60da --- /dev/null +++ b/impeller/fixtures/quad_polyline.comp @@ -0,0 +1,89 @@ +// 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. +#extension GL_KHR_shader_subgroup_arithmetic : enable + +layout(local_size_x = 512, local_size_y = 1) in; +layout(std430) buffer; + +#include + +layout(binding = 0) buffer Quads { + uint count; + QuadData data[]; +} +quads; + +layout(binding = 1) buffer Polyline { + uint count; + vec2 data[]; +} +polyline; + +uniform Config { + float tolerance; +} +config; + +shared uint point_counts[512]; +shared uint count_sums[512]; + +void main() { + uint ident = gl_GlobalInvocationID.x; + if (ident >= quads.count) { + return; + } + + QuadData quad = quads.data[ident]; + float sqrt_tolerance = sqrt(config.tolerance); + + vec2 d01 = quad.cp - quad.p1; + vec2 d12 = quad.p2 - quad.cp; + vec2 dd = d01 - d12; + float c = Cross(quad.p2 - quad.p1, dd); + float x0 = dot(d01, dd) * 1. / c; + float x2 = dot(d12, dd) * 1. / c; + float scale = abs(c / (sqrt(dd.x * dd.x + dd.y * dd.y) * (x2 - x0))); + + float a0 = ApproximateParabolaIntegral(x0); + float a2 = ApproximateParabolaIntegral(x2); + float val = 0.f; + if (isfinite(scale)) { + float da = abs(a2 - a0); + float sqrt_scale = sqrt(scale); + if ((x0 < 0 && x2 < 0) || (x0 >= 0 && x2 >= 0)) { + val = da * sqrt_scale; + } else { + // cusp case + float xmin = sqrt_tolerance / sqrt_scale; + val = sqrt_tolerance * da / ApproximateParabolaIntegral(xmin); + } + } + float u0 = ApproximateParabolaIntegral(a0); + float u2 = ApproximateParabolaIntegral(a2); + float u_scale = 1. / (u2 - u0); + + float line_count = max(1., ceil(0.5 * val / sqrt_tolerance)) + 1.; + float steps = 1. / line_count; + + point_counts[ident] = uint(line_count); + + barrier(); + count_sums[ident] = subgroupInclusiveAdd(point_counts[ident]); + barrier(); + + polyline.count = count_sums[quads.count - 1] + 1; + polyline.data[0] = quads.data[0].p1; + + // In theory this could be unrolled into a separate shader, but in practice + // line_count usually pretty low and currently lack benchmark data to show + // how much it would even help. + for (uint i = 1; i < line_count; i += 1) { + float u = i * steps; + float a = a0 + (a2 - a0) * u; + float t = (ApproximateParabolaIntegral(a) - u0) * u_scale; + uint offset = count_sums[ident] - uint(line_count); + polyline.data[offset + i] = QuadraticSolve(quad, t); + } + polyline.data[count_sums[ident]] = quad.p2; +} diff --git a/impeller/fixtures/stroke.comp b/impeller/fixtures/stroke.comp new file mode 100644 index 0000000000000..5bb16d76a1731 --- /dev/null +++ b/impeller/fixtures/stroke.comp @@ -0,0 +1,49 @@ +// 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. +#extension GL_KHR_shader_subgroup_arithmetic : enable + +#define N_ROWS 8 +#define LG_WG_SIZE 9 +#define WG_SIZE (1 << LG_WG_SIZE) + +layout(local_size_x = WG_SIZE, local_size_y = 1) in; +layout(std430) buffer; + +layout(binding = 0) buffer Polyline { + uint count; + vec2 data[]; +} +polyline; + +layout(binding = 1) buffer VertexBuffer { + vec2 position[]; +} +vertex_buffer; + +uniform Config { + float width; + uint cap; + uint join; + float miter_limit; +} +config; + +vec2 compute_offset(uint index) { + vec2 direction = normalize(polyline.data[index + 1] - polyline.data[index]); + return vec2(-direction.y, direction.x) * config.width * .5; +} + +void main() { + uint ident = gl_GlobalInvocationID.x; + if (ident >= polyline.count || ident == 0) { + return; + } + + vec2 offset = compute_offset(ident); + uint index = ident - 1; + vertex_buffer.position[index * 4 + 0] = polyline.data[ident - 1] + offset; + vertex_buffer.position[index * 4 + 1] = polyline.data[ident - 1] - offset; + vertex_buffer.position[index * 4 + 2] = polyline.data[ident] + offset; + vertex_buffer.position[index * 4 + 3] = polyline.data[ident] - offset; +} diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index 8940b5baef8f8..b6c26fcad9e2b 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -50,6 +50,10 @@ impeller_component("playground") { "//third_party/imgui:imgui_glfw", ] + if (impeller_enable_vulkan || impeller_enable_metal) { + public_deps += [ "../fixtures:shader_subgroup_fixtures" ] + } + public_configs = [ ":playground_config" ] if (is_mac) { diff --git a/impeller/playground/backend/metal/playground_impl_mtl.mm b/impeller/playground/backend/metal/playground_impl_mtl.mm index 4b94c68b3dcf2..1e900d7da7bdd 100644 --- a/impeller/playground/backend/metal/playground_impl_mtl.mm +++ b/impeller/playground/backend/metal/playground_impl_mtl.mm @@ -17,6 +17,7 @@ #include "impeller/entity/mtl/entity_shaders.h" #include "impeller/entity/mtl/modern_shaders.h" #include "impeller/fixtures/mtl/fixtures_shaders.h" +#include "impeller/fixtures/mtl/subgroup_fixtures_shaders.h" #include "impeller/playground/imgui/mtl/imgui_shaders.h" #include "impeller/renderer/backend/metal/context_mtl.h" #include "impeller/renderer/backend/metal/formats_mtl.h" @@ -39,6 +40,9 @@ impeller_modern_shaders_length), std::make_shared(impeller_fixtures_shaders_data, impeller_fixtures_shaders_length), + std::make_shared( + impeller_subgroup_fixtures_shaders_data, + impeller_subgroup_fixtures_shaders_length), std::make_shared(impeller_imgui_shaders_data, impeller_imgui_shaders_length), std::make_shared(impeller_scene_shaders_data, diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 105d6144f33ad..2ac5e90b745fb 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -112,8 +112,11 @@ impeller_component("renderer_unittests") { "renderer_unittests.cc", ] - if (impeller_enable_metal || impeller_enable_vulkan) { - sources += [ "compute_unittests.cc" ] + if (impeller_enable_vulkan || impeller_enable_metal) { + sources += [ + "compute_subgroup_unittests.cc", + "compute_unittests.cc", + ] } deps = [ diff --git a/impeller/renderer/compute_subgroup_unittests.cc b/impeller/renderer/compute_subgroup_unittests.cc new file mode 100644 index 0000000000000..ff4ea32a2bc3e --- /dev/null +++ b/impeller/renderer/compute_subgroup_unittests.cc @@ -0,0 +1,270 @@ +// 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. + +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/time/time_point.h" +#include "flutter/testing/testing.h" +#include "gmock/gmock.h" +#include "impeller/base/strings.h" +#include "impeller/fixtures/cubic_to_quads.comp.h" +#include "impeller/fixtures/golden_heart.h" +#include "impeller/fixtures/quad_polyline.comp.h" +#include "impeller/fixtures/sample.comp.h" +#include "impeller/fixtures/stage1.comp.h" +#include "impeller/fixtures/stage2.comp.h" +#include "impeller/fixtures/stroke.comp.h" +#include "impeller/geometry/path.h" +#include "impeller/geometry/path_component.h" +#include "impeller/playground/compute_playground_test.h" +#include "impeller/renderer/command_buffer.h" +#include "impeller/renderer/compute_command.h" +#include "impeller/renderer/compute_pipeline_builder.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/pipeline_library.h" + +namespace impeller { +namespace testing { +using ComputeTest = ComputePlaygroundTest; +INSTANTIATE_COMPUTE_SUITE(ComputeTest); + +TEST_P(ComputeTest, HeartCubicsToStrokeVertices) { + using CS = CubicToQuadsComputeShader; + using QS = QuadPolylineComputeShader; + using SS = StrokeComputeShader; + + auto context = GetContext(); + ASSERT_TRUE(context); + + auto cmd_buffer = context->CreateCommandBuffer(); + auto pass = cmd_buffer->CreateComputePass(); + ASSERT_TRUE(pass && pass->IsValid()); + + static constexpr size_t kCubicCount = 6; + static constexpr Scalar kAccuracy = .1; + + DeviceBufferDescriptor quad_buffer_desc; + quad_buffer_desc.storage_mode = StorageMode::kHostVisible; + quad_buffer_desc.size = sizeof(CS::Quads); + auto quads = context->GetResourceAllocator()->CreateBuffer(quad_buffer_desc); + quads->SetLabel("Quads"); + + DeviceBufferDescriptor point_buffer_desc; + point_buffer_desc.storage_mode = StorageMode::kHostVisible; + // TODO(dnfield): Size this buffer more accurately. + point_buffer_desc.size = sizeof(QS::Polyline); + auto polyline = + context->GetResourceAllocator()->CreateBuffer(point_buffer_desc); + polyline->SetLabel("polyline"); + + DeviceBufferDescriptor vertex_buffer_desc; + vertex_buffer_desc.storage_mode = StorageMode::kHostVisible; + // TODO(dnfield): Size this buffer more accurately. + vertex_buffer_desc.size = sizeof(SS::VertexBuffer); + auto vertex_buffer = + context->GetResourceAllocator()->CreateBuffer(vertex_buffer_desc); + vertex_buffer->SetLabel("VertexBuffer"); + + { + using CubicPipelineBuilder = ComputePipelineBuilder; + auto pipeline_desc = + CubicPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(pipeline_desc.has_value()); + auto compute_pipeline = + context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get(); + ASSERT_TRUE(compute_pipeline); + + pass->SetGridSize(ISize(1024, 1)); + pass->SetThreadGroupSize(ISize(1024, 1)); + + ComputeCommand cmd; + cmd.label = "Cubic To Quads"; + cmd.pipeline = compute_pipeline; + + CS::Config config{.accuracy = kAccuracy}; + CS::BindConfig(cmd, pass->GetTransientsBuffer().EmplaceUniform(config)); + CS::Cubics gpu_cubics; + + gpu_cubics.count = kCubicCount; + for (size_t i = 0; i < kCubicCount; i++) { + gpu_cubics.data[i] = { + golden_heart_cubics[i].p1, golden_heart_cubics[i].cp1, + golden_heart_cubics[i].cp2, golden_heart_cubics[i].p2}; + } + + CS::BindCubics( + cmd, pass->GetTransientsBuffer().EmplaceStorageBuffer(gpu_cubics)); + CS::BindQuads(cmd, quads->AsBufferView()); + + ASSERT_TRUE(pass->AddCommand(std::move(cmd))); + } + + { + using QuadPipelineBuilder = ComputePipelineBuilder; + auto pipeline_desc = + QuadPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(pipeline_desc.has_value()); + auto compute_pipeline = + context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get(); + ASSERT_TRUE(compute_pipeline); + + pass->SetGridSize(ISize(1024, 1)); + pass->SetThreadGroupSize(ISize(1024, 1)); + + ComputeCommand cmd; + cmd.label = "Quads to Polyline"; + cmd.pipeline = compute_pipeline; + + QS::Config config{.tolerance = kDefaultCurveTolerance}; + QS::BindConfig(cmd, pass->GetTransientsBuffer().EmplaceUniform(config)); + + QS::BindQuads(cmd, quads->AsBufferView()); + QS::BindPolyline(cmd, polyline->AsBufferView()); + + ASSERT_TRUE(pass->AddCommand(std::move(cmd))); + } + + { + using StrokePipelineBuilder = ComputePipelineBuilder; + auto pipeline_desc = + StrokePipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(pipeline_desc.has_value()); + auto compute_pipeline = + context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get(); + ASSERT_TRUE(compute_pipeline); + + pass->SetGridSize(ISize(1024, 1)); + pass->SetThreadGroupSize(ISize(1024, 1)); + + ComputeCommand cmd; + cmd.label = "Stroke"; + cmd.pipeline = compute_pipeline; + + SS::Config config{.width = 1.0f, .cap = 1, .join = 1, .miter_limit = 4.0f}; + SS::BindConfig(cmd, pass->GetTransientsBuffer().EmplaceUniform(config)); + + SS::BindPolyline(cmd, polyline->AsBufferView()); + SS::BindVertexBuffer(cmd, vertex_buffer->AsBufferView()); + + ASSERT_TRUE(pass->AddCommand(std::move(cmd))); + } + + ASSERT_TRUE(pass->EncodeCommands()); + + fml::AutoResetWaitableEvent latch; + ASSERT_TRUE(cmd_buffer->SubmitCommands([&latch, quads, polyline, + vertex_buffer]( + CommandBuffer::Status status) { + EXPECT_EQ(status, CommandBuffer::Status::kCompleted); + + auto* q = reinterpret_cast*>( + quads->AsBufferView().contents); + + EXPECT_EQ(q->count, golden_heart_quads.size()); + for (size_t i = 0; i < golden_heart_quads.size(); i++) { + EXPECT_LT(std::abs(golden_heart_quads[i].p1.x - q->data[i].p1.x), 1e-3); + EXPECT_LT(std::abs(golden_heart_quads[i].p1.y - q->data[i].p1.y), 1e-3); + + EXPECT_LT(std::abs(golden_heart_quads[i].cp.x - q->data[i].cp.x), 1e-3); + EXPECT_LT(std::abs(golden_heart_quads[i].cp.y - q->data[i].cp.y), 1e-3); + + EXPECT_LT(std::abs(golden_heart_quads[i].p2.x - q->data[i].p2.x), 1e-3); + EXPECT_LT(std::abs(golden_heart_quads[i].p2.y - q->data[i].p2.y), 1e-3); + } + + auto* p = reinterpret_cast*>( + polyline->AsBufferView().contents); + EXPECT_EQ(p->count, golden_heart_points.size()); + for (size_t i = 0; i < p->count; i++) { + EXPECT_LT(std::abs(p->data[i].x - golden_heart_points[i].x), 1e-3); + EXPECT_LT(std::abs(p->data[i].y - golden_heart_points[i].y), 1e-3); + } + + auto* v = reinterpret_cast*>( + vertex_buffer->AsBufferView().contents); + for (size_t i = 0; i < golden_heart_vertices.size(); i += 1) { + EXPECT_LT(std::abs(golden_heart_vertices[i].x - v->position[i].x), 1e-3); + EXPECT_LT(std::abs(golden_heart_vertices[i].y - v->position[i].y), 1e-3); + } + + latch.Signal(); + })); + + latch.Wait(); +} + +TEST_P(ComputeTest, QuadsToPolyline) { + using QS = QuadPolylineComputeShader; + auto context = GetContext(); + ASSERT_TRUE(context); + + auto cmd_buffer = context->CreateCommandBuffer(); + auto pass = cmd_buffer->CreateComputePass(); + ASSERT_TRUE(pass && pass->IsValid()); + + static constexpr size_t kQuadCount = 26; + static constexpr size_t kPolylineCount = 1024; + + QS::Quads quads; + quads.count = kQuadCount; + for (size_t i = 0; i < kQuadCount; i++) { + quads.data[i] = {golden_heart_quads[i].p1, golden_heart_quads[i].cp, + golden_heart_quads[i].p2}; + } + + DeviceBufferDescriptor point_buffer_desc; + point_buffer_desc.storage_mode = StorageMode::kHostVisible; + point_buffer_desc.size = sizeof(QS::Polyline); + auto polyline = + context->GetResourceAllocator()->CreateBuffer(point_buffer_desc); + polyline->SetLabel("polyline"); + + { + using QuadPipelineBuilder = ComputePipelineBuilder; + auto pipeline_desc = + QuadPipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(pipeline_desc.has_value()); + auto compute_pipeline = + context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get(); + ASSERT_TRUE(compute_pipeline); + + pass->SetGridSize(ISize(1024, 1)); + pass->SetThreadGroupSize(ISize(1024, 1)); + + ComputeCommand cmd; + cmd.label = "Quads to Polyline"; + cmd.pipeline = compute_pipeline; + + QS::Config config{.tolerance = kDefaultCurveTolerance}; + QS::BindConfig(cmd, pass->GetTransientsBuffer().EmplaceUniform(config)); + + QS::BindQuads(cmd, pass->GetTransientsBuffer().EmplaceStorageBuffer(quads)); + QS::BindPolyline(cmd, polyline->AsBufferView()); + + ASSERT_TRUE(pass->AddCommand(std::move(cmd))); + } + + ASSERT_TRUE(pass->EncodeCommands()); + + fml::AutoResetWaitableEvent latch; + ASSERT_TRUE(cmd_buffer->SubmitCommands( + [&latch, polyline](CommandBuffer::Status status) { + EXPECT_EQ(status, CommandBuffer::Status::kCompleted); + + auto* p = reinterpret_cast*>( + polyline->AsBufferView().contents); + + EXPECT_EQ(p->count, golden_heart_points.size()); + for (size_t i = 0; i < p->count; i++) { + EXPECT_LT(std::abs(p->data[i].x - golden_heart_points[i].x), 1e-3); + EXPECT_LT(std::abs(p->data[i].y - golden_heart_points[i].y), 1e-3); + } + + latch.Signal(); + })); + + latch.Wait(); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/compute_unittests.cc b/impeller/renderer/compute_unittests.cc index 4ae43c04fccbf..ca8162d309293 100644 --- a/impeller/renderer/compute_unittests.cc +++ b/impeller/renderer/compute_unittests.cc @@ -10,6 +10,8 @@ #include "impeller/fixtures/sample.comp.h" #include "impeller/fixtures/stage1.comp.h" #include "impeller/fixtures/stage2.comp.h" +#include "impeller/geometry/path.h" +#include "impeller/geometry/path_component.h" #include "impeller/playground/compute_playground_test.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/compute_command.h" @@ -19,7 +21,6 @@ namespace impeller { namespace testing { - using ComputeTest = ComputePlaygroundTest; INSTANTIATE_COMPUTE_SUITE(ComputeTest); diff --git a/impeller/tools/build_metal_library.py b/impeller/tools/build_metal_library.py index 52865e9550664..ccc96596c2d17 100644 --- a/impeller/tools/build_metal_library.py +++ b/impeller/tools/build_metal_library.py @@ -44,6 +44,11 @@ def main(): choices=['mac', 'ios', 'ios-simulator'], help='Select the platform.' ) + parser.add_argument( + '--metal-version', + required=True, + help='The language standard version to compile for.' + ) args = parser.parse_args() @@ -93,17 +98,17 @@ def main(): # The Metal standard must match the specification in impellerc. if args.platform == 'mac': command += [ - '--std=macos-metal1.2', + '--std=macos-metal%s' % args.metal_version, '-mmacos-version-min=10.14', ] elif args.platform == 'ios': command += [ - '--std=ios-metal1.2', + '--std=ios-metal%s' % args.metal_version, '-mios-version-min=11.0', ] elif args.platform == 'ios-simulator': command += [ - '--std=ios-metal1.2', + '--std=ios-metal%s' % args.metal_version, '-miphonesimulator-version-min=11.0', ] else: diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 2db7cb37877c4..4f864b49a5aa7 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -109,10 +109,14 @@ template("impeller_component") { # @param[required] sources The GLSL (4.60) sources to compiled into the Metal # library. # +# @param[required] metal_version The Metal language version to compile for. +# template("metal_library") { assert(is_ios || is_mac) assert(defined(invoker.name), "Metal library name must be specified.") assert(defined(invoker.sources), "Metal source files must be specified.") + assert(defined(invoker.metal_version), + "Metal language version must be specified.") metal_library_name = invoker.name @@ -147,6 +151,7 @@ template("metal_library") { rebase_path(metal_library_path, root_out_dir), "--depfile", rebase_path(depfile), + "--metal-version=$metal_version", ] if (is_ios) { @@ -295,6 +300,11 @@ template("impellerc") { args += [ "--gles-language-version=$gles_language_version" ] } + if (defined(invoker.metal_version)) { + metal_version = invoker.metal_version + args += [ "--metal-version=$metal_version" ] + } + if (json) { args += [ "--json" ] } @@ -388,6 +398,11 @@ template("impeller_shaders_metal") { assert(defined(invoker.shaders), "Impeller shaders must be specified.") assert(defined(invoker.name), "Name of the shader library must be specified.") + metal_version = "1.2" + if (defined(invoker.metal_version)) { + metal_version = invoker.metal_version + } + shaders_base_name = string_join("", [ invoker.name, @@ -396,6 +411,7 @@ template("impeller_shaders_metal") { impellerc_mtl = "impellerc_$target_name" impellerc(impellerc_mtl) { shaders = invoker.shaders + metal_version = metal_version sl_file_extension = "metal" shader_target_flag = "" defines = [ "IMPELLER_TARGET_METAL" ] @@ -413,6 +429,7 @@ template("impeller_shaders_metal") { mtl_lib = "genlib_$target_name" metal_library(mtl_lib) { name = invoker.name + metal_version = metal_version sources = filter_include(get_target_outputs(":$impellerc_mtl"), [ "*.metal" ]) deps = [ ":$impellerc_mtl" ] @@ -553,6 +570,9 @@ template("impeller_shaders_vk") { impellerc(impellerc_vk) { shaders = invoker.shaders sl_file_extension = "vkspv" + if (defined(invoker.metal_version)) { + metal_version = invoker.metal_version + } # Metal reflectors generate a superset of information. if (impeller_enable_metal) { @@ -595,15 +615,27 @@ template("impeller_shaders_vk") { } template("impeller_shaders") { + metal_version = "1.2" + if (defined(invoker.metal_version)) { + metal_version = invoker.metal_version + } + not_needed([ "metal_version" ]) + + enable_opengles = impeller_enable_opengles + if (defined(invoker.enable_opengles)) { + enable_opengles = invoker.enable_opengles + } + if (impeller_enable_metal) { mtl_shaders = "mtl_$target_name" impeller_shaders_metal(mtl_shaders) { name = invoker.name shaders = invoker.shaders + metal_version = metal_version } } - if (impeller_enable_opengles) { + if (enable_opengles) { analyze = true if (defined(invoker.analyze) && !invoker.analyze) { analyze = false @@ -628,6 +660,7 @@ template("impeller_shaders") { impeller_shaders_vk(vk_shaders) { name = invoker.name shaders = invoker.shaders + metal_version = metal_version } } @@ -641,7 +674,7 @@ template("impeller_shaders") { public_deps += [ ":$mtl_shaders" ] } - if (impeller_enable_opengles) { + if (enable_opengles) { public_deps += [ ":$gles_shaders" ] }