Implements anti-aliased lines (#164734)

issue: https://github.com/flutter/flutter/issues/138682
design doc:
https://docs.google.com/document/d/19I6ToHCMlSgSava-niFWzMLGJEAd-rYiBQEGOMu8IJg/edit?tab=t.0

This puts an experimental line drawing approach behind the following
flags:
  - iOS: `FLTAntialiasLines` boolean, default NO
- Android: `io.flutter.embedding.android.ImpellerAntialiasLines`
boolean, default false

Right now they just support DrawLines and don't support line caps.

A test was added that works as a playground for vulkan, opengles and
metal. Only the Metal version was turned into a golden test though here.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
gaaclarke
2025-03-24 10:32:11 -07:00
committed by GitHub
parent 4cb18b3e21
commit 84cd384682
58 changed files with 1149 additions and 171 deletions

View File

@@ -161,6 +161,7 @@
../../../flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc
../../../flutter/impeller/entity/contents/filters/matrix_filter_contents_unittests.cc
../../../flutter/impeller/entity/contents/host_buffer_unittests.cc
../../../flutter/impeller/entity/contents/line_contents_unittests.cc
../../../flutter/impeller/entity/contents/test
../../../flutter/impeller/entity/contents/text_contents_unittests.cc
../../../flutter/impeller/entity/contents/tiled_texture_contents_unittests.cc

View File

@@ -51252,6 +51252,8 @@ ORIGIN: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.cc
ORIGIN: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/contents/gradient_generator.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/contents/gradient_generator.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/contents/line_contents.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/contents/line_contents.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/contents/linear_gradient_contents.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/contents/linear_gradient_contents.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/contents/radial_gradient_contents.cc + ../../../flutter/LICENSE
@@ -51357,6 +51359,8 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_unifo
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_ssbo_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_uniform_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/line.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/line.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/rrect_blur.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/rrect_blur.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/runtime_effect.vert + ../../../flutter/LICENSE
@@ -51689,8 +51693,8 @@ ORIGIN: ../../../flutter/impeller/renderer/snapshot.cc + ../../../flutter/LICENS
ORIGIN: ../../../flutter/impeller/renderer/snapshot.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/surface.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/surface.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/texture_mipmap.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/texture_mipmap.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/texture_util.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/texture_util.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/threadgroup_sizing_test.comp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/vertex_buffer_builder.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/vertex_buffer_builder.h + ../../../flutter/LICENSE
@@ -54225,6 +54229,8 @@ FILE: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.cc
FILE: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.h
FILE: ../../../flutter/impeller/entity/contents/gradient_generator.cc
FILE: ../../../flutter/impeller/entity/contents/gradient_generator.h
FILE: ../../../flutter/impeller/entity/contents/line_contents.cc
FILE: ../../../flutter/impeller/entity/contents/line_contents.h
FILE: ../../../flutter/impeller/entity/contents/linear_gradient_contents.cc
FILE: ../../../flutter/impeller/entity/contents/linear_gradient_contents.h
FILE: ../../../flutter/impeller/entity/contents/radial_gradient_contents.cc
@@ -54330,6 +54336,8 @@ FILE: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_uniform
FILE: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_ssbo_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_uniform_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/line.frag
FILE: ../../../flutter/impeller/entity/shaders/line.vert
FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.frag
FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.vert
FILE: ../../../flutter/impeller/entity/shaders/runtime_effect.vert
@@ -54663,8 +54671,8 @@ FILE: ../../../flutter/impeller/renderer/snapshot.cc
FILE: ../../../flutter/impeller/renderer/snapshot.h
FILE: ../../../flutter/impeller/renderer/surface.cc
FILE: ../../../flutter/impeller/renderer/surface.h
FILE: ../../../flutter/impeller/renderer/texture_mipmap.cc
FILE: ../../../flutter/impeller/renderer/texture_mipmap.h
FILE: ../../../flutter/impeller/renderer/texture_util.cc
FILE: ../../../flutter/impeller/renderer/texture_util.h
FILE: ../../../flutter/impeller/renderer/threadgroup_sizing_test.comp
FILE: ../../../flutter/impeller/renderer/vertex_buffer_builder.cc
FILE: ../../../flutter/impeller/renderer/vertex_buffer_builder.h

View File

@@ -231,6 +231,9 @@ struct Settings {
// Whether to lazily initialize impeller PSO state.
bool impeller_enable_lazy_shader_mode = false;
// An experimental mode that antialiases lines.
bool impeller_antialiased_lines = false;
// Log a warning during shell initialization if Impeller is not enabled.
bool warn_on_impeller_opt_out = false;

View File

@@ -91,7 +91,9 @@ sk_sp<DlImage> DlMetalSurfaceProvider::MakeImpellerImage(const sk_sp<DisplayList
void DlMetalSurfaceProvider::InitScreenShotter() const {
if (!snapshotter_) {
snapshotter_.reset(new MetalScreenshotter(/*enable_wide_gamut=*/false));
impeller::PlaygroundSwitches switches;
switches.enable_wide_gamut = false;
snapshotter_.reset(new MetalScreenshotter(switches));
auto typographer = impeller::TypographerContextSkia::Make();
aiks_context_.reset(
new impeller::AiksContext(snapshotter_->GetPlayground().GetContext(), typographer));

View File

@@ -10,6 +10,8 @@ struct Flags {
/// Whether to defer PSO construction until first use. Usage Will introduce
/// raster jank.
bool lazy_shader_mode = false;
/// When turned on DrawLine will use the experimental antialiased path.
bool antialiased_lines = false;
};
} // namespace impeller

View File

@@ -382,6 +382,47 @@ TEST_P(AiksTest, DrawLinesRenderCorrectly) {
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
TEST_P(AiksTest, SimpleExperimentAntialiasLines) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
builder.DrawPaint(DlPaint(DlColor(0xff111111)));
DlPaint paint;
paint.setColor(DlColor::kGreenYellow());
paint.setStrokeWidth(10);
auto draw = [&builder](DlPaint& paint) {
for (auto cap :
{DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
paint.setStrokeCap(cap);
DlPoint origin = {100, 100};
builder.DrawLine(DlPoint(150, 100), DlPoint(250, 100), paint);
for (int d = 15; d < 90; d += 15) {
Matrix m = Matrix::MakeRotationZ(Degrees(d));
Point origin = {100, 100};
Point p0 = {50, 0};
Point p1 = {150, 0};
auto a = origin + m * p0;
auto b = origin + m * p1;
builder.DrawLine(a, b, paint);
}
builder.DrawLine(DlPoint(100, 150), DlPoint(100, 250), paint);
builder.DrawCircle(origin, 35, paint);
builder.DrawLine(DlPoint(250, 250), DlPoint(250, 250), paint);
builder.Translate(250, 0);
}
builder.Translate(-750, 250);
};
draw(paint);
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) {
DisplayListBuilder builder;
DlPaint paint;

View File

@@ -24,6 +24,7 @@
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/contents/framebuffer_blend_contents.h"
#include "impeller/entity/contents/line_contents.h"
#include "impeller/entity/contents/solid_rrect_blur_contents.h"
#include "impeller/entity/contents/text_contents.h"
#include "impeller/entity/contents/texture_contents.h"
@@ -459,9 +460,19 @@ void Canvas::DrawLine(const Point& p0,
entity.SetTransform(GetCurrentTransform());
entity.SetBlendMode(paint.blend_mode);
LineGeometry geom(p0, p1, paint.stroke_width, paint.stroke_cap);
AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint,
/*reuse_depth=*/reuse_depth);
auto geometry = std::make_unique<LineGeometry>(p0, p1, paint.stroke_width,
paint.stroke_cap);
if (renderer_.GetContext()->GetFlags().antialiased_lines &&
!paint.color_filter && !paint.invert_colors && !paint.image_filter &&
!paint.mask_blur_descriptor.has_value() && !paint.color_source) {
auto contents = LineContents::Make(std::move(geometry), paint.color);
entity.SetContents(std::move(contents));
AddRenderEntityToCurrentPass(entity, reuse_depth);
} else {
AddRenderEntityWithFiltersToCurrentPass(entity, geometry.get(), paint,
/*reuse_depth=*/reuse_depth);
}
}
void Canvas::DrawRect(const Rect& rect, const Paint& paint) {

View File

@@ -35,6 +35,8 @@ impeller_shaders("entity_shaders") {
"shaders/gradients/radial_gradient_uniform_fill.frag",
"shaders/gradients/sweep_gradient_fill.frag",
"shaders/gradients/sweep_gradient_uniform_fill.frag",
"shaders/line.frag",
"shaders/line.vert",
"shaders/rrect_blur.vert",
"shaders/rrect_blur.frag",
"shaders/runtime_effect.vert",
@@ -159,6 +161,8 @@ impeller_component("entity") {
"contents/framebuffer_blend_contents.h",
"contents/gradient_generator.cc",
"contents/gradient_generator.h",
"contents/line_contents.cc",
"contents/line_contents.h",
"contents/linear_gradient_contents.cc",
"contents/linear_gradient_contents.h",
"contents/radial_gradient_contents.cc",
@@ -254,6 +258,7 @@ impeller_component("entity_unittests") {
"contents/filters/inputs/filter_input_unittests.cc",
"contents/filters/matrix_filter_contents_unittests.cc",
"contents/host_buffer_unittests.cc",
"contents/line_contents_unittests.cc",
"contents/text_contents_unittests.cc",
"contents/tiled_texture_contents_unittests.cc",
"draw_order_resolver_unittests.cc",

View File

@@ -36,6 +36,15 @@ namespace impeller {
///
class ColorSourceContents : public Contents {
public:
using BindFragmentCallback = std::function<bool(RenderPass& pass)>;
using PipelineBuilderCallback =
std::function<PipelineRef(ContentContextOptions)>;
using CreateGeometryCallback =
std::function<GeometryResult(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Geometry* geom)>;
ColorSourceContents();
~ColorSourceContents() override;
@@ -99,41 +108,21 @@ class ColorSourceContents : public Contents {
// |Contents|
void SetInheritedOpacity(Scalar opacity) override;
protected:
using BindFragmentCallback = std::function<bool(RenderPass& pass)>;
using PipelineBuilderCallback =
std::function<PipelineRef(ContentContextOptions)>;
using CreateGeometryCallback =
std::function<GeometryResult(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Geometry* geom)>;
static GeometryResult DefaultCreateGeometryCallback(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Geometry* geom) {
return geom->GetPositionBuffer(renderer, entity, pass);
}
/// @brief Whether the entity should be treated as non-opaque due to stroke
/// geometry requiring alpha for coverage.
bool AppliesAlphaForStrokeCoverage(const Matrix& transform) const;
template <typename VertexShaderT>
bool DrawGeometry(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const PipelineBuilderCallback& pipeline_callback,
typename VertexShaderT::FrameInfo frame_info,
const BindFragmentCallback& bind_fragment_callback,
bool force_stencil = false,
const CreateGeometryCallback& create_geom_callback =
DefaultCreateGeometryCallback) const {
static bool DrawGeometry(const Contents* contents,
const Geometry* geometry,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const PipelineBuilderCallback& pipeline_callback,
typename VertexShaderT::FrameInfo frame_info,
const BindFragmentCallback& bind_fragment_callback,
bool force_stencil = false,
const CreateGeometryCallback& create_geom_callback =
DefaultCreateGeometryCallback) {
auto options = OptionsFromPassAndEntity(pass, entity);
GeometryResult::Mode geometry_mode = GetGeometry()->GetResultMode();
GeometryResult::Mode geometry_mode = geometry->GetResultMode();
bool do_cover_draw = false;
Rect cover_area = {};
@@ -151,7 +140,7 @@ class ColorSourceContents : public Contents {
/// Stencil preparation draw.
GeometryResult stencil_geometry_result =
GetGeometry()->GetPositionBuffer(renderer, entity, pass);
geometry->GetPositionBuffer(renderer, entity, pass);
if (stencil_geometry_result.vertex_buffer.vertex_count == 0u) {
return true;
}
@@ -194,7 +183,7 @@ class ColorSourceContents : public Contents {
options.blend_mode = entity.GetBlendMode();
options.stencil_mode = ContentContextOptions::StencilMode::kCoverCompare;
std::optional<Rect> maybe_cover_area = GetGeometry()->GetCoverage({});
std::optional<Rect> maybe_cover_area = geometry->GetCoverage({});
if (!maybe_cover_area.has_value()) {
return true;
}
@@ -207,8 +196,7 @@ class ColorSourceContents : public Contents {
RectGeometry geom(cover_area);
geometry_result = create_geom_callback(renderer, entity, pass, &geom);
} else {
geometry_result =
create_geom_callback(renderer, entity, pass, GetGeometry());
geometry_result = create_geom_callback(renderer, entity, pass, geometry);
}
if (geometry_result.vertex_buffer.vertex_count == 0u) {
@@ -261,11 +249,47 @@ class ColorSourceContents : public Contents {
if (geometry_result.mode == GeometryResult::Mode::kPreventOverdraw &&
options.blend_mode != BlendMode::kSrc) {
return RenderClipRestore(renderer, pass, entity.GetClipDepth(),
GetCoverage(entity));
contents->GetCoverage(entity));
}
return true;
}
protected:
static GeometryResult DefaultCreateGeometryCallback(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Geometry* geom) {
return geom->GetPositionBuffer(renderer, entity, pass);
}
/// @brief Whether the entity should be treated as non-opaque due to stroke
/// geometry requiring alpha for coverage.
bool AppliesAlphaForStrokeCoverage(const Matrix& transform) const;
template <typename VertexShaderT>
bool DrawGeometry(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const PipelineBuilderCallback& pipeline_callback,
typename VertexShaderT::FrameInfo frame_info,
const BindFragmentCallback& bind_fragment_callback,
bool force_stencil = false,
const CreateGeometryCallback& create_geom_callback =
DefaultCreateGeometryCallback) const {
//
return DrawGeometry<VertexShaderT>(this, //
GetGeometry(), //
renderer, //
entity, //
pass, //
pipeline_callback, //
frame_info, //
bind_fragment_callback, //
force_stencil, //
create_geom_callback);
}
private:
const Geometry* geometry_ = nullptr;
Matrix inverse_matrix_;

View File

@@ -18,7 +18,7 @@
#include "impeller/renderer/pipeline_descriptor.h"
#include "impeller/renderer/pipeline_library.h"
#include "impeller/renderer/render_target.h"
#include "impeller/renderer/texture_mipmap.h"
#include "impeller/renderer/texture_util.h"
#include "impeller/tessellator/tessellator.h"
#include "impeller/typographer/typographer_context.h"
@@ -329,6 +329,7 @@ ContentContext::ContentContext(
solid_fill_pipelines_.CreateDefault(*context_, options);
texture_pipelines_.CreateDefault(*context_, options);
fast_gradient_pipelines_.CreateDefault(*context_, options);
line_pipelines_.CreateDefault(*context_, options);
if (context_->GetCapabilities()->SupportsSSBO()) {
linear_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options);
@@ -1159,6 +1160,10 @@ PipelineRef ContentContext::GetDrawVerticesUberShader(
return GetPipeline(vertices_uber_shader_, opts);
}
PipelineRef ContentContext::GetLinePipeline(ContentContextOptions opts) const {
return GetPipeline(line_pipelines_, opts);
}
#ifdef IMPELLER_ENABLE_OPENGLES
PipelineRef ContentContext::GetDownsampleTextureGlesPipeline(
ContentContextOptions opts) const {

View File

@@ -41,6 +41,8 @@
#include "impeller/entity/glyph_atlas.frag.h"
#include "impeller/entity/glyph_atlas.vert.h"
#include "impeller/entity/gradient_fill.vert.h"
#include "impeller/entity/line.frag.h"
#include "impeller/entity/line.vert.h"
#include "impeller/entity/linear_gradient_fill.frag.h"
#include "impeller/entity/linear_to_srgb_filter.frag.h"
#include "impeller/entity/morphology_filter.frag.h"
@@ -147,6 +149,7 @@ using FramebufferBlendScreenPipeline = FramebufferBlendPipelineHandle;
using FramebufferBlendSoftLightPipeline = FramebufferBlendPipelineHandle;
using GaussianBlurPipeline = RenderPipelineHandle<FilterPositionUvVertexShader, GaussianFragmentShader>;
using GlyphAtlasPipeline = RenderPipelineHandle<GlyphAtlasVertexShader, GlyphAtlasFragmentShader>;
using LinePipeline = RenderPipelineHandle<LineVertexShader, LineFragmentShader>;
using LinearGradientFillPipeline = GradientPipelineHandle<LinearGradientFillFragmentShader>;
using LinearGradientSSBOFillPipeline = GradientPipelineHandle<LinearGradientSsboFillFragmentShader>;
using LinearGradientUniformFillPipeline = GradientPipelineHandle<LinearGradientUniformFillFragmentShader>;
@@ -352,6 +355,7 @@ class ContentContext {
PipelineRef GetFramebufferBlendSoftLightPipeline(ContentContextOptions opts) const;
PipelineRef GetGaussianBlurPipeline(ContentContextOptions opts) const;
PipelineRef GetGlyphAtlasPipeline(ContentContextOptions opts) const;
PipelineRef GetLinePipeline(ContentContextOptions opts) const;
PipelineRef GetLinearGradientFillPipeline(ContentContextOptions opts) const;
PipelineRef GetLinearGradientSSBOFillPipeline(ContentContextOptions opts) const;
PipelineRef GetLinearGradientUniformFillPipeline(ContentContextOptions opts) const;
@@ -645,6 +649,7 @@ class ContentContext {
mutable Variants<FramebufferBlendSoftLightPipeline> framebuffer_blend_softlight_pipelines_;
mutable Variants<GaussianBlurPipeline> gaussian_blur_pipelines_;
mutable Variants<GlyphAtlasPipeline> glyph_atlas_pipelines_;
mutable Variants<LinePipeline> line_pipelines_;
mutable Variants<LinearGradientFillPipeline> linear_gradient_fill_pipelines_;
mutable Variants<LinearGradientSSBOFillPipeline> linear_gradient_ssbo_fill_pipelines_;
mutable Variants<LinearGradientUniformFillPipeline> linear_gradient_uniform_fill_pipelines_;

View File

@@ -10,6 +10,7 @@
#include "impeller/core/formats.h"
#include "impeller/core/texture.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/texture_util.h"
namespace impeller {
@@ -25,29 +26,9 @@ std::shared_ptr<Texture> CreateGradientTexture(
texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible;
texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
texture_descriptor.size = {gradient_data.texture_size, 1};
auto texture =
context->GetResourceAllocator()->CreateTexture(texture_descriptor);
if (!texture) {
FML_DLOG(ERROR) << "Could not create Impeller texture.";
return nullptr;
}
auto data_mapping =
std::make_shared<fml::DataMapping>(gradient_data.color_bytes);
auto buffer =
context->GetResourceAllocator()->CreateBufferWithCopy(*data_mapping);
auto cmd_buffer = context->CreateCommandBuffer();
auto blit_pass = cmd_buffer->CreateBlitPass();
blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(buffer)), texture);
if (!blit_pass->EncodeCommands() ||
!context->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok()) {
return nullptr;
}
texture->SetLabel(impeller::SPrintF("Gradient(%p)", texture.get()).c_str());
return texture;
return CreateTexture(texture_descriptor, gradient_data.color_bytes, context,
"Gradient");
}
std::vector<StopData> CreateGradientColors(const std::vector<Color>& colors,

View File

@@ -0,0 +1,188 @@
// 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 "impeller/entity/contents/line_contents.h"
#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/color_source_contents.h"
#include "impeller/entity/geometry/rect_geometry.h"
#include "impeller/renderer/texture_util.h"
namespace impeller {
using VS = LinePipeline::VertexShader;
using FS = LinePipeline::FragmentShader;
namespace {
using BindFragmentCallback = std::function<bool(RenderPass& pass)>;
using PipelineBuilderCallback =
std::function<PipelineRef(ContentContextOptions)>;
using CreateGeometryCallback =
std::function<GeometryResult(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Geometry* geometry)>;
const int32_t kCurveResolution = 32;
const Scalar kSampleRadius = 0.5f;
struct LineInfo {
Vector3 e0;
Vector3 e1;
Vector3 e2;
Vector3 e3;
};
LineInfo CalculateLineInfo(Point p0, Point p1, Scalar width, Scalar radius) {
Vector2 diff = p0 - p1;
float k = 2.0 / ((2.0 * radius + width) * sqrt(diff.Dot(diff)));
return LineInfo{
.e0 = Vector3(k * (p0.y - p1.y), //
k * (p1.x - p0.x), //
1.0 + k * (p0.x * p1.y - p1.x * p0.y)),
.e1 = Vector3(
k * (p1.x - p0.x), //
k * (p1.y - p0.y), //
1.0 + k * (p0.x * p0.x + p0.y * p0.y - p0.x * p1.x - p0.y * p1.y)),
.e2 = Vector3(k * (p1.y - p0.y), //
k * (p0.x - p1.x), //
1.0 + k * (p1.x * p0.y - p0.x * p1.y)),
.e3 = Vector3(
k * (p0.x - p1.x), //
k * (p0.y - p1.y), //
1.0 + k * (p1.x * p1.x + p1.y * p1.y - p0.x * p1.x - p0.y * p1.y)),
};
}
uint8_t DoubleToUint8(double x) {
return static_cast<uint8_t>(std::clamp(std::round(x * 255.0), 0.0, 255.0));
}
/// See also: CreateGradientTexture
std::shared_ptr<Texture> CreateCurveTexture(
Scalar width,
const std::shared_ptr<impeller::Context>& context) {
//
impeller::TextureDescriptor texture_descriptor;
texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible;
texture_descriptor.format = PixelFormat::kR8UNormInt;
texture_descriptor.size = {kCurveResolution, 1};
std::vector<uint8_t> curve_data;
curve_data.reserve(kCurveResolution);
for (int i = 0; i < kCurveResolution; ++i) {
double norm = (static_cast<double>(i) + 1.0) / 32.0;
double loc = norm * (kSampleRadius + width / 2.0);
double den = kSampleRadius * 2.0 + 1.0;
curve_data.push_back(DoubleToUint8(loc / den));
}
return CreateTexture(texture_descriptor, curve_data, context, "LineCurve");
}
GeometryResult CreateGeometry(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Geometry* geometry) {
using PerVertexData = LineVertexShader::PerVertexData;
const LineGeometry* line_geometry =
static_cast<const LineGeometry*>(geometry);
auto& transform = entity.GetTransform();
Point corners[4];
if (!LineGeometry::ComputeCorners(
corners, transform,
/*extend_endpoints=*/line_geometry->GetCap() != Cap::kButt,
line_geometry->GetP0(), line_geometry->GetP1(),
line_geometry->GetWidth() + kSampleRadius)) {
return kEmptyResult;
}
auto& host_buffer = renderer.GetTransientsBuffer();
size_t count = 4;
LineInfo line_info =
CalculateLineInfo(line_geometry->GetP0(), line_geometry->GetP1(),
line_geometry->GetWidth(), kSampleRadius);
BufferView vertex_buffer = host_buffer.Emplace(
count * sizeof(PerVertexData), alignof(PerVertexData),
[&corners, &line_info](uint8_t* buffer) {
auto vertices = reinterpret_cast<PerVertexData*>(buffer);
for (auto& corner : corners) {
*vertices++ = {
.position = corner,
.e0 = line_info.e0,
.e1 = line_info.e1,
.e2 = line_info.e2,
.e3 = line_info.e3,
};
}
});
std::shared_ptr<Texture> curve_texture =
CreateCurveTexture(line_geometry->GetWidth(), renderer.GetContext());
SamplerDescriptor sampler_desc;
sampler_desc.min_filter = MinMagFilter::kLinear;
sampler_desc.mag_filter = MinMagFilter::kLinear;
FS::BindCurve(
pass, curve_texture,
renderer.GetContext()->GetSamplerLibrary()->GetSampler(sampler_desc));
return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = vertex_buffer,
.vertex_count = count,
.index_type = IndexType::kNone,
},
.transform = entity.GetShaderTransform(pass),
};
}
} // namespace
std::unique_ptr<LineContents> LineContents::Make(
std::unique_ptr<LineGeometry> geometry,
Color color) {
return std::unique_ptr<LineContents>(
new LineContents(std::move(geometry), color));
}
LineContents::LineContents(std::unique_ptr<LineGeometry> geometry, Color color)
: geometry_(std::move(geometry)), color_(color) {}
bool LineContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
auto& host_buffer = renderer.GetTransientsBuffer();
VS::FrameInfo frame_info;
FS::FragInfo frag_info;
frag_info.color = color_.Premultiply();
PipelineBuilderCallback pipeline_callback =
[&renderer](ContentContextOptions options) {
return renderer.GetLinePipeline(options);
};
return ColorSourceContents::DrawGeometry<VS>(
this, geometry_.get(), renderer, entity, pass, pipeline_callback,
frame_info,
/*bind_fragment_callback=*/
[&frag_info, &host_buffer](RenderPass& pass) {
FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
pass.SetCommandLabel("Line");
return true;
},
/*force_stencil=*/false,
/*create_geom_callback=*/CreateGeometry);
}
std::optional<Rect> LineContents::GetCoverage(const Entity& entity) const {
return geometry_->GetCoverage(entity.GetTransform());
}
} // namespace impeller

View File

@@ -0,0 +1,34 @@
// 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 FLUTTER_IMPELLER_ENTITY_CONTENTS_LINE_CONTENTS_H_
#define FLUTTER_IMPELLER_ENTITY_CONTENTS_LINE_CONTENTS_H_
#include <memory>
#include "flutter/impeller/entity/contents/contents.h"
#include "flutter/impeller/entity/geometry/line_geometry.h"
namespace impeller {
class LineContents : public Contents {
public:
static std::unique_ptr<LineContents> Make(
std::unique_ptr<LineGeometry> geometry,
Color color);
bool Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const override;
std::optional<Rect> GetCoverage(const Entity& entity) const override;
private:
explicit LineContents(std::unique_ptr<LineGeometry> geometry, Color color);
std::unique_ptr<LineGeometry> geometry_;
Color color_;
};
} // namespace impeller
#endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_LINE_CONTENTS_H_

View File

@@ -0,0 +1,33 @@
// 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 "impeller/entity/contents/line_contents.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace impeller {
namespace testing {
TEST(LineContents, Create) {
Path path;
Scalar width = 5.0f;
auto geometry = std::make_unique<LineGeometry>(
/*p0=*/Point{0, 0}, //
/*p1=*/Point{100, 100}, //
/*width=*/width, //
/*cap=*/Cap::kSquare);
std::unique_ptr<LineContents> contents =
LineContents::Make(std::move(geometry), Color(1.f, 0.f, 0.f, 1.f));
EXPECT_TRUE(contents);
Entity entity;
std::optional<Rect> coverage = contents->GetCoverage(entity);
EXPECT_TRUE(coverage.has_value());
if (coverage.has_value()) {
Scalar lip = sqrt((width * width) / 2.f);
EXPECT_EQ(*coverage,
Rect::MakeXYWH(-lip, -lip, 100 + 2 * lip, 100 + 2 * lip));
}
}
} // namespace testing
} // namespace impeller

View File

@@ -119,7 +119,6 @@ class Geometry {
return 1.0;
}
protected:
static GeometryResult ComputePositionGeometry(
const ContentContext& renderer,
const Tessellator::VertexGenerator& generator,

View File

@@ -26,13 +26,16 @@ Scalar LineGeometry::ComputePixelHalfWidth(const Matrix& transform,
}
Vector2 LineGeometry::ComputeAlongVector(const Matrix& transform,
bool allow_zero_length) const {
Scalar stroke_half_width = ComputePixelHalfWidth(transform, width_);
bool allow_zero_length,
Point p0,
Point p1,
Scalar width) {
Scalar stroke_half_width = ComputePixelHalfWidth(transform, width);
if (stroke_half_width < kEhCloseEnough) {
return {};
}
auto along = p1_ - p0_;
auto along = p1 - p0;
Scalar length = along.GetLength();
if (length < kEhCloseEnough) {
if (!allow_zero_length) {
@@ -47,17 +50,20 @@ Vector2 LineGeometry::ComputeAlongVector(const Matrix& transform,
bool LineGeometry::ComputeCorners(Point corners[4],
const Matrix& transform,
bool extend_endpoints) const {
auto along = ComputeAlongVector(transform, extend_endpoints);
bool extend_endpoints,
Point p0,
Point p1,
Scalar width) {
auto along = ComputeAlongVector(transform, extend_endpoints, p0, p1, width);
if (along.IsZero()) {
return false;
}
auto across = Vector2(along.y, -along.x);
corners[0] = p0_ - across;
corners[1] = p1_ - across;
corners[2] = p0_ + across;
corners[3] = p1_ + across;
corners[0] = p0 - across;
corners[1] = p1 - across;
corners[2] = p0 + across;
corners[3] = p1 + across;
if (extend_endpoints) {
corners[0] -= along;
corners[1] += along;
@@ -86,7 +92,8 @@ GeometryResult LineGeometry::GetPositionBuffer(const ContentContext& renderer,
}
Point corners[4];
if (!ComputeCorners(corners, transform, cap_ == Cap::kSquare)) {
if (!ComputeCorners(corners, transform, cap_ == Cap::kSquare, p0_, p1_,
width_)) {
return kEmptyResult;
}
@@ -118,7 +125,8 @@ GeometryResult LineGeometry::GetPositionBuffer(const ContentContext& renderer,
std::optional<Rect> LineGeometry::GetCoverage(const Matrix& transform) const {
Point corners[4];
// Note: MSAA boolean doesn't matter for coverage computation.
if (!ComputeCorners(corners, transform, cap_ != Cap::kButt)) {
if (!ComputeCorners(corners, transform, cap_ != Cap::kButt, p0_, p1_,
width_)) {
return {};
}

View File

@@ -25,7 +25,20 @@ class LineGeometry final : public Geometry {
Scalar ComputeAlphaCoverage(const Matrix& transform) const override;
private:
// |Geometry|
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
Point GetP0() const { return p0_; }
Point GetP1() const { return p1_; }
Scalar GetWidth() const { return width_; }
Cap GetCap() const { return cap_; }
static Vector2 ComputeAlongVector(const Matrix& transform,
bool allow_zero_length,
Point p0,
Point p1,
Scalar width);
// Computes the 4 corners of a rectangle that defines the line and
// possibly extended endpoints which will be rendered under the given
// transform, and returns true if such a rectangle is defined.
@@ -40,21 +53,19 @@ class LineGeometry final : public Geometry {
// if the calling code is planning to draw the round caps on the ends.
//
// @return true if the transform and width were not degenerate
bool ComputeCorners(Point corners[4],
const Matrix& transform,
bool extend_endpoints) const;
Vector2 ComputeAlongVector(const Matrix& transform,
bool allow_zero_length) const;
static bool ComputeCorners(Point corners[4],
const Matrix& transform,
bool extend_endpoints,
Point p0,
Point p1,
Scalar width);
private:
// |Geometry|
GeometryResult GetPositionBuffer(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const override;
// |Geometry|
std::optional<Rect> GetCoverage(const Matrix& transform) const override;
Point p0_;
Point p1_;
Scalar width_;

View File

@@ -13,7 +13,7 @@
#include "impeller/entity/entity_pass_target.h"
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/texture_mipmap.h"
#include "impeller/renderer/texture_util.h"
namespace impeller {

View File

@@ -0,0 +1,44 @@
// 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.
precision mediump float;
#include <impeller/types.glsl>
uniform FragInfo {
vec4 color;
}
frag_info;
uniform sampler2D curve;
highp in vec2 v_position;
// These should be `flat` but that doesn't work in our glsl compiler. It
// shouldn't make any visual difference.
highp in vec3 v_e0;
highp in vec3 v_e1;
highp in vec3 v_e2;
highp in vec3 v_e3;
out vec4 frag_color;
float lookup(float x) {
return texture(curve, vec2(x, 0)).r;
}
float CalculateLine() {
vec3 pos = vec3(v_position.xy, 1.0);
vec4 d = vec4(dot(pos, v_e0), dot(pos, v_e1), dot(pos, v_e2), dot(pos, v_e3));
if (any(lessThan(d, vec4(0.0)))) {
return 0.0;
}
return lookup(min(d.x, d.z)) * lookup(min(d.y, d.w));
}
void main() {
float line = CalculateLine();
frag_color = vec4(frag_info.color.xyz, line);
}

View File

@@ -0,0 +1,32 @@
// 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 <impeller/types.glsl>
uniform FrameInfo {
mat4 mvp;
}
frame_info;
in vec2 position;
in vec3 e0;
in vec3 e1;
in vec3 e2;
in vec3 e3;
out vec2 v_position;
out vec3 v_e0;
out vec3 v_e1;
out vec3 v_e2;
out vec3 v_e3;
void main() {
gl_Position = frame_info.mvp * vec4(position, 0.0, 1.0);
v_position = position;
v_e0 = e0;
v_e1 = e1;
v_e2 = e2;
v_e3 = e3;
}

View File

@@ -155,7 +155,11 @@ void GoldenPlaygroundTest::SetUp() {
setenv("VK_ICD_FILENAMES", icd_path.c_str(), 1);
std::string test_name = GetTestName();
bool enable_wide_gamut = test_name.find("WideGamut_") != std::string::npos;
PlaygroundSwitches switches;
switches.enable_wide_gamut =
test_name.find("WideGamut_") != std::string::npos;
switches.flags.antialiased_lines =
test_name.find("ExperimentAntialiasLines_") != std::string::npos;
switch (GetParam()) {
case PlaygroundBackend::kMetal:
if (!DoesSupportWideGamutTests()) {
@@ -163,12 +167,16 @@ void GoldenPlaygroundTest::SetUp() {
<< "This metal device doesn't support wide gamut golden tests.";
}
pimpl_->screenshotter =
std::make_unique<testing::MetalScreenshotter>(enable_wide_gamut);
std::make_unique<testing::MetalScreenshotter>(switches);
break;
case PlaygroundBackend::kVulkan: {
if (enable_wide_gamut) {
if (switches.enable_wide_gamut) {
GTEST_SKIP() << "Vulkan doesn't support wide gamut golden tests.";
}
if (switches.flags.antialiased_lines) {
GTEST_SKIP()
<< "Vulkan doesn't support antialiased lines golden tests.";
}
const std::unique_ptr<PlaygroundImpl>& playground =
GetSharedVulkanPlayground(/*enable_validations=*/true);
pimpl_->screenshotter =
@@ -176,9 +184,13 @@ void GoldenPlaygroundTest::SetUp() {
break;
}
case PlaygroundBackend::kOpenGLES: {
if (enable_wide_gamut) {
if (switches.enable_wide_gamut) {
GTEST_SKIP() << "OpenGLES doesn't support wide gamut golden tests.";
}
if (switches.flags.antialiased_lines) {
GTEST_SKIP()
<< "OpenGLES doesn't support antialiased lines golden tests.";
}
FML_CHECK(::glfwInit() == GLFW_TRUE);
PlaygroundSwitches playground_switches;
playground_switches.use_angle = true;

View File

@@ -49,12 +49,17 @@ bool SaveScreenshot(std::unique_ptr<Screenshot> screenshot) {
WorkingDirectory::Instance()->GetFilenamePath(filename));
}
PlaygroundSwitches GetPlaygroundSwitches() {
PlaygroundSwitches switches;
switches.enable_wide_gamut = false;
return switches;
}
} // namespace
class GoldenTests : public ::testing::Test {
public:
GoldenTests()
: screenshotter_(new MetalScreenshotter(/*enable_wide_gamut=*/false)) {}
: screenshotter_(new MetalScreenshotter(GetPlaygroundSwitches())) {}
MetalScreenshotter& Screenshotter() { return *screenshotter_; }

View File

@@ -17,7 +17,7 @@ namespace testing {
/// playground backend.
class MetalScreenshotter : public Screenshotter {
public:
explicit MetalScreenshotter(bool enable_wide_gamut);
explicit MetalScreenshotter(const PlaygroundSwitches& switches);
std::unique_ptr<Screenshot> MakeScreenshot(
AiksContext& aiks_context,

View File

@@ -13,10 +13,8 @@
namespace impeller {
namespace testing {
MetalScreenshotter::MetalScreenshotter(bool enable_wide_gamut) {
MetalScreenshotter::MetalScreenshotter(const PlaygroundSwitches& switches) {
FML_CHECK(::glfwInit() == GLFW_TRUE);
PlaygroundSwitches switches;
switches.enable_wide_gamut = enable_wide_gamut;
playground_ = PlaygroundImpl::Create(PlaygroundBackend::kMetal, switches);
}

View File

@@ -133,8 +133,9 @@ std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
return nullptr;
}
auto context = ContextGLES::Create(
Flags{}, std::move(gl), ShaderLibraryMappingsForPlayground(), true);
auto context =
ContextGLES::Create(switches_.flags, std::move(gl),
ShaderLibraryMappingsForPlayground(), true);
if (!context) {
FML_LOG(ERROR) << "Could not create context.";
return nullptr;

View File

@@ -77,7 +77,7 @@ PlaygroundImplMTL::PlaygroundImplMTL(PlaygroundSwitches switches)
}
auto context = ContextMTL::Create(
impeller::Flags{}, ShaderLibraryMappingsForPlayground(),
switches.flags, ShaderLibraryMappingsForPlayground(),
is_gpu_disabled_sync_switch_, "Playground Library",
switches.enable_wide_gamut
? std::optional<PixelFormat>(PixelFormat::kB10G10R10A10XR)

View File

@@ -95,8 +95,9 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches)
context_settings.enable_validation = switches_.enable_vulkan_validation;
context_settings.fatal_missing_validations =
switches_.enable_vulkan_validation;
context_settings.flags = switches_.flags;
auto context_vk = ContextVK::Create(Flags{}, std::move(context_settings));
auto context_vk = ContextVK::Create(std::move(context_settings));
if (!context_vk || !context_vk->IsValid()) {
VALIDATION_LOG << "Could not create Vulkan context in the playground.";
return;

View File

@@ -65,6 +65,9 @@ void PlaygroundTest::SetUp() {
return;
}
switches.flags.antialiased_lines =
test_name.find("ExperimentAntialiasLines/") != std::string::npos;
SetupContext(GetParam(), switches);
SetupWindow();
}

View File

@@ -9,6 +9,7 @@
#include <optional>
#include "flutter/fml/command_line.h"
#include "impeller/base/flags.h"
namespace impeller {
@@ -34,6 +35,8 @@ struct PlaygroundSwitches {
bool enable_wide_gamut = false;
Flags flags;
PlaygroundSwitches();
explicit PlaygroundSwitches(const fml::CommandLine& args);

View File

@@ -73,8 +73,8 @@ impeller_component("renderer") {
"snapshot.h",
"surface.cc",
"surface.h",
"texture_mipmap.cc",
"texture_mipmap.h",
"texture_util.cc",
"texture_util.h",
"vertex_buffer_builder.cc",
"vertex_buffer_builder.h",
"vertex_descriptor.cc",

View File

@@ -34,7 +34,7 @@ std::shared_ptr<Context> CreateContext() {
settings.enable_gpu_tracing = false;
settings.enable_surface_control = false;
return ContextVK::Create(impeller::Flags{}, std::move(settings));
return ContextVK::Create(std::move(settings));
}
TEST(AndroidVulkanTest, CanImportRGBA) {

View File

@@ -102,9 +102,8 @@ static std::optional<QueueIndexVK> PickQueue(const vk::PhysicalDevice& device,
return std::nullopt;
}
std::shared_ptr<ContextVK> ContextVK::Create(const Flags& flags,
Settings settings) {
auto context = std::shared_ptr<ContextVK>(new ContextVK(flags));
std::shared_ptr<ContextVK> ContextVK::Create(Settings settings) {
auto context = std::shared_ptr<ContextVK>(new ContextVK(settings.flags));
context->Setup(std::move(settings));
if (!context->IsValid()) {
return nullptr;

View File

@@ -85,6 +85,7 @@ class ContextVK final : public Context,
bool enable_surface_control = false;
/// If validations are requested but cannot be enabled, log a fatal error.
bool fatal_missing_validations = false;
Flags flags;
std::optional<EmbedderData> embedder_data;
@@ -98,8 +99,7 @@ class ContextVK final : public Context,
/// Visible for testing.
static size_t ChooseThreadCountForWorkers(size_t hardware_concurrency);
static std::shared_ptr<ContextVK> Create(const Flags& flags,
Settings settings);
static std::shared_ptr<ContextVK> Create(Settings settings);
uint64_t GetHash() const { return hash_; }

View File

@@ -955,8 +955,7 @@ std::shared_ptr<ContextVK> MockVulkanContextBuilder::Build() {
g_format_properties_callback = format_properties_callback_;
g_physical_device_properties_callback = physical_properties_callback_;
settings.embedder_data = embedder_data_;
std::shared_ptr<ContextVK> result =
ContextVK::Create(Flags{}, std::move(settings));
std::shared_ptr<ContextVK> result = ContextVK::Create(std::move(settings));
return result;
}

View File

@@ -1,27 +0,0 @@
// 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 "impeller/renderer/texture_mipmap.h"
#include "impeller/renderer/blit_pass.h"
#include "impeller/renderer/command_buffer.h"
namespace impeller {
fml::Status AddMipmapGeneration(
const std::shared_ptr<CommandBuffer>& command_buffer,
const std::shared_ptr<Context>& context,
const std::shared_ptr<Texture>& texture) {
std::shared_ptr<BlitPass> blit_pass = command_buffer->CreateBlitPass();
bool success = blit_pass->GenerateMipmap(texture);
if (!success) {
return fml::Status(fml::StatusCode::kUnknown, "");
}
success = blit_pass->EncodeCommands();
if (!success) {
return fml::Status(fml::StatusCode::kUnknown, "");
}
return fml::Status();
}
} // namespace impeller

View File

@@ -0,0 +1,53 @@
// 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 "impeller/renderer/texture_util.h"
#include "impeller/renderer/blit_pass.h"
#include "impeller/renderer/command_buffer.h"
namespace impeller {
std::shared_ptr<Texture> CreateTexture(
const TextureDescriptor& texture_descriptor,
const std::vector<uint8_t>& data,
const std::shared_ptr<impeller::Context>& context,
std::string_view debug_label) {
std::shared_ptr<Texture> texture =
context->GetResourceAllocator()->CreateTexture(texture_descriptor);
auto data_mapping =
std::make_shared<fml::NonOwnedMapping>(data.data(), data.size());
std::shared_ptr<DeviceBuffer> buffer =
context->GetResourceAllocator()->CreateBufferWithCopy(*data_mapping);
std::shared_ptr<CommandBuffer> cmd_buffer = context->CreateCommandBuffer();
std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(buffer)), texture);
if (!blit_pass->EncodeCommands() ||
!context->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok()) {
return nullptr;
}
texture->SetLabel(debug_label);
return texture;
}
fml::Status AddMipmapGeneration(
const std::shared_ptr<CommandBuffer>& command_buffer,
const std::shared_ptr<Context>& context,
const std::shared_ptr<Texture>& texture) {
std::shared_ptr<BlitPass> blit_pass = command_buffer->CreateBlitPass();
bool success = blit_pass->GenerateMipmap(texture);
if (!success) {
return fml::Status(fml::StatusCode::kUnknown, "");
}
success = blit_pass->EncodeCommands();
if (!success) {
return fml::Status(fml::StatusCode::kUnknown, "");
}
return fml::Status();
}
} // namespace impeller

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_IMPELLER_RENDERER_TEXTURE_MIPMAP_H_
#define FLUTTER_IMPELLER_RENDERER_TEXTURE_MIPMAP_H_
#ifndef FLUTTER_IMPELLER_RENDERER_TEXTURE_UTIL_H_
#define FLUTTER_IMPELLER_RENDERER_TEXTURE_UTIL_H_
#include "flutter/fml/status.h"
#include "impeller/core/texture.h"
@@ -12,6 +12,12 @@
namespace impeller {
std::shared_ptr<Texture> CreateTexture(
const TextureDescriptor& texture_descriptor,
const std::vector<uint8_t>& data,
const std::shared_ptr<impeller::Context>& context,
std::string_view debug_label);
/// Adds a blit command to the render pass.
[[nodiscard]] fml::Status AddMipmapGeneration(
const std::shared_ptr<CommandBuffer>& command_buffer,
@@ -20,4 +26,4 @@ namespace impeller {
} // namespace impeller
#endif // FLUTTER_IMPELLER_RENDERER_TEXTURE_MIPMAP_H_
#endif // FLUTTER_IMPELLER_RENDERER_TEXTURE_UTIL_H_

View File

@@ -53,8 +53,9 @@ ScopedObject<Context> ContextVK::Create(const Settings& settings) {
impeller_settings.enable_validation = true;
sContextVKProcAddressCallback = settings.instance_proc_address_callback;
impeller_settings.proc_address_callback = ContextVKGetInstanceProcAddress;
auto impeller_context = impeller::ContextVK::Create(
impeller::Flags{}, std::move(impeller_settings));
impeller_settings.flags = impeller::Flags{};
auto impeller_context =
impeller::ContextVK::Create(std::move(impeller_settings));
sContextVKProcAddressCallback = nullptr;
if (!impeller_context) {
VALIDATION_LOG << "Could not create Impeller context.";

View File

@@ -4418,6 +4418,278 @@
}
}
},
"flutter/impeller/entity/gles/line.frag.gles": {
"Mali-G78": {
"core": "Mali-G78",
"filename": "flutter/impeller/entity/gles/line.frag.gles",
"has_side_effects": false,
"has_uniform_computation": false,
"modifies_coverage": false,
"reads_color_buffer": false,
"type": "Fragment",
"uses_late_zs_test": false,
"uses_late_zs_update": false,
"variants": {
"Main": {
"fp16_arithmetic": 6,
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
"varying"
],
"longest_path_cycles": [
0.21875,
0.140625,
0.21875,
0.0,
0.0,
1.75,
0.5
],
"pipelines": [
"arith_total",
"arith_fma",
"arith_cvt",
"arith_sfu",
"load_store",
"varying",
"texture"
],
"shortest_path_bound_pipelines": [
"varying"
],
"shortest_path_cycles": [
0.140625,
0.125,
0.140625,
0.0,
0.0,
1.75,
0.0
],
"total_bound_pipelines": [
"varying"
],
"total_cycles": [
0.21875,
0.140625,
0.21875,
0.0,
0.0,
1.75,
0.5
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 4,
"work_registers_used": 24
}
}
},
"Mali-T880": {
"core": "Mali-T880",
"filename": "flutter/impeller/entity/gles/line.frag.gles",
"has_uniform_computation": false,
"type": "Fragment",
"variants": {
"Main": {
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
"load_store"
],
"longest_path_cycles": [
3.299999952316284,
5.0,
2.0
],
"pipelines": [
"arithmetic",
"load_store",
"texture"
],
"shortest_path_bound_pipelines": [
"load_store"
],
"shortest_path_cycles": [
2.309999942779541,
5.0,
0.0
],
"total_bound_pipelines": [
"load_store"
],
"total_cycles": [
3.6666667461395264,
5.0,
2.0
]
},
"thread_occupancy": 100,
"uniform_registers_used": 1,
"work_registers_used": 3
}
}
}
},
"flutter/impeller/entity/gles/line.vert.gles": {
"Mali-G78": {
"core": "Mali-G78",
"filename": "flutter/impeller/entity/gles/line.vert.gles",
"has_uniform_computation": false,
"type": "Vertex",
"variants": {
"Position": {
"fp16_arithmetic": 0,
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
"load_store"
],
"longest_path_cycles": [
0.140625,
0.140625,
0.0,
0.0,
2.0,
0.0
],
"pipelines": [
"arith_total",
"arith_fma",
"arith_cvt",
"arith_sfu",
"load_store",
"texture"
],
"shortest_path_bound_pipelines": [
"load_store"
],
"shortest_path_cycles": [
0.140625,
0.140625,
0.0,
0.0,
2.0,
0.0
],
"total_bound_pipelines": [
"load_store"
],
"total_cycles": [
0.140625,
0.140625,
0.0,
0.0,
2.0,
0.0
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 20,
"work_registers_used": 32
},
"Varying": {
"fp16_arithmetic": null,
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
"load_store"
],
"longest_path_cycles": [
0.09375,
0.0,
0.09375,
0.0,
10.0,
0.0
],
"pipelines": [
"arith_total",
"arith_fma",
"arith_cvt",
"arith_sfu",
"load_store",
"texture"
],
"shortest_path_bound_pipelines": [
"load_store"
],
"shortest_path_cycles": [
0.09375,
0.0,
0.09375,
0.0,
10.0,
0.0
],
"total_bound_pipelines": [
"load_store"
],
"total_cycles": [
0.09375,
0.0,
0.09375,
0.0,
10.0,
0.0
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 8,
"work_registers_used": 19
}
}
},
"Mali-T880": {
"core": "Mali-T880",
"filename": "flutter/impeller/entity/gles/line.vert.gles",
"has_uniform_computation": false,
"type": "Vertex",
"variants": {
"Main": {
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
"load_store"
],
"longest_path_cycles": [
2.640000104904175,
12.0,
0.0
],
"pipelines": [
"arithmetic",
"load_store",
"texture"
],
"shortest_path_bound_pipelines": [
"load_store"
],
"shortest_path_cycles": [
2.640000104904175,
12.0,
0.0
],
"total_bound_pipelines": [
"load_store"
],
"total_cycles": [
2.6666667461395264,
12.0,
0.0
]
},
"thread_occupancy": 100,
"uniform_registers_used": 5,
"work_registers_used": 2
}
}
}
},
"flutter/impeller/entity/gles/linear_gradient_fill.frag.gles": {
"Mali-G78": {
"core": "Mali-G78",
@@ -7966,6 +8238,188 @@
}
}
},
"flutter/impeller/entity/line.frag.vkspv": {
"Mali-G78": {
"core": "Mali-G78",
"filename": "flutter/impeller/entity/line.frag.vkspv",
"has_side_effects": false,
"has_uniform_computation": true,
"modifies_coverage": false,
"reads_color_buffer": false,
"type": "Fragment",
"uses_late_zs_test": false,
"uses_late_zs_update": false,
"variants": {
"Main": {
"fp16_arithmetic": 15,
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
"varying"
],
"longest_path_cycles": [
0.1875,
0.140625,
0.1875,
0.1875,
0.0,
1.75,
0.5
],
"pipelines": [
"arith_total",
"arith_fma",
"arith_cvt",
"arith_sfu",
"load_store",
"varying",
"texture"
],
"shortest_path_bound_pipelines": [
"varying"
],
"shortest_path_cycles": [
0.1875,
0.125,
0.140625,
0.1875,
0.0,
1.75,
0.0
],
"total_bound_pipelines": [
"varying"
],
"total_cycles": [
0.1875,
0.140625,
0.1875,
0.1875,
0.0,
1.75,
0.5
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 6,
"work_registers_used": 18
}
}
}
},
"flutter/impeller/entity/line.vert.vkspv": {
"Mali-G78": {
"core": "Mali-G78",
"filename": "flutter/impeller/entity/line.vert.vkspv",
"has_uniform_computation": true,
"type": "Vertex",
"variants": {
"Position": {
"fp16_arithmetic": 0,
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
"load_store"
],
"longest_path_cycles": [
0.125,
0.125,
0.0,
0.0,
2.0,
0.0
],
"pipelines": [
"arith_total",
"arith_fma",
"arith_cvt",
"arith_sfu",
"load_store",
"texture"
],
"shortest_path_bound_pipelines": [
"load_store"
],
"shortest_path_cycles": [
0.125,
0.125,
0.0,
0.0,
2.0,
0.0
],
"total_bound_pipelines": [
"load_store"
],
"total_cycles": [
0.125,
0.125,
0.0,
0.0,
2.0,
0.0
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 28,
"work_registers_used": 32
},
"Varying": {
"fp16_arithmetic": null,
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
"load_store"
],
"longest_path_cycles": [
0.046875,
0.0,
0.046875,
0.0,
10.0,
0.0
],
"pipelines": [
"arith_total",
"arith_fma",
"arith_cvt",
"arith_sfu",
"load_store",
"texture"
],
"shortest_path_bound_pipelines": [
"load_store"
],
"shortest_path_cycles": [
0.046875,
0.0,
0.046875,
0.0,
10.0,
0.0
],
"total_bound_pipelines": [
"load_store"
],
"total_cycles": [
0.046875,
0.0,
0.046875,
0.0,
10.0,
0.0
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 20,
"work_registers_used": 22
}
}
}
},
"flutter/impeller/entity/linear_gradient_fill.frag.vkspv": {
"Mali-G78": {
"core": "Mali-G78",

View File

@@ -40,7 +40,8 @@ ShellTestPlatformViewMetal::ShellTestPlatformViewMetal(
id<MTLDevice> device = nil;
if (GetSettings().enable_impeller) {
impeller_context_ =
[[FlutterDarwinContextMetalImpeller alloc] init:is_gpu_disabled_sync_switch];
[[FlutterDarwinContextMetalImpeller alloc] init:impeller::Flags {}
gpuDisabledSyncSwitch:is_gpu_disabled_sync_switch];
FML_CHECK(impeller_context_.context);
device = impeller_context_.context->GetMTLDevice();
} else {

View File

@@ -534,6 +534,8 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
settings.impeller_enable_lazy_shader_mode =
command_line.HasOption(FlagForSwitch(Switch::ImpellerLazyShaderMode));
settings.impeller_antialiased_lines =
command_line.HasOption(FlagForSwitch(Switch::ImpellerAntialiasLines));
return settings;
}

View File

@@ -304,6 +304,9 @@ DEF_SWITCH(ImpellerLazyShaderMode,
"impeller-lazy-shader-mode",
"Whether to defer initialization of all required PSOs for the "
"Impeller backend. Defaults to false.")
DEF_SWITCH(ImpellerAntialiasLines,
"impeller-antialias-lines",
"Experimental flag to test drawing lines with antialiasing.")
DEF_SWITCHES_END
void PrintUsage(const std::string& executable_name);

View File

@@ -48,12 +48,9 @@ static std::shared_ptr<impeller::Context> CreateImpellerContext(
settings.enable_validation = p_settings.enable_validation;
settings.enable_gpu_tracing = p_settings.enable_gpu_tracing;
settings.enable_surface_control = p_settings.enable_surface_control;
settings.flags = p_settings.impeller_flags;
auto context = impeller::ContextVK::Create(
impeller::Flags{
.lazy_shader_mode = p_settings.enable_lazy_shader_mode,
},
std::move(settings));
auto context = impeller::ContextVK::Create(std::move(settings));
if (!p_settings.quiet) {
if (context && impeller::CapabilitiesVK::Cast(*context->GetCapabilities())

View File

@@ -6,6 +6,7 @@
#define FLUTTER_SHELL_PLATFORM_ANDROID_CONTEXT_ANDROID_CONTEXT_H_
#include "flutter/fml/macros.h"
#include "flutter/impeller/base/flags.h"
#include "flutter/impeller/renderer/context.h"
#include "flutter/shell/platform/android/android_rendering_selector.h"
#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
@@ -25,8 +26,8 @@ class AndroidContext {
bool enable_validation = false;
bool enable_gpu_tracing = false;
bool enable_surface_control = false;
bool enable_lazy_shader_mode = false;
bool quiet = false;
impeller::Flags impeller_flags;
};
AndroidRenderingAPI RenderingApi() const;

View File

@@ -53,6 +53,8 @@ public class FlutterLoader {
"io.flutter.embedding.android.EnableSurfaceControl";
private static final String IMPELLER_LAZY_SHADER_MODE =
"io.flutter.embedding.android.ImpellerLazyShaderInitialization";
private static final String IMPELLER_ANTIALIAS_LINES =
"io.flutter.embedding.android.ImpellerAntialiasLines";
/**
* Set whether leave or clean up the VM after the last shell shuts down. It can be set from app's
@@ -382,6 +384,9 @@ public class FlutterLoader {
if (metaData.getBoolean(IMPELLER_LAZY_SHADER_MODE)) {
shellArgs.add("--impeller-lazy-shader-mode");
}
if (metaData.getBoolean(IMPELLER_ANTIALIAS_LINES)) {
shellArgs.add("--impeller-antialias-lines");
}
}
final String leakVM = isLeakVM(metaData) ? "true" : "false";

View File

@@ -53,8 +53,10 @@ AndroidContext::ContextSettings CreateContextSettings(
settings.enable_gpu_tracing = p_settings.enable_vulkan_gpu_tracing;
settings.enable_validation = p_settings.enable_vulkan_validation;
settings.enable_surface_control = p_settings.enable_surface_control;
settings.enable_lazy_shader_mode =
settings.impeller_flags.lazy_shader_mode =
p_settings.impeller_enable_lazy_shader_mode;
settings.impeller_flags.antialiased_lines =
p_settings.impeller_antialiased_lines;
return settings;
}
} // namespace

View File

@@ -25,7 +25,9 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Initializes a FlutterDarwinContextMetalImpeller.
*/
- (instancetype)init:(const std::shared_ptr<const fml::SyncSwitch>&)is_gpu_disabled_sync_switch;
- (instancetype)init:(const impeller::Flags&)flags
gpuDisabledSyncSwitch:
(const std::shared_ptr<const fml::SyncSwitch>&)is_gpu_disabled_sync_switch;
/**
* Creates an external texture with the specified ID and contents.

View File

@@ -16,6 +16,7 @@
FLUTTER_ASSERT_ARC
static std::shared_ptr<impeller::ContextMTL> CreateImpellerContext(
const impeller::Flags& flags,
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch) {
std::vector<std::shared_ptr<fml::Mapping>> shader_mappings = {
std::make_shared<fml::NonOwnedMapping>(impeller_entity_shaders_data,
@@ -25,16 +26,18 @@ static std::shared_ptr<impeller::ContextMTL> CreateImpellerContext(
std::make_shared<fml::NonOwnedMapping>(impeller_framebuffer_blend_shaders_data,
impeller_framebuffer_blend_shaders_length),
};
return impeller::ContextMTL::Create(impeller::Flags{}, shader_mappings,
is_gpu_disabled_sync_switch, "Impeller Library");
return impeller::ContextMTL::Create(flags, shader_mappings, is_gpu_disabled_sync_switch,
"Impeller Library");
}
@implementation FlutterDarwinContextMetalImpeller
- (instancetype)init:(const std::shared_ptr<const fml::SyncSwitch>&)is_gpu_disabled_sync_switch {
- (instancetype)init:(const impeller::Flags&)flags
gpuDisabledSyncSwitch:
(const std::shared_ptr<const fml::SyncSwitch>&)is_gpu_disabled_sync_switch {
self = [super init];
if (self != nil) {
_context = CreateImpellerContext(is_gpu_disabled_sync_switch);
_context = CreateImpellerContext(flags, is_gpu_disabled_sync_switch);
FML_CHECK(_context) << "Could not create Metal Impeller Context.";
id<MTLDevice> device = _context->GetMTLDevice();
FML_CHECK(device) << "Could not acquire Metal device.";

View File

@@ -177,6 +177,9 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* p
settings.enable_wide_gamut = enableWideGamut;
#endif
NSNumber* nsAntialiasLines = [mainBundle objectForInfoDictionaryKey:@"FLTAntialiasLines"];
settings.impeller_antialiased_lines = (nsAntialiasLines ? nsAntialiasLines.boolValue : NO);
settings.warn_on_impeller_opt_out = true;
NSNumber* enableTraceSystrace = [mainBundle objectForInfoDictionaryKey:@"FLTTraceSystrace"];

View File

@@ -9,6 +9,7 @@
#include "flutter/common/graphics/gl_context_switch.h"
#include "flutter/common/graphics/texture.h"
#include "flutter/common/settings.h"
#include "flutter/fml/concurrent_message_loop.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/synchronization/sync_switch.h"
@@ -54,7 +55,8 @@ class IOSContext {
static std::unique_ptr<IOSContext> Create(
IOSRenderingAPI api,
IOSRenderingBackend backend,
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch);
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
const Settings& settings);
//----------------------------------------------------------------------------
/// @brief Collects the context object. This must happen on the thread on

View File

@@ -21,7 +21,8 @@ IOSContext::~IOSContext() = default;
std::unique_ptr<IOSContext> IOSContext::Create(
IOSRenderingAPI api,
IOSRenderingBackend backend,
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch) {
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
const Settings& settings) {
switch (api) {
case IOSRenderingAPI::kSoftware:
FML_LOG(IMPORTANT)
@@ -37,7 +38,7 @@ std::unique_ptr<IOSContext> IOSContext::Create(
FML_LOG(FATAL) << "Impeller opt-out unavailable.";
return nullptr;
case IOSRenderingBackend::kImpeller:
return std::make_unique<IOSContextMetalImpeller>(is_gpu_disabled_sync_switch);
return std::make_unique<IOSContextMetalImpeller>(settings, is_gpu_disabled_sync_switch);
}
default:
break;

View File

@@ -21,6 +21,7 @@ namespace flutter {
class IOSContextMetalImpeller final : public IOSContext {
public:
explicit IOSContextMetalImpeller(
const Settings& settings,
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch);
~IOSContextMetalImpeller();

View File

@@ -12,11 +12,20 @@
FLUTTER_ASSERT_ARC
namespace flutter {
namespace {
impeller::Flags SettingsToFlags(const Settings& settings) {
return impeller::Flags{
.antialiased_lines = settings.impeller_antialiased_lines,
};
}
} // namespace
IOSContextMetalImpeller::IOSContextMetalImpeller(
const Settings& settings,
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch)
: darwin_context_metal_impeller_(
[[FlutterDarwinContextMetalImpeller alloc] init:is_gpu_disabled_sync_switch]) {
: darwin_context_metal_impeller_([[FlutterDarwinContextMetalImpeller alloc]
init:SettingsToFlags(settings)
gpuDisabledSyncSwitch:is_gpu_disabled_sync_switch]) {
if (darwin_context_metal_impeller_.context) {
aiks_context_ = std::make_shared<impeller::AiksContext>(
darwin_context_metal_impeller_.context, impeller::TypographerContextSkia::Make());

View File

@@ -64,7 +64,8 @@ PlatformViewIOS::PlatformViewIOS(
delegate.OnPlatformViewGetSettings().enable_impeller
? IOSRenderingBackend::kImpeller
: IOSRenderingBackend::kSkia,
is_gpu_disabled_sync_switch),
is_gpu_disabled_sync_switch,
delegate.OnPlatformViewGetSettings()),
platform_views_controller,
task_runners) {}

View File

@@ -71,8 +71,7 @@ EmbedderSurfaceVulkanImpeller::EmbedderSurfaceVulkanImpeller(
}
settings.embedder_data = data;
context_ =
impeller::ContextVK::Create(impeller::Flags{}, std::move(settings));
context_ = impeller::ContextVK::Create(std::move(settings));
if (!context_) {
FML_LOG(ERROR) << "Failed to initialize Vulkan Context.";
return;

View File

@@ -74,11 +74,11 @@ bool ImpellerVulkanContextHolder::Initialize(bool enable_validation) {
context_settings.shader_libraries_data = ShaderLibraryMappings();
context_settings.cache_directory = fml::paths::GetCachesDirectory();
context_settings.enable_validation = enable_validation;
// Enable lazy shader mode for faster test execution as most tests
// will never render anything at all.
context_settings.flags.lazy_shader_mode = true;
context = impeller::ContextVK::Create(
// Enable lazy shader mode for faster test execution as most tests
// will never render anything at all.
impeller::Flags{.lazy_shader_mode = true}, std::move(context_settings));
context = impeller::ContextVK::Create(std::move(context_settings));
if (!context || !context->IsValid()) {
VALIDATION_LOG << "Could not create Vulkan context.";
return false;

View File

@@ -895,6 +895,7 @@ impeller_Play_AiksTest_SetContentsWithRegion_Vulkan.png
impeller_Play_AiksTest_SiblingSaveLayerBoundsAreRespected_Metal.png
impeller_Play_AiksTest_SiblingSaveLayerBoundsAreRespected_OpenGLES.png
impeller_Play_AiksTest_SiblingSaveLayerBoundsAreRespected_Vulkan.png
impeller_Play_AiksTest_SimpleExperimentAntialiasLines_Metal.png
impeller_Play_AiksTest_SolidColorCircleMaskBlurTinySigma_Metal.png
impeller_Play_AiksTest_SolidColorCircleMaskBlurTinySigma_OpenGLES.png
impeller_Play_AiksTest_SolidColorCircleMaskBlurTinySigma_Vulkan.png