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