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