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 
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"
16 #include "impeller/core/formats.h"
23 #include "impeller/entity/entity.h"
26 #include "impeller/geometry/rect.h"
28 
29 #ifdef IMPELLER_DEBUG
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 
71  if (entity.GetBlendMode() == BlendMode::kSourceOver &&
72  entity.GetContents()->IsOpaque()) {
74  }
75 
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 
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 
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 
244  .load_action = LoadAction::kDontCare,
245  .store_action = StoreAction::kDontCare,
246  };
247 
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()) {
261  *context, // context
262  *renderer.GetRenderTargetCache(), // allocator
263  size, // size
264  "EntityPass", // label
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 {
275  *context, // context
276  *renderer.GetRenderTargetCache(), // allocator
277  size, // size
278  "EntityPass", // label
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(),
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 
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;
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) {
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()) {
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,
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;
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