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