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