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