diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 65ef43e095403..1a4985db5a07e 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -162,7 +162,8 @@ static std::unique_ptr CreateDefaultPipeline( ContentContext::ContentContext( std::shared_ptr context, - std::shared_ptr typographer_context) + std::shared_ptr typographer_context, + std::shared_ptr render_target_allocator) : context_(std::move(context)), lazy_glyph_atlas_( std::make_shared(std::move(typographer_context))), @@ -170,8 +171,10 @@ ContentContext::ContentContext( #if IMPELLER_ENABLE_3D scene_context_(std::make_shared(context_)), #endif // IMPELLER_ENABLE_3D - render_target_cache_(std::make_shared( - context_->GetResourceAllocator())) { + render_target_cache_(render_target_allocator == nullptr + ? std::make_shared( + context_->GetResourceAllocator()) + : std::move(render_target_allocator)) { if (!context_ || !context_->IsValid()) { return; } diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 3c72798623758..2778fec4f72c2 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -343,7 +343,8 @@ class ContentContext { public: explicit ContentContext( std::shared_ptr context, - std::shared_ptr typographer_context); + std::shared_ptr typographer_context, + std::shared_ptr render_target_allocator = nullptr); ~ContentContext(); diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 1d8b0a4f3735e..0d91112369ae7 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -783,7 +783,16 @@ bool EntityPass::OnRender( // rendered output will actually be used, and so we set this to the current // stencil coverage (which is the max clip bounds). The contents may // optionally use this hint to avoid unnecessary rendering work. - element_entity.GetContents()->SetCoverageHint(current_stencil_coverage); + if (element_entity.GetContents()->GetCoverageHint().has_value()) { + // If the element already has a coverage hint (because its an advanced + // blend), then we need to intersect the stencil coverage hint with the + // existing coverage hint. + element_entity.GetContents()->SetCoverageHint( + current_stencil_coverage->Intersection( + element_entity.GetContents()->GetCoverageHint().value())); + } else { + element_entity.GetContents()->SetCoverageHint(current_stencil_coverage); + } switch (stencil_coverage.type) { case Contents::StencilCoverage::Type::kNoChange: diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 3ff5548509b5e..c76ece221dbd8 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -7,12 +7,14 @@ #include #include #include +#include #include #include "flutter/testing/testing.h" #include "fml/logging.h" #include "fml/time/time_point.h" #include "gtest/gtest.h" +#include "impeller/core/texture_descriptor.h" #include "impeller/entity/contents/atlas_contents.h" #include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/conical_gradient_contents.h" @@ -42,6 +44,7 @@ #include "impeller/geometry/geometry_asserts.h" #include "impeller/geometry/path_builder.h" #include "impeller/geometry/sigma.h" +#include "impeller/geometry/vector.h" #include "impeller/playground/playground.h" #include "impeller/playground/widgets.h" #include "impeller/renderer/command.h" @@ -2441,6 +2444,89 @@ TEST_P(EntityTest, TextContentsCeilsGlyphScaleToDecimal) { ASSERT_EQ(TextFrame::RoundScaledFontSize(0.0f, 12), 0.0f); } +class TestRenderTargetAllocator : public RenderTargetAllocator { + public: + explicit TestRenderTargetAllocator(std::shared_ptr allocator) + : RenderTargetAllocator(std::move(allocator)) {} + + ~TestRenderTargetAllocator() = default; + + std::shared_ptr CreateTexture( + const TextureDescriptor& desc) override { + allocated_.push_back(desc); + return RenderTargetAllocator::CreateTexture(desc); + } + + void Start() override { RenderTargetAllocator::Start(); } + + void End() override { RenderTargetAllocator::End(); } + + std::vector GetDescriptors() const { return allocated_; } + + private: + std::vector allocated_; +}; + +TEST_P(EntityTest, AdvancedBlendCoverageHintIsNotResetByEntityPass) { + if (GetContext()->GetCapabilities()->SupportsFramebufferFetch()) { + GTEST_SKIP() << "Backends that support framebuffer fetch dont use coverage " + "for advanced blends."; + } + + auto contents = std::make_shared(); + contents->SetGeometry(Geometry::MakeRect({100, 100, 100, 100})); + contents->SetColor(Color::Red()); + + Entity entity; + entity.SetTransformation(Matrix::MakeScale(Vector3(2, 2, 1))); + entity.SetBlendMode(BlendMode::kColorBurn); + entity.SetContents(contents); + + auto coverage = entity.GetCoverage(); + EXPECT_TRUE(coverage.has_value()); + + auto pass = std::make_unique(); + auto test_allocator = std::make_shared( + GetContext()->GetResourceAllocator()); + auto stencil_config = RenderTarget::AttachmentConfig{ + .storage_mode = StorageMode::kDevicePrivate, + .load_action = LoadAction::kClear, + .store_action = StoreAction::kDontCare, + .clear_color = Color::BlackTransparent()}; + auto rt = RenderTarget::CreateOffscreen( + *GetContext(), *test_allocator, ISize::MakeWH(1000, 1000), "Offscreen", + RenderTarget::kDefaultColorAttachmentConfig, stencil_config); + auto content_context = ContentContext( + GetContext(), TypographerContextSkia::Make(), test_allocator); + pass->AddEntity(entity); + + EXPECT_TRUE(pass->Render(content_context, rt)); + + if (test_allocator->GetDescriptors().size() == 6u) { + EXPECT_EQ(test_allocator->GetDescriptors()[0].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[1].size, ISize(1000, 1000)); + + EXPECT_EQ(test_allocator->GetDescriptors()[2].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[3].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[4].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200)); + } else if (test_allocator->GetDescriptors().size() == 9u) { + // Onscreen render target. + EXPECT_EQ(test_allocator->GetDescriptors()[0].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[1].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[2].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[3].size, ISize(1000, 1000)); + EXPECT_EQ(test_allocator->GetDescriptors()[4].size, ISize(1000, 1000)); + + EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[5].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[6].size, ISize(200, 200)); + EXPECT_EQ(test_allocator->GetDescriptors()[7].size, ISize(200, 200)); + } else { + EXPECT_TRUE(false); + } +} + } // namespace testing } // namespace impeller