Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
blend_filter_contents.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <array>
8#include <memory>
9#include <optional>
10
11#include "flutter/fml/logging.h"
23#include "impeller/entity/texture_fill.frag.h"
24#include "impeller/entity/texture_fill.vert.h"
28
29namespace impeller {
30
65
69
71
72using PipelineProc = std::shared_ptr<Pipeline<PipelineDescriptor>> (
74
75template <typename TPipeline>
76static std::optional<Entity> AdvancedBlend(
77 const FilterInput::Vector& inputs,
78 const ContentContext& renderer,
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() *
220 Matrix::MakeTranslation(coverage.GetOrigin() -
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,
267 Color foreground_color,
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,
408 Color foreground_color,
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(
505 const FilterInput::Vector& inputs,
506 const ContentContext& renderer,
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(
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 ContentContext::SubpassCallback subpass_callback = [&](const ContentContext&
688 renderer,
689 RenderPass& pass) {
690 // First, we create a new render pass and populate it with the contents
691 // of the first (dst) input.
692 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
693
694 {
695 using FS = TextureFillFragmentShader;
696 using VS = TextureFillVertexShader;
697
698 pass.SetCommandLabel("Framebuffer Advanced Blend");
699 auto pipeline_options = OptionsFromPass(pass);
700 pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
701 pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
702
703 VS::FrameInfo frame_info;
704 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
705 frame_info.texture_sampler_y_coord_scale = 1.0;
706
707 FS::FragInfo frag_info;
708 frag_info.alpha = 1.0;
709
710 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
711 vtx_builder.AddVertices({
712 {{0, 0}, {0, 0}},
713 {Point(1, 0), {1, 0}},
714 {Point(0, 1), {0, 1}},
715 {Point(1, 1), {1, 1}},
716 });
717 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
718 pass.SetVertexBuffer(std::move(vtx_buffer));
719
720 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
721 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
722 FS::BindTextureSampler(
723 pass, dst_snapshot->texture,
724 renderer.GetContext()->GetSamplerLibrary()->GetSampler({}));
725
726 if (!pass.Draw().ok()) {
727 return false;
728 }
729 }
730
731 {
734
735 // Next, we render the second contents to a snapshot, or create a 1x1
736 // texture for the foreground color.
737 std::shared_ptr<Texture> src_texture;
738 if (foreground_color.has_value()) {
739 TextureDescriptor desc;
740 desc.size = {1, 1};
742 desc.storage_mode = StorageMode::kHostVisible;
743 src_texture =
744 renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc);
745 if (!src_texture) {
746 return false;
747 }
748
749 if (!src_texture->SetContents(
750 foreground_color->Premultiply().ToR8G8B8A8().data(), 4u)) {
751 return false;
752 }
753 } else {
754 auto src_snapshot =
755 inputs[0]->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
756 if (!src_snapshot.has_value()) {
757 return false;
758 }
759 // This doesn't really handle the case where the transforms are wildly
760 // different, but we only need to support blending two contents together
761 // in limited circumstances (mask blur).
762 src_texture = src_snapshot->texture;
763 }
764
765 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
766 vtx_builder.AddVertices({
767 {Point(0, 0), Point(0, 0)},
768 {Point(1, 0), Point(1, 0)},
769 {Point(0, 1), Point(0, 1)},
770 {Point(1, 1), Point(1, 1)},
771 });
772
773 auto options = OptionsFromPass(pass);
774 options.blend_mode = BlendMode::kSource;
775 options.primitive_type = PrimitiveType::kTriangleStrip;
776
777 pass.SetCommandLabel("Framebuffer Advanced Blend Filter");
778 pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
779
780 switch (blend_mode) {
782 pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options));
783 break;
785 pass.SetPipeline(
786 renderer.GetFramebufferBlendOverlayPipeline(options));
787 break;
789 pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options));
790 break;
792 pass.SetPipeline(
793 renderer.GetFramebufferBlendLightenPipeline(options));
794 break;
796 pass.SetPipeline(
797 renderer.GetFramebufferBlendColorDodgePipeline(options));
798 break;
800 pass.SetPipeline(
801 renderer.GetFramebufferBlendColorBurnPipeline(options));
802 break;
804 pass.SetPipeline(
805 renderer.GetFramebufferBlendHardLightPipeline(options));
806 break;
808 pass.SetPipeline(
809 renderer.GetFramebufferBlendSoftLightPipeline(options));
810 break;
812 pass.SetPipeline(
813 renderer.GetFramebufferBlendDifferencePipeline(options));
814 break;
816 pass.SetPipeline(
817 renderer.GetFramebufferBlendExclusionPipeline(options));
818 break;
820 pass.SetPipeline(
821 renderer.GetFramebufferBlendMultiplyPipeline(options));
822 break;
823 case BlendMode::kHue:
824 pass.SetPipeline(renderer.GetFramebufferBlendHuePipeline(options));
825 break;
827 pass.SetPipeline(
828 renderer.GetFramebufferBlendSaturationPipeline(options));
829 break;
831 pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options));
832 break;
834 pass.SetPipeline(
835 renderer.GetFramebufferBlendLuminosityPipeline(options));
836 break;
837 default:
838 return false;
839 }
840
841 VS::FrameInfo frame_info;
842 FS::FragInfo frag_info;
843
844 auto src_sampler_descriptor = SamplerDescriptor{};
845 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
846 src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
847 src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
848 }
849 const std::unique_ptr<const Sampler>& src_sampler =
850 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
851 src_sampler_descriptor);
852 FS::BindTextureSamplerSrc(pass, src_texture, src_sampler);
853
854 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
855 frame_info.src_y_coord_scale = src_texture->GetYCoordScale();
856 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
857
858 frag_info.src_input_alpha = 1.0;
859 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
860
861 return pass.Draw().ok();
862 }
863 };
864
865 std::shared_ptr<CommandBuffer> cmd_buffer =
866 renderer.GetContext()->CreateCommandBuffer();
867 auto render_target =
868 renderer.MakeSubpass("FramebufferBlend", dst_snapshot->texture->GetSize(),
869 cmd_buffer, subpass_callback);
870
871 if (!render_target.ok()) {
872 return std::nullopt;
873 }
874
875 if (!renderer.GetContext()
876 ->GetCommandQueue()
877 ->Submit(/*buffers=*/{std::move(cmd_buffer)})
878 .ok()) {
879 return std::nullopt;
880 }
881
883 Snapshot{
884 .texture = render_target.value().GetRenderTargetTexture(),
885 .transform = dst_snapshot->transform,
886 // Since we absorbed the transform of the inputs and used the
887 // respective snapshot sampling modes when blending, pass on
888 // the default NN clamp sampler.
889 .sampler_descriptor = {},
890 .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
891 ? 1.0f
892 : dst_snapshot->opacity) *
893 alpha.value_or(1.0)},
894 entity.GetBlendMode());
895}
896
897#define BLEND_CASE(mode) \
898 case BlendMode::k##mode: \
899 advanced_blend_proc_ = \
900 [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
901 const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
902 std::optional<Color> fg_color, \
903 ColorFilterContents::AbsorbOpacity absorb_opacity, \
904 std::optional<Scalar> alpha) { \
905 PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
906 return AdvancedBlend<Blend##mode##Pipeline>( \
907 inputs, renderer, entity, coverage, blend_mode, fg_color, \
908 absorb_opacity, p, alpha); \
909 }; \
910 break;
911
913 if (blend_mode > Entity::kLastAdvancedBlendMode) {
914 VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
915 << " assigned to BlendFilterContents.";
916 }
917
918 blend_mode_ = blend_mode;
919
920 if (blend_mode > Entity::kLastPipelineBlendMode) {
921 switch (blend_mode) {
922 BLEND_CASE(Screen)
923 BLEND_CASE(Overlay)
924 BLEND_CASE(Darken)
925 BLEND_CASE(Lighten)
926 BLEND_CASE(ColorDodge)
927 BLEND_CASE(ColorBurn)
928 BLEND_CASE(HardLight)
929 BLEND_CASE(SoftLight)
930 BLEND_CASE(Difference)
931 BLEND_CASE(Exclusion)
932 BLEND_CASE(Multiply)
933 BLEND_CASE(Hue)
937 default:
939 }
940 }
941}
942
944 foreground_color_ = color;
945}
946
948 const FilterInput::Vector& inputs,
949 const ContentContext& renderer,
950 const Entity& entity,
951 const Matrix& effect_transform,
952 const Rect& coverage,
953 const std::optional<Rect>& coverage_hint) const {
954 if (inputs.empty()) {
955 return std::nullopt;
956 }
957
958 if (inputs.size() == 1 && !foreground_color_.has_value()) {
959 // Nothing to blend.
960 return PipelineBlend(inputs, renderer, entity, coverage, BlendMode::kSource,
961 std::nullopt, GetAbsorbOpacity(), GetAlpha());
962 }
963
964 if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
965 if (inputs.size() == 1 && foreground_color_.has_value() &&
967 return CreateForegroundPorterDuffBlend(
968 inputs[0], renderer, entity, coverage, foreground_color_.value(),
969 blend_mode_, GetAlpha(), GetAbsorbOpacity());
970 }
971 return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
972 foreground_color_, GetAbsorbOpacity(), GetAlpha());
973 }
974
975 if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
976 if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) {
977 return CreateFramebufferAdvancedBlend(inputs, renderer, entity, coverage,
978 foreground_color_, blend_mode_,
980 }
981 if (inputs.size() == 1 && foreground_color_.has_value() &&
983 return CreateForegroundAdvancedBlend(
984 inputs[0], renderer, entity, coverage, foreground_color_.value(),
985 blend_mode_, GetAlpha(), GetAbsorbOpacity());
986 }
987 return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,
988 foreground_color_, GetAbsorbOpacity(),
989 GetAlpha());
990 }
991
993}
994
995} // namespace impeller
const char * options
SkColor4f color
static SkBlendMode GetBlendMode(SkSVGFeBlend::Mode mode)
#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)
std::optional< Entity > RenderFilter(const FilterInput::Vector &inputs, const ContentContext &renderer, const Entity &entity, const Matrix &effect_transform, const Rect &coverage, const std::optional< Rect > &coverage_hint) const override
Converts zero or more filter inputs into a render instruction.
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
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
std::function< std::optional< Rect >(const Entity &entity)> CoverageProc
Definition contents.h:50
std::function< bool(const ContentContext &renderer, const Entity &entity, RenderPass &pass)> RenderProc
Definition contents.h:49
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:24
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition entity.cc:173
static constexpr BlendMode kLastPipelineBlendMode
Definition entity.h:23
std::vector< FilterInput::Ref > Vector
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:106
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
#define FML_UNREACHABLE()
Definition logging.h:109
#define FML_DCHECK(condition)
Definition logging.h:103
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:746
SolidFillVertexShader VS
TPoint< Scalar > Point
Definition point.h:316
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)
TSize< int64_t > ISize
Definition size.h:138
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:20
static constexpr Scalar Luminosity(Vector3 color)
Definition color.cc:140
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:495
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:496
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:294
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:287
#define VALIDATION_LOG
Definition validation.h:73