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  frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
207  blend_info.dst_input_alpha =
209  ? dst_snapshot->opacity
210  : 1.0;
211 
212  if (foreground_color.has_value()) {
213  blend_info.color_factor = 1;
214  blend_info.color = foreground_color.value();
215  // This texture will not be sampled from due to the color factor. But
216  // this is present so that validation doesn't trip on a missing
217  // binding.
218  FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
219  } else {
220  raw_ptr<const Sampler> src_sampler =
221  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
222  src_snapshot->sampler_descriptor);
223  blend_info.color_factor = 0;
224  blend_info.src_input_alpha = src_snapshot->opacity;
225  FS::BindTextureSamplerSrc(pass, src_snapshot->texture, src_sampler);
226  frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale();
227  }
228  auto blend_uniform = data_host_buffer.EmplaceUniform(blend_info);
229  FS::BindBlendInfo(pass, blend_uniform);
230 
231  frame_info.mvp = pass.GetOrthographicTransform() *
233  subpass_coverage.GetOrigin());
234 
235  auto uniform_view = data_host_buffer.EmplaceUniform(frame_info);
236  VS::BindFrameInfo(pass, uniform_view);
237 
238  return pass.Draw().ok();
239  };
240 
241  std::shared_ptr<CommandBuffer> command_buffer =
242  renderer.GetContext()->CreateCommandBuffer();
243  if (!command_buffer) {
244  return std::nullopt;
245  }
246  fml::StatusOr<RenderTarget> render_target =
247  renderer.MakeSubpass("Advanced Blend Filter", //
248  ISize(subpass_coverage.GetSize()), //
249  command_buffer, //
250  callback, //
251  /*msaa_enabled=*/false, //
252  /*depth_stencil_enabled=*/false //
253  );
254  if (!render_target.ok()) {
255  return std::nullopt;
256  }
257  if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(command_buffer))) {
258  return std::nullopt;
259  }
260 
261  return Entity::FromSnapshot(
262  Snapshot{
263  .texture = render_target.value().GetRenderTargetTexture(),
264  .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
265  // Since we absorbed the transform of the inputs and used the
266  // respective snapshot sampling modes when blending, pass on
267  // the default NN clamp sampler.
268  .sampler_descriptor = {},
269  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
270  ? 1.0f
271  : dst_snapshot->opacity) *
272  alpha.value_or(1.0)},
273  entity.GetBlendMode());
274 }
275 
276 std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
277  const std::shared_ptr<FilterInput>& input,
278  const ContentContext& renderer,
279  const Entity& entity,
280  const Rect& coverage,
281  Color foreground_color,
282  BlendMode blend_mode,
283  std::optional<Scalar> alpha,
284  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
285  auto dst_snapshot =
286  input->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
287  if (!dst_snapshot.has_value()) {
288  return std::nullopt;
289  }
290 
291  RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode, alpha,
292  absorb_opacity](const ContentContext& renderer,
293  const Entity& entity,
294  RenderPass& pass) -> bool {
297 
298  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
299  auto size = dst_snapshot->texture->GetSize();
300 
301  std::array<VS::PerVertexData, 4> vertices = {
302  VS::PerVertexData{{0, 0}, {0, 0}, {0, 0}},
303  VS::PerVertexData{Point(size.width, 0), {1, 0}, {1, 0}},
304  VS::PerVertexData{Point(0, size.height), {0, 1}, {0, 1}},
305  VS::PerVertexData{Point(size.width, size.height), {1, 1}, {1, 1}},
306  };
307  auto vtx_buffer = CreateVertexBuffer(vertices, data_host_buffer);
308 
309 #ifdef IMPELLER_DEBUG
310  pass.SetCommandLabel(BlendModeToFilterString(blend_mode));
311 #endif // IMPELLER_DEBUG
312  pass.SetVertexBuffer(std::move(vtx_buffer));
313  auto options = OptionsFromPassAndEntity(pass, entity);
314  options.primitive_type = PrimitiveType::kTriangleStrip;
315 
316  switch (blend_mode) {
317  case BlendMode::kScreen:
318  pass.SetPipeline(renderer.GetBlendScreenPipeline(options));
319  break;
320  case BlendMode::kOverlay:
321  pass.SetPipeline(renderer.GetBlendOverlayPipeline(options));
322  break;
323  case BlendMode::kDarken:
324  pass.SetPipeline(renderer.GetBlendDarkenPipeline(options));
325  break;
326  case BlendMode::kLighten:
327  pass.SetPipeline(renderer.GetBlendLightenPipeline(options));
328  break;
330  pass.SetPipeline(renderer.GetBlendColorDodgePipeline(options));
331  break;
333  pass.SetPipeline(renderer.GetBlendColorBurnPipeline(options));
334  break;
336  pass.SetPipeline(renderer.GetBlendHardLightPipeline(options));
337  break;
339  pass.SetPipeline(renderer.GetBlendSoftLightPipeline(options));
340  break;
342  pass.SetPipeline(renderer.GetBlendDifferencePipeline(options));
343  break;
345  pass.SetPipeline(renderer.GetBlendExclusionPipeline(options));
346  break;
348  pass.SetPipeline(renderer.GetBlendMultiplyPipeline(options));
349  break;
350  case BlendMode::kHue:
351  pass.SetPipeline(renderer.GetBlendHuePipeline(options));
352  break;
354  pass.SetPipeline(renderer.GetBlendSaturationPipeline(options));
355  break;
356  case BlendMode::kColor:
357  pass.SetPipeline(renderer.GetBlendColorPipeline(options));
358  break;
360  pass.SetPipeline(renderer.GetBlendLuminosityPipeline(options));
361  break;
362  default:
363  return false;
364  }
365 
366  FS::BlendInfo blend_info;
367  VS::FrameInfo frame_info;
368 
369  raw_ptr<const Sampler> dst_sampler =
370  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
371  dst_snapshot->sampler_descriptor);
372  FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
373  frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
374 
375  frame_info.mvp = Entity::GetShaderTransform(
376  entity.GetShaderClipDepth(), pass,
377  entity.GetTransform() * dst_snapshot->transform);
378 
379  blend_info.dst_input_alpha =
381  ? dst_snapshot->opacity * alpha.value_or(1.0)
382  : 1.0;
383 
384  blend_info.color_factor = 1;
385  blend_info.color = foreground_color;
386  // This texture will not be sampled from due to the color factor. But
387  // this is present so that validation doesn't trip on a missing
388  // binding.
389  FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
390 
391  auto blend_uniform = data_host_buffer.EmplaceUniform(blend_info);
392  FS::BindBlendInfo(pass, blend_uniform);
393 
394  auto uniform_view = data_host_buffer.EmplaceUniform(frame_info);
395  VS::BindFrameInfo(pass, uniform_view);
396 
397  return pass.Draw().ok();
398  };
399  CoverageProc coverage_proc =
400  [coverage](const Entity& entity) -> std::optional<Rect> {
401  return coverage.TransformBounds(entity.GetTransform());
402  };
403 
404  auto contents = AnonymousContents::Make(render_proc, coverage_proc);
405 
406  Entity sub_entity;
407  sub_entity.SetContents(std::move(contents));
408  sub_entity.SetBlendMode(entity.GetBlendMode());
409 
410  return sub_entity;
411 }
412 
413 std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
414  const std::shared_ptr<FilterInput>& input,
415  const ContentContext& renderer,
416  const Entity& entity,
417  const Rect& coverage,
418  Color foreground_color,
419  BlendMode blend_mode,
420  std::optional<Scalar> alpha,
421  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
422  if (blend_mode == BlendMode::kClear) {
423  return std::nullopt;
424  }
425 
426  auto dst_snapshot =
427  input->GetSnapshot("ForegroundPorterDuffBlend", renderer, entity);
428  if (!dst_snapshot.has_value()) {
429  return std::nullopt;
430  }
431 
432  if (blend_mode == BlendMode::kDst) {
433  return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
434  }
435 
436  RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode,
437  absorb_opacity, alpha](
438  const ContentContext& renderer,
439  const Entity& entity, RenderPass& pass) -> bool {
442 
443  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
444  auto size = dst_snapshot->texture->GetSize();
445  auto color = foreground_color.Premultiply();
446 
447  std::array<VS::PerVertexData, 4> vertices = {
448  VS::PerVertexData{{0, 0}, {0, 0}, color},
449  VS::PerVertexData{Point(size.width, 0), {1, 0}, color},
450  VS::PerVertexData{Point(0, size.height), {0, 1}, color},
451  VS::PerVertexData{Point(size.width, size.height), {1, 1}, color},
452  };
453  auto vtx_buffer =
454  CreateVertexBuffer(vertices, renderer.GetTransientsDataBuffer());
455 
456 #ifdef IMPELLER_DEBUG
457  pass.SetCommandLabel(BlendModeToFilterString(blend_mode));
458 #endif // IMPELLER_DEBUG
459  pass.SetVertexBuffer(std::move(vtx_buffer));
460  auto options = OptionsFromPassAndEntity(pass, entity);
461  options.primitive_type = PrimitiveType::kTriangleStrip;
462  pass.SetPipeline(renderer.GetPorterDuffPipeline(blend_mode, options));
463 
464  FS::FragInfo frag_info;
465  VS::FrameInfo frame_info;
466 
467  frame_info.mvp = Entity::GetShaderTransform(
468  entity.GetShaderClipDepth(), pass,
469  entity.GetTransform() * dst_snapshot->transform);
470 
471  raw_ptr<const Sampler> dst_sampler =
472  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
473  dst_snapshot->sampler_descriptor);
474  FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
475  frame_info.texture_sampler_y_coord_scale =
476  dst_snapshot->texture->GetYCoordScale();
477 
478  frag_info.input_alpha_output_alpha_tmx_tmy =
479  Vector4(absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
480  ? dst_snapshot->opacity * alpha.value_or(1.0)
481  : 1.0,
482  1, 0, 0);
483  frag_info.use_strict_source_rect = 0.0;
484 
485  FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
486  VS::BindFrameInfo(pass, data_host_buffer.EmplaceUniform(frame_info));
487 
488  return pass.Draw().ok();
489  };
490 
491  CoverageProc coverage_proc =
492  [coverage](const Entity& entity) -> std::optional<Rect> {
493  return coverage.TransformBounds(entity.GetTransform());
494  };
495 
496  auto contents = AnonymousContents::Make(render_proc, coverage_proc);
497 
498  Entity sub_entity;
499  sub_entity.SetContents(std::move(contents));
500  sub_entity.SetBlendMode(entity.GetBlendMode());
501 
502  return sub_entity;
503 }
504 
505 static std::optional<Entity> PipelineBlend(
506  const FilterInput::Vector& inputs,
507  const ContentContext& renderer,
508  const Entity& entity,
509  const Rect& coverage,
510  BlendMode blend_mode,
511  std::optional<Color> foreground_color,
512  ColorFilterContents::AbsorbOpacity absorb_opacity,
513  std::optional<Scalar> alpha) {
516 
517  auto dst_snapshot =
518  inputs[0]->GetSnapshot("PipelineBlend(Dst)", renderer, entity);
519  if (!dst_snapshot.has_value()) {
520  return std::nullopt; // Nothing to render.
521  }
522 
523  Rect subpass_coverage = coverage;
524  if (entity.GetContents()) {
525  auto coverage_hint = entity.GetContents()->GetCoverageHint();
526 
527  if (coverage_hint.has_value()) {
528  auto maybe_subpass_coverage =
529  subpass_coverage.Intersection(*coverage_hint);
530  if (!maybe_subpass_coverage.has_value()) {
531  return std::nullopt; // Nothing to render.
532  }
533 
534  subpass_coverage = *maybe_subpass_coverage;
535  }
536  }
537 
538  ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
539  RenderPass& pass) {
540  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
541 
542 #ifdef IMPELLER_DEBUG
543  pass.SetCommandLabel(BlendModeToFilterString(blend_mode));
544 #endif // IMPELLER_DEBUG
545  auto options = OptionsFromPass(pass);
546  options.primitive_type = PrimitiveType::kTriangleStrip;
547 
548  auto add_blend_command = [&](std::optional<Snapshot> input) {
549  if (!input.has_value()) {
550  return false;
551  }
552  auto input_coverage = input->GetCoverage();
553  if (!input_coverage.has_value()) {
554  return false;
555  }
556 
557  raw_ptr<const Sampler> sampler =
558  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
559  input->sampler_descriptor);
560  FS::BindTextureSampler(pass, input->texture, sampler);
561 
562  auto size = input->texture->GetSize();
563  std::array<VS::PerVertexData, 4> vertices = {
564  VS::PerVertexData{Point(0, 0), Point(0, 0)},
565  VS::PerVertexData{Point(size.width, 0), Point(1, 0)},
566  VS::PerVertexData{Point(0, size.height), Point(0, 1)},
567  VS::PerVertexData{Point(size.width, size.height), Point(1, 1)},
568  };
569  pass.SetVertexBuffer(
570  CreateVertexBuffer(vertices, renderer.GetTransientsDataBuffer()));
571 
572  VS::FrameInfo frame_info;
573  frame_info.mvp = pass.GetOrthographicTransform() *
574  Matrix::MakeTranslation(-subpass_coverage.GetOrigin()) *
575  input->transform;
576  frame_info.texture_sampler_y_coord_scale =
577  input->texture->GetYCoordScale();
578 
579  FS::FragInfo frag_info;
580  frag_info.alpha =
582  ? input->opacity
583  : 1.0;
584  FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
585  VS::BindFrameInfo(pass, data_host_buffer.EmplaceUniform(frame_info));
586 
587  return pass.Draw().ok();
588  };
589 
590  // Draw the first texture using kSource.
591  options.blend_mode = BlendMode::kSrc;
592  pass.SetPipeline(renderer.GetTexturePipeline(options));
593  if (!add_blend_command(dst_snapshot)) {
594  return true;
595  }
596 
597  // Write subsequent textures using the selected blend mode.
598 
599  if (inputs.size() >= 2) {
600  options.blend_mode = blend_mode;
601  pass.SetPipeline(renderer.GetTexturePipeline(options));
602 
603  for (auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
604  texture_i++) {
605  auto src_input = texture_i->get()->GetSnapshot("PipelineBlend(Src)",
606  renderer, entity);
607  if (!add_blend_command(src_input)) {
608  return true;
609  }
610  }
611  }
612 
613  // If a foreground color is set, blend it in.
614 
615  if (foreground_color.has_value()) {
616  auto contents = std::make_shared<SolidColorContents>();
617  FillRectGeometry geom(Rect::MakeSize(pass.GetRenderTargetSize()));
618  contents->SetGeometry(&geom);
619  contents->SetColor(foreground_color.value());
620 
621  Entity foreground_entity;
622  foreground_entity.SetBlendMode(blend_mode);
623  foreground_entity.SetContents(contents);
624  if (!foreground_entity.Render(renderer, pass)) {
625  return false;
626  }
627  }
628 
629  return true;
630  };
631 
632  std::shared_ptr<CommandBuffer> command_buffer =
633  renderer.GetContext()->CreateCommandBuffer();
634  if (!command_buffer) {
635  return std::nullopt;
636  }
637 
638  fml::StatusOr<RenderTarget> render_target =
639  renderer.MakeSubpass("Pipeline Blend Filter", //
640  ISize(subpass_coverage.GetSize()), //
641  command_buffer, //
642  callback, //
643  /*msaa_enabled=*/false, //
644  /*depth_stencil_enabled=*/false //
645  );
646 
647  if (!render_target.ok()) {
648  return std::nullopt;
649  }
650  if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(command_buffer))) {
651  return std::nullopt;
652  }
653 
654  return Entity::FromSnapshot(
655  Snapshot{
656  .texture = render_target.value().GetRenderTargetTexture(),
657  .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
658  // Since we absorbed the transform of the inputs and used the
659  // respective snapshot sampling modes when blending, pass on
660  // the default NN clamp sampler.
661  .sampler_descriptor = {},
662  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
663  ? 1.0f
664  : dst_snapshot->opacity) *
665  alpha.value_or(1.0)},
666  entity.GetBlendMode());
667 }
668 
669 std::optional<Entity> BlendFilterContents::CreateFramebufferAdvancedBlend(
670  const FilterInput::Vector& inputs,
671  const ContentContext& renderer,
672  const Entity& entity,
673  const Rect& coverage,
674  std::optional<Color> foreground_color,
675  BlendMode blend_mode,
676  std::optional<Scalar> alpha,
677  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
678  // This works with either 2 contents or 1 contents and a foreground color.
679  FML_DCHECK(inputs.size() == 2u ||
680  (inputs.size() == 1u && foreground_color.has_value()));
681 
682  auto dst_snapshot =
683  inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
684  if (!dst_snapshot.has_value()) {
685  return std::nullopt;
686  }
687 
688  std::shared_ptr<Texture> foreground_texture;
689 
690  ContentContext::SubpassCallback subpass_callback = [&](const ContentContext&
691  renderer,
692  RenderPass& pass) {
693  // First, we create a new render pass and populate it with the contents
694  // of the first (dst) input.
695  HostBuffer& data_host_buffer = renderer.GetTransientsDataBuffer();
696 
697  {
698  using FS = TextureFillFragmentShader;
699  using VS = TextureFillVertexShader;
700 
701  pass.SetCommandLabel("Framebuffer Advanced Blend");
702  auto pipeline_options = OptionsFromPass(pass);
703  pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
704  pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
705 
706  VS::FrameInfo frame_info;
707  frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
708  frame_info.texture_sampler_y_coord_scale =
709  dst_snapshot->texture->GetYCoordScale();
710 
711  FS::FragInfo frag_info;
712  frag_info.alpha = 1.0;
713 
714  std::array<VS::PerVertexData, 4> vertices = {
715  VS::PerVertexData{{0, 0}, {0, 0}},
716  VS::PerVertexData{Point(1, 0), {1, 0}},
717  VS::PerVertexData{Point(0, 1), {0, 1}},
718  VS::PerVertexData{Point(1, 1), {1, 1}},
719  };
720  pass.SetVertexBuffer(
721  CreateVertexBuffer(vertices, renderer.GetTransientsDataBuffer()));
722 
723  VS::BindFrameInfo(pass, data_host_buffer.EmplaceUniform(frame_info));
724  FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
725  FS::BindTextureSampler(
726  pass, dst_snapshot->texture,
727  renderer.GetContext()->GetSamplerLibrary()->GetSampler({}));
728 
729  if (!pass.Draw().ok()) {
730  return false;
731  }
732  }
733 
734  {
737 
738  // Next, we render the second contents to a snapshot, or create a 1x1
739  // texture for the foreground color.
740  std::shared_ptr<Texture> src_texture;
741  SamplerDescriptor src_sampler_descriptor = SamplerDescriptor{};
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  src_sampler_descriptor = src_snapshot->sampler_descriptor;
755  }
756 
757  std::array<VS::PerVertexData, 4> vertices = {
758  VS::PerVertexData{Point(0, 0), Point(0, 0)},
759  VS::PerVertexData{Point(1, 0), Point(1, 0)},
760  VS::PerVertexData{Point(0, 1), Point(0, 1)},
761  VS::PerVertexData{Point(1, 1), Point(1, 1)},
762  };
763 
764  auto options = OptionsFromPass(pass);
765  options.blend_mode = BlendMode::kSrc;
766  options.primitive_type = PrimitiveType::kTriangleStrip;
767 
768  pass.SetCommandLabel("Framebuffer Advanced Blend Filter");
769  pass.SetVertexBuffer(
770  CreateVertexBuffer(vertices, renderer.GetTransientsDataBuffer()));
771 
772  switch (blend_mode) {
773  case BlendMode::kScreen:
774  pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options));
775  break;
776  case BlendMode::kOverlay:
777  pass.SetPipeline(
778  renderer.GetFramebufferBlendOverlayPipeline(options));
779  break;
780  case BlendMode::kDarken:
781  pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options));
782  break;
783  case BlendMode::kLighten:
784  pass.SetPipeline(
785  renderer.GetFramebufferBlendLightenPipeline(options));
786  break;
788  pass.SetPipeline(
789  renderer.GetFramebufferBlendColorDodgePipeline(options));
790  break;
792  pass.SetPipeline(
793  renderer.GetFramebufferBlendColorBurnPipeline(options));
794  break;
796  pass.SetPipeline(
797  renderer.GetFramebufferBlendHardLightPipeline(options));
798  break;
800  pass.SetPipeline(
801  renderer.GetFramebufferBlendSoftLightPipeline(options));
802  break;
804  pass.SetPipeline(
805  renderer.GetFramebufferBlendDifferencePipeline(options));
806  break;
808  pass.SetPipeline(
809  renderer.GetFramebufferBlendExclusionPipeline(options));
810  break;
812  pass.SetPipeline(
813  renderer.GetFramebufferBlendMultiplyPipeline(options));
814  break;
815  case BlendMode::kHue:
816  pass.SetPipeline(renderer.GetFramebufferBlendHuePipeline(options));
817  break;
819  pass.SetPipeline(
820  renderer.GetFramebufferBlendSaturationPipeline(options));
821  break;
822  case BlendMode::kColor:
823  pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options));
824  break;
826  pass.SetPipeline(
827  renderer.GetFramebufferBlendLuminosityPipeline(options));
828  break;
829  default:
830  return false;
831  }
832 
833  VS::FrameInfo frame_info;
834  FS::FragInfo frag_info;
835 
836  raw_ptr<const Sampler> src_sampler =
837  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
838  src_sampler_descriptor);
839  FS::BindTextureSamplerSrc(pass, src_texture, src_sampler);
840 
841  frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
842  frame_info.src_y_coord_scale = src_texture->GetYCoordScale();
843  VS::BindFrameInfo(pass, data_host_buffer.EmplaceUniform(frame_info));
844 
845  frag_info.src_input_alpha = 1.0;
846  frag_info.dst_input_alpha =
848  ? dst_snapshot->opacity
849  : 1.0;
850  FS::BindFragInfo(pass, data_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  std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
871  auto buffer_view = renderer.GetTransientsDataBuffer().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  return std::nullopt;
877  }
878  }
879 
880  fml::StatusOr<RenderTarget> render_target =
881  renderer.MakeSubpass("FramebufferBlend", //
882  dst_snapshot->texture->GetSize(), //
883  cmd_buffer, //
884  subpass_callback, //
885  /*msaa_enabled=*/true, //
886  /*depth_stencil_enabled=*/true //
887  );
888 
889  if (!render_target.ok()) {
890  return std::nullopt;
891  }
892  if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(cmd_buffer))) {
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::kSrc,
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
#define BLEND_CASE(mode)
BufferView buffer_view
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:76
Matrix GetShaderTransform(const RenderPass &pass) const
Definition: entity.cc:48
BlendMode GetBlendMode() const
Definition: entity.cc:101
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:72
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:97
static constexpr BlendMode kLastAdvancedBlendMode
Definition: entity.h:29
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition: entity.cc:144
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:164
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:89
TRect< Scalar > Rect
Definition: rect.h:788
TPoint< Scalar > Point
Definition: point.h:327
raw_ptr< Pipeline< PipelineDescriptor > > PipelineRef
A raw ptr to a pipeline object.
Definition: pipeline.h:88
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:63
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:633
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:320
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:528
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:327
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
#define VALIDATION_LOG
Definition: validation.h:91