67 using PipelineProc = std::shared_ptr<Pipeline<PipelineDescriptor>> (
70 template <
typename TPipeline>
77 std::optional<Color> foreground_color,
80 std::optional<Scalar> alpha) {
81 using VS =
typename TPipeline::VertexShader;
82 using FS =
typename TPipeline::FragmentShader;
88 const size_t total_inputs =
89 inputs.size() + (foreground_color.has_value() ? 1 : 0);
90 if (total_inputs < 2) {
95 inputs[0]->GetSnapshot(
"AdvancedBlend(Dst)", renderer, entity);
96 if (!dst_snapshot.has_value()) {
99 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
100 if (!maybe_dst_uvs.has_value()) {
103 auto dst_uvs = maybe_dst_uvs.value();
105 std::optional<Snapshot> src_snapshot;
106 std::array<Point, 4> src_uvs;
107 if (!foreground_color.has_value()) {
109 inputs[1]->GetSnapshot(
"AdvancedBlend(Src)", renderer, entity);
110 if (!src_snapshot.has_value()) {
111 if (!dst_snapshot.has_value()) {
117 auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage);
118 if (!maybe_src_uvs.has_value()) {
119 if (!dst_snapshot.has_value()) {
125 src_uvs = maybe_src_uvs.value();
128 Rect subpass_coverage = coverage;
130 auto coverage_hint = entity.
GetContents()->GetCoverageHint();
132 if (coverage_hint.has_value()) {
133 auto maybe_subpass_coverage =
135 if (!maybe_subpass_coverage.has_value()) {
139 subpass_coverage = *maybe_subpass_coverage;
151 auto size = pass.GetRenderTargetSize();
154 {
Point(0, 0), dst_uvs[0], src_uvs[0]},
155 {
Point(size.width, 0), dst_uvs[1], src_uvs[1]},
156 {
Point(0, size.height), dst_uvs[2], src_uvs[2]},
157 {
Point(size.width, size.height), dst_uvs[3], src_uvs[3]},
164 std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
165 std::invoke(pipeline_proc, renderer, options);
167 #ifdef IMPELLER_DEBUG
168 pass.SetCommandLabel(
170 #endif // IMPELLER_DEBUG
171 pass.SetVertexBuffer(std::move(vtx_buffer));
172 pass.SetPipeline(pipeline);
174 typename FS::BlendInfo blend_info;
175 typename VS::FrameInfo frame_info;
177 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
182 const std::unique_ptr<const Sampler>& dst_sampler =
183 renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
184 dst_sampler_descriptor);
185 FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
186 frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
187 blend_info.dst_input_alpha =
189 ? dst_snapshot->opacity
192 if (foreground_color.has_value()) {
193 blend_info.color_factor = 1;
194 blend_info.color = foreground_color.value();
198 FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
200 auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
205 const std::unique_ptr<const Sampler>& src_sampler =
206 renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
207 src_sampler_descriptor);
208 blend_info.color_factor = 0;
209 blend_info.src_input_alpha = src_snapshot->opacity;
210 FS::BindTextureSamplerSrc(pass, src_snapshot->texture, src_sampler);
211 frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale();
213 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
214 FS::BindBlendInfo(pass, blend_uniform);
216 frame_info.mvp = pass.GetOrthographicTransform() *
220 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
221 VS::BindFrameInfo(pass, uniform_view);
223 return pass.Draw().ok();
226 fml::StatusOr<RenderTarget> render_target = renderer.
MakeSubpass(
227 "Advanced Blend Filter",
ISize(subpass_coverage.
GetSize()), callback);
228 if (!render_target.ok()) {
234 .
texture = render_target.value().GetRenderTargetTexture(),
239 .sampler_descriptor = {},
242 : dst_snapshot->opacity) *
243 alpha.value_or(1.0)},
247 std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
248 const std::shared_ptr<FilterInput>& input,
249 const ContentContext& renderer,
250 const Entity& entity,
251 const Rect& coverage,
252 Color foreground_color,
254 std::optional<Scalar> alpha,
257 input->GetSnapshot(
"ForegroundAdvancedBlend", renderer, entity);
258 if (!dst_snapshot.has_value()) {
262 RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
263 blend_mode, alpha, absorb_opacity](
264 const ContentContext& renderer,
265 const Entity& entity, RenderPass& pass) ->
bool {
269 auto& host_buffer = renderer.GetTransientsBuffer();
271 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
272 if (!maybe_dst_uvs.has_value()) {
275 auto dst_uvs = maybe_dst_uvs.value();
277 auto size = coverage.GetSize();
278 auto origin = coverage.GetOrigin();
279 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
280 vtx_builder.AddVertices({
281 {origin, dst_uvs[0], dst_uvs[0]},
282 {
Point(origin.x + size.width, origin.y), dst_uvs[1], dst_uvs[1]},
283 {
Point(origin.x, origin.y + size.height), dst_uvs[2], dst_uvs[2]},
284 {
Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
287 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
289 #ifdef IMPELLER_DEBUG
290 pass.SetCommandLabel(
SPrintF(
"Foreground Advanced Blend Filter (%s)",
292 #endif // IMPELLER_DEBUG
293 pass.SetVertexBuffer(std::move(vtx_buffer));
294 pass.SetStencilReference(entity.GetClipDepth());
298 switch (blend_mode) {
300 pass.SetPipeline(renderer.GetBlendScreenPipeline(options));
303 pass.SetPipeline(renderer.GetBlendOverlayPipeline(options));
306 pass.SetPipeline(renderer.GetBlendDarkenPipeline(options));
309 pass.SetPipeline(renderer.GetBlendLightenPipeline(options));
312 pass.SetPipeline(renderer.GetBlendColorDodgePipeline(options));
315 pass.SetPipeline(renderer.GetBlendColorBurnPipeline(options));
318 pass.SetPipeline(renderer.GetBlendHardLightPipeline(options));
321 pass.SetPipeline(renderer.GetBlendSoftLightPipeline(options));
324 pass.SetPipeline(renderer.GetBlendDifferencePipeline(options));
327 pass.SetPipeline(renderer.GetBlendExclusionPipeline(options));
330 pass.SetPipeline(renderer.GetBlendMultiplyPipeline(options));
333 pass.SetPipeline(renderer.GetBlendHuePipeline(options));
336 pass.SetPipeline(renderer.GetBlendSaturationPipeline(options));
339 pass.SetPipeline(renderer.GetBlendColorPipeline(options));
342 pass.SetPipeline(renderer.GetBlendLuminosityPipeline(options));
348 FS::BlendInfo blend_info;
349 VS::FrameInfo frame_info;
351 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
352 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
356 const std::unique_ptr<const Sampler>& dst_sampler =
357 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
358 dst_sampler_descriptor);
359 FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
360 frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
361 blend_info.dst_input_alpha =
363 ? dst_snapshot->opacity * alpha.value_or(1.0)
366 blend_info.color_factor = 1;
367 blend_info.color = foreground_color;
371 FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
373 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
374 FS::BindBlendInfo(pass, blend_uniform);
376 frame_info.mvp = entity.GetShaderTransform(pass);
378 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
379 VS::BindFrameInfo(pass, uniform_view);
381 return pass.Draw().ok();
384 [coverage](
const Entity& entity) -> std::optional<Rect> {
385 return coverage.TransformBounds(entity.GetTransform());
391 sub_entity.SetContents(std::move(contents));
392 sub_entity.SetClipDepth(entity.GetClipDepth());
401 const Rect& coverage,
403 std::optional<Color> foreground_color,
405 std::optional<Scalar> alpha) {
410 inputs[0]->GetSnapshot(
"PipelineBlend(Dst)", renderer, entity);
411 if (!dst_snapshot.has_value()) {
415 Rect subpass_coverage = coverage;
417 auto coverage_hint = entity.
GetContents()->GetCoverageHint();
419 if (coverage_hint.has_value()) {
420 auto maybe_subpass_coverage =
422 if (!maybe_subpass_coverage.has_value()) {
426 subpass_coverage = *maybe_subpass_coverage;
434 #ifdef IMPELLER_DEBUG
435 pass.SetCommandLabel(
437 #endif // IMPELLER_DEBUG
441 auto add_blend_command = [&](std::optional<Snapshot> input) {
442 if (!input.has_value()) {
445 auto input_coverage = input->GetCoverage();
446 if (!input_coverage.has_value()) {
450 const std::unique_ptr<const Sampler>& sampler =
451 renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
452 input->sampler_descriptor);
453 FS::BindTextureSamplerSrc(pass, input->texture, sampler);
455 auto size = input->texture->GetSize();
461 {
Point(size.width, size.height),
Point(1, 1)},
465 VS::FrameInfo frame_info;
466 frame_info.mvp = pass.GetOrthographicTransform() *
469 frame_info.texture_sampler_y_coord_scale =
470 input->texture->GetYCoordScale();
472 FS::FragInfo frag_info;
473 frag_info.input_alpha =
477 FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
478 VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
480 return pass.Draw().ok();
486 if (!add_blend_command(dst_snapshot)) {
492 if (inputs.size() >= 2) {
493 options.blend_mode = blend_mode;
496 for (
auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
498 auto src_input = texture_i->get()->GetSnapshot(
"PipelineBlend(Src)",
500 if (!add_blend_command(src_input)) {
508 if (foreground_color.has_value()) {
509 auto contents = std::make_shared<SolidColorContents>();
510 contents->SetGeometry(
512 contents->SetColor(foreground_color.value());
517 if (!foreground_entity.
Render(renderer, pass)) {
525 fml::StatusOr<RenderTarget> render_target = renderer.
MakeSubpass(
526 "Pipeline Blend Filter",
ISize(subpass_coverage.
GetSize()), callback);
528 if (!render_target.ok()) {
534 .
texture = render_target.value().GetRenderTargetTexture(),
539 .sampler_descriptor = {},
542 : dst_snapshot->opacity) *
543 alpha.value_or(1.0)},
547 #define BLEND_CASE(mode) \
548 case BlendMode::k##mode: \
549 advanced_blend_proc_ = \
550 [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
551 const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
552 std::optional<Color> fg_color, \
553 ColorFilterContents::AbsorbOpacity absorb_opacity, \
554 std::optional<Scalar> alpha) { \
555 PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
556 return AdvancedBlend<Blend##mode##Pipeline>( \
557 inputs, renderer, entity, coverage, blend_mode, fg_color, \
558 absorb_opacity, p, alpha); \
564 VALIDATION_LOG <<
"Invalid blend mode " <<
static_cast<int>(blend_mode)
565 <<
" assigned to BlendFilterContents.";
568 blend_mode_ = blend_mode;
571 switch (blend_mode) {
594 foreground_color_ = color;
597 std::optional<Entity> BlendFilterContents::RenderFilter(
601 const Matrix& effect_transform,
602 const Rect& coverage,
603 const std::optional<Rect>& coverage_hint)
const {
604 if (inputs.empty()) {
608 if (inputs.size() == 1 && !foreground_color_.has_value()) {
615 return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
620 if (inputs.size() == 1 && foreground_color_.has_value() &&
622 return CreateForegroundAdvancedBlend(
623 inputs[0], renderer, entity, coverage, foreground_color_.value(),
626 return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,