Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
40 changes: 33 additions & 7 deletions impeller/compiler/compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#include <array>
#include <filesystem>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <utility>

#include "flutter/fml/paths.h"
Expand All @@ -25,16 +27,36 @@ const uint32_t kFragBindingBase = 128;
const size_t kNumUniformKinds =
static_cast<int>(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<spirv_cross::CompilerMSL>(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);

Expand Down Expand Up @@ -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);
Comment on lines +382 to +384
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chinmaygarde or @iskakaushik - is this ok?

I'm not sure why we'd be targetting Vulkan 1.0 here but Vulkan 1.1 above.

I'm also not sure I understand the implications of using SPIR-V version 1.0 here. I need 1.3 for subgroups. I think it's probably ok but don't know where to look it up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not ok all the time, I can parameterize this the way I did with the MSL version above.

break;
case TargetPlatform::kRuntimeStageMetal:
case TargetPlatform::kRuntimeStageGLES:
Expand Down Expand Up @@ -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()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the part that fixes flutter/flutter#120241

COMPILER_ERROR_NO_PREFIX << spv_result_->GetErrorMessage();
}
return;
Expand Down
1 change: 1 addition & 0 deletions impeller/compiler/impellerc_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions impeller/compiler/shader_lib/impeller/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ copy("impeller") {
"constants.glsl",
"gaussian.glsl",
"gradient.glsl",
"path.glsl",
"texture.glsl",
"transform.glsl",
"types.glsl",
Expand Down
68 changes: 68 additions & 0 deletions impeller/compiler/shader_lib/impeller/path.glsl
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions impeller/compiler/source_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct SourceOptions {
uint32_t gles_language_version = 100;
std::vector<std::string> defines;
bool json_format = false;
std::string metal_version;

SourceOptions();

Expand Down
2 changes: 2 additions & 0 deletions impeller/compiler/switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
1 change: 1 addition & 0 deletions impeller/compiler/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
23 changes: 23 additions & 0 deletions impeller/fixtures/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -117,4 +136,8 @@ group("fixtures") {
":scene_fixtures",
":shader_fixtures",
]

if (impeller_enable_vulkan || impeller_enable_metal) {
public_deps += [ ":shader_subgroup_fixtures" ]
}
}
83 changes: 83 additions & 0 deletions impeller/fixtures/cubic_to_quads.comp
Original file line number Diff line number Diff line change
@@ -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 <impeller/path.glsl>

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);
}
}
Loading