Flutter Engine
 
Loading...
Searching...
No Matches
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"
25#include "impeller/entity/texture_fill.frag.h"
26#include "impeller/entity/texture_fill.vert.h"
30
31namespace impeller {
32
33namespace {
34
35#ifdef IMPELLER_DEBUG
36
37#define _IMPELLER_BLEND_MODE_FILTER_NAME_LIST(blend_mode) \
38 "Blend Filter " #blend_mode,
39
40static constexpr const char* kBlendModeFilterNames[] = {
41 IMPELLER_FOR_EACH_BLEND_MODE(_IMPELLER_BLEND_MODE_FILTER_NAME_LIST)};
42
43const 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
51std::optional<BlendMode> InvertPorterDuffBlend(BlendMode blend_mode) {
52 switch (blend_mode) {
54 return BlendMode::kClear;
55 case BlendMode::kSrc:
56 return BlendMode::kDst;
57 case BlendMode::kDst:
58 return BlendMode::kSrc;
64 return BlendMode::kDstIn;
66 return BlendMode::kSrcIn;
68 return BlendMode::kDstOut;
70 return BlendMode::kSrcOut;
75 case BlendMode::kXor:
76 return BlendMode::kXor;
78 return BlendMode::kPlus;
81 default:
82 return std::nullopt;
83 }
84}
85
89
91
94
95template <typename TPipeline>
96static 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,
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
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
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
276std::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 {
295 using VS = BlendScreenPipeline::VertexShader;
296 using FS = BlendScreenPipeline::FragmentShader;
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) {
318 pass.SetPipeline(renderer.GetBlendScreenPipeline(options));
319 break;
321 pass.SetPipeline(renderer.GetBlendOverlayPipeline(options));
322 break;
324 pass.SetPipeline(renderer.GetBlendDarkenPipeline(options));
325 break;
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;
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
413std::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 {
440 using VS = PorterDuffBlendPipeline::VertexShader;
441 using FS = PorterDuffBlendPipeline::FragmentShader;
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
505static 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,
513 std::optional<Scalar> alpha) {
514 using VS = TexturePipeline::VertexShader;
515 using FS = TexturePipeline::FragmentShader;
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
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
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
669std::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 {
735 using VS = FramebufferBlendScreenPipeline::VertexShader;
736 using FS = FramebufferBlendScreenPipeline::FragmentShader;
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) {
774 pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options));
775 break;
777 pass.SetPipeline(
778 renderer.GetFramebufferBlendOverlayPipeline(options));
779 break;
781 pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options));
782 break;
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;
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
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:
953 }
954 }
955}
956
957void BlendFilterContents::SetForegroundColor(std::optional<Color> color) {
958 foreground_color_ = color;
959}
960
961std::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
1007}
1008
1009} // namespace impeller
#define BLEND_CASE(mode)
BufferView buffer_view
const T & value() const
Definition status_or.h:77
bool ok() const
Definition status_or.h:75
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
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
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
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
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition render_pass.h:30
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition color.h:19
static int input(yyscan_t yyscanner)
FlutterDesktopBinaryReply callback
#define FML_UNREACHABLE()
Definition logging.h:128
#define FML_DCHECK(condition)
Definition logging.h:122
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
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.
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)
std::optional< BlendMode > InvertPorterDuffBlend(BlendMode blend_mode)
BlendMode
Definition color.h:58
LinePipeline::VertexShader VS
PipelineRef(ContentContext::*)(ContentContextOptions) const PipelineProc
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)
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 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 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
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
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
#define VALIDATION_LOG
Definition validation.h:91