Flutter Engine
The Flutter Engine
gaussian_blur_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
6
7#include <cmath>
8
9#include "flutter/fml/make_copyable.h"
12#include "impeller/entity/texture_fill.frag.h"
13#include "impeller/entity/texture_fill.vert.h"
16
17namespace impeller {
18
21
23
24namespace {
25
26// 48 comes from gaussian.frag.
27const int32_t kMaxKernelSize = 50;
28constexpr Scalar kMaxSigma = 500.0f;
29
30SamplerDescriptor MakeSamplerDescriptor(MinMagFilter filter,
31 SamplerAddressMode address_mode) {
32 SamplerDescriptor sampler_desc;
33 sampler_desc.min_filter = filter;
34 sampler_desc.mag_filter = filter;
35 sampler_desc.width_address_mode = address_mode;
36 sampler_desc.height_address_mode = address_mode;
37 return sampler_desc;
38}
39
40template <typename T>
41void BindVertices(RenderPass& pass,
42 HostBuffer& host_buffer,
43 std::initializer_list<typename T::PerVertexData>&& vertices) {
45 vtx_builder.AddVertices(vertices);
46 pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
47}
48
49void SetTileMode(SamplerDescriptor* descriptor,
51 Entity::TileMode tile_mode) {
52 switch (tile_mode) {
54 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
57 }
58 break;
62 break;
66 break;
70 break;
71 }
72}
73
74/// Makes a subpass that will render the scaled down input and add the
75/// transparent gutter required for the blur halo.
76fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
78 const std::shared_ptr<CommandBuffer>& command_buffer,
79 std::shared_ptr<Texture> input_texture,
80 const SamplerDescriptor& sampler_descriptor,
81 const Quad& uvs,
82 const ISize& subpass_size,
83 Entity::TileMode tile_mode) {
84 ContentContext::SubpassCallback subpass_callback =
85 [&](const ContentContext& renderer, RenderPass& pass) {
86 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
87
88 pass.SetCommandLabel("Gaussian blur downsample");
89 auto pipeline_options = OptionsFromPass(pass);
90 pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
91 pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
92
93 TextureFillVertexShader::FrameInfo frame_info;
94 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
95 frame_info.texture_sampler_y_coord_scale = 1.0;
96
97 TextureFillFragmentShader::FragInfo frag_info;
98 frag_info.alpha = 1.0;
99
100 BindVertices<TextureFillVertexShader>(pass, host_buffer,
101 {
102 {Point(0, 0), uvs[0]},
103 {Point(1, 0), uvs[1]},
104 {Point(0, 1), uvs[2]},
105 {Point(1, 1), uvs[3]},
106 });
107
108 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
109 SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
110 linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
111 linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
112 TextureFillVertexShader::BindFrameInfo(
113 pass, host_buffer.EmplaceUniform(frame_info));
114 TextureFillFragmentShader::BindFragInfo(
115 pass, host_buffer.EmplaceUniform(frag_info));
116 TextureFillFragmentShader::BindTextureSampler(
117 pass, input_texture,
118 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
119 linear_sampler_descriptor));
120
121 return pass.Draw().ok();
122 };
123 fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
124 "Gaussian Blur Filter", subpass_size, command_buffer, subpass_callback);
125 return render_target;
126}
127
128fml::StatusOr<RenderTarget> MakeBlurSubpass(
130 const std::shared_ptr<CommandBuffer>& command_buffer,
131 const RenderTarget& input_pass,
132 const SamplerDescriptor& sampler_descriptor,
133 Entity::TileMode tile_mode,
134 const BlurParameters& blur_info,
135 std::optional<RenderTarget> destination_target,
136 const Quad& blur_uvs) {
137 if (blur_info.blur_sigma < kEhCloseEnough) {
138 return input_pass;
139 }
140
141 std::shared_ptr<Texture> input_texture = input_pass.GetRenderTargetTexture();
142
143 // TODO(gaaclarke): This blurs the whole image, but because we know the clip
144 // region we could focus on just blurring that.
145 ISize subpass_size = input_texture->GetSize();
146 ContentContext::SubpassCallback subpass_callback =
147 [&](const ContentContext& renderer, RenderPass& pass) {
148 GaussianBlurVertexShader::FrameInfo frame_info{
149 .mvp = Matrix::MakeOrthographic(ISize(1, 1)),
150 .texture_sampler_y_coord_scale = 1.0};
151
152 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
153
155 options.primitive_type = PrimitiveType::kTriangleStrip;
156 pass.SetPipeline(renderer.GetGaussianBlurPipeline(options));
157
158 BindVertices<GaussianBlurVertexShader>(pass, host_buffer,
159 {
160 {blur_uvs[0], blur_uvs[0]},
161 {blur_uvs[1], blur_uvs[1]},
162 {blur_uvs[2], blur_uvs[2]},
163 {blur_uvs[3], blur_uvs[3]},
164 });
165
166 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
167 linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
168 linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
169 GaussianBlurFragmentShader::BindTextureSampler(
170 pass, input_texture,
171 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
172 linear_sampler_descriptor));
173 GaussianBlurVertexShader::BindFrameInfo(
174 pass, host_buffer.EmplaceUniform(frame_info));
175 GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples =
177 FML_CHECK(kernel_samples.sample_count < kMaxKernelSize);
178 GaussianBlurFragmentShader::BindKernelSamples(
179 pass, host_buffer.EmplaceUniform(kernel_samples));
180 return pass.Draw().ok();
181 };
182 if (destination_target.has_value()) {
183 return renderer.MakeSubpass("Gaussian Blur Filter",
184 destination_target.value(), command_buffer,
185 subpass_callback);
186 } else {
187 return renderer.MakeSubpass("Gaussian Blur Filter", subpass_size,
188 command_buffer, subpass_callback);
189 }
190}
191
192/// Returns `rect` relative to `reference`, where Rect::MakeXYWH(0,0,1,1) will
193/// be returned when `rect` == `reference`.
194Rect MakeReferenceUVs(const Rect& reference, const Rect& rect) {
195 Rect result = Rect::MakeOriginSize(rect.GetOrigin() - reference.GetOrigin(),
196 rect.GetSize());
197 return result.Scale(1.0f / Vector2(reference.GetSize()));
198}
199
200int ScaleBlurRadius(Scalar radius, Scalar scalar) {
201 return static_cast<int>(std::round(radius * scalar));
202}
203
204Entity ApplyClippedBlurStyle(Entity::ClipOperation clip_operation,
205 const Entity& entity,
206 const std::shared_ptr<FilterInput>& input,
207 const Snapshot& input_snapshot,
208 Entity blur_entity,
209 const std::shared_ptr<Geometry>& geometry) {
210 auto clip_contents = std::make_shared<ClipContents>();
211 clip_contents->SetClipOperation(clip_operation);
212 clip_contents->SetGeometry(geometry);
213 Entity clipper;
214 clipper.SetContents(clip_contents);
215 auto restore = std::make_unique<ClipRestoreContents>();
216 Matrix entity_transform = entity.GetTransform();
217 Matrix blur_transform = blur_entity.GetTransform();
219 [blur_entity = blur_entity.Clone(), clipper = std::move(clipper),
220 restore = std::move(restore), entity_transform,
221 blur_transform](const ContentContext& renderer, const Entity& entity,
222 RenderPass& pass) mutable {
223 bool result = true;
224 clipper.SetClipDepth(entity.GetClipDepth());
225 clipper.SetTransform(entity.GetTransform() * entity_transform);
226 result = clipper.Render(renderer, pass) && result;
227 blur_entity.SetClipDepth(entity.GetClipDepth());
228 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
229 result = blur_entity.Render(renderer, pass) && result;
230 return result;
231 });
232 auto coverage =
233 fml::MakeCopyable([blur_entity = std::move(blur_entity),
234 blur_transform](const Entity& entity) mutable {
235 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
236 return blur_entity.GetCoverage();
237 });
240 return result;
241}
242
243Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style,
244 const Entity& entity,
245 const std::shared_ptr<FilterInput>& input,
246 const Snapshot& input_snapshot,
247 Entity blur_entity,
248 const std::shared_ptr<Geometry>& geometry,
249 Vector2 source_space_scalar) {
250 switch (blur_style) {
252 return blur_entity;
254 return ApplyClippedBlurStyle(Entity::ClipOperation::kIntersect, entity,
255 input, input_snapshot,
256 std::move(blur_entity), geometry);
257 break;
259 return ApplyClippedBlurStyle(Entity::ClipOperation::kDifference, entity,
260 input, input_snapshot,
261 std::move(blur_entity), geometry);
263 Entity snapshot_entity =
264 Entity::FromSnapshot(input_snapshot, entity.GetBlendMode());
266 Matrix blurred_transform = blur_entity.GetTransform();
267 Matrix snapshot_transform =
268 entity.GetTransform() * snapshot_entity.GetTransform();
271 [blur_entity = blur_entity.Clone(), blurred_transform,
272 source_space_scalar, snapshot_transform,
273 snapshot_entity = std::move(snapshot_entity)](
274 const ContentContext& renderer, const Entity& entity,
275 RenderPass& pass) mutable {
276 bool result = true;
277 blur_entity.SetClipDepth(entity.GetClipDepth());
278 blur_entity.SetTransform(entity.GetTransform() *
279 blurred_transform);
280 result = result && blur_entity.Render(renderer, pass);
281 snapshot_entity.SetTransform(
282 entity.GetTransform() *
283 Matrix::MakeScale(1.f / source_space_scalar) *
284 snapshot_transform);
285 snapshot_entity.SetClipDepth(entity.GetClipDepth());
286 result = result && snapshot_entity.Render(renderer, pass);
287 return result;
288 }),
289 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
290 blurred_transform](const Entity& entity) mutable {
291 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
292 return blur_entity.GetCoverage();
293 })));
294 return result;
295 }
296 }
297}
298} // namespace
299
301 "Applying gaussian blur without mipmap.";
302
304 Scalar sigma_x,
305 Scalar sigma_y,
306 Entity::TileMode tile_mode,
307 BlurStyle mask_blur_style,
308 const std::shared_ptr<Geometry>& mask_geometry)
309 : sigma_x_(sigma_x),
310 sigma_y_(sigma_y),
311 tile_mode_(tile_mode),
312 mask_blur_style_(mask_blur_style),
313 mask_geometry_(mask_geometry) {
314 // This is supposed to be enforced at a higher level.
315 FML_DCHECK(mask_blur_style == BlurStyle::kNormal || mask_geometry);
316}
317
318// This value was extracted from Skia, see:
319// * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/ganesh/GrBlurUtils.cpp#L2561-L2576
320// * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/BlurUtils.h#L57
322 if (sigma <= 4) {
323 return 1.0;
324 }
325 Scalar raw_result = 4.0 / sigma;
326 // Round to the nearest 1/(2^n) to get the best quality down scaling.
327 Scalar exponent = round(log2f(raw_result));
328 // Don't scale down below 1/16th to preserve signal.
329 exponent = std::max(-4.0f, exponent);
330 Scalar rounded = powf(2.0f, exponent);
331 Scalar result = rounded;
332 // Extend the range of the 1/8th downsample based on the effective kernel size
333 // for the blur.
334 if (rounded < 0.125f) {
335 Scalar rounded_plus = powf(2.0f, exponent + 1);
336 Scalar blur_radius = CalculateBlurRadius(sigma);
337 int kernel_size_plus = (ScaleBlurRadius(blur_radius, rounded_plus) * 2) + 1;
338 // This constant was picked by looking at the results to make sure no
339 // shimmering was introduced at the highest sigma values that downscale to
340 // 1/16th.
341 static constexpr int32_t kEighthDownsampleKernalWidthMax = 41;
342 result = kernel_size_plus <= kEighthDownsampleKernalWidthMax ? rounded_plus
343 : rounded;
344 }
345 return result;
346};
347
349 const Matrix& effect_transform,
350 const Rect& output_limit) const {
351 Vector2 scaled_sigma = {ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)};
352 Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x),
353 CalculateBlurRadius(scaled_sigma.y)};
354 Vector3 blur_radii =
355 effect_transform.Basis() * Vector3{blur_radius.x, blur_radius.y, 0.0};
356 return output_limit.Expand(Point(blur_radii.x, blur_radii.y));
357}
358
359namespace {
360Vector2 ExtractScale(const Matrix& matrix) {
361 Vector2 entity_scale_x = matrix * Vector2(1.0, 0.0);
362 Vector2 entity_scale_y = matrix * Vector2(0.0, 1.0);
363 return Vector2(entity_scale_x.GetLength(), entity_scale_y.GetLength());
364}
365} // namespace
366
369 const Entity& entity,
370 const Matrix& effect_transform) const {
371 if (inputs.empty()) {
372 return {};
373 }
374 std::optional<Rect> input_coverage = inputs[0]->GetCoverage(entity);
375 if (!input_coverage.has_value()) {
376 return {};
377 }
378
379 const Vector2 source_space_scalar =
380 ExtractScale(entity.GetTransform().Basis());
381 Vector2 scaled_sigma = (Matrix::MakeScale(source_space_scalar) *
382 Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)))
383 .Abs();
384 scaled_sigma.x = std::min(scaled_sigma.x, kMaxSigma);
385 scaled_sigma.y = std::min(scaled_sigma.y, kMaxSigma);
386 Vector2 blur_radius = Vector2(CalculateBlurRadius(scaled_sigma.x),
387 CalculateBlurRadius(scaled_sigma.y));
388 Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
389 Vector2 local_padding =
390 (Matrix::MakeScale(source_space_scalar) * padding).Abs();
391 return input_coverage.value().Expand(Point(local_padding.x, local_padding.y));
392}
393
394// A brief overview how this works:
395// 1) Snapshot the filter input.
396// 2) Perform downsample pass. This also inserts the gutter around the input
397// snapshot since the blur can render outside the bounds of the snapshot.
398// 3) Perform 1D horizontal blur pass.
399// 4) Perform 1D vertical blur pass.
400// 5) Apply the blur style to the blur result. This may just mask the output or
401// draw the original snapshot over the result.
402std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
405 const Entity& entity,
406 const Matrix& effect_transform,
407 const Rect& coverage,
408 const std::optional<Rect>& coverage_hint) const {
409 if (inputs.empty()) {
410 return std::nullopt;
411 }
412
413 // Source space here is scaled by the entity's transform. This is a
414 // requirement for text to be rendered correctly. You can think of this as
415 // "scaled source space" or "un-rotated local space". The entity's rotation is
416 // applied to the result of the blur as part of the result's transform.
417 const Vector2 source_space_scalar =
418 ExtractScale(entity.GetTransform().Basis());
419
420 Vector2 scaled_sigma =
421 (effect_transform.Basis() * Matrix::MakeScale(source_space_scalar) * //
422 Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)))
423 .Abs();
424 scaled_sigma.x = std::min(scaled_sigma.x, kMaxSigma);
425 scaled_sigma.y = std::min(scaled_sigma.y, kMaxSigma);
426 Vector2 blur_radius = Vector2(CalculateBlurRadius(scaled_sigma.x),
427 CalculateBlurRadius(scaled_sigma.y));
428 Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
429 Vector2 local_padding =
430 (Matrix::MakeScale(source_space_scalar) * padding).Abs();
431
432 // Apply as much of the desired padding as possible from the source. This may
433 // be ignored so must be accounted for in the downsample pass by adding a
434 // transparent gutter.
435 std::optional<Rect> expanded_coverage_hint;
436 if (coverage_hint.has_value()) {
437 expanded_coverage_hint = coverage_hint->Expand(local_padding);
438 }
439
440 int32_t mip_count = kBlurFilterRequiredMipCount;
441 if (renderer.GetContext()->GetBackendType() ==
443 // TODO(https://github.com/flutter/flutter/issues/141732): Implement mip map
444 // generation on opengles.
445 mip_count = 1;
446 }
447
448 Entity snapshot_entity = entity.Clone();
449 snapshot_entity.SetTransform(Matrix::MakeScale(source_space_scalar));
450 std::optional<Rect> source_expanded_coverage_hint;
451 if (expanded_coverage_hint.has_value()) {
452 source_expanded_coverage_hint = expanded_coverage_hint->TransformBounds(
453 Matrix::MakeScale(source_space_scalar) *
454 entity.GetTransform().Invert());
455 }
456
457 std::optional<Snapshot> input_snapshot =
458 inputs[0]->GetSnapshot("GaussianBlur", renderer, snapshot_entity,
459 /*coverage_limit=*/source_expanded_coverage_hint,
460 /*mip_count=*/mip_count);
461 if (!input_snapshot.has_value()) {
462 return std::nullopt;
463 }
464
465 if (scaled_sigma.x < kEhCloseEnough && scaled_sigma.y < kEhCloseEnough) {
466 Entity result =
467 Entity::FromSnapshot(input_snapshot.value(),
468 entity.GetBlendMode()); // No blur to render.
469 result.SetTransform(entity.GetTransform() *
470 Matrix::MakeScale(1.f / source_space_scalar) *
471 input_snapshot->transform);
472 return result;
473 }
474
475 // In order to avoid shimmering in downsampling step, we should have mips.
476 if (input_snapshot->texture->GetMipCount() <= 1) {
478 }
479 FML_DCHECK(!input_snapshot->texture->NeedsMipmapGeneration());
480
481 Scalar desired_scalar =
482 std::min(CalculateScale(scaled_sigma.x), CalculateScale(scaled_sigma.y));
483 // TODO(jonahwilliams): If desired_scalar is 1.0 and we fully acquired the
484 // gutter from the expanded_coverage_hint, we can skip the downsample pass.
485 // pass.
486 Vector2 downsample_scalar(desired_scalar, desired_scalar);
487 Rect source_rect = Rect::MakeSize(input_snapshot->texture->GetSize());
488 Rect source_rect_padded = source_rect.Expand(padding);
489 Matrix padding_snapshot_adjustment = Matrix::MakeTranslation(-padding);
490 // TODO(gaaclarke): The padding could be removed if we know it's not needed or
491 // resized to account for the expanded_clip_coverage. There doesn't appear
492 // to be the math to make those calculations though. The following
493 // optimization works, but causes a shimmer as a result of
494 // https://github.com/flutter/flutter/issues/140193 so it isn't applied.
495 //
496 // !input_snapshot->GetCoverage()->Expand(-local_padding)
497 // .Contains(coverage_hint.value()))
498 Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
499 ISize subpass_size =
500 ISize(round(downsampled_size.x), round(downsampled_size.y));
501 Vector2 effective_scalar =
502 Vector2(subpass_size) / source_rect_padded.GetSize();
503
504 Quad uvs = CalculateUVs(inputs[0], snapshot_entity, source_rect_padded,
505 input_snapshot->texture->GetSize());
506
507 std::shared_ptr<CommandBuffer> command_buffer =
508 renderer.GetContext()->CreateCommandBuffer();
509 if (!command_buffer) {
510 return std::nullopt;
511 }
512
513 fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
514 renderer, command_buffer, input_snapshot->texture,
515 input_snapshot->sampler_descriptor, uvs, subpass_size, tile_mode_);
516
517 if (!pass1_out.ok()) {
518 return std::nullopt;
519 }
520
521 Vector2 pass1_pixel_size =
522 1.0 / Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize());
523
524 std::optional<Rect> input_snapshot_coverage = input_snapshot->GetCoverage();
525 Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
526 FML_DCHECK(input_snapshot.value().transform.IsTranslationScaleOnly());
527 if (source_expanded_coverage_hint.has_value() &&
528 input_snapshot_coverage.has_value()) {
529 // Only process the uvs where the blur is happening, not the whole texture.
530 std::optional<Rect> uvs =
531 MakeReferenceUVs(input_snapshot_coverage.value(),
532 source_expanded_coverage_hint.value())
533 .Intersection(Rect::MakeSize(Size(1, 1)));
534 FML_DCHECK(uvs.has_value());
535 if (uvs.has_value()) {
536 blur_uvs[0] = uvs->GetLeftTop();
537 blur_uvs[1] = uvs->GetRightTop();
538 blur_uvs[2] = uvs->GetLeftBottom();
539 blur_uvs[3] = uvs->GetRightBottom();
540 }
541 }
542
543 fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
544 renderer, command_buffer, /*input_pass=*/pass1_out.value(),
545 input_snapshot->sampler_descriptor, tile_mode_,
546 BlurParameters{
547 .blur_uv_offset = Point(0.0, pass1_pixel_size.y),
548 .blur_sigma = scaled_sigma.y * effective_scalar.y,
549 .blur_radius = ScaleBlurRadius(blur_radius.y, effective_scalar.y),
550 .step_size = 1,
551 },
552 /*destination_target=*/std::nullopt, blur_uvs);
553
554 if (!pass2_out.ok()) {
555 return std::nullopt;
556 }
557
558 // Only ping pong if the first pass actually created a render target.
559 auto pass3_destination = pass2_out.value().GetRenderTargetTexture() !=
560 pass1_out.value().GetRenderTargetTexture()
561 ? std::optional<RenderTarget>(pass1_out.value())
562 : std::optional<RenderTarget>(std::nullopt);
563
564 fml::StatusOr<RenderTarget> pass3_out = MakeBlurSubpass(
565 renderer, command_buffer, /*input_pass=*/pass2_out.value(),
566 input_snapshot->sampler_descriptor, tile_mode_,
567 BlurParameters{
568 .blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
569 .blur_sigma = scaled_sigma.x * effective_scalar.x,
570 .blur_radius = ScaleBlurRadius(blur_radius.x, effective_scalar.x),
571 .step_size = 1,
572 },
573 pass3_destination, blur_uvs);
574
575 if (!pass3_out.ok()) {
576 return std::nullopt;
577 }
578
579 if (!renderer.GetContext()
580 ->GetCommandQueue()
581 ->Submit(/*buffers=*/{command_buffer})
582 .ok()) {
583 return std::nullopt;
584 }
585
586 // The ping-pong approach requires that each render pass output has the same
587 // size.
588 FML_DCHECK((pass1_out.value().GetRenderTargetSize() ==
589 pass2_out.value().GetRenderTargetSize()) &&
590 (pass2_out.value().GetRenderTargetSize() ==
591 pass3_out.value().GetRenderTargetSize()));
592
593 SamplerDescriptor sampler_desc = MakeSamplerDescriptor(
595
596 Entity blur_output_entity = Entity::FromSnapshot(
597 Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(),
598 .transform = entity.GetTransform() * //
599 Matrix::MakeScale(1.f / source_space_scalar) * //
600 input_snapshot->transform * //
601 padding_snapshot_adjustment * //
602 Matrix::MakeScale(1 / effective_scalar),
603 .sampler_descriptor = sampler_desc,
604 .opacity = input_snapshot->opacity},
605 entity.GetBlendMode());
606
607 return ApplyBlurStyle(mask_blur_style_, entity, inputs[0],
608 input_snapshot.value(), std::move(blur_output_entity),
609 mask_geometry_, source_space_scalar);
610}
611
613 return static_cast<Radius>(Sigma(sigma)).radius;
614}
615
617 const std::shared_ptr<FilterInput>& filter_input,
618 const Entity& entity,
619 const Rect& source_rect,
620 const ISize& texture_size) {
621 Matrix input_transform = filter_input->GetLocalTransform(entity);
622 Quad coverage_quad = source_rect.GetTransformedPoints(input_transform);
623
624 Matrix uv_transform = Matrix::MakeScale(
625 {1.0f / texture_size.width, 1.0f / texture_size.height, 1.0f});
626 return uv_transform.Transform(coverage_quad);
627}
628
629// This function was calculated by observing Skia's behavior. Its blur at 500
630// seemed to be 0.15. Since we clamp at 500 I solved the quadratic equation
631// that puts the minima there and a f(0)=1.
633 // Limit the kernel size to 1000x1000 pixels, like Skia does.
634 Scalar clamped = std::min(sigma, kMaxSigma);
635 constexpr Scalar a = 3.4e-06;
636 constexpr Scalar b = -3.4e-3;
637 constexpr Scalar c = 1.f;
638 Scalar scalar = c + b * clamped + a * clamped * clamped;
639 return clamped * scalar;
640}
641
642GaussianBlurPipeline::FragmentShader::KernelSamples GenerateBlurInfo(
643 BlurParameters parameters) {
644 GaussianBlurPipeline::FragmentShader::KernelSamples result;
645 result.sample_count =
646 ((2 * parameters.blur_radius) / parameters.step_size) + 1;
647
648 // Chop off the last samples if the radius >= 3 where they account for < 1.56%
649 // of the result.
650 int x_offset = 0;
651 if (parameters.blur_radius >= 3) {
652 result.sample_count -= 2;
653 x_offset = 1;
654 }
655
656 Scalar tally = 0.0f;
657 for (int i = 0; i < result.sample_count; ++i) {
658 int x = x_offset + (i * parameters.step_size) - parameters.blur_radius;
659 result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{
660 .uv_offset = parameters.blur_uv_offset * x,
661 .coefficient = expf(-0.5f * (x * x) /
662 (parameters.blur_sigma * parameters.blur_sigma)) /
663 (sqrtf(2.0f * M_PI) * parameters.blur_sigma),
664 };
665 tally += result.samples[i].coefficient;
666 }
667
668 // Make sure everything adds up to 1.
669 for (auto& sample : result.samples) {
670 sample.coefficient /= tally;
671 }
672
673 return result;
674}
675
676// This works by shrinking the kernel size by 2 and relying on lerp to read
677// between the samples.
678GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(
679 GaussianBlurPipeline::FragmentShader::KernelSamples parameters) {
680 GaussianBlurPipeline::FragmentShader::KernelSamples result;
681 result.sample_count = ((parameters.sample_count - 1) / 2) + 1;
682 int32_t middle = result.sample_count / 2;
683 int32_t j = 0;
684 for (int i = 0; i < result.sample_count; i++) {
685 if (i == middle) {
686 result.samples[i] = parameters.samples[j++];
687 } else {
688 GaussianBlurPipeline::FragmentShader::KernelSample left =
689 parameters.samples[j];
690 GaussianBlurPipeline::FragmentShader::KernelSample right =
691 parameters.samples[j + 1];
692 result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{
693 .uv_offset = (left.uv_offset * left.coefficient +
694 right.uv_offset * right.coefficient) /
695 (left.coefficient + right.coefficient),
696 .coefficient = left.coefficient + right.coefficient,
697 };
698 j += 2;
699 }
700 }
701
702 return result;
703}
704
705} // namespace impeller
const char * options
#define M_PI
static void round(SkPoint *p)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
const T & value() const
Definition: status_or.h:77
bool ok() const
Definition: status_or.h:75
bool ok() const
Definition: status.h:71
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
static std::shared_ptr< Contents > MakeAnonymous(RenderProc render_proc, CoverageProc coverage_proc)
Definition: contents.cc:41
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:62
static Entity FromSnapshot(const Snapshot &snapshot, BlendMode blend_mode=BlendMode::kSourceOver)
Create an entity that can be used to render a given snapshot.
Definition: entity.cc:22
std::optional< Rect > GetCoverage() const
Definition: entity.cc:66
BlendMode GetBlendMode() const
Definition: entity.cc:119
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:90
Entity Clone() const
Definition: entity.cc:191
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
@ kNormal
Blurred inside and outside.
@ kOuter
Nothing inside, blurred outside.
@ kInner
Blurred inside, nothing outside.
@ kSolid
Solid inside, blurred outside.
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
GaussianBlurFilterContents(Scalar sigma_x, Scalar sigma_y, Entity::TileMode tile_mode, BlurStyle mask_blur_style, const std::shared_ptr< Geometry > &mask_geometry)
std::optional< Rect > GetFilterSourceCoverage(const Matrix &effect_transform, const Rect &output_limit) const override
Internal utility method for |GetSourceCoverage| that computes the inverse effect of this transform on...
std::optional< Rect > GetFilterCoverage(const FilterInput::Vector &inputs, const Entity &entity, const Matrix &effect_transform) const override
Internal utility method for |GetLocalCoverage| that computes the output coverage of this filter acros...
static Quad CalculateUVs(const std::shared_ptr< FilterInput > &filter_input, const Entity &entity, const Rect &source_rect, const ISize &texture_size)
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Definition: host_buffer.h:50
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
virtual bool SetVertexBuffer(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
Definition: render_pass.cc:123
virtual void SetPipeline(const std::shared_ptr< Pipeline< PipelineDescriptor > > &pipeline)
The pipeline to use for this command.
Definition: render_pass.cc:92
virtual fml::Status Draw()
Record the currently pending command.
Definition: render_pass.cc:127
virtual void SetCommandLabel(std::string_view label)
The debugging label to use for the command.
Definition: render_pass.cc:97
FragmentShader_ FragmentShader
Definition: pipeline.h:107
std::shared_ptr< Texture > GetRenderTargetTexture() const
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
static bool b
struct MyStruct a[10]
GAsyncResult * result
#define FML_DLOG(severity)
Definition: logging.h:102
#define FML_CHECK(condition)
Definition: logging.h:85
#define FML_DCHECK(condition)
Definition: logging.h:103
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
GaussianBlurPipeline::FragmentShader::KernelSamples GenerateBlurInfo(BlurParameters parameters)
Point Vector2
Definition: point.h:326
GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(GaussianBlurPipeline::FragmentShader::KernelSamples parameters)
float Scalar
Definition: scalar.h:18
constexpr float kEhCloseEnough
Definition: constants.h:56
TRect< Scalar > Rect
Definition: rect.h:769
TPoint< Scalar > Point
Definition: point.h:322
SamplerAddressMode
Definition: formats.h:435
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...
TSize< Scalar > Size
Definition: size.h:137
MinMagFilter
Describes how the texture should be sampled when the texture is being shrunk (minified) or expanded (...
Definition: formats.h:409
ContentContextOptions OptionsFromPass(const RenderPass &pass)
Definition: contents.cc:19
ISize64 ISize
Definition: size.h:140
std::array< Point, 4 > Quad
Definition: point.h:327
GaussianBlurPipeline::FragmentShader GaussianBlurFragmentShader
GaussianBlurPipeline::VertexShader GaussianBlurVertexShader
SK_API sk_sp< PrecompileColorFilter > Matrix()
SIN Vec< N, float > ceil(const Vec< N, float > &x)
Definition: SkVx.h:702
Definition: ref_ptr.h:256
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:497
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:229
Matrix Invert() const
Definition: matrix.cc:97
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:487
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
SamplerAddressMode width_address_mode
SamplerAddressMode height_address_mode
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
Represents a texture and its intended draw transform/sampler configuration.
Definition: snapshot.h:24
constexpr Type GetLength() const
Definition: point.h:206
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:317
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
constexpr std::array< TPoint< T >, 4 > GetTransformedPoints(const Matrix &transform) const
Definition: rect.h:417
static constexpr TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition: rect.h:605
constexpr TPoint< Type > GetOrigin() const
Returns the upper left corner of the rectangle as specified by the left/top or x/y values when it was...
Definition: rect.h:310
Type height
Definition: size.h:23
Type width
Definition: size.h:22
#define ERROR(message)
Definition: elf_loader.cc:260