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::kSource:
58  return BlendMode::kSource;
66  return BlendMode::kSourceIn;
70  return BlendMode::kSourceOut;
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& host_buffer = renderer.GetTransientsBuffer();
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.GetTransientsBuffer());
187 
188  auto options = OptionsFromPass(pass);
189  options.primitive_type = PrimitiveType::kTriangleStrip;
190  options.blend_mode = BlendMode::kSource;
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  auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
204  dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
205  dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
206  }
207  raw_ptr<const Sampler> dst_sampler =
208  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
209  dst_sampler_descriptor);
210  FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
211  frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
212  blend_info.dst_input_alpha =
214  ? dst_snapshot->opacity
215  : 1.0;
216 
217  if (foreground_color.has_value()) {
218  blend_info.color_factor = 1;
219  blend_info.color = foreground_color.value();
220  // This texture will not be sampled from due to the color factor. But
221  // this is present so that validation doesn't trip on a missing
222  // binding.
223  FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
224  } else {
225  auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
227  src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
228  src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
229  }
230  raw_ptr<const Sampler> src_sampler =
231  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
232  src_sampler_descriptor);
233  blend_info.color_factor = 0;
234  blend_info.src_input_alpha = src_snapshot->opacity;
235  FS::BindTextureSamplerSrc(pass, src_snapshot->texture, src_sampler);
236  frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale();
237  }
238  auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
239  FS::BindBlendInfo(pass, blend_uniform);
240 
241  frame_info.mvp = pass.GetOrthographicTransform() *
243  subpass_coverage.GetOrigin());
244 
245  auto uniform_view = host_buffer.EmplaceUniform(frame_info);
246  VS::BindFrameInfo(pass, uniform_view);
247 
248  return pass.Draw().ok();
249  };
250 
251  std::shared_ptr<CommandBuffer> command_buffer =
252  renderer.GetContext()->CreateCommandBuffer();
253  if (!command_buffer) {
254  return std::nullopt;
255  }
256  fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
257  "Advanced Blend Filter", ISize(subpass_coverage.GetSize()),
258  command_buffer, callback);
259  if (!render_target.ok()) {
260  return std::nullopt;
261  }
262  if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(command_buffer))) {
263  return std::nullopt;
264  }
265 
266  return Entity::FromSnapshot(
267  Snapshot{
268  .texture = render_target.value().GetRenderTargetTexture(),
269  .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
270  // Since we absorbed the transform of the inputs and used the
271  // respective snapshot sampling modes when blending, pass on
272  // the default NN clamp sampler.
273  .sampler_descriptor = {},
274  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
275  ? 1.0f
276  : dst_snapshot->opacity) *
277  alpha.value_or(1.0)},
278  entity.GetBlendMode());
279 }
280 
281 std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
282  const std::shared_ptr<FilterInput>& input,
283  const ContentContext& renderer,
284  const Entity& entity,
285  const Rect& coverage,
286  Color foreground_color,
287  BlendMode blend_mode,
288  std::optional<Scalar> alpha,
289  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
290  auto dst_snapshot =
291  input->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
292  if (!dst_snapshot.has_value()) {
293  return std::nullopt;
294  }
295 
296  RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode, alpha,
297  absorb_opacity](const ContentContext& renderer,
298  const Entity& entity,
299  RenderPass& pass) -> bool {
302 
303  auto& host_buffer = renderer.GetTransientsBuffer();
304  auto size = dst_snapshot->texture->GetSize();
305 
306  std::array<VS::PerVertexData, 4> vertices = {
307  VS::PerVertexData{{0, 0}, {0, 0}, {0, 0}},
308  VS::PerVertexData{Point(size.width, 0), {1, 0}, {1, 0}},
309  VS::PerVertexData{Point(0, size.height), {0, 1}, {0, 1}},
310  VS::PerVertexData{Point(size.width, size.height), {1, 1}, {1, 1}},
311  };
312  auto vtx_buffer =
313  CreateVertexBuffer(vertices, renderer.GetTransientsBuffer());
314 
315 #ifdef IMPELLER_DEBUG
316  pass.SetCommandLabel(BlendModeToFilterString(blend_mode));
317 #endif // IMPELLER_DEBUG
318  pass.SetVertexBuffer(std::move(vtx_buffer));
319  auto options = OptionsFromPassAndEntity(pass, entity);
320  options.primitive_type = PrimitiveType::kTriangleStrip;
321 
322  switch (blend_mode) {
323  case BlendMode::kScreen:
324  pass.SetPipeline(renderer.GetBlendScreenPipeline(options));
325  break;
326  case BlendMode::kOverlay:
327  pass.SetPipeline(renderer.GetBlendOverlayPipeline(options));
328  break;
329  case BlendMode::kDarken:
330  pass.SetPipeline(renderer.GetBlendDarkenPipeline(options));
331  break;
332  case BlendMode::kLighten:
333  pass.SetPipeline(renderer.GetBlendLightenPipeline(options));
334  break;
336  pass.SetPipeline(renderer.GetBlendColorDodgePipeline(options));
337  break;
339  pass.SetPipeline(renderer.GetBlendColorBurnPipeline(options));
340  break;
342  pass.SetPipeline(renderer.GetBlendHardLightPipeline(options));
343  break;
345  pass.SetPipeline(renderer.GetBlendSoftLightPipeline(options));
346  break;
348  pass.SetPipeline(renderer.GetBlendDifferencePipeline(options));
349  break;
351  pass.SetPipeline(renderer.GetBlendExclusionPipeline(options));
352  break;
354  pass.SetPipeline(renderer.GetBlendMultiplyPipeline(options));
355  break;
356  case BlendMode::kHue:
357  pass.SetPipeline(renderer.GetBlendHuePipeline(options));
358  break;
360  pass.SetPipeline(renderer.GetBlendSaturationPipeline(options));
361  break;
362  case BlendMode::kColor:
363  pass.SetPipeline(renderer.GetBlendColorPipeline(options));
364  break;
366  pass.SetPipeline(renderer.GetBlendLuminosityPipeline(options));
367  break;
368  default:
369  return false;
370  }
371 
372  FS::BlendInfo blend_info;
373  VS::FrameInfo frame_info;
374 
375  auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
376  if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
377  dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
378  dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
379  }
380  raw_ptr<const Sampler> dst_sampler =
381  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
382  dst_sampler_descriptor);
383  FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
384  frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
385 
386  frame_info.mvp = Entity::GetShaderTransform(
387  entity.GetShaderClipDepth(), pass,
388  entity.GetTransform() * dst_snapshot->transform);
389 
390  blend_info.dst_input_alpha =
392  ? dst_snapshot->opacity * alpha.value_or(1.0)
393  : 1.0;
394 
395  blend_info.color_factor = 1;
396  blend_info.color = foreground_color;
397  // This texture will not be sampled from due to the color factor. But
398  // this is present so that validation doesn't trip on a missing
399  // binding.
400  FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
401 
402  auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
403  FS::BindBlendInfo(pass, blend_uniform);
404 
405  auto uniform_view = host_buffer.EmplaceUniform(frame_info);
406  VS::BindFrameInfo(pass, uniform_view);
407 
408  return pass.Draw().ok();
409  };
410  CoverageProc coverage_proc =
411  [coverage](const Entity& entity) -> std::optional<Rect> {
412  return coverage.TransformBounds(entity.GetTransform());
413  };
414 
415  auto contents = AnonymousContents::Make(render_proc, coverage_proc);
416 
417  Entity sub_entity;
418  sub_entity.SetContents(std::move(contents));
419  sub_entity.SetBlendMode(entity.GetBlendMode());
420 
421  return sub_entity;
422 }
423 
424 std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
425  const std::shared_ptr<FilterInput>& input,
426  const ContentContext& renderer,
427  const Entity& entity,
428  const Rect& coverage,
429  Color foreground_color,
430  BlendMode blend_mode,
431  std::optional<Scalar> alpha,
432  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
433  if (blend_mode == BlendMode::kClear) {
434  return std::nullopt;
435  }
436 
437  auto dst_snapshot =
438  input->GetSnapshot("ForegroundPorterDuffBlend", renderer, entity);
439  if (!dst_snapshot.has_value()) {
440  return std::nullopt;
441  }
442 
443  if (blend_mode == BlendMode::kDestination) {
444  return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
445  }
446 
447  RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode,
448  absorb_opacity, alpha](
449  const ContentContext& renderer,
450  const Entity& entity, RenderPass& pass) -> bool {
453 
454  auto& host_buffer = renderer.GetTransientsBuffer();
455  auto size = dst_snapshot->texture->GetSize();
456  auto color = foreground_color.Premultiply();
457 
458  std::array<VS::PerVertexData, 4> vertices = {
459  VS::PerVertexData{{0, 0}, {0, 0}, color},
460  VS::PerVertexData{Point(size.width, 0), {1, 0}, color},
461  VS::PerVertexData{Point(0, size.height), {0, 1}, color},
462  VS::PerVertexData{Point(size.width, size.height), {1, 1}, color},
463  };
464  auto vtx_buffer =
465  CreateVertexBuffer(vertices, renderer.GetTransientsBuffer());
466 
467 #ifdef IMPELLER_DEBUG
468  pass.SetCommandLabel(BlendModeToFilterString(blend_mode));
469 #endif // IMPELLER_DEBUG
470  pass.SetVertexBuffer(std::move(vtx_buffer));
471  auto options = OptionsFromPassAndEntity(pass, entity);
472  options.primitive_type = PrimitiveType::kTriangleStrip;
473  pass.SetPipeline(renderer.GetPorterDuffPipeline(blend_mode, options));
474 
475  FS::FragInfo frag_info;
476  VS::FrameInfo frame_info;
477 
478  frame_info.mvp = Entity::GetShaderTransform(
479  entity.GetShaderClipDepth(), pass,
480  entity.GetTransform() * dst_snapshot->transform);
481 
482  auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
483  if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
484  dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
485  dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
486  }
487  raw_ptr<const Sampler> dst_sampler =
488  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
489  dst_sampler_descriptor);
490  FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
491  frame_info.texture_sampler_y_coord_scale =
492  dst_snapshot->texture->GetYCoordScale();
493 
494  frag_info.input_alpha =
496  ? dst_snapshot->opacity * alpha.value_or(1.0)
497  : 1.0;
498  frag_info.output_alpha = 1.0;
499 
500  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
501  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
502 
503  return pass.Draw().ok();
504  };
505 
506  CoverageProc coverage_proc =
507  [coverage](const Entity& entity) -> std::optional<Rect> {
508  return coverage.TransformBounds(entity.GetTransform());
509  };
510 
511  auto contents = AnonymousContents::Make(render_proc, coverage_proc);
512 
513  Entity sub_entity;
514  sub_entity.SetContents(std::move(contents));
515  sub_entity.SetBlendMode(entity.GetBlendMode());
516 
517  return sub_entity;
518 }
519 
520 static std::optional<Entity> PipelineBlend(
521  const FilterInput::Vector& inputs,
522  const ContentContext& renderer,
523  const Entity& entity,
524  const Rect& coverage,
525  BlendMode blend_mode,
526  std::optional<Color> foreground_color,
527  ColorFilterContents::AbsorbOpacity absorb_opacity,
528  std::optional<Scalar> alpha) {
531 
532  auto dst_snapshot =
533  inputs[0]->GetSnapshot("PipelineBlend(Dst)", renderer, entity);
534  if (!dst_snapshot.has_value()) {
535  return std::nullopt; // Nothing to render.
536  }
537 
538  Rect subpass_coverage = coverage;
539  if (entity.GetContents()) {
540  auto coverage_hint = entity.GetContents()->GetCoverageHint();
541 
542  if (coverage_hint.has_value()) {
543  auto maybe_subpass_coverage =
544  subpass_coverage.Intersection(*coverage_hint);
545  if (!maybe_subpass_coverage.has_value()) {
546  return std::nullopt; // Nothing to render.
547  }
548 
549  subpass_coverage = *maybe_subpass_coverage;
550  }
551  }
552 
553  ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
554  RenderPass& pass) {
555  auto& host_buffer = renderer.GetTransientsBuffer();
556 
557 #ifdef IMPELLER_DEBUG
558  pass.SetCommandLabel(BlendModeToFilterString(blend_mode));
559 #endif // IMPELLER_DEBUG
560  auto options = OptionsFromPass(pass);
561  options.primitive_type = PrimitiveType::kTriangleStrip;
562 
563  auto add_blend_command = [&](std::optional<Snapshot> input) {
564  if (!input.has_value()) {
565  return false;
566  }
567  auto input_coverage = input->GetCoverage();
568  if (!input_coverage.has_value()) {
569  return false;
570  }
571 
572  raw_ptr<const Sampler> sampler =
573  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
574  input->sampler_descriptor);
575  FS::BindTextureSampler(pass, input->texture, sampler);
576 
577  auto size = input->texture->GetSize();
578  std::array<VS::PerVertexData, 4> vertices = {
579  VS::PerVertexData{Point(0, 0), Point(0, 0)},
580  VS::PerVertexData{Point(size.width, 0), Point(1, 0)},
581  VS::PerVertexData{Point(0, size.height), Point(0, 1)},
582  VS::PerVertexData{Point(size.width, size.height), Point(1, 1)},
583  };
584  pass.SetVertexBuffer(
585  CreateVertexBuffer(vertices, renderer.GetTransientsBuffer()));
586 
587  VS::FrameInfo frame_info;
588  frame_info.mvp = pass.GetOrthographicTransform() *
589  Matrix::MakeTranslation(-subpass_coverage.GetOrigin()) *
590  input->transform;
591  frame_info.texture_sampler_y_coord_scale =
592  input->texture->GetYCoordScale();
593 
594  FS::FragInfo frag_info;
595  frag_info.alpha =
597  ? input->opacity
598  : 1.0;
599  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
600  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
601 
602  return pass.Draw().ok();
603  };
604 
605  // Draw the first texture using kSource.
606  options.blend_mode = BlendMode::kSource;
607  pass.SetPipeline(renderer.GetTexturePipeline(options));
608  if (!add_blend_command(dst_snapshot)) {
609  return true;
610  }
611 
612  // Write subsequent textures using the selected blend mode.
613 
614  if (inputs.size() >= 2) {
615  options.blend_mode = blend_mode;
616  pass.SetPipeline(renderer.GetTexturePipeline(options));
617 
618  for (auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
619  texture_i++) {
620  auto src_input = texture_i->get()->GetSnapshot("PipelineBlend(Src)",
621  renderer, entity);
622  if (!add_blend_command(src_input)) {
623  return true;
624  }
625  }
626  }
627 
628  // If a foreground color is set, blend it in.
629 
630  if (foreground_color.has_value()) {
631  auto contents = std::make_shared<SolidColorContents>();
632  RectGeometry geom(Rect::MakeSize(pass.GetRenderTargetSize()));
633  contents->SetGeometry(&geom);
634  contents->SetColor(foreground_color.value());
635 
636  Entity foreground_entity;
637  foreground_entity.SetBlendMode(blend_mode);
638  foreground_entity.SetContents(contents);
639  if (!foreground_entity.Render(renderer, pass)) {
640  return false;
641  }
642  }
643 
644  return true;
645  };
646 
647  std::shared_ptr<CommandBuffer> command_buffer =
648  renderer.GetContext()->CreateCommandBuffer();
649  if (!command_buffer) {
650  return std::nullopt;
651  }
652 
653  fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
654  "Pipeline Blend Filter", ISize(subpass_coverage.GetSize()),
655  command_buffer, callback);
656 
657  if (!render_target.ok()) {
658  return std::nullopt;
659  }
660  if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(command_buffer))) {
661  return std::nullopt;
662  }
663 
664  return Entity::FromSnapshot(
665  Snapshot{
666  .texture = render_target.value().GetRenderTargetTexture(),
667  .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
668  // Since we absorbed the transform of the inputs and used the
669  // respective snapshot sampling modes when blending, pass on
670  // the default NN clamp sampler.
671  .sampler_descriptor = {},
672  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
673  ? 1.0f
674  : dst_snapshot->opacity) *
675  alpha.value_or(1.0)},
676  entity.GetBlendMode());
677 }
678 
679 std::optional<Entity> BlendFilterContents::CreateFramebufferAdvancedBlend(
680  const FilterInput::Vector& inputs,
681  const ContentContext& renderer,
682  const Entity& entity,
683  const Rect& coverage,
684  std::optional<Color> foreground_color,
685  BlendMode blend_mode,
686  std::optional<Scalar> alpha,
687  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
688  // This works with either 2 contents or 1 contents and a foreground color.
689  FML_DCHECK(inputs.size() == 2u ||
690  (inputs.size() == 1u && foreground_color.has_value()));
691 
692  auto dst_snapshot =
693  inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
694  if (!dst_snapshot.has_value()) {
695  return std::nullopt;
696  }
697 
698  std::shared_ptr<Texture> foreground_texture;
699 
700  ContentContext::SubpassCallback subpass_callback = [&](const ContentContext&
701  renderer,
702  RenderPass& pass) {
703  // First, we create a new render pass and populate it with the contents
704  // of the first (dst) input.
705  HostBuffer& host_buffer = renderer.GetTransientsBuffer();
706 
707  {
708  using FS = TextureFillFragmentShader;
709  using VS = TextureFillVertexShader;
710 
711  pass.SetCommandLabel("Framebuffer Advanced Blend");
712  auto pipeline_options = OptionsFromPass(pass);
713  pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
714  pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
715 
716  VS::FrameInfo frame_info;
717  frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
718  frame_info.texture_sampler_y_coord_scale = 1.0;
719 
720  FS::FragInfo frag_info;
721  frag_info.alpha = 1.0;
722 
723  std::array<VS::PerVertexData, 4> vertices = {
724  VS::PerVertexData{{0, 0}, {0, 0}},
725  VS::PerVertexData{Point(1, 0), {1, 0}},
726  VS::PerVertexData{Point(0, 1), {0, 1}},
727  VS::PerVertexData{Point(1, 1), {1, 1}},
728  };
729  pass.SetVertexBuffer(
730  CreateVertexBuffer(vertices, renderer.GetTransientsBuffer()));
731 
732  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
733  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
734  FS::BindTextureSampler(
735  pass, dst_snapshot->texture,
736  renderer.GetContext()->GetSamplerLibrary()->GetSampler({}));
737 
738  if (!pass.Draw().ok()) {
739  return false;
740  }
741  }
742 
743  {
746 
747  // Next, we render the second contents to a snapshot, or create a 1x1
748  // texture for the foreground color.
749  std::shared_ptr<Texture> src_texture;
750  if (foreground_color.has_value()) {
751  src_texture = foreground_texture;
752  } else {
753  auto src_snapshot =
754  inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
755  if (!src_snapshot.has_value()) {
756  return false;
757  }
758  // This doesn't really handle the case where the transforms are wildly
759  // different, but we only need to support blending two contents together
760  // in limited circumstances (mask blur).
761  src_texture = src_snapshot->texture;
762  }
763 
764  std::array<VS::PerVertexData, 4> vertices = {
765  VS::PerVertexData{Point(0, 0), Point(0, 0)},
766  VS::PerVertexData{Point(1, 0), Point(1, 0)},
767  VS::PerVertexData{Point(0, 1), Point(0, 1)},
768  VS::PerVertexData{Point(1, 1), Point(1, 1)},
769  };
770 
771  auto options = OptionsFromPass(pass);
772  options.blend_mode = BlendMode::kSource;
773  options.primitive_type = PrimitiveType::kTriangleStrip;
774 
775  pass.SetCommandLabel("Framebuffer Advanced Blend Filter");
776  pass.SetVertexBuffer(
777  CreateVertexBuffer(vertices, renderer.GetTransientsBuffer()));
778 
779  switch (blend_mode) {
780  case BlendMode::kScreen:
781  pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options));
782  break;
783  case BlendMode::kOverlay:
784  pass.SetPipeline(
785  renderer.GetFramebufferBlendOverlayPipeline(options));
786  break;
787  case BlendMode::kDarken:
788  pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options));
789  break;
790  case BlendMode::kLighten:
791  pass.SetPipeline(
792  renderer.GetFramebufferBlendLightenPipeline(options));
793  break;
795  pass.SetPipeline(
796  renderer.GetFramebufferBlendColorDodgePipeline(options));
797  break;
799  pass.SetPipeline(
800  renderer.GetFramebufferBlendColorBurnPipeline(options));
801  break;
803  pass.SetPipeline(
804  renderer.GetFramebufferBlendHardLightPipeline(options));
805  break;
807  pass.SetPipeline(
808  renderer.GetFramebufferBlendSoftLightPipeline(options));
809  break;
811  pass.SetPipeline(
812  renderer.GetFramebufferBlendDifferencePipeline(options));
813  break;
815  pass.SetPipeline(
816  renderer.GetFramebufferBlendExclusionPipeline(options));
817  break;
819  pass.SetPipeline(
820  renderer.GetFramebufferBlendMultiplyPipeline(options));
821  break;
822  case BlendMode::kHue:
823  pass.SetPipeline(renderer.GetFramebufferBlendHuePipeline(options));
824  break;
826  pass.SetPipeline(
827  renderer.GetFramebufferBlendSaturationPipeline(options));
828  break;
829  case BlendMode::kColor:
830  pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options));
831  break;
833  pass.SetPipeline(
834  renderer.GetFramebufferBlendLuminosityPipeline(options));
835  break;
836  default:
837  return false;
838  }
839 
840  VS::FrameInfo frame_info;
841  FS::FragInfo frag_info;
842 
843  auto src_sampler_descriptor = SamplerDescriptor{};
844  if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
845  src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
846  src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
847  }
848  raw_ptr<const Sampler> src_sampler =
849  renderer.GetContext()->GetSamplerLibrary()->GetSampler(
850  src_sampler_descriptor);
851  FS::BindTextureSamplerSrc(pass, src_texture, src_sampler);
852 
853  frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
854  frame_info.src_y_coord_scale = src_texture->GetYCoordScale();
855  VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
856 
857  frag_info.src_input_alpha = 1.0;
858  frag_info.dst_input_alpha =
860  ? dst_snapshot->opacity
861  : 1.0;
862  FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
863 
864  return pass.Draw().ok();
865  }
866  };
867 
868  std::shared_ptr<CommandBuffer> cmd_buffer =
869  renderer.GetContext()->CreateCommandBuffer();
870 
871  // Generate a 1x1 texture to implement foreground color blending.
872  if (foreground_color.has_value()) {
873  TextureDescriptor desc;
874  desc.size = {1, 1};
875  desc.format = PixelFormat::kR8G8B8A8UNormInt;
876  desc.storage_mode = StorageMode::kDevicePrivate;
877  foreground_texture =
878  renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc);
879  if (!foreground_texture) {
880  return std::nullopt;
881  }
882  auto blit_pass = cmd_buffer->CreateBlitPass();
883  auto buffer_view = renderer.GetTransientsBuffer().Emplace(
884  foreground_color->Premultiply().ToR8G8B8A8(), /*alignment=*/4);
885 
886  blit_pass->AddCopy(std::move(buffer_view), foreground_texture);
887  if (!blit_pass->EncodeCommands()) {
888  return std::nullopt;
889  }
890  }
891 
892  auto render_target =
893  renderer.MakeSubpass("FramebufferBlend", dst_snapshot->texture->GetSize(),
894  cmd_buffer, subpass_callback);
895 
896  if (!render_target.ok()) {
897  return std::nullopt;
898  }
899  if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(cmd_buffer))) {
900  return std::nullopt;
901  }
902 
903  return Entity::FromSnapshot(
904  Snapshot{
905  .texture = render_target.value().GetRenderTargetTexture(),
906  .transform = dst_snapshot->transform,
907  // Since we absorbed the transform of the inputs and used the
908  // respective snapshot sampling modes when blending, pass on
909  // the default NN clamp sampler.
910  .sampler_descriptor = {},
911  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
912  ? 1.0f
913  : dst_snapshot->opacity) *
914  alpha.value_or(1.0)},
915  entity.GetBlendMode());
916 }
917 
918 #define BLEND_CASE(mode) \
919  case BlendMode::k##mode: \
920  advanced_blend_proc_ = \
921  [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
922  const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
923  std::optional<Color> fg_color, \
924  ColorFilterContents::AbsorbOpacity absorb_opacity, \
925  std::optional<Scalar> alpha) { \
926  PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
927  return AdvancedBlend<Blend##mode##Pipeline>( \
928  inputs, renderer, entity, coverage, blend_mode, fg_color, \
929  absorb_opacity, p, alpha); \
930  }; \
931  break;
932 
934  if (blend_mode > Entity::kLastAdvancedBlendMode) {
935  VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
936  << " assigned to BlendFilterContents.";
937  }
938 
939  blend_mode_ = blend_mode;
940 
941  if (blend_mode > Entity::kLastPipelineBlendMode) {
942  switch (blend_mode) {
943  BLEND_CASE(Screen)
944  BLEND_CASE(Overlay)
945  BLEND_CASE(Darken)
946  BLEND_CASE(Lighten)
947  BLEND_CASE(ColorDodge)
948  BLEND_CASE(ColorBurn)
949  BLEND_CASE(HardLight)
950  BLEND_CASE(SoftLight)
951  BLEND_CASE(Difference)
952  BLEND_CASE(Exclusion)
953  BLEND_CASE(Multiply)
954  BLEND_CASE(Hue)
958  default:
959  FML_UNREACHABLE();
960  }
961  }
962 }
963 
964 void BlendFilterContents::SetForegroundColor(std::optional<Color> color) {
965  foreground_color_ = color;
966 }
967 
968 std::optional<Entity> BlendFilterContents::RenderFilter(
969  const FilterInput::Vector& inputs,
970  const ContentContext& renderer,
971  const Entity& entity,
972  const Matrix& effect_transform,
973  const Rect& coverage,
974  const std::optional<Rect>& coverage_hint) const {
975  if (inputs.empty()) {
976  return std::nullopt;
977  }
978 
979  if (inputs.size() == 1 && !foreground_color_.has_value()) {
980  // Nothing to blend.
981  return PipelineBlend(inputs, renderer, entity, coverage, BlendMode::kSource,
982  std::nullopt, GetAbsorbOpacity(), GetAlpha());
983  }
984 
985  if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
986  if (inputs.size() == 1 && foreground_color_.has_value() &&
988  return CreateForegroundPorterDuffBlend(
989  inputs[0], renderer, entity, coverage, foreground_color_.value(),
990  blend_mode_, GetAlpha(), GetAbsorbOpacity());
991  }
992  return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
993  foreground_color_, GetAbsorbOpacity(), GetAlpha());
994  }
995 
996  if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
998  return CreateFramebufferAdvancedBlend(inputs, renderer, entity, coverage,
999  foreground_color_, blend_mode_,
1000  GetAlpha(), GetAbsorbOpacity());
1001  }
1002  if (inputs.size() == 1 && foreground_color_.has_value() &&
1004  return CreateForegroundAdvancedBlend(
1005  inputs[0], renderer, entity, coverage, foreground_color_.value(),
1006  blend_mode_, GetAlpha(), GetAbsorbOpacity());
1007  }
1008  return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,
1009  foreground_color_, GetAbsorbOpacity(),
1010  GetAlpha());
1011  }
1012 
1013  FML_UNREACHABLE();
1014 }
1015 
1016 } // 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 ...
virtual bool SupportsDecalSamplerAddressMode() const =0
Whether the context backend supports SamplerAddressMode::Decal.
std::optional< Scalar > GetAlpha() const
AbsorbOpacity GetAbsorbOpacity() const
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
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
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
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:18
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:76
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
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:23
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition: entity.cc:145
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:22
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:114
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:89
TRect< Scalar > Rect
Definition: rect.h:792
GlyphAtlasPipeline::VertexShader VS
TPoint< Scalar > Point
Definition: point.h:327
raw_ptr< Pipeline< PipelineDescriptor > > PipelineRef
A raw ptr to a pipeline object.
Definition: pipeline.h:86
VertexBuffer CreateVertexBuffer(std::array< VertexType, size > input, HostBuffer &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
GlyphAtlasPipeline::FragmentShader FS
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:174
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:553
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:324
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:532
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:331
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
#define VALIDATION_LOG
Definition: validation.h:91