11 #include "flutter/fml/logging.h"
23 #include "impeller/entity/texture_fill.frag.h"
24 #include "impeller/entity/texture_fill.vert.h"
72 using PipelineProc = std::shared_ptr<Pipeline<PipelineDescriptor>> (
75 template <
typename TPipeline>
82 std::optional<Color> foreground_color,
85 std::optional<Scalar> alpha) {
86 using VS =
typename TPipeline::VertexShader;
87 using FS =
typename TPipeline::FragmentShader;
93 const size_t total_inputs =
94 inputs.size() + (foreground_color.has_value() ? 1 : 0);
95 if (total_inputs < 2) {
100 inputs[0]->GetSnapshot(
"AdvancedBlend(Dst)", renderer, entity);
101 if (!dst_snapshot.has_value()) {
104 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
105 if (!maybe_dst_uvs.has_value()) {
108 auto dst_uvs = maybe_dst_uvs.value();
110 std::optional<Snapshot> src_snapshot;
111 std::array<Point, 4> src_uvs;
112 if (!foreground_color.has_value()) {
114 inputs[1]->GetSnapshot(
"AdvancedBlend(Src)", renderer, entity);
115 if (!src_snapshot.has_value()) {
116 if (!dst_snapshot.has_value()) {
121 auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage);
122 if (!maybe_src_uvs.has_value()) {
123 if (!dst_snapshot.has_value()) {
128 src_uvs = maybe_src_uvs.value();
131 Rect subpass_coverage = coverage;
133 auto coverage_hint = entity.
GetContents()->GetCoverageHint();
135 if (coverage_hint.has_value()) {
136 auto maybe_subpass_coverage =
138 if (!maybe_subpass_coverage.has_value()) {
142 subpass_coverage = *maybe_subpass_coverage;
154 auto size = pass.GetRenderTargetSize();
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]},
167 std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
168 std::invoke(pipeline_proc, renderer, options);
170 #ifdef IMPELLER_DEBUG
171 pass.SetCommandLabel(
173 #endif // IMPELLER_DEBUG
174 pass.SetVertexBuffer(std::move(vtx_buffer));
175 pass.SetPipeline(pipeline);
177 typename FS::BlendInfo blend_info;
178 typename VS::FrameInfo frame_info;
180 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
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
195 if (foreground_color.has_value()) {
196 blend_info.color_factor = 1;
197 blend_info.color = foreground_color.value();
201 FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
203 auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
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();
216 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
217 FS::BindBlendInfo(pass, blend_uniform);
219 frame_info.mvp = pass.GetOrthographicTransform() *
223 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
224 VS::BindFrameInfo(pass, uniform_view);
226 return pass.Draw().ok();
229 std::shared_ptr<CommandBuffer> command_buffer =
231 if (!command_buffer) {
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()) {
242 ->Submit({std::move(command_buffer)})
249 .
texture = render_target.value().GetRenderTargetTexture(),
254 .sampler_descriptor = {},
257 : dst_snapshot->opacity) *
258 alpha.value_or(1.0)},
262 std::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,
269 std::optional<Scalar> alpha,
272 input->GetSnapshot(
"ForegroundAdvancedBlend", renderer, entity);
273 if (!dst_snapshot.has_value()) {
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 {
284 auto& host_buffer = renderer.GetTransientsBuffer();
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}},
294 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
296 #ifdef IMPELLER_DEBUG
297 pass.SetCommandLabel(
SPrintF(
"Foreground Advanced Blend Filter (%s)",
299 #endif // IMPELLER_DEBUG
300 pass.SetVertexBuffer(std::move(vtx_buffer));
304 switch (blend_mode) {
306 pass.SetPipeline(renderer.GetBlendScreenPipeline(options));
309 pass.SetPipeline(renderer.GetBlendOverlayPipeline(options));
312 pass.SetPipeline(renderer.GetBlendDarkenPipeline(options));
315 pass.SetPipeline(renderer.GetBlendLightenPipeline(options));
318 pass.SetPipeline(renderer.GetBlendColorDodgePipeline(options));
321 pass.SetPipeline(renderer.GetBlendColorBurnPipeline(options));
324 pass.SetPipeline(renderer.GetBlendHardLightPipeline(options));
327 pass.SetPipeline(renderer.GetBlendSoftLightPipeline(options));
330 pass.SetPipeline(renderer.GetBlendDifferencePipeline(options));
333 pass.SetPipeline(renderer.GetBlendExclusionPipeline(options));
336 pass.SetPipeline(renderer.GetBlendMultiplyPipeline(options));
339 pass.SetPipeline(renderer.GetBlendHuePipeline(options));
342 pass.SetPipeline(renderer.GetBlendSaturationPipeline(options));
345 pass.SetPipeline(renderer.GetBlendColorPipeline(options));
348 pass.SetPipeline(renderer.GetBlendLuminosityPipeline(options));
354 FS::BlendInfo blend_info;
355 VS::FrameInfo frame_info;
357 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
358 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
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();
368 frame_info.mvp = pass.GetOrthographicTransform() * dst_snapshot->transform;
370 blend_info.dst_input_alpha =
372 ? dst_snapshot->opacity * alpha.value_or(1.0)
375 blend_info.color_factor = 1;
376 blend_info.color = foreground_color;
380 FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
382 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
383 FS::BindBlendInfo(pass, blend_uniform);
385 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
386 VS::BindFrameInfo(pass, uniform_view);
388 return pass.Draw().ok();
391 [coverage](
const Entity& entity) -> std::optional<Rect> {
392 return coverage.TransformBounds(entity.GetTransform());
398 sub_entity.SetContents(std::move(contents));
403 std::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,
410 std::optional<Scalar> alpha,
417 input->GetSnapshot(
"ForegroundPorterDuffBlend", renderer, entity);
418 if (!dst_snapshot.has_value()) {
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 {
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},
441 {
Point(size.width, size.height), {1, 1},
color},
443 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
445 #ifdef IMPELLER_DEBUG
446 pass.SetCommandLabel(
SPrintF(
"Foreground PorterDuff Blend Filter (%s)",
448 #endif // IMPELLER_DEBUG
449 pass.SetVertexBuffer(std::move(vtx_buffer));
452 pass.SetPipeline(renderer.GetPorterDuffBlendPipeline(options));
454 FS::FragInfo frag_info;
455 VS::FrameInfo frame_info;
457 frame_info.mvp = pass.GetOrthographicTransform() * dst_snapshot->transform;
459 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
460 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
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();
471 frag_info.input_alpha =
473 ? dst_snapshot->opacity * alpha.value_or(1.0)
475 frag_info.output_alpha = 1.0;
477 auto blend_coefficients =
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];
485 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
486 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
488 return pass.Draw().ok();
492 [coverage](
const Entity& entity) -> std::optional<Rect> {
493 return coverage.TransformBounds(entity.GetTransform());
499 sub_entity.SetContents(std::move(contents));
508 const Rect& coverage,
510 std::optional<Color> foreground_color,
512 std::optional<Scalar> alpha) {
517 inputs[0]->GetSnapshot(
"PipelineBlend(Dst)", renderer, entity);
518 if (!dst_snapshot.has_value()) {
522 Rect subpass_coverage = coverage;
524 auto coverage_hint = entity.
GetContents()->GetCoverageHint();
526 if (coverage_hint.has_value()) {
527 auto maybe_subpass_coverage =
529 if (!maybe_subpass_coverage.has_value()) {
533 subpass_coverage = *maybe_subpass_coverage;
541 #ifdef IMPELLER_DEBUG
542 pass.SetCommandLabel(
544 #endif // IMPELLER_DEBUG
548 auto add_blend_command = [&](std::optional<Snapshot> input) {
549 if (!input.has_value()) {
552 auto input_coverage = input->GetCoverage();
553 if (!input_coverage.has_value()) {
557 const std::unique_ptr<const Sampler>& sampler =
558 renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
559 input->sampler_descriptor);
560 FS::BindTextureSampler(pass, input->texture, sampler);
562 auto size = input->texture->GetSize();
568 {
Point(size.width, size.height),
Point(1, 1)},
572 VS::FrameInfo frame_info;
573 frame_info.mvp = pass.GetOrthographicTransform() *
576 frame_info.texture_sampler_y_coord_scale =
577 input->texture->GetYCoordScale();
579 FS::FragInfo frag_info;
584 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
585 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
587 return pass.Draw().ok();
593 if (!add_blend_command(dst_snapshot)) {
599 if (inputs.size() >= 2) {
600 options.blend_mode = blend_mode;
603 for (
auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
605 auto src_input = texture_i->get()->GetSnapshot(
"PipelineBlend(Src)",
607 if (!add_blend_command(src_input)) {
615 if (foreground_color.has_value()) {
616 auto contents = std::make_shared<SolidColorContents>();
617 contents->SetGeometry(
619 contents->SetColor(foreground_color.value());
624 if (!foreground_entity.
Render(renderer, pass)) {
632 std::shared_ptr<CommandBuffer> command_buffer =
634 if (!command_buffer) {
638 fml::StatusOr<RenderTarget> render_target = renderer.
MakeSubpass(
639 "Pipeline Blend Filter",
ISize(subpass_coverage.
GetSize()),
640 command_buffer, callback);
642 if (!render_target.ok()) {
648 ->Submit({std::move(command_buffer)})
655 .
texture = render_target.value().GetRenderTargetTexture(),
660 .sampler_descriptor = {},
663 : dst_snapshot->opacity) *
664 alpha.value_or(1.0)},
668 std::optional<Entity> BlendFilterContents::CreateFramebufferAdvancedBlend(
670 const ContentContext& renderer,
671 const Entity& entity,
672 const Rect& coverage,
673 std::optional<Color> foreground_color,
675 std::optional<Scalar> alpha,
678 FML_DCHECK(inputs.size() == 2u ||
679 (inputs.size() == 1u && foreground_color.has_value()));
682 inputs[0]->GetSnapshot(
"ForegroundAdvancedBlend", renderer, entity);
683 if (!dst_snapshot.has_value()) {
687 std::shared_ptr<Texture> foreground_texture;
694 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
697 using FS = TextureFillFragmentShader;
698 using VS = TextureFillVertexShader;
700 pass.SetCommandLabel(
"Framebuffer Advanced Blend");
703 pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
705 VS::FrameInfo frame_info;
707 frame_info.texture_sampler_y_coord_scale = 1.0;
709 FS::FragInfo frag_info;
710 frag_info.alpha = 1.0;
712 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
713 vtx_builder.AddVertices({
715 {
Point(1, 0), {1, 0}},
716 {
Point(0, 1), {0, 1}},
717 {
Point(1, 1), {1, 1}},
719 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
720 pass.SetVertexBuffer(std::move(vtx_buffer));
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({}));
728 if (!pass.Draw().ok()) {
739 std::shared_ptr<Texture> src_texture;
740 if (foreground_color.has_value()) {
741 src_texture = foreground_texture;
744 inputs[0]->GetSnapshot(
"ForegroundAdvancedBlend", renderer, entity);
745 if (!src_snapshot.has_value()) {
751 src_texture = src_snapshot->texture;
754 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
755 vtx_builder.AddVertices({
766 pass.SetCommandLabel(
"Framebuffer Advanced Blend Filter");
767 pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
769 switch (blend_mode) {
771 pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options));
775 renderer.GetFramebufferBlendOverlayPipeline(options));
778 pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options));
782 renderer.GetFramebufferBlendLightenPipeline(options));
786 renderer.GetFramebufferBlendColorDodgePipeline(options));
790 renderer.GetFramebufferBlendColorBurnPipeline(options));
794 renderer.GetFramebufferBlendHardLightPipeline(options));
798 renderer.GetFramebufferBlendSoftLightPipeline(options));
802 renderer.GetFramebufferBlendDifferencePipeline(options));
806 renderer.GetFramebufferBlendExclusionPipeline(options));
810 renderer.GetFramebufferBlendMultiplyPipeline(options));
813 pass.SetPipeline(renderer.GetFramebufferBlendHuePipeline(options));
817 renderer.GetFramebufferBlendSaturationPipeline(options));
820 pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options));
824 renderer.GetFramebufferBlendLuminosityPipeline(options));
830 VS::FrameInfo frame_info;
831 FS::FragInfo frag_info;
833 auto src_sampler_descriptor = SamplerDescriptor{};
834 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
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);
844 frame_info.src_y_coord_scale = src_texture->GetYCoordScale();
845 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
847 frag_info.src_input_alpha = 1.0;
848 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
850 return pass.Draw().ok();
854 std::shared_ptr<CommandBuffer> cmd_buffer =
855 renderer.GetContext()->CreateCommandBuffer();
858 if (foreground_color.has_value()) {
859 TextureDescriptor desc;
864 renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc);
865 if (!foreground_texture) {
868 auto blit_pass = cmd_buffer->CreateBlitPass();
869 auto buffer_view = renderer.GetTransientsBuffer().Emplace(
870 foreground_color->Premultiply().ToR8G8B8A8(), 4);
872 blit_pass->AddCopy(std::move(
buffer_view), foreground_texture);
873 if (!blit_pass->EncodeCommands(
874 renderer.GetContext()->GetResourceAllocator())) {
880 renderer.MakeSubpass(
"FramebufferBlend", dst_snapshot->texture->GetSize(),
881 cmd_buffer, subpass_callback);
883 if (!render_target.ok()) {
887 if (!renderer.GetContext()
889 ->Submit({std::move(cmd_buffer)})
896 .texture = render_target.value().GetRenderTargetTexture(),
901 .sampler_descriptor = {},
904 : dst_snapshot->opacity) *
905 alpha.value_or(1.0)},
906 entity.GetBlendMode());
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); \
926 VALIDATION_LOG <<
"Invalid blend mode " <<
static_cast<int>(blend_mode)
927 <<
" assigned to BlendFilterContents.";
930 blend_mode_ = blend_mode;
933 switch (blend_mode) {
956 foreground_color_ =
color;
959 std::optional<Entity> BlendFilterContents::RenderFilter(
963 const Matrix& effect_transform,
964 const Rect& coverage,
965 const std::optional<Rect>& coverage_hint)
const {
966 if (inputs.empty()) {
970 if (inputs.size() == 1 && !foreground_color_.has_value()) {
977 if (inputs.size() == 1 && foreground_color_.has_value() &&
979 return CreateForegroundPorterDuffBlend(
980 inputs[0], renderer, entity, coverage, foreground_color_.value(),
983 return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
989 return CreateFramebufferAdvancedBlend(inputs, renderer, entity, coverage,
990 foreground_color_, blend_mode_,
993 if (inputs.size() == 1 && foreground_color_.has_value() &&
995 return CreateForegroundAdvancedBlend(
996 inputs[0], renderer, entity, coverage, foreground_color_.value(),
999 return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,