9 #include "flutter/fml/make_copyable.h"
12 #include "impeller/entity/texture_fill.frag.h"
13 #include "impeller/entity/texture_fill.vert.h"
26 constexpr
Scalar kMaxSigma = 500.0f;
41 std::initializer_list<typename T::PerVertexData>&& vertices) {
73 return Vector2(std::clamp(vec2.
x, min, max),
74 std::clamp(vec2.
y, min, max));
98 BlurInfo CalculateBlurInfo(
const Entity& entity,
99 const Matrix& effect_transform,
106 ExtractScale(entity.GetTransform().Basis());
130 std::optional<Snapshot> GetSnapshot(
const std::shared_ptr<FilterInput>& input,
131 const ContentContext& renderer,
132 const Entity& entity,
133 const std::optional<Rect>& coverage_hint) {
135 if (renderer.GetContext()->GetBackendType() ==
142 std::optional<Snapshot> input_snapshot =
143 input->GetSnapshot(
"GaussianBlur", renderer, entity,
146 if (!input_snapshot.has_value()) {
151 if (input_snapshot->texture->GetMipCount() <= 1) {
154 FML_DCHECK(!input_snapshot->texture->NeedsMipmapGeneration());
156 return input_snapshot;
161 Rect MakeReferenceUVs(
const Rect& reference,
const Rect& rect) {
164 return result.Scale(1.0f /
Vector2(reference.GetSize()));
167 Quad CalculateSnapshotUVs(
168 const Snapshot& input_snapshot,
169 const std::optional<Rect>& source_expanded_coverage_hint) {
170 std::optional<Rect> input_snapshot_coverage = input_snapshot.GetCoverage();
172 FML_DCHECK(input_snapshot.transform.IsTranslationScaleOnly());
173 if (source_expanded_coverage_hint.has_value() &&
174 input_snapshot_coverage.has_value()) {
176 std::optional<Rect>
uvs =
177 MakeReferenceUVs(input_snapshot_coverage.value(),
178 source_expanded_coverage_hint.value())
180 FML_DCHECK(
uvs.has_value());
181 if (
uvs.has_value()) {
182 blur_uvs[0] =
uvs->GetLeftTop();
183 blur_uvs[1] =
uvs->GetRightTop();
184 blur_uvs[2] =
uvs->GetLeftBottom();
185 blur_uvs[3] =
uvs->GetRightBottom();
192 if (divisor == 0.0f) {
196 Scalar remainder = fmod(val, divisor);
197 if (remainder != 0.0f) {
198 return val + (divisor - remainder);
205 if (divisor == 0.0f) {
209 Scalar remainder = fmod(val, divisor);
210 if (remainder != 0.0f) {
211 return val - remainder;
217 struct DownsamplePassArgs {
235 DownsamplePassArgs CalculateDownsamplePassArgs(
238 const Snapshot& input_snapshot,
239 const std::optional<Rect>& source_expanded_coverage_hint,
240 const std::shared_ptr<FilterInput>& input,
241 const Entity& snapshot_entity) {
248 Vector2 downsample_scalar(desired_scalar, desired_scalar);
258 std::optional<Rect> snapshot_coverage = input_snapshot.GetCoverage();
259 if (input_snapshot.transform.IsIdentity() &&
260 source_expanded_coverage_hint.has_value() &&
261 snapshot_coverage.has_value() &&
262 snapshot_coverage->Contains(source_expanded_coverage_hint.value())) {
271 int32_t divisor = std::round(1.0f / desired_scalar);
273 FloorToDivisible(source_expanded_coverage_hint->GetLeft(), divisor),
274 FloorToDivisible(source_expanded_coverage_hint->GetTop(), divisor),
275 source_expanded_coverage_hint->GetRight(),
276 source_expanded_coverage_hint->GetBottom());
278 aligned_coverage_hint.GetX(), aligned_coverage_hint.GetY(),
279 CeilToDivisible(aligned_coverage_hint.GetWidth(), divisor),
280 CeilToDivisible(aligned_coverage_hint.GetHeight(), divisor));
281 ISize source_size =
ISize(aligned_coverage_hint.GetSize().width,
282 aligned_coverage_hint.GetSize().height);
283 Vector2 downsampled_size = source_size * downsample_scalar;
285 FML_DCHECK(std::modf(downsampled_size.x, &int_part) == 0.0f);
286 FML_DCHECK(std::modf(downsampled_size.y, &int_part) == 0.0f);
292 Quad uvs = CalculateSnapshotUVs(input_snapshot, aligned_coverage_hint);
298 {aligned_coverage_hint.GetX(), aligned_coverage_hint.GetY(), 0})};
301 auto input_snapshot_size = input_snapshot.texture->GetSize();
304 Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
306 ISize(round(downsampled_size.x), round(downsampled_size.y));
310 input, snapshot_entity, source_rect_padded, input_snapshot_size);
323 fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
324 const ContentContext& renderer,
325 const std::shared_ptr<CommandBuffer>& command_buffer,
326 std::shared_ptr<Texture> input_texture,
327 const SamplerDescriptor& sampler_descriptor,
328 const DownsamplePassArgs& pass_args,
331 [&](
const ContentContext& renderer, RenderPass& pass) {
332 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
337 pass.
SetPipeline(renderer.GetTexturePipeline(pipeline_options));
339 TextureFillVertexShader::FrameInfo frame_info;
341 frame_info.texture_sampler_y_coord_scale = 1.0;
343 TextureFillFragmentShader::FragInfo frag_info;
344 frag_info.alpha = 1.0;
346 const Quad&
uvs = pass_args.uvs;
347 BindVertices<TextureFillVertexShader>(pass, host_buffer,
355 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
356 SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
359 TextureFillVertexShader::BindFrameInfo(
360 pass, host_buffer.EmplaceUniform(frame_info));
361 TextureFillFragmentShader::BindFragInfo(
362 pass, host_buffer.EmplaceUniform(frag_info));
363 TextureFillFragmentShader::BindTextureSampler(
365 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
366 linear_sampler_descriptor));
368 return pass.
Draw().ok();
370 fml::StatusOr<RenderTarget> render_target =
371 renderer.MakeSubpass(
"Gaussian Blur Filter", pass_args.subpass_size,
372 command_buffer, subpass_callback);
373 return render_target;
376 fml::StatusOr<RenderTarget> MakeBlurSubpass(
377 const ContentContext& renderer,
378 const std::shared_ptr<CommandBuffer>& command_buffer,
379 const RenderTarget& input_pass,
380 const SamplerDescriptor& sampler_descriptor,
382 const BlurParameters& blur_info,
383 std::optional<RenderTarget> destination_target,
384 const Quad& blur_uvs) {
389 std::shared_ptr<Texture> input_texture = input_pass.GetRenderTargetTexture();
395 [&](
const ContentContext& renderer, RenderPass& pass) {
396 GaussianBlurVertexShader::FrameInfo frame_info{
398 .texture_sampler_y_coord_scale = 1.0};
400 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
404 pass.
SetPipeline(renderer.GetGaussianBlurPipeline(options));
406 BindVertices<GaussianBlurVertexShader>(pass, host_buffer,
408 {blur_uvs[0], blur_uvs[0]},
409 {blur_uvs[1], blur_uvs[1]},
410 {blur_uvs[2], blur_uvs[2]},
411 {blur_uvs[3], blur_uvs[3]},
414 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
417 GaussianBlurFragmentShader::BindTextureSampler(
419 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
420 linear_sampler_descriptor));
421 GaussianBlurVertexShader::BindFrameInfo(
422 pass, host_buffer.EmplaceUniform(frame_info));
423 GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples =
426 GaussianBlurFragmentShader::BindKernelSamples(
427 pass, host_buffer.EmplaceUniform(kernel_samples));
428 return pass.
Draw().ok();
430 if (destination_target.has_value()) {
431 return renderer.MakeSubpass(
"Gaussian Blur Filter",
432 destination_target.value(), command_buffer,
435 return renderer.MakeSubpass(
"Gaussian Blur Filter",
subpass_size,
436 command_buffer, subpass_callback);
441 return static_cast<int>(std::round(radius * scalar));
445 const Entity& entity,
446 const std::shared_ptr<FilterInput>& input,
447 const Snapshot& input_snapshot,
449 const std::shared_ptr<Geometry>& geometry) {
450 auto clip_contents = std::make_shared<ClipContents>();
451 clip_contents->SetClipOperation(clip_operation);
452 clip_contents->SetGeometry(geometry);
454 clipper.SetContents(clip_contents);
455 auto restore = std::make_unique<ClipRestoreContents>();
456 Matrix entity_transform = entity.GetTransform();
457 Matrix blur_transform = blur_entity.GetTransform();
458 auto renderer = fml::MakeCopyable(
459 [blur_entity = blur_entity.Clone(), clipper = std::move(clipper),
460 restore = std::move(restore), entity_transform,
461 blur_transform](
const ContentContext& renderer,
const Entity& entity,
462 RenderPass& pass)
mutable {
464 clipper.SetClipDepth(entity.GetClipDepth());
465 clipper.SetTransform(entity.GetTransform() * entity_transform);
466 result = clipper.Render(renderer, pass) && result;
467 blur_entity.SetClipDepth(entity.GetClipDepth());
468 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
469 result = blur_entity.Render(renderer, pass) && result;
473 fml::MakeCopyable([blur_entity = std::move(blur_entity),
474 blur_transform](
const Entity& entity)
mutable {
475 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
476 return blur_entity.GetCoverage();
484 const Entity& entity,
485 const std::shared_ptr<FilterInput>& input,
486 const Snapshot& input_snapshot,
488 const std::shared_ptr<Geometry>& geometry,
490 switch (blur_style) {
495 input, input_snapshot,
496 std::move(blur_entity), geometry);
500 input, input_snapshot,
501 std::move(blur_entity), geometry);
503 Entity snapshot_entity =
506 Matrix blurred_transform = blur_entity.GetTransform();
507 Matrix snapshot_transform =
508 entity.GetTransform() * snapshot_entity.GetTransform();
511 [blur_entity = blur_entity.Clone(), blurred_transform,
513 snapshot_entity = std::move(snapshot_entity)](
514 const ContentContext& renderer,
const Entity& entity,
515 RenderPass& pass)
mutable {
517 blur_entity.SetClipDepth(entity.GetClipDepth());
518 blur_entity.SetTransform(entity.GetTransform() *
520 result = result && blur_entity.Render(renderer, pass);
521 snapshot_entity.SetTransform(
522 entity.GetTransform() *
523 Matrix::MakeScale(1.f / source_space_scalar) *
525 snapshot_entity.SetClipDepth(entity.GetClipDepth());
526 result = result && snapshot_entity.Render(renderer, pass);
529 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
530 blurred_transform](
const Entity& entity)
mutable {
531 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
532 return blur_entity.GetCoverage();
541 "Applying gaussian blur without mipmap.";
548 const std::shared_ptr<Geometry>& mask_geometry)
549 : sigma_(sigma_x, sigma_y),
550 tile_mode_(tile_mode),
551 mask_blur_style_(mask_blur_style),
552 mask_geometry_(mask_geometry) {
564 Scalar raw_result = 4.0 / sigma;
566 Scalar exponent = round(log2f(raw_result));
568 exponent = std::max(-4.0f, exponent);
569 Scalar rounded = powf(2.0f, exponent);
573 if (rounded < 0.125f) {
574 Scalar rounded_plus = powf(2.0f, exponent + 1);
576 int kernel_size_plus = (ScaleBlurRadius(
blur_radius, rounded_plus) * 2) + 1;
580 static constexpr int32_t kEighthDownsampleKernalWidthMax = 41;
581 result = kernel_size_plus <= kEighthDownsampleKernalWidthMax ? rounded_plus
588 const Matrix& effect_transform,
589 const Rect& output_limit)
const {
595 return output_limit.
Expand(
Point(blur_radii.
x, blur_radii.
y));
601 const Matrix& effect_transform)
const {
602 if (inputs.empty()) {
605 std::optional<Rect> input_coverage = inputs[0]->GetCoverage(entity);
606 if (!input_coverage.has_value()) {
610 BlurInfo blur_info = CalculateBlurInfo(entity, effect_transform, sigma_);
611 return input_coverage.value().Expand(
612 Point(blur_info.local_padding.x, blur_info.local_padding.y));
623 std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
627 const Matrix& effect_transform,
628 const Rect& coverage,
629 const std::optional<Rect>& coverage_hint)
const {
630 if (inputs.empty()) {
634 BlurInfo blur_info = CalculateBlurInfo(entity, effect_transform, sigma_);
639 std::optional<Rect> expanded_coverage_hint;
640 if (coverage_hint.has_value()) {
641 expanded_coverage_hint = coverage_hint->Expand(blur_info.local_padding);
644 Entity snapshot_entity = entity.
Clone();
648 std::optional<Rect> source_expanded_coverage_hint;
649 if (expanded_coverage_hint.has_value()) {
650 source_expanded_coverage_hint = expanded_coverage_hint->TransformBounds(
655 std::optional<Snapshot> input_snapshot = GetSnapshot(
656 inputs[0], renderer, snapshot_entity, source_expanded_coverage_hint);
657 if (!input_snapshot.has_value()) {
668 input_snapshot->transform);
672 std::shared_ptr<CommandBuffer> command_buffer =
674 if (!command_buffer) {
678 DownsamplePassArgs downsample_pass_args = CalculateDownsamplePassArgs(
679 blur_info.scaled_sigma, blur_info.padding, input_snapshot.value(),
680 source_expanded_coverage_hint, inputs[0], snapshot_entity);
682 fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
683 renderer, command_buffer, input_snapshot->texture,
684 input_snapshot->sampler_descriptor, downsample_pass_args, tile_mode_);
686 if (!pass1_out.ok()) {
691 1.0 /
Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize());
695 fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
696 renderer, command_buffer, pass1_out.value(),
697 input_snapshot->sampler_descriptor, tile_mode_,
699 .blur_uv_offset = Point(0.0, pass1_pixel_size.y),
700 .blur_sigma = blur_info.scaled_sigma.y *
701 downsample_pass_args.effective_scalar.y,
702 .blur_radius = ScaleBlurRadius(
703 blur_info.blur_radius.y, downsample_pass_args.effective_scalar.y),
706 std::nullopt, blur_uvs);
708 if (!pass2_out.ok()) {
713 auto pass3_destination = pass2_out.value().GetRenderTargetTexture() !=
714 pass1_out.value().GetRenderTargetTexture()
715 ? std::optional<RenderTarget>(pass1_out.value())
716 :
std::optional<RenderTarget>(
std::nullopt);
718 fml::StatusOr<RenderTarget> pass3_out = MakeBlurSubpass(
719 renderer, command_buffer, pass2_out.value(),
720 input_snapshot->sampler_descriptor, tile_mode_,
722 .blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
723 .blur_sigma = blur_info.scaled_sigma.x *
724 downsample_pass_args.effective_scalar.x,
725 .blur_radius = ScaleBlurRadius(
726 blur_info.blur_radius.x, downsample_pass_args.effective_scalar.x),
729 pass3_destination, blur_uvs);
731 if (!pass3_out.ok()) {
737 ->Submit({command_buffer})
744 FML_DCHECK((pass1_out.value().GetRenderTargetSize() ==
745 pass2_out.value().GetRenderTargetSize()) &&
746 (pass2_out.value().GetRenderTargetSize() ==
747 pass3_out.value().GetRenderTargetSize()));
749 SamplerDescriptor sampler_desc = MakeSamplerDescriptor(
753 Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(),
757 downsample_pass_args.transform *
759 .sampler_descriptor = sampler_desc,
760 .opacity = input_snapshot->opacity},
763 return ApplyBlurStyle(mask_blur_style_, entity, inputs[0],
764 input_snapshot.value(), std::move(blur_output_entity),
765 mask_geometry_, blur_info.source_space_scalar);
773 const std::shared_ptr<FilterInput>& filter_input,
775 const Rect& source_rect,
776 const ISize& texture_size) {
777 Matrix input_transform = filter_input->GetLocalTransform(entity);
781 {1.0f / texture_size.
width, 1.0f / texture_size.
height, 1.0f});
782 return uv_transform.
Transform(coverage_quad);
790 Scalar clamped = std::min(sigma, kMaxSigma);
791 constexpr
Scalar a = 3.4e-06;
794 Scalar scalar = c +
b * clamped + a * clamped * clamped;
795 return clamped * scalar;
826 result.
samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{
828 .coefficient = expf(-0.5f * (x * x) /
832 tally += result.
samples[i].coefficient;
836 for (
auto& sample : result.samples) {
837 sample.coefficient /= tally;
847 GaussianBlurPipeline::FragmentShader::KernelSamples result;
848 result.sample_count = ((parameters.
sample_count - 1) / 2) + 1;
849 int32_t middle = result.sample_count / 2;
852 for (
int i = 0; i < result.sample_count; i++) {
854 result.samples[i] = parameters.
samples[j++];
856 GaussianBlurPipeline::FragmentShader::KernelSample left =
858 GaussianBlurPipeline::FragmentShader::KernelSample right =
860 result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{
861 .uv_offset = (left.uv_offset * left.coefficient +
862 right.uv_offset * right.coefficient) /
863 (left.coefficient + right.coefficient),
864 .coefficient = left.coefficient + right.coefficient,