diff options
14 files changed, 431 insertions, 87 deletions
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 08147edcfc..d738ccdb90 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -57,6 +57,7 @@ cc_library { "src/planner/LayerState.cpp", "src/planner/Planner.cpp", "src/planner/Predictor.cpp", + "src/planner/TexturePool.cpp", "src/ClientCompositionRequestCache.cpp", "src/CompositionEngine.cpp", "src/Display.cpp", @@ -107,6 +108,7 @@ cc_test { "tests/planner/FlattenerTest.cpp", "tests/planner/LayerStateTest.cpp", "tests/planner/PredictorTest.cpp", + "tests/planner/TexturePoolTest.cpp", "tests/CompositionEngineTest.cpp", "tests/DisplayColorProfileTest.cpp", "tests/DisplayTest.cpp", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index 2c18a60cd9..a4356c50c6 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -19,6 +19,7 @@ #include <compositionengine/Output.h> #include <compositionengine/ProjectionSpace.h> #include <compositionengine/impl/planner/LayerState.h> +#include <compositionengine/impl/planner/TexturePool.h> #include <renderengine/RenderEngine.h> #include <chrono> @@ -64,9 +65,12 @@ public: size_t getLayerCount() const { return mLayers.size(); } const Layer& getFirstLayer() const { return mLayers[0]; } const Rect& getBounds() const { return mBounds; } + Rect getTextureBounds() const { return mOutputSpace.content; } const Region& getVisibleRegion() const { return mVisibleRegion; } size_t getAge() const { return mAge; } - const std::shared_ptr<renderengine::ExternalTexture>& getBuffer() const { return mTexture; } + std::shared_ptr<renderengine::ExternalTexture> getBuffer() const { + return mTexture ? mTexture->get() : nullptr; + } const sp<Fence>& getDrawFence() const { return mDrawFence; } const ProjectionSpace& getOutputSpace() const { return mOutputSpace; } ui::Dataspace getOutputDataspace() const { return mOutputDataspace; } @@ -89,7 +93,7 @@ public: void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; } void append(const CachedSet& other) { - mTexture = nullptr; + mTexture.reset(); mOutputDataspace = ui::Dataspace::UNKNOWN; mDrawFence = nullptr; mBlurLayer = nullptr; @@ -105,7 +109,8 @@ public: void incrementAge() { ++mAge; } // Renders the cached set with the supplied output composition state. - void render(renderengine::RenderEngine& re, const OutputCompositionState& outputState); + void render(renderengine::RenderEngine& re, TexturePool& texturePool, + const OutputCompositionState& outputState); void dump(std::string& result) const; @@ -151,7 +156,9 @@ private: Region mVisibleRegion; size_t mAge = 0; - std::shared_ptr<renderengine::ExternalTexture> mTexture; + // TODO(b/190411067): This is a shared pointer only because CachedSets are copied into different + // containers in the Flattener. Logically this should have unique ownership otherwise. + std::shared_ptr<TexturePool::AutoTexture> mTexture; sp<Fence> mDrawFence; ProjectionSpace mOutputSpace; ui::Dataspace mOutputDataspace; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h index c76078d39a..94a169e11c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h @@ -37,16 +37,18 @@ class Predictor; class Flattener { public: - Flattener(bool enableHolePunch = false); + Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false); - void setDisplaySize(ui::Size size) { mDisplaySize = size; } + void setDisplaySize(ui::Size size) { + mDisplaySize = size; + mTexturePool.setDisplaySize(size); + } NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash, std::chrono::steady_clock::time_point now); // Renders the newest cached sets with the supplied output composition state - void renderCachedSets(renderengine::RenderEngine& re, - const OutputCompositionState& outputState); + void renderCachedSets(const OutputCompositionState& outputState); void dump(std::string& result) const; void dumpLayers(std::string& result) const; @@ -145,8 +147,11 @@ private: void buildCachedSets(std::chrono::steady_clock::time_point now); + renderengine::RenderEngine& mRenderEngine; const bool mEnableHolePunch; + TexturePool mTexturePool; + ui::Size mDisplaySize; NonBufferHash mCurrentGeometry; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h index 4365b93bb9..fd1ddfc2f0 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h @@ -41,7 +41,7 @@ namespace compositionengine::impl::planner { // as a more efficient representation of parts of the layer stack. class Planner { public: - Planner(); + Planner(renderengine::RenderEngine& renderengine); void setDisplaySize(ui::Size); @@ -59,8 +59,7 @@ public: compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers); // The planner will call to the Flattener to render any pending cached set - void renderCachedSets(renderengine::RenderEngine& re, - const OutputCompositionState& outputState); + void renderCachedSets(const OutputCompositionState& outputState); void dump(const Vector<String16>& args, std::string&); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h new file mode 100644 index 0000000000..fb53ee04cd --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#pragma once + +#include <compositionengine/Output.h> +#include <compositionengine/ProjectionSpace.h> +#include <compositionengine/impl/planner/LayerState.h> +#include <renderengine/RenderEngine.h> + +#include <renderengine/ExternalTexture.h> +#include <chrono> +#include "android-base/macros.h" + +namespace android::compositionengine::impl::planner { + +// A pool of textures that only manages textures of a single size. +// While it is possible to define a texture pool supporting variable-sized textures to save on +// memory, it is a simpler implementation to only manage screen-sized textures. The texture pool is +// unbounded - there are a minimum number of textures preallocated. Under heavy system load, new +// textures may be allocated, but only a maximum number of retained once those textures are no +// longer necessary. +class TexturePool { +public: + // RAII class helping with managing textures from the texture pool + // Textures once they're no longer used should be returned to the pool instead of outright + // deleted. + class AutoTexture { + public: + AutoTexture(TexturePool& texturePool, + std::shared_ptr<renderengine::ExternalTexture> texture, const sp<Fence>& fence) + : mTexturePool(texturePool), mTexture(texture), mFence(fence) {} + + ~AutoTexture() { mTexturePool.returnTexture(std::move(mTexture), mFence); } + + sp<Fence> getReadyFence() { return mFence; } + + void setReadyFence(const sp<Fence>& fence) { mFence = fence; } + + // Disable copying and assigning + AutoTexture(const AutoTexture&) = delete; + AutoTexture& operator=(const AutoTexture&) = delete; + + // Gets a pointer to the underlying external texture + const std::shared_ptr<renderengine::ExternalTexture>& get() const { return mTexture; } + + private: + TexturePool& mTexturePool; + std::shared_ptr<renderengine::ExternalTexture> mTexture; + sp<Fence> mFence; + }; + + TexturePool(renderengine::RenderEngine& renderEngine) : mRenderEngine(renderEngine) {} + + virtual ~TexturePool() = default; + + // Sets the display size for the texture pool. + // This will trigger a reallocation for all remaining textures in the pool. + // setDisplaySize must be called for the texture pool to be used. + void setDisplaySize(ui::Size size); + + // Borrows a new texture from the pool. + // If the pool is currently starved of textures, then a new texture is generated. + // When the AutoTexture object is destroyed, the scratch texture is automatically returned + // to the pool. + std::shared_ptr<AutoTexture> borrowTexture(); + +protected: + // Proteted visibility so that they can be used for testing + const static constexpr size_t kMinPoolSize = 3; + const static constexpr size_t kMaxPoolSize = 4; + + struct Entry { + std::shared_ptr<renderengine::ExternalTexture> texture; + sp<Fence> fence; + }; + + std::deque<Entry> mPool; + +private: + std::shared_ptr<renderengine::ExternalTexture> genTexture(); + // Returns a previously borrowed texture to the pool. + void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture, + const sp<Fence>& fence); + renderengine::RenderEngine& mRenderEngine; + ui::Size mSize; +}; + +} // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index e9a8b912f2..cd2f742da3 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -129,7 +129,7 @@ void Output::setLayerCachingEnabled(bool enabled) { } if (enabled) { - mPlanner = std::make_unique<planner::Planner>(); + mPlanner = std::make_unique<planner::Planner>(getCompositionEngine().getRenderEngine()); if (mRenderSurface) { mPlanner->setDisplaySize(mRenderSurface->getSize()); } @@ -1314,7 +1314,7 @@ void Output::postFramebuffer() { void Output::renderCachedSets() { if (mPlanner) { - mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState()); + mPlanner->renderCachedSets(getState()); } } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index dcfb05d491..69e8c7d41e 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -134,7 +134,7 @@ bool CachedSet::hasBufferUpdate() const { } bool CachedSet::hasReadyBuffer() const { - return mTexture != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled; + return mTexture && mDrawFence->getStatus() == Fence::Status::Signaled; } std::vector<CachedSet> CachedSet::decompose() const { @@ -156,7 +156,7 @@ void CachedSet::updateAge(std::chrono::steady_clock::time_point now) { } } -void CachedSet::render(renderengine::RenderEngine& renderEngine, +void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool, const OutputCompositionState& outputState) { ATRACE_CALL(); const Rect& viewport = outputState.layerStackSpace.content; @@ -165,10 +165,7 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation); renderengine::DisplaySettings displaySettings{ - .physicalDisplay = Rect(-mBounds.left + outputState.framebufferSpace.content.left, - -mBounds.top + outputState.framebufferSpace.content.top, - -mBounds.left + outputState.framebufferSpace.content.right, - -mBounds.top + outputState.framebufferSpace.content.bottom), + .physicalDisplay = outputState.framebufferSpace.content, .clip = viewport, .outputDataspace = outputDataspace, .orientation = orientation, @@ -255,30 +252,33 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, layerSettingsPointers.emplace_back(&highlight); } - const uint64_t usageFlags = GraphicBuffer::USAGE_HW_RENDER | GraphicBuffer::USAGE_HW_COMPOSER | - GraphicBuffer::USAGE_HW_TEXTURE; - sp<GraphicBuffer> buffer = new GraphicBuffer(static_cast<uint32_t>(mBounds.getWidth()), - static_cast<uint32_t>(mBounds.getHeight()), - HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags); - const auto texture = std::make_shared< - renderengine::ExternalTexture>(buffer, renderEngine, - renderengine::ExternalTexture::Usage::READABLE | - renderengine::ExternalTexture::Usage::WRITEABLE); - LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK); - base::unique_fd drawFence; + auto texture = texturePool.borrowTexture(); + LOG_ALWAYS_FATAL_IF(texture->get()->getBuffer()->initCheck() != OK); + + base::unique_fd bufferFence; + if (texture->getReadyFence()) { + // Bail out if the buffer is not ready, because there is some pending GPU work left. + if (texture->getReadyFence()->getStatus() != Fence::Status::Signaled) { + return; + } + bufferFence.reset(texture->getReadyFence()->dup()); + } - status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture, - false, base::unique_fd(), &drawFence); + base::unique_fd drawFence; + status_t result = + renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture->get(), false, + std::move(bufferFence), &drawFence); if (result == NO_ERROR) { mDrawFence = new Fence(drawFence.release()); mOutputSpace = outputState.framebufferSpace; - mTexture = std::move(texture); + mTexture = texture; + mTexture->setReadyFence(mDrawFence); mOutputSpace.orientation = outputState.framebufferSpace.orientation; mOutputDataspace = outputDataspace; mOrientation = orientation; } else { - mTexture = nullptr; + mTexture.reset(); } } @@ -363,7 +363,7 @@ void CachedSet::dump(std::string& result) const { base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n", mFingerprint, durationString(lastUpdate).c_str(), mAge); { - const auto b = mTexture ? mTexture->getBuffer().get() : nullptr; + const auto b = mTexture ? mTexture->get()->getBuffer().get() : nullptr; base::StringAppendF(&result, " Override buffer: %p\n", b); } base::StringAppendF(&result, " HolePunchLayer: %p\n", mHolePunchLayer); diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 48fb51f7ac..192c411a4f 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -60,7 +60,10 @@ bool isSameStack(const std::vector<const LayerState*>& incomingLayers, } // namespace -Flattener::Flattener(bool enableHolePunch) : mEnableHolePunch(enableHolePunch) { +Flattener::Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch) + : mRenderEngine(renderEngine), + mEnableHolePunch(enableHolePunch), + mTexturePool(mRenderEngine) { const int timeoutInMs = base::GetIntProperty(std::string("debug.sf.layer_caching_active_layer_timeout_ms"), 0); if (timeoutInMs != 0) { @@ -102,14 +105,13 @@ NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& lay return hash; } -void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine, - const OutputCompositionState& outputState) { +void Flattener::renderCachedSets(const OutputCompositionState& outputState) { ATRACE_CALL(); if (!mNewCachedSet || mNewCachedSet->hasRenderedBuffer()) { return; } - mNewCachedSet->render(renderEngine, outputState); + mNewCachedSet->render(mRenderEngine, mTexturePool, outputState); } void Flattener::dumpLayers(std::string& result) const { @@ -285,7 +287,7 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers state.overrideInfo = { .buffer = mNewCachedSet->getBuffer(), .acquireFence = mNewCachedSet->getDrawFence(), - .displayFrame = mNewCachedSet->getBounds(), + .displayFrame = mNewCachedSet->getTextureBounds(), .dataspace = mNewCachedSet->getOutputDataspace(), .displaySpace = mNewCachedSet->getOutputSpace(), .damageRegion = Region::INVALID_REGION, @@ -325,7 +327,7 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers state.overrideInfo = { .buffer = currentLayerIter->getBuffer(), .acquireFence = currentLayerIter->getDrawFence(), - .displayFrame = currentLayerIter->getBounds(), + .displayFrame = currentLayerIter->getTextureBounds(), .dataspace = currentLayerIter->getOutputDataspace(), .displaySpace = currentLayerIter->getOutputSpace(), .damageRegion = Region(), diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp index 297c0b2f7e..711a63499b 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp @@ -29,12 +29,13 @@ namespace android::compositionengine::impl::planner { -Planner::Planner() +Planner::Planner(renderengine::RenderEngine& renderEngine) // Implicitly, layer caching must also be enabled for the hole punch or // predictor to have any effect. // E.g., setprop debug.sf.enable_layer_caching 1, or // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>] - : mFlattener(base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true)) { + : mFlattener(renderEngine, + base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true)) { mPredictorEnabled = base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false); } @@ -160,10 +161,9 @@ void Planner::reportFinalPlan( finalPlan); } -void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine, - const OutputCompositionState& outputState) { +void Planner::renderCachedSets(const OutputCompositionState& outputState) { ATRACE_CALL(); - mFlattener.renderCachedSets(renderEngine, outputState); + mFlattener.renderCachedSets(outputState); } void Planner::dump(const Vector<String16>& args, std::string& result) { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp new file mode 100644 index 0000000000..e3772a22d2 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp @@ -0,0 +1,84 @@ +/* + * 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. + */ + +// #define LOG_NDEBUG 0 + +#undef LOG_TAG +#define LOG_TAG "Planner" + +#include <compositionengine/impl/planner/TexturePool.h> +#include <utils/Log.h> + +namespace android::compositionengine::impl::planner { + +void TexturePool::setDisplaySize(ui::Size size) { + if (mSize == size) { + return; + } + mSize = size; + mPool.clear(); + mPool.resize(kMinPoolSize); + std::generate_n(mPool.begin(), kMinPoolSize, [&]() { return Entry{genTexture(), nullptr}; }); +} + +std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() { + if (mPool.empty()) { + return std::make_shared<AutoTexture>(*this, genTexture(), nullptr); + } + + const auto entry = mPool.front(); + mPool.pop_front(); + return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence); +} + +void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture, + const sp<Fence>& fence) { + // Drop the texture on the floor if the pool is no longer tracking textures of the same size. + if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() || + static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) { + ALOGV("Deallocating texture from Planner's pool - display size changed (previous: (%dx%d), " + "current: (%dx%d))", + texture->getBuffer()->getWidth(), texture->getBuffer()->getHeight(), mSize.getWidth(), + mSize.getHeight()); + return; + } + + // Also ensure the pool does not grow beyond a maximum size. + if (mPool.size() == kMaxPoolSize) { + ALOGD("Deallocating texture from Planner's pool - max size [%" PRIu64 "] reached", + static_cast<uint64_t>(kMaxPoolSize)); + return; + } + + mPool.push_back({std::move(texture), fence}); +} + +std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() { + LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size"); + return std::make_shared< + renderengine::ExternalTexture>(sp<GraphicBuffer>:: + make(mSize.getWidth(), mSize.getHeight(), + HAL_PIXEL_FORMAT_RGBA_8888, 1, + GraphicBuffer::USAGE_HW_RENDER | + GraphicBuffer::USAGE_HW_COMPOSER | + GraphicBuffer::USAGE_HW_TEXTURE, + "Planner"), + mRenderEngine, + renderengine::ExternalTexture::Usage::READABLE | + renderengine::ExternalTexture::Usage::WRITEABLE); +} + +} // namespace android::compositionengine::impl::planner
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 52e042821a..c381081c26 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -143,6 +143,7 @@ struct OutputTest : public testing::Test { mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); mOutput->editState().displaySpace.bounds = kDefaultDisplaySize; + EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); } void injectOutputLayer(InjectedLayer& layer) { @@ -156,6 +157,7 @@ struct OutputTest : public testing::Test { static const Rect kDefaultDisplaySize; StrictMock<mock::CompositionEngine> mCompositionEngine; + StrictMock<renderengine::mock::RenderEngine> mRenderEngine; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>(); mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>(); std::shared_ptr<Output> mOutput = createOutput(mCompositionEngine); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index 8eeb0bfa16..b15e4f3814 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -40,6 +40,7 @@ using testing::SetArgPointee; using impl::planner::CachedSet; using impl::planner::LayerState; using impl::planner::LayerStateField; +using impl::planner::TexturePool; namespace { @@ -50,6 +51,7 @@ MATCHER_P(ClientCompositionTargetSettingsBlurSettingsEq, expectedBlurSetting, "" return expectedBlurSetting == arg.blurSetting; } +static const ui::Size kOutputSize = ui::Size(1, 1); class CachedSetTest : public testing::Test { public: @@ -76,9 +78,11 @@ protected: impl::OutputCompositionState mOutputState; android::renderengine::mock::RenderEngine mRenderEngine; + TexturePool mTexturePool = TexturePool(mRenderEngine); }; void CachedSetTest::SetUp() { + mTexturePool.setDisplaySize(kOutputSize); for (size_t i = 0; i < kNumLayers; i++) { auto testLayer = std::make_unique<TestLayer>(); auto pos = static_cast<int32_t>(i); @@ -319,7 +323,7 @@ TEST_F(CachedSetTest, render) { const std::vector<const renderengine::LayerSettings*>& layers, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, base::unique_fd&&, base::unique_fd*) -> size_t { - EXPECT_EQ(Rect(-1, -1, 9, 4), displaySettings.physicalDisplay); + EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay); EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip); EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation), displaySettings.orientation); @@ -333,10 +337,11 @@ TEST_F(CachedSetTest, render) { EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); - cachedSet.render(mRenderEngine, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState); expectReadyBuffer(cachedSet); EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); + EXPECT_EQ(mOutputState.framebufferSpace.content, cachedSet.getTextureBounds()); // Now check that appending a new cached set properly cleans up RenderEngine resources. CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); @@ -367,7 +372,7 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { const std::vector<const renderengine::LayerSettings*>& layers, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, base::unique_fd&&, base::unique_fd*) -> size_t { - EXPECT_EQ(Rect(1, 2, 9, 4), displaySettings.physicalDisplay); + EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay); EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip); EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation), displaySettings.orientation); @@ -381,7 +386,7 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); - cachedSet.render(mRenderEngine, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState); expectReadyBuffer(cachedSet); EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); @@ -554,7 +559,7 @@ TEST_F(CachedSetTest, addHolePunch) { }; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); - cachedSet.render(mRenderEngine, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState); } TEST_F(CachedSetTest, addHolePunch_noBuffer) { @@ -604,7 +609,7 @@ TEST_F(CachedSetTest, addHolePunch_noBuffer) { }; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); - cachedSet.render(mRenderEngine, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState); } TEST_F(CachedSetTest, append_removesHolePunch) { @@ -741,7 +746,7 @@ TEST_F(CachedSetTest, addBlur) { }; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); - cachedSet.render(mRenderEngine, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState); } } // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 8b03964ad2..e176c98018 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -46,13 +46,14 @@ namespace { class TestableFlattener : public Flattener { public: - TestableFlattener(bool enableHolePunch) : Flattener(enableHolePunch) {} + TestableFlattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch) + : Flattener(renderEngine, enableHolePunch) {} const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; } }; class FlattenerTest : public testing::Test { public: - FlattenerTest() : mFlattener(std::make_unique<TestableFlattener>(true)) {} + FlattenerTest() : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, true)) {} void SetUp() override; protected: @@ -60,7 +61,7 @@ protected: void initializeFlattener(const std::vector<const LayerState*>& layers); void expectAllLayersFlattened(const std::vector<const LayerState*>& layers); - // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first. + // mRenderEngine is held as a reference in mFlattener, so mFlattener must be destroyed first. renderengine::mock::RenderEngine mRenderEngine; std::unique_ptr<TestableFlattener> mFlattener; @@ -84,6 +85,7 @@ protected: }; void FlattenerTest::SetUp() { + mFlattener->setDisplaySize({1, 1}); for (size_t i = 0; i < kNumLayers; i++) { auto testLayer = std::make_unique<TestLayer>(); auto pos = static_cast<int32_t>(i); @@ -146,13 +148,13 @@ void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& la initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); // same geometry, update the internal layer stack initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); } void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) { @@ -162,7 +164,7 @@ void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState* initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); @@ -172,7 +174,7 @@ void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState* initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer; EXPECT_NE(nullptr, buffer); @@ -207,7 +209,7 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); } TEST_F(FlattenerTest, flattenLayers_basicFlatten) { @@ -253,7 +255,7 @@ TEST_F(FlattenerTest, flattenLayers_FlattenedLayersStayFlattenWhenNoUpdate) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -358,7 +360,7 @@ TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, overrideBuffer2); @@ -395,7 +397,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, overrideBuffer2); @@ -404,7 +406,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_NE(nullptr, overrideBuffer2); @@ -417,7 +419,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_NE(nullptr, overrideBuffer2); @@ -426,7 +428,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -468,7 +470,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, overrideBuffer2); @@ -482,7 +484,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); mOutputState.framebufferSpace.orientation = ui::ROTATION_90; - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -495,7 +497,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); mOutputState.framebufferSpace.orientation = ui::ROTATION_180; - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -510,7 +512,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -522,7 +524,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); mOutputState.framebufferSpace.orientation = ui::ROTATION_270; - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -561,7 +563,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { // This will render a CachedSet. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer1); @@ -574,7 +576,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -623,7 +625,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) { // This will render a CachedSet. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer1); @@ -636,7 +638,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -680,7 +682,7 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); @@ -690,7 +692,7 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); EXPECT_EQ(nullptr, overrideBuffer3); @@ -724,7 +726,7 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); @@ -735,7 +737,7 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); } @@ -776,7 +778,7 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); @@ -786,7 +788,7 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, blurOverrideBuffer); EXPECT_NE(nullptr, overrideBuffer3); @@ -823,7 +825,7 @@ TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiv initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); const auto& cachedSet = mFlattener->getNewCachedSetForTesting(); ASSERT_NE(std::nullopt, cachedSet); @@ -837,7 +839,7 @@ TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiv initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer2, overrideBuffer1); EXPECT_EQ(nullptr, blurOverrideBuffer); @@ -864,7 +866,7 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, overrideBuffer2); @@ -872,12 +874,12 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { // Simulate attempting to render prior to merging the new cached set with the layer stack. // Here we should not try to re-render. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); // We provide the override buffer now that it's rendered EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mRenderEngine, mOutputState); + mFlattener->renderCachedSets(mOutputState); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer2, overrideBuffer1); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp new file mode 100644 index 0000000000..b802e51234 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp @@ -0,0 +1,134 @@ +/* + * 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 "TexturePoolTest" + +#include <compositionengine/impl/planner/TexturePool.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <renderengine/mock/RenderEngine.h> + +namespace android::compositionengine::impl::planner { +namespace { + +const ui::Size kDisplaySize(1, 1); +const ui::Size kDisplaySizeTwo(2, 2); + +class TestableTexturePool : public TexturePool { +public: + TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {} + + size_t getMinPoolSize() const { return kMinPoolSize; } + size_t getMaxPoolSize() const { return kMaxPoolSize; } + size_t getPoolSize() const { return mPool.size(); } +}; + +struct TexturePoolTest : public testing::Test { + TexturePoolTest() { + 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()); + mTexturePool.setDisplaySize(kDisplaySize); + } + + ~TexturePoolTest() { + 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()); + } + + renderengine::mock::RenderEngine mRenderEngine; + TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine); +}; + +TEST_F(TexturePoolTest, preallocatesMinPool) { + EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); +} + +TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) { + for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) { + auto texture = mTexturePool.borrowTexture(); + } + + EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); +} + +TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) { + std::unordered_set<uint64_t> bufferIds; + std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures; + for (size_t i = 0; i < mTexturePool.getMaxPoolSize(); i++) { + textures.emplace_back(mTexturePool.borrowTexture()); + bufferIds.insert(textures.back()->get()->getBuffer()->getId()); + } + + EXPECT_EQ(mTexturePool.getMaxPoolSize(), bufferIds.size()); + + for (size_t i = 0; i < 3; i++) { + textures.pop_front(); + textures.emplace_back(mTexturePool.borrowTexture()); + bufferIds.insert(textures.back()->get()->getBuffer()->getId()); + } + + EXPECT_EQ(mTexturePool.getMaxPoolSize(), bufferIds.size()); +} + +TEST_F(TexturePoolTest, goesPastMaxSizeAndRebounds) { + std::unordered_set<uint64_t> bufferIds; + std::vector<std::shared_ptr<TexturePool::AutoTexture>> textures; + for (size_t i = 0; i < mTexturePool.getMaxPoolSize() + 2; i++) { + textures.emplace_back(mTexturePool.borrowTexture()); + bufferIds.insert(textures.back()->get()->getBuffer()->getId()); + } + + EXPECT_EQ(mTexturePool.getMaxPoolSize() + 2, bufferIds.size()); + + // Return the textures to the pool. + // Now when we cycle through the pool it's again bounded by max textures. + textures.clear(); + + std::unordered_set<uint64_t> newBufferIds; + for (size_t i = 0; i < 2 * mTexturePool.getMaxPoolSize(); i++) { + auto texture = mTexturePool.borrowTexture(); + newBufferIds.insert(texture->get()->getBuffer()->getId()); + } + + EXPECT_EQ(mTexturePool.getMaxPoolSize(), newBufferIds.size()); +} + +TEST_F(TexturePoolTest, reallocatesWhenDisplaySizeChanges) { + auto texture = mTexturePool.borrowTexture(); + + EXPECT_EQ(kDisplaySize.getWidth(), + static_cast<int32_t>(texture->get()->getBuffer()->getWidth())); + EXPECT_EQ(kDisplaySize.getHeight(), + static_cast<int32_t>(texture->get()->getBuffer()->getHeight())); + mTexturePool.setDisplaySize(kDisplaySizeTwo); + + EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); + texture.reset(); + // When the texture is returned to the pool, the pool now destroys it. + EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); + + texture = mTexturePool.borrowTexture(); + EXPECT_EQ(kDisplaySizeTwo.getWidth(), + static_cast<int32_t>(texture->get()->getBuffer()->getWidth())); + EXPECT_EQ(kDisplaySizeTwo.getHeight(), + static_cast<int32_t>(texture->get()->getBuffer()->getHeight())); +} + +} // namespace +} // namespace android::compositionengine::impl::planner |