| Flutter Impeller |

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
5 #include "impeller/entity/entity_pass.h"
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"
15 #include "impeller/base/strings.h"
16 #include "impeller/base/validation.h"
17 #include "impeller/core/formats.h"
18 #include "impeller/entity/contents/content_context.h"
19 #include "impeller/entity/contents/filters/color_filter_contents.h"
20 #include "impeller/entity/contents/filters/inputs/filter_input.h"
21 #include "impeller/entity/contents/framebuffer_blend_contents.h"
22 #include "impeller/entity/contents/texture_contents.h"
23 #include "impeller/entity/entity.h"
24 #include "impeller/entity/entity_pass_clip_stack.h"
25 #include "impeller/entity/inline_pass_context.h"
26 #include "impeller/geometry/color.h"
27 #include "impeller/geometry/rect.h"
28 #include "impeller/geometry/size.h"
29 #include "impeller/renderer/command_buffer.h"
30
31 namespace impeller {
32
33 namespace {
34 std::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
47 EntityPass::EntityPass() = default;
48
49 EntityPass::~EntityPass() = default;
50
51 void EntityPass::SetDelegate(std::shared_ptr<EntityPassDelegate> delegate) {
52 if (!delegate) {
53 return;
54 }
55 delegate_ = std::move(delegate);
56 }
57
58 void 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
62 : ContentBoundsPromise::kUnknown;
63 }
64
65 std::optional<Rect> EntityPass::GetBoundsLimit() const {
66 return bounds_limit_;
67 }
68
69 bool EntityPass::GetBoundsLimitMightClipContent() const {
70 switch (bounds_promise_) {
71 case ContentBoundsPromise::kUnknown:
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();
78 case ContentBoundsPromise::kContainsContents:
79 FML_DCHECK(bounds_limit_.has_value());
80 return false;
81 case ContentBoundsPromise::kMayClipContents:
82 FML_DCHECK(bounds_limit_.has_value());
83 return true;
84 }
85 FML_UNREACHABLE();
86 }
87
88 bool EntityPass::GetBoundsLimitIsSnug() const {
89 switch (bounds_promise_) {
90 case ContentBoundsPromise::kUnknown:
91 return false;
92 case ContentBoundsPromise::kContainsContents:
93 case ContentBoundsPromise::kMayClipContents:
94 FML_DCHECK(bounds_limit_.has_value());
95 return true;
96 }
97 FML_UNREACHABLE();
98 }
99
100 void EntityPass::AddEntity(Entity entity) {
101 if (entity.GetBlendMode() == BlendMode::kSourceOver &&
102 entity.GetContents()->IsOpaque()) {
103 entity.SetBlendMode(BlendMode::kSource);
104 }
105
106 if (entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
107 advanced_blend_reads_from_pass_texture_ = true;
108 }
109 elements_.emplace_back(std::move(entity));
110 }
111
112 void EntityPass::PushClip(Entity entity) {
113 elements_.emplace_back(std::move(entity));
114 active_clips_.emplace_back(elements_.size() - 1);
115 }
116
117 void EntityPass::PopClips(size_t num_clips, uint64_t depth) {
118 if (num_clips > active_clips_.size()) {
119 VALIDATION_LOG
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
135 void EntityPass::PopAllClips(uint64_t depth) {
136 PopClips(active_clips_.size(), depth);
137 }
138
139 void EntityPass::SetElements(std::vector<Element> elements) {
140 elements_ = std::move(elements);
141 }
142
143 size_t EntityPass::GetSubpassesDepth() const {
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
154 std::optional<Rect> EntityPass::GetElementsCoverage(
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_,
187 Entity::RenderingMode::kSubpassAppendSnapshotTransform);
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 {
223 FML_UNREACHABLE();
224 }
225
226 accumulated_coverage = Rect::Union(accumulated_coverage, element_coverage);
227 }
228 return accumulated_coverage;
229 }
230
231 std::optional<Rect> EntityPass::GetSubpassCoverage(
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
263 EntityPass* EntityPass::GetSuperpass() const {
264 return superpass_;
265 }
266
267 EntityPass* 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
286 static const constexpr RenderTarget::AttachmentConfig kDefaultStencilConfig =
287 RenderTarget::AttachmentConfig{
288 .storage_mode = StorageMode::kDeviceTransient,
289 .load_action = LoadAction::kDontCare,
290 .store_action = StoreAction::kDontCare,
291 };
292
293 static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
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
310 RenderTarget target;
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=*/
318 RenderTarget::AttachmentConfigMSAA{
319 .storage_mode = StorageMode::kDeviceTransient,
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=*/
325 kDefaultStencilConfig);
326 } else {
327 target = renderer.GetRenderTargetCache()->CreateOffscreen(
328 *context, // context
329 size, // size
330 /*mip_count=*/mip_count,
331 "EntityPass", // label
332 RenderTarget::AttachmentConfig{
333 .storage_mode = StorageMode::kDevicePrivate,
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
347 bool 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
354 bool EntityPass::Render(ContentContext& renderer,
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
384 EntityPassClipStack clip_stack = EntityPassClipStack(
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(),
393 GetRequiredMipCount(),
394 GetClearColorOrDefault(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);
449 entity.SetBlendMode(BlendMode::kSource);
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 =
493 GetClearColorOrDefault(render_target.GetRenderTargetSize());
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
511 EntityPass::EntityResult EntityPass::GetEntityForElement(
512 const EntityPass::Element& element,
513 ContentContext& renderer,
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.
564 return EntityPass::EntityResult::Failure();
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()
580 ? Entity::RenderingMode::kSubpassPrependSnapshotTransform
581 : Entity::RenderingMode::kSubpassAppendSnapshotTransform);
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()
611 .GetRenderTarget()
612 .GetRenderTargetSize()))
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.";
645 return EntityPass::EntityResult::Failure();
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.
671 return EntityPass::EntityResult::Failure();
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.
695 return EntityPass::EntityResult::Failure();
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 }
722 FML_UNREACHABLE();
723 }
724
725 static 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
741 bool 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
839 bool 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()) {
854 VALIDATION_LOG << SPrintF(
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) {
877 VALIDATION_LOG
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) {
921 case EntityResult::kSuccess:
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()) {
955 VALIDATION_LOG
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
970 FilterInput::Vector inputs = {
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
995 void EntityPass::IterateAllElements(
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
1011 void EntityPass::IterateAllElements(
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
1030 void EntityPass::IterateAllEntities(
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 }
1047 FML_UNREACHABLE();
1048 }
1049 }
1050
1051 void EntityPass::IterateAllEntities(
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 }
1069 FML_UNREACHABLE();
1070 }
1071 }
1072
1073 bool EntityPass::IterateUntilSubpass(
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
1091 size_t EntityPass::GetElementCount() const {
1092 return elements_.size();
1093 }
1094
1095 void EntityPass::SetTransform(Matrix transform) {
1096 transform_ = transform;
1097 }
1098
1099 void EntityPass::SetClipHeight(size_t clip_height) {
1100 clip_height_ = clip_height;
1101 }
1102
1103 size_t EntityPass::GetClipHeight() const {
1104 return clip_height_;
1105 }
1106
1107 void EntityPass::SetClipDepth(size_t clip_depth) {
1108 clip_depth_ = clip_depth;
1109 }
1110
1111 uint32_t EntityPass::GetClipDepth() const {
1112 return clip_depth_;
1113 }
1114
1115 void EntityPass::SetBlendMode(BlendMode blend_mode) {
1116 blend_mode_ = blend_mode;
1117 flood_clip_ = Entity::IsBlendModeDestructive(blend_mode);
1118 }
1119
1120 Color EntityPass::GetClearColorOrDefault(ISize size) const {
1121 return GetClearColor(size).value_or(Color::BlackTransparent());
1122 }
1123
1124 std::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 }
1136 result = result.value_or(Color::BlackTransparent())
1137 .Blend(entity_color.value(), blend_mode);
1138 }
1139 if (result.has_value()) {
1140 return result->Premultiply();
1141 }
1142 return result;
1143 }
1144
1145 void EntityPass::SetBackdropFilter(BackdropFilterProc proc) {
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
impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::StoreAction::kMultisampleResolve
@ kMultisampleResolve
impeller::EntityPass::IterateAllEntities
void IterateAllEntities(const std::function< bool(Entity &)> &iterator)
Iterate all entities in this pass, recursively including entities of child passes....
Definition: entity_pass.cc:1030
impeller::Entity::SetClipDepth
void SetClipDepth(uint32_t clip_depth)
Definition: entity.cc:98
impeller::EntityPass::SetBackdropFilter
void SetBackdropFilter(BackdropFilterProc proc)
Definition: entity_pass.cc:1145
impeller::Entity::kLastPipelineBlendMode
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:22
impeller::Capabilities::SupportsReadFromResolve
virtual bool SupportsReadFromResolve() const =0
Whether the context backend supports binding the current RenderPass attachments. This is supported if...
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::EntityPass::PopAllClips
void PopAllClips(uint64_t depth)
Definition: entity_pass.cc:135
impeller::EntityPass::GetSubpassCoverage
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
impeller::Entity::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:115
impeller::ContentBoundsPromise::kMayClipContents
@ kMayClipContents
The caller claims the bounds are a subset of an estimate of the reasonably tight bounds but likely cl...
impeller::EntityPassClipStack::PushSubpass
void PushSubpass(std::optional< Rect > subpass_coverage, size_t clip_height)
Definition: entity_pass_clip_stack.cc:31
impeller::ContentContext::GetLazyGlyphAtlas
const std::shared_ptr< LazyGlyphAtlas > & GetLazyGlyphAtlas() const
Definition: content_context.h:720
texture_contents.h
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
impeller::EntityPass::GetClipDepth
uint32_t GetClipDepth() const
Definition: entity_pass.cc:1111
impeller::EntityPass::GetClipHeight
size_t GetClipHeight() const
Definition: entity_pass.cc:1103
entity.h
impeller::EntityPass::GetSubpassesDepth
size_t GetSubpassesDepth() const
Definition: entity_pass.cc:143
impeller::BlendMode
BlendMode
Definition: color.h:59
impeller::Color
Definition: color.h:124
impeller::EntityPass::GetBoundsLimitMightClipContent
bool GetBoundsLimitMightClipContent() const
Indicates if the bounds limit set using |SetBoundsLimit()| might clip the contents of the pass.
Definition: entity_pass.cc:69
impeller::EntityPass::EntityPass
EntityPass()
impeller::FilterInput::Make
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
Definition: filter_input.cc:19
impeller::BlendMode::kSource
@ kSource
impeller::EntityPass::AddSubpass
EntityPass * AddSubpass(std::unique_ptr< EntityPass > pass)
Appends a given pass as a subpass.
Definition: entity_pass.cc:267
impeller::EntityPass::IterateUntilSubpass
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...
Definition: entity_pass.cc:1073
impeller::Entity::IsBlendModeDestructive
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
impeller::Entity::RenderingMode::kSubpassAppendSnapshotTransform
@ kSubpassAppendSnapshotTransform
formats.h
impeller::EntityPassClipStack::HasCoverage
bool HasCoverage() const
Definition: entity_pass_clip_stack.cc:27
impeller::TRect< Scalar >::Intersection
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:519
impeller::EntityPass::GetBoundsLimitIsSnug
bool GetBoundsLimitIsSnug() const
Indicates if the bounds limit set using |SetBoundsLimit()| is a reasonably tight estimate of the boun...
Definition: entity_pass.cc:88
impeller::EntityPass::IterateAllElements
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
impeller::StoreAction::kDontCare
@ kDontCare
impeller::ContentBoundsPromise
ContentBoundsPromise
Definition: entity_pass.h:28
impeller::ContentBoundsPromise::kUnknown
@ kUnknown
The caller makes no claims related to the size of the bounds.
impeller::EntityPassTarget
Definition: entity_pass_target.h:14
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
impeller::EntityPass::GetElementsCoverage
std::optional< Rect > GetElementsCoverage(std::optional< Rect > coverage_limit) const
Definition: entity_pass.cc:154
impeller::EntityPass::~EntityPass
~EntityPass()
framebuffer_blend_contents.h
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
validation.h
subpass_size
ISize subpass_size
The output size of the down-sampling pass.
Definition: gaussian_blur_filter_contents.cc:219
impeller::RenderTarget::AttachmentConfigMSAA
Definition: render_target.h:47
impeller::EntityPass::SetClipDepth
void SetClipDepth(size_t clip_depth)
Definition: entity_pass.cc:1107
impeller::EntityPass::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: entity_pass.cc:1115
impeller::TRect::Shift
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:589
impeller::RenderTarget::AttachmentConfig
Definition: render_target.h:40
impeller::TPoint::IsZero
constexpr bool IsZero() const
Definition: point.h:240
impeller::Entity::SetContents
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:90
impeller::TRect::RoundOut
RoundOut(const TRect< U > &r)
Definition: rect.h:666
impeller::kDefaultStencilConfig
static const constexpr RenderTarget::AttachmentConfig kDefaultStencilConfig
Definition: experimental_canvas.cc:48
impeller::Entity
Definition: entity.h:20
impeller::RenderPass::GetRenderTargetSize
ISize GetRenderTargetSize() const
Definition: render_pass.cc:43
impeller::TSize
Definition: size.h:19
impeller::StorageMode::kDeviceTransient
@ kDeviceTransient
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
impeller::EntityPass::SetElements
void SetElements(std::vector< Element > elements)
Definition: entity_pass.cc:139
impeller::EntityPass::GetBoundsLimit
std::optional< Rect > GetBoundsLimit() const
Get the bounds limit, which is provided by the user when creating a SaveLayer.
Definition: entity_pass.cc:65
impeller::RenderTarget::GetRenderTargetTexture
std::shared_ptr< Texture > GetRenderTargetTexture() const
Definition: render_target.cc:144
impeller::Entity::Render
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition: entity.cc:173
impeller::EntityPass::GetSuperpass
EntityPass * GetSuperpass() const
Definition: entity_pass.cc:263
impeller::EntityPassClipStack::CurrentClipCoverage
std::optional< Rect > CurrentClipCoverage() const
Definition: entity_pass_clip_stack.cc:23
impeller::Context::BackendType::kOpenGLES
@ kOpenGLES
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:231
impeller::Capabilities::SupportsImplicitResolvingMSAA
virtual bool SupportsImplicitResolvingMSAA() const =0
Whether the context backend supports multisampled rendering to the on-screen surface without requirin...
impeller::EntityPass
Definition: entity_pass.h:43
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:553
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::EntityPass::Element
std::variant< Entity, std::unique_ptr< EntityPass > > Element
Definition: entity_pass.h:54
impeller::InlinePassContext::GetRenderPass
RenderPassResult GetRenderPass(uint32_t pass_depth)
Definition: inline_pass_context.cc:93
impeller::EntityPass::GetRequiredMipCount
int32_t GetRequiredMipCount() const
Definition: entity_pass.h:175
impeller::TRect< Scalar >::MakeOriginSize
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
impeller::EntityPass::GetElementCount
size_t GetElementCount() const
Return the number of elements on this pass.
Definition: entity_pass.cc:1091
impeller::InlinePassContext
Definition: inline_pass_context.h:17
impeller::Entity::GetContents
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:94
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::RenderTarget::AttachmentConfig::storage_mode
StorageMode storage_mode
Definition: render_target.h:41
impeller::InlinePassContext::GetTexture
std::shared_ptr< Texture > GetTexture()
Definition: inline_pass_context.cc:48
impeller::RenderTarget
Definition: render_target.h:38
entity_pass.h
impeller::InlinePassContext::EndPass
bool EndPass()
Definition: inline_pass_context.cc:55
impeller::EntityPass::SetTransform
void SetTransform(Matrix transform)
Definition: entity_pass.cc:1095
filter_input.h
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:769
strings.h
impeller::SetClipScissor
static void SetClipScissor(std::optional< Rect > clip_coverage, RenderPass &pass, Point global_pass_position)
Definition: entity_pass.cc:725
color_filter_contents.h
impeller::EntityPass::SetBoundsLimit
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
impeller::EntityPassClipStack::PopSubpass
void PopSubpass()
Definition: entity_pass_clip_stack.cc:42
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:119
impeller::InlinePassContext::GetPassTarget
EntityPassTarget & GetPassTarget() const
Definition: inline_pass_context.cc:89
entity_pass_clip_stack.h
impeller::RenderPass::SetScissor
virtual void SetScissor(IRect scissor)
Definition: render_pass.cc:115
impeller::RenderTarget::GetRenderTargetSize
ISize GetRenderTargetSize() const
Definition: render_target.cc:139
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
command_buffer.h
content_context.h
impeller::ContentContext::GetRenderTargetCache
const std::shared_ptr< RenderTargetAllocator > & GetRenderTargetCache() const
Definition: content_context.h:724
impeller::Entity::SetTransform
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:62
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:557
impeller::Entity::RenderingMode::kSubpassPrependSnapshotTransform
@ kSubpassPrependSnapshotTransform
impeller::RenderTarget::AttachmentConfigMSAA::storage_mode
StorageMode storage_mode
Definition: render_target.h:48
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
impeller::EntityPass::PopClips
void PopClips(size_t num_clips, uint64_t depth)
Definition: entity_pass.cc:117
impeller::LoadAction::kDontCare
@ kDontCare
rect.h
impeller::Entity::Clone
Entity Clone() const
Definition: entity.cc:191
impeller::TPoint< Scalar >
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:272
impeller::EntityPass::PushClip
void PushClip(Entity entity)
Definition: entity_pass.cc:112
impeller::EntityPass::Render
bool Render(ContentContext &renderer, const RenderTarget &render_target) const
Definition: entity_pass.cc:354
impeller::TRect< Scalar >::Union
constexpr TRect Union(const TRect &o) const
Definition: rect.h:504
color.h
impeller::EntityPassClipStack
A class that tracks all clips that have been recorded in the current entity pass stencil.
Definition: entity_pass_clip_stack.h:24
impeller::IRect
IRect64 IRect
Definition: rect.h:772
impeller::EntityPass::SetDelegate
void SetDelegate(std::shared_ptr< EntityPassDelegate > delgate)
Definition: entity_pass.cc:51
impeller::EntityPass::SetClipHeight
void SetClipHeight(size_t clip_height)
Definition: entity_pass.cc:1099
impeller::FilterInput::Vector
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
impeller::ColorFilterContents::MakeBlend
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.
Definition: color_filter_contents.cc:17
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::ContentBoundsPromise::kContainsContents
@ kContainsContents
The caller claims the bounds are a reasonably tight estimate of the coverage of the contents and shou...
impeller
Definition: aiks_blend_unittests.cc:18
impeller::EntityPassTarget::GetRenderTarget
const RenderTarget & GetRenderTarget() const
Definition: entity_pass_target.cc:68
impeller::ContentContext
Definition: content_context.h:366
impeller::TRect
Definition: rect.h:122
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Vector3
Definition: vector.h:20
size.h
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::EntityPass::GetClearColor
std::optional< Color > GetClearColor(ISize size=ISize::Infinite()) const
Return the premultiplied clear color of the pass entities, if any.
Definition: entity_pass.cc:1124
impeller::EntityPass::GetClearColorOrDefault
Color GetClearColorOrDefault(ISize size=ISize::Infinite()) const
Return the premultiplied clear color of the pass entities.
Definition: entity_pass.cc:1120
impeller::EntityPass::AddEntity
void AddEntity(Entity entity)
Add an entity to the current entity pass.
Definition: entity_pass.cc:100
inline_pass_context.h