| Flutter Impeller |

blend_filter_contents.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "impeller/entity/contents/filters/blend_filter_contents.h"
6
7 #include <array>
8 #include <memory>
9 #include <optional>
10
11 #include "impeller/base/strings.h"
12 #include "impeller/core/formats.h"
13 #include "impeller/entity/contents/anonymous_contents.h"
14 #include "impeller/entity/contents/content_context.h"
15 #include "impeller/entity/contents/contents.h"
16 #include "impeller/entity/contents/filters/color_filter_contents.h"
17 #include "impeller/entity/contents/filters/inputs/filter_input.h"
18 #include "impeller/entity/contents/solid_color_contents.h"
19 #include "impeller/entity/entity.h"
20 #include "impeller/geometry/color.h"
21 #include "impeller/geometry/path_builder.h"
22 #include "impeller/renderer/render_pass.h"
23 #include "impeller/renderer/sampler_library.h"
24 #include "impeller/renderer/snapshot.h"
25
26 namespace impeller {
27
28 std::optional<BlendMode> InvertPorterDuffBlend(BlendMode blend_mode) {
29 switch (blend_mode) {
30 case BlendMode::kClear:
31 return BlendMode::kClear;
32 case BlendMode::kSource:
33 return BlendMode::kDestination;
34 case BlendMode::kDestination:
35 return BlendMode::kSource;
36 case BlendMode::kSourceOver:
37 return BlendMode::kDestinationOver;
38 case BlendMode::kDestinationOver:
39 return BlendMode::kSourceOver;
40 case BlendMode::kSourceIn:
41 return BlendMode::kDestinationIn;
42 case BlendMode::kDestinationIn:
43 return BlendMode::kSourceIn;
44 case BlendMode::kSourceOut:
45 return BlendMode::kDestinationOut;
46 case BlendMode::kDestinationOut:
47 return BlendMode::kSourceOut;
48 case BlendMode::kSourceATop:
49 return BlendMode::kDestinationATop;
50 case BlendMode::kDestinationATop:
51 return BlendMode::kSourceATop;
52 case BlendMode::kXor:
53 return BlendMode::kXor;
54 case BlendMode::kPlus:
55 return BlendMode::kPlus;
56 case BlendMode::kModulate:
57 return BlendMode::kModulate;
58 default:
59 return std::nullopt;
60 }
61 }
62
63 BlendFilterContents::BlendFilterContents() {
64 SetBlendMode(BlendMode::kSourceOver);
65 }
66
67 BlendFilterContents::~BlendFilterContents() = default;
68
69 using PipelineProc = std::shared_ptr<Pipeline<PipelineDescriptor>> (
70 ContentContext::*)(ContentContextOptions) const;
71
72 template <typename TPipeline>
73 static std::optional<Entity> AdvancedBlend(
74 const FilterInput::Vector& inputs,
75 const ContentContext& renderer,
76 const Entity& entity,
77 const Rect& coverage,
78 BlendMode blend_mode,
79 std::optional<Color> foreground_color,
80 ColorFilterContents::AbsorbOpacity absorb_opacity,
81 PipelineProc pipeline_proc,
82 std::optional<Scalar> alpha) {
83 using VS = typename TPipeline::VertexShader;
84 using FS = typename TPipeline::FragmentShader;
85
86 //----------------------------------------------------------------------------
87 /// Handle inputs.
88 ///
89
90 const size_t total_inputs =
91 inputs.size() + (foreground_color.has_value() ? 1 : 0);
92 if (total_inputs < 2) {
93 return std::nullopt;
94 }
95
96 auto dst_snapshot =
97 inputs[0]->GetSnapshot("AdvancedBlend(Dst)", renderer, entity);
98 if (!dst_snapshot.has_value()) {
99 return std::nullopt;
100 }
101 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
102 if (!maybe_dst_uvs.has_value()) {
103 return std::nullopt;
104 }
105 auto dst_uvs = maybe_dst_uvs.value();
106
107 std::optional<Snapshot> src_snapshot;
108 std::array<Point, 4> src_uvs;
109 if (!foreground_color.has_value()) {
110 src_snapshot =
111 inputs[1]->GetSnapshot("AdvancedBlend(Src)", renderer, entity);
112 if (!src_snapshot.has_value()) {
113 if (!dst_snapshot.has_value()) {
114 return std::nullopt;
115 }
116 return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(),
117 entity.GetStencilDepth());
118 }
119 auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage);
120 if (!maybe_src_uvs.has_value()) {
121 if (!dst_snapshot.has_value()) {
122 return std::nullopt;
123 }
124 return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(),
125 entity.GetStencilDepth());
126 }
127 src_uvs = maybe_src_uvs.value();
128 }
129
130 Rect subpass_coverage = coverage;
131 if (entity.GetContents()) {
132 auto coverage_hint = entity.GetContents()->GetCoverageHint();
133
134 if (coverage_hint.has_value()) {
135 auto maybe_subpass_coverage =
136 subpass_coverage.Intersection(*coverage_hint);
137 if (!maybe_subpass_coverage.has_value()) {
138 return std::nullopt; // Nothing to render.
139 }
140
141 subpass_coverage = *maybe_subpass_coverage;
142 }
143 }
144
145 //----------------------------------------------------------------------------
146 /// Render to texture.
147 ///
148
149 ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
150 RenderPass& pass) {
151 auto& host_buffer = pass.GetTransientsBuffer();
152
153 auto size = pass.GetRenderTargetSize();
154 VertexBufferBuilder<typename VS::PerVertexData> vtx_builder;
155 vtx_builder.AddVertices({
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]},
162 });
163 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
164
165 auto options = OptionsFromPass(pass);
166 options.blend_mode = BlendMode::kSource;
167 std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
168 std::invoke(pipeline_proc, renderer, options);
169
170 Command cmd;
171 DEBUG_COMMAND_INFO(cmd, SPrintF("Advanced Blend Filter (%s)",
172 BlendModeToString(blend_mode)));
173 cmd.BindVertices(vtx_buffer);
174 cmd.pipeline = std::move(pipeline);
175
176 typename FS::BlendInfo blend_info;
177 typename VS::FrameInfo frame_info;
178
179 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
180 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
181 dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
182 dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
183 }
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 =
189 absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
190 ? dst_snapshot->opacity
191 : 1.0;
192
193 if (foreground_color.has_value()) {
194 blend_info.color_factor = 1;
195 blend_info.color = foreground_color.value();
196 // This texture will not be sampled from due to the color factor. But
197 // this is present so that validation doesn't trip on a missing
198 // binding.
199 FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler);
200 } else {
201 auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
202 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
203 src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
204 src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
205 }
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();
212 }
213 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
214 FS::BindBlendInfo(cmd, blend_uniform);
215
216 frame_info.mvp =
217 Matrix::MakeOrthographic(size) *
218 Matrix::MakeTranslation(coverage.origin - subpass_coverage.origin);
219
220 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
221 VS::BindFrameInfo(cmd, uniform_view);
222 pass.AddCommand(std::move(cmd));
223
224 return true;
225 };
226
227 auto out_texture = renderer.MakeSubpass(
228 "Advanced Blend Filter", ISize(subpass_coverage.size), callback);
229 if (!out_texture) {
230 return std::nullopt;
231 }
232
233 return Entity::FromSnapshot(
234 Snapshot{
235 .texture = out_texture,
236 .transform = Matrix::MakeTranslation(subpass_coverage.origin),
237 // Since we absorbed the transform of the inputs and used the
238 // respective snapshot sampling modes when blending, pass on
239 // the default NN clamp sampler.
240 .sampler_descriptor = {},
241 .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
242 ? 1.0f
243 : dst_snapshot->opacity) *
244 alpha.value_or(1.0)},
245 entity.GetBlendMode(), entity.GetStencilDepth());
246 }
247
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,
254 BlendMode blend_mode,
255 std::optional<Scalar> alpha,
256 ColorFilterContents::AbsorbOpacity absorb_opacity) const {
257 auto dst_snapshot =
258 input->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
259 if (!dst_snapshot.has_value()) {
260 return std::nullopt;
261 }
262
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 {
267 using VS = BlendScreenPipeline::VertexShader;
268 using FS = BlendScreenPipeline::FragmentShader;
269
270 auto& host_buffer = pass.GetTransientsBuffer();
271
272 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
273 if (!maybe_dst_uvs.has_value()) {
274 return false;
275 }
276 auto dst_uvs = maybe_dst_uvs.value();
277
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],
285 dst_uvs[3]},
286 {origin, dst_uvs[0], dst_uvs[0]},
287 {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
288 dst_uvs[3]},
289 {Point(origin.x, origin.y + size.height), dst_uvs[2], dst_uvs[2]},
290 });
291 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
292
293 Command cmd;
294 DEBUG_COMMAND_INFO(cmd, SPrintF("Foreground Advanced Blend Filter (%s)",
295 BlendModeToString(blend_mode)));
296 cmd.BindVertices(vtx_buffer);
297 cmd.stencil_reference = entity.GetStencilDepth();
298 auto options = OptionsFromPass(pass);
299
300 switch (blend_mode) {
301 case BlendMode::kScreen:
302 cmd.pipeline = renderer.GetBlendScreenPipeline(options);
303 break;
304 case BlendMode::kOverlay:
305 cmd.pipeline = renderer.GetBlendOverlayPipeline(options);
306 break;
307 case BlendMode::kDarken:
308 cmd.pipeline = renderer.GetBlendDarkenPipeline(options);
309 break;
310 case BlendMode::kLighten:
311 cmd.pipeline = renderer.GetBlendLightenPipeline(options);
312 break;
313 case BlendMode::kColorDodge:
314 cmd.pipeline = renderer.GetBlendColorDodgePipeline(options);
315 break;
316 case BlendMode::kColorBurn:
317 cmd.pipeline = renderer.GetBlendColorBurnPipeline(options);
318 break;
319 case BlendMode::kHardLight:
320 cmd.pipeline = renderer.GetBlendHardLightPipeline(options);
321 break;
322 case BlendMode::kSoftLight:
323 cmd.pipeline = renderer.GetBlendSoftLightPipeline(options);
324 break;
325 case BlendMode::kDifference:
326 cmd.pipeline = renderer.GetBlendDifferencePipeline(options);
327 break;
328 case BlendMode::kExclusion:
329 cmd.pipeline = renderer.GetBlendExclusionPipeline(options);
330 break;
331 case BlendMode::kMultiply:
332 cmd.pipeline = renderer.GetBlendMultiplyPipeline(options);
333 break;
334 case BlendMode::kHue:
335 cmd.pipeline = renderer.GetBlendHuePipeline(options);
336 break;
337 case BlendMode::kSaturation:
338 cmd.pipeline = renderer.GetBlendSaturationPipeline(options);
339 break;
340 case BlendMode::kColor:
341 cmd.pipeline = renderer.GetBlendColorPipeline(options);
342 break;
343 case BlendMode::kLuminosity:
344 cmd.pipeline = renderer.GetBlendLuminosityPipeline(options);
345 break;
346 default:
347 return false;
348 }
349
350 FS::BlendInfo blend_info;
351 VS::FrameInfo frame_info;
352
353 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
354 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
355 dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
356 dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
357 }
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 =
363 absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
364 ? dst_snapshot->opacity * alpha.value_or(1.0)
365 : 1.0;
366
367 blend_info.color_factor = 1;
368 blend_info.color = foreground_color;
369 // This texture will not be sampled from due to the color factor. But
370 // this is present so that validation doesn't trip on a missing
371 // binding.
372 FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler);
373
374 auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
375 FS::BindBlendInfo(cmd, blend_uniform);
376
377 frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
378 entity.GetTransformation();
379
380 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
381 VS::BindFrameInfo(cmd, uniform_view);
382
383 return pass.AddCommand(std::move(cmd));
384 };
385 CoverageProc coverage_proc =
386 [coverage](const Entity& entity) -> std::optional<Rect> {
387 return coverage.TransformBounds(entity.GetTransformation());
388 };
389
390 auto contents = AnonymousContents::Make(render_proc, coverage_proc);
391
392 Entity sub_entity;
393 sub_entity.SetContents(std::move(contents));
394 sub_entity.SetStencilDepth(entity.GetStencilDepth());
395
396 return sub_entity;
397 }
398
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,
405 BlendMode blend_mode,
406 std::optional<Scalar> alpha,
407 ColorFilterContents::AbsorbOpacity absorb_opacity) const {
408 if (blend_mode == BlendMode::kClear) {
409 return std::nullopt;
410 }
411
412 if (blend_mode == BlendMode::kSource) {
413 auto contents = std::make_shared<SolidColorContents>();
414 contents->SetGeometry(Geometry::MakeRect(coverage));
415 contents->SetColor(foreground_color);
416
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;
422 }
423
424 auto dst_snapshot =
425 input->GetSnapshot("ForegroundPorterDuffBlend", renderer, entity);
426 if (!dst_snapshot.has_value()) {
427 return std::nullopt;
428 }
429
430 if (blend_mode == BlendMode::kDestination) {
431 return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(),
432 entity.GetStencilDepth());
433 }
434
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 {
439 using VS = PorterDuffBlendPipeline::VertexShader;
440 using FS = PorterDuffBlendPipeline::FragmentShader;
441
442 auto& host_buffer = pass.GetTransientsBuffer();
443
444 auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
445 if (!maybe_dst_uvs.has_value()) {
446 return false;
447 }
448 auto dst_uvs = maybe_dst_uvs.value();
449
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],
458 color},
459 {origin, dst_uvs[0], color},
460 {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
461 color},
462 {Point(origin.x, origin.y + size.height), dst_uvs[2], color},
463 });
464 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
465
466 Command cmd;
467 DEBUG_COMMAND_INFO(cmd, SPrintF("Foreground PorterDuff Blend Filter (%s)",
468 BlendModeToString(blend_mode)));
469 cmd.BindVertices(vtx_buffer);
470 cmd.stencil_reference = entity.GetStencilDepth();
471 auto options = OptionsFromPass(pass);
472 cmd.pipeline = renderer.GetPorterDuffBlendPipeline(options);
473
474 FS::FragInfo frag_info;
475 VS::FrameInfo frame_info;
476
477 auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
478 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
479 dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
480 dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
481 }
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();
487
488 frag_info.input_alpha =
489 absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
490 ? dst_snapshot->opacity * alpha.value_or(1.0)
491 : 1.0;
492 frag_info.output_alpha = 1.0;
493
494 auto blend_coefficients =
495 kPorterDuffCoefficients[static_cast<int>(blend_mode)];
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];
501
502 FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
503
504 frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
505 entity.GetTransformation();
506
507 auto uniform_view = host_buffer.EmplaceUniform(frame_info);
508 VS::BindFrameInfo(cmd, uniform_view);
509
510 return pass.AddCommand(std::move(cmd));
511 };
512
513 CoverageProc coverage_proc =
514 [coverage](const Entity& entity) -> std::optional<Rect> {
515 return coverage.TransformBounds(entity.GetTransformation());
516 };
517
518 auto contents = AnonymousContents::Make(render_proc, coverage_proc);
519
520 Entity sub_entity;
521 sub_entity.SetContents(std::move(contents));
522 sub_entity.SetStencilDepth(entity.GetStencilDepth());
523
524 return sub_entity;
525 }
526
527 static std::optional<Entity> PipelineBlend(
528 const FilterInput::Vector& inputs,
529 const ContentContext& renderer,
530 const Entity& entity,
531 const Rect& coverage,
532 BlendMode blend_mode,
533 std::optional<Color> foreground_color,
534 ColorFilterContents::AbsorbOpacity absorb_opacity,
535 std::optional<Scalar> alpha) {
536 using VS = BlendPipeline::VertexShader;
537 using FS = BlendPipeline::FragmentShader;
538
539 auto dst_snapshot =
540 inputs[0]->GetSnapshot("PipelineBlend(Dst)", renderer, entity);
541 if (!dst_snapshot.has_value()) {
542 return std::nullopt; // Nothing to render.
543 }
544
545 Rect subpass_coverage = coverage;
546 if (entity.GetContents()) {
547 auto coverage_hint = entity.GetContents()->GetCoverageHint();
548
549 if (coverage_hint.has_value()) {
550 auto maybe_subpass_coverage =
551 subpass_coverage.Intersection(*coverage_hint);
552 if (!maybe_subpass_coverage.has_value()) {
553 return std::nullopt; // Nothing to render.
554 }
555
556 subpass_coverage = *maybe_subpass_coverage;
557 }
558 }
559
560 ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
561 RenderPass& pass) {
562 auto& host_buffer = pass.GetTransientsBuffer();
563
564 Command cmd;
565 DEBUG_COMMAND_INFO(cmd, SPrintF("Pipeline Blend Filter (%s)",
566 BlendModeToString(blend_mode)));
567 auto options = OptionsFromPass(pass);
568
569 auto add_blend_command = [&](std::optional<Snapshot> input) {
570 if (!input.has_value()) {
571 return false;
572 }
573 auto input_coverage = input->GetCoverage();
574 if (!input_coverage.has_value()) {
575 return false;
576 }
577
578 auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
579 input->sampler_descriptor);
580 FS::BindTextureSamplerSrc(cmd, input->texture, sampler);
581
582 auto size = input->texture->GetSize();
583 VertexBufferBuilder<VS::PerVertexData> vtx_builder;
584 vtx_builder.AddVertices({
585 {Point(0, 0), Point(0, 0)},
586 {Point(size.width, 0), Point(1, 0)},
587 {Point(size.width, size.height), Point(1, 1)},
588 {Point(0, 0), Point(0, 0)},
589 {Point(size.width, size.height), Point(1, 1)},
590 {Point(0, size.height), Point(0, 1)},
591 });
592 auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
593 cmd.BindVertices(vtx_buffer);
594
595 VS::FrameInfo frame_info;
596 frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
597 Matrix::MakeTranslation(-subpass_coverage.origin) *
598 input->transform;
599 frame_info.texture_sampler_y_coord_scale =
600 input->texture->GetYCoordScale();
601
602 FS::FragInfo frag_info;
603 frag_info.input_alpha =
604 absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
605 ? input->opacity
606 : 1.0;
607 FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
608 VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
609
610 pass.AddCommand(std::move(cmd));
611 return true;
612 };
613
614 // Draw the first texture using kSource.
615 options.blend_mode = BlendMode::kSource;
616 cmd.pipeline = renderer.GetBlendPipeline(options);
617 if (!add_blend_command(dst_snapshot)) {
618 return true;
619 }
620
621 // Write subsequent textures using the selected blend mode.
622
623 if (inputs.size() >= 2) {
624 options.blend_mode = blend_mode;
625 cmd.pipeline = renderer.GetBlendPipeline(options);
626
627 for (auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
628 texture_i++) {
629 auto src_input = texture_i->get()->GetSnapshot("PipelineBlend(Src)",
630 renderer, entity);
631 if (!add_blend_command(src_input)) {
632 return true;
633 }
634 }
635 }
636
637 // If a foreground color is set, blend it in.
638
639 if (foreground_color.has_value()) {
640 auto contents = std::make_shared<SolidColorContents>();
641 contents->SetGeometry(
642 Geometry::MakeRect(Rect::MakeSize(pass.GetRenderTargetSize())));
643 contents->SetColor(foreground_color.value());
644
645 Entity foreground_entity;
646 foreground_entity.SetBlendMode(blend_mode);
647 foreground_entity.SetContents(contents);
648 if (!foreground_entity.Render(renderer, pass)) {
649 return false;
650 }
651 }
652
653 return true;
654 };
655
656 auto out_texture = renderer.MakeSubpass(
657 "Pipeline Blend Filter", ISize(subpass_coverage.size), callback);
658
659 if (!out_texture) {
660 return std::nullopt;
661 }
662
663 return Entity::FromSnapshot(
664 Snapshot{
665 .texture = out_texture,
666 .transform = Matrix::MakeTranslation(subpass_coverage.origin),
667 // Since we absorbed the transform of the inputs and used the
668 // respective snapshot sampling modes when blending, pass on
669 // the default NN clamp sampler.
670 .sampler_descriptor = {},
671 .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
672 ? 1.0f
673 : dst_snapshot->opacity) *
674 alpha.value_or(1.0)},
675 entity.GetBlendMode(), entity.GetStencilDepth());
676 }
677
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); \
690 }; \
691 break;
692
693 void BlendFilterContents::SetBlendMode(BlendMode blend_mode) {
694 if (blend_mode > Entity::kLastAdvancedBlendMode) {
695 VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
696 << " assigned to BlendFilterContents.";
697 }
698
699 blend_mode_ = blend_mode;
700
701 if (blend_mode > Entity::kLastPipelineBlendMode) {
702 switch (blend_mode) {
703 BLEND_CASE(Screen)
704 BLEND_CASE(Overlay)
705 BLEND_CASE(Darken)
706 BLEND_CASE(Lighten)
707 BLEND_CASE(ColorDodge)
708 BLEND_CASE(ColorBurn)
709 BLEND_CASE(HardLight)
710 BLEND_CASE(SoftLight)
711 BLEND_CASE(Difference)
712 BLEND_CASE(Exclusion)
713 BLEND_CASE(Multiply)
714 BLEND_CASE(Hue)
715 BLEND_CASE(Saturation)
716 BLEND_CASE(Color)
717 BLEND_CASE(Luminosity)
718 default:
719 FML_UNREACHABLE();
720 }
721 }
722 }
723
724 void BlendFilterContents::SetForegroundColor(std::optional<Color> color) {
725 foreground_color_ = color;
726 }
727
728 std::optional<Entity> BlendFilterContents::RenderFilter(
729 const FilterInput::Vector& inputs,
730 const ContentContext& renderer,
731 const Entity& entity,
732 const Matrix& effect_transform,
733 const Rect& coverage,
734 const std::optional<Rect>& coverage_hint) const {
735 if (inputs.empty()) {
736 return std::nullopt;
737 }
738
739 if (inputs.size() == 1 && !foreground_color_.has_value()) {
740 // Nothing to blend.
741 return PipelineBlend(inputs, renderer, entity, coverage, BlendMode::kSource,
742 std::nullopt, GetAbsorbOpacity(), GetAlpha());
743 }
744
745 if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
746 if (inputs.size() == 1 && foreground_color_.has_value() &&
747 GetAbsorbOpacity() == ColorFilterContents::AbsorbOpacity::kYes) {
748 return CreateForegroundPorterDuffBlend(
749 inputs[0], renderer, entity, coverage, foreground_color_.value(),
750 blend_mode_, GetAlpha(), GetAbsorbOpacity());
751 }
752 return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
753 foreground_color_, GetAbsorbOpacity(), GetAlpha());
754 }
755
756 if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
757 if (inputs.size() == 1 && foreground_color_.has_value() &&
758 GetAbsorbOpacity() == ColorFilterContents::AbsorbOpacity::kYes) {
759 return CreateForegroundAdvancedBlend(
760 inputs[0], renderer, entity, coverage, foreground_color_.value(),
761 blend_mode_, GetAlpha(), GetAbsorbOpacity());
762 }
763 return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,
764 foreground_color_, GetAbsorbOpacity(),
765 GetAlpha());
766 }
767
768 FML_UNREACHABLE();
769 }
770
771 } // namespace impeller
impeller::BlendMode
BlendMode
Definition: color.h:57
impeller::OptionsFromPass
ContentContextOptions OptionsFromPass(const RenderPass &pass)
Definition: contents.cc:20
impeller::Command
An object used to specify work to the GPU along with references to resources the GPU will used when d...
Definition: command.h:99
BLEND_CASE
#define BLEND_CASE(mode)
Definition: blend_filter_contents.cc:678
DEBUG_COMMAND_INFO
#define DEBUG_COMMAND_INFO(obj, arg)
Definition: command.h:31
impeller::Entity::kLastPipelineBlendMode
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:23
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
contents.h
impeller::BlendFilterContents::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: blend_filter_contents.cc:693
impeller::Entity::GetStencilDepth
uint32_t GetStencilDepth() const
Definition: entity.cc:93
impeller::TRect::size
TSize< Type > size
Definition: rect.h:24
impeller::ContentContext::GetBlendPipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > GetBlendPipeline(ContentContextOptions opts) const
Definition: content_context.h:422
impeller::Entity::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:101
impeller::BlendFilterContents::SetForegroundColor
void SetForegroundColor(std::optional< Color > color)
Sets a source color which is blended after all of the inputs have been blended.
Definition: blend_filter_contents.cc:724
impeller::Entity::FromSnapshot
static std::optional< Entity > FromSnapshot(const std::optional< Snapshot > &snapshot, BlendMode blend_mode=BlendMode::kSourceOver, uint32_t stencil_depth=0)
Create an entity that can be used to render a given snapshot.
Definition: entity.cc:21
sampler_library.h
impeller::BlendMode::kMultiply
@ kMultiply
entity.h
solid_color_contents.h
impeller::Color
Definition: color.h:122
formats.h
impeller::InvertPorterDuffBlend
std::optional< BlendMode > InvertPorterDuffBlend(BlendMode blend_mode)
Definition: blend_filter_contents.cc:28
impeller::BlendMode::kSourceIn
@ kSourceIn
impeller::Geometry::MakeRect
static std::unique_ptr< Geometry > MakeRect(Rect rect)
Definition: geometry.cc:142
impeller::RenderPipelineT::FragmentShader
FragmentShader_ FragmentShader
Definition: pipeline.h:91
impeller::BlendMode::kSaturation
@ kSaturation
impeller::VertexBufferBuilder::AddVertices
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
Definition: vertex_buffer_builder.h:64
impeller::BlendMode::kHue
@ kHue
impeller::BlendMode::kDestinationIn
@ kDestinationIn
impeller::BlendMode::kDarken
@ kDarken
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:94
impeller::BlendMode::kColor
@ kColor
impeller::BlendMode::kLuminosity
@ kLuminosity
impeller::AdvancedBlend
static std::optional< Entity > AdvancedBlend(const FilterInput::Vector &inputs, const ContentContext &renderer, const Entity &entity, const Rect &coverage, BlendMode blend_mode, std::optional< Color > foreground_color, ColorFilterContents::AbsorbOpacity absorb_opacity, PipelineProc pipeline_proc, std::optional< Scalar > alpha)
Definition: blend_filter_contents.cc:73
impeller::BlendMode::kXor
@ kXor
impeller::Entity::SetContents
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:81
path_builder.h
impeller::BlendMode::kOverlay
@ kOverlay
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::Entity
Definition: entity.h:21
impeller::ContentContext::MakeSubpass
std::shared_ptr< Texture > MakeSubpass(const std::string &label, ISize texture_size, const SubpassCallback &subpass_callback, bool msaa_enabled=true) const
Creates a new texture of size texture_size and calls subpass_callback with a RenderPass for drawing t...
Definition: content_context.cc:373
impeller::Point
TPoint< Scalar > Point
Definition: point.h:306
impeller::BlendMode::kLighten
@ kLighten
impeller::TRect::Intersection
constexpr std::optional< TRect< T > > Intersection(const TRect &o) const
Definition: rect.h:219
render_pass.h
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Entity::Render
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition: entity.cc:159
impeller::BlendMode::kHardLight
@ kHardLight
impeller::BlendMode::kPlus
@ kPlus
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::BlendMode::kDestination
@ kDestination
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:440
impeller::VertexBufferBuilder
Definition: vertex_buffer_builder.h:23
impeller::Entity::kLastAdvancedBlendMode
static constexpr BlendMode kLastAdvancedBlendMode
Definition: entity.h:24
impeller::ColorFilterContents::GetAbsorbOpacity
AbsorbOpacity GetAbsorbOpacity() const
Definition: color_filter_contents.cc:89
impeller::Snapshot
Represents a texture and its intended draw transform/sampler configuration.
Definition: snapshot.h:24
impeller::BlendMode::kDestinationATop
@ kDestinationATop
impeller::kPorterDuffCoefficients
constexpr std::array< std::array< Scalar, 5 >, 15 > kPorterDuffCoefficients
Definition: blend_filter_contents.h:14
impeller::Entity::GetContents
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:85
impeller::PipelineProc
std::shared_ptr< Pipeline< PipelineDescriptor > >(ContentContext::*)(ContentContextOptions) const PipelineProc
Definition: blend_filter_contents.cc:70
impeller::TRect::origin
TPoint< Type > origin
Definition: rect.h:23
filter_input.h
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:306
impeller::Luminosity
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:140
strings.h
impeller::BlendMode::kDestinationOut
@ kDestinationOut
color_filter_contents.h
impeller::BlendFilterContents::~BlendFilterContents
~BlendFilterContents() override
impeller::BlendMode::kSoftLight
@ kSoftLight
anonymous_contents.h
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:105
impeller::VertexBufferBuilder::CreateVertexBuffer
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
Definition: vertex_buffer_builder.h:78
impeller::ContentContext::SubpassCallback
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
Definition: content_context.h:703
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:136
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:27
impeller::BlendMode::kDifference
@ kDifference
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:60
content_context.h
impeller::Contents::RenderProc
std::function< bool(const ContentContext &renderer, const Entity &entity, RenderPass &pass)> RenderProc
Definition: contents.h:48
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:444
impeller::BlendMode::kExclusion
@ kExclusion
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:52
snapshot.h
impeller::BlendMode::kDestinationOver
@ kDestinationOver
impeller::BlendMode::kClear
@ kClear
impeller::PipelineBlend
static std::optional< Entity > PipelineBlend(const FilterInput::Vector &inputs, const ContentContext &renderer, const Entity &entity, const Rect &coverage, BlendMode blend_mode, std::optional< Color > foreground_color, ColorFilterContents::AbsorbOpacity absorb_opacity, std::optional< Scalar > alpha)
Definition: blend_filter_contents.cc:527
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:448
color.h
blend_filter_contents.h
impeller::BlendMode::kColorDodge
@ kColorDodge
impeller::Contents::CoverageProc
std::function< std::optional< Rect >(const Entity &entity)> CoverageProc
Definition: contents.h:49
impeller::Command::BindVertices
bool BindVertices(const VertexBuffer &buffer)
Specify the vertex and index buffer to use for this command.
Definition: command.cc:15
impeller::BlendMode::kSourceATop
@ kSourceATop
impeller::Snapshot::texture
std::shared_ptr< Texture > texture
Definition: snapshot.h:25
impeller::Command::pipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > pipeline
Definition: command.h:103
impeller::BlendMode::kModulate
@ kModulate
impeller::ColorFilterContents::AbsorbOpacity::kYes
@ kYes
impeller::FilterInput::Vector
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:32
impeller::ColorFilterContents::GetAlpha
std::optional< Scalar > GetAlpha() const
Definition: color_filter_contents.cc:98
impeller::Capabilities::SupportsDecalSamplerAddressMode
virtual bool SupportsDecalSamplerAddressMode() const =0
Whether the context backend supports SamplerAddressMode::Decal.
impeller::RenderPipelineT::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:90
impeller::ContentContextOptions
Definition: content_context.h:302
impeller::ColorFilterContents::AbsorbOpacity
AbsorbOpacity
Definition: color_filter_contents.h:13
impeller::BlendMode::kScreen
@ kScreen
impeller
Definition: aiks_context.cc:10
impeller::ContentContext
Definition: content_context.h:344
impeller::BlendMode::kSourceOut
@ kSourceOut
impeller::BlendFilterContents::BlendFilterContents
BlendFilterContents()
Definition: blend_filter_contents.cc:63
impeller::TRect
Definition: rect.h:20
impeller::BlendMode::kSource
@ kSource
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:36
impeller::AnonymousContents::Make
static std::shared_ptr< Contents > Make(RenderProc render_proc, CoverageProc coverage_proc)
Definition: anonymous_contents.cc:11
impeller::Saturation
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:166
impeller::SamplerAddressMode::kDecal
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...