69 using PipelineProc = std::shared_ptr<Pipeline<PipelineDescriptor>> (
72 template <
typename TPipeline>
79 std::optional<Color> foreground_color,
82 std::optional<Scalar> alpha) {
83 using VS =
typename TPipeline::VertexShader;
84 using FS =
typename TPipeline::FragmentShader;
90 const size_t total_inputs =
91 inputs.size() + (foreground_color.has_value() ? 1 : 0);
92 if (total_inputs < 2) {
97 inputs[0]->GetSnapshot(
"AdvancedBlend(Dst)", renderer, entity);
98 if (!dst_snapshot.has_value()) {
101 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
102 if (!maybe_dst_uvs.has_value()) {
105 auto dst_uvs = maybe_dst_uvs.value();
107 std::optional<Snapshot> src_snapshot;
108 std::array<Point, 4> src_uvs;
109 if (!foreground_color.has_value()) {
111 inputs[1]->GetSnapshot(
"AdvancedBlend(Src)", renderer, entity);
112 if (!src_snapshot.has_value()) {
113 if (!dst_snapshot.has_value()) {
119 auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage);
120 if (!maybe_src_uvs.has_value()) {
121 if (!dst_snapshot.has_value()) {
127 src_uvs = maybe_src_uvs.value();
130 Rect subpass_coverage = coverage;
132 auto coverage_hint = entity.
GetContents()->GetCoverageHint();
134 if (coverage_hint.has_value()) {
135 auto maybe_subpass_coverage =
137 if (!maybe_subpass_coverage.has_value()) {
141 subpass_coverage = *maybe_subpass_coverage;
151 auto& host_buffer = pass.GetTransientsBuffer();
153 auto size = pass.GetRenderTargetSize();
156 {
Point(0, 0), dst_uvs[0], src_uvs[0]},
157 {
Point(size.width, 0), dst_uvs[1], src_uvs[1]},
158 {
Point(size.width, size.height), dst_uvs[3], src_uvs[3]},
159 {
Point(0, 0), dst_uvs[0], src_uvs[0]},
160 {
Point(size.width, size.height), dst_uvs[3], src_uvs[3]},
161 {
Point(0, size.height), dst_uvs[2], src_uvs[2]},
167 std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
168 std::invoke(pipeline_proc, renderer, options);
176 typename FS::BlendInfo blend_info;
177 typename VS::FrameInfo frame_info;
179 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
184 auto dst_sampler = renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
185 dst_sampler_descriptor);
186 FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
187 frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
188 blend_info.dst_input_alpha =
190 ? dst_snapshot->opacity
193 if (foreground_color.has_value()) {
194 blend_info.color_factor = 1;
195 blend_info.color = foreground_color.value();
199 FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler);
201 auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
206 auto src_sampler = 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(cmd, 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(cmd, blend_uniform);
220 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
221 VS::BindFrameInfo(cmd, uniform_view);
222 pass.AddCommand(std::move(cmd));
228 "Advanced Blend Filter",
ISize(subpass_coverage.
size), callback);
240 .sampler_descriptor = {},
243 : dst_snapshot->opacity) *
244 alpha.value_or(1.0)},
248 std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
249 const std::shared_ptr<FilterInput>& input,
250 const ContentContext& renderer,
251 const Entity& entity,
252 const Rect& coverage,
253 Color foreground_color,
255 std::optional<Scalar> alpha,
258 input->GetSnapshot(
"ForegroundAdvancedBlend", renderer, entity);
259 if (!dst_snapshot.has_value()) {
263 RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
264 blend_mode, alpha, absorb_opacity](
265 const ContentContext& renderer,
266 const Entity& entity, RenderPass& pass) ->
bool {
270 auto& host_buffer = pass.GetTransientsBuffer();
272 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
273 if (!maybe_dst_uvs.has_value()) {
276 auto dst_uvs = maybe_dst_uvs.value();
278 auto size = coverage.size;
279 auto origin = coverage.origin;
280 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
281 vtx_builder.AddVertices({
282 {origin, dst_uvs[0], dst_uvs[0]},
283 {
Point(origin.x + size.width, origin.y), dst_uvs[1], dst_uvs[1]},
284 {
Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
286 {origin, dst_uvs[0], dst_uvs[0]},
287 {
Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
289 {
Point(origin.x, origin.y + size.height), dst_uvs[2], dst_uvs[2]},
291 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
296 cmd.BindVertices(vtx_buffer);
297 cmd.stencil_reference = entity.GetStencilDepth();
300 switch (blend_mode) {
302 cmd.pipeline = renderer.GetBlendScreenPipeline(options);
305 cmd.pipeline = renderer.GetBlendOverlayPipeline(options);
308 cmd.pipeline = renderer.GetBlendDarkenPipeline(options);
311 cmd.pipeline = renderer.GetBlendLightenPipeline(options);
314 cmd.pipeline = renderer.GetBlendColorDodgePipeline(options);
317 cmd.pipeline = renderer.GetBlendColorBurnPipeline(options);
320 cmd.pipeline = renderer.GetBlendHardLightPipeline(options);
323 cmd.pipeline = renderer.GetBlendSoftLightPipeline(options);
326 cmd.pipeline = renderer.GetBlendDifferencePipeline(options);
329 cmd.pipeline = renderer.GetBlendExclusionPipeline(options);
332 cmd.pipeline = renderer.GetBlendMultiplyPipeline(options);
335 cmd.pipeline = renderer.GetBlendHuePipeline(options);
338 cmd.pipeline = renderer.GetBlendSaturationPipeline(options);
341 cmd.pipeline = renderer.GetBlendColorPipeline(options);
344 cmd.pipeline = renderer.GetBlendLuminosityPipeline(options);
350 FS::BlendInfo blend_info;
351 VS::FrameInfo frame_info;
353 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
354 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
358 auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
359 dst_sampler_descriptor);
360 FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
361 frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
362 blend_info.dst_input_alpha =
364 ? dst_snapshot->opacity * alpha.value_or(1.0)
367 blend_info.color_factor = 1;
368 blend_info.color = foreground_color;
372 FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler);
374 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
375 FS::BindBlendInfo(cmd, blend_uniform);
378 entity.GetTransformation();
380 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
381 VS::BindFrameInfo(cmd, uniform_view);
383 return pass.AddCommand(std::move(cmd));
386 [coverage](
const Entity& entity) -> std::optional<Rect> {
387 return coverage.TransformBounds(entity.GetTransformation());
393 sub_entity.SetContents(std::move(contents));
394 sub_entity.SetStencilDepth(entity.GetStencilDepth());
399 std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
400 const std::shared_ptr<FilterInput>& input,
401 const ContentContext& renderer,
402 const Entity& entity,
403 const Rect& coverage,
404 Color foreground_color,
406 std::optional<Scalar> alpha,
413 auto contents = std::make_shared<SolidColorContents>();
415 contents->SetColor(foreground_color);
417 Entity foreground_entity;
418 foreground_entity.SetBlendMode(entity.GetBlendMode());
419 foreground_entity.SetStencilDepth(entity.GetStencilDepth());
420 foreground_entity.SetContents(std::move(contents));
421 return foreground_entity;
425 input->GetSnapshot(
"ForegroundPorterDuffBlend", renderer, entity);
426 if (!dst_snapshot.has_value()) {
432 entity.GetStencilDepth());
435 RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
436 blend_mode, absorb_opacity, alpha](
437 const ContentContext& renderer,
438 const Entity& entity, RenderPass& pass) ->
bool {
442 auto& host_buffer = pass.GetTransientsBuffer();
444 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
445 if (!maybe_dst_uvs.has_value()) {
448 auto dst_uvs = maybe_dst_uvs.value();
450 auto size = coverage.size;
451 auto origin = coverage.origin;
452 auto color = foreground_color.Premultiply();
453 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
454 vtx_builder.AddVertices({
455 {origin, dst_uvs[0], color},
456 {
Point(origin.x + size.width, origin.y), dst_uvs[1], color},
457 {
Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
459 {origin, dst_uvs[0], color},
460 {
Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
462 {
Point(origin.x, origin.y + size.height), dst_uvs[2], color},
464 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
469 cmd.BindVertices(vtx_buffer);
470 cmd.stencil_reference = entity.GetStencilDepth();
472 cmd.pipeline = renderer.GetPorterDuffBlendPipeline(options);
474 FS::FragInfo frag_info;
475 VS::FrameInfo frame_info;
477 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
478 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
482 auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
483 dst_sampler_descriptor);
484 FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
485 frame_info.texture_sampler_y_coord_scale =
486 dst_snapshot->texture->GetYCoordScale();
488 frag_info.input_alpha =
490 ? dst_snapshot->opacity * alpha.value_or(1.0)
492 frag_info.output_alpha = 1.0;
494 auto blend_coefficients =
496 frag_info.src_coeff = blend_coefficients[0];
497 frag_info.src_coeff_dst_alpha = blend_coefficients[1];
498 frag_info.dst_coeff = blend_coefficients[2];
499 frag_info.dst_coeff_src_alpha = blend_coefficients[3];
500 frag_info.dst_coeff_src_color = blend_coefficients[4];
502 FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
505 entity.GetTransformation();
507 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
508 VS::BindFrameInfo(cmd, uniform_view);
510 return pass.AddCommand(std::move(cmd));
514 [coverage](
const Entity& entity) -> std::optional<Rect> {
515 return coverage.TransformBounds(entity.GetTransformation());
521 sub_entity.SetContents(std::move(contents));
522 sub_entity.SetStencilDepth(entity.GetStencilDepth());
531 const Rect& coverage,
533 std::optional<Color> foreground_color,
535 std::optional<Scalar> alpha) {
540 inputs[0]->GetSnapshot(
"PipelineBlend(Dst)", renderer, entity);
541 if (!dst_snapshot.has_value()) {
545 Rect subpass_coverage = coverage;
547 auto coverage_hint = entity.
GetContents()->GetCoverageHint();
549 if (coverage_hint.has_value()) {
550 auto maybe_subpass_coverage =
552 if (!maybe_subpass_coverage.has_value()) {
556 subpass_coverage = *maybe_subpass_coverage;
562 auto& host_buffer = pass.GetTransientsBuffer();
569 auto add_blend_command = [&](std::optional<Snapshot> input) {
570 if (!input.has_value()) {
573 auto input_coverage = input->GetCoverage();
574 if (!input_coverage.has_value()) {
578 auto sampler = renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
579 input->sampler_descriptor);
580 FS::BindTextureSamplerSrc(cmd, input->texture, sampler);
582 auto size = input->texture->GetSize();
587 {
Point(size.width, size.height),
Point(1, 1)},
589 {
Point(size.width, size.height),
Point(1, 1)},
595 VS::FrameInfo frame_info;
599 frame_info.texture_sampler_y_coord_scale =
600 input->texture->GetYCoordScale();
602 FS::FragInfo frag_info;
603 frag_info.input_alpha =
607 FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
608 VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
610 pass.AddCommand(std::move(cmd));
617 if (!add_blend_command(dst_snapshot)) {
623 if (inputs.size() >= 2) {
624 options.blend_mode = blend_mode;
627 for (
auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
629 auto src_input = texture_i->get()->GetSnapshot(
"PipelineBlend(Src)",
631 if (!add_blend_command(src_input)) {
639 if (foreground_color.has_value()) {
640 auto contents = std::make_shared<SolidColorContents>();
641 contents->SetGeometry(
643 contents->SetColor(foreground_color.value());
648 if (!foreground_entity.
Render(renderer, pass)) {
657 "Pipeline Blend Filter",
ISize(subpass_coverage.
size), callback);
670 .sampler_descriptor = {},
673 : dst_snapshot->opacity) *
674 alpha.value_or(1.0)},
678 #define BLEND_CASE(mode) \
679 case BlendMode::k##mode: \
680 advanced_blend_proc_ = \
681 [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
682 const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
683 std::optional<Color> fg_color, \
684 ColorFilterContents::AbsorbOpacity absorb_opacity, \
685 std::optional<Scalar> alpha) { \
686 PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
687 return AdvancedBlend<Blend##mode##Pipeline>( \
688 inputs, renderer, entity, coverage, blend_mode, fg_color, \
689 absorb_opacity, p, alpha); \
695 VALIDATION_LOG <<
"Invalid blend mode " <<
static_cast<int>(blend_mode)
696 <<
" assigned to BlendFilterContents.";
699 blend_mode_ = blend_mode;
702 switch (blend_mode) {
725 foreground_color_ = color;
728 std::optional<Entity> BlendFilterContents::RenderFilter(
732 const Matrix& effect_transform,
733 const Rect& coverage,
734 const std::optional<Rect>& coverage_hint)
const {
735 if (inputs.empty()) {
739 if (inputs.size() == 1 && !foreground_color_.has_value()) {
746 if (inputs.size() == 1 && foreground_color_.has_value() &&
748 return CreateForegroundPorterDuffBlend(
749 inputs[0], renderer, entity, coverage, foreground_color_.value(),
752 return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
757 if (inputs.size() == 1 && foreground_color_.has_value() &&
759 return CreateForegroundAdvancedBlend(
760 inputs[0], renderer, entity, coverage, foreground_color_.value(),
763 return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,