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;
149 auto& host_buffer = pass.GetTransientsBuffer();
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);
173 typename FS::BlendInfo blend_info;
174 typename VS::FrameInfo frame_info;
176 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
181 auto dst_sampler = renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
182 dst_sampler_descriptor);
183 FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
184 frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
185 blend_info.dst_input_alpha =
187 ? dst_snapshot->opacity
190 if (foreground_color.has_value()) {
191 blend_info.color_factor = 1;
192 blend_info.color = foreground_color.value();
196 FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler);
198 auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
203 auto src_sampler = renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
204 src_sampler_descriptor);
205 blend_info.color_factor = 0;
206 blend_info.src_input_alpha = src_snapshot->opacity;
207 FS::BindTextureSamplerSrc(cmd, src_snapshot->texture, src_sampler);
208 frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale();
210 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
211 FS::BindBlendInfo(cmd, blend_uniform);
213 frame_info.mvp = pass.GetOrthographicTransform() *
217 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
218 VS::BindFrameInfo(cmd, uniform_view);
219 pass.AddCommand(std::move(cmd));
224 fml::StatusOr<RenderTarget> render_target = renderer.
MakeSubpass(
225 "Advanced Blend Filter",
ISize(subpass_coverage.
GetSize()), callback);
226 if (!render_target.ok()) {
232 .
texture = render_target.value().GetRenderTargetTexture(),
237 .sampler_descriptor = {},
240 : dst_snapshot->opacity) *
241 alpha.value_or(1.0)},
245 std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
246 const std::shared_ptr<FilterInput>& input,
247 const ContentContext& renderer,
248 const Entity& entity,
249 const Rect& coverage,
250 Color foreground_color,
252 std::optional<Scalar> alpha,
255 input->GetSnapshot(
"ForegroundAdvancedBlend", renderer, entity);
256 if (!dst_snapshot.has_value()) {
260 RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
261 blend_mode, alpha, absorb_opacity](
262 const ContentContext& renderer,
263 const Entity& entity, RenderPass& pass) ->
bool {
267 auto& host_buffer = pass.GetTransientsBuffer();
269 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
270 if (!maybe_dst_uvs.has_value()) {
273 auto dst_uvs = maybe_dst_uvs.value();
275 auto size = coverage.GetSize();
276 auto origin = coverage.GetOrigin();
277 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
278 vtx_builder.AddVertices({
279 {origin, dst_uvs[0], dst_uvs[0]},
280 {
Point(origin.x + size.width, origin.y), dst_uvs[1], dst_uvs[1]},
281 {
Point(origin.x, origin.y + size.height), dst_uvs[2], dst_uvs[2]},
282 {
Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
285 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
290 cmd.BindVertices(std::move(vtx_buffer));
291 cmd.stencil_reference = entity.GetClipDepth();
295 switch (blend_mode) {
297 cmd.pipeline = renderer.GetBlendScreenPipeline(options);
300 cmd.pipeline = renderer.GetBlendOverlayPipeline(options);
303 cmd.pipeline = renderer.GetBlendDarkenPipeline(options);
306 cmd.pipeline = renderer.GetBlendLightenPipeline(options);
309 cmd.pipeline = renderer.GetBlendColorDodgePipeline(options);
312 cmd.pipeline = renderer.GetBlendColorBurnPipeline(options);
315 cmd.pipeline = renderer.GetBlendHardLightPipeline(options);
318 cmd.pipeline = renderer.GetBlendSoftLightPipeline(options);
321 cmd.pipeline = renderer.GetBlendDifferencePipeline(options);
324 cmd.pipeline = renderer.GetBlendExclusionPipeline(options);
327 cmd.pipeline = renderer.GetBlendMultiplyPipeline(options);
330 cmd.pipeline = renderer.GetBlendHuePipeline(options);
333 cmd.pipeline = renderer.GetBlendSaturationPipeline(options);
336 cmd.pipeline = renderer.GetBlendColorPipeline(options);
339 cmd.pipeline = renderer.GetBlendLuminosityPipeline(options);
345 FS::BlendInfo blend_info;
346 VS::FrameInfo frame_info;
348 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
349 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
353 auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
354 dst_sampler_descriptor);
355 FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
356 frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
357 blend_info.dst_input_alpha =
359 ? dst_snapshot->opacity * alpha.value_or(1.0)
362 blend_info.color_factor = 1;
363 blend_info.color = foreground_color;
367 FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler);
369 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
370 FS::BindBlendInfo(cmd, blend_uniform);
372 frame_info.mvp = pass.GetOrthographicTransform() * entity.GetTransform();
374 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
375 VS::BindFrameInfo(cmd, uniform_view);
377 return pass.AddCommand(std::move(cmd));
380 [coverage](
const Entity& entity) -> std::optional<Rect> {
381 return coverage.TransformBounds(entity.GetTransform());
387 sub_entity.SetContents(std::move(contents));
388 sub_entity.SetClipDepth(entity.GetClipDepth());
393 std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
394 const std::shared_ptr<FilterInput>& input,
395 const ContentContext& renderer,
396 const Entity& entity,
397 const Rect& coverage,
398 Color foreground_color,
400 std::optional<Scalar> alpha,
407 auto contents = std::make_shared<SolidColorContents>();
409 contents->SetColor(foreground_color);
411 Entity foreground_entity;
412 foreground_entity.SetBlendMode(entity.GetBlendMode());
413 foreground_entity.SetClipDepth(entity.GetClipDepth());
414 foreground_entity.SetContents(std::move(contents));
415 return foreground_entity;
419 input->GetSnapshot(
"ForegroundPorterDuffBlend", renderer, entity);
420 if (!dst_snapshot.has_value()) {
426 entity.GetClipDepth());
429 RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
430 blend_mode, absorb_opacity, alpha](
431 const ContentContext& renderer,
432 const Entity& entity, RenderPass& pass) ->
bool {
436 auto& host_buffer = pass.GetTransientsBuffer();
438 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
439 if (!maybe_dst_uvs.has_value()) {
442 auto dst_uvs = maybe_dst_uvs.value();
444 auto size = coverage.GetSize();
445 auto origin = coverage.GetOrigin();
446 auto color = foreground_color.Premultiply();
447 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
448 vtx_builder.AddVertices({
449 {origin, dst_uvs[0], color},
450 {
Point(origin.x + size.width, origin.y), dst_uvs[1], color},
451 {
Point(origin.x, origin.y + size.height), dst_uvs[2], color},
452 {
Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
455 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
460 cmd.BindVertices(std::move(vtx_buffer));
461 cmd.stencil_reference = entity.GetClipDepth();
464 cmd.pipeline = renderer.GetPorterDuffBlendPipeline(options);
466 FS::FragInfo frag_info;
467 VS::FrameInfo frame_info;
469 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
470 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
474 auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
475 dst_sampler_descriptor);
476 FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
477 frame_info.texture_sampler_y_coord_scale =
478 dst_snapshot->texture->GetYCoordScale();
480 frag_info.input_alpha =
482 ? dst_snapshot->opacity * alpha.value_or(1.0)
484 frag_info.output_alpha = 1.0;
486 auto blend_coefficients =
488 frag_info.src_coeff = blend_coefficients[0];
489 frag_info.src_coeff_dst_alpha = blend_coefficients[1];
490 frag_info.dst_coeff = blend_coefficients[2];
491 frag_info.dst_coeff_src_alpha = blend_coefficients[3];
492 frag_info.dst_coeff_src_color = blend_coefficients[4];
494 FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
496 frame_info.mvp = pass.GetOrthographicTransform() * entity.GetTransform();
498 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
499 VS::BindFrameInfo(cmd, uniform_view);
501 return pass.AddCommand(std::move(cmd));
505 [coverage](
const Entity& entity) -> std::optional<Rect> {
506 return coverage.TransformBounds(entity.GetTransform());
512 sub_entity.SetContents(std::move(contents));
513 sub_entity.SetClipDepth(entity.GetClipDepth());
522 const Rect& coverage,
524 std::optional<Color> foreground_color,
526 std::optional<Scalar> alpha) {
531 inputs[0]->GetSnapshot(
"PipelineBlend(Dst)", renderer, entity);
532 if (!dst_snapshot.has_value()) {
536 Rect subpass_coverage = coverage;
538 auto coverage_hint = entity.
GetContents()->GetCoverageHint();
540 if (coverage_hint.has_value()) {
541 auto maybe_subpass_coverage =
543 if (!maybe_subpass_coverage.has_value()) {
547 subpass_coverage = *maybe_subpass_coverage;
553 auto& host_buffer = pass.GetTransientsBuffer();
561 auto add_blend_command = [&](std::optional<Snapshot> input) {
562 if (!input.has_value()) {
565 auto input_coverage = input->GetCoverage();
566 if (!input_coverage.has_value()) {
570 auto sampler = renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
571 input->sampler_descriptor);
572 FS::BindTextureSamplerSrc(cmd, input->texture, sampler);
574 auto size = input->texture->GetSize();
580 {
Point(size.width, size.height),
Point(1, 1)},
584 VS::FrameInfo frame_info;
585 frame_info.mvp = pass.GetOrthographicTransform() *
588 frame_info.texture_sampler_y_coord_scale =
589 input->texture->GetYCoordScale();
591 FS::FragInfo frag_info;
592 frag_info.input_alpha =
596 FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
597 VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
599 pass.AddCommand(std::move(cmd));
606 if (!add_blend_command(dst_snapshot)) {
612 if (inputs.size() >= 2) {
613 options.blend_mode = blend_mode;
616 for (
auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
618 auto src_input = texture_i->get()->GetSnapshot(
"PipelineBlend(Src)",
620 if (!add_blend_command(src_input)) {
628 if (foreground_color.has_value()) {
629 auto contents = std::make_shared<SolidColorContents>();
630 contents->SetGeometry(
632 contents->SetColor(foreground_color.value());
637 if (!foreground_entity.
Render(renderer, pass)) {
645 fml::StatusOr<RenderTarget> render_target = renderer.
MakeSubpass(
646 "Pipeline Blend Filter",
ISize(subpass_coverage.
GetSize()), callback);
648 if (!render_target.ok()) {
654 .
texture = render_target.value().GetRenderTargetTexture(),
659 .sampler_descriptor = {},
662 : dst_snapshot->opacity) *
663 alpha.value_or(1.0)},
667 #define BLEND_CASE(mode) \
668 case BlendMode::k##mode: \
669 advanced_blend_proc_ = \
670 [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
671 const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
672 std::optional<Color> fg_color, \
673 ColorFilterContents::AbsorbOpacity absorb_opacity, \
674 std::optional<Scalar> alpha) { \
675 PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
676 return AdvancedBlend<Blend##mode##Pipeline>( \
677 inputs, renderer, entity, coverage, blend_mode, fg_color, \
678 absorb_opacity, p, alpha); \
684 VALIDATION_LOG <<
"Invalid blend mode " <<
static_cast<int>(blend_mode)
685 <<
" assigned to BlendFilterContents.";
688 blend_mode_ = blend_mode;
691 switch (blend_mode) {
714 foreground_color_ = color;
717 std::optional<Entity> BlendFilterContents::RenderFilter(
721 const Matrix& effect_transform,
722 const Rect& coverage,
723 const std::optional<Rect>& coverage_hint)
const {
724 if (inputs.empty()) {
728 if (inputs.size() == 1 && !foreground_color_.has_value()) {
735 if (inputs.size() == 1 && foreground_color_.has_value() &&
737 return CreateForegroundPorterDuffBlend(
738 inputs[0], renderer, entity, coverage, foreground_color_.value(),
741 return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
746 if (inputs.size() == 1 && foreground_color_.has_value() &&
748 return CreateForegroundAdvancedBlend(
749 inputs[0], renderer, entity, coverage, foreground_color_.value(),
752 return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,