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 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 FillRectGeometry geom(Rect::MakeSize(pass.GetRenderTargetSize()));
617 auto contents = std::make_shared<SolidColorContents>(&geom);
618 contents->SetColor(foreground_color.value());
619
620 Entity foreground_entity;
621 foreground_entity.SetBlendMode(blend_mode);
622 foreground_entity.SetContents(contents);
623 if (!foreground_entity.Render(renderer, pass)) {
624 return false;
625 }
626 }
627
628 return true;
629 };
630
631 std::shared_ptr<CommandBuffer> command_buffer =
632 renderer.GetContext()->CreateCommandBuffer();
633 if (!command_buffer) {
634 return std::nullopt;
635 }
636
637 fml::StatusOr<RenderTarget> render_target =
638 renderer.MakeSubpass("Pipeline Blend Filter", //
639 ISize(subpass_coverage.GetSize()), //
640 command_buffer, //
641 callback, //
642 /*msaa_enabled=*/false, //
643 /*depth_stencil_enabled=*/false //
644 );
645
646 if (!render_target.ok()) {
647 return std::nullopt;
648 }
649 if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(command_buffer))) {
650 return std::nullopt;
651 }
652
654 Snapshot{
655 .texture = render_target.value().GetRenderTargetTexture(),
656 .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
657 // Since we absorbed the transform of the inputs and used the
658 // respective snapshot sampling modes when blending, pass on
659 // the default NN clamp sampler.
660 .sampler_descriptor = {},
661 .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
662 ? 1.0f
663 : dst_snapshot->opacity) *
664 alpha.value_or(1.0)},
665 entity.GetBlendMode());
666}
667
668std::optional<Entity> BlendFilterContents::CreateFramebufferAdvancedBlend(
669 const FilterInput::Vector& inputs,
670 const ContentContext& renderer,
671 const Entity& entity,
672 const Rect& coverage,
673 std::optional<Color> foreground_color,
674 BlendMode blend_mode,
675 std::optional<Scalar> alpha,
676 ColorFilterContents::AbsorbOpacity absorb_opacity) const {
677 // This works with either 2 contents or 1 contents and a foreground color.
678 FML_DCHECK(inputs.size() == 2u ||
679 (inputs.size() == 1u && foreground_color.has_value()));
680
681 auto dst_snapshot =
682 inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
683 if (!dst_snapshot.has_value()) {
684 return std::nullopt;
685 }
686
687 std::shared_ptr<Texture> foreground_texture;
688
689 ContentContext::SubpassCallback subpass_callback = [&](const ContentContext&
690 renderer,
691 RenderPass& pass) {
692 // First, we create a new render pass and populate it with the contents
693 // of the first (dst) input.
694 HostBuffer& data_host_buffer = renderer.GetTransientsDataBuffer();
695
696 {
697 using FS = TextureFillFragmentShader;
698 using VS = TextureFillVertexShader;
699
700 pass.SetCommandLabel("Framebuffer Advanced Blend");
701 auto pipeline_options = OptionsFromPass(pass);
702 pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
703 pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
704
705 VS::FrameInfo frame_info;
706 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
707 frame_info.texture_sampler_y_coord_scale =
708 dst_snapshot->texture->GetYCoordScale();
709
710 FS::FragInfo frag_info;
711 frag_info.alpha = 1.0;
712
713 std::array<VS::PerVertexData, 4> vertices = {
714 VS::PerVertexData{{0, 0}, {0, 0}},
715 VS::PerVertexData{Point(1, 0), {1, 0}},
716 VS::PerVertexData{Point(0, 1), {0, 1}},
717 VS::PerVertexData{Point(1, 1), {1, 1}},
718 };
719 pass.SetVertexBuffer(
720 CreateVertexBuffer(vertices, renderer.GetTransientsDataBuffer()));
721
722 VS::BindFrameInfo(pass, data_host_buffer.EmplaceUniform(frame_info));
723 FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
724 FS::BindTextureSampler(
725 pass, dst_snapshot->texture,
726 renderer.GetContext()->GetSamplerLibrary()->GetSampler({}));
727
728 if (!pass.Draw().ok()) {
729 return false;
730 }
731 }
732
733 {
734 using VS = FramebufferBlendScreenPipeline::VertexShader;
735 using FS = FramebufferBlendScreenPipeline::FragmentShader;
736
737 // Next, we render the second contents to a snapshot, or create a 1x1
738 // texture for the foreground color.
739 std::shared_ptr<Texture> src_texture;
740 SamplerDescriptor src_sampler_descriptor = SamplerDescriptor{};
741 if (foreground_color.has_value()) {
742 src_texture = foreground_texture;
743 } else {
744 auto src_snapshot =
745 inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
746 if (!src_snapshot.has_value()) {
747 return false;
748 }
749 // This doesn't really handle the case where the transforms are wildly
750 // different, but we only need to support blending two contents together
751 // in limited circumstances (mask blur).
752 src_texture = src_snapshot->texture;
753 src_sampler_descriptor = src_snapshot->sampler_descriptor;
754 }
755
756 std::array<VS::PerVertexData, 4> vertices = {
757 VS::PerVertexData{Point(0, 0), Point(0, 0)},
758 VS::PerVertexData{Point(1, 0), Point(1, 0)},
759 VS::PerVertexData{Point(0, 1), Point(0, 1)},
760 VS::PerVertexData{Point(1, 1), Point(1, 1)},
761 };
762
763 auto options = OptionsFromPass(pass);
764 options.blend_mode = BlendMode::kSrc;
765 options.primitive_type = PrimitiveType::kTriangleStrip;
766
767 pass.SetCommandLabel("Framebuffer Advanced Blend Filter");
768 pass.SetVertexBuffer(
769 CreateVertexBuffer(vertices, renderer.GetTransientsDataBuffer()));
770
771 switch (blend_mode) {
773 pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options));
774 break;
776 pass.SetPipeline(
777 renderer.GetFramebufferBlendOverlayPipeline(options));
778 break;
780 pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options));
781 break;
783 pass.SetPipeline(
784 renderer.GetFramebufferBlendLightenPipeline(options));
785 break;
787 pass.SetPipeline(
788 renderer.GetFramebufferBlendColorDodgePipeline(options));
789 break;
791 pass.SetPipeline(
792 renderer.GetFramebufferBlendColorBurnPipeline(options));
793 break;
795 pass.SetPipeline(
796 renderer.GetFramebufferBlendHardLightPipeline(options));
797 break;
799 pass.SetPipeline(
800 renderer.GetFramebufferBlendSoftLightPipeline(options));
801 break;
803 pass.SetPipeline(
804 renderer.GetFramebufferBlendDifferencePipeline(options));
805 break;
807 pass.SetPipeline(
808 renderer.GetFramebufferBlendExclusionPipeline(options));
809 break;
811 pass.SetPipeline(
812 renderer.GetFramebufferBlendMultiplyPipeline(options));
813 break;
814 case BlendMode::kHue:
815 pass.SetPipeline(renderer.GetFramebufferBlendHuePipeline(options));
816 break;
818 pass.SetPipeline(
819 renderer.GetFramebufferBlendSaturationPipeline(options));
820 break;
822 pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options));
823 break;
825 pass.SetPipeline(
826 renderer.GetFramebufferBlendLuminosityPipeline(options));
827 break;
828 default:
829 return false;
830 }
831
832 VS::FrameInfo frame_info;
833 FS::FragInfo frag_info;
834
835 raw_ptr<const Sampler> src_sampler =
836 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
837 src_sampler_descriptor);
838 FS::BindTextureSamplerSrc(pass, src_texture, src_sampler);
839
840 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
841 frame_info.src_y_coord_scale = src_texture->GetYCoordScale();
842 VS::BindFrameInfo(pass, data_host_buffer.EmplaceUniform(frame_info));
843
844 frag_info.src_input_alpha = 1.0;
845 frag_info.dst_input_alpha =
847 ? dst_snapshot->opacity
848 : 1.0;
849 FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info));
850
851 return pass.Draw().ok();
852 }
853 };
854
855 std::shared_ptr<CommandBuffer> cmd_buffer =
856 renderer.GetContext()->CreateCommandBuffer();
857
858 // Generate a 1x1 texture to implement foreground color blending.
859 if (foreground_color.has_value()) {
860 TextureDescriptor desc;
861 desc.size = {1, 1};
862 desc.format = PixelFormat::kR8G8B8A8UNormInt;
863 desc.storage_mode = StorageMode::kDevicePrivate;
864 foreground_texture =
865 renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc);
866 if (!foreground_texture) {
867 return std::nullopt;
868 }
869 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
870 auto buffer_view = renderer.GetTransientsDataBuffer().Emplace(
871 foreground_color->Premultiply().ToR8G8B8A8(), /*alignment=*/4);
872
873 blit_pass->AddCopy(std::move(buffer_view), foreground_texture);
874 if (!blit_pass->EncodeCommands()) {
875 return std::nullopt;
876 }
877 }
878
879 fml::StatusOr<RenderTarget> render_target =
880 renderer.MakeSubpass("FramebufferBlend", //
881 dst_snapshot->texture->GetSize(), //
882 cmd_buffer, //
883 subpass_callback, //
884 /*msaa_enabled=*/true, //
885 /*depth_stencil_enabled=*/true //
886 );
887
888 if (!render_target.ok()) {
889 return std::nullopt;
890 }
891 if (!renderer.GetContext()->EnqueueCommandBuffer(std::move(cmd_buffer))) {
892 return std::nullopt;
893 }
894
896 Snapshot{
897 .texture = render_target.value().GetRenderTargetTexture(),
898 .transform = dst_snapshot->transform,
899 // Since we absorbed the transform of the inputs and used the
900 // respective snapshot sampling modes when blending, pass on
901 // the default NN clamp sampler.
902 .sampler_descriptor = {},
903 .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
904 ? 1.0f
905 : dst_snapshot->opacity) *
906 alpha.value_or(1.0)},
907 entity.GetBlendMode());
908}
909
910#define BLEND_CASE(mode) \
911 case BlendMode::k##mode: \
912 advanced_blend_proc_ = \
913 [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
914 const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
915 std::optional<Color> fg_color, \
916 ColorFilterContents::AbsorbOpacity absorb_opacity, \
917 std::optional<Scalar> alpha) { \
918 PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
919 return AdvancedBlend<Blend##mode##Pipeline>( \
920 inputs, renderer, entity, coverage, blend_mode, fg_color, \
921 absorb_opacity, p, alpha); \
922 }; \
923 break;
924
926 if (blend_mode > Entity::kLastAdvancedBlendMode) {
927 VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
928 << " assigned to BlendFilterContents.";
929 }
930
931 blend_mode_ = blend_mode;
932
933 if (blend_mode > Entity::kLastPipelineBlendMode) {
934 switch (blend_mode) {
935 BLEND_CASE(Screen)
936 BLEND_CASE(Overlay)
937 BLEND_CASE(Darken)
938 BLEND_CASE(Lighten)
939 BLEND_CASE(ColorDodge)
940 BLEND_CASE(ColorBurn)
941 BLEND_CASE(HardLight)
942 BLEND_CASE(SoftLight)
943 BLEND_CASE(Difference)
944 BLEND_CASE(Exclusion)
945 BLEND_CASE(Multiply)
946 BLEND_CASE(Hue)
950 default:
952 }
953 }
954}
955
956void BlendFilterContents::SetForegroundColor(std::optional<Color> color) {
957 foreground_color_ = color;
958}
959
960std::optional<Entity> BlendFilterContents::RenderFilter(
961 const FilterInput::Vector& inputs,
962 const ContentContext& renderer,
963 const Entity& entity,
964 const Matrix& effect_transform,
965 const Rect& coverage,
966 const std::optional<Rect>& coverage_hint) const {
967 if (inputs.empty()) {
968 return std::nullopt;
969 }
970
971 if (inputs.size() == 1 && !foreground_color_.has_value()) {
972 // Nothing to blend.
973 return PipelineBlend(inputs, renderer, entity, coverage, BlendMode::kSrc,
974 std::nullopt, GetAbsorbOpacity(), GetAlpha());
975 }
976
977 if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
978 if (inputs.size() == 1 && foreground_color_.has_value() &&
980 return CreateForegroundPorterDuffBlend(
981 inputs[0], renderer, entity, coverage, foreground_color_.value(),
982 blend_mode_, GetAlpha(), GetAbsorbOpacity());
983 }
984 return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
985 foreground_color_, GetAbsorbOpacity(), GetAlpha());
986 }
987
988 if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
990 return CreateFramebufferAdvancedBlend(inputs, renderer, entity, coverage,
991 foreground_color_, blend_mode_,
993 }
994 if (inputs.size() == 1 && foreground_color_.has_value() &&
996 return CreateForegroundAdvancedBlend(
997 inputs[0], renderer, entity, coverage, foreground_color_.value(),
998 blend_mode_, GetAlpha(), GetAbsorbOpacity());
999 }
1000 return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,
1001 foreground_color_, GetAbsorbOpacity(),
1002 GetAlpha());
1003 }
1004
1006}
1007
1008} // 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
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