Flutter Impeller
blend_filter_contents.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 <array>
8 #include <memory>
9 #include <optional>
10 
11 #include "flutter/fml/logging.h"
12 #include "impeller/base/strings.h"
13 #include "impeller/core/formats.h"
22 #include "impeller/entity/entity.h"
23 #include "impeller/entity/texture_fill.frag.h"
24 #include "impeller/entity/texture_fill.vert.h"
28 
29 namespace impeller {
30 
31 std::optional<BlendMode> InvertPorterDuffBlend(BlendMode blend_mode) {
32  switch (blend_mode) {
33  case BlendMode::kClear:
34  return BlendMode::kClear;
35  case BlendMode::kSource:
38  return BlendMode::kSource;
46  return BlendMode::kSourceIn;
50  return BlendMode::kSourceOut;
55  case BlendMode::kXor:
56  return BlendMode::kXor;
57  case BlendMode::kPlus:
58  return BlendMode::kPlus;
60  return BlendMode::kModulate;
61  default:
62  return std::nullopt;
63  }
64 }
65 
68 }
69 
71 
72 using PipelineProc = std::shared_ptr<Pipeline<PipelineDescriptor>> (
74 
75 template <typename TPipeline>
76 static std::optional<Entity> AdvancedBlend(
77  const FilterInput::Vector& inputs,
78  const ContentContext& renderer,
79  const Entity& entity,
80  const Rect& coverage,
81  BlendMode blend_mode,
82  std::optional<Color> foreground_color,
84  PipelineProc pipeline_proc,
85  std::optional<Scalar> alpha) {
86  using VS = typename TPipeline::VertexShader;
87  using FS = typename TPipeline::FragmentShader;
88 
89  //----------------------------------------------------------------------------
90  /// Handle inputs.
91  ///
92 
93  const size_t total_inputs =
94  inputs.size() + (foreground_color.has_value() ? 1 : 0);
95  if (total_inputs < 2) {
96  return std::nullopt;
97  }
98 
99  auto dst_snapshot =
100  inputs[0]->GetSnapshot("AdvancedBlend(Dst)", renderer, entity);
101  if (!dst_snapshot.has_value()) {
102  return std::nullopt;
103  }
104  auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
105  if (!maybe_dst_uvs.has_value()) {
106  return std::nullopt;
107  }
108  auto dst_uvs = maybe_dst_uvs.value();
109 
110  std::optional<Snapshot> src_snapshot;
111  std::array<Point, 4> src_uvs;
112  if (!foreground_color.has_value()) {
113  src_snapshot =
114  inputs[1]->GetSnapshot("AdvancedBlend(Src)", renderer, entity);
115  if (!src_snapshot.has_value()) {
116  if (!dst_snapshot.has_value()) {
117  return std::nullopt;
118  }
119  return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
120  }
121  auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage);
122  if (!maybe_src_uvs.has_value()) {
123  if (!dst_snapshot.has_value()) {
124  return std::nullopt;
125  }
126  return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
127  }
128  src_uvs = maybe_src_uvs.value();
129  }
130 
131  Rect subpass_coverage = coverage;
132  if (entity.GetContents()) {
133  auto coverage_hint = entity.GetContents()->GetCoverageHint();
134 
135  if (coverage_hint.has_value()) {
136  auto maybe_subpass_coverage =
137  subpass_coverage.Intersection(*coverage_hint);
138  if (!maybe_subpass_coverage.has_value()) {
139  return std::nullopt; // Nothing to render.
140  }
141 
142  subpass_coverage = *maybe_subpass_coverage;
143  }
144  }
145 
146  //----------------------------------------------------------------------------
147  /// Render to texture.
148  ///
149 
150  ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
151  RenderPass& pass) {
152  auto& host_buffer = renderer.GetTransientsBuffer();
153 
154  auto size = pass.GetRenderTargetSize();
156  vtx_builder.AddVertices({
157  {Point(0, 0), dst_uvs[0], src_uvs[0]},
158  {Point(size.width, 0), dst_uvs[1], src_uvs[1]},
159  {Point(0, size.height), dst_uvs[2], src_uvs[2]},
160  {Point(size.width, size.height), dst_uvs[3], src_uvs[3]},
161  });
162  auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
163 
164  auto options = OptionsFromPass(pass);
165  options.primitive_type = PrimitiveType::kTriangleStrip;
166  options.blend_mode = BlendMode::kSource;
167  std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
168  std::invoke(pipeline_proc, renderer, options);
169 
170 #ifdef IMPELLER_DEBUG
171  pass.SetCommandLabel(
172  SPrintF("Advanced Blend Filter (%s)", BlendModeToString(blend_mode)));
173 #endif // IMPELLER_DEBUG
174  pass.SetVertexBuffer(std::move(vtx_buffer));
175  pass.SetPipeline(pipeline);
176 
177  typename FS::BlendInfo blend_info;
178  typename VS::FrameInfo frame_info;
179 
180  auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
182  dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
183  dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
184  }
185  const std::unique_ptr<const Sampler>& dst_sampler =
186  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
187  dst_sampler_descriptor);
188  FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
189  frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
190  blend_info.dst_input_alpha =
192  ? dst_snapshot->opacity
193  : 1.0;
194 
195  if (foreground_color.has_value()) {
196  blend_info.color_factor = 1;
197  blend_info.color = foreground_color.value();
198  // This texture will not be sampled from due to the color factor. But
199  // this is present so that validation doesn't trip on a missing
200  // binding.
201  FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
202  } else {
203  auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
205  src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
206  src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
207  }
208  const std::unique_ptr<const Sampler>& src_sampler =
209  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
210  src_sampler_descriptor);
211  blend_info.color_factor = 0;
212  blend_info.src_input_alpha = src_snapshot->opacity;
213  FS::BindTextureSamplerSrc(pass, src_snapshot->texture, src_sampler);
214  frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale();
215  }
216  auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
217  FS::BindBlendInfo(pass, blend_uniform);
218 
219  frame_info.mvp = pass.GetOrthographicTransform() *
221  subpass_coverage.GetOrigin());
222 
223  auto uniform_view = host_buffer.EmplaceUniform(frame_info);
224  VS::BindFrameInfo(pass, uniform_view);
225 
226  return pass.Draw().ok();
227  };
228 
229  std::shared_ptr<CommandBuffer> command_buffer =
230  renderer.GetContext()->CreateCommandBuffer();
231  if (!command_buffer) {
232  return std::nullopt;
233  }
234  fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
235  "Advanced Blend Filter", ISize(subpass_coverage.GetSize()),
236  command_buffer, callback);
237  if (!render_target.ok()) {
238  return std::nullopt;
239  }
240  if (!renderer.GetContext()
241  ->GetCommandQueue()
242  ->Submit(/*buffers=*/{std::move(command_buffer)})
243  .ok()) {
244  return std::nullopt;
245  }
246 
247  return Entity::FromSnapshot(
248  Snapshot{
249  .texture = render_target.value().GetRenderTargetTexture(),
250  .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
251  // Since we absorbed the transform of the inputs and used the
252  // respective snapshot sampling modes when blending, pass on
253  // the default NN clamp sampler.
254  .sampler_descriptor = {},
255  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
256  ? 1.0f
257  : dst_snapshot->opacity) *
258  alpha.value_or(1.0)},
259  entity.GetBlendMode());
260 }
261 
262 std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
263  const std::shared_ptr<FilterInput>& input,
264  const ContentContext& renderer,
265  const Entity& entity,
266  const Rect& coverage,
267  Color foreground_color,
268  BlendMode blend_mode,
269  std::optional<Scalar> alpha,
270  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
271  auto dst_snapshot =
272  input->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
273  if (!dst_snapshot.has_value()) {
274  return std::nullopt;
275  }
276 
277  RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode, alpha,
278  absorb_opacity](const ContentContext& renderer,
279  const Entity& entity,
280  RenderPass& pass) -> bool {
283 
284  auto& host_buffer = renderer.GetTransientsBuffer();
285 
286  auto size = dst_snapshot->texture->GetSize();
287  VertexBufferBuilder<VS::PerVertexData> vtx_builder;
288  vtx_builder.AddVertices({
289  {{0, 0}, {0, 0}, {0, 0}},
290  {Point(size.width, 0), {1, 0}, {1, 0}},
291  {Point(0, size.height), {0, 1}, {0, 1}},
292  {Point(size.width, size.height), {1, 1}, {1, 1}},
293  });
294  auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
295 
296 #ifdef IMPELLER_DEBUG
297  pass.SetCommandLabel(SPrintF("Foreground Advanced Blend Filter (%s)",
298  BlendModeToString(blend_mode)));
299 #endif // IMPELLER_DEBUG
300  pass.SetVertexBuffer(std::move(vtx_buffer));
301  auto options = OptionsFromPass(pass);
302  options.primitive_type = PrimitiveType::kTriangleStrip;
303 
304  switch (blend_mode) {
305  case BlendMode::kScreen:
306  pass.SetPipeline(renderer.GetBlendScreenPipeline(options));
307  break;
308  case BlendMode::kOverlay:
309  pass.SetPipeline(renderer.GetBlendOverlayPipeline(options));
310  break;
311  case BlendMode::kDarken:
312  pass.SetPipeline(renderer.GetBlendDarkenPipeline(options));
313  break;
314  case BlendMode::kLighten:
315  pass.SetPipeline(renderer.GetBlendLightenPipeline(options));
316  break;
318  pass.SetPipeline(renderer.GetBlendColorDodgePipeline(options));
319  break;
321  pass.SetPipeline(renderer.GetBlendColorBurnPipeline(options));
322  break;
324  pass.SetPipeline(renderer.GetBlendHardLightPipeline(options));
325  break;
327  pass.SetPipeline(renderer.GetBlendSoftLightPipeline(options));
328  break;
330  pass.SetPipeline(renderer.GetBlendDifferencePipeline(options));
331  break;
333  pass.SetPipeline(renderer.GetBlendExclusionPipeline(options));
334  break;
336  pass.SetPipeline(renderer.GetBlendMultiplyPipeline(options));
337  break;
338  case BlendMode::kHue:
339  pass.SetPipeline(renderer.GetBlendHuePipeline(options));
340  break;
342  pass.SetPipeline(renderer.GetBlendSaturationPipeline(options));
343  break;
344  case BlendMode::kColor:
345  pass.SetPipeline(renderer.GetBlendColorPipeline(options));
346  break;
348  pass.SetPipeline(renderer.GetBlendLuminosityPipeline(options));
349  break;
350  default:
351  return false;
352  }
353 
354  FS::BlendInfo blend_info;
355  VS::FrameInfo frame_info;
356 
357  auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
358  if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
359  dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
360  dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
361  }
362  const std::unique_ptr<const Sampler>& dst_sampler =
363  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
364  dst_sampler_descriptor);
365  FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
366  frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
367 
368  frame_info.mvp = Entity::GetShaderTransform(entity.GetShaderClipDepth(),
369  pass, dst_snapshot->transform);
370 
371  blend_info.dst_input_alpha =
373  ? dst_snapshot->opacity * alpha.value_or(1.0)
374  : 1.0;
375 
376  blend_info.color_factor = 1;
377  blend_info.color = foreground_color;
378  // This texture will not be sampled from due to the color factor. But
379  // this is present so that validation doesn't trip on a missing
380  // binding.
381  FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
382 
383  auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
384  FS::BindBlendInfo(pass, blend_uniform);
385 
386  auto uniform_view = host_buffer.EmplaceUniform(frame_info);
387  VS::BindFrameInfo(pass, uniform_view);
388 
389  return pass.Draw().ok();
390  };
391  CoverageProc coverage_proc =
392  [coverage](const Entity& entity) -> std::optional<Rect> {
393  return coverage.TransformBounds(entity.GetTransform());
394  };
395 
396  auto contents = AnonymousContents::Make(render_proc, coverage_proc);
397 
398  Entity sub_entity;
399  sub_entity.SetContents(std::move(contents));
400 
401  return sub_entity;
402 }
403 
404 std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
405  const std::shared_ptr<FilterInput>& input,
406  const ContentContext& renderer,
407  const Entity& entity,
408  const Rect& coverage,
409  Color foreground_color,
410  BlendMode blend_mode,
411  std::optional<Scalar> alpha,
412  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
413  if (blend_mode == BlendMode::kClear) {
414  return std::nullopt;
415  }
416 
417  auto dst_snapshot =
418  input->GetSnapshot("ForegroundPorterDuffBlend", renderer, entity);
419  if (!dst_snapshot.has_value()) {
420  return std::nullopt;
421  }
422 
423  if (blend_mode == BlendMode::kDestination) {
424  return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
425  }
426 
427  RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode,
428  absorb_opacity, alpha](
429  const ContentContext& renderer,
430  const Entity& entity, RenderPass& pass) -> bool {
433 
434  auto& host_buffer = renderer.GetTransientsBuffer();
435  auto size = dst_snapshot->texture->GetSize();
436  auto color = foreground_color.Premultiply();
437  VertexBufferBuilder<VS::PerVertexData> vtx_builder;
438  vtx_builder.AddVertices({
439  {{0, 0}, {0, 0}, color},
440  {Point(size.width, 0), {1, 0}, color},
441  {Point(0, size.height), {0, 1}, color},
442  {Point(size.width, size.height), {1, 1}, color},
443  });
444  auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
445 
446 #ifdef IMPELLER_DEBUG
447  pass.SetCommandLabel(SPrintF("Foreground PorterDuff Blend Filter (%s)",
448  BlendModeToString(blend_mode)));
449 #endif // IMPELLER_DEBUG
450  pass.SetVertexBuffer(std::move(vtx_buffer));
451  auto options = OptionsFromPass(pass);
452  options.primitive_type = PrimitiveType::kTriangleStrip;
453  pass.SetPipeline(renderer.GetPorterDuffBlendPipeline(options));
454 
455  FS::FragInfo frag_info;
456  VS::FrameInfo frame_info;
457 
458  frame_info.mvp = Entity::GetShaderTransform(entity.GetShaderClipDepth(),
459  pass, dst_snapshot->transform);
460 
461  auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
462  if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
463  dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
464  dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
465  }
466  const std::unique_ptr<const Sampler>& dst_sampler =
467  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
468  dst_sampler_descriptor);
469  FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
470  frame_info.texture_sampler_y_coord_scale =
471  dst_snapshot->texture->GetYCoordScale();
472 
473  frag_info.input_alpha =
475  ? dst_snapshot->opacity * alpha.value_or(1.0)
476  : 1.0;
477  frag_info.output_alpha = 1.0;
478 
479  auto blend_coefficients =
480  kPorterDuffCoefficients[static_cast<int>(blend_mode)];
481  frag_info.src_coeff = blend_coefficients[0];
482  frag_info.src_coeff_dst_alpha = blend_coefficients[1];
483  frag_info.dst_coeff = blend_coefficients[2];
484  frag_info.dst_coeff_src_alpha = blend_coefficients[3];
485  frag_info.dst_coeff_src_color = blend_coefficients[4];
486 
487  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
488  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
489 
490  return pass.Draw().ok();
491  };
492 
493  CoverageProc coverage_proc =
494  [coverage](const Entity& entity) -> std::optional<Rect> {
495  return coverage.TransformBounds(entity.GetTransform());
496  };
497 
498  auto contents = AnonymousContents::Make(render_proc, coverage_proc);
499 
500  Entity sub_entity;
501  sub_entity.SetContents(std::move(contents));
502 
503  return sub_entity;
504 }
505 
506 static std::optional<Entity> PipelineBlend(
507  const FilterInput::Vector& inputs,
508  const ContentContext& renderer,
509  const Entity& entity,
510  const Rect& coverage,
511  BlendMode blend_mode,
512  std::optional<Color> foreground_color,
513  ColorFilterContents::AbsorbOpacity absorb_opacity,
514  std::optional<Scalar> alpha) {
517 
518  auto dst_snapshot =
519  inputs[0]->GetSnapshot("PipelineBlend(Dst)", renderer, entity);
520  if (!dst_snapshot.has_value()) {
521  return std::nullopt; // Nothing to render.
522  }
523 
524  Rect subpass_coverage = coverage;
525  if (entity.GetContents()) {
526  auto coverage_hint = entity.GetContents()->GetCoverageHint();
527 
528  if (coverage_hint.has_value()) {
529  auto maybe_subpass_coverage =
530  subpass_coverage.Intersection(*coverage_hint);
531  if (!maybe_subpass_coverage.has_value()) {
532  return std::nullopt; // Nothing to render.
533  }
534 
535  subpass_coverage = *maybe_subpass_coverage;
536  }
537  }
538 
539  ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
540  RenderPass& pass) {
541  auto& host_buffer = renderer.GetTransientsBuffer();
542 
543 #ifdef IMPELLER_DEBUG
544  pass.SetCommandLabel(
545  SPrintF("Pipeline Blend Filter (%s)", BlendModeToString(blend_mode)));
546 #endif // IMPELLER_DEBUG
547  auto options = OptionsFromPass(pass);
548  options.primitive_type = PrimitiveType::kTriangleStrip;
549 
550  auto add_blend_command = [&](std::optional<Snapshot> input) {
551  if (!input.has_value()) {
552  return false;
553  }
554  auto input_coverage = input->GetCoverage();
555  if (!input_coverage.has_value()) {
556  return false;
557  }
558 
559  const std::unique_ptr<const Sampler>& sampler =
560  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
561  input->sampler_descriptor);
562  FS::BindTextureSampler(pass, input->texture, sampler);
563 
564  auto size = input->texture->GetSize();
566  vtx_builder.AddVertices({
567  {Point(0, 0), Point(0, 0)},
568  {Point(size.width, 0), Point(1, 0)},
569  {Point(0, size.height), Point(0, 1)},
570  {Point(size.width, size.height), Point(1, 1)},
571  });
572  pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
573 
574  VS::FrameInfo frame_info;
575  frame_info.mvp = pass.GetOrthographicTransform() *
576  Matrix::MakeTranslation(-subpass_coverage.GetOrigin()) *
577  input->transform;
578  frame_info.texture_sampler_y_coord_scale =
579  input->texture->GetYCoordScale();
580 
581  FS::FragInfo frag_info;
582  frag_info.alpha =
584  ? input->opacity
585  : 1.0;
586  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
587  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
588 
589  return pass.Draw().ok();
590  };
591 
592  // Draw the first texture using kSource.
593  options.blend_mode = BlendMode::kSource;
594  pass.SetPipeline(renderer.GetTexturePipeline(options));
595  if (!add_blend_command(dst_snapshot)) {
596  return true;
597  }
598 
599  // Write subsequent textures using the selected blend mode.
600 
601  if (inputs.size() >= 2) {
602  options.blend_mode = blend_mode;
603  pass.SetPipeline(renderer.GetTexturePipeline(options));
604 
605  for (auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
606  texture_i++) {
607  auto src_input = texture_i->get()->GetSnapshot("PipelineBlend(Src)",
608  renderer, entity);
609  if (!add_blend_command(src_input)) {
610  return true;
611  }
612  }
613  }
614 
615  // If a foreground color is set, blend it in.
616 
617  if (foreground_color.has_value()) {
618  auto contents = std::make_shared<SolidColorContents>();
619  contents->SetGeometry(
620  Geometry::MakeRect(Rect::MakeSize(pass.GetRenderTargetSize())));
621  contents->SetColor(foreground_color.value());
622 
623  Entity foreground_entity;
624  foreground_entity.SetBlendMode(blend_mode);
625  foreground_entity.SetContents(contents);
626  if (!foreground_entity.Render(renderer, pass)) {
627  return false;
628  }
629  }
630 
631  return true;
632  };
633 
634  std::shared_ptr<CommandBuffer> command_buffer =
635  renderer.GetContext()->CreateCommandBuffer();
636  if (!command_buffer) {
637  return std::nullopt;
638  }
639 
640  fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
641  "Pipeline Blend Filter", ISize(subpass_coverage.GetSize()),
642  command_buffer, callback);
643 
644  if (!render_target.ok()) {
645  return std::nullopt;
646  }
647 
648  if (!renderer.GetContext()
649  ->GetCommandQueue()
650  ->Submit(/*buffers=*/{std::move(command_buffer)})
651  .ok()) {
652  return std::nullopt;
653  }
654 
655  return Entity::FromSnapshot(
656  Snapshot{
657  .texture = render_target.value().GetRenderTargetTexture(),
658  .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
659  // Since we absorbed the transform of the inputs and used the
660  // respective snapshot sampling modes when blending, pass on
661  // the default NN clamp sampler.
662  .sampler_descriptor = {},
663  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
664  ? 1.0f
665  : dst_snapshot->opacity) *
666  alpha.value_or(1.0)},
667  entity.GetBlendMode());
668 }
669 
670 std::optional<Entity> BlendFilterContents::CreateFramebufferAdvancedBlend(
671  const FilterInput::Vector& inputs,
672  const ContentContext& renderer,
673  const Entity& entity,
674  const Rect& coverage,
675  std::optional<Color> foreground_color,
676  BlendMode blend_mode,
677  std::optional<Scalar> alpha,
678  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
679  // This works with either 2 contents or 1 contents and a foreground color.
680  FML_DCHECK(inputs.size() == 2u ||
681  (inputs.size() == 1u && foreground_color.has_value()));
682 
683  auto dst_snapshot =
684  inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
685  if (!dst_snapshot.has_value()) {
686  return std::nullopt;
687  }
688 
689  std::shared_ptr<Texture> foreground_texture;
690 
691  ContentContext::SubpassCallback subpass_callback = [&](const ContentContext&
692  renderer,
693  RenderPass& pass) {
694  // First, we create a new render pass and populate it with the contents
695  // of the first (dst) input.
696  HostBuffer& host_buffer = renderer.GetTransientsBuffer();
697 
698  {
699  using FS = TextureFillFragmentShader;
700  using VS = TextureFillVertexShader;
701 
702  pass.SetCommandLabel("Framebuffer Advanced Blend");
703  auto pipeline_options = OptionsFromPass(pass);
704  pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
705  pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
706 
707  VS::FrameInfo frame_info;
708  frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
709  frame_info.texture_sampler_y_coord_scale = 1.0;
710 
711  FS::FragInfo frag_info;
712  frag_info.alpha = 1.0;
713 
714  VertexBufferBuilder<VS::PerVertexData> vtx_builder;
715  vtx_builder.AddVertices({
716  {{0, 0}, {0, 0}},
717  {Point(1, 0), {1, 0}},
718  {Point(0, 1), {0, 1}},
719  {Point(1, 1), {1, 1}},
720  });
721  auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
722  pass.SetVertexBuffer(std::move(vtx_buffer));
723 
724  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
725  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
726  FS::BindTextureSampler(
727  pass, dst_snapshot->texture,
728  renderer.GetContext()->GetSamplerLibrary()->GetSampler({}));
729 
730  if (!pass.Draw().ok()) {
731  return false;
732  }
733  }
734 
735  {
738 
739  // Next, we render the second contents to a snapshot, or create a 1x1
740  // texture for the foreground color.
741  std::shared_ptr<Texture> src_texture;
742  if (foreground_color.has_value()) {
743  src_texture = foreground_texture;
744  } else {
745  auto src_snapshot =
746  inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
747  if (!src_snapshot.has_value()) {
748  return false;
749  }
750  // This doesn't really handle the case where the transforms are wildly
751  // different, but we only need to support blending two contents together
752  // in limited circumstances (mask blur).
753  src_texture = src_snapshot->texture;
754  }
755 
756  VertexBufferBuilder<VS::PerVertexData> vtx_builder;
757  vtx_builder.AddVertices({
758  {Point(0, 0), Point(0, 0)},
759  {Point(1, 0), Point(1, 0)},
760  {Point(0, 1), Point(0, 1)},
761  {Point(1, 1), Point(1, 1)},
762  });
763 
764  auto options = OptionsFromPass(pass);
765  options.blend_mode = BlendMode::kSource;
766  options.primitive_type = PrimitiveType::kTriangleStrip;
767 
768  pass.SetCommandLabel("Framebuffer Advanced Blend Filter");
769  pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
770 
771  switch (blend_mode) {
772  case BlendMode::kScreen:
773  pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options));
774  break;
775  case BlendMode::kOverlay:
776  pass.SetPipeline(
777  renderer.GetFramebufferBlendOverlayPipeline(options));
778  break;
779  case BlendMode::kDarken:
780  pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options));
781  break;
782  case BlendMode::kLighten:
783  pass.SetPipeline(
784  renderer.GetFramebufferBlendLightenPipeline(options));
785  break;
787  pass.SetPipeline(
788  renderer.GetFramebufferBlendColorDodgePipeline(options));
789  break;
791  pass.SetPipeline(
792  renderer.GetFramebufferBlendColorBurnPipeline(options));
793  break;
795  pass.SetPipeline(
796  renderer.GetFramebufferBlendHardLightPipeline(options));
797  break;
799  pass.SetPipeline(
800  renderer.GetFramebufferBlendSoftLightPipeline(options));
801  break;
803  pass.SetPipeline(
804  renderer.GetFramebufferBlendDifferencePipeline(options));
805  break;
807  pass.SetPipeline(
808  renderer.GetFramebufferBlendExclusionPipeline(options));
809  break;
811  pass.SetPipeline(
812  renderer.GetFramebufferBlendMultiplyPipeline(options));
813  break;
814  case BlendMode::kHue:
815  pass.SetPipeline(renderer.GetFramebufferBlendHuePipeline(options));
816  break;
818  pass.SetPipeline(
819  renderer.GetFramebufferBlendSaturationPipeline(options));
820  break;
821  case BlendMode::kColor:
822  pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options));
823  break;
825  pass.SetPipeline(
826  renderer.GetFramebufferBlendLuminosityPipeline(options));
827  break;
828  default:
829  return false;
830  }
831 
832  VS::FrameInfo frame_info;
833  FS::FragInfo frag_info;
834 
835  auto src_sampler_descriptor = SamplerDescriptor{};
836  if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
837  src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
838  src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
839  }
840  const std::unique_ptr<const Sampler>& src_sampler =
841  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
842  src_sampler_descriptor);
843  FS::BindTextureSamplerSrc(pass, src_texture, src_sampler);
844 
845  frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
846  frame_info.src_y_coord_scale = src_texture->GetYCoordScale();
847  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
848 
849  frag_info.src_input_alpha = 1.0;
850  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
851 
852  return pass.Draw().ok();
853  }
854  };
855 
856  std::shared_ptr<CommandBuffer> cmd_buffer =
857  renderer.GetContext()->CreateCommandBuffer();
858 
859  // Generate a 1x1 texture to implement foreground color blending.
860  if (foreground_color.has_value()) {
861  TextureDescriptor desc;
862  desc.size = {1, 1};
863  desc.format = PixelFormat::kR8G8B8A8UNormInt;
864  desc.storage_mode = StorageMode::kDevicePrivate;
865  foreground_texture =
866  renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc);
867  if (!foreground_texture) {
868  return std::nullopt;
869  }
870  auto blit_pass = cmd_buffer->CreateBlitPass();
871  auto buffer_view = renderer.GetTransientsBuffer().Emplace(
872  foreground_color->Premultiply().ToR8G8B8A8(), /*alignment=*/4);
873 
874  blit_pass->AddCopy(std::move(buffer_view), foreground_texture);
875  if (!blit_pass->EncodeCommands(
876  renderer.GetContext()->GetResourceAllocator())) {
877  return std::nullopt;
878  }
879  }
880 
881  auto render_target =
882  renderer.MakeSubpass("FramebufferBlend", dst_snapshot->texture->GetSize(),
883  cmd_buffer, subpass_callback);
884 
885  if (!render_target.ok()) {
886  return std::nullopt;
887  }
888 
889  if (!renderer.GetContext()
890  ->GetCommandQueue()
891  ->Submit(/*buffers=*/{std::move(cmd_buffer)})
892  .ok()) {
893  return std::nullopt;
894  }
895 
896  return Entity::FromSnapshot(
897  Snapshot{
898  .texture = render_target.value().GetRenderTargetTexture(),
899  .transform = dst_snapshot->transform,
900  // Since we absorbed the transform of the inputs and used the
901  // respective snapshot sampling modes when blending, pass on
902  // the default NN clamp sampler.
903  .sampler_descriptor = {},
904  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
905  ? 1.0f
906  : dst_snapshot->opacity) *
907  alpha.value_or(1.0)},
908  entity.GetBlendMode());
909 }
910 
911 #define BLEND_CASE(mode) \
912  case BlendMode::k##mode: \
913  advanced_blend_proc_ = \
914  [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
915  const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
916  std::optional<Color> fg_color, \
917  ColorFilterContents::AbsorbOpacity absorb_opacity, \
918  std::optional<Scalar> alpha) { \
919  PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
920  return AdvancedBlend<Blend##mode##Pipeline>( \
921  inputs, renderer, entity, coverage, blend_mode, fg_color, \
922  absorb_opacity, p, alpha); \
923  }; \
924  break;
925 
927  if (blend_mode > Entity::kLastAdvancedBlendMode) {
928  VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
929  << " assigned to BlendFilterContents.";
930  }
931 
932  blend_mode_ = blend_mode;
933 
934  if (blend_mode > Entity::kLastPipelineBlendMode) {
935  switch (blend_mode) {
936  BLEND_CASE(Screen)
937  BLEND_CASE(Overlay)
938  BLEND_CASE(Darken)
939  BLEND_CASE(Lighten)
940  BLEND_CASE(ColorDodge)
941  BLEND_CASE(ColorBurn)
942  BLEND_CASE(HardLight)
943  BLEND_CASE(SoftLight)
944  BLEND_CASE(Difference)
945  BLEND_CASE(Exclusion)
946  BLEND_CASE(Multiply)
947  BLEND_CASE(Hue)
951  default:
952  FML_UNREACHABLE();
953  }
954  }
955 }
956 
957 void BlendFilterContents::SetForegroundColor(std::optional<Color> color) {
958  foreground_color_ = color;
959 }
960 
961 std::optional<Entity> BlendFilterContents::RenderFilter(
962  const FilterInput::Vector& inputs,
963  const ContentContext& renderer,
964  const Entity& entity,
965  const Matrix& effect_transform,
966  const Rect& coverage,
967  const std::optional<Rect>& coverage_hint) const {
968  if (inputs.empty()) {
969  return std::nullopt;
970  }
971 
972  if (inputs.size() == 1 && !foreground_color_.has_value()) {
973  // Nothing to blend.
974  return PipelineBlend(inputs, renderer, entity, coverage, BlendMode::kSource,
975  std::nullopt, GetAbsorbOpacity(), GetAlpha());
976  }
977 
978  if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
979  if (inputs.size() == 1 && foreground_color_.has_value() &&
981  return CreateForegroundPorterDuffBlend(
982  inputs[0], renderer, entity, coverage, foreground_color_.value(),
983  blend_mode_, GetAlpha(), GetAbsorbOpacity());
984  }
985  return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
986  foreground_color_, GetAbsorbOpacity(), GetAlpha());
987  }
988 
989  if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
991  return CreateFramebufferAdvancedBlend(inputs, renderer, entity, coverage,
992  foreground_color_, blend_mode_,
994  }
995  if (inputs.size() == 1 && foreground_color_.has_value() &&
997  return CreateForegroundAdvancedBlend(
998  inputs[0], renderer, entity, coverage, foreground_color_.value(),
999  blend_mode_, GetAlpha(), GetAbsorbOpacity());
1000  }
1001  return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,
1002  foreground_color_, GetAbsorbOpacity(),
1003  GetAlpha());
1004  }
1005 
1006  FML_UNREACHABLE();
1007 }
1008 
1009 } // namespace impeller
impeller::ContentContext::GetTexturePipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > GetTexturePipeline(ContentContextOptions opts) const
Definition: content_context.h:442
impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::RenderPipelineHandle::FragmentShader
FragmentShader_ FragmentShader
Definition: pipeline.h:107
impeller::Entity::GetShaderTransform
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
Definition: entity.cc:50
impeller::OptionsFromPass
ContentContextOptions OptionsFromPass(const RenderPass &pass)
Definition: contents.cc:19
BLEND_CASE
#define BLEND_CASE(mode)
Definition: blend_filter_contents.cc:911
impeller::Entity::kLastPipelineBlendMode
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:22
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
impeller::BlendMode::kDestinationATop
@ kDestinationATop
contents.h
impeller::BlendFilterContents::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: blend_filter_contents.cc:926
impeller::Entity::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:115
impeller::BlendFilterContents::SetForegroundColor
void SetForegroundColor(std::optional< Color > color)
Sets a source color which is blended after all of the inputs have been blended.
Definition: blend_filter_contents.cc:957
entity.h
impeller::BlendMode
BlendMode
Definition: color.h:59
solid_color_contents.h
impeller::Color
Definition: color.h:124
impeller::BlendMode::kLuminosity
@ kLuminosity
impeller::BlendMode::kSource
@ kSource
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
impeller::BlendMode::kColorDodge
@ kColorDodge
impeller::BlendMode::kDestination
@ kDestination
texture_descriptor.h
impeller::BlendMode::kDarken
@ kDarken
formats.h
impeller::TRect::Intersection
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:519
impeller::InvertPorterDuffBlend
std::optional< BlendMode > InvertPorterDuffBlend(BlendMode blend_mode)
Definition: blend_filter_contents.cc:31
impeller::BlendMode::kColor
@ kColor
impeller::BlendMode::kDestinationOver
@ kDestinationOver
impeller::BlendMode::kPlus
@ kPlus
impeller::BlendMode::kOverlay
@ kOverlay
impeller::VertexBufferBuilder::AddVertices
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
Definition: vertex_buffer_builder.h:67
buffer_view
BufferView buffer_view
Definition: blit_command_gles.cc:127
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::TRect::GetOrigin
constexpr TPoint< Type > GetOrigin() const
Returns the upper left corner of the rectangle as specified by the left/top or x/y values when it was...
Definition: rect.h:310
impeller::BlendMode::kModulate
@ kModulate
impeller::AdvancedBlend
static std::optional< Entity > AdvancedBlend(const FilterInput::Vector &inputs, const ContentContext &renderer, const Entity &entity, const Rect &coverage, BlendMode blend_mode, std::optional< Color > foreground_color, ColorFilterContents::AbsorbOpacity absorb_opacity, PipelineProc pipeline_proc, std::optional< Scalar > alpha)
Definition: blend_filter_contents.cc:76
impeller::BlendMode::kSourceOut
@ kSourceOut
impeller::BlendMode::kSaturation
@ kSaturation
impeller::Entity::SetContents
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:90
impeller::BlendMode::kDifference
@ kDifference
impeller::VS
SolidFillVertexShader VS
Definition: stroke_path_geometry.cc:16
impeller::Entity
Definition: entity.h:20
impeller::BlendMode::kLighten
@ kLighten
impeller::PrimitiveType::kTriangleStrip
@ kTriangleStrip
impeller::BlendMode::kSoftLight
@ kSoftLight
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
render_pass.h
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::Entity::Render
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition: entity.cc:173
impeller::BlendMode::kHardLight
@ kHardLight
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:231
impeller::Entity::FromSnapshot
static Entity FromSnapshot(const Snapshot &snapshot, BlendMode blend_mode=BlendMode::kSourceOver)
Create an entity that can be used to render a given snapshot.
Definition: entity.cc:22
impeller::BlendMode::kClear
@ kClear
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:553
impeller::VertexBufferBuilder
Definition: vertex_buffer_builder.h:21
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::Entity::kLastAdvancedBlendMode
static constexpr BlendMode kLastAdvancedBlendMode
Definition: entity.h:23
impeller::ColorFilterContents::GetAbsorbOpacity
AbsorbOpacity GetAbsorbOpacity() const
Definition: color_filter_contents.cc:89
impeller::ContentContext::MakeSubpass
fml::StatusOr< RenderTarget > MakeSubpass(std::string_view label, ISize texture_size, const std::shared_ptr< CommandBuffer > &command_buffer, const SubpassCallback &subpass_callback, bool msaa_enabled=true, bool depth_stencil_enabled=false, int32_t mip_count=1) const
Creates a new texture of size texture_size and calls subpass_callback with a RenderPass for drawing t...
Definition: content_context.cc:475
impeller::Snapshot
Represents a texture and its intended draw transform/sampler configuration.
Definition: snapshot.h:24
impeller::RenderPipelineHandle::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:106
impeller::kPorterDuffCoefficients
constexpr std::array< std::array< Scalar, 5 >, 15 > kPorterDuffCoefficients
Definition: blend_filter_contents.h:15
impeller::Entity::GetContents
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:94
impeller::PipelineProc
std::shared_ptr< Pipeline< PipelineDescriptor > >(ContentContext::*)(ContentContextOptions) const PipelineProc
Definition: blend_filter_contents.cc:73
filter_input.h
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:769
impeller::Luminosity
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:140
strings.h
color_filter_contents.h
impeller::BlendFilterContents::~BlendFilterContents
~BlendFilterContents() override
anonymous_contents.h
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:119
impeller::VertexBufferBuilder::CreateVertexBuffer
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
Definition: vertex_buffer_builder.h:81
impeller::ContentContext::SubpassCallback
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
Definition: content_context.h:700
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
impeller::Geometry::MakeRect
static std::shared_ptr< Geometry > MakeRect(const Rect &rect)
Definition: geometry.cc:89
sampler_descriptor.h
impeller::BlendMode::kDestinationIn
@ kDestinationIn
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::BlendMode::kExclusion
@ kExclusion
content_context.h
impeller::BlendMode::kDestinationOut
@ kDestinationOut
impeller::Contents::RenderProc
std::function< bool(const ContentContext &renderer, const Entity &entity, RenderPass &pass)> RenderProc
Definition: contents.h:46
impeller::TRect::GetSize
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:317
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:557
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
snapshot.h
impeller::PipelineBlend
static std::optional< Entity > PipelineBlend(const FilterInput::Vector &inputs, const ContentContext &renderer, const Entity &entity, const Rect &coverage, BlendMode blend_mode, std::optional< Color > foreground_color, ColorFilterContents::AbsorbOpacity absorb_opacity, std::optional< Scalar > alpha)
Definition: blend_filter_contents.cc:506
impeller::BlendMode::kSourceIn
@ kSourceIn
impeller::BlendMode::kScreen
@ kScreen
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:497
color.h
color
DlColor color
Definition: dl_golden_blur_unittests.cc:23
impeller::BlendMode::kHue
@ kHue
blend_filter_contents.h
impeller::Contents::CoverageProc
std::function< std::optional< Rect >(const Entity &entity)> CoverageProc
Definition: contents.h:47
impeller::Snapshot::texture
std::shared_ptr< Texture > texture
Definition: snapshot.h:25
impeller::ColorFilterContents::AbsorbOpacity::kYes
@ kYes
impeller::FilterInput::Vector
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
impeller::BlendMode::kXor
@ kXor
impeller::ColorFilterContents::GetAlpha
std::optional< Scalar > GetAlpha() const
Definition: color_filter_contents.cc:98
impeller::Capabilities::SupportsDecalSamplerAddressMode
virtual bool SupportsDecalSamplerAddressMode() const =0
Whether the context backend supports SamplerAddressMode::Decal.
impeller::ContentContextOptions
Definition: content_context.h:254
impeller::Capabilities::SupportsFramebufferFetch
virtual bool SupportsFramebufferFetch() const =0
Whether the context backend is able to support pipelines with shaders that read from the framebuffer ...
impeller::ColorFilterContents::AbsorbOpacity
AbsorbOpacity
Definition: color_filter_contents.h:14
impeller
Definition: aiks_blend_unittests.cc:18
impeller::BlendMode::kSourceATop
@ kSourceATop
impeller::BlendMode::kMultiply
@ kMultiply
impeller::ContentContext
Definition: content_context.h:366
impeller::BlendFilterContents::BlendFilterContents
BlendFilterContents()
Definition: blend_filter_contents.cc:66
impeller::TRect< Scalar >
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::AnonymousContents::Make
static std::shared_ptr< Contents > Make(RenderProc render_proc, CoverageProc coverage_proc)
Definition: anonymous_contents.cc:11
impeller::ContentContext::GetTransientsBuffer
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
Definition: content_context.h:752
impeller::Saturation
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:166
impeller::SamplerAddressMode::kDecal
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...