diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 4e1ce88280d4a..45f533e76a955 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -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", diff --git a/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents_unittests.cc b/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents_unittests.cc new file mode 100644 index 0000000000000..a2c8df040c8d5 --- /dev/null +++ b/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents_unittests.cc @@ -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 mock_capabilities_ = + std::make_shared(); + std::shared_ptr capabilities_; +}; + +TEST_F(DirectionalGaussianBlurFilterContentsTest, CoverageWithEffectTransform) { + TextureDescriptor desc = { + .size = ISize(100, 100), + }; + Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); + auto contents = std::make_unique(); + contents->SetSigma(Sigma{sigma_radius_1}); + contents->SetDirection({1.0, 0.0}); + std::shared_ptr texture = std::make_shared(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 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(); + contents->SetSigma(Sigma{sigma_radius_1}); + contents->SetDirection({1.0, 0.0}); + std::optional 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(); + contents->SetSigma(Sigma{sigma_radius_1}); + contents->SetDirection({1.0, 0.0}); + auto mock_context = std::make_shared(); + auto mock_typographer_context = std::make_shared(); + auto mock_allocator = std::make_shared(); + auto mock_render_target_allocator = + std::make_shared(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 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 texture = std::make_shared(desc); + EXPECT_CALL(*texture, GetSize()).WillRepeatedly(Return(ISize(100, 100))); + Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0); + auto contents = std::make_unique(); + contents->SetSigma(Sigma{sigma_radius_1}); + contents->SetDirection({1.0, 0.0}); + contents->SetInputs({FilterInput::Make(texture)}); + auto mock_context = std::make_shared(); + EXPECT_CALL(*mock_context, GetCapabilities()) + .WillRepeatedly(ReturnRef(capabilities_)); + EXPECT_CALL(*mock_context, IsValid()).WillRepeatedly(Return(true)); + auto mock_sampler_library = std::make_shared(); + auto mock_shader_library = std::make_shared(); + auto mock_pipeline_library = std::make_shared(); + EXPECT_CALL(*mock_pipeline_library, GetPipeline(An())) + .WillRepeatedly( + Invoke([&mock_pipeline_library](PipelineDescriptor descriptor) { + PipelineFuture result; + std::promise>> promise; + auto mock_pipeline = + std::make_shared>( + 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(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(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(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(); + auto mock_allocator = std::make_shared(); + auto mock_render_target_allocator = + std::make_shared(mock_allocator); + EXPECT_CALL(*mock_render_target_allocator, CreateTexture(_)) + .WillRepeatedly(Invoke(([](const TextureDescriptor& desc) { + auto result = std::make_shared(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 result = + contents->GetEntity(renderer, entity, coverage_hint); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(result.value().GetBlendMode(), BlendMode::kSourceOver); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/testing/mocks.h b/impeller/renderer/testing/mocks.h index 8d50ac115c27b..15c8ae8ef86e4 100644 --- a/impeller/renderer/testing/mocks.h +++ b/impeller/renderer/testing/mocks.h @@ -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 { @@ -164,5 +169,113 @@ class MockTexture : public Texture { (override)); }; +class MockTypographerContext : public TypographerContext { + public: + MOCK_METHOD(std::shared_ptr, + CreateGlyphAtlas, + (Context & context, + GlyphAtlas::Type type, + std::shared_ptr atlas_context, + const FontGlyphMap& font_glyph_map), + (const, override)); + MOCK_METHOD(std::shared_ptr, + CreateGlyphAtlasContext, + (), + (const, override)); +}; + +class MockRenderTargetAllocator : public RenderTargetAllocator { + public: + MockRenderTargetAllocator(std::shared_ptr allocator) + : RenderTargetAllocator(allocator) {} + MOCK_METHOD(std::shared_ptr, + 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 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, + GetSampler, + (SamplerDescriptor), + (override)); +}; + +class MockShaderLibrary : public ShaderLibrary { + public: + MOCK_METHOD(bool, IsValid, (), (const, override)); + MOCK_METHOD(std::shared_ptr, + 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, + GetPipeline, + (PipelineDescriptor descriptor), + (override)); + MOCK_METHOD(PipelineFuture, + GetPipeline, + (ComputePipelineDescriptor descriptor), + (override)); + MOCK_METHOD(void, + RemovePipelinesWithEntryPoint, + (std::shared_ptr function), + (override)); +}; + +template +class MockPipeline : public Pipeline { + public: + MockPipeline(std::weak_ptr library, T desc) + : Pipeline(library, desc) {} + MOCK_METHOD(bool, IsValid, (), (const, override)); +}; + } // namespace testing } // namespace impeller