| Flutter Impeller |

Classes | Public Member Functions | List of all members
impeller::ExperimentalCanvas Class Reference

`#include <experimental_canvas.h>`

Inheritance diagram for impeller::ExperimentalCanvas:

## Classes
struct | SaveLayerState

## Public Member Functions
| ExperimentalCanvas (ContentContext &renderer, RenderTarget &render_target, bool requires_readback)
| ExperimentalCanvas (ContentContext &renderer, RenderTarget &render_target, bool requires_readback, Rect cull_rect)
| ExperimentalCanvas (ContentContext &renderer, RenderTarget &render_target, bool requires_readback, IRect cull_rect)
| ~ExperimentalCanvas () override=default
void | Save (uint32_t total_content_depth) override
void | SaveLayer (const Paint &paint, std::optional< Rect > bounds, const std::shared_ptr< ImageFilter > &backdrop_filter, ContentBoundsPromise bounds_promise, uint32_t total_content_depth, bool can_distribute_opacity) override
bool | Restore () override
void | EndReplay ()
void | DrawTextFrame (const std::shared_ptr< TextFrame > &text_frame, Point position, const Paint &paint) override
Public Member Functions inherited from impeller::Canvas
| Canvas ()
| Canvas (Rect cull_rect)
| Canvas (IRect cull_rect)
virtual | ~Canvas ()
size_t | GetSaveCount () const
void | RestoreToCount (size_t count)
const Matrix & | GetCurrentTransform () const
const std::optional< Rect > | GetCurrentLocalCullingBounds () const
void | ResetTransform ()
void | Transform (const Matrix &transform)
void | Concat (const Matrix &transform)
void | PreConcat (const Matrix &transform)
void | Translate (const Vector3 &offset)
void | Scale (const Vector2 &scale)
void | Scale (const Vector3 &scale)
void | Skew (Scalar sx, Scalar sy)
void | Rotate (Radians radians)
void | DrawPath (const Path &path, const Paint &paint)
void | DrawPaint (const Paint &paint)
void | DrawLine (const Point &p0, const Point &p1, const Paint &paint)
void | DrawRect (const Rect &rect, const Paint &paint)
void | DrawOval (const Rect &rect, const Paint &paint)
void | DrawRRect (const Rect &rect, const Size &corner_radii, const Paint &paint)
void | DrawCircle (const Point &center, Scalar radius, const Paint &paint)
void | DrawPoints (std::vector< Point > points, Scalar radius, const Paint &paint, PointStyle point_style)
void | DrawImage (const std::shared_ptr< Image > &image, Point offset, const Paint &paint, SamplerDescriptor sampler={})
void | DrawImageRect (const std::shared_ptr< Image > &image, Rect source, Rect dest, const Paint &paint, SamplerDescriptor sampler={}, SourceRectConstraint src_rect_constraint=SourceRectConstraint::kFast)
void | ClipPath (const Path &path, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
void | ClipRect (const Rect &rect, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
void | ClipOval (const Rect &bounds, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
void | ClipRRect (const Rect &rect, const Size &corner_radii, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
void | DrawVertices (const std::shared_ptr< VerticesGeometry > &vertices, BlendMode blend_mode, const Paint &paint)
void | DrawAtlas (const std::shared_ptr< Image > &atlas, std::vector< Matrix > transforms, std::vector< Rect > texture_coordinates, std::vector< Color > colors, BlendMode blend_mode, SamplerDescriptor sampler, std::optional< Rect > cull_rect, const Paint &paint)
Picture | EndRecordingAsPicture ()

## Additional Inherited Members
Static Public Attributes inherited from impeller::Canvas
static constexpr uint32_t | kMaxDepth = 1 << 24
Protected Member Functions inherited from impeller::Canvas
size_t | GetClipHeight () const
void | Initialize (std::optional< Rect > cull_rect)
void | Reset ()
Protected Attributes inherited from impeller::Canvas
std::deque< CanvasStackEntry > | transform_stack_
std::optional< Rect > | initial_cull_rect_
uint64_t | current_depth_ = 0u

## Detailed Description

This Canvas attempts to translate from display lists to draw calls directly.

It's not fully implemented yet but if successful it will be replacing the aiks Canvas functionality.

See also:

- go/impeller-canvas-efficiency

Definition at line 40 of file experimental_canvas.h.

## Constructor & Destructor Documentation

## ◆ ExperimentalCanvas() [1/3]

impeller::ExperimentalCanvas::ExperimentalCanvas | ( | ContentContext & | renderer,
|  | RenderTarget & | render_target,
|  | bool | requires_readback
| ) |  |

Definition at line 109 of file experimental_canvas.cc.

112 : Canvas(),
113 renderer_(renderer),
114 render_target_(render_target),
115 requires_readback_(requires_readback),
116 clip_coverage_stack_(EntityPassClipStack(
117 Rect::MakeSize(render_target.GetRenderTargetSize()))) {
118 SetupRenderPass();
119 }

## ◆ ExperimentalCanvas() [2/3]

impeller::ExperimentalCanvas::ExperimentalCanvas | ( | ContentContext & | renderer,
|  | RenderTarget & | render_target,
|  | bool | requires_readback,
|  | Rect | cull_rect
| ) |  |

Definition at line 121 of file experimental_canvas.cc.

125 : Canvas(cull_rect),
126 renderer_(renderer),
127 render_target_(render_target),
128 requires_readback_(requires_readback),
129 clip_coverage_stack_(EntityPassClipStack(
130 Rect::MakeSize(render_target.GetRenderTargetSize()))) {
131 SetupRenderPass();
132 }

## ◆ ExperimentalCanvas() [3/3]

impeller::ExperimentalCanvas::ExperimentalCanvas | ( | ContentContext & | renderer,
|  | RenderTarget & | render_target,
|  | bool | requires_readback,
|  | IRect | cull_rect
| ) |  |

Definition at line 134 of file experimental_canvas.cc.

138 : Canvas(cull_rect),
139 renderer_(renderer),
140 render_target_(render_target),
141 requires_readback_(requires_readback),
142 clip_coverage_stack_(EntityPassClipStack(
143 Rect::MakeSize(render_target.GetRenderTargetSize()))) {
144 SetupRenderPass();
145 }

## ◆ ~ExperimentalCanvas()

impeller::ExperimentalCanvas::~ExperimentalCanvas | ( |  | ) |
| override default

## Member Function Documentation

## ◆ DrawTextFrame()

void impeller::ExperimentalCanvas::DrawTextFrame | ( | const std::shared_ptr< TextFrame > & | text_frame,
|  | Point | position,
|  | const Paint & | paint
| ) |  |
| override virtual

Reimplemented from impeller::Canvas.

Definition at line 558 of file experimental_canvas.cc.

561 {
562 Entity entity;
563 entity.SetClipDepth(GetClipHeight());
564 entity.SetBlendMode(paint.blend_mode);
565
566 auto text_contents = std::make_shared<TextContents>();
567 text_contents->SetTextFrame(text_frame);
568 text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
569 text_contents->SetScale(GetCurrentTransform().GetMaxBasisLengthXY());
570 text_contents->SetColor(paint.color);
571 text_contents->SetOffset(position);
572 text_contents->SetTextProperties(paint.color, //
573 paint.style == Paint::Style::kStroke, //
574 paint.stroke_width, //
575 paint.stroke_cap, //
576 paint.stroke_join, //
577 paint.stroke_miter //
578 );
579
580 entity.SetTransform(GetCurrentTransform() *
581 Matrix::MakeTranslation(position));
582
583 // TODO(bdero): This mask blur application is a hack. It will always wind up
584 // doing a gaussian blur that affects the color source itself
585 // instead of just the mask. The color filter text support
586 // needs to be reworked in order to interact correctly with
587 // mask filters.
588 // https://github.com/flutter/flutter/issues/133297
589 entity.SetContents(paint.WithFilters(paint.WithMaskBlur(
590 std::move(text_contents), true, GetCurrentTransform())));
591
592 AddRenderEntityToCurrentPass(std::move(entity), false);
593 }

References impeller::Canvas::GetClipHeight(), impeller::Canvas::GetCurrentTransform(), impeller::Paint::kStroke, impeller::Matrix::MakeTranslation(), paint, impeller::Entity::SetBlendMode(), impeller::Entity::SetClipDepth(), impeller::Entity::SetContents(), and impeller::Entity::SetTransform().

## ◆ EndReplay()

void impeller::ExperimentalCanvas::EndReplay | ( |  | ) |

Definition at line 745 of file experimental_canvas.cc.

745 {
746 FML_DCHECK(render_passes_.size() == 1u);
747 render_passes_.back().inline_pass_context->EndPass();
748
749 // If requires_readback_ was true, then we rendered to an offscreen texture
750 // instead of to the onscreen provided in the render target. Now we need to
751 // draw or blit the offscreen back to the onscreen.
752 if (requires_readback_) {
753 BlitToOnscreen();
754 }
755
756 render_passes_.clear();
757 renderer_.GetRenderTargetCache()->End();
758
759 Reset();
760 Initialize(initial_cull_rect_);
761 }

References impeller::ContentContext::GetRenderTargetCache(), impeller::Canvas::initial_cull_rect_, impeller::Canvas::Initialize(), and impeller::Canvas::Reset().

Referenced by impeller::ExperimentalDlDispatcher::FinishRecording().

## ◆ Restore()

bool impeller::ExperimentalCanvas::Restore | ( |  | ) |
| override virtual

Reimplemented from impeller::Canvas.

Definition at line 416 of file experimental_canvas.cc.

416 {
417 FML_DCHECK(transform_stack_.size() > 0);
418 if (transform_stack_.size() == 1) {
419 return false;
420 }
421
422 // This check is important to make sure we didn't exceed the depth
423 // that the clips were rendered at while rendering any of the
424 // rendering ops. It is OK for the current depth to equal the
425 // outgoing clip depth because that means the clipping would have
426 // been successful up through the last rendering op, but it cannot
427 // be greater.
428 // Also, we bump the current rendering depth to the outgoing clip
429 // depth so that future rendering operations are not clipped by
430 // any of the pixels set by the expiring clips. It is OK for the
431 // estimates used to determine the clip depth in save/saveLayer
432 // to be overly conservative, but we need to jump the depth to
433 // the clip depth so that the next rendering op will get a
434 // larger depth (it will pre-increment the current_depth_ value).
435 FML_CHECK(current_depth_ <= transform_stack_.back().clip_depth)
436 << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
437 current_depth_ = transform_stack_.back().clip_depth;
438
439 if (transform_stack_.back().rendering_mode ==
440 Entity::RenderingMode::kSubpassAppendSnapshotTransform ||
441 transform_stack_.back().rendering_mode ==
442 Entity::RenderingMode::kSubpassPrependSnapshotTransform) {
443 auto lazy_render_pass = std::move(render_passes_.back());
444 render_passes_.pop_back();
445 // Force the render pass to be constructed if it never was.
446 lazy_render_pass.inline_pass_context->GetRenderPass(0);
447
448 SaveLayerState save_layer_state = save_layer_state_.back();
449 save_layer_state_.pop_back();
450
451 std::shared_ptr<Contents> contents =
452 PaintPassDelegate(save_layer_state.paint)
453 .CreateContentsForSubpassTarget(
454 lazy_render_pass.inline_pass_context->GetTexture(),
455 transform_stack_.back().transform);
456
457 lazy_render_pass.inline_pass_context->EndPass();
458
459 // Round the subpass texture position for pixel alignment with the parent
460 // pass render target. By default, we draw subpass textures with nearest
461 // sampling, so aligning here is important for avoiding visual nearest
462 // sampling errors caused by limited floating point precision when
463 // straddling a half pixel boundary.
464 //
465 // We do this in lieu of expanding/rounding out the subpass coverage in
466 // order to keep the bounds wrapping consistently tight around subpass
467 // elements. Which is necessary to avoid intense flickering in cases
468 // where a subpass texture has a large blur filter with clamp sampling.
469 //
470 // See also this bug: https://github.com/flutter/flutter/issues/144213
471 Point subpass_texture_position =
472 (save_layer_state.coverage.GetOrigin() - GetGlobalPassPosition())
473 .Round();
474
475 Entity element_entity;
476 element_entity.SetClipDepth(++current_depth_);
477 element_entity.SetContents(std::move(contents));
478 element_entity.SetBlendMode(save_layer_state.paint.blend_mode);
479 element_entity.SetTransform(
480 Matrix::MakeTranslation(Vector3(subpass_texture_position)));
481
482 if (element_entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
483 if (renderer_.GetDeviceCapabilities().SupportsFramebufferFetch()) {
484 ApplyFramebufferBlend(element_entity);
485 } else {
486 VALIDATION_LOG << "Emulated advanced blends are currently unsupported.";
487 element_entity.SetBlendMode(BlendMode::kSourceOver);
488 }
489 }
490
491 element_entity.Render(
492 renderer_, //
493 *render_passes_.back().inline_pass_context->GetRenderPass(0).pass //
494 );
495 clip_coverage_stack_.PopSubpass();
496 transform_stack_.pop_back();
497
498 // We don't need to restore clips if a saveLayer was performed, as the clip
499 // state is per render target, and no more rendering operations will be
500 // performed as the render target workloaded is completed in the restore.
501 return true;
502 }
503
504 size_t num_clips = transform_stack_.back().num_clips;
505 transform_stack_.pop_back();
506
507 if (num_clips > 0) {
508 Entity entity;
509 entity.SetTransform(
510 Matrix::MakeTranslation(Vector3(-GetGlobalPassPosition())) *
511 GetCurrentTransform());
512 // This path is empty because ClipRestoreContents just generates a quad that
513 // takes up the full render target.
514 auto clip_restore = std::make_shared<ClipRestoreContents>();
515 clip_restore->SetRestoreHeight(GetClipHeight());
516 entity.SetContents(std::move(clip_restore));
517
518 auto current_clip_coverage = clip_coverage_stack_.CurrentClipCoverage();
519 if (current_clip_coverage.has_value()) {
520 // Entity transforms are relative to the current pass position, so we need
521 // to check clip coverage in the same space.
522 current_clip_coverage =
523 current_clip_coverage->Shift(-GetGlobalPassPosition());
524 }
525
526 auto clip_coverage = entity.GetClipCoverage(current_clip_coverage);
527 if (clip_coverage.coverage.has_value()) {
528 clip_coverage.coverage =
529 clip_coverage.coverage->Shift(GetGlobalPassPosition());
530 }
531
532 EntityPassClipStack::ClipStateResult clip_state_result =
533 clip_coverage_stack_.ApplyClipState(clip_coverage, entity,
534 GetClipHeightFloor(),
535 GetGlobalPassPosition());
536
537 if (clip_state_result.clip_did_change) {
538 // We only need to update the pass scissor if the clip state has changed.
539 SetClipScissor(
540 clip_coverage_stack_.CurrentClipCoverage(), //
541 *render_passes_.back().inline_pass_context->GetRenderPass(0).pass, //
542 GetGlobalPassPosition() //
543 );
544 }
545
546 if (!clip_state_result.should_render) {
547 return true;
548 }
549
550 entity.Render(
551 renderer_,
552 *render_passes_.back().inline_pass_context->GetRenderPass(0).pass);
553 }
554
555 return true;
556 }

References impeller::EntityPassClipStack::ApplyClipState(), impeller::Paint::blend_mode, impeller::EntityPassClipStack::ClipStateResult::clip_did_change, impeller::Contents::ClipCoverage::coverage, impeller::ExperimentalCanvas::SaveLayerState::coverage, impeller::PaintPassDelegate::CreateContentsForSubpassTarget(), impeller::Canvas::current_depth_, impeller::EntityPassClipStack::CurrentClipCoverage(), impeller::Entity::GetBlendMode(), impeller::Entity::GetClipCoverage(), impeller::Canvas::GetClipHeight(), impeller::Canvas::GetCurrentTransform(), impeller::ContentContext::GetDeviceCapabilities(), impeller::TRect< T >::GetOrigin(), impeller::Entity::kLastPipelineBlendMode, impeller::kSourceOver, impeller::Entity::kSubpassAppendSnapshotTransform, impeller::Entity::kSubpassPrependSnapshotTransform, impeller::Matrix::MakeTranslation(), impeller::ExperimentalCanvas::SaveLayerState::paint, impeller::EntityPassClipStack::PopSubpass(), impeller::Entity::Render(), impeller::Entity::SetBlendMode(), impeller::Entity::SetClipDepth(), impeller::SetClipScissor(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), impeller::EntityPassClipStack::ClipStateResult::should_render, impeller::Capabilities::SupportsFramebufferFetch(), impeller::Canvas::transform_stack_, and VALIDATION_LOG.

## ◆ Save()

void impeller::ExperimentalCanvas::Save | ( | uint32_t | total_content_depth | ) |
| override virtual

Reimplemented from impeller::Canvas.

Definition at line 193 of file experimental_canvas.cc.

193 {
194 auto entry = CanvasStackEntry{};
195 entry.transform = transform_stack_.back().transform;
196 entry.cull_rect = transform_stack_.back().cull_rect;
197 entry.clip_depth = current_depth_ + total_content_depth;
198 entry.distributed_opacity = transform_stack_.back().distributed_opacity;
199 FML_CHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
200 << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
201 << " after allocating " << total_content_depth;
202 entry.clip_height = transform_stack_.back().clip_height;
203 entry.rendering_mode = Entity::RenderingMode::kDirect;
204 transform_stack_.emplace_back(entry);
205 }

References impeller::Canvas::current_depth_, impeller::Entity::kDirect, impeller::CanvasStackEntry::transform, and impeller::Canvas::transform_stack_.

Referenced by SaveLayer().

## ◆ SaveLayer()

void impeller::ExperimentalCanvas::SaveLayer | ( | const Paint & | paint,
|  | std::optional< Rect > | bounds,
|  | const std::shared_ptr< ImageFilter > & | backdrop_filter,
|  | ContentBoundsPromise | bounds_promise,
|  | uint32_t | total_content_depth,
|  | bool | can_distribute_opacity
| ) |  |
| override virtual

Reimplemented from impeller::Canvas.

Definition at line 207 of file experimental_canvas.cc.

213 {
214 // Can we always guarantee that we get a bounds? Does a lack of bounds
215 // indicate something?
216 if (!bounds.has_value()) {
217 bounds = Rect::MakeSize(render_target_.GetRenderTargetSize());
218 }
219
220 // SaveLayer is a no-op, depending on the bounds promise. Should/Can DL elide
221 // this?
222 if (bounds->IsEmpty()) {
223 Save(total_content_depth);
224 return;
225 }
226
227 // The maximum coverage of the subpass. Subpasses textures should never
228 // extend outside the parent pass texture or the current clip coverage.
229 Rect coverage_limit = Rect::MakeOriginSize(
230 GetGlobalPassPosition(),
231 Size(render_passes_.back().inline_pass_context->GetTexture()->GetSize()));
232
233 // BDF No-op. need to do some precomputation to ensure this is fully skipped.
234 if (backdrop_filter) {
235 if (!clip_coverage_stack_.HasCoverage()) {
236 Save(total_content_depth);
237 return;
238 }
239 auto maybe_clip_coverage = clip_coverage_stack_.CurrentClipCoverage();
240 if (!maybe_clip_coverage.has_value()) {
241 Save(total_content_depth);
242 return;
243 }
244 auto clip_coverage = maybe_clip_coverage.value();
245 if (clip_coverage.IsEmpty() ||
246 !coverage_limit.IntersectsWithRect(clip_coverage)) {
247 Save(total_content_depth);
248 return;
249 }
250 }
251
252 if (can_distribute_opacity && !backdrop_filter &&
253 Paint::CanApplyOpacityPeephole(paint)) {
254 Save(total_content_depth);
255 transform_stack_.back().distributed_opacity *= paint.color.alpha;
256 return;
257 }
258
259 // Backdrop filter state, ignored if there is no BDF.
260 std::shared_ptr<FilterContents> backdrop_filter_contents;
261 Point local_position = {0, 0};
262 if (backdrop_filter) {
263 auto current_clip_coverage = clip_coverage_stack_.CurrentClipCoverage();
264 if (current_clip_coverage.has_value()) {
265 local_position =
266 current_clip_coverage->GetOrigin() - GetGlobalPassPosition();
267 }
268 EntityPass::BackdropFilterProc backdrop_filter_proc =
269 [backdrop_filter = backdrop_filter->Clone()](
270 const FilterInput::Ref& input, const Matrix& effect_transform,
271 Entity::RenderingMode rendering_mode) {
272 auto filter = backdrop_filter->WrapInput(input);
273 filter->SetEffectTransform(effect_transform);
274 filter->SetRenderingMode(rendering_mode);
275 return filter;
276 };
277
278 auto rendering_config = std::move(render_passes_.back());
279 render_passes_.pop_back();
280
281 // If the very first thing we render in this EntityPass is a subpass that
282 // happens to have a backdrop filter, than that backdrop filter will end
283 // may wind up sampling from the raw, uncleared texture that came straight
284 // out of the texture cache. By calling `pass_context.GetRenderPass` here,
285 // we force the texture to pass through at least one RenderPass with the
286 // correct clear configuration before any sampling occurs.
287 rendering_config.inline_pass_context->GetRenderPass(0);
288
289 ISize restore_size =
290 rendering_config.inline_pass_context->GetTexture()->GetSize();
291
292 std::shared_ptr<Texture> input_texture =
293 rendering_config.entity_pass_target->Flip(
294 *renderer_.GetContext()->GetResourceAllocator());
295
296 backdrop_filter_contents = backdrop_filter_proc(
297 FilterInput::Make(std::move(input_texture)),
298 transform_stack_.back().transform.Basis(),
299 // When the subpass has a translation that means the math with
300 // the snapshot has to be different.
301 transform_stack_.back().transform.HasTranslation()
302 ? Entity::RenderingMode::kSubpassPrependSnapshotTransform
303 : Entity::RenderingMode::kSubpassAppendSnapshotTransform);
304
305 // The subpass will need to read from the current pass texture when
306 // rendering the backdrop, so if there's an active pass, end it prior to
307 // rendering the subpass.
308 rendering_config.inline_pass_context->EndPass();
309
310 // Create a new render pass that the backdrop filter contents will be
311 // restored to in order to continue rendering.
312 render_passes_.push_back(LazyRenderingConfig(
313 renderer_, std::move(rendering_config.entity_pass_target)));
314 // Eagerly restore the BDF contents.
315
316 // If the pass context returns a backdrop texture, we need to draw it to the
317 // current pass. We do this because it's faster and takes significantly less
318 // memory than storing/loading large MSAA textures. Also, it's not possible
319 // to blit the non-MSAA resolve texture of the previous pass to MSAA
320 // textures (let alone a transient one).
321 Rect size_rect = Rect::MakeSize(restore_size);
322 auto msaa_backdrop_contents = TextureContents::MakeRect(size_rect);
323 msaa_backdrop_contents->SetStencilEnabled(false);
324 msaa_backdrop_contents->SetLabel("MSAA backdrop");
325 msaa_backdrop_contents->SetSourceRect(size_rect);
326 msaa_backdrop_contents->SetTexture(
327 rendering_config.inline_pass_context->GetTexture());
328
329 Entity msaa_backdrop_entity;
330 msaa_backdrop_entity.SetContents(std::move(msaa_backdrop_contents));
331 msaa_backdrop_entity.SetBlendMode(BlendMode::kSource);
332 msaa_backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
333 if (!msaa_backdrop_entity.Render(renderer_,
334 *render_passes_.back()
335 .inline_pass_context->GetRenderPass(0)
336 .pass)) {
337 VALIDATION_LOG << "Failed to render MSAA backdrop filter entity.";
338 return;
339 }
340
341 // Restore any clips that were recorded before the backdrop filter was
342 // applied.
343 auto& replay_entities = clip_coverage_stack_.GetReplayEntities();
344 for (const auto& replay : replay_entities) {
345 SetClipScissor(
346 clip_coverage_stack_.CurrentClipCoverage(),
347 *render_passes_.back().inline_pass_context->GetRenderPass(0).pass,
348 GetGlobalPassPosition());
349 if (!replay.entity.Render(renderer_,
350 *render_passes_.back()
351 .inline_pass_context->GetRenderPass(0)
352 .pass)) {
353 VALIDATION_LOG << "Failed to render entity for clip restore.";
354 }
355 }
356 }
357
358 // When applying a save layer, absorb any pending distributed opacity.
359 Paint paint_copy = paint;
360 paint_copy.color.alpha *= transform_stack_.back().distributed_opacity;
361 transform_stack_.back().distributed_opacity = 1.0;
362
363 // Backdrop Filter must expand bounds to at least the clip stack, otherwise
364 // the coverage of the parent render pass.
365 Rect subpass_coverage = bounds->TransformBounds(GetCurrentTransform());
366 if (backdrop_filter_contents) {
367 FML_CHECK(clip_coverage_stack_.HasCoverage());
368 // We should never hit this case as we check the intersection above.
369 // NOLINTBEGIN(bugprone-unchecked-optional-access)
370 subpass_coverage =
371 coverage_limit
372 .Intersection(clip_coverage_stack_.CurrentClipCoverage().value())
373 .value();
374 // NOLINTEND(bugprone-unchecked-optional-access)
375 }
376
377 render_passes_.push_back(LazyRenderingConfig(
378 renderer_, //
379 CreateRenderTarget(renderer_, //
380 ISize(subpass_coverage.GetSize()), //
381 1u, Color::BlackTransparent())));
382 save_layer_state_.push_back(SaveLayerState{paint_copy, subpass_coverage});
383
384 CanvasStackEntry entry;
385 entry.transform = transform_stack_.back().transform;
386 entry.cull_rect = transform_stack_.back().cull_rect;
387 entry.clip_depth = current_depth_ + total_content_depth;
388 FML_CHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
389 << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
390 << " after allocating " << total_content_depth;
391 entry.clip_height = transform_stack_.back().clip_height;
392 entry.rendering_mode = Entity::RenderingMode::kSubpassAppendSnapshotTransform;
393 transform_stack_.emplace_back(entry);
394
395 // Start non-collapsed subpasses with a fresh clip coverage stack limited by
396 // the subpass coverage. This is important because image filters applied to
397 // save layers may transform the subpass texture after it's rendered,
398 // causing parent clip coverage to get misaligned with the actual area that
399 // the subpass will affect in the parent pass.
400 clip_coverage_stack_.PushSubpass(subpass_coverage, GetClipHeight());
401
402 if (backdrop_filter_contents) {
403 // Render the backdrop entity.
404 Entity backdrop_entity;
405 backdrop_entity.SetContents(std::move(backdrop_filter_contents));
406 backdrop_entity.SetTransform(
407 Matrix::MakeTranslation(Vector3(-local_position)));
408 backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
409
410 backdrop_entity.Render(
411 renderer_,
412 *render_passes_.back().inline_pass_context->GetRenderPass(0).pass);
413 }
414 }

References impeller::Color::alpha, impeller::Color::BlackTransparent(), impeller::Paint::CanApplyOpacityPeephole(), impeller::CanvasStackEntry::clip_depth, impeller::CanvasStackEntry::clip_height, impeller::Paint::color, impeller::CreateRenderTarget(), impeller::CanvasStackEntry::cull_rect, impeller::Canvas::current_depth_, impeller::EntityPassClipStack::CurrentClipCoverage(), impeller::Canvas::GetClipHeight(), impeller::ContentContext::GetContext(), impeller::Canvas::GetCurrentTransform(), impeller::RenderTarget::GetRenderTargetSize(), impeller::EntityPassClipStack::GetReplayEntities(), impeller::TRect< T >::GetSize(), impeller::EntityPassClipStack::HasCoverage(), impeller::TRect< T >::Intersection(), impeller::TRect< T >::IntersectsWithRect(), impeller::kSource, impeller::Entity::kSubpassAppendSnapshotTransform, impeller::Entity::kSubpassPrependSnapshotTransform, impeller::FilterInput::Make(), impeller::TRect< Scalar >::MakeOriginSize(), impeller::TextureContents::MakeRect(), impeller::TRect< Scalar >::MakeSize(), impeller::Matrix::MakeTranslation(), paint, impeller::EntityPassClipStack::PushSubpass(), impeller::Entity::Render(), impeller::CanvasStackEntry::rendering_mode, Save(), impeller::Entity::SetBlendMode(), impeller::Entity::SetClipDepth(), impeller::SetClipScissor(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), impeller::CanvasStackEntry::transform, impeller::Canvas::transform_stack_, impeller::TRect< T >::TransformBounds(), and VALIDATION_LOG.

The documentation for this class was generated from the following files:

- impeller/aiks/experimental_canvas.h
- impeller/aiks/experimental_canvas.cc

impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::Entity::kLastPipelineBlendMode
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:22
impeller::EntityPass::BackdropFilterProc
std::function< std::shared_ptr< FilterContents >(FilterInput::Ref, const Matrix &effect_transform, Entity::RenderingMode rendering_mode)> BackdropFilterProc
Definition: entity_pass.h:59
impeller::EntityPassClipStack::PushSubpass
void PushSubpass(std::optional< Rect > subpass_coverage, size_t clip_height)
Definition: entity_pass_clip_stack.cc:31
impeller::Paint::Style::kStroke
@ kStroke
impeller::FilterInput::Make
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
Definition: filter_input.cc:19
impeller::BlendMode::kSource
@ kSource
impeller::Entity::RenderingMode::kSubpassAppendSnapshotTransform
@ kSubpassAppendSnapshotTransform
impeller::TRect::TransformBounds
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:463
impeller::EntityPassClipStack::HasCoverage
bool HasCoverage() const
Definition: entity_pass_clip_stack.cc:27
impeller::FilterInput::Ref
std::shared_ptr< FilterInput > Ref
Definition: filter_input.h:32
impeller::ExperimentalCanvas::Save
void Save(uint32_t total_content_depth) override
Definition: experimental_canvas.cc:193
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Canvas::initial_cull_rect_
std::optional< Rect > initial_cull_rect_
Definition: canvas.h:182
impeller::Canvas::GetCurrentTransform
const Matrix & GetCurrentTransform() const
Definition: canvas.cc:298
impeller::Canvas::Initialize
void Initialize(std::optional< Rect > cull_rect)
Definition: canvas.cc:164
impeller::EntityPassClipStack::ApplyClipState
ClipStateResult ApplyClipState(Contents::ClipCoverage global_clip_coverage, Entity &entity, size_t clip_height_floor, Point global_pass_position)
Applies the current clip state to an Entity. If the given Entity is a clip operation,...
Definition: entity_pass_clip_stack.cc:51
impeller::EntityPassClipStack::GetReplayEntities
const std::vector< ReplayResult > & GetReplayEntities() const
Definition: entity_pass_clip_stack.cc:158
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
impeller::EntityPassClipStack::CurrentClipCoverage
std::optional< Rect > CurrentClipCoverage() const
Definition: entity_pass_clip_stack.cc:23
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:553
impeller::TRect< Scalar >::MakeOriginSize
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
impeller::CreateRenderTarget
static std::unique_ptr< EntityPassTarget > CreateRenderTarget(ContentContext &renderer, ISize size, int mip_count, const Color &clear_color)
Definition: experimental_canvas.cc:55
impeller::Entity::RenderingMode::kDirect
@ kDirect
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:769
impeller::SetClipScissor
static void SetClipScissor(std::optional< Rect > clip_coverage, RenderPass &pass, Point global_pass_position)
Definition: entity_pass.cc:725
impeller::Canvas::Canvas
Canvas()
Definition: canvas.cc:149
impeller::EntityPassClipStack::PopSubpass
void PopSubpass()
Definition: entity_pass_clip_stack.cc:42
impeller::Canvas::current_depth_
uint64_t current_depth_
Definition: canvas.h:183
impeller::RenderTarget::GetRenderTargetSize
ISize GetRenderTargetSize() const
Definition: render_target.cc:139
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::Canvas::GetClipHeight
size_t GetClipHeight() const
Definition: canvas.cc:825
impeller::ContentContext::GetRenderTargetCache
const std::shared_ptr< RenderTargetAllocator > & GetRenderTargetCache() const
Definition: content_context.h:724
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:557
impeller::Entity::RenderingMode::kSubpassPrependSnapshotTransform
@ kSubpassPrependSnapshotTransform
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:272
paint
const Paint & paint
Definition: color_source.cc:38
impeller::Paint::CanApplyOpacityPeephole
static bool CanApplyOpacityPeephole(const Paint &paint)
Whether or not a save layer with the provided paint can perform the opacity peephole optimization.
Definition: paint.h:36
impeller::Entity::RenderingMode
RenderingMode
Definition: entity.h:27
impeller::Capabilities::SupportsFramebufferFetch
virtual bool SupportsFramebufferFetch() const =0
Whether the context backend is able to support pipelines with shaders that read from the framebuffer ...
impeller::TextureContents::MakeRect
static std::shared_ptr< TextureContents > MakeRect(Rect destination)
A common case factory that marks the texture contents as having a destination rectangle....
Definition: texture_contents.cc:27
impeller::Canvas::transform_stack_
std::deque< CanvasStackEntry > transform_stack_
Definition: canvas.h:181
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Canvas::Reset
void Reset()
Definition: canvas.cc:177