Skip to content

Commit

Permalink
[Impeller] backfilled blur unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gaaclarke committed Nov 2, 2023
2 parents b0d6ba1 + 9b1fb10 commit fc36579
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 0 deletions.
1 change: 1 addition & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ impeller_component("entity_unittests") {
testonly = true

sources = [
"contents/filters/directional_gaussian_blur_filter_contents_unittests.cc",
"contents/filters/inputs/filter_input_unittests.cc",
"entity_playground.cc",
"entity_playground.h",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/testing/testing.h"
#include "gmock/gmock.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.h"
#include "impeller/renderer/testing/mocks.h"

namespace impeller {
namespace testing {

using ::testing::_;
using ::testing::An;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::ReturnRef;

namespace {

Scalar CalculateSigmaForBlurRadius(Scalar blur_radius) {
// See Sigma.h
return (blur_radius / kKernelRadiusPerSigma) + 0.5;
}
} // namespace

class DirectionalGaussianBlurFilterContentsTest : public ::testing::Test {
public:
void SetUp() override { capabilities_ = mock_capabilities_; }

std::shared_ptr<MockCapabilities> mock_capabilities_ =
std::make_shared<MockCapabilities>();
std::shared_ptr<const Capabilities> capabilities_;
};

TEST_F(DirectionalGaussianBlurFilterContentsTest, CoverageWithEffectTransform) {
TextureDescriptor desc = {
.size = ISize(100, 100),
};
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
contents->SetSigma(Sigma{sigma_radius_1});
contents->SetDirection({1.0, 0.0});
std::shared_ptr<MockTexture> texture = std::make_shared<MockTexture>(desc);
EXPECT_CALL(*texture, GetSize()).WillRepeatedly(Return(ISize(100, 100)));
FilterInput::Vector inputs = {FilterInput::Make(texture)};
Entity entity;
entity.SetTransformation(Matrix::MakeTranslation({100, 100, 0}));
std::optional<Rect> coverage = contents->GetFilterCoverage(
inputs, entity, /*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}));
ASSERT_EQ(coverage, Rect::MakeLTRB(100 - 2, 100, 200 + 2, 200));
}

TEST_F(DirectionalGaussianBlurFilterContentsTest, FilterSourceCoverage) {
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
contents->SetSigma(Sigma{sigma_radius_1});
contents->SetDirection({1.0, 0.0});
std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
/*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}),
/*output_limit=*/Rect::MakeLTRB(100, 100, 200, 200));
ASSERT_EQ(coverage, Rect::MakeLTRB(100 - 2, 100, 200 + 2, 200));
}

TEST_F(DirectionalGaussianBlurFilterContentsTest, RenderNoCoverage) {
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
contents->SetSigma(Sigma{sigma_radius_1});
contents->SetDirection({1.0, 0.0});
auto mock_context = std::make_shared<MockImpellerContext>();
auto mock_typographer_context = std::make_shared<MockTypographerContext>();
auto mock_allocator = std::make_shared<MockAllocator>();
auto mock_render_target_allocator =
std::make_shared<MockRenderTargetAllocator>(mock_allocator);
ContentContext renderer(mock_context, mock_typographer_context,
mock_render_target_allocator);
Entity entity;
Rect coverage_hint = Rect::MakeLTRB(0, 0, 0, 0);
std::optional<Entity> result =
contents->GetEntity(renderer, entity, coverage_hint);
ASSERT_FALSE(result.has_value());
}

TEST_F(DirectionalGaussianBlurFilterContentsTest, RenderSomething) {
TextureDescriptor desc = {
.size = ISize(100, 100),
};
std::shared_ptr<MockTexture> texture = std::make_shared<MockTexture>(desc);
EXPECT_CALL(*texture, GetSize()).WillRepeatedly(Return(ISize(100, 100)));
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
contents->SetSigma(Sigma{sigma_radius_1});
contents->SetDirection({1.0, 0.0});
contents->SetInputs({FilterInput::Make(texture)});
auto mock_context = std::make_shared<MockImpellerContext>();
EXPECT_CALL(*mock_context, GetCapabilities())
.WillRepeatedly(ReturnRef(capabilities_));
EXPECT_CALL(*mock_context, IsValid()).WillRepeatedly(Return(true));
auto mock_sampler_library = std::make_shared<MockSamplerLibrary>();
auto mock_shader_library = std::make_shared<MockShaderLibrary>();
auto mock_pipeline_library = std::make_shared<MockPipelineLibrary>();
EXPECT_CALL(*mock_pipeline_library, GetPipeline(An<PipelineDescriptor>()))
.WillRepeatedly(
Invoke([&mock_pipeline_library](PipelineDescriptor descriptor) {
PipelineFuture<PipelineDescriptor> result;
std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>> promise;
auto mock_pipeline =
std::make_shared<MockPipeline<PipelineDescriptor>>(
mock_pipeline_library, descriptor);
EXPECT_CALL(*mock_pipeline, IsValid()).WillRepeatedly(Return(true));
promise.set_value(mock_pipeline);
result.descriptor = descriptor;
result.future = promise.get_future();
return result;
}));
EXPECT_CALL(*mock_shader_library, GetFunction(_, _))
.WillRepeatedly(Invoke([](std::string_view name, ShaderStage stage) {
return std::make_shared<MockShaderFunction>(UniqueID(),
std::string(name), stage);
}));
EXPECT_CALL(*mock_context, GetSamplerLibrary())
.WillRepeatedly(Return(mock_sampler_library));
EXPECT_CALL(*mock_context, GetShaderLibrary())
.WillRepeatedly(Return(mock_shader_library));
EXPECT_CALL(*mock_context, GetPipelineLibrary())
.WillRepeatedly(Return(mock_pipeline_library));
EXPECT_CALL(*mock_context, CreateCommandBuffer())
.WillRepeatedly(Invoke(([&mock_context]() {
auto result = std::make_shared<MockCommandBuffer>(mock_context);
EXPECT_CALL(*result, IsValid()).WillRepeatedly(Return(true));
EXPECT_CALL(*result, OnSubmitCommands(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*result, OnCreateRenderPass(_))
.WillRepeatedly(
Invoke(([&mock_context](const RenderTarget& render_target) {
auto result = std::make_shared<MockRenderPass>(mock_context,
render_target);
EXPECT_CALL(*result, IsValid).WillRepeatedly(Return(true));
EXPECT_CALL(*result, OnEncodeCommands(_))
.WillRepeatedly(Return(true));
return result;
})));
return result;
})));
auto mock_typographer_context = std::make_shared<MockTypographerContext>();
auto mock_allocator = std::make_shared<MockAllocator>();
auto mock_render_target_allocator =
std::make_shared<MockRenderTargetAllocator>(mock_allocator);
EXPECT_CALL(*mock_render_target_allocator, CreateTexture(_))
.WillRepeatedly(Invoke(([](const TextureDescriptor& desc) {
auto result = std::make_shared<MockTexture>(desc);
EXPECT_CALL(*result, IsValid()).WillRepeatedly(Return(true));
return result;
})));
ContentContext renderer(mock_context, mock_typographer_context,
mock_render_target_allocator);
Entity entity;
Rect coverage_hint = Rect::MakeLTRB(0, 0, 0, 0);
std::optional<Entity> result =
contents->GetEntity(renderer, entity, coverage_hint);
ASSERT_TRUE(result.has_value());
ASSERT_EQ(result.value().GetBlendMode(), BlendMode::kSourceOver);
}

} // namespace testing
} // namespace impeller
113 changes: 113 additions & 0 deletions impeller/renderer/testing/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
#include "impeller/core/texture.h"
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/pipeline_library.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/render_target.h"
#include "impeller/renderer/sampler_library.h"
#include "impeller/renderer/shader_function.h"
#include "impeller/typographer/typographer_context.h"

namespace impeller {
namespace testing {
Expand Down Expand Up @@ -164,5 +169,113 @@ class MockTexture : public Texture {
(override));
};

class MockTypographerContext : public TypographerContext {
public:
MOCK_METHOD(std::shared_ptr<GlyphAtlas>,
CreateGlyphAtlas,
(Context & context,
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const FontGlyphMap& font_glyph_map),
(const, override));
MOCK_METHOD(std::shared_ptr<GlyphAtlasContext>,
CreateGlyphAtlasContext,
(),
(const, override));
};

class MockRenderTargetAllocator : public RenderTargetAllocator {
public:
MockRenderTargetAllocator(std::shared_ptr<Allocator> allocator)
: RenderTargetAllocator(allocator) {}
MOCK_METHOD(std::shared_ptr<Texture>,
CreateTexture,
(const TextureDescriptor& desc),
(override));
};

class MockCapabilities : public Capabilities {
public:
MOCK_METHOD(bool, SupportsOffscreenMSAA, (), (const, override));
MOCK_METHOD(bool, SupportsImplicitResolvingMSAA, (), (const, override));
MOCK_METHOD(bool, SupportsSSBO, (), (const, override));
MOCK_METHOD(bool, SupportsBufferToTextureBlits, (), (const, override));
MOCK_METHOD(bool, SupportsTextureToTextureBlits, (), (const, override));
MOCK_METHOD(bool, SupportsFramebufferFetch, (), (const, override));
MOCK_METHOD(bool, SupportsCompute, (), (const, override));
MOCK_METHOD(bool, SupportsComputeSubgroups, (), (const, override));
MOCK_METHOD(bool, SupportsReadFromOnscreenTexture, (), (const, override));
MOCK_METHOD(bool, SupportsReadFromResolve, (), (const, override));
MOCK_METHOD(bool, SupportsDecalSamplerAddressMode, (), (const, override));
MOCK_METHOD(bool, SupportsDeviceTransientTextures, (), (const, override));
MOCK_METHOD(PixelFormat, GetDefaultColorFormat, (), (const, override));
MOCK_METHOD(PixelFormat, GetDefaultStencilFormat, (), (const, override));
MOCK_METHOD(PixelFormat, GetDefaultDepthStencilFormat, (), (const, override));
};

class MockRenderPass : public RenderPass {
public:
MockRenderPass(std::weak_ptr<const Context> context,
const RenderTarget& target)
: RenderPass(context, target) {}
MOCK_METHOD(bool, IsValid, (), (const, override));
MOCK_METHOD(void, OnSetLabel, (std::string), (override));
MOCK_METHOD(bool, OnEncodeCommands, (const Context&), (const, override));
};

class MockSamplerLibrary : public SamplerLibrary {
public:
MOCK_METHOD(std::shared_ptr<const Sampler>,
GetSampler,
(SamplerDescriptor),
(override));
};

class MockShaderLibrary : public ShaderLibrary {
public:
MOCK_METHOD(bool, IsValid, (), (const, override));
MOCK_METHOD(std::shared_ptr<const ShaderFunction>,
GetFunction,
(std::string_view name, ShaderStage stage),
(override));
MOCK_METHOD(void,
UnregisterFunction,
(std::string name, ShaderStage stage),
(override));
};

class MockShaderFunction : public ShaderFunction {
public:
MockShaderFunction(UniqueID parent_library_id,
std::string name,
ShaderStage stage)
: ShaderFunction(parent_library_id, name, stage) {}
};

class MockPipelineLibrary : public PipelineLibrary {
public:
MOCK_METHOD(bool, IsValid, (), (const, override));
MOCK_METHOD(PipelineFuture<PipelineDescriptor>,
GetPipeline,
(PipelineDescriptor descriptor),
(override));
MOCK_METHOD(PipelineFuture<ComputePipelineDescriptor>,
GetPipeline,
(ComputePipelineDescriptor descriptor),
(override));
MOCK_METHOD(void,
RemovePipelinesWithEntryPoint,
(std::shared_ptr<const ShaderFunction> function),
(override));
};

template <typename T>
class MockPipeline : public Pipeline<T> {
public:
MockPipeline(std::weak_ptr<PipelineLibrary> library, T desc)
: Pipeline<T>(library, desc) {}
MOCK_METHOD(bool, IsValid, (), (const, override));
};

} // namespace testing
} // namespace impeller

0 comments on commit fc36579

Please sign in to comment.