Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Impeller] Switch from AIKS canvas to DL based canvas implementation. #53781

Merged
merged 53 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
ad4202f
[Impeller] enable experimental canvas.
jonahwilliams Jul 9, 2024
2d5f54d
Merge branch 'main' of github.com:flutter/engine into enable_exp_canvas
jonahwilliams Jul 9, 2024
c148f01
remove redefinition
jonahwilliams Jul 9, 2024
5e3ebb1
temp test adaption.
jonahwilliams Jul 9, 2024
492f461
fix host buffer reset for exp canvas.
jonahwilliams Jul 9, 2024
ff2fa94
add capture for reset_host_buffer
jonahwilliams Jul 9, 2024
a2ef961
capture.
jonahwilliams Jul 9, 2024
2432a3f
remove extra assert.
jonahwilliams Jul 9, 2024
f0a0114
temp disable for testing.
jonahwilliams Jul 10, 2024
188ecd1
cull rect correctly
jonahwilliams Jul 10, 2024
9343752
Merge branch 'main' of github.com:flutter/engine into enable_exp_canvas
jonahwilliams Jul 23, 2024
0c2926e
++
jonahwilliams Jul 23, 2024
f42ec97
++
jonahwilliams Jul 23, 2024
239402c
add back trace event.
jonahwilliams Jul 23, 2024
10c1efa
Merge branch 'main' into enable_exp_canvas
jonahwilliams Jul 24, 2024
d8e0120
merge
jonahwilliams Jul 25, 2024
1056a6e
Merge branch 'enable_exp_canvas' of github.com:jonahwilliams/engine i…
jonahwilliams Jul 25, 2024
3a35555
Update gpu_surface_metal_impeller_unittests.mm
jonahwilliams Jul 25, 2024
de6a507
Merge branch 'main' of github.com:flutter/engine into enable_exp_canvas
jonahwilliams Jul 25, 2024
9b62dfe
Merge branch 'enable_exp_canvas' of github.com:jonahwilliams/engine i…
jonahwilliams Jul 25, 2024
45c00cf
++
jonahwilliams Jul 25, 2024
94b4340
fix entity pass flip
jonahwilliams Jul 26, 2024
03d136d
Merge branch 'main' into enable_exp_canvas
jonahwilliams Jul 26, 2024
ed8ad3f
++
jonahwilliams Jul 31, 2024
41b2844
Merge branch 'enable_exp_canvas' of github.com:jonahwilliams/engine i…
jonahwilliams Jul 31, 2024
e3cf6c1
Merge branch 'main' of github.com:flutter/engine into enable_exp_canvas
jonahwilliams Jul 31, 2024
5b9c14b
++
jonahwilliams Jul 31, 2024
c5ad678
check bounds.
jonahwilliams Jul 31, 2024
bbdec46
++
jonahwilliams Jul 31, 2024
59decda
Merge branch 'main' of github.com:flutter/engine into enable_exp_canvas
jonahwilliams Jul 31, 2024
35204a3
more adjustments.
jonahwilliams Jul 31, 2024
de3e49a
Merge branch 'main' of github.com:flutter/engine into enable_exp_canvas
jonahwilliams Aug 10, 2024
50497ce
++
jonahwilliams Aug 10, 2024
464b21e
++
jonahwilliams Aug 10, 2024
8324a61
++
jonahwilliams Aug 10, 2024
b0481d1
Merge branch 'main' of github.com:flutter/engine into enable_exp_canvas
jonahwilliams Aug 10, 2024
cbb19a4
bounds adjustment.
jonahwilliams Aug 10, 2024
dc131c0
++
jonahwilliams Aug 10, 2024
5414db3
fix advanced blend coverage hint.
jonahwilliams Aug 10, 2024
ca34138
Add missing return.
jonahwilliams Aug 10, 2024
2c6f55e
force final rp construction.
jonahwilliams Aug 10, 2024
1236b73
++
jonahwilliams Aug 10, 2024
630bb36
++
jonahwilliams Aug 13, 2024
86600bf
++
jonahwilliams Aug 14, 2024
d11a4ac
fix stroked text.
jonahwilliams Aug 14, 2024
c3a64cf
adjust more things.
jonahwilliams Aug 15, 2024
02b7834
++
jonahwilliams Aug 15, 2024
3899c58
++
jonahwilliams Aug 15, 2024
4f459a7
Merge branch 'main' into enable_exp_canvas
jonahwilliams Aug 15, 2024
3e21b07
remove incorrect clip
jonahwilliams Aug 15, 2024
c2b8938
remove test opt outs.
jonahwilliams Aug 15, 2024
62a5a0a
Merge branch 'enable_exp_canvas' of github.com:jonahwilliams/engine i…
jonahwilliams Aug 15, 2024
79648d3
Merge branch 'main' into enable_exp_canvas
jonahwilliams Aug 15, 2024
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
2 changes: 1 addition & 1 deletion common/config.gni
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ declare_args() {
slimpeller = false

# Opt into new DL dispatcher that skips AIKS layer
experimental_canvas = false
experimental_canvas = true
}

# feature_defines_list ---------------------------------------------------------
Expand Down
21 changes: 17 additions & 4 deletions impeller/aiks/aiks_playground.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,7 @@ bool AiksPlayground::ImGuiBegin(const char* name,

bool AiksPlayground::OpenPlaygroundHere(
const sk_sp<flutter::DisplayList>& list) {
DlDispatcher dispatcher;
list->Dispatch(dispatcher);
Picture picture = dispatcher.EndRecordingAsPicture();
return OpenPlaygroundHere(std::move(picture));
return OpenPlaygroundHere([list]() { return list; });
}

bool AiksPlayground::OpenPlaygroundHere(
Expand All @@ -91,12 +88,28 @@ bool AiksPlayground::OpenPlaygroundHere(

return Playground::OpenPlaygroundHere(
[&renderer, &callback](RenderTarget& render_target) -> bool {
#if EXPERIMENTAL_CANVAS
auto display_list = callback();
TextFrameDispatcher collector(renderer.GetContentContext(), Matrix());
display_list->Dispatch(collector);

ExperimentalDlDispatcher impeller_dispatcher(
renderer.GetContentContext(), render_target,
display_list->root_has_backdrop_filter(),
display_list->max_root_blend_mode(), IRect::MakeMaximum());
display_list->Dispatch(impeller_dispatcher);
impeller_dispatcher.FinishRecording();
renderer.GetContentContext().GetTransientsBuffer().Reset();
renderer.GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
return true;
#else
auto display_list = callback();
DlDispatcher dispatcher;
display_list->Dispatch(dispatcher);
Picture picture = dispatcher.EndRecordingAsPicture();

return renderer.Render(picture, render_target, true);
#endif // EXPERIMENTAL_CANVAS
});
}

Expand Down
3 changes: 2 additions & 1 deletion impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,8 @@ void Canvas::SaveLayer(const Paint& paint,
const std::shared_ptr<ImageFilter>& backdrop_filter,
ContentBoundsPromise bounds_promise,
uint32_t total_content_depth,
bool can_distribute_opacity) {
bool can_distribute_opacity,
bool bounds_from_caller) {
if (can_distribute_opacity && !backdrop_filter &&
Paint::CanApplyOpacityPeephole(paint) &&
bounds_promise != ContentBoundsPromise::kMayClipContents) {
Expand Down
3 changes: 2 additions & 1 deletion impeller/aiks/canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class Canvas {
const std::shared_ptr<ImageFilter>& backdrop_filter = nullptr,
ContentBoundsPromise bounds_promise = ContentBoundsPromise::kUnknown,
uint32_t total_content_depth = kMaxDepth,
bool can_distribute_opacity = false);
bool can_distribute_opacity = false,
bool bounds_from_caller = false);

virtual bool Restore();

Expand Down
180 changes: 106 additions & 74 deletions impeller/aiks/experimental_canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "impeller/aiks/experimental_canvas.h"
#include "fml/logging.h"
#include "fml/trace_event.h"
#include "impeller/aiks/canvas.h"
#include "impeller/aiks/paint_pass_delegate.h"
#include "impeller/base/validation.h"
Expand Down Expand Up @@ -82,26 +83,28 @@ static std::shared_ptr<Texture> FlipBackdrop(
// unbalanced save layers. Ideally, this method would return false and the
// renderer could handle that by terminating dispatch.
render_passes.push_back(LazyRenderingConfig(
renderer, std::move(rendering_config.entity_pass_target)));
renderer, std::move(rendering_config.entity_pass_target),
std::move(rendering_config.inline_pass_context)));
return nullptr;
}

std::shared_ptr<Texture> input_texture =
rendering_config.entity_pass_target->Flip(
*renderer.GetContext()->GetResourceAllocator());
rendering_config.inline_pass_context->GetTexture();

if (!input_texture) {
VALIDATION_LOG << "Failed to fetch the color texture in order to "
"apply an advanced blend or backdrop filter.";

// Note: see above.
render_passes.push_back(LazyRenderingConfig(
renderer, std::move(rendering_config.entity_pass_target)));
renderer, std::move(rendering_config.entity_pass_target),
std::move(rendering_config.inline_pass_context)));
return nullptr;
}

render_passes.push_back(LazyRenderingConfig(
renderer, std::move(rendering_config.entity_pass_target)));
renderer, std::move(rendering_config.entity_pass_target),
std::move(rendering_config.inline_pass_context)));
// Eagerly restore the BDF contents.

// If the pass context returns a backdrop texture, we need to draw it to the
Expand Down Expand Up @@ -157,7 +160,6 @@ static const constexpr RenderTarget::AttachmentConfig kDefaultStencilConfig =
static std::unique_ptr<EntityPassTarget> CreateRenderTarget(
ContentContext& renderer,
ISize size,
int mip_count,
const Color& clear_color) {
const std::shared_ptr<Context>& context = renderer.GetContext();

Expand All @@ -166,18 +168,12 @@ static std::unique_ptr<EntityPassTarget> CreateRenderTarget(
/// What's important is the `StorageMode` of the textures, which cannot be
/// changed for the lifetime of the textures.

if (context->GetBackendType() == Context::BackendType::kOpenGLES) {
// TODO(https://github.com/flutter/flutter/issues/141732): Implement mip map
// generation on opengles.
mip_count = 1;
}

RenderTarget target;
if (context->GetCapabilities()->SupportsOffscreenMSAA()) {
target = renderer.GetRenderTargetCache()->CreateOffscreenMSAA(
/*context=*/*context,
/*size=*/size,
/*mip_count=*/mip_count,
/*mip_count=*/1,
/*label=*/"EntityPass",
/*color_attachment_config=*/
RenderTarget::AttachmentConfigMSAA{
Expand All @@ -191,7 +187,7 @@ static std::unique_ptr<EntityPassTarget> CreateRenderTarget(
target = renderer.GetRenderTargetCache()->CreateOffscreen(
*context, // context
size, // size
/*mip_count=*/mip_count,
/*mip_count=*/1,
"EntityPass", // label
RenderTarget::AttachmentConfig{
.storage_mode = StorageMode::kDevicePrivate,
Expand Down Expand Up @@ -273,12 +269,10 @@ void ExperimentalCanvas::SetupRenderPass() {
// a second save layer with the same dimensions as the onscreen. When
// rendering is completed, we must blit this saveLayer to the onscreen.
if (requires_readback_) {
auto entity_pass_target = CreateRenderTarget(
renderer_, //
color0.texture->GetSize(), //
// Note: this is incorrect, we also need to know what kind of filter.
/*mip_count=*/4, //
/*clear_color=*/Color::BlackTransparent());
auto entity_pass_target =
CreateRenderTarget(renderer_, //
color0.texture->GetSize(), //
/*clear_color=*/Color::BlackTransparent());
render_passes_.push_back(
LazyRenderingConfig(renderer_, std::move(entity_pass_target)));
} else {
Expand Down Expand Up @@ -312,47 +306,54 @@ void ExperimentalCanvas::SaveLayer(
const std::shared_ptr<ImageFilter>& backdrop_filter,
ContentBoundsPromise bounds_promise,
uint32_t total_content_depth,
bool can_distribute_opacity) {
// Can we always guarantee that we get a bounds? Does a lack of bounds
// indicate something?
if (!bounds.has_value()) {
bounds = Rect::MakeSize(render_target_.GetRenderTargetSize());
bool can_distribute_opacity,
bool bounds_from_caller) {
TRACE_EVENT0("flutter", "Canvas::saveLayer");

if (bounds.has_value() && bounds->IsEmpty()) {
Save(total_content_depth);
return;
}

if (!clip_coverage_stack_.HasCoverage()) {
// The current clip is empty. This means the pass texture won't be
// visible, so skip it.
Save(total_content_depth);
return;
}

// SaveLayer is a no-op, depending on the bounds promise. Should/Can DL elide
// this?
if (bounds->IsEmpty()) {
auto maybe_current_clip_coverage = clip_coverage_stack_.CurrentClipCoverage();
if (!maybe_current_clip_coverage.has_value()) {
Save(total_content_depth);
return;
}
auto current_clip_coverage = maybe_current_clip_coverage.value();

// The maximum coverage of the subpass. Subpasses textures should never
// extend outside the parent pass texture or the current clip coverage.
Rect coverage_limit = Rect::MakeOriginSize(
GetGlobalPassPosition(),
Size(render_passes_.back().inline_pass_context->GetTexture()->GetSize()));
std::optional<Rect> maybe_coverage_limit =
Rect::MakeOriginSize(GetGlobalPassPosition(),
Size(render_passes_.back()
.inline_pass_context->GetTexture()
->GetSize()))
.Intersection(current_clip_coverage);

if (!maybe_coverage_limit.has_value()) {
Save(total_content_depth);
return;
}
maybe_coverage_limit = maybe_coverage_limit->Intersection(
Rect::MakeSize(render_target_.GetRenderTargetSize()));

// BDF No-op. need to do some precomputation to ensure this is fully skipped.
if (backdrop_filter) {
if (!clip_coverage_stack_.HasCoverage()) {
Save(total_content_depth);
return;
}
auto maybe_clip_coverage = clip_coverage_stack_.CurrentClipCoverage();
if (!maybe_clip_coverage.has_value()) {
Save(total_content_depth);
return;
}
auto clip_coverage = maybe_clip_coverage.value();
if (clip_coverage.IsEmpty() ||
!coverage_limit.IntersectsWithRect(clip_coverage)) {
Save(total_content_depth);
return;
}
if (!maybe_coverage_limit.has_value() || maybe_coverage_limit->IsEmpty()) {
Save(total_content_depth);
return;
}
auto coverage_limit = maybe_coverage_limit.value();

if (can_distribute_opacity && !backdrop_filter &&
Paint::CanApplyOpacityPeephole(paint)) {
Paint::CanApplyOpacityPeephole(paint) &&
bounds_promise != ContentBoundsPromise::kMayClipContents) {
Save(total_content_depth);
transform_stack_.back().distributed_opacity *= paint.color.alpha;
return;
Expand All @@ -362,11 +363,8 @@ void ExperimentalCanvas::SaveLayer(
std::shared_ptr<FilterContents> backdrop_filter_contents;
Point local_position = {0, 0};
if (backdrop_filter) {
auto current_clip_coverage = clip_coverage_stack_.CurrentClipCoverage();
if (current_clip_coverage.has_value()) {
local_position =
current_clip_coverage->GetOrigin() - GetGlobalPassPosition();
}
local_position =
current_clip_coverage.GetOrigin() - GetGlobalPassPosition();
EntityPass::BackdropFilterProc backdrop_filter_proc =
[backdrop_filter = backdrop_filter->Clone()](
const FilterInput::Ref& input, const Matrix& effect_transform,
Expand Down Expand Up @@ -401,23 +399,25 @@ void ExperimentalCanvas::SaveLayer(

// Backdrop Filter must expand bounds to at least the clip stack, otherwise
// the coverage of the parent render pass.
Rect subpass_coverage = bounds->TransformBounds(GetCurrentTransform());
if (backdrop_filter_contents) {
FML_CHECK(clip_coverage_stack_.HasCoverage());
// We should never hit this case as we check the intersection above.
// NOLINTBEGIN(bugprone-unchecked-optional-access)
subpass_coverage =
coverage_limit
.Intersection(clip_coverage_stack_.CurrentClipCoverage().value())
.value();
// NOLINTEND(bugprone-unchecked-optional-access)
Rect subpass_coverage;
if (backdrop_filter_contents ||
Entity::IsBlendModeDestructive(paint.blend_mode) || !bounds.has_value()) {
subpass_coverage = coverage_limit;
// TODO(jonahwilliams): if we have tight bounds we should be able to reduce
// this size here. if (bounds.has_value() && bounds_from_caller) {
// subpass_coverage =
// coverage_limit.Intersection(bounds.value()).value_or(bounds.value());
// }
} else {
subpass_coverage = bounds->TransformBounds(GetCurrentTransform());
}

render_passes_.push_back(LazyRenderingConfig(
renderer_, //
CreateRenderTarget(renderer_, //
ISize(subpass_coverage.GetSize()), //
1u, Color::BlackTransparent())));
Color::BlackTransparent() //
)));
save_layer_state_.push_back(SaveLayerState{paint_copy, subpass_coverage});

CanvasStackEntry entry;
Expand Down Expand Up @@ -491,7 +491,8 @@ bool ExperimentalCanvas::Restore() {
PaintPassDelegate(save_layer_state.paint)
.CreateContentsForSubpassTarget(
lazy_render_pass.inline_pass_context->GetTexture(),
transform_stack_.back().transform);
Matrix::MakeTranslation(Vector3{-GetGlobalPassPosition()}) *
transform_stack_.back().transform);

lazy_render_pass.inline_pass_context->EndPass();

Expand Down Expand Up @@ -669,20 +670,22 @@ void ExperimentalCanvas::AddRenderEntityToCurrentPass(Entity entity,
// conditionally update the backdrop color to its solid color value blended
// with the current backdrop.
if (render_passes_.back().IsApplyingClearColor()) {
std::optional<Color> maybe_color =
entity.AsBackgroundColor(render_passes_.back()
.entity_pass_target->GetRenderTarget()
.GetRenderTargetSize());
std::optional<Color> maybe_color = entity.AsBackgroundColor(
render_passes_.back().inline_pass_context->GetTexture()->GetSize());
if (maybe_color.has_value()) {
Color color = maybe_color.value();
RenderTarget& render_target =
render_passes_.back().entity_pass_target->GetRenderTarget();
RenderTarget& render_target = render_passes_.back()
.inline_pass_context->GetPassTarget()
.GetRenderTarget();
ColorAttachment attachment =
render_target.GetColorAttachments().find(0u)->second;
// Attachment.clear color needs to be premultiplied at all times, but the
// Color::Blend function requires unpremultiplied colors.
attachment.clear_color = attachment.clear_color.Unpremultiply()
.Blend(color, entity.GetBlendMode())
.Premultiply();
render_target.SetColorAttachment(attachment, 0u);
return;
}
}

Expand All @@ -700,8 +703,36 @@ void ExperimentalCanvas::AddRenderEntityToCurrentPass(Entity entity,
if (renderer_.GetDeviceCapabilities().SupportsFramebufferFetch()) {
ApplyFramebufferBlend(entity);
} else {
VALIDATION_LOG << "Emulated advanced blends are currently unsupported.";
return;
// End the active pass and flush the buffer before rendering "advanced"
// blends. Advanced blends work by binding the current render target
// texture as an input ("destination"), blending with a second texture
// input ("source"), writing the result to an intermediate texture, and
// finally copying the data from the intermediate texture back to the
// render target texture. And so all of the commands that have written
// to the render target texture so far need to execute before it's bound
// for blending (otherwise the blend pass will end up executing before
// all the previous commands in the active pass).
auto input_texture = FlipBackdrop(render_passes_, GetGlobalPassPosition(),
clip_coverage_stack_, renderer_);
if (!input_texture) {
return;
}

// The coverage hint tells the rendered Contents which portion of the
// rendered output will actually be used, and so we set this to the
// current clip coverage (which is the max clip bounds). The contents may
// optionally use this hint to avoid unnecessary rendering work.
auto element_coverage_hint = entity.GetContents()->GetCoverageHint();
entity.GetContents()->SetCoverageHint(Rect::Intersection(
element_coverage_hint, clip_coverage_stack_.CurrentClipCoverage()));

FilterInput::Vector inputs = {
FilterInput::Make(input_texture, entity.GetTransform().Invert()),
FilterInput::Make(entity.GetContents())};
auto contents =
ColorFilterContents::MakeBlend(entity.GetBlendMode(), inputs);
entity.SetContents(std::move(contents));
entity.SetBlendMode(BlendMode::kSource);
}
}

Expand Down Expand Up @@ -831,6 +862,7 @@ bool ExperimentalCanvas::BlitToOnscreen() {

void ExperimentalCanvas::EndReplay() {
FML_DCHECK(render_passes_.size() == 1u);
render_passes_.back().inline_pass_context->GetRenderPass(0);
render_passes_.back().inline_pass_context->EndPass();

// If requires_readback_ was true, then we rendered to an offscreen texture
Expand Down
Loading