SF: Predict HWC composition strategy
Asynchronously run ValidateDisplay to determine client composition
strategy. If we can predict that the composition strategy will not
change from frame to frame (for example by looking at whether
the geometry of the layers is changing), the hwc call is run
asynchronously with the gpu composition. If the result of the call is
incorrectly predicted, the gpu composition is rerun.
ValidateDisplay can take multiple milliseconds to complete and by
making it non-blocking in cases where we can predict the strategy
chosen by the hardware, we can jump start the client composition
and shave valuable milliseconds from the frame time.
This feature is disabled by default.
Test: perfetto traces
Bug: 220031739
Change-Id: Ib4dcc0e055d5272c89cfff853211edac73aa449b
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index aefc014..9302b7b 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -41,6 +41,10 @@
"libtonemap",
"libtrace_proto",
"libaidlcommonsupport",
+ "libprocessgroup",
+ "libcgrouprc",
+ "libjsoncpp",
+ "libcgrouprc_format",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -68,6 +72,7 @@
"src/DisplayColorProfile.cpp",
"src/DisplaySurface.cpp",
"src/DumpHelpers.cpp",
+ "src/HwcAsyncWorker.cpp",
"src/HwcBufferCache.cpp",
"src/LayerFECompositionState.cpp",
"src/Output.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index c553fce..ca86f4c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -72,6 +72,9 @@
virtual void resizeBuffers(const ui::Size&) = 0;
virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
+
+ // Returns true if the render surface supports client composition prediction.
+ virtual bool supportsCompositionStrategyPrediction() const;
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index d8644a4..1555102 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -35,6 +35,7 @@
#include <utils/Vector.h>
#include <ui/DisplayIdentification.h>
+#include "DisplayHardware/HWComposer.h"
namespace android {
@@ -54,6 +55,7 @@
namespace impl {
struct OutputCompositionState;
+struct GpuCompositionResult;
} // namespace impl
/**
@@ -262,6 +264,9 @@
// Latches the front-end layer state for each output layer
virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0;
+ // Enables predicting composition strategy to run client composition earlier
+ virtual void setPredictCompositionStrategy(bool) = 0;
+
protected:
virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
@@ -278,13 +283,22 @@
virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
virtual void beginFrame() = 0;
virtual void prepareFrame() = 0;
+
+ using GpuCompositionResult = compositionengine::impl::GpuCompositionResult;
+ // Runs prepare frame in another thread while running client composition using
+ // the previous frame's composition strategy.
+ virtual GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) = 0;
virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
- virtual void finishFrame(const CompositionRefreshArgs&) = 0;
+ virtual void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) = 0;
virtual std::optional<base::unique_fd> composeSurfaces(
- const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
+ const Region&, const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
virtual void postFramebuffer() = 0;
virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
- virtual void chooseCompositionStrategy() = 0;
+ virtual std::optional<android::HWComposer::DeviceRequestedChanges>
+ chooseCompositionStrategy() = 0;
+ virtual void applyCompositionStrategy(
+ const std::optional<android::HWComposer::DeviceRequestedChanges>& changes) = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
@@ -295,6 +309,7 @@
std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
virtual void setExpensiveRenderingExpected(bool enabled) = 0;
virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
+ virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index daee83b..9ee779c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -100,6 +100,9 @@
// Debugging - gets the page flip count for the RenderSurface
virtual std::uint32_t getPageFlipCount() const = 0;
+
+ // Returns true if the render surface supports client composition prediction.
+ virtual bool supportsCompositionStrategyPrediction() const = 0;
};
} // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index c7984bd..47f07b7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -22,6 +22,7 @@
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/GpuCompositionResult.h>
#include <compositionengine/impl/Output.h>
#include <ui/PixelFormat.h>
#include <ui/Size.h>
@@ -51,11 +52,14 @@
void setReleasedLayers(const CompositionRefreshArgs&) override;
void setColorTransform(const CompositionRefreshArgs&) override;
void setColorProfile(const ColorProfile&) override;
- void chooseCompositionStrategy() override;
+
+ using DeviceRequestedChanges = android::HWComposer::DeviceRequestedChanges;
+ std::optional<DeviceRequestedChanges> chooseCompositionStrategy() override;
+ void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override;
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
void setExpensiveRenderingExpected(bool) override;
- void finishFrame(const CompositionRefreshArgs&) override;
+ void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
// compositionengine::Display overrides
DisplayId getId() const override;
@@ -73,7 +77,6 @@
using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
- virtual bool anyLayersRequireClientComposition() const;
virtual bool allLayersRequireClientComposition() const;
virtual void applyChangedTypesToLayers(const ChangedTypes&);
virtual void applyDisplayRequests(const DisplayRequests&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h
new file mode 100644
index 0000000..ed1ddc1
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/GpuCompositionResult.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 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 <android-base/unique_fd.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::compositionengine::impl {
+
+struct GpuCompositionResult {
+ // True if composition strategy was predicted successfully.
+ bool succeeded = false;
+
+ // Composition ready fence.
+ base::unique_fd fence{};
+
+ // Buffer to be used for gpu composition. If gpu composition was not successful,
+ // then we want to reuse the buffer instead of dequeuing another buffer.
+ std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+
+ bool bufferAvailable() const { return buffer != nullptr; };
+};
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h
new file mode 100644
index 0000000..11c0054
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 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 <android-base/thread_annotations.h>
+#include <future>
+#include <optional>
+#include <thread>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine::impl {
+
+// HWC Validate call may take multiple milliseconds to complete and can account for
+// a signification amount of time in the display hotpath. This helper class allows
+// us to run the hwc validate function on a real time thread if we can predict what
+// the composition strategy will be and if composition includes client composition.
+// While the hwc validate runs, client composition is kicked off with the prediction.
+// When the worker returns with a value, the composition continues if the prediction
+// was successful otherwise the client composition is re-executed.
+//
+// Note: This does not alter the sequence between HWC and surfaceflinger.
+class HwcAsyncWorker final {
+public:
+ HwcAsyncWorker();
+ ~HwcAsyncWorker();
+ // Runs the provided function which calls hwc validate and returns the requested
+ // device changes as a future.
+ std::future<std::optional<android::HWComposer::DeviceRequestedChanges>> send(
+ std::function<std::optional<android::HWComposer::DeviceRequestedChanges>()>);
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCv GUARDED_BY(mMutex);
+ bool mDone GUARDED_BY(mMutex) = false;
+ bool mTaskRequested GUARDED_BY(mMutex) = false;
+ std::packaged_task<std::optional<android::HWComposer::DeviceRequestedChanges>()> mTask
+ GUARDED_BY(mMutex);
+ std::thread mThread;
+ void run();
+};
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index a7a8e97..0be5d01 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -17,9 +17,13 @@
#pragma once
#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
#include <compositionengine/impl/ClientCompositionRequestCache.h>
+#include <compositionengine/impl/GpuCompositionResult.h>
+#include <compositionengine/impl/HwcAsyncWorker.h>
#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/LayerSettings.h>
@@ -92,25 +96,38 @@
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
void beginFrame() override;
void prepareFrame() override;
+ GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) override;
void devOptRepaintFlash(const CompositionRefreshArgs&) override;
- void finishFrame(const CompositionRefreshArgs&) override;
- std::optional<base::unique_fd> composeSurfaces(
- const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
+ void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
+ std::optional<base::unique_fd> composeSurfaces(const Region&,
+ const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&) override;
void postFramebuffer() override;
void renderCachedSets(const CompositionRefreshArgs&) override;
void cacheClientCompositionRequests(uint32_t) override;
+ bool canPredictCompositionStrategy(const CompositionRefreshArgs&) override;
+ void setPredictCompositionStrategy(bool) override;
// Testing
const ReleasedLayers& getReleasedLayersForTest() const;
void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
bool plannerEnabled() const { return mPlanner != nullptr; }
+ virtual bool anyLayersRequireClientComposition() const;
+ virtual void updateProtectedContentState();
+ virtual bool dequeueRenderBuffer(base::unique_fd*,
+ std::shared_ptr<renderengine::ExternalTexture>*);
+ virtual std::future<std::optional<android::HWComposer::DeviceRequestedChanges>>
+ chooseCompositionStrategyAsync();
protected:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
std::optional<size_t> findCurrentOutputLayerForLayer(
const sp<compositionengine::LayerFE>&) const;
- void chooseCompositionStrategy() override;
+ using DeviceRequestedChanges = android::HWComposer::DeviceRequestedChanges;
+ std::optional<DeviceRequestedChanges> chooseCompositionStrategy() override;
+ void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
@@ -131,6 +148,7 @@
private:
void dirtyEntireOutput();
compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
+ void finishPrepareFrame();
ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
compositionengine::Output::ColorProfile pickColorProfile(
const compositionengine::CompositionRefreshArgs&) const;
@@ -144,6 +162,7 @@
OutputLayer* mLayerRequestingBackgroundBlur = nullptr;
std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
std::unique_ptr<planner::Planner> mPlanner;
+ std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 66dd825..92f22b6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -37,6 +37,8 @@
#include <ui/Region.h>
#include <ui/Transform.h>
+#include "DisplayHardware/HWComposer.h"
+
namespace android {
namespace compositionengine::impl {
@@ -114,6 +116,8 @@
// Current target dataspace
ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
+ std::optional<android::HWComposer::DeviceRequestedChanges> previousDeviceRequestedChanges{};
+
// The earliest time to send the present command to the HAL
std::chrono::steady_clock::time_point earliestPresentTime;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index a8a5380..e4cb113 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -63,6 +63,7 @@
void queueBuffer(base::unique_fd readyFence) override;
void onPresentDisplayCompleted() override;
void flip() override;
+ bool supportsCompositionStrategyPrediction() const override;
// Debugging
void dump(std::string& result) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 84afb59..1649843 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -41,6 +41,7 @@
MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
+ MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index b68b95d..27303a8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -22,6 +22,7 @@
#include <compositionengine/Output.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/GpuCompositionResult.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <gmock/gmock.h>
@@ -99,16 +100,22 @@
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
- MOCK_METHOD0(chooseCompositionStrategy, void());
+ MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+ MOCK_METHOD0(chooseCompositionStrategy,
+ std::optional<android::HWComposer::DeviceRequestedChanges>());
+ MOCK_METHOD1(applyCompositionStrategy,
+ void(const std::optional<android::HWComposer::DeviceRequestedChanges>&));
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD2(finishFrame,
+ void(const compositionengine::CompositionRefreshArgs&, GpuCompositionResult&&));
- MOCK_METHOD2(composeSurfaces,
+ MOCK_METHOD4(composeSurfaces,
std::optional<base::unique_fd>(
const Region&,
- const compositionengine::CompositionRefreshArgs& refreshArgs));
+ const compositionengine::CompositionRefreshArgs& refreshArgs,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
@@ -121,6 +128,8 @@
void(const Region&, std::vector<LayerFE::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
MOCK_METHOD1(cacheClientCompositionRequests, void(uint32_t));
+ MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
+ MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index fe858c2..e12aebb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -45,6 +45,7 @@
MOCK_METHOD0(flip, void());
MOCK_CONST_METHOD1(dump, void(std::string& result));
MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t());
+ MOCK_CONST_METHOD0(supportsCompositionStrategyPrediction, bool());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 6390025..ecf5ecf 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -221,12 +221,12 @@
setReleasedLayers(std::move(releasedLayers));
}
-void Display::chooseCompositionStrategy() {
+std::optional<android::HWComposer::DeviceRequestedChanges> Display::chooseCompositionStrategy() {
ATRACE_CALL();
ALOGV(__FUNCTION__);
if (mIsDisconnected) {
- return;
+ return {};
}
// Default to the base settings -- client composition only.
@@ -235,7 +235,7 @@
// If we don't have a HWC display, then we are done.
const auto halDisplayId = HalDisplayId::tryCast(mId);
if (!halDisplayId) {
- return;
+ return {};
}
// Get any composition changes requested by the HWC device, and apply them.
@@ -260,8 +260,13 @@
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
- return;
+ return {};
}
+
+ return changes;
+}
+
+void Display::applyCompositionStrategy(const std::optional<DeviceRequestedChanges>& changes) {
if (changes) {
applyChangedTypesToLayers(changes->changedTypes);
applyDisplayRequests(changes->displayRequests);
@@ -287,12 +292,6 @@
return hwc.hasCapability(Capability::SKIP_CLIENT_COLOR_TRANSFORM);
}
-bool Display::anyLayersRequireClientComposition() const {
- const auto layers = getOutputLayersOrderedByZ();
- return std::any_of(layers.begin(), layers.end(),
- [](const auto& layer) { return layer->requiresClientComposition(); });
-}
-
bool Display::allLayersRequireClientComposition() const {
const auto layers = getOutputLayersOrderedByZ();
return std::all_of(layers.begin(), layers.end(),
@@ -390,7 +389,8 @@
}
}
-void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs,
+ GpuCompositionResult&& result) {
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
@@ -400,7 +400,7 @@
return;
}
- impl::Output::finishFrame(refreshArgs);
+ impl::Output::finishFrame(refreshArgs, std::move(result));
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
index db6d4f2..28900af 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
@@ -20,4 +20,8 @@
DisplaySurface::~DisplaySurface() = default;
+bool DisplaySurface::supportsCompositionStrategyPrediction() const {
+ return true;
+}
+
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
new file mode 100644
index 0000000..497424a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include <compositionengine/impl/HwcAsyncWorker.h>
+#include <processgroup/sched_policy.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <system/thread_defs.h>
+
+#include <android-base/thread_annotations.h>
+#include <cutils/sched_policy.h>
+
+namespace android::compositionengine::impl {
+
+HwcAsyncWorker::HwcAsyncWorker() {
+ mThread = std::thread(&HwcAsyncWorker::run, this);
+ pthread_setname_np(mThread.native_handle(), "HwcAsyncWorker");
+}
+
+HwcAsyncWorker::~HwcAsyncWorker() {
+ {
+ std::scoped_lock lock(mMutex);
+ mDone = true;
+ mCv.notify_all();
+ }
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+std::future<std::optional<android::HWComposer::DeviceRequestedChanges>> HwcAsyncWorker::send(
+ std::function<std::optional<android::HWComposer::DeviceRequestedChanges>()> task) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ android::base::ScopedLockAssertion assumeLock(mMutex);
+ mTask = std::packaged_task<std::optional<android::HWComposer::DeviceRequestedChanges>()>(
+ [task = std::move(task)]() { return task(); });
+ mTaskRequested = true;
+ mCv.notify_one();
+ return mTask.get_future();
+}
+
+void HwcAsyncWorker::run() {
+ set_sched_policy(0, SP_FOREGROUND);
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ sched_setscheduler(gettid(), SCHED_FIFO, ¶m);
+
+ std::unique_lock<std::mutex> lock(mMutex);
+ android::base::ScopedLockAssertion assumeLock(mMutex);
+ while (!mDone) {
+ mCv.wait(lock);
+ if (mTaskRequested && mTask.valid()) {
+ mTask();
+ mTaskRequested = false;
+ }
+ }
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ff332eb..8d560d7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -22,6 +22,7 @@
#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/HwcAsyncWorker.h>
#include <compositionengine/impl/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
@@ -434,9 +435,17 @@
writeCompositionState(refreshArgs);
setColorTransform(refreshArgs);
beginFrame();
- prepareFrame();
+
+ GpuCompositionResult result;
+ const bool predictCompositionStrategy = canPredictCompositionStrategy(refreshArgs);
+ if (predictCompositionStrategy) {
+ result = prepareFrameAsync(refreshArgs);
+ } else {
+ prepareFrame();
+ }
+
devOptRepaintFlash(refreshArgs);
- finishFrame(refreshArgs);
+ finishFrame(refreshArgs, std::move(result));
postFramebuffer();
renderCachedSets(refreshArgs);
}
@@ -951,19 +960,62 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
- const auto& outputState = getState();
+ auto& outputState = editState();
if (!outputState.isEnabled) {
return;
}
- chooseCompositionStrategy();
+ auto changes = chooseCompositionStrategy();
+ outputState.previousDeviceRequestedChanges = changes;
+ if (changes) {
+ applyCompositionStrategy(changes);
+ }
+ finishPrepareFrame();
+}
- if (mPlanner) {
- mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+std::future<std::optional<android::HWComposer::DeviceRequestedChanges>>
+Output::chooseCompositionStrategyAsync() {
+ return mHwComposerAsyncWorker->send([&]() { return chooseCompositionStrategy(); });
+}
+
+GpuCompositionResult Output::prepareFrameAsync(const CompositionRefreshArgs& refreshArgs) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+ auto& state = editState();
+ const auto& previousChanges = state.previousDeviceRequestedChanges;
+ auto hwcResult = chooseCompositionStrategyAsync();
+ applyCompositionStrategy(previousChanges);
+ finishPrepareFrame();
+
+ base::unique_fd bufferFence;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
+ updateProtectedContentState();
+ const bool dequeueSucceeded = dequeueRenderBuffer(&bufferFence, &buffer);
+ GpuCompositionResult compositionResult;
+ if (dequeueSucceeded) {
+ std::optional<base::unique_fd> optFd =
+ composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+ if (optFd) {
+ compositionResult.fence = std::move(*optFd);
+ }
}
- mRenderSurface->prepareFrame(outputState.usesClientComposition,
- outputState.usesDeviceComposition);
+ auto changes = hwcResult.valid() ? hwcResult.get() : std::nullopt;
+ const bool predictionSucceeded = dequeueSucceeded && changes == previousChanges;
+ compositionResult.succeeded = predictionSucceeded;
+ if (!predictionSucceeded) {
+ ATRACE_NAME("CompositionStrategyPredictionMiss");
+ if (changes) {
+ applyCompositionStrategy(changes);
+ }
+ finishPrepareFrame();
+ // Track the dequeued buffer to reuse so we don't need to dequeue another one.
+ compositionResult.buffer = buffer;
+ } else {
+ ATRACE_NAME("CompositionStrategyPredictionHit");
+ }
+ state.previousDeviceRequestedChanges = std::move(changes);
+ return compositionResult;
}
void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& refreshArgs) {
@@ -973,7 +1025,11 @@
if (getState().isEnabled) {
if (const auto dirtyRegion = getDirtyRegion(); !dirtyRegion.isEmpty()) {
- static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs));
+ base::unique_fd bufferFence;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
+ updateProtectedContentState();
+ dequeueRenderBuffer(&bufferFence, &buffer);
+ static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs, buffer, bufferFence));
mRenderSurface->queueBuffer(base::unique_fd());
}
}
@@ -985,7 +1041,7 @@
prepareFrame();
}
-void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -993,9 +1049,25 @@
return;
}
- // Repaint the framebuffer (if needed), getting the optional fence for when
- // the composition completes.
- auto optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs);
+ std::optional<base::unique_fd> optReadyFence;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
+ base::unique_fd bufferFence;
+ if (result.succeeded) {
+ optReadyFence = std::move(result.fence);
+ } else {
+ if (result.bufferAvailable()) {
+ buffer = std::move(result.buffer);
+ bufferFence = std::move(result.fence);
+ } else {
+ updateProtectedContentState();
+ if (!dequeueRenderBuffer(&bufferFence, &buffer)) {
+ return;
+ }
+ }
+ // Repaint the framebuffer (if needed), getting the optional fence for when
+ // the composition completes.
+ optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+ }
if (!optReadyFence) {
return;
}
@@ -1004,16 +1076,8 @@
mRenderSurface->queueBuffer(std::move(*optReadyFence));
}
-std::optional<base::unique_fd> Output::composeSurfaces(
- const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs) {
- ATRACE_CALL();
- ALOGV(__FUNCTION__);
-
+void Output::updateProtectedContentState() {
const auto& outputState = getState();
- OutputCompositionState& outputCompositionState = editState();
- const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
- outputState.usesClientComposition};
-
auto& renderEngine = getCompositionEngine().getRenderEngine();
const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
@@ -1035,29 +1099,48 @@
} else if (!outputState.isSecure && renderEngine.isProtected()) {
renderEngine.useProtectedContext(false);
}
+}
- base::unique_fd fd;
-
- std::shared_ptr<renderengine::ExternalTexture> tex;
+bool Output::dequeueRenderBuffer(base::unique_fd* bufferFence,
+ std::shared_ptr<renderengine::ExternalTexture>* tex) {
+ const auto& outputState = getState();
// If we aren't doing client composition on this output, but do have a
// flipClientTarget request for this frame on this output, we still need to
// dequeue a buffer.
- if (hasClientComposition || outputState.flipClientTarget) {
- tex = mRenderSurface->dequeueBuffer(&fd);
- if (tex == nullptr) {
+ if (outputState.usesClientComposition || outputState.flipClientTarget) {
+ *tex = mRenderSurface->dequeueBuffer(bufferFence);
+ if (*tex == nullptr) {
ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
"client composition for this frame",
mName.c_str());
- return {};
+ return false;
}
}
+ return true;
+}
+std::optional<base::unique_fd> Output::composeSurfaces(
+ const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs,
+ std::shared_ptr<renderengine::ExternalTexture> tex, base::unique_fd& fd) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ const auto& outputState = getState();
+ const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
+ outputState.usesClientComposition};
if (!hasClientComposition) {
setExpensiveRenderingExpected(false);
return base::unique_fd();
}
+ if (tex == nullptr) {
+ ALOGW("Buffer not valid for display [%s], bailing out of "
+ "client composition for this frame",
+ mName.c_str());
+ return {};
+ }
+
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
@@ -1085,6 +1168,8 @@
outputState.usesDeviceComposition || getSkipColorTransform();
// Generate the client composition requests for the layers on this output.
+ auto& renderEngine = getCompositionEngine().getRenderEngine();
+ const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
std::vector<LayerFE*> clientCompositionLayersFE;
std::vector<LayerFE::LayerSettings> clientCompositionLayers =
generateClientCompositionRequests(supportsProtectedContent,
@@ -1092,16 +1177,19 @@
clientCompositionLayersFE);
appendRegionFlashRequests(debugRegion, clientCompositionLayers);
+ OutputCompositionState& outputCompositionState = editState();
// Check if the client composition requests were rendered into the provided graphic buffer. If
// so, we can reuse the buffer and avoid client composition.
if (mClientCompositionRequestCache) {
if (mClientCompositionRequestCache->exists(tex->getBuffer()->getId(),
clientCompositionDisplay,
clientCompositionLayers)) {
+ ATRACE_NAME("ClientCompositionCacheHit");
outputCompositionState.reusedClientComposition = true;
setExpensiveRenderingExpected(false);
return base::unique_fd();
}
+ ATRACE_NAME("ClientCompositionCacheMiss");
mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
clientCompositionLayers);
}
@@ -1349,12 +1437,13 @@
outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect());
}
-void Output::chooseCompositionStrategy() {
+std::optional<android::HWComposer::DeviceRequestedChanges> Output::chooseCompositionStrategy() {
// The base output implementation can only do client composition
auto& outputState = editState();
outputState.usesClientComposition = true;
outputState.usesDeviceComposition = false;
outputState.reusedClientComposition = false;
+ return {};
}
bool Output::getSkipColorTransform() const {
@@ -1369,5 +1458,63 @@
return result;
}
+void Output::setPredictCompositionStrategy(bool predict) {
+ if (predict) {
+ mHwComposerAsyncWorker = std::make_unique<HwcAsyncWorker>();
+ } else {
+ mHwComposerAsyncWorker.reset(nullptr);
+ }
+}
+
+bool Output::canPredictCompositionStrategy(const CompositionRefreshArgs& refreshArgs) {
+ if (!getState().isEnabled || !mHwComposerAsyncWorker) {
+ ALOGV("canPredictCompositionStrategy disabled");
+ return false;
+ }
+
+ if (!getState().previousDeviceRequestedChanges) {
+ ALOGV("canPredictCompositionStrategy previous changes not available");
+ return false;
+ }
+
+ if (!mRenderSurface->supportsCompositionStrategyPrediction()) {
+ ALOGV("canPredictCompositionStrategy surface does not support");
+ return false;
+ }
+
+ if (refreshArgs.devOptFlashDirtyRegionsDelay) {
+ ALOGV("canPredictCompositionStrategy devOptFlashDirtyRegionsDelay");
+ return false;
+ }
+
+ // If no layer uses clientComposition, then don't predict composition strategy
+ // because we have less work to do in parallel.
+ if (!anyLayersRequireClientComposition()) {
+ ALOGV("canPredictCompositionStrategy no layer uses clientComposition");
+ return false;
+ }
+
+ if (!refreshArgs.updatingOutputGeometryThisFrame) {
+ return true;
+ }
+
+ ALOGV("canPredictCompositionStrategy updatingOutputGeometryThisFrame");
+ return false;
+}
+
+bool Output::anyLayersRequireClientComposition() const {
+ const auto layers = getOutputLayersOrderedByZ();
+ return std::any_of(layers.begin(), layers.end(),
+ [](const auto& layer) { return layer->requiresClientComposition(); });
+}
+
+void Output::finishPrepareFrame() {
+ const auto& state = getState();
+ if (mPlanner) {
+ mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+ }
+ mRenderSurface->prepareFrame(state.usesClientComposition, state.usesDeviceComposition);
+}
+
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 12c2c8e..5a3af7b 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -289,5 +289,9 @@
return mTexture;
}
+bool RenderSurface::supportsCompositionStrategyPrediction() const {
+ return mDisplaySurface->supportsCompositionStrategyPrediction();
+}
+
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index cd03235..36b04d9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -30,7 +30,9 @@
#include <compositionengine/mock/OutputLayer.h>
#include <compositionengine/mock/RenderSurface.h>
#include <gtest/gtest.h>
+#include <renderengine/mock/FakeExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
+
#include <ui/Rect.h>
#include <ui/StaticDisplayInfo.h>
@@ -198,6 +200,22 @@
std::shared_ptr<Display> mDisplay =
createPartialMockDisplay<Display>(mCompositionEngine,
getDisplayCreationArgsForPhysicalDisplay());
+
+ android::HWComposer::DeviceRequestedChanges mDeviceRequestedChanges{
+ {{nullptr, Composition::CLIENT}},
+ hal::DisplayRequest::FLIP_CLIENT_TARGET,
+ {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
+ {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
+ -1.f,
+ };
+
+ void chooseCompositionStrategy(Display* display) {
+ std::optional<android::HWComposer::DeviceRequestedChanges> changes =
+ display->chooseCompositionStrategy();
+ if (changes) {
+ display->applyCompositionStrategy(changes);
+ }
+ }
};
struct FullDisplayImplTestCommon : public DisplayTestCommon {
@@ -214,6 +232,11 @@
std::unique_ptr<compositionengine::OutputLayer>(mLayer2.outputLayer));
mDisplay->injectOutputLayerForTest(
std::unique_ptr<compositionengine::OutputLayer>(mLayer3.outputLayer));
+ mResultWithBuffer.buffer = std::make_shared<
+ renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ 1ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0ULL /*usage*/);
}
Layer mLayer1;
@@ -222,6 +245,8 @@
StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
std::shared_ptr<Display> mDisplay =
createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
+ impl::GpuCompositionResult mResultWithBuffer;
+ impl::GpuCompositionResult mResultWithoutBuffer;
};
/*
@@ -554,7 +579,7 @@
createPartialMockDisplay<Display>(mCompositionEngine, args);
EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId()));
- gpuDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(gpuDisplay.get());
auto& state = gpuDisplay->getState();
EXPECT_TRUE(state.usesClientComposition);
@@ -567,11 +592,12 @@
getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _, _))
.WillOnce(Return(INVALID_OPERATION));
- mDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(mDisplay.get());
auto& state = mDisplay->getState();
EXPECT_TRUE(state.usesClientComposition);
EXPECT_FALSE(state.usesDeviceComposition);
+ EXPECT_FALSE(state.previousDeviceRequestedChanges.has_value());
}
TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) {
@@ -588,10 +614,16 @@
EXPECT_CALL(mHwComposer,
getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
- .WillOnce(Return(NO_ERROR));
+ .WillOnce(testing::DoAll(testing::SetArgPointee<5>(mDeviceRequestedChanges),
+ Return(NO_ERROR)));
+ EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
+ .Times(1);
+ EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
+ EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
+ .Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
- mDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(mDisplay.get());
auto& state = mDisplay->getState();
EXPECT_FALSE(state.usesClientComposition);
@@ -618,11 +650,17 @@
EXPECT_CALL(mHwComposer,
getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
- .WillOnce(Return(NO_ERROR));
+ .WillOnce(testing::DoAll(testing::SetArgPointee<5>(mDeviceRequestedChanges),
+ Return(NO_ERROR)));
+ EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
+ .Times(1);
+ EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
+ EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
+ .Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
mDisplay->setNextBrightness(kDisplayBrightness);
- mDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(mDisplay.get());
auto& state = mDisplay->getState();
EXPECT_FALSE(state.usesClientComposition);
@@ -631,14 +669,6 @@
}
TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
- android::HWComposer::DeviceRequestedChanges changes{
- {{nullptr, Composition::CLIENT}},
- hal::DisplayRequest::FLIP_CLIENT_TARGET,
- {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
- {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
- -1.f,
- };
-
// Since two calls are made to anyLayersRequireClientComposition with different return
// values, use a Sequence to control the matching so the values are returned in a known
// order.
@@ -652,13 +682,15 @@
EXPECT_CALL(mHwComposer,
getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<5>(changes), Return(NO_ERROR)));
- EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
- EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
- EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
+ .WillOnce(DoAll(SetArgPointee<5>(mDeviceRequestedChanges), Return(NO_ERROR)));
+ EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
+ .Times(1);
+ EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
+ EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
+ .Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
- mDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(mDisplay.get());
auto& state = mDisplay->getState();
EXPECT_FALSE(state.usesClientComposition);
@@ -922,7 +954,7 @@
mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
- mDisplay->finishFrame({});
+ mDisplay->finishFrame({}, std::move(mResultWithBuffer));
}
TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
@@ -940,7 +972,7 @@
gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
- gpuDisplay->finishFrame({});
+ gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
}
TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
@@ -957,7 +989,7 @@
gpuDisplay->editState().usesClientComposition = false;
gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
- gpuDisplay->finishFrame({});
+ gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c2521b2..4e875c8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -28,6 +28,7 @@
#include <gtest/gtest.h>
#include <renderengine/ExternalTexture.h>
#include <renderengine/impl/ExternalTexture.h>
+#include <renderengine/mock/FakeExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -989,7 +990,7 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD0(chooseCompositionStrategy, void());
+ MOCK_METHOD0(chooseCompositionStrategy, std::optional<DeviceRequestedChanges>());
};
OutputPrepareFrameTest() {
@@ -1037,6 +1038,133 @@
EXPECT_FALSE(mOutput->getState().usesDeviceComposition);
}
+struct OutputPrepareFrameAsyncTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD0(chooseCompositionStrategy, std::optional<DeviceRequestedChanges>());
+ MOCK_METHOD0(updateProtectedContentState, void());
+ MOCK_METHOD2(dequeueRenderBuffer,
+ bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
+ MOCK_METHOD0(chooseCompositionStrategyAsync,
+ std::future<std::optional<android::HWComposer::DeviceRequestedChanges>>());
+ MOCK_METHOD4(composeSurfaces,
+ std::optional<base::unique_fd>(
+ const Region&, const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ };
+
+ OutputPrepareFrameAsyncTest() {
+ 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;
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputPrepareFrameAsyncTest, delegatesToChooseCompositionStrategyAndRenderSurface) {
+ mOutput.editState().isEnabled = true;
+ mOutput.editState().usesClientComposition = false;
+ mOutput.editState().usesDeviceComposition = true;
+ mOutput.editState().previousDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+ p.set_value(mOutput.editState().previousDeviceRequestedChanges);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
+ EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+ EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ EXPECT_TRUE(result.succeeded);
+ EXPECT_FALSE(result.bufferAvailable());
+}
+
+TEST_F(OutputPrepareFrameAsyncTest, skipCompositionOnDequeueFailure) {
+ mOutput.editState().isEnabled = true;
+ mOutput.editState().usesClientComposition = false;
+ mOutput.editState().usesDeviceComposition = true;
+ mOutput.editState().previousDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+ p.set_value(mOutput.editState().previousDeviceRequestedChanges);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(2);
+ EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ EXPECT_FALSE(result.succeeded);
+ EXPECT_FALSE(result.bufferAvailable());
+}
+
+// Tests that in the event of hwc error when choosing composition strategy, we would fall back
+// client composition
+TEST_F(OutputPrepareFrameAsyncTest, chooseCompositionStrategyFailureCallsPrepareFrame) {
+ mOutput.editState().isEnabled = true;
+ mOutput.editState().usesClientComposition = false;
+ mOutput.editState().usesDeviceComposition = true;
+ mOutput.editState().previousDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+ p.set_value({});
+ std::shared_ptr<renderengine::ExternalTexture> tex =
+ std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ 2);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _))
+ .WillOnce(DoAll(SetArgPointee<1>(tex), Return(true)));
+ EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(2);
+ EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+ EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ EXPECT_FALSE(result.succeeded);
+ EXPECT_TRUE(result.bufferAvailable());
+}
+
+TEST_F(OutputPrepareFrameAsyncTest, predictionMiss) {
+ mOutput.editState().isEnabled = true;
+ mOutput.editState().usesClientComposition = false;
+ mOutput.editState().usesDeviceComposition = true;
+ mOutput.editState().previousDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ auto newDeviceRequestedChanges =
+ std::make_optional<android::HWComposer::DeviceRequestedChanges>({});
+ newDeviceRequestedChanges->clientTargetBrightness = 5.f;
+ std::promise<std::optional<android::HWComposer::DeviceRequestedChanges>> p;
+ p.set_value(newDeviceRequestedChanges);
+ std::shared_ptr<renderengine::ExternalTexture> tex =
+ std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ 2);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _))
+ .WillOnce(DoAll(SetArgPointee<1>(tex), Return(true)));
+ EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(2);
+ EXPECT_CALL(mOutput, chooseCompositionStrategyAsync()).WillOnce([&] { return p.get_future(); });
+ EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ EXPECT_FALSE(result.succeeded);
+ EXPECT_TRUE(result.bufferAvailable());
+}
+
/*
* Output::prepare()
*/
@@ -1795,10 +1923,14 @@
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
+ MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD2(finishFrame,
+ void(const compositionengine::CompositionRefreshArgs&,
+ GpuCompositionResult&&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
};
StrictMock<OutputPartialMock> mOutput;
@@ -1814,9 +1946,30 @@
EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
EXPECT_CALL(mOutput, beginFrame());
+ EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(false));
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
- EXPECT_CALL(mOutput, finishFrame(Ref(args)));
+ EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
+ EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
+
+ mOutput.present(args);
+}
+
+TEST_F(OutputPresentTest, predictingCompositionStrategyInvokesPrepareFrameAsync) {
+ CompositionRefreshArgs args;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, updateColorProfile(Ref(args)));
+ EXPECT_CALL(mOutput, updateCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, planComposition());
+ EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
+ EXPECT_CALL(mOutput, beginFrame());
+ EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(true));
+ EXPECT_CALL(mOutput, prepareFrameAsync(Ref(args)));
+ EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
+ EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
@@ -2714,11 +2867,15 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_METHOD(Region, getDirtyRegion, (), (const));
- MOCK_METHOD2(composeSurfaces,
+ MOCK_METHOD4(composeSurfaces,
std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&));
+ const Region&, const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD0(prepareFrame, void());
+ MOCK_METHOD0(updateProtectedContentState, void());
+ MOCK_METHOD2(dequeueRenderBuffer,
+ bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
};
OutputDevOptRepaintFlashTest() {
@@ -2775,7 +2932,9 @@
InSequence seq;
EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion));
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs)));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs), _, _));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, prepareFrame());
@@ -2791,10 +2950,14 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD2(composeSurfaces,
+ MOCK_METHOD4(composeSurfaces,
std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&));
+ const Region&, const compositionengine::CompositionRefreshArgs&,
+ std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(updateProtectedContentState, void());
+ MOCK_METHOD2(dequeueRenderBuffer,
+ bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
};
OutputFinishFrameTest() {
@@ -2812,27 +2975,63 @@
TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
mOutput.mState.isEnabled = false;
- mOutput.finishFrame(mRefreshArgs);
+ impl::GpuCompositionResult result;
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
}
TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
mOutput.mState.isEnabled = true;
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _));
- InSequence seq;
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _));
-
- mOutput.finishFrame(mRefreshArgs);
+ impl::GpuCompositionResult result;
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
}
TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
mOutput.mState.isEnabled = true;
InSequence seq;
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _))
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _))
.WillOnce(Return(ByMove(base::unique_fd())));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
- mOutput.finishFrame(mRefreshArgs);
+ impl::GpuCompositionResult result;
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
+}
+
+TEST_F(OutputFinishFrameTest, predictionSucceeded) {
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+
+ impl::GpuCompositionResult result;
+ result.succeeded = true;
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
+}
+
+TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) {
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+
+ impl::GpuCompositionResult result;
+ result.succeeded = false;
+ result.buffer =
+ std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ 2);
+
+ EXPECT_CALL(mOutput,
+ composeSurfaces(RegionEq(Region::INVALID_REGION), _, result.buffer,
+ Eq(ByRef(result.fence))))
+ .WillOnce(Return(ByMove(base::unique_fd())));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+ mOutput.finishFrame(mRefreshArgs, std::move(result));
}
/*
@@ -3079,8 +3278,15 @@
struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
auto execute() {
- getInstance()->mReadyFence =
- getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fence;
+ std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+ const bool success =
+ getInstance()->mOutput.dequeueRenderBuffer(&fence, &externalTexture);
+ if (success) {
+ getInstance()->mReadyFence =
+ getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs,
+ externalTexture, fence);
+ }
return nextState<FenceCheckState>();
}
};
@@ -3641,7 +3847,11 @@
EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
EXPECT_CALL(mRenderEngine, useProtectedContext(false));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
@@ -3649,7 +3859,11 @@
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
@@ -3661,7 +3875,11 @@
EXPECT_CALL(mRenderEngine, useProtectedContext(false));
EXPECT_CALL(*mRenderSurface, setProtected(false));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
@@ -3683,7 +3901,11 @@
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
@@ -3693,7 +3915,11 @@
EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
@@ -3704,7 +3930,11 @@
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
EXPECT_CALL(mRenderEngine, useProtectedContext(true));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
@@ -3715,7 +3945,11 @@
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
EXPECT_CALL(*mRenderSurface, setProtected(true));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
@@ -3726,7 +3960,11 @@
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
EXPECT_CALL(mRenderEngine, useProtectedContext(true));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
@@ -3755,7 +3993,11 @@
.WillOnce(Return(ByMove(
futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur
@@ -3790,7 +4032,12 @@
mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
- mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
+
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
@@ -3800,7 +4047,12 @@
mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
- mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
+
+ base::unique_fd fd;
+ std::shared_ptr<renderengine::ExternalTexture> tex;
+ mOutput.updateProtectedContentState();
+ mOutput.dequeueRenderBuffer(&fd, &tex);
+ mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
}
/*
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index eef0052..87119ee 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -91,6 +91,7 @@
static_cast<uint32_t>(SurfaceFlinger::maxFrameBufferAcquiredBuffers));
}
+ mCompositionDisplay->setPredictCompositionStrategy(mFlinger->mPredictCompositionStrategy);
mCompositionDisplay->createDisplayColorProfile(
compositionengine::DisplayColorProfileCreationArgsBuilder()
.setHasWideColorGamut(args.hasWideColorGamut)
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 5b2e265..a8d439b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -269,6 +269,14 @@
support) = 0;
};
+static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
+ const android::HWComposer::DeviceRequestedChanges& rhs) {
+ return lhs.changedTypes == rhs.changedTypes && lhs.displayRequests == rhs.displayRequests &&
+ lhs.layerRequests == rhs.layerRequests &&
+ lhs.clientTargetProperty == rhs.clientTargetProperty &&
+ lhs.clientTargetBrightness == rhs.clientTargetBrightness;
+}
+
namespace impl {
class HWComposer final : public android::HWComposer {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 307da41..e21095a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -89,6 +89,9 @@
virtual void dumpAsString(String8& result) const;
virtual void resizeBuffers(const ui::Size&) override;
virtual const sp<Fence>& getClientTargetAcquireFence() const override;
+ // Virtual display surface needs to prepare the frame based on composition type. Skip
+ // any client composition prediction.
+ virtual bool supportsCompositionStrategyPrediction() const override { return false; };
private:
enum Source : size_t {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5ccbccf..c8f0dfb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -480,6 +480,9 @@
property_get("debug.sf.disable_client_composition_cache", value, "0");
mDisableClientCompositionCache = atoi(value);
+ property_get("debug.sf.predict_hwc_composition_strategy", value, "0");
+ mPredictCompositionStrategy = atoi(value);
+
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
// comes online to attempt to read the property. The property is
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a7ff245..5829838 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -344,6 +344,11 @@
void disableExpensiveRendering();
FloatRect getMaxDisplayBounds();
+ // If set, composition engine tries to predict the composition strategy provided by HWC
+ // based on the previous frame. If the strategy can be predicted, gpu composition will
+ // run parallel to the hwc validateDisplay call and re-run if the predition is incorrect.
+ bool mPredictCompositionStrategy = false;
+
protected:
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();