summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Lloyd Pique <lpique@google.com> 2019-08-28 15:45:25 -0700
committer Lloyd Pique <lpique@google.com> 2019-08-28 18:00:16 -0700
commit56eba802e4966725ded65b54d29fca0faa17c331 (patch)
tree59226de5825fca37e6d96ab994978a488e8167ba
parentf66166b5ce32b3af74cbf901b20f2604f34a23cc (diff)
SF: Fix layer outside of viewport test
When performing client (GPU) composition, the code checks to see if the visible region for each layer is contained in the output viewport. Unfortunately when moving the code to CompositionEngine, I used the "wrong" visible region -- one that had been transformed by the output rotation. This led to layers that should have been visible in split screen portrait mode not being rendered, leaving a black space on the screen. The fix was simply to use the non-transformed visible region, as set in the output-independent state in LayerStateFE. Additionally as there was a lack of any unit test coverage for the Output::composeSurfaces and Output::generateClientCompositionRequests functions, I've added some quick basic tests, as well as a specific test to simulate the reported regression. Bug: 139806358 Bug: 121291683 Test: Manual test on crosshatch Test: atest libcompositionengine_test Change-Id: I028ef238a2e08759cf32ea2f2e27aaea32f3e89b
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Output.cpp2
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp271
2 files changed, 272 insertions, 1 deletions
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 55fdacde44..e4404bffd6 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -402,7 +402,7 @@ std::vector<renderengine::LayerSettings> Output::generateClientCompositionReques
const auto& layerFEState = layer->getLayer().getState().frontEnd;
auto& layerFE = layer->getLayerFE();
- const Region clip(viewportRegion.intersect(layer->getState().visibleRegion));
+ const Region clip(viewportRegion.intersect(layerFEState.geomVisibleRegion));
ALOGV("Layer: %s", layerFE.getDebugName());
if (clip.isEmpty()) {
ALOGV(" Skipping for empty clip");
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index aa35d2581c..318818a11c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -16,8 +16,10 @@
#include <cmath>
+#include <compositionengine/impl/LayerCompositionState.h>
#include <compositionengine/impl/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/CompositionEngine.h>
#include <compositionengine/mock/DisplayColorProfile.h>
#include <compositionengine/mock/Layer.h>
@@ -25,6 +27,7 @@
#include <compositionengine/mock/OutputLayer.h>
#include <compositionengine/mock/RenderSurface.h>
#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -34,10 +37,14 @@
namespace android::compositionengine {
namespace {
+using testing::_;
using testing::Return;
using testing::ReturnRef;
using testing::StrictMock;
+constexpr auto TR_IDENT = 0u;
+constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90;
+
struct OutputTest : public testing::Test {
OutputTest() {
mOutput.setDisplayColorProfileForTest(
@@ -446,5 +453,269 @@ TEST_F(OutputTest, prepareFrameSetsClientCompositionOnlyByDefault) {
EXPECT_FALSE(mOutput.getState().usesDeviceComposition);
}
+/*
+ * Output::composeSurfaces()
+ */
+
+struct OutputComposeSurfacesTest : public testing::Test {
+ static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
+ static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::DISPLAY_P3;
+
+ static const Rect kDefaultOutputFrame;
+ static const Rect kDefaultOutputViewport;
+ static const Rect kDefaultOutputScissor;
+ static const mat4 kDefaultColorTransformMat;
+
+ struct OutputPartialMock : public impl::Output {
+ OutputPartialMock(const compositionengine::CompositionEngine& compositionEngine)
+ : impl::Output(compositionEngine) {}
+
+ // Sets up the helper functions called by composeSurfaces to use a mock
+ // implementations.
+ MOCK_CONST_METHOD0(getSkipColorTransform, bool());
+ MOCK_METHOD2(generateClientCompositionRequests,
+ std::vector<renderengine::LayerSettings>(bool, Region&));
+ MOCK_METHOD2(appendRegionFlashRequests,
+ void(const Region&, std::vector<renderengine::LayerSettings>&));
+ MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+ };
+
+ OutputComposeSurfacesTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+ Output::OutputLayers outputLayers;
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(mOutputLayer1));
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(mOutputLayer2));
+ mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+
+ mOutput.editState().frame = kDefaultOutputFrame;
+ mOutput.editState().viewport = kDefaultOutputViewport;
+ mOutput.editState().scissor = kDefaultOutputScissor;
+ mOutput.editState().transform = ui::Transform{kDefaultOutputOrientation};
+ mOutput.editState().orientation = kDefaultOutputOrientation;
+ mOutput.editState().dataspace = kDefaultOutputDataspace;
+ mOutput.editState().colorTransformMat = kDefaultColorTransformMat;
+ mOutput.editState().isSecure = true;
+ mOutput.editState().needsFiltering = false;
+ mOutput.editState().usesClientComposition = true;
+ mOutput.editState().usesDeviceComposition = false;
+
+ EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+ }
+
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ mock::OutputLayer* mOutputLayer1 = new StrictMock<mock::OutputLayer>();
+ mock::OutputLayer* mOutputLayer2 = new StrictMock<mock::OutputLayer>();
+ StrictMock<OutputPartialMock> mOutput{mCompositionEngine};
+ sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer();
+};
+
+const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
+const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
+const Rect OutputComposeSurfacesTest::kDefaultOutputScissor{1009, 1010, 1011, 1012};
+const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5};
+
+// TODO(b/121291683): Expand unit test coverage for composeSurfaces beyond these
+// basic tests.
+
+TEST_F(OutputComposeSurfacesTest, doesNothingIfNoClientComposition) {
+ mOutput.editState().usesClientComposition = false;
+
+ Region debugRegion;
+ base::unique_fd readyFence;
+ EXPECT_EQ(true, mOutput.composeSurfaces(debugRegion, &readyFence));
+}
+
+TEST_F(OutputComposeSurfacesTest, worksIfNoClientLayersQueued) {
+ const Region kDebugRegion{Rect{100, 101, 102, 103}};
+
+ constexpr float kDefaultMaxLuminance = 1.0f;
+ constexpr float kDefaultAvgLuminance = 0.7f;
+ constexpr float kDefaultMinLuminance = 0.1f;
+ HdrCapabilities HdrCapabilities{{},
+ kDefaultMaxLuminance,
+ kDefaultAvgLuminance,
+ kDefaultMinLuminance};
+
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).Times(1);
+
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillOnce(Return(true));
+ EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities()).WillOnce(ReturnRef(HdrCapabilities));
+
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
+
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillOnce(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _)).Times(1);
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)).Times(1);
+ EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(1);
+ EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)).Times(1);
+
+ base::unique_fd readyFence;
+ EXPECT_EQ(true, mOutput.composeSurfaces(kDebugRegion, &readyFence));
+}
+
+/*
+ * Output::generateClientCompositionRequests()
+ */
+
+struct GenerateClientCompositionRequestsTest : public testing::Test {
+ struct OutputPartialMock : public impl::Output {
+ OutputPartialMock(const compositionengine::CompositionEngine& compositionEngine)
+ : impl::Output(compositionEngine) {}
+
+ std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
+ bool supportsProtectedContent, Region& clearRegion) override {
+ return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
+ clearRegion);
+ }
+ };
+
+ GenerateClientCompositionRequestsTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ StrictMock<OutputPartialMock> mOutput{mCompositionEngine};
+};
+
+// TODO(b/121291683): Add more unit test coverage for generateClientCompositionRequests
+
+TEST_F(GenerateClientCompositionRequestsTest, worksForLandscapeModeSplitScreen) {
+ // In split-screen landscape mode, the screen is rotated 90 degrees, with
+ // one layer on the left covering the left side of the output, and one layer
+ // on the right covering that side of the output.
+
+ mock::OutputLayer* leftOutputLayer = new StrictMock<mock::OutputLayer>();
+ mock::OutputLayer* rightOutputLayer = new StrictMock<mock::OutputLayer>();
+
+ StrictMock<mock::Layer> leftLayer;
+ StrictMock<mock::LayerFE> leftLayerFE;
+ StrictMock<mock::Layer> rightLayer;
+ StrictMock<mock::LayerFE> rightLayerFE;
+
+ impl::OutputLayerCompositionState leftOutputLayerState;
+ leftOutputLayerState.clearClientTarget = false;
+
+ impl::LayerCompositionState leftLayerState;
+ leftLayerState.frontEnd.geomVisibleRegion = Region{Rect{0, 0, 1000, 1000}};
+ leftLayerState.frontEnd.isOpaque = true;
+
+ const half3 leftLayerColor{1.f, 0.f, 0.f};
+ renderengine::LayerSettings leftLayerRESettings;
+ leftLayerRESettings.source.solidColor = leftLayerColor;
+
+ impl::OutputLayerCompositionState rightOutputLayerState;
+ rightOutputLayerState.clearClientTarget = false;
+
+ impl::LayerCompositionState rightLayerState;
+ rightLayerState.frontEnd.geomVisibleRegion = Region{Rect{1000, 0, 2000, 1000}};
+ rightLayerState.frontEnd.isOpaque = true;
+
+ const half3 rightLayerColor{0.f, 1.f, 0.f};
+ renderengine::LayerSettings rightLayerRESettings;
+ rightLayerRESettings.source.solidColor = rightLayerColor;
+
+ EXPECT_CALL(*leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
+ EXPECT_CALL(*leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
+ EXPECT_CALL(*leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
+ EXPECT_CALL(*leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(leftLayer, getState()).WillRepeatedly(ReturnRef(leftLayerState));
+ EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
+
+ EXPECT_CALL(*rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
+ EXPECT_CALL(*rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
+ EXPECT_CALL(*rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
+ EXPECT_CALL(*rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(rightLayer, getState()).WillRepeatedly(ReturnRef(rightLayerState));
+ EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
+
+ Output::OutputLayers outputLayers;
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(leftOutputLayer));
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(rightOutputLayer));
+ mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+
+ const Rect kPortraitFrame(0, 0, 1000, 2000);
+ const Rect kPortraitViewport(0, 0, 2000, 1000);
+ const Rect kPortraitScissor(0, 0, 1000, 2000);
+ const uint32_t kPortraitOrientation = TR_ROT_90;
+
+ mOutput.editState().frame = kPortraitFrame;
+ mOutput.editState().viewport = kPortraitViewport;
+ mOutput.editState().scissor = kPortraitScissor;
+ mOutput.editState().transform = ui::Transform{kPortraitOrientation};
+ mOutput.editState().orientation = kPortraitOrientation;
+ mOutput.editState().needsFiltering = true;
+ mOutput.editState().isSecure = false;
+
+ constexpr bool supportsProtectedContent = false;
+ Region clearRegion;
+ auto requests =
+ mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+
+ ASSERT_EQ(2u, requests.size());
+ EXPECT_EQ(leftLayerColor, requests[0].source.solidColor);
+ EXPECT_EQ(rightLayerColor, requests[1].source.solidColor);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest, ignoresLayersThatDoNotIntersectWithViewport) {
+ // Layers whose visible region does not intersect with the viewport will be
+ // skipped when generating client composition request state.
+
+ mock::OutputLayer* outputLayer = new StrictMock<mock::OutputLayer>();
+ StrictMock<mock::Layer> layer;
+ StrictMock<mock::LayerFE> layerFE;
+
+ impl::OutputLayerCompositionState outputLayerState;
+ outputLayerState.clearClientTarget = false;
+
+ impl::LayerCompositionState layerState;
+ layerState.frontEnd.geomVisibleRegion = Region{Rect{3000, 0, 4000, 1000}};
+ layerState.frontEnd.isOpaque = true;
+
+ EXPECT_CALL(*outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(*outputLayer, getLayer()).WillRepeatedly(ReturnRef(layer));
+ EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(*outputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*outputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(layerState));
+ EXPECT_CALL(layerFE, prepareClientComposition(_)).Times(0);
+
+ Output::OutputLayers outputLayers;
+ outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer));
+ mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+
+ const Rect kPortraitFrame(0, 0, 1000, 2000);
+ const Rect kPortraitViewport(0, 0, 2000, 1000);
+ const Rect kPortraitScissor(0, 0, 1000, 2000);
+ const uint32_t kPortraitOrientation = TR_ROT_90;
+
+ mOutput.editState().frame = kPortraitFrame;
+ mOutput.editState().viewport = kPortraitViewport;
+ mOutput.editState().scissor = kPortraitScissor;
+ mOutput.editState().transform = ui::Transform{kPortraitOrientation};
+ mOutput.editState().orientation = kPortraitOrientation;
+ mOutput.editState().needsFiltering = true;
+ mOutput.editState().isSecure = false;
+
+ constexpr bool supportsProtectedContent = false;
+ Region clearRegion;
+ auto requests =
+ mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+
+ EXPECT_EQ(0u, requests.size());
+}
+
} // namespace
} // namespace android::compositionengine