Flutter Engine
The Flutter Engine
entity_pass.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <limits>
8#include <memory>
9#include <utility>
10#include <variant>
11
12#include "flutter/fml/closure.h"
13#include "flutter/fml/logging.h"
14#include "flutter/fml/trace_event.h"
30
31namespace impeller {
32
33namespace {
34std::tuple<std::optional<Color>, BlendMode> ElementAsBackgroundColor(
35 const EntityPass::Element& element,
36 ISize target_size) {
37 if (const Entity* entity = std::get_if<Entity>(&element)) {
38 std::optional<Color> entity_color = entity->AsBackgroundColor(target_size);
39 if (entity_color.has_value()) {
40 return {entity_color.value(), entity->GetBlendMode()};
41 }
42 }
43 return {};
44}
45} // namespace
46
47EntityPass::EntityPass() = default;
48
49EntityPass::~EntityPass() = default;
50
51void EntityPass::SetDelegate(std::shared_ptr<EntityPassDelegate> delegate) {
52 if (!delegate) {
53 return;
54 }
55 delegate_ = std::move(delegate);
56}
57
58void EntityPass::SetBoundsLimit(std::optional<Rect> bounds_limit,
59 ContentBoundsPromise bounds_promise) {
60 bounds_limit_ = bounds_limit;
61 bounds_promise_ = bounds_limit.has_value() ? bounds_promise
63}
64
65std::optional<Rect> EntityPass::GetBoundsLimit() const {
66 return bounds_limit_;
67}
68
70 switch (bounds_promise_) {
72 // If the promise is unknown due to not having a bounds limit,
73 // then no clipping will occur. But if we have a bounds limit
74 // and it is unkown, then we can make no promises about whether
75 // it causes clipping of the entity pass contents and we
76 // conservatively return true.
77 return bounds_limit_.has_value();
79 FML_DCHECK(bounds_limit_.has_value());
80 return false;
82 FML_DCHECK(bounds_limit_.has_value());
83 return true;
84 }
86}
87
89 switch (bounds_promise_) {
91 return false;
94 FML_DCHECK(bounds_limit_.has_value());
95 return true;
96 }
98}
99
101 if (entity.GetBlendMode() == BlendMode::kSourceOver &&
102 entity.GetContents()->IsOpaque()) {
104 }
105
107 advanced_blend_reads_from_pass_texture_ = true;
108 }
109 elements_.emplace_back(std::move(entity));
110}
111
113 elements_.emplace_back(std::move(entity));
114 active_clips_.emplace_back(elements_.size() - 1);
115}
116
117void EntityPass::PopClips(size_t num_clips, uint64_t depth) {
118 if (num_clips > active_clips_.size()) {
120 << "Attempted to pop more clips than are currently active. Active: "
121 << active_clips_.size() << ", Popped: " << num_clips
122 << ", Depth: " << depth;
123 }
124
125 size_t max = std::min(num_clips, active_clips_.size());
126 for (size_t i = 0; i < max; i++) {
127 FML_DCHECK(active_clips_.back() < elements_.size());
128 Entity* element = std::get_if<Entity>(&elements_[active_clips_.back()]);
129 FML_DCHECK(element);
130 element->SetClipDepth(depth);
131 active_clips_.pop_back();
132 }
133}
134
135void EntityPass::PopAllClips(uint64_t depth) {
136 PopClips(active_clips_.size(), depth);
137}
138
139void EntityPass::SetElements(std::vector<Element> elements) {
140 elements_ = std::move(elements);
141}
142
144 size_t max_subpass_depth = 0u;
145 for (const auto& element : elements_) {
146 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
147 max_subpass_depth =
148 std::max(max_subpass_depth, subpass->get()->GetSubpassesDepth());
149 }
150 }
151 return max_subpass_depth + 1u;
152}
153
155 std::optional<Rect> coverage_limit) const {
156 std::optional<Rect> accumulated_coverage;
157 for (const auto& element : elements_) {
158 std::optional<Rect> element_coverage;
159
160 if (auto entity = std::get_if<Entity>(&element)) {
161 element_coverage = entity->GetCoverage();
162
163 // When the coverage limit is std::nullopt, that means there is no limit,
164 // as opposed to empty coverage.
165 if (element_coverage.has_value() && coverage_limit.has_value()) {
166 const auto* filter = entity->GetContents()->AsFilter();
167 if (!filter || filter->IsTranslationOnly()) {
168 element_coverage =
169 element_coverage->Intersection(coverage_limit.value());
170 }
171 }
172 } else if (auto subpass_ptr =
173 std::get_if<std::unique_ptr<EntityPass>>(&element)) {
174 auto& subpass = *subpass_ptr->get();
175
176 std::optional<Rect> unfiltered_coverage =
177 GetSubpassCoverage(subpass, std::nullopt);
178
179 // If the current pass elements have any coverage so far and there's a
180 // backdrop filter, then incorporate the backdrop filter in the
181 // pre-filtered coverage of the subpass.
182 if (accumulated_coverage.has_value() && subpass.backdrop_filter_proc_) {
183 std::shared_ptr<FilterContents> backdrop_filter =
184 subpass.backdrop_filter_proc_(
185 FilterInput::Make(accumulated_coverage.value()),
186 subpass.transform_,
188 if (backdrop_filter) {
189 auto backdrop_coverage = backdrop_filter->GetCoverage({});
190 unfiltered_coverage =
191 Rect::Union(unfiltered_coverage, backdrop_coverage);
192 } else {
193 VALIDATION_LOG << "The EntityPass backdrop filter proc didn't return "
194 "a valid filter.";
195 }
196 }
197
198 if (!unfiltered_coverage.has_value()) {
199 continue;
200 }
201
202 // Additionally, subpass textures may be passed through filters, which may
203 // modify the coverage.
204 //
205 // Note that we currently only assume that ImageFilters (such as blurs and
206 // matrix transforms) may modify coverage, although it's technically
207 // possible ColorFilters to affect coverage as well. For example: A
208 // ColorMatrixFilter could output a completely transparent result, and
209 // we could potentially detect this case as zero coverage in the future.
210 std::shared_ptr<FilterContents> image_filter =
211 subpass.delegate_->WithImageFilter(*unfiltered_coverage,
212 subpass.transform_);
213 if (image_filter) {
214 Entity subpass_entity;
215 subpass_entity.SetTransform(subpass.transform_);
216 element_coverage = image_filter->GetCoverage(subpass_entity);
217 } else {
218 element_coverage = unfiltered_coverage;
219 }
220
221 element_coverage = Rect::Intersection(element_coverage, coverage_limit);
222 } else {
224 }
225
226 accumulated_coverage = Rect::Union(accumulated_coverage, element_coverage);
227 }
228 return accumulated_coverage;
229}
230
232 const EntityPass& subpass,
233 std::optional<Rect> coverage_limit) const {
234 if (subpass.bounds_limit_.has_value() && subpass.GetBoundsLimitIsSnug()) {
235 return subpass.bounds_limit_->TransformBounds(subpass.transform_);
236 }
237
238 std::shared_ptr<FilterContents> image_filter =
239 subpass.delegate_->WithImageFilter(Rect(), subpass.transform_);
240
241 // If the subpass has an image filter, then its coverage space may deviate
242 // from the parent pass and make intersecting with the pass coverage limit
243 // unsafe.
244 if (image_filter && coverage_limit.has_value()) {
245 coverage_limit = image_filter->GetSourceCoverage(subpass.transform_,
246 coverage_limit.value());
247 }
248
249 auto entities_coverage = subpass.GetElementsCoverage(coverage_limit);
250 // The entities don't cover anything. There is nothing to do.
251 if (!entities_coverage.has_value()) {
252 return std::nullopt;
253 }
254
255 if (!subpass.bounds_limit_.has_value()) {
256 return entities_coverage;
257 }
258 auto user_bounds_coverage =
259 subpass.bounds_limit_->TransformBounds(subpass.transform_);
260 return entities_coverage->Intersection(user_bounds_coverage);
261}
262
264 return superpass_;
265}
266
267EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
268 if (!pass) {
269 return nullptr;
270 }
271 FML_DCHECK(pass->superpass_ == nullptr);
272 pass->superpass_ = this;
273
274 if (pass->backdrop_filter_proc_) {
275 backdrop_filter_reads_from_pass_texture_ = true;
276 }
277 if (pass->blend_mode_ > Entity::kLastPipelineBlendMode) {
278 advanced_blend_reads_from_pass_texture_ = true;
279 }
280
281 auto subpass_pointer = pass.get();
282 elements_.emplace_back(std::move(pass));
283 return subpass_pointer;
284}
285
289 .load_action = LoadAction::kDontCare,
290 .store_action = StoreAction::kDontCare,
291 };
292
294 ISize size,
295 int mip_count,
296 const Color& clear_color) {
297 const std::shared_ptr<Context>& context = renderer.GetContext();
298
299 /// All of the load/store actions are managed by `InlinePassContext` when
300 /// `RenderPasses` are created, so we just set them to `kDontCare` here.
301 /// What's important is the `StorageMode` of the textures, which cannot be
302 /// changed for the lifetime of the textures.
303
304 if (context->GetBackendType() == Context::BackendType::kOpenGLES) {
305 // TODO(https://github.com/flutter/flutter/issues/141732): Implement mip map
306 // generation on opengles.
307 mip_count = 1;
308 }
309
311 if (context->GetCapabilities()->SupportsOffscreenMSAA()) {
312 target = renderer.GetRenderTargetCache()->CreateOffscreenMSAA(
313 /*context=*/*context,
314 /*size=*/size,
315 /*mip_count=*/mip_count,
316 /*label=*/"EntityPass",
317 /*color_attachment_config=*/
320 .resolve_storage_mode = StorageMode::kDevicePrivate,
321 .load_action = LoadAction::kDontCare,
322 .store_action = StoreAction::kMultisampleResolve,
323 .clear_color = clear_color},
324 /*stencil_attachment_config=*/
326 } else {
327 target = renderer.GetRenderTargetCache()->CreateOffscreen(
328 *context, // context
329 size, // size
330 /*mip_count=*/mip_count,
331 "EntityPass", // label
334 .load_action = LoadAction::kDontCare,
335 .store_action = StoreAction::kDontCare,
336 .clear_color = clear_color,
337 }, // color_attachment_config
338 kDefaultStencilConfig // stencil_attachment_config
339 );
340 }
341
342 return EntityPassTarget(
343 target, renderer.GetDeviceCapabilities().SupportsReadFromResolve(),
344 renderer.GetDeviceCapabilities().SupportsImplicitResolvingMSAA());
345}
346
347bool EntityPass::DoesBackdropGetRead(ContentContext& renderer) const {
348 return renderer.GetDeviceCapabilities().SupportsFramebufferFetch()
349 ? backdrop_filter_reads_from_pass_texture_
350 : backdrop_filter_reads_from_pass_texture_ ||
351 advanced_blend_reads_from_pass_texture_;
352}
353
355 const RenderTarget& render_target) const {
356 renderer.GetRenderTargetCache()->Start();
357 fml::ScopedCleanupClosure reset_state([&renderer]() {
358 renderer.GetLazyGlyphAtlas()->ResetTextFrames();
359 renderer.GetRenderTargetCache()->End();
360 });
361
362 auto root_render_target = render_target;
363
364 if (root_render_target.GetColorAttachments().find(0u) ==
365 root_render_target.GetColorAttachments().end()) {
366 VALIDATION_LOG << "The root RenderTarget must have a color attachment.";
367 return false;
368 }
369 if (root_render_target.GetDepthAttachment().has_value() !=
370 root_render_target.GetStencilAttachment().has_value()) {
371 VALIDATION_LOG << "The root RenderTarget should have a stencil attachment "
372 "iff it has a depth attachment.";
373 return false;
374 }
375
376 const auto& lazy_glyph_atlas = renderer.GetLazyGlyphAtlas();
377 IterateAllEntities([&lazy_glyph_atlas](const Entity& entity) {
378 if (const auto& contents = entity.GetContents()) {
379 contents->PopulateGlyphAtlas(lazy_glyph_atlas, entity.DeriveTextScale());
380 }
381 return true;
382 });
383
385 Rect::MakeSize(root_render_target.GetRenderTargetSize()));
386
387 // In this branch path, we need to render everything to an offscreen texture
388 // and then blit the results onto the onscreen texture. If using this branch,
389 // there's no need to set up a stencil attachment on the root render target.
390 if (DoesBackdropGetRead(renderer)) {
391 EntityPassTarget offscreen_target = CreateRenderTarget(
392 renderer, root_render_target.GetRenderTargetSize(),
395
396 if (!OnRender(renderer, // renderer
397 offscreen_target.GetRenderTarget()
398 .GetRenderTargetSize(), // root_pass_size
399 offscreen_target, // pass_target
400 Point(), // global_pass_position
401 Point(), // local_pass_position
402 0, // pass_depth
403 clip_stack // clip_coverage_stack
404 )) {
405 // Validation error messages are triggered for all `OnRender()` failure
406 // cases.
407 return false;
408 }
409
410 auto command_buffer = renderer.GetContext()->CreateCommandBuffer();
411 command_buffer->SetLabel("EntityPass Root Command Buffer");
412
413 // If the context supports blitting, blit the offscreen texture to the
414 // onscreen texture. Otherwise, draw it to the parent texture using a
415 // pipeline (slower).
416 if (renderer.GetContext()
417 ->GetCapabilities()
418 ->SupportsTextureToTextureBlits()) {
419 auto blit_pass = command_buffer->CreateBlitPass();
420 blit_pass->AddCopy(
421 offscreen_target.GetRenderTarget().GetRenderTargetTexture(),
422 root_render_target.GetRenderTargetTexture());
423 if (!blit_pass->EncodeCommands(
424 renderer.GetContext()->GetResourceAllocator())) {
425 VALIDATION_LOG << "Failed to encode root pass blit command.";
426 return false;
427 }
428 if (!renderer.GetContext()
429 ->GetCommandQueue()
430 ->Submit({command_buffer})
431 .ok()) {
432 return false;
433 }
434 } else {
435 auto render_pass = command_buffer->CreateRenderPass(root_render_target);
436 render_pass->SetLabel("EntityPass Root Render Pass");
437
438 {
439 auto size_rect = Rect::MakeSize(
440 offscreen_target.GetRenderTarget().GetRenderTargetSize());
441 auto contents = TextureContents::MakeRect(size_rect);
442 contents->SetTexture(
443 offscreen_target.GetRenderTarget().GetRenderTargetTexture());
444 contents->SetSourceRect(size_rect);
445 contents->SetLabel("Root pass blit");
446
447 Entity entity;
448 entity.SetContents(contents);
450
451 if (!entity.Render(renderer, *render_pass)) {
452 VALIDATION_LOG << "Failed to render EntityPass root blit.";
453 return false;
454 }
455 }
456
457 if (!render_pass->EncodeCommands()) {
458 VALIDATION_LOG << "Failed to encode root pass command buffer.";
459 return false;
460 }
461 if (!renderer.GetContext()
462 ->GetCommandQueue()
463 ->Submit({command_buffer})
464 .ok()) {
465 return false;
466 }
467 }
468
469 return true;
470 }
471
472 // If we make it this far, that means the context is capable of rendering
473 // everything directly to the onscreen texture.
474
475 // The safety check for fetching this color attachment is at the beginning of
476 // this method.
477 auto color0 = root_render_target.GetColorAttachments().find(0u)->second;
478
479 auto stencil_attachment = root_render_target.GetStencilAttachment();
480 auto depth_attachment = root_render_target.GetDepthAttachment();
481 if (!stencil_attachment.has_value() || !depth_attachment.has_value()) {
482 // Setup a new root stencil with an optimal configuration if one wasn't
483 // provided by the caller.
484 root_render_target.SetupDepthStencilAttachments(
485 *renderer.GetContext(), *renderer.GetContext()->GetResourceAllocator(),
486 color0.texture->GetSize(),
487 renderer.GetContext()->GetCapabilities()->SupportsOffscreenMSAA(),
488 "ImpellerOnscreen", kDefaultStencilConfig);
489 }
490
491 // Set up the clear color of the root pass.
492 color0.clear_color =
494 root_render_target.SetColorAttachment(color0, 0);
495
496 EntityPassTarget pass_target(
497 root_render_target,
498 renderer.GetDeviceCapabilities().SupportsReadFromResolve(),
499 renderer.GetDeviceCapabilities().SupportsImplicitResolvingMSAA());
500
501 return OnRender( //
502 renderer, // renderer
503 root_render_target.GetRenderTargetSize(), // root_pass_size
504 pass_target, // pass_target
505 Point(), // global_pass_position
506 Point(), // local_pass_position
507 0, // pass_depth
508 clip_stack); // clip_coverage_stack
509}
510
511EntityPass::EntityResult EntityPass::GetEntityForElement(
512 const EntityPass::Element& element,
514 InlinePassContext& pass_context,
515 ISize root_pass_size,
516 Point global_pass_position,
517 uint32_t pass_depth,
518 EntityPassClipStack& clip_coverage_stack,
519 size_t clip_height_floor) const {
520 //--------------------------------------------------------------------------
521 /// Setup entity element.
522 ///
523 if (const auto& entity = std::get_if<Entity>(&element)) {
524 Entity element_entity = entity->Clone();
525
526 if (!global_pass_position.IsZero()) {
527 // If the pass image is going to be rendered with a non-zero position,
528 // apply the negative translation to entity copies before rendering them
529 // so that they'll end up rendering to the correct on-screen position.
530 element_entity.SetTransform(
531 Matrix::MakeTranslation(Vector3(-global_pass_position)) *
532 element_entity.GetTransform());
533 }
534 return EntityPass::EntityResult::Success(std::move(element_entity));
535 }
536
537 //--------------------------------------------------------------------------
538 /// Setup subpass element.
539 ///
540 if (const auto& subpass_ptr =
541 std::get_if<std::unique_ptr<EntityPass>>(&element)) {
542 auto subpass = subpass_ptr->get();
543 if (subpass->delegate_->CanElide()) {
544 return EntityPass::EntityResult::Skip();
545 }
546
547 if (!subpass->backdrop_filter_proc_ &&
548 subpass->delegate_->CanCollapseIntoParentPass(subpass)) {
549 // Directly render into the parent target and move on.
550 if (!subpass->OnRender(
551 renderer, // renderer
552 root_pass_size, // root_pass_size
553 pass_context.GetPassTarget(), // pass_target
554 global_pass_position, // global_pass_position
555 Point(), // local_pass_position
556 pass_depth, // pass_depth
557 clip_coverage_stack, // clip_coverage_stack
558 clip_height_, // clip_height_floor
559 nullptr, // backdrop_filter_contents
560 pass_context.GetRenderPass(pass_depth) // collapsed_parent_pass
561 )) {
562 // Validation error messages are triggered for all `OnRender()` failure
563 // cases.
565 }
566 return EntityPass::EntityResult::Skip();
567 }
568
569 std::shared_ptr<Contents> subpass_backdrop_filter_contents = nullptr;
570 if (subpass->backdrop_filter_proc_) {
571 auto texture = pass_context.GetTexture();
572 // Render the backdrop texture before any of the pass elements.
573 const auto& proc = subpass->backdrop_filter_proc_;
574
575 subpass_backdrop_filter_contents = proc(
576 FilterInput::Make(std::move(texture)), subpass->transform_.Basis(),
577 // When the subpass has a translation that means the math with
578 // the snapshot has to be different.
579 subpass->transform_.HasTranslation()
582
583 // If the very first thing we render in this EntityPass is a subpass that
584 // happens to have a backdrop filter, than that backdrop filter will end
585 // may wind up sampling from the raw, uncleared texture that came straight
586 // out of the texture cache. By calling `pass_context.GetRenderPass` here,
587 // we force the texture to pass through at least one RenderPass with the
588 // correct clear configuration before any sampling occurs.
589 pass_context.GetRenderPass(pass_depth);
590
591 // The subpass will need to read from the current pass texture when
592 // rendering the backdrop, so if there's an active pass, end it prior to
593 // rendering the subpass.
594 pass_context.EndPass();
595 }
596
597 if (!clip_coverage_stack.HasCoverage()) {
598 // The current clip is empty. This means the pass texture won't be
599 // visible, so skip it.
600 return EntityPass::EntityResult::Skip();
601 }
602 auto clip_coverage_back = clip_coverage_stack.CurrentClipCoverage();
603 if (!clip_coverage_back.has_value()) {
604 return EntityPass::EntityResult::Skip();
605 }
606
607 // The maximum coverage of the subpass. Subpasses textures should never
608 // extend outside the parent pass texture or the current clip coverage.
609 auto coverage_limit = Rect::MakeOriginSize(global_pass_position,
610 Size(pass_context.GetPassTarget()
613 .Intersection(clip_coverage_back.value());
614 if (!coverage_limit.has_value()) {
615 return EntityPass::EntityResult::Skip();
616 }
617
618 coverage_limit =
619 coverage_limit->Intersection(Rect::MakeSize(root_pass_size));
620 if (!coverage_limit.has_value()) {
621 return EntityPass::EntityResult::Skip();
622 }
623
624 auto subpass_coverage =
625 (subpass->flood_clip_ || subpass_backdrop_filter_contents)
626 ? coverage_limit
627 : GetSubpassCoverage(*subpass, coverage_limit);
628 if (!subpass_coverage.has_value()) {
629 return EntityPass::EntityResult::Skip();
630 }
631
632 auto subpass_size = ISize(subpass_coverage->GetSize());
633 if (subpass_size.IsEmpty()) {
634 return EntityPass::EntityResult::Skip();
635 }
636
637 auto subpass_target = CreateRenderTarget(
638 renderer, // renderer
639 subpass_size, // size
640 subpass->GetRequiredMipCount(),
641 subpass->GetClearColorOrDefault(subpass_size)); // clear_color
642
643 if (!subpass_target.IsValid()) {
644 VALIDATION_LOG << "Subpass render target is invalid.";
646 }
647
648 // Start non-collapsed subpasses with a fresh clip coverage stack limited by
649 // the subpass coverage. This is important because image filters applied to
650 // save layers may transform the subpass texture after it's rendered,
651 // causing parent clip coverage to get misaligned with the actual area that
652 // the subpass will affect in the parent pass.
653 clip_coverage_stack.PushSubpass(subpass_coverage, subpass->clip_height_);
654
655 // Stencil textures aren't shared between EntityPasses (as much of the
656 // time they are transient).
657 if (!subpass->OnRender(
658 renderer, // renderer
659 root_pass_size, // root_pass_size
660 subpass_target, // pass_target
661 subpass_coverage->GetOrigin(), // global_pass_position
662 subpass_coverage->GetOrigin() -
663 global_pass_position, // local_pass_position
664 ++pass_depth, // pass_depth
665 clip_coverage_stack, // clip_coverage_stack
666 subpass->clip_height_, // clip_height_floor
667 subpass_backdrop_filter_contents // backdrop_filter_contents
668 )) {
669 // Validation error messages are triggered for all `OnRender()` failure
670 // cases.
672 }
673
674 clip_coverage_stack.PopSubpass();
675
676 // The subpass target's texture may have changed during OnRender.
677 auto subpass_texture =
678 subpass_target.GetRenderTarget().GetRenderTargetTexture();
679
680 auto offscreen_texture_contents =
681 subpass->delegate_->CreateContentsForSubpassTarget(
682 subpass_texture,
683 Matrix::MakeTranslation(Vector3{-global_pass_position}) *
684 subpass->transform_);
685
686 if (!offscreen_texture_contents) {
687 // This is an error because the subpass delegate said the pass couldn't
688 // be collapsed into its parent. Yet, when asked how it want's to
689 // postprocess the offscreen texture, it couldn't give us an answer.
690 //
691 // Theoretically, we could collapse the pass now. But that would be
692 // wasteful as we already have the offscreen texture and we don't want
693 // to discard it without ever using it. Just make the delegate do the
694 // right thing.
696 }
697
698 // Round the subpass texture position for pixel alignment with the parent
699 // pass render target. By default, we draw subpass textures with nearest
700 // sampling, so aligning here is important for avoiding visual nearest
701 // sampling errors caused by limited floating point precision when
702 // straddling a half pixel boundary.
703 //
704 // We do this in lieu of expanding/rounding out the subpass coverage in
705 // order to keep the bounds wrapping consistently tight around subpass
706 // elements. Which is necessary to avoid intense flickering in cases
707 // where a subpass texture has a large blur filter with clamp sampling.
708 //
709 // See also this bug: https://github.com/flutter/flutter/issues/144213
710 Point subpass_texture_position =
711 (subpass_coverage->GetOrigin() - global_pass_position).Round();
712
713 Entity element_entity;
714 element_entity.SetClipDepth(subpass->clip_depth_);
715 element_entity.SetContents(std::move(offscreen_texture_contents));
716 element_entity.SetBlendMode(subpass->blend_mode_);
717 element_entity.SetTransform(
718 Matrix::MakeTranslation(Vector3(subpass_texture_position)));
719
720 return EntityPass::EntityResult::Success(std::move(element_entity));
721 }
723}
724
725static void SetClipScissor(std::optional<Rect> clip_coverage,
726 RenderPass& pass,
727 Point global_pass_position) {
728 // Set the scissor to the clip coverage area. We do this prior to rendering
729 // the clip itself and all its contents.
730 IRect scissor;
731 if (clip_coverage.has_value()) {
732 clip_coverage = clip_coverage->Shift(-global_pass_position);
733 scissor = IRect::RoundOut(clip_coverage.value());
734 // The scissor rect must not exceed the size of the render target.
735 scissor = scissor.Intersection(IRect::MakeSize(pass.GetRenderTargetSize()))
736 .value_or(IRect());
737 }
738 pass.SetScissor(scissor);
739}
740
741bool EntityPass::RenderElement(Entity& element_entity,
742 size_t clip_height_floor,
743 InlinePassContext& pass_context,
744 int32_t pass_depth,
745 ContentContext& renderer,
746 EntityPassClipStack& clip_coverage_stack,
747 Point global_pass_position) const {
748 auto result = pass_context.GetRenderPass(pass_depth);
749 if (!result.pass) {
750 // Failure to produce a render pass should be explained by specific errors
751 // in `InlinePassContext::GetRenderPass()`, so avoid log spam and don't
752 // append a validation log here.
753 return false;
754 }
755
756 // If the pass context returns a backdrop texture, we need to draw it to the
757 // current pass. We do this because it's faster and takes significantly less
758 // memory than storing/loading large MSAA textures. Also, it's not possible to
759 // blit the non-MSAA resolve texture of the previous pass to MSAA textures
760 // (let alone a transient one).
761 if (result.backdrop_texture) {
762 auto size_rect = Rect::MakeSize(result.pass->GetRenderTargetSize());
763 auto msaa_backdrop_contents = TextureContents::MakeRect(size_rect);
764 msaa_backdrop_contents->SetStencilEnabled(false);
765 msaa_backdrop_contents->SetLabel("MSAA backdrop");
766 msaa_backdrop_contents->SetSourceRect(size_rect);
767 msaa_backdrop_contents->SetTexture(result.backdrop_texture);
768
769 Entity msaa_backdrop_entity;
770 msaa_backdrop_entity.SetContents(std::move(msaa_backdrop_contents));
771 msaa_backdrop_entity.SetBlendMode(BlendMode::kSource);
772 msaa_backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
773 if (!msaa_backdrop_entity.Render(renderer, *result.pass)) {
774 VALIDATION_LOG << "Failed to render MSAA backdrop filter entity.";
775 return false;
776 }
777 }
778
779 if (result.just_created) {
780 // Restore any clips that were recorded before the backdrop filter was
781 // applied.
782 auto& replay_entities = clip_coverage_stack.GetReplayEntities();
783 for (const auto& replay : replay_entities) {
784 SetClipScissor(clip_coverage_stack.CurrentClipCoverage(), *result.pass,
785 global_pass_position);
786 if (!replay.entity.Render(renderer, *result.pass)) {
787 VALIDATION_LOG << "Failed to render entity for clip restore.";
788 }
789 }
790 }
791
792 auto current_clip_coverage = clip_coverage_stack.CurrentClipCoverage();
793 if (current_clip_coverage.has_value()) {
794 // Entity transforms are relative to the current pass position, so we need
795 // to check clip coverage in the same space.
796 current_clip_coverage = current_clip_coverage->Shift(-global_pass_position);
797 }
798
799 if (!element_entity.ShouldRender(current_clip_coverage)) {
800 return true; // Nothing to render.
801 }
802
803 auto clip_coverage = element_entity.GetClipCoverage(current_clip_coverage);
804 if (clip_coverage.coverage.has_value()) {
805 clip_coverage.coverage =
806 clip_coverage.coverage->Shift(global_pass_position);
807 }
808
809 // The coverage hint tells the rendered Contents which portion of the
810 // rendered output will actually be used, and so we set this to the current
811 // clip coverage (which is the max clip bounds). The contents may
812 // optionally use this hint to avoid unnecessary rendering work.
813 auto element_coverage_hint = element_entity.GetContents()->GetCoverageHint();
814 element_entity.GetContents()->SetCoverageHint(
815 Rect::Intersection(element_coverage_hint, current_clip_coverage));
816
817 EntityPassClipStack::ClipStateResult clip_state_result =
818 clip_coverage_stack.ApplyClipState(clip_coverage, element_entity,
819 clip_height_floor,
820 global_pass_position);
821
822 if (clip_state_result.clip_did_change) {
823 // We only need to update the pass scissor if the clip state has changed.
824 SetClipScissor(clip_coverage_stack.CurrentClipCoverage(), *result.pass,
825 global_pass_position);
826 }
827
828 if (!clip_state_result.should_render) {
829 return true;
830 }
831
832 if (!element_entity.Render(renderer, *result.pass)) {
833 VALIDATION_LOG << "Failed to render entity.";
834 return false;
835 }
836 return true;
837}
838
839bool EntityPass::OnRender(
840 ContentContext& renderer,
841 ISize root_pass_size,
842 EntityPassTarget& pass_target,
843 Point global_pass_position,
844 Point local_pass_position,
845 uint32_t pass_depth,
846 EntityPassClipStack& clip_coverage_stack,
847 size_t clip_height_floor,
848 std::shared_ptr<Contents> backdrop_filter_contents,
849 const std::optional<InlinePassContext::RenderPassResult>&
850 collapsed_parent_pass) const {
851 TRACE_EVENT0("impeller", "EntityPass::OnRender");
852
853 if (!active_clips_.empty()) {
855 "EntityPass (Depth=%d) contains one or more clips with an unresolved "
856 "depth value.",
857 pass_depth);
858 }
859
860 InlinePassContext pass_context(renderer, pass_target, GetElementCount(),
861 collapsed_parent_pass);
862 if (!pass_context.IsValid()) {
863 VALIDATION_LOG << SPrintF("Pass context invalid (Depth=%d)", pass_depth);
864 return false;
865 }
866 auto clear_color_size = pass_target.GetRenderTarget().GetRenderTargetSize();
867
868 if (!collapsed_parent_pass) {
869 // Always force the pass to construct the render pass object, even if there
870 // is not a clear color. This ensures that the attachment textures are
871 // cleared/transitioned to the right state.
872 pass_context.GetRenderPass(pass_depth);
873 }
874
875 if (backdrop_filter_proc_) {
876 if (!backdrop_filter_contents) {
878 << "EntityPass contains a backdrop filter, but no backdrop filter "
879 "contents was supplied by the parent pass at render time. This is "
880 "a bug in EntityPass. Parent passes are responsible for setting "
881 "up backdrop filters for their children.";
882 return false;
883 }
884
885 Entity backdrop_entity;
886 backdrop_entity.SetContents(std::move(backdrop_filter_contents));
887 backdrop_entity.SetTransform(
888 Matrix::MakeTranslation(Vector3(-local_pass_position)));
889 backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
890
891 RenderElement(backdrop_entity, clip_height_floor, pass_context, pass_depth,
892 renderer, clip_coverage_stack, global_pass_position);
893 }
894
895 bool is_collapsing_clear_colors = !collapsed_parent_pass &&
896 // Backdrop filters act as a entity before
897 // everything and disrupt the optimization.
898 !backdrop_filter_proc_;
899 for (const auto& element : elements_) {
900 // Skip elements that are incorporated into the clear color.
901 if (is_collapsing_clear_colors) {
902 auto [entity_color, _] =
903 ElementAsBackgroundColor(element, clear_color_size);
904 if (entity_color.has_value()) {
905 continue;
906 }
907 is_collapsing_clear_colors = false;
908 }
909
910 EntityResult result =
911 GetEntityForElement(element, // element
912 renderer, // renderer
913 pass_context, // pass_context
914 root_pass_size, // root_pass_size
915 global_pass_position, // global_pass_position
916 pass_depth, // pass_depth
917 clip_coverage_stack, // clip_coverage_stack
918 clip_height_floor); // clip_height_floor
919
920 switch (result.status) {
922 break;
923 case EntityResult::kFailure:
924 // All failure cases should be covered by specific validation messages
925 // in `GetEntityForElement()`.
926 return false;
927 case EntityResult::kSkip:
928 continue;
929 };
930
931 //--------------------------------------------------------------------------
932 /// Setup advanced blends.
933 ///
934
935 if (result.entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
936 if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) {
937 auto src_contents = result.entity.GetContents();
938 auto contents = std::make_shared<FramebufferBlendContents>();
939 contents->SetChildContents(src_contents);
940 contents->SetBlendMode(result.entity.GetBlendMode());
941 result.entity.SetContents(std::move(contents));
942 result.entity.SetBlendMode(BlendMode::kSource);
943 } else {
944 // End the active pass and flush the buffer before rendering "advanced"
945 // blends. Advanced blends work by binding the current render target
946 // texture as an input ("destination"), blending with a second texture
947 // input ("source"), writing the result to an intermediate texture, and
948 // finally copying the data from the intermediate texture back to the
949 // render target texture. And so all of the commands that have written
950 // to the render target texture so far need to execute before it's bound
951 // for blending (otherwise the blend pass will end up executing before
952 // all the previous commands in the active pass).
953
954 if (!pass_context.EndPass()) {
956 << "Failed to end the current render pass in order to read from "
957 "the backdrop texture and apply an advanced blend.";
958 return false;
959 }
960
961 // Amend an advanced blend filter to the contents, attaching the pass
962 // texture.
963 auto texture = pass_context.GetTexture();
964 if (!texture) {
965 VALIDATION_LOG << "Failed to fetch the color texture in order to "
966 "apply an advanced blend.";
967 return false;
968 }
969
971 FilterInput::Make(texture, result.entity.GetTransform().Invert()),
972 FilterInput::Make(result.entity.GetContents())};
973 auto contents = ColorFilterContents::MakeBlend(
974 result.entity.GetBlendMode(), inputs);
975 contents->SetCoverageHint(result.entity.GetCoverage());
976 result.entity.SetContents(std::move(contents));
977 result.entity.SetBlendMode(BlendMode::kSource);
978 }
979 }
980
981 //--------------------------------------------------------------------------
982 /// Render the Element.
983 ///
984 if (!RenderElement(result.entity, clip_height_floor, pass_context,
985 pass_depth, renderer, clip_coverage_stack,
986 global_pass_position)) {
987 // Specific validation logs are handled in `render_element()`.
988 return false;
989 }
990 }
991
992 return true;
993}
994
996 const std::function<bool(Element&)>& iterator) {
997 if (!iterator) {
998 return;
999 }
1000
1001 for (auto& element : elements_) {
1002 if (!iterator(element)) {
1003 return;
1004 }
1005 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1006 subpass->get()->IterateAllElements(iterator);
1007 }
1008 }
1009}
1010
1012 const std::function<bool(const Element&)>& iterator) const {
1013 /// TODO(gaaclarke): Remove duplication here between const and non-const
1014 /// versions.
1015 if (!iterator) {
1016 return;
1017 }
1018
1019 for (auto& element : elements_) {
1020 if (!iterator(element)) {
1021 return;
1022 }
1023 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1024 const EntityPass* entity_pass = subpass->get();
1025 entity_pass->IterateAllElements(iterator);
1026 }
1027 }
1028}
1029
1031 const std::function<bool(Entity&)>& iterator) {
1032 if (!iterator) {
1033 return;
1034 }
1035
1036 for (auto& element : elements_) {
1037 if (auto entity = std::get_if<Entity>(&element)) {
1038 if (!iterator(*entity)) {
1039 return;
1040 }
1041 continue;
1042 }
1043 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1044 subpass->get()->IterateAllEntities(iterator);
1045 continue;
1046 }
1048 }
1049}
1050
1052 const std::function<bool(const Entity&)>& iterator) const {
1053 if (!iterator) {
1054 return;
1055 }
1056
1057 for (const auto& element : elements_) {
1058 if (auto entity = std::get_if<Entity>(&element)) {
1059 if (!iterator(*entity)) {
1060 return;
1061 }
1062 continue;
1063 }
1064 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1065 const EntityPass* entity_pass = subpass->get();
1066 entity_pass->IterateAllEntities(iterator);
1067 continue;
1068 }
1070 }
1071}
1072
1074 const std::function<bool(Entity&)>& iterator) {
1075 if (!iterator) {
1076 return true;
1077 }
1078
1079 for (auto& element : elements_) {
1080 if (auto entity = std::get_if<Entity>(&element)) {
1081 if (!iterator(*entity)) {
1082 return false;
1083 }
1084 continue;
1085 }
1086 return true;
1087 }
1088 return false;
1089}
1090
1092 return elements_.size();
1093}
1094
1096 transform_ = transform;
1097}
1098
1099void EntityPass::SetClipHeight(size_t clip_height) {
1100 clip_height_ = clip_height;
1101}
1102
1104 return clip_height_;
1105}
1106
1107void EntityPass::SetClipDepth(size_t clip_depth) {
1108 clip_depth_ = clip_depth;
1109}
1110
1112 return clip_depth_;
1113}
1114
1116 blend_mode_ = blend_mode;
1117 flood_clip_ = Entity::IsBlendModeDestructive(blend_mode);
1118}
1119
1121 return GetClearColor(size).value_or(Color::BlackTransparent());
1122}
1123
1124std::optional<Color> EntityPass::GetClearColor(ISize target_size) const {
1125 if (backdrop_filter_proc_) {
1126 return std::nullopt;
1127 }
1128
1129 std::optional<Color> result = std::nullopt;
1130 for (const Element& element : elements_) {
1131 auto [entity_color, blend_mode] =
1132 ElementAsBackgroundColor(element, target_size);
1133 if (!entity_color.has_value()) {
1134 break;
1135 }
1137 .Blend(entity_color.value(), blend_mode);
1138 }
1139 if (result.has_value()) {
1140 return result->Premultiply();
1141 }
1142 return result;
1143}
1144
1146 if (superpass_) {
1147 VALIDATION_LOG << "Backdrop filters cannot be set on EntityPasses that "
1148 "have already been appended to another pass.";
1149 }
1150
1151 backdrop_filter_proc_ = std::move(proc);
1152}
1153
1154} // namespace impeller
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition: closure.h:32
static std::shared_ptr< ColorFilterContents > MakeBlend(BlendMode blend_mode, FilterInput::Vector inputs, std::optional< Color > foreground_color=std::nullopt)
the [inputs] are expected to be in the order of dst, src.
A class that tracks all clips that have been recorded in the current entity pass stencil.
std::optional< Rect > CurrentClipCoverage() const
void PushSubpass(std::optional< Rect > subpass_coverage, size_t clip_height)
const RenderTarget & GetRenderTarget() const
bool IterateUntilSubpass(const std::function< bool(Entity &)> &iterator)
Iterate entities in this pass up until the first subpass is found. This is useful for limiting look-a...
void SetElements(std::vector< Element > elements)
Definition: entity_pass.cc:139
void IterateAllElements(const std::function< bool(Element &)> &iterator)
Iterate all elements (entities and subpasses) in this pass, recursively including elements of child p...
Definition: entity_pass.cc:995
std::variant< Entity, std::unique_ptr< EntityPass > > Element
Definition: entity_pass.h:54
size_t GetElementCount() const
Return the number of elements on this pass.
bool GetBoundsLimitMightClipContent() const
Indicates if the bounds limit set using |SetBoundsLimit()| might clip the contents of the pass.
Definition: entity_pass.cc:69
void SetBoundsLimit(std::optional< Rect > bounds_limit, ContentBoundsPromise bounds_promise=ContentBoundsPromise::kUnknown)
Set the bounds limit, which is provided by the user when creating a SaveLayer. This is a hint that al...
Definition: entity_pass.cc:58
Color GetClearColorOrDefault(ISize size=ISize::Infinite()) const
Return the premultiplied clear color of the pass entities.
void SetClipDepth(size_t clip_depth)
void AddEntity(Entity entity)
Add an entity to the current entity pass.
Definition: entity_pass.cc:100
size_t GetClipHeight() const
void SetBackdropFilter(BackdropFilterProc proc)
bool GetBoundsLimitIsSnug() const
Indicates if the bounds limit set using |SetBoundsLimit()| is a reasonably tight estimate of the boun...
Definition: entity_pass.cc:88
std::optional< Rect > GetBoundsLimit() const
Get the bounds limit, which is provided by the user when creating a SaveLayer.
Definition: entity_pass.cc:65
EntityPass * GetSuperpass() const
Definition: entity_pass.cc:263
size_t GetSubpassesDepth() const
Definition: entity_pass.cc:143
void PopClips(size_t num_clips, uint64_t depth)
Definition: entity_pass.cc:117
int32_t GetRequiredMipCount() const
Definition: entity_pass.h:175
void SetClipHeight(size_t clip_height)
std::optional< Color > GetClearColor(ISize size=ISize::Infinite()) const
Return the premultiplied clear color of the pass entities, if any.
void SetBlendMode(BlendMode blend_mode)
void SetTransform(Matrix transform)
void IterateAllEntities(const std::function< bool(Entity &)> &iterator)
Iterate all entities in this pass, recursively including entities of child passes....
std::optional< Rect > GetSubpassCoverage(const EntityPass &subpass, std::optional< Rect > coverage_limit) const
Computes the coverage of a given subpass. This is used to determine the texture size of a given subpa...
Definition: entity_pass.cc:231
void PopAllClips(uint64_t depth)
Definition: entity_pass.cc:135
EntityPass * AddSubpass(std::unique_ptr< EntityPass > pass)
Appends a given pass as a subpass.
Definition: entity_pass.cc:267
void SetDelegate(std::shared_ptr< EntityPassDelegate > delgate)
Definition: entity_pass.cc:51
bool Render(ContentContext &renderer, const RenderTarget &render_target) const
Definition: entity_pass.cc:354
std::optional< Rect > GetElementsCoverage(std::optional< Rect > coverage_limit) const
Definition: entity_pass.cc:154
void PushClip(Entity entity)
Definition: entity_pass.cc:112
uint32_t GetClipDepth() const
std::function< std::shared_ptr< FilterContents >(FilterInput::Ref, const Matrix &effect_transform, Entity::RenderingMode rendering_mode)> BackdropFilterProc
Definition: entity_pass.h:59
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:62
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:94
void SetClipDepth(uint32_t clip_depth)
Definition: entity.cc:98
BlendMode GetBlendMode() const
Definition: entity.cc:119
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:90
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:115
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition: entity.cc:173
Entity Clone() const
Definition: entity.cc:191
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:22
static bool IsBlendModeDestructive(BlendMode blend_mode)
Returns true if the blend mode is "destructive", meaning that even fully transparent source colors wo...
Definition: entity.cc:156
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
Definition: filter_input.cc:19
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
EntityPassTarget & GetPassTarget() const
std::shared_ptr< Texture > GetTexture()
RenderPassResult GetRenderPass(uint32_t pass_depth)
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
virtual void SetScissor(IRect scissor)
Definition: render_pass.cc:115
ISize GetRenderTargetSize() const
Definition: render_pass.cc:43
std::shared_ptr< Texture > GetRenderTargetTexture() const
ISize GetRenderTargetSize() const
static std::shared_ptr< TextureContents > MakeRect(Rect destination)
A common case factory that marks the texture contents as having a destination rectangle....
@ kSuccess
Definition: embedder.h:73
GAsyncResult * result
uint32_t * target
#define FML_UNREACHABLE()
Definition: logging.h:109
#define FML_DCHECK(condition)
Definition: logging.h:103
Dart_NativeFunction function
Definition: fuchsia.cc:51
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
Definition: dart.idl:42
FlTexture * texture
static TTSTestCase Failure(const TTSTestCase &original)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static void SetClipScissor(std::optional< Rect > clip_coverage, RenderPass &pass, Point global_pass_position)
Definition: entity_pass.cc:725
static std::unique_ptr< EntityPassTarget > CreateRenderTarget(ContentContext &renderer, ISize size, int mip_count, const Color &clear_color)
TRect< Scalar > Rect
Definition: rect.h:769
TPoint< Scalar > Point
Definition: point.h:322
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
IRect64 IRect
Definition: rect.h:772
BlendMode
Definition: color.h:59
ContentBoundsPromise
Definition: entity_pass.h:28
@ kUnknown
The caller makes no claims related to the size of the bounds.
@ kMayClipContents
The caller claims the bounds are a subset of an estimate of the reasonably tight bounds but likely cl...
@ kContainsContents
The caller claims the bounds are a reasonably tight estimate of the coverage of the contents and shou...
TSize< Scalar > Size
Definition: size.h:137
ISize64 ISize
Definition: size.h:140
static const constexpr RenderTarget::AttachmentConfig kDefaultStencilConfig
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
static constexpr Color BlackTransparent()
Definition: color.h:272
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
constexpr bool IsZero() const
Definition: point.h:240
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:519
constexpr TRect Union(const TRect &o) const
Definition: rect.h:504
RoundOut(const TRect< U > &r)
Definition: rect.h:666
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
static constexpr TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:589
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131
#define VALIDATION_LOG
Definition: validation.h:73