diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 212e9095ac440..a3b47fc20bb3f 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -144,6 +144,7 @@ ../../../flutter/impeller/display_list/skia_conversions_unittests.cc ../../../flutter/impeller/docs ../../../flutter/impeller/entity/contents/checkerboard_contents_unittests.cc +../../../flutter/impeller/entity/contents/clip_contents_unittests.cc ../../../flutter/impeller/entity/contents/content_context_unittests.cc ../../../flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc ../../../flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 9e1a56a21a3c5..b4698a4f1e9d9 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -267,6 +267,7 @@ impeller_component("entity_unittests") { sources = [ "contents/checkerboard_contents_unittests.cc", + "contents/clip_contents_unittests.cc", "contents/content_context_unittests.cc", "contents/filters/gaussian_blur_filter_contents_unittests.cc", "contents/filters/inputs/filter_input_unittests.cc", diff --git a/impeller/entity/contents/clip_contents.cc b/impeller/entity/contents/clip_contents.cc index 4897be855815a..c964a196d9852 100644 --- a/impeller/entity/contents/clip_contents.cc +++ b/impeller/entity/contents/clip_contents.cc @@ -89,6 +89,18 @@ bool ClipContents::RenderDepthClip(const ContentContext& renderer, const Geometry& geometry) const { using VS = ClipPipeline::VertexShader; + if (clip_op == Entity::ClipOperation::kIntersect && + geometry.IsAxisAlignedRect() && + entity.GetTransform().IsTranslationScaleOnly()) { + std::optional coverage = geometry.GetCoverage(entity.GetTransform()); + if (coverage.has_value() && + coverage->Contains(Rect::MakeSize(pass.GetRenderTargetSize()))) { + // Skip axis-aligned intersect clips that cover the whole render target + // since they won't draw anything to the depth buffer. + return true; + } + } + VS::FrameInfo info; info.depth = GetShaderClipDepth(entity); diff --git a/impeller/entity/contents/clip_contents_unittests.cc b/impeller/entity/contents/clip_contents_unittests.cc new file mode 100644 index 0000000000000..c7fc1dd9d92c1 --- /dev/null +++ b/impeller/entity/contents/clip_contents_unittests.cc @@ -0,0 +1,59 @@ +// 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 +#include + +#include "gtest/gtest.h" + +#include "impeller/entity/contents/checkerboard_contents.h" +#include "impeller/entity/contents/clip_contents.h" +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/test/recording_render_pass.h" +#include "impeller/entity/entity.h" +#include "impeller/entity/entity_playground.h" +#include "impeller/renderer/render_target.h" + +namespace impeller { +namespace testing { + +using EntityTest = EntityPlayground; + +TEST_P(EntityTest, ClipContentsOptimizesFullScreenIntersectClips) { + if (!ContentContext::kEnableStencilThenCover) { + GTEST_SKIP(); + return; + } + + // Set up mock environment. + + auto content_context = GetContentContext(); + auto buffer = content_context->GetContext()->CreateCommandBuffer(); + auto render_target = + GetContentContext()->GetRenderTargetCache()->CreateOffscreenMSAA( + *content_context->GetContext(), {100, 100}, + /*mip_count=*/1); + auto render_pass = buffer->CreateRenderPass(render_target); + auto recording_pass = std::make_shared( + render_pass, GetContext(), render_target); + + // Set up clip contents. + + auto contents = std::make_shared(); + contents->SetClipOperation(Entity::ClipOperation::kIntersect); + contents->SetGeometry(Geometry::MakeCover()); + + Entity entity; + entity.SetContents(std::move(contents)); + + // Render the clip contents. + + ASSERT_TRUE(recording_pass->GetCommands().empty()); + ASSERT_TRUE(entity.Render(*content_context, *recording_pass)); + ASSERT_FALSE(recording_pass->GetCommands().empty()); +} + +} // namespace testing +} // namespace impeller