Flutter Engine
The Flutter Engine
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"
23#include "impeller/entity/texture_fill.frag.h"
24#include "impeller/entity/texture_fill.vert.h"
28
29namespace impeller {
30
31std::optional<BlendMode> InvertPorterDuffBlend(BlendMode blend_mode) {
32 switch (blend_mode) {
34 return BlendMode::kClear;
38 return BlendMode::kSource;
55 case BlendMode::kXor:
56 return BlendMode::kXor;
58 return BlendMode::kPlus;
61 default:
62 return std::nullopt;
63 }
64}
65
68}
69
71
72using PipelineProc = std::shared_ptr<Pipeline<PipelineDescriptor>> (
74
75template <typename TPipeline>
76static std::optional<Entity> AdvancedBlend(
79 const Entity& entity,
80 const Rect& coverage,
81 BlendMode blend_mode,
82 std::optional<Color> foreground_color,
84 PipelineProc pipeline_proc,
85 std::optional<Scalar> alpha) {
86 using VS = typename TPipeline::VertexShader;
87 using FS = typename TPipeline::FragmentShader;
88
89 //----------------------------------------------------------------------------
90 /// Handle inputs.
91 ///
92
93 const size_t total_inputs =
94 inputs.size() + (foreground_color.has_value() ? 1 : 0);
95 if (total_inputs < 2) {
96 return std::nullopt;
97 }
98
99 auto dst_snapshot =
100 inputs[0]->GetSnapshot("AdvancedBlend(Dst)", renderer, entity);
101 if (!dst_snapshot.has_value()) {
102 return std::nullopt;
103 }
104 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
105 if (!maybe_dst_uvs.has_value()) {
106 return std::nullopt;
107 }
108 auto dst_uvs = maybe_dst_uvs.value();
109
110 std::optional<Snapshot> src_snapshot;
111 std::array<Point, 4> src_uvs;
112 if (!foreground_color.has_value()) {
113 src_snapshot =
114 inputs[1]->GetSnapshot("AdvancedBlend(Src)", renderer, entity);
115 if (!src_snapshot.has_value()) {
116 if (!dst_snapshot.has_value()) {
117 return std::nullopt;
118 }
119 return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
120 }
121 auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage);
122 if (!maybe_src_uvs.has_value()) {
123 if (!dst_snapshot.has_value()) {
124 return std::nullopt;
125 }
126 return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
127 }
128 src_uvs = maybe_src_uvs.value();
129 }
130
131 Rect subpass_coverage = coverage;
132 if (entity.GetContents()) {
133 auto coverage_hint = entity.GetContents()->GetCoverageHint();
134
135 if (coverage_hint.has_value()) {
136 auto maybe_subpass_coverage =
137 subpass_coverage.Intersection(*coverage_hint);
138 if (!maybe_subpass_coverage.has_value()) {
139 return std::nullopt; // Nothing to render.
140 }
141
142 subpass_coverage = *maybe_subpass_coverage;
143 }
144 }
145
146 //----------------------------------------------------------------------------
147 /// Render to texture.
148 ///
149
151 RenderPass& pass) {
152 auto& host_buffer = renderer.GetTransientsBuffer();
153
154 auto size = pass.GetRenderTargetSize();
156 vtx_builder.AddVertices({
157 {Point(0, 0), dst_uvs[0], src_uvs[0]},
158 {Point(size.width, 0), dst_uvs[1], src_uvs[1]},
159 {Point(0, size.height), dst_uvs[2], src_uvs[2]},
160 {Point(size.width, size.height), dst_uvs[3], src_uvs[3]},
161 });
162 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
163
164 auto options = OptionsFromPass(pass);
165 options.primitive_type = PrimitiveType::kTriangleStrip;
166 options.blend_mode = BlendMode::kSource;
167 std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
168 std::invoke(pipeline_proc, renderer, options);
169
170#ifdef IMPELLER_DEBUG
171 pass.SetCommandLabel(
172 SPrintF("Advanced Blend Filter (%s)", BlendModeToString(blend_mode)));
173#endif // IMPELLER_DEBUG
174 pass.SetVertexBuffer(std::move(vtx_buffer));
175 pass.SetPipeline(pipeline);
176
177 typename FS::BlendInfo blend_info;
178 typename VS::FrameInfo frame_info;
179
180 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
181 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
182 dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
183 dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
184 }
185 const std::unique_ptr<const Sampler>& dst_sampler =
186 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
187 dst_sampler_descriptor);
188 FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
189 frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
190 blend_info.dst_input_alpha =
192 ? dst_snapshot->opacity
193 : 1.0;
194
195 if (foreground_color.has_value()) {
196 blend_info.color_factor = 1;
197 blend_info.color = foreground_color.value();
198 // This texture will not be sampled from due to the color factor. But
199 // this is present so that validation doesn't trip on a missing
200 // binding.
201 FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
202 } else {
203 auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
204 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
205 src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
206 src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
207 }
208 const std::unique_ptr<const Sampler>& src_sampler =
209 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
210 src_sampler_descriptor);
211 blend_info.color_factor = 0;
212 blend_info.src_input_alpha = src_snapshot->opacity;
213 FS::BindTextureSamplerSrc(pass, src_snapshot->texture, src_sampler);
214 frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale();
215 }
216 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
217 FS::BindBlendInfo(pass, blend_uniform);
218
219 frame_info.mvp = pass.GetOrthographicTransform() *
221 subpass_coverage.GetOrigin());
222
223 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
224 VS::BindFrameInfo(pass, uniform_view);
225
226 return pass.Draw().ok();
227 };
228
229 std::shared_ptr<CommandBuffer> command_buffer =
230 renderer.GetContext()->CreateCommandBuffer();
231 if (!command_buffer) {
232 return std::nullopt;
233 }
234 fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
235 "Advanced Blend Filter", ISize(subpass_coverage.GetSize()),
236 command_buffer, callback);
237 if (!render_target.ok()) {
238 return std::nullopt;
239 }
240 if (!renderer.GetContext()
241 ->GetCommandQueue()
242 ->Submit(/*buffers=*/{std::move(command_buffer)})
243 .ok()) {
244 return std::nullopt;
245 }
246
248 Snapshot{
249 .texture = render_target.value().GetRenderTargetTexture(),
250 .transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
251 // Since we absorbed the transform of the inputs and used the
252 // respective snapshot sampling modes when blending, pass on
253 // the default NN clamp sampler.
254 .sampler_descriptor = {},
255 .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
256 ? 1.0f
257 : dst_snapshot->opacity) *
258 alpha.value_or(1.0)},
259 entity.GetBlendMode());
260}
261
262std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
263 const std::shared_ptr<FilterInput>& input,
264 const ContentContext& renderer,
265 const Entity& entity,
266 const Rect& coverage,
268 BlendMode blend_mode,
269 std::optional<Scalar> alpha,
270 ColorFilterContents::AbsorbOpacity absorb_opacity) const {
271 auto dst_snapshot =
272 input->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
273 if (!dst_snapshot.has_value()) {
274 return std::nullopt;
275 }
276
277 RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode, alpha,
278 absorb_opacity](const ContentContext& renderer,
279 const Entity& entity,
280 RenderPass& pass) -> bool {
283
284 auto& host_buffer = renderer.GetTransientsBuffer();
285
286 auto size = dst_snapshot->texture->GetSize();
287 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
288 vtx_builder.AddVertices({
289 {{0, 0}, {0, 0}, {0, 0}},
290 {Point(size.width, 0), {1, 0}, {1, 0}},
291 {Point(0, size.height), {0, 1}, {0, 1}},
292 {Point(size.width, size.height), {1, 1}, {1, 1}},
293 });
294 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
295
296#ifdef IMPELLER_DEBUG
297 pass.SetCommandLabel(SPrintF("Foreground Advanced Blend Filter (%s)",
298 BlendModeToString(blend_mode)));
299#endif // IMPELLER_DEBUG
300 pass.SetVertexBuffer(std::move(vtx_buffer));
301 auto options = OptionsFromPass(pass);
302 options.primitive_type = PrimitiveType::kTriangleStrip;
303
304 switch (blend_mode) {
306 pass.SetPipeline(renderer.GetBlendScreenPipeline(options));
307 break;
309 pass.SetPipeline(renderer.GetBlendOverlayPipeline(options));
310 break;
312 pass.SetPipeline(renderer.GetBlendDarkenPipeline(options));
313 break;
315 pass.SetPipeline(renderer.GetBlendLightenPipeline(options));
316 break;
318 pass.SetPipeline(renderer.GetBlendColorDodgePipeline(options));
319 break;
321 pass.SetPipeline(renderer.GetBlendColorBurnPipeline(options));
322 break;
324 pass.SetPipeline(renderer.GetBlendHardLightPipeline(options));
325 break;
327 pass.SetPipeline(renderer.GetBlendSoftLightPipeline(options));
328 break;
330 pass.SetPipeline(renderer.GetBlendDifferencePipeline(options));
331 break;
333 pass.SetPipeline(renderer.GetBlendExclusionPipeline(options));
334 break;
336 pass.SetPipeline(renderer.GetBlendMultiplyPipeline(options));
337 break;
338 case BlendMode::kHue:
339 pass.SetPipeline(renderer.GetBlendHuePipeline(options));
340 break;
342 pass.SetPipeline(renderer.GetBlendSaturationPipeline(options));
343 break;
345 pass.SetPipeline(renderer.GetBlendColorPipeline(options));
346 break;
348 pass.SetPipeline(renderer.GetBlendLuminosityPipeline(options));
349 break;
350 default:
351 return false;
352 }
353
354 FS::BlendInfo blend_info;
355 VS::FrameInfo frame_info;
356
357 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
358 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
359 dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
360 dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
361 }
362 const std::unique_ptr<const Sampler>& dst_sampler =
363 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
364 dst_sampler_descriptor);
365 FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
366 frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
367
368 frame_info.mvp = pass.GetOrthographicTransform() * dst_snapshot->transform;
369
370 blend_info.dst_input_alpha =
372 ? dst_snapshot->opacity * alpha.value_or(1.0)
373 : 1.0;
374
375 blend_info.color_factor = 1;
376 blend_info.color = foreground_color;
377 // This texture will not be sampled from due to the color factor. But
378 // this is present so that validation doesn't trip on a missing
379 // binding.
380 FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
381
382 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
383 FS::BindBlendInfo(pass, blend_uniform);
384
385 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
386 VS::BindFrameInfo(pass, uniform_view);
387
388 return pass.Draw().ok();
389 };
390 CoverageProc coverage_proc =
391 [coverage](const Entity& entity) -> std::optional<Rect> {
392 return coverage.TransformBounds(entity.GetTransform());
393 };
394
395 auto contents = AnonymousContents::Make(render_proc, coverage_proc);
396
397 Entity sub_entity;
398 sub_entity.SetContents(std::move(contents));
399
400 return sub_entity;
401}
402
403std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
404 const std::shared_ptr<FilterInput>& input,
405 const ContentContext& renderer,
406 const Entity& entity,
407 const Rect& coverage,
409 BlendMode blend_mode,
410 std::optional<Scalar> alpha,
411 ColorFilterContents::AbsorbOpacity absorb_opacity) const {
412 if (blend_mode == BlendMode::kClear) {
413 return std::nullopt;
414 }
415
416 auto dst_snapshot =
417 input->GetSnapshot("ForegroundPorterDuffBlend", renderer, entity);
418 if (!dst_snapshot.has_value()) {
419 return std::nullopt;
420 }
421
422 if (blend_mode == BlendMode::kDestination) {
423 return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
424 }
425
426 RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode,
427 absorb_opacity, alpha](
428 const ContentContext& renderer,
429 const Entity& entity, RenderPass& pass) -> bool {
432
433 auto& host_buffer = renderer.GetTransientsBuffer();
434 auto size = dst_snapshot->texture->GetSize();
435 auto color = foreground_color.Premultiply();
436 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
437 vtx_builder.AddVertices({
438 {{0, 0}, {0, 0}, color},
439 {Point(size.width, 0), {1, 0}, color},
440 {Point(0, size.height), {0, 1}, color},
441 {Point(size.width, size.height), {1, 1}, color},
442 });
443 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
444
445#ifdef IMPELLER_DEBUG
446 pass.SetCommandLabel(SPrintF("Foreground PorterDuff Blend Filter (%s)",
447 BlendModeToString(blend_mode)));
448#endif // IMPELLER_DEBUG
449 pass.SetVertexBuffer(std::move(vtx_buffer));
450 auto options = OptionsFromPass(pass);
451 options.primitive_type = PrimitiveType::kTriangleStrip;
452 pass.SetPipeline(renderer.GetPorterDuffBlendPipeline(options));
453
454 FS::FragInfo frag_info;
455 VS::FrameInfo frame_info;
456
457 frame_info.mvp = pass.GetOrthographicTransform() * dst_snapshot->transform;
458
459 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
460 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
461 dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
462 dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
463 }
464 const std::unique_ptr<const Sampler>& dst_sampler =
465 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
466 dst_sampler_descriptor);
467 FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
468 frame_info.texture_sampler_y_coord_scale =
469 dst_snapshot->texture->GetYCoordScale();
470
471 frag_info.input_alpha =
473 ? dst_snapshot->opacity * alpha.value_or(1.0)
474 : 1.0;
475 frag_info.output_alpha = 1.0;
476
477 auto blend_coefficients =
478 kPorterDuffCoefficients[static_cast<int>(blend_mode)];
479 frag_info.src_coeff = blend_coefficients[0];
480 frag_info.src_coeff_dst_alpha = blend_coefficients[1];
481 frag_info.dst_coeff = blend_coefficients[2];
482 frag_info.dst_coeff_src_alpha = blend_coefficients[3];
483 frag_info.dst_coeff_src_color = blend_coefficients[4];
484
485 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
486 VS::BindFrameInfo(pass, 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
501 return sub_entity;
502}
503
504static std::optional<Entity> PipelineBlend(
507 const Entity& entity,
508 const Rect& coverage,
509 BlendMode blend_mode,
510 std::optional<Color> foreground_color,
512 std::optional<Scalar> alpha) {
515
516 auto dst_snapshot =
517 inputs[0]->GetSnapshot("PipelineBlend(Dst)", renderer, entity);
518 if (!dst_snapshot.has_value()) {
519 return std::nullopt; // Nothing to render.
520 }
521
522 Rect subpass_coverage = coverage;
523 if (entity.GetContents()) {
524 auto coverage_hint = entity.GetContents()->GetCoverageHint();
525
526 if (coverage_hint.has_value()) {
527 auto maybe_subpass_coverage =
528 subpass_coverage.Intersection(*coverage_hint);
529 if (!maybe_subpass_coverage.has_value()) {
530 return std::nullopt; // Nothing to render.
531 }
532
533 subpass_coverage = *maybe_subpass_coverage;
534 }
535 }
536
538 RenderPass& pass) {
539 auto& host_buffer = renderer.GetTransientsBuffer();
540
541#ifdef IMPELLER_DEBUG
542 pass.SetCommandLabel(
543 SPrintF("Pipeline Blend Filter (%s)", BlendModeToString(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 const std::unique_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();
564 vtx_builder.AddVertices({
565 {Point(0, 0), Point(0, 0)},
566 {Point(size.width, 0), Point(1, 0)},
567 {Point(0, size.height), Point(0, 1)},
568 {Point(size.width, size.height), Point(1, 1)},
569 });
570 pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
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, host_buffer.EmplaceUniform(frag_info));
585 VS::BindFrameInfo(pass, 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::kSource;
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 contents->SetGeometry(
618 Geometry::MakeRect(Rect::MakeSize(pass.GetRenderTargetSize())));
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 = renderer.MakeSubpass(
639 "Pipeline Blend Filter", ISize(subpass_coverage.GetSize()),
640 command_buffer, callback);
641
642 if (!render_target.ok()) {
643 return std::nullopt;
644 }
645
646 if (!renderer.GetContext()
647 ->GetCommandQueue()
648 ->Submit(/*buffers=*/{std::move(command_buffer)})
649 .ok()) {
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(
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& host_buffer = renderer.GetTransientsBuffer();
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 = 1.0;
708
709 FS::FragInfo frag_info;
710 frag_info.alpha = 1.0;
711
712 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
713 vtx_builder.AddVertices({
714 {{0, 0}, {0, 0}},
715 {Point(1, 0), {1, 0}},
716 {Point(0, 1), {0, 1}},
717 {Point(1, 1), {1, 1}},
718 });
719 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
720 pass.SetVertexBuffer(std::move(vtx_buffer));
721
722 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
723 FS::BindFragInfo(pass, 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 {
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 if (foreground_color.has_value()) {
741 src_texture = foreground_texture;
742 } else {
743 auto src_snapshot =
744 inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
745 if (!src_snapshot.has_value()) {
746 return false;
747 }
748 // This doesn't really handle the case where the transforms are wildly
749 // different, but we only need to support blending two contents together
750 // in limited circumstances (mask blur).
751 src_texture = src_snapshot->texture;
752 }
753
754 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
755 vtx_builder.AddVertices({
756 {Point(0, 0), Point(0, 0)},
757 {Point(1, 0), Point(1, 0)},
758 {Point(0, 1), Point(0, 1)},
759 {Point(1, 1), Point(1, 1)},
760 });
761
762 auto options = OptionsFromPass(pass);
763 options.blend_mode = BlendMode::kSource;
764 options.primitive_type = PrimitiveType::kTriangleStrip;
765
766 pass.SetCommandLabel("Framebuffer Advanced Blend Filter");
767 pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
768
769 switch (blend_mode) {
771 pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options));
772 break;
774 pass.SetPipeline(
775 renderer.GetFramebufferBlendOverlayPipeline(options));
776 break;
778 pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options));
779 break;
781 pass.SetPipeline(
782 renderer.GetFramebufferBlendLightenPipeline(options));
783 break;
785 pass.SetPipeline(
786 renderer.GetFramebufferBlendColorDodgePipeline(options));
787 break;
789 pass.SetPipeline(
790 renderer.GetFramebufferBlendColorBurnPipeline(options));
791 break;
793 pass.SetPipeline(
794 renderer.GetFramebufferBlendHardLightPipeline(options));
795 break;
797 pass.SetPipeline(
798 renderer.GetFramebufferBlendSoftLightPipeline(options));
799 break;
801 pass.SetPipeline(
802 renderer.GetFramebufferBlendDifferencePipeline(options));
803 break;
805 pass.SetPipeline(
806 renderer.GetFramebufferBlendExclusionPipeline(options));
807 break;
809 pass.SetPipeline(
810 renderer.GetFramebufferBlendMultiplyPipeline(options));
811 break;
812 case BlendMode::kHue:
813 pass.SetPipeline(renderer.GetFramebufferBlendHuePipeline(options));
814 break;
816 pass.SetPipeline(
817 renderer.GetFramebufferBlendSaturationPipeline(options));
818 break;
820 pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options));
821 break;
823 pass.SetPipeline(
824 renderer.GetFramebufferBlendLuminosityPipeline(options));
825 break;
826 default:
827 return false;
828 }
829
830 VS::FrameInfo frame_info;
831 FS::FragInfo frag_info;
832
833 auto src_sampler_descriptor = SamplerDescriptor{};
834 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
835 src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
836 src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
837 }
838 const std::unique_ptr<const Sampler>& src_sampler =
839 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
840 src_sampler_descriptor);
841 FS::BindTextureSamplerSrc(pass, src_texture, src_sampler);
842
843 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
844 frame_info.src_y_coord_scale = src_texture->GetYCoordScale();
845 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
846
847 frag_info.src_input_alpha = 1.0;
848 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
849
850 return pass.Draw().ok();
851 }
852 };
853
854 std::shared_ptr<CommandBuffer> cmd_buffer =
855 renderer.GetContext()->CreateCommandBuffer();
856
857 // Generate a 1x1 texture to implement foreground color blending.
858 if (foreground_color.has_value()) {
859 TextureDescriptor desc;
860 desc.size = {1, 1};
862 desc.storage_mode = StorageMode::kDevicePrivate;
863 foreground_texture =
864 renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc);
865 if (!foreground_texture) {
866 return std::nullopt;
867 }
868 auto blit_pass = cmd_buffer->CreateBlitPass();
869 auto buffer_view = renderer.GetTransientsBuffer().Emplace(
870 foreground_color->Premultiply().ToR8G8B8A8(), /*alignment=*/4);
871
872 blit_pass->AddCopy(std::move(buffer_view), foreground_texture);
873 if (!blit_pass->EncodeCommands(
874 renderer.GetContext()->GetResourceAllocator())) {
875 return std::nullopt;
876 }
877 }
878
879 auto render_target =
880 renderer.MakeSubpass("FramebufferBlend", dst_snapshot->texture->GetSize(),
881 cmd_buffer, subpass_callback);
882
883 if (!render_target.ok()) {
884 return std::nullopt;
885 }
886
887 if (!renderer.GetContext()
888 ->GetCommandQueue()
889 ->Submit(/*buffers=*/{std::move(cmd_buffer)})
890 .ok()) {
891 return std::nullopt;
892 }
893
895 Snapshot{
896 .texture = render_target.value().GetRenderTargetTexture(),
897 .transform = dst_snapshot->transform,
898 // Since we absorbed the transform of the inputs and used the
899 // respective snapshot sampling modes when blending, pass on
900 // the default NN clamp sampler.
901 .sampler_descriptor = {},
902 .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
903 ? 1.0f
904 : dst_snapshot->opacity) *
905 alpha.value_or(1.0)},
906 entity.GetBlendMode());
907}
908
909#define BLEND_CASE(mode) \
910 case BlendMode::k##mode: \
911 advanced_blend_proc_ = \
912 [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
913 const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
914 std::optional<Color> fg_color, \
915 ColorFilterContents::AbsorbOpacity absorb_opacity, \
916 std::optional<Scalar> alpha) { \
917 PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
918 return AdvancedBlend<Blend##mode##Pipeline>( \
919 inputs, renderer, entity, coverage, blend_mode, fg_color, \
920 absorb_opacity, p, alpha); \
921 }; \
922 break;
923
925 if (blend_mode > Entity::kLastAdvancedBlendMode) {
926 VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
927 << " assigned to BlendFilterContents.";
928 }
929
930 blend_mode_ = blend_mode;
931
932 if (blend_mode > Entity::kLastPipelineBlendMode) {
933 switch (blend_mode) {
934 BLEND_CASE(Screen)
935 BLEND_CASE(Overlay)
936 BLEND_CASE(Darken)
937 BLEND_CASE(Lighten)
938 BLEND_CASE(ColorDodge)
939 BLEND_CASE(ColorBurn)
940 BLEND_CASE(HardLight)
941 BLEND_CASE(SoftLight)
942 BLEND_CASE(Difference)
943 BLEND_CASE(Exclusion)
944 BLEND_CASE(Multiply)
945 BLEND_CASE(Hue)
949 default:
951 }
952 }
953}
954
956 foreground_color_ = color;
957}
958
959std::optional<Entity> BlendFilterContents::RenderFilter(
962 const Entity& entity,
963 const Matrix& effect_transform,
964 const Rect& coverage,
965 const std::optional<Rect>& coverage_hint) const {
966 if (inputs.empty()) {
967 return std::nullopt;
968 }
969
970 if (inputs.size() == 1 && !foreground_color_.has_value()) {
971 // Nothing to blend.
973 std::nullopt, GetAbsorbOpacity(), GetAlpha());
974 }
975
976 if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
977 if (inputs.size() == 1 && foreground_color_.has_value() &&
979 return CreateForegroundPorterDuffBlend(
980 inputs[0], renderer, entity, coverage, foreground_color_.value(),
981 blend_mode_, GetAlpha(), GetAbsorbOpacity());
982 }
983 return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
984 foreground_color_, GetAbsorbOpacity(), GetAlpha());
985 }
986
987 if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
988 if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) {
989 return CreateFramebufferAdvancedBlend(inputs, renderer, entity, coverage,
990 foreground_color_, blend_mode_,
992 }
993 if (inputs.size() == 1 && foreground_color_.has_value() &&
995 return CreateForegroundAdvancedBlend(
996 inputs[0], renderer, entity, coverage, foreground_color_.value(),
997 blend_mode_, GetAlpha(), GetAbsorbOpacity());
998 }
999 return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,
1000 foreground_color_, GetAbsorbOpacity(),
1001 GetAlpha());
1002 }
1003
1005}
1006
1007} // namespace impeller
const char * options
#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.
std::optional< Scalar > GetAlpha() const
AbsorbOpacity GetAbsorbOpacity() const
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
std::function< std::optional< Rect >(const Entity &entity)> CoverageProc
Definition: contents.h:47
std::function< bool(const ContentContext &renderer, const Entity &entity, RenderPass &pass)> RenderProc
Definition: contents.h:46
static Entity FromSnapshot(const Snapshot &snapshot, BlendMode blend_mode=BlendMode::kSourceOver)
Create an entity that can be used to render a given snapshot.
Definition: entity.cc:22
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:94
BlendMode GetBlendMode() const
Definition: entity.cc:119
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:90
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:115
static constexpr BlendMode kLastAdvancedBlendMode
Definition: entity.h:23
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition: entity.cc:173
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:22
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
static std::shared_ptr< Geometry > MakeRect(const Rect &rect)
Definition: geometry.cc:89
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
FragmentShader_ FragmentShader
Definition: pipeline.h:107
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
DlColor color
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
#define FML_UNREACHABLE()
Definition: logging.h:109
#define FML_DCHECK(condition)
Definition: logging.h:103
SK_API sk_sp< SkShader > Color(SkColor)
it will be possible to load the file into Perfetto s trace viewer 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
Definition: switches.h:259
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:166
TRect< Scalar > Rect
Definition: rect.h:769
SolidFillVertexShader VS
TPoint< Scalar > Point
Definition: point.h:322
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
constexpr std::array< std::array< Scalar, 5 >, 15 > kPorterDuffCoefficients
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:59
std::shared_ptr< Pipeline< PipelineDescriptor > >(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 OptionsFromPass(const RenderPass &pass)
Definition: contents.cc:19
ISize64 ISize
Definition: size.h:140
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:140
const uint32_t foreground_color[]
Definition: colrv1.cpp:213
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:497
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:519
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:317
static constexpr TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
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:310
#define VALIDATION_LOG
Definition: validation.h:73