9 #include "flutter/fml/make_copyable.h"
12 #include "impeller/entity/texture_downsample.frag.h"
13 #include "impeller/entity/texture_fill.frag.h"
14 #include "impeller/entity/texture_fill.vert.h"
26 constexpr
Scalar kMaxSigma = 500.0f;
64 return Vector2(std::clamp(vec2.
x, min, max),
65 std::clamp(vec2.
y, min, max));
92 BlurInfo CalculateBlurInfo(
const Entity& entity,
93 const Matrix& effect_transform,
100 ExtractScale(entity.GetTransform().Basis());
102 Vector2(entity.GetTransform().m[12], entity.GetTransform().m[13]);
127 std::optional<Snapshot> GetSnapshot(
const std::shared_ptr<FilterInput>& input,
128 const ContentContext& renderer,
129 const Entity& entity,
130 const std::optional<Rect>& coverage_hint) {
131 std::optional<Snapshot> input_snapshot =
132 input->GetSnapshot(
"GaussianBlur", renderer, entity,
134 if (!input_snapshot.has_value()) {
138 return input_snapshot;
143 Rect MakeReferenceUVs(
const Rect& reference,
const Rect& rect) {
146 return result.Scale(1.0f /
Vector2(reference.GetSize()));
149 Quad CalculateSnapshotUVs(
150 const Snapshot& input_snapshot,
151 const std::optional<Rect>& source_expanded_coverage_hint) {
152 std::optional<Rect> input_snapshot_coverage = input_snapshot.GetCoverage();
154 FML_DCHECK(input_snapshot.transform.IsTranslationScaleOnly());
155 if (source_expanded_coverage_hint.has_value() &&
156 input_snapshot_coverage.has_value()) {
158 std::optional<Rect>
uvs =
159 MakeReferenceUVs(input_snapshot_coverage.value(),
160 source_expanded_coverage_hint.value())
162 FML_DCHECK(
uvs.has_value());
163 if (
uvs.has_value()) {
164 blur_uvs[0] =
uvs->GetLeftTop();
165 blur_uvs[1] =
uvs->GetRightTop();
166 blur_uvs[2] =
uvs->GetLeftBottom();
167 blur_uvs[3] =
uvs->GetRightBottom();
174 if (divisor == 0.0f) {
178 Scalar remainder = fmod(val, divisor);
179 if (remainder != 0.0f) {
180 return val + (divisor - remainder);
187 if (divisor == 0.0f) {
191 Scalar remainder = fmod(val, divisor);
192 if (remainder != 0.0f) {
193 return val - remainder;
199 struct DownsamplePassArgs {
217 DownsamplePassArgs CalculateDownsamplePassArgs(
220 const Snapshot& input_snapshot,
221 const std::optional<Rect>& source_expanded_coverage_hint,
222 const std::shared_ptr<FilterInput>& input,
223 const Entity& snapshot_entity) {
231 Vector2 downsample_scalar(desired_scalar, desired_scalar);
241 std::optional<Rect> snapshot_coverage = input_snapshot.GetCoverage();
242 if (input_snapshot.transform.Equals(snapshot_entity.GetTransform()) &&
243 source_expanded_coverage_hint.has_value() &&
244 snapshot_coverage.has_value() &&
245 snapshot_coverage->Contains(source_expanded_coverage_hint.value())) {
254 int32_t divisor = std::round(1.0f / desired_scalar);
256 FloorToDivisible(source_expanded_coverage_hint->GetLeft(), divisor),
257 FloorToDivisible(source_expanded_coverage_hint->GetTop(), divisor),
258 source_expanded_coverage_hint->GetRight(),
259 source_expanded_coverage_hint->GetBottom());
261 aligned_coverage_hint.GetX(), aligned_coverage_hint.GetY(),
262 CeilToDivisible(aligned_coverage_hint.GetWidth(), divisor),
263 CeilToDivisible(aligned_coverage_hint.GetHeight(), divisor));
264 ISize source_size =
ISize(aligned_coverage_hint.GetSize().width,
265 aligned_coverage_hint.GetSize().height);
266 Vector2 downsampled_size = source_size * downsample_scalar;
268 FML_DCHECK(std::modf(downsampled_size.x, &int_part) == 0.0f);
269 FML_DCHECK(std::modf(downsampled_size.y, &int_part) == 0.0f);
275 Quad uvs = CalculateSnapshotUVs(input_snapshot, aligned_coverage_hint);
281 {aligned_coverage_hint.GetX(), aligned_coverage_hint.GetY(), 0})};
284 auto input_snapshot_size = input_snapshot.texture->GetSize();
287 Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
289 ISize(ceil(downsampled_size.x), ceil(downsampled_size.y));
290 Vector2 divisible_size(CeilToDivisible(source_rect_padded.GetSize().width,
291 1.0 / downsample_scalar.x),
292 CeilToDivisible(source_rect_padded.GetSize().height,
293 1.0 / downsample_scalar.y));
299 (divisible_size.x - source_rect_padded.GetSize().width) / 2.0
303 (divisible_size.y - source_rect_padded.GetSize().height) / 2.0
305 source_rect_padded = source_rect.Expand(divisible_padding);
310 input, snapshot_entity, source_rect_padded, input_snapshot_size);
315 .transform = input_snapshot.transform *
323 fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
324 const ContentContext& renderer,
325 const std::shared_ptr<CommandBuffer>& command_buffer,
326 const std::shared_ptr<Texture>& input_texture,
327 const SamplerDescriptor& sampler_descriptor,
328 const DownsamplePassArgs& pass_args,
330 using VS = TextureFillVertexShader;
334 if (pass_args.effective_scalar.x >= 0.5f ||
335 (!input_texture->NeedsMipmapGeneration() &&
336 input_texture->GetTextureDescriptor().mip_count > 1)) {
338 [&](
const ContentContext& renderer, RenderPass& pass) {
339 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
341 pass.SetCommandLabel(
"Gaussian blur downsample");
344 pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
346 TextureFillVertexShader::FrameInfo frame_info;
348 frame_info.texture_sampler_y_coord_scale =
349 input_texture->GetYCoordScale();
351 TextureFillFragmentShader::FragInfo frag_info;
352 frag_info.alpha = 1.0;
354 const Quad&
uvs = pass_args.uvs;
355 std::array<VS::PerVertexData, 4> vertices = {
356 VS::PerVertexData{
Point(0, 0),
uvs[0]},
357 VS::PerVertexData{
Point(1, 0),
uvs[1]},
358 VS::PerVertexData{
Point(0, 1),
uvs[2]},
359 VS::PerVertexData{
Point(1, 1),
uvs[3]},
363 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
364 SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
367 TextureFillVertexShader::BindFrameInfo(
368 pass, host_buffer.EmplaceUniform(frame_info));
369 TextureFillFragmentShader::BindFragInfo(
370 pass, host_buffer.EmplaceUniform(frag_info));
371 TextureFillFragmentShader::BindTextureSampler(
373 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
374 linear_sampler_descriptor));
376 return pass.Draw().ok();
378 return renderer.MakeSubpass(
"Gaussian Blur Filter", pass_args.subpass_size,
379 command_buffer, subpass_callback);
384 if (pass_args.effective_scalar.x <= 0.0625f) {
386 ratio = 1.0f / 64.0f;
387 }
else if (pass_args.effective_scalar.x <= 0.125f) {
389 ratio = 1.0f / 16.0f;
392 [&](
const ContentContext& renderer, RenderPass& pass) {
393 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
395 pass.SetCommandLabel(
"Gaussian blur downsample");
398 pass.SetPipeline(renderer.GetDownsamplePipeline(pipeline_options));
400 TextureFillVertexShader::FrameInfo frame_info;
402 frame_info.texture_sampler_y_coord_scale =
403 input_texture->GetYCoordScale();
405 TextureDownsampleFragmentShader::FragInfo frag_info;
406 frag_info.edge = edge;
407 frag_info.ratio = ratio;
408 frag_info.pixel_size =
Vector2(1.0f /
Size(input_texture->GetSize()));
410 const Quad&
uvs = pass_args.uvs;
411 std::array<VS::PerVertexData, 4> vertices = {
412 VS::PerVertexData{
Point(0, 0),
uvs[0]},
413 VS::PerVertexData{
Point(1, 0),
uvs[1]},
414 VS::PerVertexData{
Point(0, 1),
uvs[2]},
415 VS::PerVertexData{
Point(1, 1),
uvs[3]},
419 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
420 SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
423 TextureFillVertexShader::BindFrameInfo(
424 pass, host_buffer.EmplaceUniform(frame_info));
425 TextureDownsampleFragmentShader::BindFragInfo(
426 pass, host_buffer.EmplaceUniform(frag_info));
427 TextureDownsampleFragmentShader::BindTextureSampler(
429 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
430 linear_sampler_descriptor));
432 return pass.Draw().ok();
434 return renderer.MakeSubpass(
"Gaussian Blur Filter", pass_args.subpass_size,
435 command_buffer, subpass_callback);
439 fml::StatusOr<RenderTarget> MakeBlurSubpass(
440 const ContentContext& renderer,
441 const std::shared_ptr<CommandBuffer>& command_buffer,
442 const RenderTarget& input_pass,
443 const SamplerDescriptor& sampler_descriptor,
445 const BlurParameters& blur_info,
446 std::optional<RenderTarget> destination_target,
447 const Quad& blur_uvs) {
454 const std::shared_ptr<Texture>& input_texture =
455 input_pass.GetRenderTargetTexture();
461 [&](
const ContentContext& renderer, RenderPass& pass) {
462 GaussianBlurVertexShader::FrameInfo frame_info;
464 frame_info.texture_sampler_y_coord_scale =
465 input_texture->GetYCoordScale();
467 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
471 pass.SetPipeline(renderer.GetGaussianBlurPipeline(options));
473 std::array<VS::PerVertexData, 4> vertices = {
474 VS::PerVertexData{blur_uvs[0], blur_uvs[0]},
475 VS::PerVertexData{blur_uvs[1], blur_uvs[1]},
476 VS::PerVertexData{blur_uvs[2], blur_uvs[2]},
477 VS::PerVertexData{blur_uvs[3], blur_uvs[3]},
481 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
484 GaussianBlurFragmentShader::BindTextureSampler(
486 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
487 linear_sampler_descriptor));
488 GaussianBlurVertexShader::BindFrameInfo(
489 pass, host_buffer.EmplaceUniform(frame_info));
490 GaussianBlurFragmentShader::BindKernelSamples(
491 pass, host_buffer.EmplaceUniform(
493 return pass.Draw().ok();
495 if (destination_target.has_value()) {
496 return renderer.MakeSubpass(
"Gaussian Blur Filter",
497 destination_target.value(), command_buffer,
500 return renderer.MakeSubpass(
"Gaussian Blur Filter",
subpass_size,
501 command_buffer, subpass_callback);
506 return static_cast<int>(std::round(radius * scalar));
510 const Entity& entity,
511 const std::shared_ptr<FilterInput>& input,
512 const Snapshot& input_snapshot,
514 const Geometry* geometry) {
515 auto clip_contents = std::make_shared<ClipContents>();
516 clip_contents->SetClipOperation(clip_operation);
517 clip_contents->SetGeometry(geometry);
519 clipper.SetContents(clip_contents);
520 auto restore = std::make_unique<ClipRestoreContents>();
521 Matrix entity_transform = entity.GetTransform();
522 Matrix blur_transform = blur_entity.GetTransform();
523 auto renderer = fml::MakeCopyable(
524 [blur_entity = blur_entity.Clone(), clipper = std::move(clipper),
525 restore = std::move(restore), entity_transform,
526 blur_transform](
const ContentContext& renderer,
const Entity& entity,
527 RenderPass& pass)
mutable {
529 clipper.SetClipDepth(entity.GetClipDepth());
530 clipper.SetTransform(entity.GetTransform() * entity_transform);
531 result = clipper.Render(renderer, pass) && result;
532 blur_entity.SetClipDepth(entity.GetClipDepth());
533 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
534 result = blur_entity.Render(renderer, pass) && result;
538 fml::MakeCopyable([blur_entity = std::move(blur_entity),
539 blur_transform](
const Entity& entity)
mutable {
540 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
541 return blur_entity.GetCoverage();
549 const Entity& entity,
550 const std::shared_ptr<FilterInput>& input,
551 const Snapshot& input_snapshot,
553 const Geometry* geometry,
556 switch (blur_style) {
561 input, input_snapshot,
562 std::move(blur_entity), geometry);
566 input, input_snapshot,
567 std::move(blur_entity), geometry);
569 Entity snapshot_entity =
572 Matrix blurred_transform = blur_entity.GetTransform();
573 Matrix snapshot_transform =
574 entity.GetTransform() *
577 input_snapshot.transform;
579 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
580 blurred_transform, snapshot_transform,
581 snapshot_entity = std::move(snapshot_entity)](
582 const ContentContext& renderer,
583 const Entity& entity,
584 RenderPass& pass)
mutable {
586 snapshot_entity.SetTransform(entity.GetTransform() *
588 snapshot_entity.SetClipDepth(entity.GetClipDepth());
589 result = result && snapshot_entity.Render(renderer, pass);
590 blur_entity.SetClipDepth(entity.GetClipDepth());
591 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
592 result = result && blur_entity.Render(renderer, pass);
595 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
596 blurred_transform](
const Entity& entity)
mutable {
597 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
598 return blur_entity.GetCoverage();
610 BlurStyle mask_blur_style,
612 : sigma_(sigma_x, sigma_y),
613 tile_mode_(tile_mode),
614 mask_blur_style_(mask_blur_style),
615 mask_geometry_(mask_geometry) {
627 Scalar raw_result = 4.0 / sigma;
629 Scalar exponent = round(log2f(raw_result));
631 exponent = std::max(-4.0f, exponent);
632 Scalar rounded = powf(2.0f, exponent);
636 if (rounded < 0.125f) {
637 Scalar rounded_plus = powf(2.0f, exponent + 1);
639 int kernel_size_plus = (ScaleBlurRadius(
blur_radius, rounded_plus) * 2) + 1;
643 static constexpr int32_t kEighthDownsampleKernalWidthMax = 41;
644 result = kernel_size_plus <= kEighthDownsampleKernalWidthMax ? rounded_plus
651 const Matrix& effect_transform,
652 const Rect& output_limit)
const {
658 return output_limit.
Expand(
Point(blur_radii.
x, blur_radii.
y));
664 const Matrix& effect_transform)
const {
665 if (inputs.empty()) {
668 std::optional<Rect> input_coverage = inputs[0]->GetCoverage(entity);
669 if (!input_coverage.has_value()) {
673 BlurInfo blur_info = CalculateBlurInfo(entity, effect_transform, sigma_);
674 return input_coverage.value().Expand(
675 Point(blur_info.local_padding.x, blur_info.local_padding.y));
686 std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
690 const Matrix& effect_transform,
691 const Rect& coverage,
692 const std::optional<Rect>& coverage_hint)
const {
693 if (inputs.empty()) {
697 BlurInfo blur_info = CalculateBlurInfo(entity, effect_transform, sigma_);
702 std::optional<Rect> expanded_coverage_hint;
703 if (coverage_hint.has_value()) {
704 expanded_coverage_hint = coverage_hint->Expand(blur_info.local_padding);
707 Entity snapshot_entity = entity.
Clone();
712 std::optional<Rect> source_expanded_coverage_hint;
713 if (expanded_coverage_hint.has_value()) {
714 source_expanded_coverage_hint = expanded_coverage_hint->TransformBounds(
720 std::optional<Snapshot> input_snapshot = GetSnapshot(
721 inputs[0], renderer, snapshot_entity, source_expanded_coverage_hint);
722 if (!input_snapshot.has_value()) {
735 input_snapshot->transform);
745 std::shared_ptr<CommandBuffer> command_buffer_1 =
747 if (!command_buffer_1) {
751 DownsamplePassArgs downsample_pass_args = CalculateDownsamplePassArgs(
752 blur_info.scaled_sigma, blur_info.padding, input_snapshot.value(),
753 source_expanded_coverage_hint, inputs[0], snapshot_entity);
755 fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
756 renderer, command_buffer_1, input_snapshot->texture,
757 input_snapshot->sampler_descriptor, downsample_pass_args, tile_mode_);
759 if (!pass1_out.ok()) {
764 1.0 /
Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize());
768 std::shared_ptr<CommandBuffer> command_buffer_2 =
770 if (!command_buffer_2) {
774 fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
775 renderer, command_buffer_2, pass1_out.value(),
776 input_snapshot->sampler_descriptor, tile_mode_,
778 .blur_uv_offset = Point(0.0, pass1_pixel_size.y),
779 .blur_sigma = blur_info.scaled_sigma.y *
780 downsample_pass_args.effective_scalar.y,
781 .blur_radius = ScaleBlurRadius(
782 blur_info.blur_radius.y, downsample_pass_args.effective_scalar.y),
785 std::nullopt, blur_uvs);
787 if (!pass2_out.ok()) {
791 std::shared_ptr<CommandBuffer> command_buffer_3 =
793 if (!command_buffer_3) {
798 auto pass3_destination = pass2_out.value().GetRenderTargetTexture() !=
799 pass1_out.value().GetRenderTargetTexture()
800 ? std::optional<RenderTarget>(pass1_out.value())
801 :
std::optional<RenderTarget>(
std::nullopt);
803 fml::StatusOr<RenderTarget> pass3_out = MakeBlurSubpass(
804 renderer, command_buffer_3, pass2_out.value(),
805 input_snapshot->sampler_descriptor, tile_mode_,
807 .blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
808 .blur_sigma = blur_info.scaled_sigma.x *
809 downsample_pass_args.effective_scalar.x,
810 .blur_radius = ScaleBlurRadius(
811 blur_info.blur_radius.x, downsample_pass_args.effective_scalar.x),
814 pass3_destination, blur_uvs);
816 if (!pass3_out.ok()) {
822 ->Submit({command_buffer_1, command_buffer_2,
830 FML_DCHECK((pass1_out.value().GetRenderTargetSize() ==
831 pass2_out.value().GetRenderTargetSize()) &&
832 (pass2_out.value().GetRenderTargetSize() ==
833 pass3_out.value().GetRenderTargetSize()));
835 SamplerDescriptor sampler_desc = MakeSamplerDescriptor(
839 Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(),
844 downsample_pass_args.transform *
846 .sampler_descriptor = sampler_desc,
847 .opacity = input_snapshot->opacity},
850 return ApplyBlurStyle(mask_blur_style_, entity, inputs[0],
851 input_snapshot.value(), std::move(blur_output_entity),
852 mask_geometry_, blur_info.source_space_scalar,
853 blur_info.source_space_offset);
861 const std::shared_ptr<FilterInput>& filter_input,
863 const Rect& source_rect,
864 const ISize& texture_size) {
865 Matrix input_transform = filter_input->GetLocalTransform(entity);
869 {1.0f / texture_size.
width, 1.0f / texture_size.
height, 1.0f});
870 return uv_transform.
Transform(coverage_quad);
878 Scalar clamped = std::min(sigma, kMaxSigma);
879 constexpr
Scalar a = 3.4e-06;
882 Scalar scalar = c +
b * clamped + a * clamped * clamped;
883 return clamped * scalar;
916 .coefficient = expf(-0.5f * (x * x) /
924 for (
auto& sample : result.samples) {
925 sample.coefficient /= tally;
935 GaussianBlurPipeline::FragmentShader::KernelSamples result = {};
936 result.sample_count = ((parameters.
sample_count - 1) / 2) + 1;
937 int32_t middle = result.sample_count / 2;
940 static_assert(
sizeof(result.sample_data) ==
941 sizeof(std::array<Vector4, kGaussianBlurMaxKernelSize>));
943 for (
int i = 0; i < result.sample_count; i++) {
958 result.sample_data[i].
x = uv.
x;
959 result.sample_data[i].y = uv.
y;