| Flutter Impeller |

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
5 #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
6
7 #include <cmath>
8
9 #include "flutter/fml/make_copyable.h"
10 #include "impeller/entity/contents/clip_contents.h"
11 #include "impeller/entity/contents/content_context.h"
12 #include "impeller/entity/texture_fill.frag.h"
13 #include "impeller/entity/texture_fill.vert.h"
14 #include "impeller/renderer/render_pass.h"
15 #include "impeller/renderer/vertex_buffer_builder.h"
16
17 namespace impeller {
18
19 using GaussianBlurVertexShader = GaussianBlurPipeline::VertexShader;
20 using GaussianBlurFragmentShader = GaussianBlurPipeline::FragmentShader;
21
22 const int32_t GaussianBlurFilterContents::kBlurFilterRequiredMipCount = 4;
23
24 namespace {
25
26 constexpr Scalar kMaxSigma = 500.0f;
27
28 SamplerDescriptor MakeSamplerDescriptor(MinMagFilter filter,
29 SamplerAddressMode address_mode) {
30 SamplerDescriptor sampler_desc;
31 sampler_desc.min_filter = filter;
32 sampler_desc.mag_filter = filter;
33 sampler_desc.width_address_mode = address_mode;
34 sampler_desc.height_address_mode = address_mode;
35 return sampler_desc;
36 }
37
38 template <typename T>
39 void BindVertices(RenderPass& pass,
40 HostBuffer& host_buffer,
41 std::initializer_list<typename T::PerVertexData>&& vertices) {
42 VertexBufferBuilder<typename T::PerVertexData> vtx_builder;
43 vtx_builder.AddVertices(vertices);
44 pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
45 }
46
47 void SetTileMode(SamplerDescriptor* descriptor,
48 const ContentContext& renderer,
49 Entity::TileMode tile_mode) {
50 switch (tile_mode) {
51 case Entity::TileMode::kDecal:
52 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
53 descriptor->width_address_mode = SamplerAddressMode::kDecal;
54 descriptor->height_address_mode = SamplerAddressMode::kDecal;
55 }
56 break;
57 case Entity::TileMode::kClamp:
58 descriptor->width_address_mode = SamplerAddressMode::kClampToEdge;
59 descriptor->height_address_mode = SamplerAddressMode::kClampToEdge;
60 break;
61 case Entity::TileMode::kMirror:
62 descriptor->width_address_mode = SamplerAddressMode::kMirror;
63 descriptor->height_address_mode = SamplerAddressMode::kMirror;
64 break;
65 case Entity::TileMode::kRepeat:
66 descriptor->width_address_mode = SamplerAddressMode::kRepeat;
67 descriptor->height_address_mode = SamplerAddressMode::kRepeat;
68 break;
69 }
70 }
71
72 Vector2 Clamp(Vector2 vec2, Scalar min, Scalar max) {
73 return Vector2(std::clamp(vec2.x, /*lo=*/min, /*hi=*/max),
74 std::clamp(vec2.y, /*lo=*/min, /*hi=*/max));
75 }
76
77 Vector2 ExtractScale(const Matrix& matrix) {
78 Vector2 entity_scale_x = matrix * Vector2(1.0, 0.0);
79 Vector2 entity_scale_y = matrix * Vector2(0.0, 1.0);
80 return Vector2(entity_scale_x.GetLength(), entity_scale_y.GetLength());
81 }
82
83 struct BlurInfo {
84 /// The scalar that is used to get from source space to unrotated local space.
85 Vector2 source_space_scalar;
86 /// Sigma when considering an entity's scale and the effect transform.
87 Vector2 scaled_sigma;
88 /// Blur radius in source pixels based on scaled_sigma.
89 Vector2 blur_radius;
90 /// The halo padding in source space.
91 Vector2 padding;
92 /// Padding in unrotated local space.
93 Vector2 local_padding;
94 };
95
96 /// Calculates sigma derivatives necessary for rendering or calculating
97 /// coverage.
98 BlurInfo CalculateBlurInfo(const Entity& entity,
99 const Matrix& effect_transform,
100 Vector2 sigma) {
101 // Source space here is scaled by the entity's transform. This is a
102 // requirement for text to be rendered correctly. You can think of this as
103 // "scaled source space" or "un-rotated local space". The entity's rotation is
104 // applied to the result of the blur as part of the result's transform.
105 const Vector2 source_space_scalar =
106 ExtractScale(entity.GetTransform().Basis());
107
108 Vector2 scaled_sigma =
109 (effect_transform.Basis() * Matrix::MakeScale(source_space_scalar) * //
110 Vector2(GaussianBlurFilterContents::ScaleSigma(sigma.x),
111 GaussianBlurFilterContents::ScaleSigma(sigma.y)))
112 .Abs();
113 scaled_sigma = Clamp(scaled_sigma, 0, kMaxSigma);
114 Vector2 blur_radius =
115 Vector2(GaussianBlurFilterContents::CalculateBlurRadius(scaled_sigma.x),
116 GaussianBlurFilterContents::CalculateBlurRadius(scaled_sigma.y));
117 Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
118 Vector2 local_padding =
119 (Matrix::MakeScale(source_space_scalar) * padding).Abs();
120 return {
121 .source_space_scalar = source_space_scalar,
122 .scaled_sigma = scaled_sigma,
123 .blur_radius = blur_radius,
124 .padding = padding,
125 .local_padding = local_padding,
126 };
127 }
128
129 /// Perform FilterInput::GetSnapshot with safety checks.
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) {
134 int32_t mip_count = GaussianBlurFilterContents::kBlurFilterRequiredMipCount;
135 if (renderer.GetContext()->GetBackendType() ==
136 Context::BackendType::kOpenGLES) {
137 // TODO(https://github.com/flutter/flutter/issues/141732): Implement mip map
138 // generation on opengles.
139 mip_count = 1;
140 }
141
142 std::optional<Snapshot> input_snapshot =
143 input->GetSnapshot("GaussianBlur", renderer, entity,
144 /*coverage_limit=*/coverage_hint,
145 /*mip_count=*/mip_count);
146 if (!input_snapshot.has_value()) {
147 return std::nullopt;
148 }
149
150 // In order to avoid shimmering in downsampling step, we should have mips.
151 if (input_snapshot->texture->GetMipCount() <= 1) {
152 FML_DLOG(ERROR) << GaussianBlurFilterContents::kNoMipsError;
153 }
154 FML_DCHECK(!input_snapshot->texture->NeedsMipmapGeneration());
155
156 return input_snapshot;
157 }
158
159 /// Returns `rect` relative to `reference`, where Rect::MakeXYWH(0,0,1,1) will
160 /// be returned when `rect` == `reference`.
161 Rect MakeReferenceUVs(const Rect& reference, const Rect& rect) {
162 Rect result = Rect::MakeOriginSize(rect.GetOrigin() - reference.GetOrigin(),
163 rect.GetSize());
164 return result.Scale(1.0f / Vector2(reference.GetSize()));
165 }
166
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();
171 Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
172 FML_DCHECK(input_snapshot.transform.IsTranslationScaleOnly());
173 if (source_expanded_coverage_hint.has_value() &&
174 input_snapshot_coverage.has_value()) {
175 // Only process the uvs where the blur is happening, not the whole texture.
176 std::optional<Rect> uvs =
177 MakeReferenceUVs(input_snapshot_coverage.value(),
178 source_expanded_coverage_hint.value())
179 .Intersection(Rect::MakeSize(Size(1, 1)));
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();
186 }
187 }
188 return blur_uvs;
189 }
190
191 Scalar CeilToDivisible(Scalar val, Scalar divisor) {
192 if (divisor == 0.0f) {
193 return val;
194 }
195
196 Scalar remainder = fmod(val, divisor);
197 if (remainder != 0.0f) {
198 return val + (divisor - remainder);
199 } else {
200 return val;
201 }
202 }
203
204 Scalar FloorToDivisible(Scalar val, Scalar divisor) {
205 if (divisor == 0.0f) {
206 return val;
207 }
208
209 Scalar remainder = fmod(val, divisor);
210 if (remainder != 0.0f) {
211 return val - remainder;
212 } else {
213 return val;
214 }
215 }
216
217 struct DownsamplePassArgs {
218 /// The output size of the down-sampling pass.
219 ISize subpass_size;
220 /// The UVs that will be used for drawing to the down-sampling pass.
221 /// This effectively is chopping out a region of the input.
222 Quad uvs;
223 /// The effective scalar of the down-sample pass.
224 /// This isn't usually exactly as we'd calculate because it has to be rounded
225 /// to integer boundaries for generating the texture for the output.
226 Vector2 effective_scalar;
227 /// Transforms from unrotated local space to position the output from the
228 /// down-sample pass.
229 /// This can differ if we request a coverage hint but it is rejected, as is
230 /// the case with backdrop filters.
231 Matrix transform;
232 };
233
234 /// Calculates info required for the down-sampling pass.
235 DownsamplePassArgs CalculateDownsamplePassArgs(
236 Vector2 scaled_sigma,
237 Vector2 padding,
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) {
242 Scalar desired_scalar =
243 std::min(GaussianBlurFilterContents::CalculateScale(scaled_sigma.x),
244 GaussianBlurFilterContents::CalculateScale(scaled_sigma.y));
245 // TODO(jonahwilliams): If desired_scalar is 1.0 and we fully acquired the
246 // gutter from the expanded_coverage_hint, we can skip the downsample pass.
247 // pass.
248 Vector2 downsample_scalar(desired_scalar, desired_scalar);
249 // TODO(gaaclarke): The padding could be removed if we know it's not needed or
250 // resized to account for the expanded_clip_coverage. There doesn't appear
251 // to be the math to make those calculations though. The following
252 // optimization works, but causes a shimmer as a result of
253 // https://github.com/flutter/flutter/issues/140193 so it isn't applied.
254 //
255 // !input_snapshot->GetCoverage()->Expand(-local_padding)
256 // .Contains(coverage_hint.value()))
257
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())) {
263 // If the snapshot's transform is the identity transform and we have
264 // coverage hint that fits inside of the snapshots coverage that means the
265 // coverage hint was ignored so we will trim out the area we are interested
266 // in the down-sample pass. This usually means we have a backdrop image
267 // filter.
268 //
269 // The region we cut out will be aligned with the down-sample divisor to
270 // avoid pixel alignment problems that create shimmering.
271 int32_t divisor = std::round(1.0f / desired_scalar);
272 Rect aligned_coverage_hint = Rect::MakeLTRB(
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());
277 aligned_coverage_hint = Rect::MakeXYWH(
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;
284 Scalar int_part;
285 FML_DCHECK(std::modf(downsampled_size.x, &int_part) == 0.0f);
286 FML_DCHECK(std::modf(downsampled_size.y, &int_part) == 0.0f);
287 (void)int_part;
288 ISize subpass_size = ISize(downsampled_size.x, downsampled_size.y);
289 Vector2 effective_scalar = Vector2(subpass_size) / source_size;
290 FML_DCHECK(effective_scalar == downsample_scalar);
291
292 Quad uvs = CalculateSnapshotUVs(input_snapshot, aligned_coverage_hint);
293 return {
294 .subpass_size = subpass_size,
295 .uvs = uvs,
296 .effective_scalar = effective_scalar,
297 .transform = Matrix::MakeTranslation(
298 {aligned_coverage_hint.GetX(), aligned_coverage_hint.GetY(), 0})};
299 } else {
300 //////////////////////////////////////////////////////////////////////////////
301 auto input_snapshot_size = input_snapshot.texture->GetSize();
302 Rect source_rect = Rect::MakeSize(input_snapshot_size);
303 Rect source_rect_padded = source_rect.Expand(padding);
304 Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
305 ISize subpass_size =
306 ISize(round(downsampled_size.x), round(downsampled_size.y));
307 Vector2 effective_scalar =
308 Vector2(subpass_size) / source_rect_padded.GetSize();
309 Quad uvs = GaussianBlurFilterContents::CalculateUVs(
310 input, snapshot_entity, source_rect_padded, input_snapshot_size);
311 return {
312 .subpass_size = subpass_size,
313 .uvs = uvs,
314 .effective_scalar = effective_scalar,
315 .transform =
316 input_snapshot.transform * Matrix::MakeTranslation(-padding),
317 };
318 }
319 }
320
321 /// Makes a subpass that will render the scaled down input and add the
322 /// transparent gutter required for the blur halo.
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,
329 Entity::TileMode tile_mode) {
330 ContentContext::SubpassCallback subpass_callback =
331 [&](const ContentContext& renderer, RenderPass& pass) {
332 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
333
334 pass.SetCommandLabel("Gaussian blur downsample");
335 auto pipeline_options = OptionsFromPass(pass);
336 pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
337 pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
338
339 TextureFillVertexShader::FrameInfo frame_info;
340 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
341 frame_info.texture_sampler_y_coord_scale = 1.0;
342
343 TextureFillFragmentShader::FragInfo frag_info;
344 frag_info.alpha = 1.0;
345
346 const Quad& uvs = pass_args.uvs;
347 BindVertices<TextureFillVertexShader>(pass, host_buffer,
348 {
349 {Point(0, 0), uvs[0]},
350 {Point(1, 0), uvs[1]},
351 {Point(0, 1), uvs[2]},
352 {Point(1, 1), uvs[3]},
353 });
354
355 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
356 SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
357 linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
358 linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
359 TextureFillVertexShader::BindFrameInfo(
360 pass, host_buffer.EmplaceUniform(frame_info));
361 TextureFillFragmentShader::BindFragInfo(
362 pass, host_buffer.EmplaceUniform(frag_info));
363 TextureFillFragmentShader::BindTextureSampler(
364 pass, input_texture,
365 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
366 linear_sampler_descriptor));
367
368 return pass.Draw().ok();
369 };
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;
374 }
375
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,
381 Entity::TileMode tile_mode,
382 const BlurParameters& blur_info,
383 std::optional<RenderTarget> destination_target,
384 const Quad& blur_uvs) {
385 if (blur_info.blur_sigma < kEhCloseEnough) {
386 return input_pass;
387 }
388
389 std::shared_ptr<Texture> input_texture = input_pass.GetRenderTargetTexture();
390
391 // TODO(gaaclarke): This blurs the whole image, but because we know the clip
392 // region we could focus on just blurring that.
393 ISize subpass_size = input_texture->GetSize();
394 ContentContext::SubpassCallback subpass_callback =
395 [&](const ContentContext& renderer, RenderPass& pass) {
396 GaussianBlurVertexShader::FrameInfo frame_info{
397 .mvp = Matrix::MakeOrthographic(ISize(1, 1)),
398 .texture_sampler_y_coord_scale = 1.0};
399
400 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
401
402 ContentContextOptions options = OptionsFromPass(pass);
403 options.primitive_type = PrimitiveType::kTriangleStrip;
404 pass.SetPipeline(renderer.GetGaussianBlurPipeline(options));
405
406 BindVertices<GaussianBlurVertexShader>(pass, host_buffer,
407 {
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]},
412 });
413
414 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
415 linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
416 linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
417 GaussianBlurFragmentShader::BindTextureSampler(
418 pass, input_texture,
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 =
424 LerpHackKernelSamples(GenerateBlurInfo(blur_info));
425 FML_CHECK(kernel_samples.sample_count <= kGaussianBlurMaxKernelSize);
426 GaussianBlurFragmentShader::BindKernelSamples(
427 pass, host_buffer.EmplaceUniform(kernel_samples));
428 return pass.Draw().ok();
429 };
430 if (destination_target.has_value()) {
431 return renderer.MakeSubpass("Gaussian Blur Filter",
432 destination_target.value(), command_buffer,
433 subpass_callback);
434 } else {
435 return renderer.MakeSubpass("Gaussian Blur Filter", subpass_size,
436 command_buffer, subpass_callback);
437 }
438 }
439
440 int ScaleBlurRadius(Scalar radius, Scalar scalar) {
441 return static_cast<int>(std::round(radius * scalar));
442 }
443
444 Entity ApplyClippedBlurStyle(Entity::ClipOperation clip_operation,
445 const Entity& entity,
446 const std::shared_ptr<FilterInput>& input,
447 const Snapshot& input_snapshot,
448 Entity blur_entity,
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);
453 Entity clipper;
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 {
463 bool result = true;
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;
470 return result;
471 });
472 auto coverage =
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();
477 });
478 Entity result;
479 result.SetContents(Contents::MakeAnonymous(renderer, coverage));
480 return result;
481 }
482
483 Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style,
484 const Entity& entity,
485 const std::shared_ptr<FilterInput>& input,
486 const Snapshot& input_snapshot,
487 Entity blur_entity,
488 const std::shared_ptr<Geometry>& geometry,
489 Vector2 source_space_scalar) {
490 switch (blur_style) {
491 case FilterContents::BlurStyle::kNormal:
492 return blur_entity;
493 case FilterContents::BlurStyle::kInner:
494 return ApplyClippedBlurStyle(Entity::ClipOperation::kIntersect, entity,
495 input, input_snapshot,
496 std::move(blur_entity), geometry);
497 break;
498 case FilterContents::BlurStyle::kOuter:
499 return ApplyClippedBlurStyle(Entity::ClipOperation::kDifference, entity,
500 input, input_snapshot,
501 std::move(blur_entity), geometry);
502 case FilterContents::BlurStyle::kSolid: {
503 Entity snapshot_entity =
504 Entity::FromSnapshot(input_snapshot, entity.GetBlendMode());
505 Entity result;
506 Matrix blurred_transform = blur_entity.GetTransform();
507 Matrix snapshot_transform =
508 entity.GetTransform() * snapshot_entity.GetTransform();
509 result.SetContents(Contents::MakeAnonymous(
510 fml::MakeCopyable(
511 [blur_entity = blur_entity.Clone(), blurred_transform,
512 source_space_scalar, snapshot_transform,
513 snapshot_entity = std::move(snapshot_entity)](
514 const ContentContext& renderer, const Entity& entity,
515 RenderPass& pass) mutable {
516 bool result = true;
517 blur_entity.SetClipDepth(entity.GetClipDepth());
518 blur_entity.SetTransform(entity.GetTransform() *
519 blurred_transform);
520 result = result && blur_entity.Render(renderer, pass);
521 snapshot_entity.SetTransform(
522 entity.GetTransform() *
523 Matrix::MakeScale(1.f / source_space_scalar) *
524 snapshot_transform);
525 snapshot_entity.SetClipDepth(entity.GetClipDepth());
526 result = result && snapshot_entity.Render(renderer, pass);
527 return result;
528 }),
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();
533 })));
534 return result;
535 }
536 }
537 }
538 } // namespace
539
540 std::string_view GaussianBlurFilterContents::kNoMipsError =
541 "Applying gaussian blur without mipmap.";
542
543 GaussianBlurFilterContents::GaussianBlurFilterContents(
544 Scalar sigma_x,
545 Scalar sigma_y,
546 Entity::TileMode tile_mode,
547 BlurStyle mask_blur_style,
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) {
553 // This is supposed to be enforced at a higher level.
554 FML_DCHECK(mask_blur_style == BlurStyle::kNormal || mask_geometry);
555 }
556
557 // This value was extracted from Skia, see:
558 // * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/ganesh/GrBlurUtils.cpp#L2561-L2576
559 // * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/BlurUtils.h#L57
560 Scalar GaussianBlurFilterContents::CalculateScale(Scalar sigma) {
561 if (sigma <= 4) {
562 return 1.0;
563 }
564 Scalar raw_result = 4.0 / sigma;
565 // Round to the nearest 1/(2^n) to get the best quality down scaling.
566 Scalar exponent = round(log2f(raw_result));
567 // Don't scale down below 1/16th to preserve signal.
568 exponent = std::max(-4.0f, exponent);
569 Scalar rounded = powf(2.0f, exponent);
570 Scalar result = rounded;
571 // Extend the range of the 1/8th downsample based on the effective kernel size
572 // for the blur.
573 if (rounded < 0.125f) {
574 Scalar rounded_plus = powf(2.0f, exponent + 1);
575 Scalar blur_radius = CalculateBlurRadius(sigma);
576 int kernel_size_plus = (ScaleBlurRadius(blur_radius, rounded_plus) * 2) + 1;
577 // This constant was picked by looking at the results to make sure no
578 // shimmering was introduced at the highest sigma values that downscale to
579 // 1/16th.
580 static constexpr int32_t kEighthDownsampleKernalWidthMax = 41;
581 result = kernel_size_plus <= kEighthDownsampleKernalWidthMax ? rounded_plus
582 : rounded;
583 }
584 return result;
585 };
586
587 std::optional<Rect> GaussianBlurFilterContents::GetFilterSourceCoverage(
588 const Matrix& effect_transform,
589 const Rect& output_limit) const {
590 Vector2 scaled_sigma = {ScaleSigma(sigma_.x), ScaleSigma(sigma_.y)};
591 Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x),
592 CalculateBlurRadius(scaled_sigma.y)};
593 Vector3 blur_radii =
594 effect_transform.Basis() * Vector3{blur_radius.x, blur_radius.y, 0.0};
595 return output_limit.Expand(Point(blur_radii.x, blur_radii.y));
596 }
597
598 std::optional<Rect> GaussianBlurFilterContents::GetFilterCoverage(
599 const FilterInput::Vector& inputs,
600 const Entity& entity,
601 const Matrix& effect_transform) const {
602 if (inputs.empty()) {
603 return {};
604 }
605 std::optional<Rect> input_coverage = inputs[0]->GetCoverage(entity);
606 if (!input_coverage.has_value()) {
607 return {};
608 }
609
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));
613 }
614
615 // A brief overview how this works:
616 // 1) Snapshot the filter input.
617 // 2) Perform downsample pass. This also inserts the gutter around the input
618 // snapshot since the blur can render outside the bounds of the snapshot.
619 // 3) Perform 1D horizontal blur pass.
620 // 4) Perform 1D vertical blur pass.
621 // 5) Apply the blur style to the blur result. This may just mask the output or
622 // draw the original snapshot over the result.
623 std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
624 const FilterInput::Vector& inputs,
625 const ContentContext& renderer,
626 const Entity& entity,
627 const Matrix& effect_transform,
628 const Rect& coverage,
629 const std::optional<Rect>& coverage_hint) const {
630 if (inputs.empty()) {
631 return std::nullopt;
632 }
633
634 BlurInfo blur_info = CalculateBlurInfo(entity, effect_transform, sigma_);
635
636 // Apply as much of the desired padding as possible from the source. This may
637 // be ignored so must be accounted for in the downsample pass by adding a
638 // transparent gutter.
639 std::optional<Rect> expanded_coverage_hint;
640 if (coverage_hint.has_value()) {
641 expanded_coverage_hint = coverage_hint->Expand(blur_info.local_padding);
642 }
643
644 Entity snapshot_entity = entity.Clone();
645 snapshot_entity.SetTransform(
646 Matrix::MakeScale(blur_info.source_space_scalar));
647
648 std::optional<Rect> source_expanded_coverage_hint;
649 if (expanded_coverage_hint.has_value()) {
650 source_expanded_coverage_hint = expanded_coverage_hint->TransformBounds(
651 Matrix::MakeScale(blur_info.source_space_scalar) *
652 entity.GetTransform().Invert());
653 }
654
655 std::optional<Snapshot> input_snapshot = GetSnapshot(
656 inputs[0], renderer, snapshot_entity, source_expanded_coverage_hint);
657 if (!input_snapshot.has_value()) {
658 return std::nullopt;
659 }
660
661 if (blur_info.scaled_sigma.x < kEhCloseEnough &&
662 blur_info.scaled_sigma.y < kEhCloseEnough) {
663 Entity result =
664 Entity::FromSnapshot(input_snapshot.value(),
665 entity.GetBlendMode()); // No blur to render.
666 result.SetTransform(entity.GetTransform() *
667 Matrix::MakeScale(1.f / blur_info.source_space_scalar) *
668 input_snapshot->transform);
669 return result;
670 }
671
672 // Note: The code below uses three different command buffers when it would be
673 // possible to combine the operations into a single buffer. From testing and
674 // user bug reports (see https://github.com/flutter/flutter/issues/154046 ),
675 // this sometimes causes deviceLost errors on older Adreno devices. Breaking
676 // the work up into three different command buffers seems to prevent this
677 // crash.
678 std::shared_ptr<CommandBuffer> command_buffer_1 =
679 renderer.GetContext()->CreateCommandBuffer();
680 if (!command_buffer_1) {
681 return std::nullopt;
682 }
683
684 DownsamplePassArgs downsample_pass_args = CalculateDownsamplePassArgs(
685 blur_info.scaled_sigma, blur_info.padding, input_snapshot.value(),
686 source_expanded_coverage_hint, inputs[0], snapshot_entity);
687
688 fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
689 renderer, command_buffer_1, input_snapshot->texture,
690 input_snapshot->sampler_descriptor, downsample_pass_args, tile_mode_);
691
692 if (!pass1_out.ok()) {
693 return std::nullopt;
694 }
695
696 Vector2 pass1_pixel_size =
697 1.0 / Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize());
698
699 Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
700
701 std::shared_ptr<CommandBuffer> command_buffer_2 =
702 renderer.GetContext()->CreateCommandBuffer();
703 if (!command_buffer_2) {
704 return std::nullopt;
705 }
706
707 fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
708 renderer, command_buffer_2, /*input_pass=*/pass1_out.value(),
709 input_snapshot->sampler_descriptor, tile_mode_,
710 BlurParameters{
711 .blur_uv_offset = Point(0.0, pass1_pixel_size.y),
712 .blur_sigma = blur_info.scaled_sigma.y *
713 downsample_pass_args.effective_scalar.y,
714 .blur_radius = ScaleBlurRadius(
715 blur_info.blur_radius.y, downsample_pass_args.effective_scalar.y),
716 .step_size = 1,
717 },
718 /*destination_target=*/std::nullopt, blur_uvs);
719
720 if (!pass2_out.ok()) {
721 return std::nullopt;
722 }
723
724 std::shared_ptr<CommandBuffer> command_buffer_3 =
725 renderer.GetContext()->CreateCommandBuffer();
726 if (!command_buffer_3) {
727 return std::nullopt;
728 }
729
730 // Only ping pong if the first pass actually created a render target.
731 auto pass3_destination = pass2_out.value().GetRenderTargetTexture() !=
732 pass1_out.value().GetRenderTargetTexture()
733 ? std::optional<RenderTarget>(pass1_out.value())
734 : std::optional<RenderTarget>(std::nullopt);
735
736 fml::StatusOr<RenderTarget> pass3_out = MakeBlurSubpass(
737 renderer, command_buffer_3, /*input_pass=*/pass2_out.value(),
738 input_snapshot->sampler_descriptor, tile_mode_,
739 BlurParameters{
740 .blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
741 .blur_sigma = blur_info.scaled_sigma.x *
742 downsample_pass_args.effective_scalar.x,
743 .blur_radius = ScaleBlurRadius(
744 blur_info.blur_radius.x, downsample_pass_args.effective_scalar.x),
745 .step_size = 1,
746 },
747 pass3_destination, blur_uvs);
748
749 if (!pass3_out.ok()) {
750 return std::nullopt;
751 }
752
753 if (!renderer.GetContext()
754 ->GetCommandQueue()
755 ->Submit(/*buffers=*/{command_buffer_1, command_buffer_2,
756 command_buffer_3})
757 .ok()) {
758 return std::nullopt;
759 }
760
761 // The ping-pong approach requires that each render pass output has the same
762 // size.
763 FML_DCHECK((pass1_out.value().GetRenderTargetSize() ==
764 pass2_out.value().GetRenderTargetSize()) &&
765 (pass2_out.value().GetRenderTargetSize() ==
766 pass3_out.value().GetRenderTargetSize()));
767
768 SamplerDescriptor sampler_desc = MakeSamplerDescriptor(
769 MinMagFilter::kLinear, SamplerAddressMode::kClampToEdge);
770
771 Entity blur_output_entity = Entity::FromSnapshot(
772 Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(),
773 .transform =
774 entity.GetTransform() * //
775 Matrix::MakeScale(1.f / blur_info.source_space_scalar) * //
776 downsample_pass_args.transform * //
777 Matrix::MakeScale(1 / downsample_pass_args.effective_scalar),
778 .sampler_descriptor = sampler_desc,
779 .opacity = input_snapshot->opacity},
780 entity.GetBlendMode());
781
782 return ApplyBlurStyle(mask_blur_style_, entity, inputs[0],
783 input_snapshot.value(), std::move(blur_output_entity),
784 mask_geometry_, blur_info.source_space_scalar);
785 }
786
787 Scalar GaussianBlurFilterContents::CalculateBlurRadius(Scalar sigma) {
788 return static_cast<Radius>(Sigma(sigma)).radius;
789 }
790
791 Quad GaussianBlurFilterContents::CalculateUVs(
792 const std::shared_ptr<FilterInput>& filter_input,
793 const Entity& entity,
794 const Rect& source_rect,
795 const ISize& texture_size) {
796 Matrix input_transform = filter_input->GetLocalTransform(entity);
797 Quad coverage_quad = source_rect.GetTransformedPoints(input_transform);
798
799 Matrix uv_transform = Matrix::MakeScale(
800 {1.0f / texture_size.width, 1.0f / texture_size.height, 1.0f});
801 return uv_transform.Transform(coverage_quad);
802 }
803
804 // This function was calculated by observing Skia's behavior. Its blur at 500
805 // seemed to be 0.15. Since we clamp at 500 I solved the quadratic equation
806 // that puts the minima there and a f(0)=1.
807 Scalar GaussianBlurFilterContents::ScaleSigma(Scalar sigma) {
808 // Limit the kernel size to 1000x1000 pixels, like Skia does.
809 Scalar clamped = std::min(sigma, kMaxSigma);
810 constexpr Scalar a = 3.4e-06;
811 constexpr Scalar b = -3.4e-3;
812 constexpr Scalar c = 1.f;
813 Scalar scalar = c + b * clamped + a * clamped * clamped;
814 return clamped * scalar;
815 }
816
817 KernelSamples GenerateBlurInfo(BlurParameters parameters) {
818 KernelSamples result;
819 result.sample_count =
820 ((2 * parameters.blur_radius) / parameters.step_size) + 1;
821
822 // Chop off the last samples if the radius >= 3 where they account for < 1.56%
823 // of the result.
824 int x_offset = 0;
825 if (parameters.blur_radius >= 3) {
826 result.sample_count -= 2;
827 x_offset = 1;
828 }
829
830 // This is a safe-guard to make sure we don't overflow the fragment shader.
831 // The kernel size is multiplied by 2 since we'll use the lerp hack on the
832 // result. In practice this isn't throwing away much data since the blur radii
833 // are around 53 before the down-sampling and max sigma of 500 kick in.
834 //
835 // TODO(https://github.com/flutter/flutter/issues/150462): Come up with a more
836 // wholistic remedy for this. A proper downsample size should not make this
837 // required. Or we can increase the kernel size.
838 if (result.sample_count > KernelSamples::kMaxKernelSize) {
839 result.sample_count = KernelSamples::kMaxKernelSize;
840 }
841
842 Scalar tally = 0.0f;
843 for (int i = 0; i < result.sample_count; ++i) {
844 int x = x_offset + (i * parameters.step_size) - parameters.blur_radius;
845 result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{
846 .uv_offset = parameters.blur_uv_offset * x,
847 .coefficient = expf(-0.5f * (x * x) /
848 (parameters.blur_sigma * parameters.blur_sigma)) /
849 (sqrtf(2.0f * M_PI) * parameters.blur_sigma),
850 };
851 tally += result.samples[i].coefficient;
852 }
853
854 // Make sure everything adds up to 1.
855 for (auto& sample : result.samples) {
856 sample.coefficient /= tally;
857 }
858
859 return result;
860 }
861
862 // This works by shrinking the kernel size by 2 and relying on lerp to read
863 // between the samples.
864 GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(
865 KernelSamples parameters) {
866 GaussianBlurPipeline::FragmentShader::KernelSamples result;
867 result.sample_count = ((parameters.sample_count - 1) / 2) + 1;
868 int32_t middle = result.sample_count / 2;
869 int32_t j = 0;
870 FML_DCHECK(result.sample_count <= kGaussianBlurMaxKernelSize);
871 for (int i = 0; i < result.sample_count; i++) {
872 if (i == middle) {
873 result.samples[i] = parameters.samples[j++];
874 } else {
875 GaussianBlurPipeline::FragmentShader::KernelSample left =
876 parameters.samples[j];
877 GaussianBlurPipeline::FragmentShader::KernelSample right =
878 parameters.samples[j + 1];
879 result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{
880 .uv_offset = (left.uv_offset * left.coefficient +
881 right.uv_offset * right.coefficient) /
882 (left.coefficient + right.coefficient),
883 .coefficient = left.coefficient + right.coefficient,
884 };
885 j += 2;
886 }
887 }
888
889 return result;
890 }
891
892 } // namespace impeller
impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::RenderPipelineHandle::FragmentShader
FragmentShader_ FragmentShader
Definition: pipeline.h:107
impeller::Entity::ClipOperation::kIntersect
@ kIntersect
impeller::Entity::TileMode::kClamp
@ kClamp
impeller::OptionsFromPass
ContentContextOptions OptionsFromPass(const RenderPass &pass)
Definition: contents.cc:19
impeller::KernelSamples
Definition: gaussian_blur_filter_contents.h:30
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Entity::ClipOperation::kDifference
@ kDifference
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::KernelSamples::sample_count
int sample_count
Definition: gaussian_blur_filter_contents.h:32
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
uvs
Quad uvs
Definition: gaussian_blur_filter_contents.cc:222
impeller::BlurParameters::blur_uv_offset
Point blur_uv_offset
Definition: gaussian_blur_filter_contents.h:19
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::FilterContents::BlurStyle
BlurStyle
Definition: filter_contents.h:26
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
impeller::SamplerAddressMode
SamplerAddressMode
Definition: formats.h:435
impeller::HostBuffer
Definition: host_buffer.h:28
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::KernelSamples::samples
GaussianBlurPipeline::FragmentShader::KernelSample samples[kMaxKernelSize]
Definition: gaussian_blur_filter_contents.h:33
impeller::Vector2
Point Vector2
Definition: point.h:326
impeller::RenderPass::SetVertexBuffer
virtual bool SetVertexBuffer(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
Definition: render_pass.cc:123
impeller::VertexBufferBuilder::AddVertices
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
Definition: vertex_buffer_builder.h:67
padding
Vector2 padding
The halo padding in source space.
Definition: gaussian_blur_filter_contents.cc:91
impeller::SamplerAddressMode::kClampToEdge
@ kClampToEdge
gaussian_blur_filter_contents.h
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
impeller::FilterContents::BlurStyle::kNormal
@ kNormal
Blurred inside and outside.
impeller::kGaussianBlurMaxKernelSize
static constexpr int32_t kGaussianBlurMaxKernelSize
Definition: gaussian_blur_filter_contents.h:16
impeller::GaussianBlurFilterContents::CalculateBlurRadius
static Scalar CalculateBlurRadius(Scalar sigma)
Definition: gaussian_blur_filter_contents.cc:787
impeller::Entity::TileMode::kRepeat
@ kRepeat
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::RenderPass::SetCommandLabel
virtual void SetCommandLabel(std::string_view label)
The debugging label to use for the command.
Definition: render_pass.cc:97
subpass_size
ISize subpass_size
The output size of the down-sampling pass.
Definition: gaussian_blur_filter_contents.cc:219
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::GaussianBlurVertexShader
GaussianBlurPipeline::VertexShader GaussianBlurVertexShader
Definition: gaussian_blur_filter_contents.cc:19
impeller::Entity::TileMode::kMirror
@ kMirror
impeller::RenderPass::Draw
virtual fml::Status Draw()
Record the currently pending command.
Definition: render_pass.cc:127
impeller::SamplerDescriptor::mag_filter
MinMagFilter mag_filter
Definition: sampler_descriptor.h:17
impeller::SamplerDescriptor
Definition: sampler_descriptor.h:15
impeller::GaussianBlurFilterContents::GetFilterCoverage
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...
Definition: gaussian_blur_filter_contents.cc:598
impeller::BlurParameters::blur_radius
int blur_radius
Definition: gaussian_blur_filter_contents.h:21
impeller::Entity
Definition: entity.h:20
impeller::Matrix::Basis
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:229
impeller::TSize
Definition: size.h:19
impeller::PrimitiveType::kTriangleStrip
@ kTriangleStrip
impeller::SamplerDescriptor::min_filter
MinMagFilter min_filter
Definition: sampler_descriptor.h:16
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
impeller::Quad
std::array< Point, 4 > Quad
Definition: point.h:327
render_pass.h
impeller::GaussianBlurFragmentShader
GaussianBlurPipeline::FragmentShader GaussianBlurFragmentShader
Definition: gaussian_blur_filter_contents.cc:20
impeller::Context::BackendType::kOpenGLES
@ kOpenGLES
impeller::Radius
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
impeller::FilterContents::BlurStyle::kSolid
@ kSolid
Solid inside, blurred outside.
impeller::TRect::GetTransformedPoints
constexpr std::array< TPoint< T >, 4 > GetTransformedPoints(const Matrix &transform) const
Definition: rect.h:417
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:231
impeller::Entity::FromSnapshot
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
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:553
impeller::VertexBufferBuilder
Definition: vertex_buffer_builder.h:21
impeller::MinMagFilter::kLinear
@ kLinear
impeller::GaussianBlurFilterContents::GetFilterSourceCoverage
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...
Definition: gaussian_blur_filter_contents.cc:587
impeller::TRect< Scalar >::MakeOriginSize
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
impeller::RenderPipelineHandle::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:106
local_padding
Vector2 local_padding
Padding in unrotated local space.
Definition: gaussian_blur_filter_contents.cc:93
impeller::SamplerDescriptor::width_address_mode
SamplerAddressMode width_address_mode
Definition: sampler_descriptor.h:20
impeller::BlurParameters::step_size
int step_size
Definition: gaussian_blur_filter_contents.h:22
impeller::Sigma
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
impeller::MinMagFilter
MinMagFilter
Describes how the texture should be sampled when the texture is being shrunk (minified) or expanded (...
Definition: formats.h:409
clip_contents.h
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:769
impeller::Vector3::y
Scalar y
Definition: vector.h:24
impeller::FilterContents::BlurStyle::kInner
@ kInner
Blurred inside, nothing outside.
impeller::BlurParameters
Definition: gaussian_blur_filter_contents.h:18
impeller::Entity::TileMode
TileMode
Definition: entity.h:42
impeller::TSize::width
Type width
Definition: size.h:22
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:119
impeller::Matrix::Transform
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:487
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::VertexBufferBuilder::CreateVertexBuffer
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
Definition: vertex_buffer_builder.h:81
impeller::GenerateBlurInfo
KernelSamples GenerateBlurInfo(BlurParameters parameters)
Definition: gaussian_blur_filter_contents.cc:817
impeller::ContentContext::SubpassCallback
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
Definition: content_context.h:700
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
content_context.h
impeller::FilterContents::BlurStyle::kOuter
@ kOuter
Nothing inside, blurred outside.
impeller::Entity::SetTransform
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:62
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:557
scaled_sigma
Vector2 scaled_sigma
Sigma when considering an entity's scale and the effect transform.
Definition: gaussian_blur_filter_contents.cc:87
impeller::SamplerAddressMode::kMirror
@ kMirror
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:146
impeller::TPoint::GetLength
constexpr Type GetLength() const
Definition: point.h:206
std
Definition: comparable.h:95
impeller::GaussianBlurFilterContents::kNoMipsError
static std::string_view kNoMipsError
Definition: gaussian_blur_filter_contents.h:49
impeller::Entity::Clone
Entity Clone() const
Definition: entity.cc:191
impeller::TPoint
Definition: point.h:27
impeller::saturated::b
SI b
Definition: saturated_math.h:87
blur_radius
Vector2 blur_radius
Blur radius in source pixels based on scaled_sigma.
Definition: gaussian_blur_filter_contents.cc:89
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:497
impeller::Entity::ClipOperation
ClipOperation
Definition: entity.h:61
impeller::GaussianBlurFilterContents::CalculateScale
static Scalar CalculateScale(Scalar sigma)
Definition: gaussian_blur_filter_contents.cc:560
impeller::LerpHackKernelSamples
GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(KernelSamples parameters)
Definition: gaussian_blur_filter_contents.cc:864
impeller::Contents::MakeAnonymous
static std::shared_ptr< Contents > MakeAnonymous(RenderProc render_proc, CoverageProc coverage_proc)
Definition: contents.cc:41
impeller::TSize::height
Type height
Definition: size.h:23
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::GaussianBlurFilterContents::CalculateUVs
static Quad CalculateUVs(const std::shared_ptr< FilterInput > &filter_input, const Entity &entity, const Rect &source_rect, const ISize &texture_size)
Definition: gaussian_blur_filter_contents.cc:791
impeller::RenderPass::SetPipeline
virtual void SetPipeline(const std::shared_ptr< Pipeline< PipelineDescriptor >> &pipeline)
The pipeline to use for this command.
Definition: render_pass.cc:92
impeller::FilterInput::Vector
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
impeller::Capabilities::SupportsDecalSamplerAddressMode
virtual bool SupportsDecalSamplerAddressMode() const =0
Whether the context backend supports SamplerAddressMode::Decal.
impeller::SamplerDescriptor::height_address_mode
SamplerAddressMode height_address_mode
Definition: sampler_descriptor.h:21
impeller
Definition: aiks_blend_unittests.cc:18
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::BlurParameters::blur_sigma
Scalar blur_sigma
Definition: gaussian_blur_filter_contents.h:20
impeller::ContentContext
Definition: content_context.h:366
impeller::KernelSamples::kMaxKernelSize
static constexpr int kMaxKernelSize
Definition: gaussian_blur_filter_contents.h:31
effective_scalar
Vector2 effective_scalar
Definition: gaussian_blur_filter_contents.cc:226
impeller::TRect< Scalar >
impeller::GaussianBlurFilterContents::kBlurFilterRequiredMipCount
static const int32_t kBlurFilterRequiredMipCount
Definition: gaussian_blur_filter_contents.h:50
impeller::GaussianBlurFilterContents::GaussianBlurFilterContents
GaussianBlurFilterContents(Scalar sigma_x, Scalar sigma_y, Entity::TileMode tile_mode, BlurStyle mask_blur_style, const std::shared_ptr< Geometry > &mask_geometry)
Definition: gaussian_blur_filter_contents.cc:543
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Vector3
Definition: vector.h:20
source_space_scalar
Vector2 source_space_scalar
The scalar that is used to get from source space to unrotated local space.
Definition: gaussian_blur_filter_contents.cc:85
impeller::TRect::Expand
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
vertex_buffer_builder.h
impeller::GaussianBlurFilterContents::ScaleSigma
static Scalar ScaleSigma(Scalar sigma)
Definition: gaussian_blur_filter_contents.cc:807
impeller::SamplerAddressMode::kRepeat
@ kRepeat
impeller::SamplerAddressMode::kDecal
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...