5 #include "flutter/testing/testing.h"
6 #include "fml/status_or.h"
7 #include "gmock/gmock.h"
13 #include "impeller/renderer/testing/mocks.h"
16 #define IMPELLER_RAND arc4random
18 #define IMPELLER_RAND rand
29 fml::StatusOr<float> LowerBoundNewtonianMethod(
30 const std::function<
float(
float)>& func,
34 const double delta = 1e-6;
37 static const int kMaxIterations = 1000;
41 fx = func(x) - target;
42 double derivative = (func(x + delta) - func(x)) / delta;
43 x = x - fx / derivative;
44 if (++count > kMaxIterations) {
45 return fml::Status(fml::StatusCode::kDeadlineExceeded,
46 "Did not converge on answer.");
48 }
while (std::abs(fx) > tolerance ||
54 fml::StatusOr<Scalar> CalculateSigmaForBlurRadius(
56 const Matrix& effect_transform) {
69 return LowerBoundNewtonianMethod(f, radius, 2.f, 0.001f);
78 std::shared_ptr<CommandBuffer> command_buffer =
80 if (!command_buffer) {
85 "Clear Subpass", size, command_buffer,
91 ->Submit({command_buffer})
96 if (render_target.ok()) {
97 return render_target.value().GetRenderTargetTexture();
118 std::optional<Rect> coverage =
120 ASSERT_FALSE(coverage.has_value());
130 std::optional<Rect> coverage =
137 fml::StatusOr<Scalar> sigma_radius_1 =
138 CalculateSigmaForBlurRadius(1.0,
Matrix());
139 ASSERT_TRUE(sigma_radius_1.ok());
141 sigma_radius_1.value(),
147 std::optional<Rect> coverage =
150 EXPECT_TRUE(coverage.has_value());
151 if (coverage.has_value()) {
157 fml::StatusOr<Scalar> sigma_radius_1 =
158 CalculateSigmaForBlurRadius(1.0,
Matrix());
159 ASSERT_TRUE(sigma_radius_1.ok());
161 sigma_radius_1.value(),
164 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
168 std::optional<Rect> coverage =
171 EXPECT_TRUE(coverage.has_value());
172 if (coverage.has_value()) {
179 fml::StatusOr<Scalar> sigma_radius_1 =
180 CalculateSigmaForBlurRadius(1.0, effect_transform);
181 ASSERT_TRUE(sigma_radius_1.ok());
183 sigma_radius_1.value(),
186 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
190 std::optional<Rect> coverage =
192 EXPECT_TRUE(coverage.has_value());
193 if (coverage.has_value()) {
200 fml::StatusOr<Scalar> sigma_radius_1 =
201 CalculateSigmaForBlurRadius(1.0,
Matrix());
202 ASSERT_TRUE(sigma_radius_1.ok());
203 auto contents = std::make_unique<GaussianBlurFilterContents>(
206 std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
209 EXPECT_TRUE(coverage.has_value());
210 if (coverage.has_value()) {
230 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
231 fml::StatusOr<Scalar> sigma_radius_1 =
232 CalculateSigmaForBlurRadius(1.0,
Matrix());
233 ASSERT_TRUE(sigma_radius_1.ok());
234 auto contents = std::make_unique<GaussianBlurFilterContents>(
238 std::shared_ptr<ContentContext> renderer = GetContentContext();
241 std::optional<Entity> result =
242 contents->GetEntity(*renderer, entity, {});
243 EXPECT_TRUE(result.has_value());
244 if (result.has_value()) {
246 std::optional<Rect> result_coverage = result.value().GetCoverage();
247 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
248 EXPECT_TRUE(result_coverage.has_value());
249 EXPECT_TRUE(contents_coverage.has_value());
250 if (result_coverage.has_value() && contents_coverage.has_value()) {
251 EXPECT_TRUE(
RectNear(contents_coverage.value(),
260 RenderCoverageMatchesGetCoverageTranslate) {
261 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
262 fml::StatusOr<Scalar> sigma_radius_1 =
263 CalculateSigmaForBlurRadius(1.0,
Matrix());
264 ASSERT_TRUE(sigma_radius_1.ok());
265 auto contents = std::make_unique<GaussianBlurFilterContents>(
269 std::shared_ptr<ContentContext> renderer = GetContentContext();
273 std::optional<Entity> result =
274 contents->GetEntity(*renderer, entity, {});
276 EXPECT_TRUE(result.has_value());
277 if (result.has_value()) {
279 std::optional<Rect> result_coverage = result.value().GetCoverage();
280 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
281 EXPECT_TRUE(result_coverage.has_value());
282 EXPECT_TRUE(contents_coverage.has_value());
283 if (result_coverage.has_value() && contents_coverage.has_value()) {
284 EXPECT_TRUE(
RectNear(contents_coverage.value(),
293 RenderCoverageMatchesGetCoverageRotated) {
294 std::shared_ptr<Texture> texture = MakeTexture(
ISize(400, 300));
295 fml::StatusOr<Scalar> sigma_radius_1 =
296 CalculateSigmaForBlurRadius(1.0,
Matrix());
297 auto contents = std::make_unique<GaussianBlurFilterContents>(
301 std::shared_ptr<ContentContext> renderer = GetContentContext();
307 std::optional<Entity> result =
308 contents->GetEntity(*renderer, entity, {});
309 EXPECT_TRUE(result.has_value());
310 if (result.has_value()) {
312 std::optional<Rect> result_coverage = result.value().GetCoverage();
313 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
314 EXPECT_TRUE(result_coverage.has_value());
315 EXPECT_TRUE(contents_coverage.has_value());
316 if (result_coverage.has_value() && contents_coverage.has_value()) {
317 EXPECT_TRUE(
RectNear(contents_coverage.value(),
326 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
332 EXPECT_TRUE(uvs_bounds.has_value());
333 if (uvs_bounds.has_value()) {
339 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
340 auto texture_contents = std::make_shared<TextureContents>();
341 texture_contents->SetSourceRect(
Rect::MakeSize(texture->GetSize()));
342 texture_contents->SetTexture(texture);
344 50, 40, texture->GetSize().width, texture->GetSize().height));
346 fml::StatusOr<Scalar> sigma_radius_1 =
347 CalculateSigmaForBlurRadius(1.0,
Matrix());
348 auto contents = std::make_unique<GaussianBlurFilterContents>(
352 std::shared_ptr<ContentContext> renderer = GetContentContext();
355 std::optional<Entity> result =
356 contents->GetEntity(*renderer, entity, {});
357 EXPECT_TRUE(result.has_value());
358 if (result.has_value()) {
360 std::optional<Rect> result_coverage = result.value().GetCoverage();
361 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
362 EXPECT_TRUE(result_coverage.has_value());
363 EXPECT_TRUE(contents_coverage.has_value());
364 if (result_coverage.has_value() && contents_coverage.has_value()) {
365 EXPECT_TRUE(
RectNear(result_coverage.value(), contents_coverage.value()));
366 EXPECT_TRUE(
RectNear(result_coverage.value(),
373 TextureContentsWithDestinationRectScaled) {
374 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
375 auto texture_contents = std::make_shared<TextureContents>();
376 texture_contents->SetSourceRect(
Rect::MakeSize(texture->GetSize()));
377 texture_contents->SetTexture(texture);
379 50, 40, texture->GetSize().width, texture->GetSize().height));
381 fml::StatusOr<Scalar> sigma_radius_1 =
382 CalculateSigmaForBlurRadius(1.0,
Matrix());
383 auto contents = std::make_unique<GaussianBlurFilterContents>(
388 std::shared_ptr<ContentContext> renderer = GetContentContext();
392 std::optional<Entity> result =
393 contents->GetEntity(*renderer, entity, {});
394 EXPECT_TRUE(result.has_value());
395 if (result.has_value()) {
397 std::optional<Rect> result_coverage = result.value().GetCoverage();
398 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
399 EXPECT_TRUE(result_coverage.has_value());
400 EXPECT_TRUE(contents_coverage.has_value());
401 if (result_coverage.has_value() && contents_coverage.has_value()) {
402 EXPECT_TRUE(
RectNear(result_coverage.value(), contents_coverage.value()));
406 EXPECT_TRUE(
RectNear(contents_coverage.value(),
414 std::shared_ptr<Texture> texture = MakeTexture(
ISize(100, 100));
415 auto texture_contents = std::make_shared<TextureContents>();
416 texture_contents->SetSourceRect(
Rect::MakeSize(texture->GetSize()));
417 texture_contents->SetTexture(texture);
419 50, 40, texture->GetSize().width, texture->GetSize().height));
421 fml::StatusOr<Scalar> sigma_radius_1 =
422 CalculateSigmaForBlurRadius(1.0, effect_transform);
423 ASSERT_TRUE(sigma_radius_1.ok());
424 auto contents = std::make_unique<GaussianBlurFilterContents>(
428 contents->SetEffectTransform(effect_transform);
429 std::shared_ptr<ContentContext> renderer = GetContentContext();
432 std::optional<Entity> result =
433 contents->GetEntity(*renderer, entity, {});
434 EXPECT_TRUE(result.has_value());
435 if (result.has_value()) {
437 std::optional<Rect> result_coverage = result.value().GetCoverage();
438 std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
439 EXPECT_TRUE(result_coverage.has_value());
440 EXPECT_TRUE(contents_coverage.has_value());
441 if (result_coverage.has_value() && contents_coverage.has_value()) {
442 EXPECT_TRUE(
RectNear(result_coverage.value(), contents_coverage.value()));
443 EXPECT_TRUE(
RectNear(contents_coverage.value(),
453 fml::StatusOr<Scalar> derived_sigma =
454 CalculateSigmaForBlurRadius(radius,
Matrix());
455 ASSERT_TRUE(derived_sigma.ok());
456 EXPECT_NEAR(sigma, derived_sigma.value(), 0.01f);
470 tally += samples.
samples[i].coefficient;
472 EXPECT_FLOAT_EQ(tally, 1.0f);
475 for (
int i = 0; i < 4; ++i) {
476 EXPECT_FLOAT_EQ(samples.
samples[i].coefficient,
477 samples.
samples[8 - i].coefficient);
478 EXPECT_TRUE(samples.
samples[i + 1].coefficient >
479 samples.
samples[i].coefficient);
511 GaussianBlurPipeline::FragmentShader::KernelSamples fast_kernel_samples =
513 EXPECT_EQ(fast_kernel_samples.sample_count, 3);
515 GaussianBlurPipeline::FragmentShader::KernelSample* samples =
517 GaussianBlurPipeline::FragmentShader::KernelSample* fast_samples =
518 fast_kernel_samples.samples;
523 EXPECT_FLOAT_EQ(fast_samples[0].uv_offset.x, -1.3333333);
524 EXPECT_FLOAT_EQ(fast_samples[0].uv_offset.y, 0);
525 EXPECT_FLOAT_EQ(fast_samples[0].coefficient, 0.3);
526 EXPECT_FLOAT_EQ(fast_samples[1].uv_offset.x, 0);
527 EXPECT_FLOAT_EQ(fast_samples[1].uv_offset.y, 0);
528 EXPECT_FLOAT_EQ(fast_samples[1].coefficient, 0.4);
529 EXPECT_FLOAT_EQ(fast_samples[2].uv_offset.x, 1.3333333);
530 EXPECT_FLOAT_EQ(fast_samples[2].uv_offset.y, 0);
531 EXPECT_FLOAT_EQ(fast_samples[2].coefficient, 0.3);
538 samples[0].coefficient *
data[0] + samples[1].coefficient *
data[1] +
539 samples[2].coefficient *
data[2] + samples[3].coefficient *
data[3] +
540 samples[4].coefficient *
data[4];
544 Scalar fract = fabsf(modf(point.
x, &int_part));
546 return left * fract + right * (1.0 - fract);
548 return left * (1.0 - fract) + right * fract;
552 lerp(fast_samples[0].uv_offset,
data[0],
data[1]) *
553 fast_samples[0].coefficient +
554 data[2] * fast_samples[1].coefficient +
555 lerp(fast_samples[2].uv_offset,
data[3],
data[4]) *
556 fast_samples[2].coefficient;
558 EXPECT_NEAR(original_output, fast_output, 0.01);
571 GaussianBlurPipeline::FragmentShader::KernelSamples fast_kernel_samples =
573 EXPECT_EQ(fast_kernel_samples.sample_count, 17);
576 for (
int i = 0; i < 33; i++) {
581 FML_CHECK(point.y == 0.0f);
582 FML_CHECK(point.x >= -16);
583 FML_CHECK(point.x <= 16);
585 Scalar fract = fabsf(modf(point.x, &fint_part));
587 int32_t int_part =
static_cast<int32_t
>(fint_part) + 16;
588 return data[int_part];
590 int32_t left =
static_cast<int32_t
>(floor(point.x)) + 16;
591 int32_t right =
static_cast<int32_t
>(ceil(point.x)) + 16;
593 return fract *
data[left] + (1.0 - fract) *
data[right];
595 return (1.0 - fract) *
data[left] + fract *
data[right];
602 auto sample = kernel_samples.
samples[i];
603 output += sample.coefficient * sampler(sample.uv_offset);
607 for (
int i = 0; i < fast_kernel_samples.sample_count; ++i) {
608 auto sample = fast_kernel_samples.samples[i];
609 fast_output += sample.coefficient * sampler(sample.uv_offset);
612 EXPECT_NEAR(output, fast_output, 0.1);
624 GaussianBlurPipeline::FragmentShader::KernelSamples frag_kernel_samples =