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