| 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 <memory>
8 #include <utility>
9 #include <variant>
10
11 #include "flutter/fml/closure.h"
12 #include "flutter/fml/logging.h"
13 #include "flutter/fml/trace_event.h"
14 #include "impeller/base/strings.h"
15 #include "impeller/base/validation.h"
16 #include "impeller/core/formats.h"
17 #include "impeller/entity/contents/clip_contents.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/inline_pass_context.h"
25 #include "impeller/geometry/color.h"
26 #include "impeller/geometry/rect.h"
27 #include "impeller/renderer/command_buffer.h"
28
29 #ifdef IMPELLER_DEBUG
30 #include "impeller/entity/contents/checkerboard_contents.h"
31 #endif // IMPELLER_DEBUG
32
33 namespace impeller {
34
35 namespace {
36 std::tuple<std::optional<Color>, BlendMode> ElementAsBackgroundColor(
37 const EntityPass::Element& element,
38 ISize target_size) {
39 if (const Entity* entity = std::get_if<Entity>(&element)) {
40 std::optional<Color> entity_color = entity->AsBackgroundColor(target_size);
41 if (entity_color.has_value()) {
42 return {entity_color.value(), entity->GetBlendMode()};
43 }
44 }
45 return {};
46 }
47 } // namespace
48
49 const std::string EntityPass::kCaptureDocumentName = "EntityPass";
50
51 EntityPass::EntityPass() = default;
52
53 EntityPass::~EntityPass() = default;
54
55 void EntityPass::SetDelegate(std::shared_ptr<EntityPassDelegate> delegate) {
56 if (!delegate) {
57 return;
58 }
59 delegate_ = std::move(delegate);
60 }
61
62 void EntityPass::SetBoundsLimit(std::optional<Rect> bounds_limit) {
63 bounds_limit_ = bounds_limit;
64 }
65
66 std::optional<Rect> EntityPass::GetBoundsLimit() const {
67 return bounds_limit_;
68 }
69
70 void EntityPass::AddEntity(Entity entity) {
71 if (entity.GetBlendMode() == BlendMode::kSourceOver &&
72 entity.GetContents()->IsOpaque()) {
73 entity.SetBlendMode(BlendMode::kSource);
74 }
75
76 if (entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
77 advanced_blend_reads_from_pass_texture_ += 1;
78 }
79 elements_.emplace_back(std::move(entity));
80 }
81
82 void EntityPass::SetElements(std::vector<Element> elements) {
83 elements_ = std::move(elements);
84 }
85
86 size_t EntityPass::GetSubpassesDepth() const {
87 size_t max_subpass_depth = 0u;
88 for (const auto& element : elements_) {
89 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
90 max_subpass_depth =
91 std::max(max_subpass_depth, subpass->get()->GetSubpassesDepth());
92 }
93 }
94 return max_subpass_depth + 1u;
95 }
96
97 std::optional<Rect> EntityPass::GetElementsCoverage(
98 std::optional<Rect> coverage_limit) const {
99 std::optional<Rect> accumulated_coverage;
100 for (const auto& element : elements_) {
101 std::optional<Rect> element_coverage;
102
103 if (auto entity = std::get_if<Entity>(&element)) {
104 element_coverage = entity->GetCoverage();
105
106 // When the coverage limit is std::nullopt, that means there is no limit,
107 // as opposed to empty coverage.
108 if (element_coverage.has_value() && coverage_limit.has_value()) {
109 const auto* filter = entity->GetContents()->AsFilter();
110 if (!filter || filter->IsTranslationOnly()) {
111 element_coverage =
112 element_coverage->Intersection(coverage_limit.value());
113 }
114 }
115 } else if (auto subpass_ptr =
116 std::get_if<std::unique_ptr<EntityPass>>(&element)) {
117 auto& subpass = *subpass_ptr->get();
118
119 std::optional<Rect> unfiltered_coverage =
120 GetSubpassCoverage(subpass, std::nullopt);
121
122 // If the current pass elements have any coverage so far and there's a
123 // backdrop filter, then incorporate the backdrop filter in the
124 // pre-filtered coverage of the subpass.
125 if (accumulated_coverage.has_value() && subpass.backdrop_filter_proc_) {
126 std::shared_ptr<FilterContents> backdrop_filter =
127 subpass.backdrop_filter_proc_(
128 FilterInput::Make(accumulated_coverage.value()),
129 subpass.transform_, Entity::RenderingMode::kSubpass);
130 if (backdrop_filter) {
131 auto backdrop_coverage = backdrop_filter->GetCoverage({});
132 unfiltered_coverage =
133 Rect::Union(unfiltered_coverage, backdrop_coverage);
134 } else {
135 VALIDATION_LOG << "The EntityPass backdrop filter proc didn't return "
136 "a valid filter.";
137 }
138 }
139
140 if (!unfiltered_coverage.has_value()) {
141 continue;
142 }
143
144 // Additionally, subpass textures may be passed through filters, which may
145 // modify the coverage.
146 //
147 // Note that we currently only assume that ImageFilters (such as blurs and
148 // matrix transforms) may modify coverage, although it's technically
149 // possible ColorFilters to affect coverage as well. For example: A
150 // ColorMatrixFilter could output a completely transparent result, and
151 // we could potentially detect this case as zero coverage in the future.
152 std::shared_ptr<FilterContents> image_filter =
153 subpass.delegate_->WithImageFilter(*unfiltered_coverage,
154 subpass.transform_);
155 if (image_filter) {
156 Entity subpass_entity;
157 subpass_entity.SetTransform(subpass.transform_);
158 element_coverage = image_filter->GetCoverage(subpass_entity);
159 } else {
160 element_coverage = unfiltered_coverage;
161 }
162
163 element_coverage = Rect::Intersection(element_coverage, coverage_limit);
164 } else {
165 FML_UNREACHABLE();
166 }
167
168 accumulated_coverage = Rect::Union(accumulated_coverage, element_coverage);
169 }
170 return accumulated_coverage;
171 }
172
173 std::optional<Rect> EntityPass::GetSubpassCoverage(
174 const EntityPass& subpass,
175 std::optional<Rect> coverage_limit) const {
176 std::shared_ptr<FilterContents> image_filter =
177 subpass.delegate_->WithImageFilter(Rect(), subpass.transform_);
178
179 // If the subpass has an image filter, then its coverage space may deviate
180 // from the parent pass and make intersecting with the pass coverage limit
181 // unsafe.
182 if (image_filter && coverage_limit.has_value()) {
183 coverage_limit = image_filter->GetSourceCoverage(subpass.transform_,
184 coverage_limit.value());
185 }
186
187 auto entities_coverage = subpass.GetElementsCoverage(coverage_limit);
188 // The entities don't cover anything. There is nothing to do.
189 if (!entities_coverage.has_value()) {
190 return std::nullopt;
191 }
192
193 if (!subpass.bounds_limit_.has_value()) {
194 return entities_coverage;
195 }
196 auto user_bounds_coverage =
197 subpass.bounds_limit_->TransformBounds(subpass.transform_);
198 return entities_coverage->Intersection(user_bounds_coverage);
199 }
200
201 EntityPass* EntityPass::GetSuperpass() const {
202 return superpass_;
203 }
204
205 EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
206 if (!pass) {
207 return nullptr;
208 }
209 FML_DCHECK(pass->superpass_ == nullptr);
210 pass->superpass_ = this;
211
212 if (pass->backdrop_filter_proc_) {
213 backdrop_filter_reads_from_pass_texture_ += 1;
214 }
215 if (pass->blend_mode_ > Entity::kLastPipelineBlendMode) {
216 advanced_blend_reads_from_pass_texture_ += 1;
217 }
218
219 auto subpass_pointer = pass.get();
220 elements_.emplace_back(std::move(pass));
221 return subpass_pointer;
222 }
223
224 void EntityPass::AddSubpassInline(std::unique_ptr<EntityPass> pass) {
225 if (!pass) {
226 return;
227 }
228 FML_DCHECK(pass->superpass_ == nullptr);
229
230 std::vector<Element>& elements = pass->elements_;
231 for (auto i = 0u; i < elements.size(); i++) {
232 elements_.emplace_back(std::move(elements[i]));
233 }
234
235 backdrop_filter_reads_from_pass_texture_ +=
236 pass->backdrop_filter_reads_from_pass_texture_;
237 advanced_blend_reads_from_pass_texture_ +=
238 pass->advanced_blend_reads_from_pass_texture_;
239 }
240
241 static RenderTarget::AttachmentConfig GetDefaultStencilConfig(bool readable) {
242 return RenderTarget::AttachmentConfig{
243 .storage_mode = readable ? StorageMode::kDevicePrivate
244 : StorageMode::kDeviceTransient,
245 .load_action = LoadAction::kDontCare,
246 .store_action = StoreAction::kDontCare,
247 };
248 }
249
250 static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
251 ISize size,
252 bool readable,
253 const Color& clear_color) {
254 auto context = renderer.GetContext();
255
256 /// All of the load/store actions are managed by `InlinePassContext` when
257 /// `RenderPasses` are created, so we just set them to `kDontCare` here.
258 /// What's important is the `StorageMode` of the textures, which cannot be
259 /// changed for the lifetime of the textures.
260
261 RenderTarget target;
262 if (context->GetCapabilities()->SupportsOffscreenMSAA()) {
263 target = RenderTarget::CreateOffscreenMSAA(
264 *context, // context
265 *renderer.GetRenderTargetCache(), // allocator
266 size, // size
267 "EntityPass", // label
268 RenderTarget::AttachmentConfigMSAA{
269 .storage_mode = StorageMode::kDeviceTransient,
270 .resolve_storage_mode = StorageMode::kDevicePrivate,
271 .load_action = LoadAction::kDontCare,
272 .store_action = StoreAction::kMultisampleResolve,
273 .clear_color = clear_color}, // color_attachment_config
274 GetDefaultStencilConfig(readable) // stencil_attachment_config
275 );
276 } else {
277 target = RenderTarget::CreateOffscreen(
278 *context, // context
279 *renderer.GetRenderTargetCache(), // allocator
280 size, // size
281 "EntityPass", // label
282 RenderTarget::AttachmentConfig{
283 .storage_mode = StorageMode::kDevicePrivate,
284 .load_action = LoadAction::kDontCare,
285 .store_action = StoreAction::kDontCare,
286 .clear_color = clear_color,
287 }, // color_attachment_config
288 GetDefaultStencilConfig(readable) // stencil_attachment_config
289 );
290 }
291
292 return EntityPassTarget(
293 target, renderer.GetDeviceCapabilities().SupportsReadFromResolve(),
294 renderer.GetDeviceCapabilities().SupportsImplicitResolvingMSAA());
295 }
296
297 uint32_t EntityPass::GetTotalPassReads(ContentContext& renderer) const {
298 return renderer.GetDeviceCapabilities().SupportsFramebufferFetch()
299 ? backdrop_filter_reads_from_pass_texture_
300 : backdrop_filter_reads_from_pass_texture_ +
301 advanced_blend_reads_from_pass_texture_;
302 }
303
304 bool EntityPass::Render(ContentContext& renderer,
305 const RenderTarget& render_target) const {
306 auto capture =
307 renderer.GetContext()->capture.GetDocument(kCaptureDocumentName);
308
309 renderer.GetRenderTargetCache()->Start();
310
311 auto root_render_target = render_target;
312
313 if (root_render_target.GetColorAttachments().find(0u) ==
314 root_render_target.GetColorAttachments().end()) {
315 VALIDATION_LOG << "The root RenderTarget must have a color attachment.";
316 return false;
317 }
318
319 capture.AddRect("Coverage",
320 Rect::MakeSize(root_render_target.GetRenderTargetSize()),
321 {.readonly = true});
322
323 fml::ScopedCleanupClosure reset_state([&renderer]() {
324 renderer.GetLazyGlyphAtlas()->ResetTextFrames();
325 renderer.GetRenderTargetCache()->End();
326 });
327
328 IterateAllEntities([lazy_glyph_atlas =
329 renderer.GetLazyGlyphAtlas()](const Entity& entity) {
330 if (const auto& contents = entity.GetContents()) {
331 contents->PopulateGlyphAtlas(lazy_glyph_atlas, entity.DeriveTextScale());
332 }
333 return true;
334 });
335
336 ClipCoverageStack clip_coverage_stack = {ClipCoverageLayer{
337 .coverage = Rect::MakeSize(root_render_target.GetRenderTargetSize()),
338 .clip_depth = 0}};
339
340 bool reads_from_onscreen_backdrop = GetTotalPassReads(renderer) > 0;
341 // In this branch path, we need to render everything to an offscreen texture
342 // and then blit the results onto the onscreen texture. If using this branch,
343 // there's no need to set up a stencil attachment on the root render target.
344 if (reads_from_onscreen_backdrop) {
345 auto offscreen_target = CreateRenderTarget(
346 renderer, root_render_target.GetRenderTargetSize(), true,
347 GetClearColorOrDefault(render_target.GetRenderTargetSize()));
348
349 if (!OnRender(renderer, // renderer
350 capture, // capture
351 offscreen_target.GetRenderTarget()
352 .GetRenderTargetSize(), // root_pass_size
353 offscreen_target, // pass_target
354 Point(), // global_pass_position
355 Point(), // local_pass_position
356 0, // pass_depth
357 clip_coverage_stack // clip_coverage_stack
358 )) {
359 // Validation error messages are triggered for all `OnRender()` failure
360 // cases.
361 return false;
362 }
363
364 auto command_buffer = renderer.GetContext()->CreateCommandBuffer();
365 command_buffer->SetLabel("EntityPass Root Command Buffer");
366
367 // If the context supports blitting, blit the offscreen texture to the
368 // onscreen texture. Otherwise, draw it to the parent texture using a
369 // pipeline (slower).
370 if (renderer.GetContext()
371 ->GetCapabilities()
372 ->SupportsTextureToTextureBlits()) {
373 auto blit_pass = command_buffer->CreateBlitPass();
374 blit_pass->AddCopy(
375 offscreen_target.GetRenderTarget().GetRenderTargetTexture(),
376 root_render_target.GetRenderTargetTexture());
377
378 if (!command_buffer->EncodeAndSubmit(
379 blit_pass, renderer.GetContext()->GetResourceAllocator())) {
380 VALIDATION_LOG << "Failed to encode root pass blit command.";
381 return false;
382 }
383 } else {
384 auto render_pass = command_buffer->CreateRenderPass(root_render_target);
385 render_pass->SetLabel("EntityPass Root Render Pass");
386
387 {
388 auto size_rect = Rect::MakeSize(
389 offscreen_target.GetRenderTarget().GetRenderTargetSize());
390 auto contents = TextureContents::MakeRect(size_rect);
391 contents->SetTexture(
392 offscreen_target.GetRenderTarget().GetRenderTargetTexture());
393 contents->SetSourceRect(size_rect);
394 contents->SetLabel("Root pass blit");
395
396 Entity entity;
397 entity.SetContents(contents);
398 entity.SetBlendMode(BlendMode::kSource);
399
400 if (!entity.Render(renderer, *render_pass)) {
401 VALIDATION_LOG << "Failed to render EntityPass root blit.";
402 return false;
403 }
404 }
405
406 if (!command_buffer->EncodeAndSubmit(render_pass)) {
407 VALIDATION_LOG << "Failed to encode root pass command buffer.";
408 return false;
409 }
410 }
411
412 return true;
413 }
414
415 // If we make it this far, that means the context is capable of rendering
416 // everything directly to the onscreen texture.
417
418 // The safety check for fetching this color attachment is at the beginning of
419 // this method.
420 auto color0 = root_render_target.GetColorAttachments().find(0u)->second;
421
422 // If a root stencil was provided by the caller, then verify that it has a
423 // configuration which can be used to render this pass.
424 auto stencil_attachment = root_render_target.GetStencilAttachment();
425 if (stencil_attachment.has_value()) {
426 auto stencil_texture = stencil_attachment->texture;
427 if (!stencil_texture) {
428 VALIDATION_LOG << "The root RenderTarget must have a stencil texture.";
429 return false;
430 }
431
432 auto stencil_storage_mode =
433 stencil_texture->GetTextureDescriptor().storage_mode;
434 if (reads_from_onscreen_backdrop &&
435 stencil_storage_mode == StorageMode::kDeviceTransient) {
436 VALIDATION_LOG << "The given root RenderTarget stencil needs to be read, "
437 "but it's marked as transient.";
438 return false;
439 }
440 }
441 // Setup a new root stencil with an optimal configuration if one wasn't
442 // provided by the caller.
443 else {
444 root_render_target.SetupStencilAttachment(
445 *renderer.GetContext(), *renderer.GetRenderTargetCache(),
446 color0.texture->GetSize(),
447 renderer.GetContext()->GetCapabilities()->SupportsOffscreenMSAA(),
448 "ImpellerOnscreen",
449 GetDefaultStencilConfig(reads_from_onscreen_backdrop));
450 }
451
452 // Set up the clear color of the root pass.
453 color0.clear_color =
454 GetClearColorOrDefault(render_target.GetRenderTargetSize());
455 root_render_target.SetColorAttachment(color0, 0);
456
457 EntityPassTarget pass_target(
458 root_render_target,
459 renderer.GetDeviceCapabilities().SupportsReadFromResolve(),
460 renderer.GetDeviceCapabilities().SupportsImplicitResolvingMSAA());
461
462 return OnRender( //
463 renderer, // renderer
464 capture, // capture
465 root_render_target.GetRenderTargetSize(), // root_pass_size
466 pass_target, // pass_target
467 Point(), // global_pass_position
468 Point(), // local_pass_position
469 0, // pass_depth
470 clip_coverage_stack); // clip_coverage_stack
471 }
472
473 EntityPass::EntityResult EntityPass::GetEntityForElement(
474 const EntityPass::Element& element,
475 ContentContext& renderer,
476 Capture& capture,
477 InlinePassContext& pass_context,
478 ISize root_pass_size,
479 Point global_pass_position,
480 uint32_t pass_depth,
481 ClipCoverageStack& clip_coverage_stack,
482 size_t clip_depth_floor) const {
483 //--------------------------------------------------------------------------
484 /// Setup entity element.
485 ///
486
487 if (const auto& entity = std::get_if<Entity>(&element)) {
488 Entity element_entity = entity->Clone();
489 element_entity.SetCapture(capture.CreateChild("Entity"));
490 if (!global_pass_position.IsZero()) {
491 // If the pass image is going to be rendered with a non-zero position,
492 // apply the negative translation to entity copies before rendering them
493 // so that they'll end up rendering to the correct on-screen position.
494 element_entity.SetTransform(
495 Matrix::MakeTranslation(Vector3(-global_pass_position)) *
496 element_entity.GetTransform());
497 }
498 return EntityPass::EntityResult::Success(std::move(element_entity));
499 }
500
501 //--------------------------------------------------------------------------
502 /// Setup subpass element.
503 ///
504
505 else if (const auto& subpass_ptr =
506 std::get_if<std::unique_ptr<EntityPass>>(&element)) {
507 auto subpass = subpass_ptr->get();
508
509 if (subpass->delegate_->CanElide()) {
510 return EntityPass::EntityResult::Skip();
511 }
512
513 if (!subpass->backdrop_filter_proc_ &&
514 subpass->delegate_->CanCollapseIntoParentPass(subpass)) {
515 auto subpass_capture = capture.CreateChild("EntityPass (Collapsed)");
516 // Directly render into the parent target and move on.
517 if (!subpass->OnRender(
518 renderer, // renderer
519 subpass_capture, // capture
520 root_pass_size, // root_pass_size
521 pass_context.GetPassTarget(), // pass_target
522 global_pass_position, // global_pass_position
523 Point(), // local_pass_position
524 pass_depth, // pass_depth
525 clip_coverage_stack, // clip_coverage_stack
526 clip_depth_, // clip_depth_floor
527 nullptr, // backdrop_filter_contents
528 pass_context.GetRenderPass(pass_depth) // collapsed_parent_pass
529 )) {
530 // Validation error messages are triggered for all `OnRender()` failure
531 // cases.
532 return EntityPass::EntityResult::Failure();
533 }
534 return EntityPass::EntityResult::Skip();
535 }
536
537 std::shared_ptr<Contents> subpass_backdrop_filter_contents = nullptr;
538 if (subpass->backdrop_filter_proc_) {
539 auto texture = pass_context.GetTexture();
540 // Render the backdrop texture before any of the pass elements.
541 const auto& proc = subpass->backdrop_filter_proc_;
542 subpass_backdrop_filter_contents =
543 proc(FilterInput::Make(std::move(texture)),
544 subpass->transform_.Basis(), Entity::RenderingMode::kSubpass);
545
546 // If the very first thing we render in this EntityPass is a subpass that
547 // happens to have a backdrop filter, than that backdrop filter will end
548 // may wind up sampling from the raw, uncleared texture that came straight
549 // out of the texture cache. By calling `pass_context.GetRenderPass` here,
550 // we force the texture to pass through at least one RenderPass with the
551 // correct clear configuration before any sampling occurs.
552 pass_context.GetRenderPass(pass_depth);
553
554 // The subpass will need to read from the current pass texture when
555 // rendering the backdrop, so if there's an active pass, end it prior to
556 // rendering the subpass.
557 pass_context.EndPass();
558 }
559
560 if (clip_coverage_stack.empty()) {
561 // The current clip is empty. This means the pass texture won't be
562 // visible, so skip it.
563 capture.CreateChild("Subpass Entity (Skipped: Empty clip A)");
564 return EntityPass::EntityResult::Skip();
565 }
566 auto clip_coverage_back = clip_coverage_stack.back().coverage;
567 if (!clip_coverage_back.has_value()) {
568 capture.CreateChild("Subpass Entity (Skipped: Empty clip B)");
569 return EntityPass::EntityResult::Skip();
570 }
571
572 // The maximum coverage of the subpass. Subpasses textures should never
573 // extend outside the parent pass texture or the current clip coverage.
574 auto coverage_limit = Rect::MakeOriginSize(global_pass_position,
575 Size(pass_context.GetPassTarget()
576 .GetRenderTarget()
577 .GetRenderTargetSize()))
578 .Intersection(clip_coverage_back.value());
579 if (!coverage_limit.has_value()) {
580 capture.CreateChild("Subpass Entity (Skipped: Empty coverage limit A)");
581 return EntityPass::EntityResult::Skip();
582 }
583
584 coverage_limit =
585 coverage_limit->Intersection(Rect::MakeSize(root_pass_size));
586 if (!coverage_limit.has_value()) {
587 capture.CreateChild("Subpass Entity (Skipped: Empty coverage limit B)");
588 return EntityPass::EntityResult::Skip();
589 }
590
591 auto subpass_coverage =
592 (subpass->flood_clip_ || subpass_backdrop_filter_contents)
593 ? coverage_limit
594 : GetSubpassCoverage(*subpass, coverage_limit);
595 if (!subpass_coverage.has_value()) {
596 capture.CreateChild("Subpass Entity (Skipped: Empty subpass coverage A)");
597 return EntityPass::EntityResult::Skip();
598 }
599
600 auto subpass_size = ISize(subpass_coverage->GetSize());
601 if (subpass_size.IsEmpty()) {
602 capture.CreateChild("Subpass Entity (Skipped: Empty subpass coverage B)");
603 return EntityPass::EntityResult::Skip();
604 }
605
606 auto subpass_target = CreateRenderTarget(
607 renderer, // renderer
608 subpass_size, // size
609 subpass->GetTotalPassReads(renderer) > 0, // readable
610 subpass->GetClearColorOrDefault(subpass_size)); // clear_color
611
612 if (!subpass_target.IsValid()) {
613 VALIDATION_LOG << "Subpass render target is invalid.";
614 return EntityPass::EntityResult::Failure();
615 }
616
617 auto subpass_capture = capture.CreateChild("EntityPass");
618 subpass_capture.AddRect("Coverage", *subpass_coverage, {.readonly = true});
619
620 // Start non-collapsed subpasses with a fresh clip coverage stack limited by
621 // the subpass coverage. This is important because image filters applied to
622 // save layers may transform the subpass texture after it's rendered,
623 // causing parent clip coverage to get misaligned with the actual area that
624 // the subpass will affect in the parent pass.
625 ClipCoverageStack subpass_clip_coverage_stack = {ClipCoverageLayer{
626 .coverage = subpass_coverage, .clip_depth = subpass->clip_depth_}};
627
628 // Stencil textures aren't shared between EntityPasses (as much of the
629 // time they are transient).
630 if (!subpass->OnRender(
631 renderer, // renderer
632 subpass_capture, // capture
633 root_pass_size, // root_pass_size
634 subpass_target, // pass_target
635 subpass_coverage->GetOrigin(), // global_pass_position
636 subpass_coverage->GetOrigin() -
637 global_pass_position, // local_pass_position
638 ++pass_depth, // pass_depth
639 subpass_clip_coverage_stack, // clip_coverage_stack
640 subpass->clip_depth_, // clip_depth_floor
641 subpass_backdrop_filter_contents // backdrop_filter_contents
642 )) {
643 // Validation error messages are triggered for all `OnRender()` failure
644 // cases.
645 return EntityPass::EntityResult::Failure();
646 }
647
648 // The subpass target's texture may have changed during OnRender.
649 auto subpass_texture =
650 subpass_target.GetRenderTarget().GetRenderTargetTexture();
651
652 auto offscreen_texture_contents =
653 subpass->delegate_->CreateContentsForSubpassTarget(
654 subpass_texture,
655 Matrix::MakeTranslation(Vector3{-global_pass_position}) *
656 subpass->transform_);
657
658 if (!offscreen_texture_contents) {
659 // This is an error because the subpass delegate said the pass couldn't
660 // be collapsed into its parent. Yet, when asked how it want's to
661 // postprocess the offscreen texture, it couldn't give us an answer.
662 //
663 // Theoretically, we could collapse the pass now. But that would be
664 // wasteful as we already have the offscreen texture and we don't want
665 // to discard it without ever using it. Just make the delegate do the
666 // right thing.
667 return EntityPass::EntityResult::Failure();
668 }
669 Entity element_entity;
670 Capture subpass_texture_capture =
671 capture.CreateChild("Entity (Subpass texture)");
672 element_entity.SetCapture(subpass_texture_capture);
673 element_entity.SetContents(std::move(offscreen_texture_contents));
674 element_entity.SetClipDepth(subpass->clip_depth_);
675 element_entity.SetBlendMode(subpass->blend_mode_);
676 element_entity.SetTransform(subpass_texture_capture.AddMatrix(
677 "Transform",
678 Matrix::MakeTranslation(
679 Vector3(subpass_coverage->GetOrigin() - global_pass_position))));
680
681 return EntityPass::EntityResult::Success(std::move(element_entity));
682 }
683
684 FML_UNREACHABLE();
685 }
686
687 bool EntityPass::RenderElement(Entity& element_entity,
688 size_t clip_depth_floor,
689 InlinePassContext& pass_context,
690 int32_t pass_depth,
691 ContentContext& renderer,
692 ClipCoverageStack& clip_coverage_stack,
693 Point global_pass_position) const {
694 auto result = pass_context.GetRenderPass(pass_depth);
695 if (!result.pass) {
696 // Failure to produce a render pass should be explained by specific errors
697 // in `InlinePassContext::GetRenderPass()`, so avoid log spam and don't
698 // append a validation log here.
699 return false;
700 }
701
702 // If the pass context returns a backdrop texture, we need to draw it to the
703 // current pass. We do this because it's faster and takes significantly less
704 // memory than storing/loading large MSAA textures. Also, it's not possible to
705 // blit the non-MSAA resolve texture of the previous pass to MSAA textures
706 // (let alone a transient one).
707 if (result.backdrop_texture) {
708 auto size_rect = Rect::MakeSize(result.pass->GetRenderTargetSize());
709 auto msaa_backdrop_contents = TextureContents::MakeRect(size_rect);
710 msaa_backdrop_contents->SetStencilEnabled(false);
711 msaa_backdrop_contents->SetLabel("MSAA backdrop");
712 msaa_backdrop_contents->SetSourceRect(size_rect);
713 msaa_backdrop_contents->SetTexture(result.backdrop_texture);
714
715 Entity msaa_backdrop_entity;
716 msaa_backdrop_entity.SetContents(std::move(msaa_backdrop_contents));
717 msaa_backdrop_entity.SetBlendMode(BlendMode::kSource);
718 if (!msaa_backdrop_entity.Render(renderer, *result.pass)) {
719 VALIDATION_LOG << "Failed to render MSAA backdrop filter entity.";
720 return false;
721 }
722 }
723
724 auto current_clip_coverage = clip_coverage_stack.back().coverage;
725 if (current_clip_coverage.has_value()) {
726 // Entity transforms are relative to the current pass position, so we need
727 // to check clip coverage in the same space.
728 current_clip_coverage = current_clip_coverage->Shift(-global_pass_position);
729 }
730
731 if (!element_entity.ShouldRender(current_clip_coverage)) {
732 return true; // Nothing to render.
733 }
734
735 auto clip_coverage = element_entity.GetClipCoverage(current_clip_coverage);
736 if (clip_coverage.coverage.has_value()) {
737 clip_coverage.coverage =
738 clip_coverage.coverage->Shift(global_pass_position);
739 }
740
741 // The coverage hint tells the rendered Contents which portion of the
742 // rendered output will actually be used, and so we set this to the current
743 // clip coverage (which is the max clip bounds). The contents may
744 // optionally use this hint to avoid unnecessary rendering work.
745 auto element_coverage_hint = element_entity.GetContents()->GetCoverageHint();
746 element_entity.GetContents()->SetCoverageHint(
747 Rect::Intersection(element_coverage_hint, current_clip_coverage));
748
749 switch (clip_coverage.type) {
750 case Contents::ClipCoverage::Type::kNoChange:
751 break;
752 case Contents::ClipCoverage::Type::kAppend: {
753 auto op = clip_coverage_stack.back().coverage;
754 clip_coverage_stack.push_back(
755 ClipCoverageLayer{.coverage = clip_coverage.coverage,
756 .clip_depth = element_entity.GetClipDepth() + 1});
757 FML_DCHECK(clip_coverage_stack.back().clip_depth ==
758 clip_coverage_stack.front().clip_depth +
759 clip_coverage_stack.size() - 1);
760
761 if (!op.has_value()) {
762 // Running this append op won't impact the clip buffer because the
763 // whole screen is already being clipped, so skip it.
764 return true;
765 }
766 } break;
767 case Contents::ClipCoverage::Type::kRestore: {
768 if (clip_coverage_stack.back().clip_depth <=
769 element_entity.GetClipDepth()) {
770 // Drop clip restores that will do nothing.
771 return true;
772 }
773
774 auto restoration_index = element_entity.GetClipDepth() -
775 clip_coverage_stack.front().clip_depth;
776 FML_DCHECK(restoration_index < clip_coverage_stack.size());
777
778 // We only need to restore the area that covers the coverage of the
779 // clip rect at target depth + 1.
780 std::optional<Rect> restore_coverage =
781 (restoration_index + 1 < clip_coverage_stack.size())
782 ? clip_coverage_stack[restoration_index + 1].coverage
783 : std::nullopt;
784 if (restore_coverage.has_value()) {
785 // Make the coverage rectangle relative to the current pass.
786 restore_coverage = restore_coverage->Shift(-global_pass_position);
787 }
788 clip_coverage_stack.resize(restoration_index + 1);
789
790 if (!clip_coverage_stack.back().coverage.has_value()) {
791 // Running this restore op won't make anything renderable, so skip it.
792 return true;
793 }
794
795 auto restore_contents =
796 static_cast<ClipRestoreContents*>(element_entity.GetContents().get());
797 restore_contents->SetRestoreCoverage(restore_coverage);
798
799 } break;
800 }
801
802 #ifdef IMPELLER_ENABLE_CAPTURE
803 {
804 auto element_entity_coverage = element_entity.GetCoverage();
805 if (element_entity_coverage.has_value()) {
806 element_entity_coverage =
807 element_entity_coverage->Shift(global_pass_position);
808 element_entity.GetCapture().AddRect("Coverage", *element_entity_coverage,
809 {.readonly = true});
810 }
811 }
812 #endif
813
814 element_entity.SetClipDepth(element_entity.GetClipDepth() - clip_depth_floor);
815 if (!element_entity.Render(renderer, *result.pass)) {
816 VALIDATION_LOG << "Failed to render entity.";
817 return false;
818 }
819 return true;
820 }
821
822 bool EntityPass::OnRender(
823 ContentContext& renderer,
824 Capture& capture,
825 ISize root_pass_size,
826 EntityPassTarget& pass_target,
827 Point global_pass_position,
828 Point local_pass_position,
829 uint32_t pass_depth,
830 ClipCoverageStack& clip_coverage_stack,
831 size_t clip_depth_floor,
832 std::shared_ptr<Contents> backdrop_filter_contents,
833 const std::optional<InlinePassContext::RenderPassResult>&
834 collapsed_parent_pass) const {
835 TRACE_EVENT0("impeller", "EntityPass::OnRender");
836
837 auto context = renderer.GetContext();
838 InlinePassContext pass_context(context, pass_target,
839 GetTotalPassReads(renderer), GetElementCount(),
840 collapsed_parent_pass);
841 if (!pass_context.IsValid()) {
842 VALIDATION_LOG << SPrintF("Pass context invalid (Depth=%d)", pass_depth);
843 return false;
844 }
845 auto clear_color_size = pass_target.GetRenderTarget().GetRenderTargetSize();
846
847 if (!collapsed_parent_pass && GetClearColor(clear_color_size).has_value()) {
848 // Force the pass context to create at least one new pass if the clear color
849 // is present.
850 pass_context.GetRenderPass(pass_depth);
851 }
852
853 if (backdrop_filter_proc_) {
854 if (!backdrop_filter_contents) {
855 VALIDATION_LOG
856 << "EntityPass contains a backdrop filter, but no backdrop filter "
857 "contents was supplied by the parent pass at render time. This is "
858 "a bug in EntityPass. Parent passes are responsible for setting "
859 "up backdrop filters for their children.";
860 return false;
861 }
862
863 Entity backdrop_entity;
864 backdrop_entity.SetContents(std::move(backdrop_filter_contents));
865 backdrop_entity.SetTransform(
866 Matrix::MakeTranslation(Vector3(-local_pass_position)));
867 backdrop_entity.SetClipDepth(clip_depth_floor);
868
869 RenderElement(backdrop_entity, clip_depth_floor, pass_context, pass_depth,
870 renderer, clip_coverage_stack, global_pass_position);
871 }
872
873 bool is_collapsing_clear_colors = !collapsed_parent_pass &&
874 // Backdrop filters act as a entity before
875 // everything and disrupt the optimization.
876 !backdrop_filter_proc_;
877 for (const auto& element : elements_) {
878 // Skip elements that are incorporated into the clear color.
879 if (is_collapsing_clear_colors) {
880 auto [entity_color, _] =
881 ElementAsBackgroundColor(element, clear_color_size);
882 if (entity_color.has_value()) {
883 continue;
884 }
885 is_collapsing_clear_colors = false;
886 }
887
888 EntityResult result =
889 GetEntityForElement(element, // element
890 renderer, // renderer
891 capture, // capture
892 pass_context, // pass_context
893 root_pass_size, // root_pass_size
894 global_pass_position, // global_pass_position
895 pass_depth, // pass_depth
896 clip_coverage_stack, // clip_coverage_stack
897 clip_depth_floor); // clip_depth_floor
898
899 switch (result.status) {
900 case EntityResult::kSuccess:
901 break;
902 case EntityResult::kFailure:
903 // All failure cases should be covered by specific validation messages
904 // in `GetEntityForElement()`.
905 return false;
906 case EntityResult::kSkip:
907 continue;
908 };
909
910 //--------------------------------------------------------------------------
911 /// Setup advanced blends.
912 ///
913
914 if (result.entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
915 if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) {
916 auto src_contents = result.entity.GetContents();
917 auto contents = std::make_shared<FramebufferBlendContents>();
918 contents->SetChildContents(src_contents);
919 contents->SetBlendMode(result.entity.GetBlendMode());
920 result.entity.SetContents(std::move(contents));
921 result.entity.SetBlendMode(BlendMode::kSource);
922 } else {
923 // End the active pass and flush the buffer before rendering "advanced"
924 // blends. Advanced blends work by binding the current render target
925 // texture as an input ("destination"), blending with a second texture
926 // input ("source"), writing the result to an intermediate texture, and
927 // finally copying the data from the intermediate texture back to the
928 // render target texture. And so all of the commands that have written
929 // to the render target texture so far need to execute before it's bound
930 // for blending (otherwise the blend pass will end up executing before
931 // all the previous commands in the active pass).
932
933 if (!pass_context.EndPass()) {
934 VALIDATION_LOG
935 << "Failed to end the current render pass in order to read from "
936 "the backdrop texture and apply an advanced blend.";
937 return false;
938 }
939
940 // Amend an advanced blend filter to the contents, attaching the pass
941 // texture.
942 auto texture = pass_context.GetTexture();
943 if (!texture) {
944 VALIDATION_LOG << "Failed to fetch the color texture in order to "
945 "apply an advanced blend.";
946 return false;
947 }
948
949 FilterInput::Vector inputs = {
950 FilterInput::Make(texture, result.entity.GetTransform().Invert()),
951 FilterInput::Make(result.entity.GetContents())};
952 auto contents = ColorFilterContents::MakeBlend(
953 result.entity.GetBlendMode(), inputs);
954 contents->SetCoverageHint(result.entity.GetCoverage());
955 result.entity.SetContents(std::move(contents));
956 result.entity.SetBlendMode(BlendMode::kSource);
957 }
958 }
959
960 //--------------------------------------------------------------------------
961 /// Render the Element.
962 ///
963 if (!RenderElement(result.entity, clip_depth_floor, pass_context,
964 pass_depth, renderer, clip_coverage_stack,
965 global_pass_position)) {
966 // Specific validation logs are handled in `render_element()`.
967 return false;
968 }
969 }
970
971 #ifdef IMPELLER_DEBUG
972 //--------------------------------------------------------------------------
973 /// Draw debug checkerboard over offscreen textures.
974 ///
975
976 // When the pass depth is > 0, this EntityPass is being rendered to an
977 // offscreen texture.
978 if (enable_offscreen_debug_checkerboard_ &&
979 !collapsed_parent_pass.has_value() && pass_depth > 0) {
980 auto result = pass_context.GetRenderPass(pass_depth);
981 if (!result.pass) {
982 // Failure to produce a render pass should be explained by specific errors
983 // in `InlinePassContext::GetRenderPass()`.
984 return false;
985 }
986 auto checkerboard = CheckerboardContents();
987 auto color = ColorHSB(0, // hue
988 1, // saturation
989 std::max(0.0, 0.6 - pass_depth / 5), // brightness
990 0.25); // alpha
991 checkerboard.SetColor(Color(color));
992 checkerboard.Render(renderer, {}, *result.pass);
993 }
994 #endif
995
996 return true;
997 }
998
999 void EntityPass::IterateAllElements(
1000 const std::function<bool(Element&)>& iterator) {
1001 if (!iterator) {
1002 return;
1003 }
1004
1005 for (auto& element : elements_) {
1006 if (!iterator(element)) {
1007 return;
1008 }
1009 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1010 subpass->get()->IterateAllElements(iterator);
1011 }
1012 }
1013 }
1014
1015 void EntityPass::IterateAllEntities(
1016 const std::function<bool(Entity&)>& iterator) {
1017 if (!iterator) {
1018 return;
1019 }
1020
1021 for (auto& element : elements_) {
1022 if (auto entity = std::get_if<Entity>(&element)) {
1023 if (!iterator(*entity)) {
1024 return;
1025 }
1026 continue;
1027 }
1028 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1029 subpass->get()->IterateAllEntities(iterator);
1030 continue;
1031 }
1032 FML_UNREACHABLE();
1033 }
1034 }
1035
1036 void EntityPass::IterateAllEntities(
1037 const std::function<bool(const Entity&)>& iterator) const {
1038 if (!iterator) {
1039 return;
1040 }
1041
1042 for (const auto& element : elements_) {
1043 if (auto entity = std::get_if<Entity>(&element)) {
1044 if (!iterator(*entity)) {
1045 return;
1046 }
1047 continue;
1048 }
1049 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1050 const EntityPass* entity_pass = subpass->get();
1051 entity_pass->IterateAllEntities(iterator);
1052 continue;
1053 }
1054 FML_UNREACHABLE();
1055 }
1056 }
1057
1058 bool EntityPass::IterateUntilSubpass(
1059 const std::function<bool(Entity&)>& iterator) {
1060 if (!iterator) {
1061 return true;
1062 }
1063
1064 for (auto& element : elements_) {
1065 if (auto entity = std::get_if<Entity>(&element)) {
1066 if (!iterator(*entity)) {
1067 return false;
1068 }
1069 continue;
1070 }
1071 return true;
1072 }
1073 return false;
1074 }
1075
1076 size_t EntityPass::GetElementCount() const {
1077 return elements_.size();
1078 }
1079
1080 std::unique_ptr<EntityPass> EntityPass::Clone() const {
1081 std::vector<Element> new_elements;
1082 new_elements.reserve(elements_.size());
1083
1084 for (const auto& element : elements_) {
1085 if (auto entity = std::get_if<Entity>(&element)) {
1086 new_elements.push_back(entity->Clone());
1087 continue;
1088 }
1089 if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
1090 new_elements.push_back(subpass->get()->Clone());
1091 continue;
1092 }
1093 FML_UNREACHABLE();
1094 }
1095
1096 auto pass = std::make_unique<EntityPass>();
1097 pass->SetElements(std::move(new_elements));
1098 pass->backdrop_filter_reads_from_pass_texture_ =
1099 backdrop_filter_reads_from_pass_texture_;
1100 pass->advanced_blend_reads_from_pass_texture_ =
1101 advanced_blend_reads_from_pass_texture_;
1102 pass->backdrop_filter_proc_ = backdrop_filter_proc_;
1103 pass->blend_mode_ = blend_mode_;
1104 pass->delegate_ = delegate_;
1105 // Note: I tried also adding flood clip and bounds limit but one of the
1106 // two caused rendering in wonderous to break. It's 10:51 PM, and I'm
1107 // ready to move on.
1108 return pass;
1109 }
1110
1111 void EntityPass::SetTransform(Matrix transform) {
1112 transform_ = transform;
1113 }
1114
1115 void EntityPass::SetClipDepth(size_t clip_depth) {
1116 clip_depth_ = clip_depth;
1117 }
1118
1119 size_t EntityPass::GetClipDepth() {
1120 return clip_depth_;
1121 }
1122
1123 void EntityPass::SetBlendMode(BlendMode blend_mode) {
1124 blend_mode_ = blend_mode;
1125 flood_clip_ = Entity::IsBlendModeDestructive(blend_mode);
1126 }
1127
1128 Color EntityPass::GetClearColorOrDefault(ISize size) const {
1129 return GetClearColor(size).value_or(Color::BlackTransparent());
1130 }
1131
1132 std::optional<Color> EntityPass::GetClearColor(ISize target_size) const {
1133 if (backdrop_filter_proc_) {
1134 return std::nullopt;
1135 }
1136
1137 std::optional<Color> result = std::nullopt;
1138 for (const Element& element : elements_) {
1139 auto [entity_color, blend_mode] =
1140 ElementAsBackgroundColor(element, target_size);
1141 if (!entity_color.has_value()) {
1142 break;
1143 }
1144 result = result.value_or(Color::BlackTransparent())
1145 .Blend(entity_color.value(), blend_mode);
1146 }
1147 if (result.has_value()) {
1148 return result->Premultiply();
1149 }
1150 return result;
1151 }
1152
1153 void EntityPass::SetBackdropFilter(BackdropFilterProc proc) {
1154 if (superpass_) {
1155 VALIDATION_LOG << "Backdrop filters cannot be set on EntityPasses that "
1156 "have already been appended to another pass.";
1157 }
1158
1159 backdrop_filter_proc_ = std::move(proc);
1160 }
1161
1162 void EntityPass::SetEnableOffscreenCheckerboard(bool enabled) {
1163 enable_offscreen_debug_checkerboard_ = enabled;
1164 }
1165
1166 } // namespace impeller
impeller::EntityPass::AddSubpassInline
void AddSubpassInline(std::unique_ptr< EntityPass > pass)
Merges a given pass into this pass. Useful for drawing pre-recorded pictures that don't require rende...
Definition: entity_pass.cc:224
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:1015
impeller::Entity::kLastPipelineBlendMode
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:23
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:43
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:173
impeller::Entity::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:101
texture_contents.h
impeller::ContentContext::GetLazyGlyphAtlas
std::shared_ptr< LazyGlyphAtlas > GetLazyGlyphAtlas() const
Definition: content_context.h:724
entity.h
impeller::EntityPass::GetSubpassesDepth
size_t GetSubpassesDepth() const
Definition: entity_pass.cc:86
impeller::EntityPass::SetBoundsLimit
void SetBoundsLimit(std::optional< Rect > bounds_limit)
Set the bounds limit, which is provided by the user when creating a SaveLayer. This is a hint that al...
Definition: entity_pass.cc:62
impeller::BlendMode
BlendMode
Definition: color.h:59
impeller::Color
Definition: color.h:124
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:205
formats.h
impeller::StoreAction::kDontCare
@ kDontCare
impeller::EntityPassTarget
Definition: entity_pass_target.h:15
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:97
impeller::EntityPass::~EntityPass
~EntityPass()
framebuffer_blend_contents.h
checkerboard_contents.h
validation.h
impeller::RenderTarget::AttachmentConfigMSAA
Definition: render_target.h:58
impeller::CreateRenderTarget
static EntityPassTarget CreateRenderTarget(ContentContext &renderer, ISize size, bool readable, const Color &clear_color)
Definition: entity_pass.cc:250
impeller::RenderTarget::AttachmentConfig
Definition: render_target.h:51
impeller::Entity
Definition: entity.h:21
impeller::TSize< int64_t >
impeller::StorageMode::kDeviceTransient
@ kDeviceTransient
impeller::Point
TPoint< Scalar > Point
Definition: point.h:308
impeller::EntityPass::SetElements
void SetElements(std::vector< Element > elements)
Definition: entity_pass.cc:82
impeller::TRect< Scalar >::Intersection
constexpr std::optional< TRect< Scalar > > Intersection(const TRect &o) const
Definition: rect.h:312
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:66
impeller::EntityPass::GetSuperpass
EntityPass * GetSuperpass() const
Definition: entity_pass.cc:201
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
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:25
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:479
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::EntityPass::Element
std::variant< Entity, std::unique_ptr< EntityPass > > Element
Definition: entity_pass.h:36
impeller::ContentContext::GetRenderTargetCache
std::shared_ptr< RenderTargetAllocator > GetRenderTargetCache() const
Definition: content_context.h:728
impeller::Entity::GetContents
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:85
impeller::GetDefaultStencilConfig
static RenderTarget::AttachmentConfig GetDefaultStencilConfig(bool readable)
Definition: entity_pass.cc:241
impeller::RenderTarget::AttachmentConfig::storage_mode
StorageMode storage_mode
Definition: render_target.h:52
clip_contents.h
impeller::RenderTarget
Definition: render_target.h:49
entity_pass.h
filter_input.h
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:488
strings.h
color_filter_contents.h
impeller::Entity::RenderingMode::kSubpass
@ kSubpass
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:105
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:138
impeller::RenderTarget::CreateOffscreen
static RenderTarget CreateOffscreen(const Context &context, RenderTargetAllocator &allocator, ISize size, const std::string &label="Offscreen", AttachmentConfig color_attachment_config=kDefaultColorAttachmentConfig, std::optional< AttachmentConfig > stencil_attachment_config=kDefaultStencilAttachmentConfig)
Definition: render_target.cc:223
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:67
command_buffer.h
content_context.h
impeller::Entity::SetTransform
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:53
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:483
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:44
impeller::LoadAction::kDontCare
@ kDontCare
rect.h
impeller::EntityPass::Render
bool Render(ContentContext &renderer, const RenderTarget &render_target) const
Definition: entity_pass.cc:304
impeller::TRect< Scalar >::Union
constexpr TRect Union(const TRect &o) const
Definition: rect.h:302
color.h
impeller::EntityPass::kCaptureDocumentName
static const std::string kCaptureDocumentName
Definition: entity_pass.h:38
impeller::EntityPass::SetDelegate
void SetDelegate(std::shared_ptr< EntityPassDelegate > delgate)
Definition: entity_pass.cc:55
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
Definition: aiks_context.cc:10
impeller::kRestore
@ kRestore
Definition: canvas_recorder.h:23
impeller::ContentContext
Definition: content_context.h:332
impeller::RenderTarget::CreateOffscreenMSAA
static RenderTarget CreateOffscreenMSAA(const Context &context, RenderTargetAllocator &allocator, ISize size, const std::string &label="Offscreen MSAA", AttachmentConfigMSAA color_attachment_config=kDefaultColorAttachmentConfigMSAA, std::optional< AttachmentConfig > stencil_attachment_config=kDefaultStencilAttachmentConfig)
Definition: render_target.cc:265
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::EntityPass::AddEntity
void AddEntity(Entity entity)
Add an entity to the current entity pass.
Definition: entity_pass.cc:70
inline_pass_context.h