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();
369 pass, dst_snapshot->transform);
371 blend_info.dst_input_alpha =
373 ? dst_snapshot->opacity * alpha.value_or(1.0)
376 blend_info.color_factor = 1;
377 blend_info.color = foreground_color;
381 FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
383 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
384 FS::BindBlendInfo(pass, blend_uniform);
386 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
387 VS::BindFrameInfo(pass, uniform_view);
389 return pass.Draw().ok();
392 [coverage](
const Entity& entity) -> std::optional<Rect> {
393 return coverage.TransformBounds(entity.GetTransform());
399 sub_entity.SetContents(std::move(contents));
404 std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
405 const std::shared_ptr<FilterInput>& input,
406 const ContentContext& renderer,
407 const Entity& entity,
408 const Rect& coverage,
409 Color foreground_color,
411 std::optional<Scalar> alpha,
418 input->GetSnapshot(
"ForegroundPorterDuffBlend", renderer, entity);
419 if (!dst_snapshot.has_value()) {
427 RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode,
428 absorb_opacity, alpha](
429 const ContentContext& renderer,
430 const Entity& entity, RenderPass& pass) ->
bool {
434 auto& host_buffer = renderer.GetTransientsBuffer();
435 auto size = dst_snapshot->texture->GetSize();
436 auto color = foreground_color.Premultiply();
437 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
438 vtx_builder.AddVertices({
439 {{0, 0}, {0, 0},
color},
442 {
Point(size.width, size.height), {1, 1},
color},
444 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
446 #ifdef IMPELLER_DEBUG
447 pass.SetCommandLabel(
SPrintF(
"Foreground PorterDuff Blend Filter (%s)",
449 #endif // IMPELLER_DEBUG
450 pass.SetVertexBuffer(std::move(vtx_buffer));
453 pass.SetPipeline(renderer.GetPorterDuffBlendPipeline(options));
455 FS::FragInfo frag_info;
456 VS::FrameInfo frame_info;
459 pass, dst_snapshot->transform);
461 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
462 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
466 const std::unique_ptr<const Sampler>& dst_sampler =
467 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
468 dst_sampler_descriptor);
469 FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
470 frame_info.texture_sampler_y_coord_scale =
471 dst_snapshot->texture->GetYCoordScale();
473 frag_info.input_alpha =
475 ? dst_snapshot->opacity * alpha.value_or(1.0)
477 frag_info.output_alpha = 1.0;
479 auto blend_coefficients =
481 frag_info.src_coeff = blend_coefficients[0];
482 frag_info.src_coeff_dst_alpha = blend_coefficients[1];
483 frag_info.dst_coeff = blend_coefficients[2];
484 frag_info.dst_coeff_src_alpha = blend_coefficients[3];
485 frag_info.dst_coeff_src_color = blend_coefficients[4];
487 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
488 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
490 return pass.Draw().ok();
494 [coverage](
const Entity& entity) -> std::optional<Rect> {
495 return coverage.TransformBounds(entity.GetTransform());
501 sub_entity.SetContents(std::move(contents));
510 const Rect& coverage,
512 std::optional<Color> foreground_color,
514 std::optional<Scalar> alpha) {
519 inputs[0]->GetSnapshot(
"PipelineBlend(Dst)", renderer, entity);
520 if (!dst_snapshot.has_value()) {
524 Rect subpass_coverage = coverage;
526 auto coverage_hint = entity.
GetContents()->GetCoverageHint();
528 if (coverage_hint.has_value()) {
529 auto maybe_subpass_coverage =
531 if (!maybe_subpass_coverage.has_value()) {
535 subpass_coverage = *maybe_subpass_coverage;
543 #ifdef IMPELLER_DEBUG
544 pass.SetCommandLabel(
546 #endif // IMPELLER_DEBUG
550 auto add_blend_command = [&](std::optional<Snapshot> input) {
551 if (!input.has_value()) {
554 auto input_coverage = input->GetCoverage();
555 if (!input_coverage.has_value()) {
559 const std::unique_ptr<const Sampler>& sampler =
560 renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
561 input->sampler_descriptor);
562 FS::BindTextureSampler(pass, input->texture, sampler);
564 auto size = input->texture->GetSize();
570 {
Point(size.width, size.height),
Point(1, 1)},
574 VS::FrameInfo frame_info;
575 frame_info.mvp = pass.GetOrthographicTransform() *
578 frame_info.texture_sampler_y_coord_scale =
579 input->texture->GetYCoordScale();
581 FS::FragInfo frag_info;
586 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
587 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
589 return pass.Draw().ok();
595 if (!add_blend_command(dst_snapshot)) {
601 if (inputs.size() >= 2) {
602 options.blend_mode = blend_mode;
605 for (
auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
607 auto src_input = texture_i->get()->GetSnapshot(
"PipelineBlend(Src)",
609 if (!add_blend_command(src_input)) {
617 if (foreground_color.has_value()) {
618 auto contents = std::make_shared<SolidColorContents>();
619 contents->SetGeometry(
621 contents->SetColor(foreground_color.value());
626 if (!foreground_entity.
Render(renderer, pass)) {
634 std::shared_ptr<CommandBuffer> command_buffer =
636 if (!command_buffer) {
640 fml::StatusOr<RenderTarget> render_target = renderer.
MakeSubpass(
641 "Pipeline Blend Filter",
ISize(subpass_coverage.
GetSize()),
642 command_buffer, callback);
644 if (!render_target.ok()) {
650 ->Submit({std::move(command_buffer)})
657 .
texture = render_target.value().GetRenderTargetTexture(),
662 .sampler_descriptor = {},
665 : dst_snapshot->opacity) *
666 alpha.value_or(1.0)},
670 std::optional<Entity> BlendFilterContents::CreateFramebufferAdvancedBlend(
672 const ContentContext& renderer,
673 const Entity& entity,
674 const Rect& coverage,
675 std::optional<Color> foreground_color,
677 std::optional<Scalar> alpha,
680 FML_DCHECK(inputs.size() == 2u ||
681 (inputs.size() == 1u && foreground_color.has_value()));
684 inputs[0]->GetSnapshot(
"ForegroundAdvancedBlend", renderer, entity);
685 if (!dst_snapshot.has_value()) {
689 std::shared_ptr<Texture> foreground_texture;
696 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
699 using FS = TextureFillFragmentShader;
700 using VS = TextureFillVertexShader;
702 pass.SetCommandLabel(
"Framebuffer Advanced Blend");
705 pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
707 VS::FrameInfo frame_info;
709 frame_info.texture_sampler_y_coord_scale = 1.0;
711 FS::FragInfo frag_info;
712 frag_info.alpha = 1.0;
714 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
715 vtx_builder.AddVertices({
717 {
Point(1, 0), {1, 0}},
718 {
Point(0, 1), {0, 1}},
719 {
Point(1, 1), {1, 1}},
721 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
722 pass.SetVertexBuffer(std::move(vtx_buffer));
724 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
725 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
726 FS::BindTextureSampler(
727 pass, dst_snapshot->texture,
728 renderer.GetContext()->GetSamplerLibrary()->GetSampler({}));
730 if (!pass.Draw().ok()) {
741 std::shared_ptr<Texture> src_texture;
742 if (foreground_color.has_value()) {
743 src_texture = foreground_texture;
746 inputs[0]->GetSnapshot(
"ForegroundAdvancedBlend", renderer, entity);
747 if (!src_snapshot.has_value()) {
753 src_texture = src_snapshot->texture;
756 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
757 vtx_builder.AddVertices({
768 pass.SetCommandLabel(
"Framebuffer Advanced Blend Filter");
769 pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
771 switch (blend_mode) {
773 pass.SetPipeline(renderer.GetFramebufferBlendScreenPipeline(options));
777 renderer.GetFramebufferBlendOverlayPipeline(options));
780 pass.SetPipeline(renderer.GetFramebufferBlendDarkenPipeline(options));
784 renderer.GetFramebufferBlendLightenPipeline(options));
788 renderer.GetFramebufferBlendColorDodgePipeline(options));
792 renderer.GetFramebufferBlendColorBurnPipeline(options));
796 renderer.GetFramebufferBlendHardLightPipeline(options));
800 renderer.GetFramebufferBlendSoftLightPipeline(options));
804 renderer.GetFramebufferBlendDifferencePipeline(options));
808 renderer.GetFramebufferBlendExclusionPipeline(options));
812 renderer.GetFramebufferBlendMultiplyPipeline(options));
815 pass.SetPipeline(renderer.GetFramebufferBlendHuePipeline(options));
819 renderer.GetFramebufferBlendSaturationPipeline(options));
822 pass.SetPipeline(renderer.GetFramebufferBlendColorPipeline(options));
826 renderer.GetFramebufferBlendLuminosityPipeline(options));
832 VS::FrameInfo frame_info;
833 FS::FragInfo frag_info;
835 auto src_sampler_descriptor = SamplerDescriptor{};
836 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
840 const std::unique_ptr<const Sampler>& src_sampler =
841 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
842 src_sampler_descriptor);
843 FS::BindTextureSamplerSrc(pass, src_texture, src_sampler);
846 frame_info.src_y_coord_scale = src_texture->GetYCoordScale();
847 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
849 frag_info.src_input_alpha = 1.0;
850 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
852 return pass.Draw().ok();
856 std::shared_ptr<CommandBuffer> cmd_buffer =
857 renderer.GetContext()->CreateCommandBuffer();
860 if (foreground_color.has_value()) {
861 TextureDescriptor desc;
866 renderer.GetContext()->GetResourceAllocator()->CreateTexture(desc);
867 if (!foreground_texture) {
870 auto blit_pass = cmd_buffer->CreateBlitPass();
871 auto buffer_view = renderer.GetTransientsBuffer().Emplace(
872 foreground_color->Premultiply().ToR8G8B8A8(), 4);
874 blit_pass->AddCopy(std::move(
buffer_view), foreground_texture);
875 if (!blit_pass->EncodeCommands(
876 renderer.GetContext()->GetResourceAllocator())) {
882 renderer.MakeSubpass(
"FramebufferBlend", dst_snapshot->texture->GetSize(),
883 cmd_buffer, subpass_callback);
885 if (!render_target.ok()) {
889 if (!renderer.GetContext()
891 ->Submit({std::move(cmd_buffer)})
898 .texture = render_target.value().GetRenderTargetTexture(),
903 .sampler_descriptor = {},
906 : dst_snapshot->opacity) *
907 alpha.value_or(1.0)},
908 entity.GetBlendMode());
911 #define BLEND_CASE(mode) \
912 case BlendMode::k##mode: \
913 advanced_blend_proc_ = \
914 [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
915 const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
916 std::optional<Color> fg_color, \
917 ColorFilterContents::AbsorbOpacity absorb_opacity, \
918 std::optional<Scalar> alpha) { \
919 PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
920 return AdvancedBlend<Blend##mode##Pipeline>( \
921 inputs, renderer, entity, coverage, blend_mode, fg_color, \
922 absorb_opacity, p, alpha); \
928 VALIDATION_LOG <<
"Invalid blend mode " <<
static_cast<int>(blend_mode)
929 <<
" assigned to BlendFilterContents.";
932 blend_mode_ = blend_mode;
935 switch (blend_mode) {
958 foreground_color_ =
color;
961 std::optional<Entity> BlendFilterContents::RenderFilter(
965 const Matrix& effect_transform,
966 const Rect& coverage,
967 const std::optional<Rect>& coverage_hint)
const {
968 if (inputs.empty()) {
972 if (inputs.size() == 1 && !foreground_color_.has_value()) {
979 if (inputs.size() == 1 && foreground_color_.has_value() &&
981 return CreateForegroundPorterDuffBlend(
982 inputs[0], renderer, entity, coverage, foreground_color_.value(),
985 return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
991 return CreateFramebufferAdvancedBlend(inputs, renderer, entity, coverage,
992 foreground_color_, blend_mode_,
995 if (inputs.size() == 1 && foreground_color_.has_value() &&
997 return CreateForegroundAdvancedBlend(
998 inputs[0], renderer, entity, coverage, foreground_color_.value(),
1001 return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,