diff options
-rw-r--r-- | aidl/gui/android/view/LayerMetadataKey.aidl | 1 | ||||
-rw-r--r-- | libs/gui/LayerMetadata.cpp | 2 | ||||
-rw-r--r-- | libs/gui/include/gui/LayerMetadata.h | 3 | ||||
-rw-r--r-- | services/surfaceflinger/Layer.cpp | 14 | ||||
-rw-r--r-- | services/surfaceflinger/Layer.h | 11 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 7 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/Android.bp | 1 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/GameModeTest.cpp | 159 |
8 files changed, 197 insertions, 1 deletions
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl index a1d8ce5962..d6ca3db5d7 100644 --- a/aidl/gui/android/view/LayerMetadataKey.aidl +++ b/aidl/gui/android/view/LayerMetadataKey.aidl @@ -26,4 +26,5 @@ enum LayerMetadataKey { METADATA_ACCESSIBILITY_ID = 5, METADATA_OWNER_PID = 6, METADATA_DEQUEUE_TIME = 7, + METADATA_GAME_MODE = 8, } diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp index 634d8b7df0..189d51a4c1 100644 --- a/libs/gui/LayerMetadata.cpp +++ b/libs/gui/LayerMetadata.cpp @@ -136,6 +136,8 @@ std::string LayerMetadata::itemToString(uint32_t key, const char* separator) con return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0)); case view::LayerMetadataKey::METADATA_DEQUEUE_TIME: return StringPrintf("dequeueTime%s%" PRId64, separator, *getInt64(key)); + case view::LayerMetadataKey::METADATA_GAME_MODE: + return StringPrintf("gameMode%s%d", separator, getInt32(key, 0)); default: return StringPrintf("%d%s%dbytes", key, separator, static_cast<int>(mMap.at(key).size())); diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index 41982c28a2..de14b3d785 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -29,7 +29,8 @@ enum { METADATA_MOUSE_CURSOR = 4, METADATA_ACCESSIBILITY_ID = 5, METADATA_OWNER_PID = 6, - METADATA_DEQUEUE_TIME = 7 + METADATA_DEQUEUE_TIME = 7, + METADATA_GAME_MODE = 8 }; struct LayerMetadata : public Parcelable { diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index a7c870483b..d78a8223c4 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1651,12 +1651,25 @@ size_t Layer::getChildrenCount() const { return count; } +void Layer::setGameModeForTree(int parentGameMode) { + int gameMode = parentGameMode; + auto& currentState = getCurrentState(); + if (currentState.metadata.has(METADATA_GAME_MODE)) { + gameMode = currentState.metadata.getInt32(METADATA_GAME_MODE, 0); + } + setGameMode(gameMode); + for (const sp<Layer>& child : mCurrentChildren) { + child->setGameModeForTree(gameMode); + } +} + void Layer::addChild(const sp<Layer>& layer) { mChildrenChanged = true; setTransactionFlags(eTransactionNeeded); mCurrentChildren.add(layer); layer->setParent(this); + layer->setGameModeForTree(mGameMode); updateTreeHasFrameRateVote(); } @@ -1668,6 +1681,7 @@ ssize_t Layer::removeChild(const sp<Layer>& layer) { const auto removeResult = mCurrentChildren.remove(layer); updateTreeHasFrameRateVote(); + layer->setGameModeForTree(0); layer->updateTreeHasFrameRateVote(); return removeResult; diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 66d70185a7..af2604582b 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -847,6 +847,13 @@ public: */ bool hasInputInfo() const; + // Sets the parent's gameMode for this layer and all its children. Parent's gameMode is applied + // only to layers that do not have the GAME_MODE_METADATA set by WMShell. Any layer(along with + // its children) that has the metadata set will use the gameMode from the metadata. + void setGameModeForTree(int parentGameMode); + void setGameMode(int gameMode) { mGameMode = gameMode; }; + int getGameMode() const { return mGameMode; } + virtual uid_t getOwnerUid() const { return mOwnerUid; } pid_t getOwnerPid() { return mOwnerPid; } @@ -1089,6 +1096,10 @@ private: // shadow radius is the set shadow radius, otherwise its the parent's shadow radius. float mEffectiveShadowRadius = 0.f; + // Game mode for the layer. Set by WindowManagerShell, game mode is used in + // metrics(SurfaceFlingerStats). + int mGameMode = 0; + // A list of regions on this layer that should have blurs. const std::vector<BlurRegion> getBlurRegions() const; }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 881ee5b8f4..3f75af2908 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -4018,6 +4018,13 @@ uint32_t SurfaceFlinger::setClientStateLocked( std::optional<nsecs_t> dequeueBufferTimestamp; if (what & layer_state_t::eMetadataChanged) { dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME); + auto gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1); + if (gameMode != -1) { + // The transaction will be received on the Task layer and needs to be applied to all + // child layers. Child layers that are added at a later point will obtain the game mode + // info through addChild(). + layer->setGameModeForTree(gameMode); + } if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded; } if (what & layer_state_t::eColorSpaceAgnosticChanged) { diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 1b25a3684e..736ef30d7c 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -57,6 +57,7 @@ cc_test { "FpsTest.cpp", "FramebufferSurfaceTest.cpp", "FrameTimelineTest.cpp", + "GameModeTest.cpp", "HWComposerTest.cpp", "OneShotTimerTest.cpp", "LayerHistoryTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp new file mode 100644 index 0000000000..3fa1a2c2f5 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp @@ -0,0 +1,159 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <gui/LayerMetadata.h> +#include <gui/SurfaceComposerClient.h> +#include <log/log.h> + +#include "TestableSurfaceFlinger.h" +#include "mock/DisplayHardware/MockComposer.h" +#include "mock/MockEventThread.h" +#include "mock/MockVsyncController.h" + +namespace android { + +using testing::_; +using testing::Mock; +using testing::Return; +using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + +class GameModeTest : public testing::Test { +public: + GameModeTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + setupScheduler(); + setupComposer(); + } + + ~GameModeTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + sp<BufferStateLayer> createBufferStateLayer() { + sp<Client> client; + LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0, + LayerMetadata()); + return new BufferStateLayer(args); + } + + void setupScheduler() { + auto eventThread = std::make_unique<mock::EventThread>(); + auto sfEventThread = std::make_unique<mock::EventThread>(); + + EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*eventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback()))); + + EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback()))); + + auto vsyncController = std::make_unique<mock::VsyncController>(); + auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); + + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, currentPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), + std::move(eventThread), std::move(sfEventThread)); + } + + void setupComposer() { + mComposer = new Hwc2::mock::Composer(); + mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + + Mock::VerifyAndClear(mComposer); + } + + // Mocks the behavior of applying a transaction from WMShell + void setGameModeMetadata(sp<Layer> layer, int gameMode) { + mLayerMetadata.setInt32(METADATA_GAME_MODE, gameMode); + layer->setMetadata(mLayerMetadata); + layer->setGameModeForTree(gameMode); + } + + TestableSurfaceFlinger mFlinger; + Hwc2::mock::Composer* mComposer = nullptr; + client_cache_t mClientCache; + LayerMetadata mLayerMetadata; +}; + +TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) { + sp<BufferStateLayer> rootLayer = createBufferStateLayer(); + sp<BufferStateLayer> childLayer1 = createBufferStateLayer(); + sp<BufferStateLayer> childLayer2 = createBufferStateLayer(); + rootLayer->addChild(childLayer1); + rootLayer->addChild(childLayer2); + rootLayer->setGameModeForTree(/*gameMode*/ 2); + + EXPECT_EQ(rootLayer->getGameMode(), 2); + EXPECT_EQ(childLayer1->getGameMode(), 2); + EXPECT_EQ(childLayer2->getGameMode(), 2); +} + +TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) { + sp<BufferStateLayer> rootLayer = createBufferStateLayer(); + sp<BufferStateLayer> childLayer = createBufferStateLayer(); + rootLayer->setGameModeForTree(/*gameMode*/ 2); + rootLayer->addChild(childLayer); + + EXPECT_EQ(rootLayer->getGameMode(), 2); + EXPECT_EQ(childLayer->getGameMode(), 2); +} + +TEST_F(GameModeTest, RemoveChildResetsGameMode) { + sp<BufferStateLayer> rootLayer = createBufferStateLayer(); + sp<BufferStateLayer> childLayer = createBufferStateLayer(); + rootLayer->setGameModeForTree(/*gameMode*/ 2); + rootLayer->addChild(childLayer); + + EXPECT_EQ(rootLayer->getGameMode(), 2); + EXPECT_EQ(childLayer->getGameMode(), 2); + + rootLayer->removeChild(childLayer); + EXPECT_EQ(childLayer->getGameMode(), 0); +} + +TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) { + sp<BufferStateLayer> rootLayer = createBufferStateLayer(); + sp<BufferStateLayer> childLayer1 = createBufferStateLayer(); + sp<BufferStateLayer> childLayer2 = createBufferStateLayer(); + rootLayer->setGameModeForTree(/*gameMode*/ 1); + rootLayer->addChild(childLayer1); + + setGameModeMetadata(childLayer2, /*gameMode*/ 2); + rootLayer->addChild(childLayer2); + + EXPECT_EQ(rootLayer->getGameMode(), 1); + EXPECT_EQ(childLayer1->getGameMode(), 1); + EXPECT_EQ(childLayer2->getGameMode(), 2); + + rootLayer->removeChild(childLayer2); + EXPECT_EQ(childLayer2->getGameMode(), 2); +} +} // namespace android
\ No newline at end of file |