Merge "SF: Predict HWC composition strategy" into tm-dev
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 516c3ef..11a9e19 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -42,6 +42,10 @@
"libtonemap",
"libtrace_proto",
"libaidlcommonsupport",
+ "libprocessgroup",
+ "libcgrouprc",
+ "libjsoncpp",
+ "libcgrouprc_format",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -69,6 +73,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..5846e67 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 bool chooseCompositionStrategy(
+ std::optional<android::HWComposer::DeviceRequestedChanges>*) = 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 8bb8e92..61a0e6a 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,16 @@
void setReleasedLayers(const CompositionRefreshArgs&) override;
void setColorTransform(const CompositionRefreshArgs&) override;
void setColorProfile(const ColorProfile&) override;
- void chooseCompositionStrategy() override;
+
+ void beginFrame() override;
+ using DeviceRequestedChanges = android::HWComposer::DeviceRequestedChanges;
+ bool chooseCompositionStrategy(
+ std::optional<android::HWComposer::DeviceRequestedChanges>*) 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;
@@ -72,7 +78,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..0c7da6c
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcAsyncWorker.h
@@ -0,0 +1,55 @@
+/*
+ * 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<bool> send(std::function<bool()>);
+
+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<bool()> 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..0feb9f7 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,42 @@
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<bool> chooseCompositionStrategyAsync(
+ std::optional<android::HWComposer::DeviceRequestedChanges>*);
+ virtual void resetCompositionStrategy();
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;
+ bool chooseCompositionStrategy(
+ std::optional<android::HWComposer::DeviceRequestedChanges>*) override {
+ return true;
+ };
+ void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
@@ -131,6 +152,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 +166,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 2438f80..20e5d71 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -38,6 +38,8 @@
#include <ui/Region.h>
#include <ui/Transform.h>
+#include "DisplayHardware/HWComposer.h"
+
namespace android {
namespace compositionengine::impl {
@@ -115,6 +117,10 @@
// Current target dataspace
ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
+ std::optional<android::HWComposer::DeviceRequestedChanges> previousDeviceRequestedChanges{};
+
+ bool previousDeviceRequestedSuccess = false;
+
// 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 d90cc90..72e6f3b 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..fa86076 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,24 @@
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
- MOCK_METHOD0(chooseCompositionStrategy, void());
+ MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+ MOCK_METHOD1(chooseCompositionStrategy,
+ bool(std::optional<android::HWComposer::DeviceRequestedChanges>*));
+ MOCK_METHOD1(chooseCompositionStrategyAsync,
+ std::future<bool>(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 +130,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 54daf38..84c22cf 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -207,16 +207,8 @@
setReleasedLayers(std::move(releasedLayers));
}
-void Display::chooseCompositionStrategy() {
- ATRACE_CALL();
- ALOGV(__FUNCTION__);
-
- if (mIsDisconnected) {
- return;
- }
-
- // Default to the base settings -- client composition only.
- Output::chooseCompositionStrategy();
+void Display::beginFrame() {
+ Output::beginFrame();
// If we don't have a HWC display, then we are done.
const auto halDisplayId = HalDisplayId::tryCast(mId);
@@ -224,8 +216,6 @@
return;
}
- // Get any composition changes requested by the HWC device, and apply them.
- std::optional<android::HWComposer::DeviceRequestedChanges> changes;
auto& hwc = getCompositionEngine().getHwComposer();
if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId);
physicalDisplayId && getState().displayBrightness) {
@@ -237,17 +227,43 @@
ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
getName().c_str(), result, strerror(-result));
}
+ // Clear out the display brightness now that it's been communicated to composer.
+ editState().displayBrightness.reset();
+}
+bool Display::chooseCompositionStrategy(
+ std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ if (mIsDisconnected) {
+ return false;
+ }
+
+ // If we don't have a HWC display, then we are done.
+ const auto halDisplayId = HalDisplayId::tryCast(mId);
+ if (!halDisplayId) {
+ return false;
+ }
+
+ // Get any composition changes requested by the HWC device, and apply them.
+ std::optional<android::HWComposer::DeviceRequestedChanges> changes;
+ auto& hwc = getCompositionEngine().getHwComposer();
if (status_t result =
hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
getState().earliestPresentTime,
getState().previousPresentFence,
- getState().expectedPresentTime, &changes);
+ getState().expectedPresentTime, outChanges);
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
- return;
+ return false;
}
+
+ return true;
+}
+
+void Display::applyCompositionStrategy(const std::optional<DeviceRequestedChanges>& changes) {
if (changes) {
applyChangedTypesToLayers(changes->changedTypes);
applyDisplayRequests(changes->displayRequests);
@@ -259,8 +275,6 @@
auto& state = editState();
state.usesClientComposition = anyLayersRequireClientComposition();
state.usesDeviceComposition = !allLayersRequireClientComposition();
- // Clear out the display brightness now that it's been communicated to composer.
- state.displayBrightness.reset();
}
bool Display::getSkipColorTransform() const {
@@ -273,12 +287,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(),
@@ -379,7 +387,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
@@ -389,7 +398,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..6086f0b
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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<bool> HwcAsyncWorker::send(std::function<bool()> task) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ android::base::ScopedLockAssertion assumeLock(mMutex);
+ mTask = std::packaged_task<bool()>([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 25155b9..6595ed3 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);
}
@@ -953,19 +962,72 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
- const auto& outputState = getState();
+ auto& outputState = editState();
if (!outputState.isEnabled) {
return;
}
- chooseCompositionStrategy();
+ std::optional<android::HWComposer::DeviceRequestedChanges> changes;
+ bool success = chooseCompositionStrategy(&changes);
+ resetCompositionStrategy();
+ outputState.previousDeviceRequestedChanges = changes;
+ outputState.previousDeviceRequestedSuccess = success;
+ if (success) {
+ applyCompositionStrategy(changes);
+ }
+ finishPrepareFrame();
+}
- if (mPlanner) {
- mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+std::future<bool> Output::chooseCompositionStrategyAsync(
+ std::optional<android::HWComposer::DeviceRequestedChanges>* changes) {
+ return mHwComposerAsyncWorker->send(
+ [&, changes]() { return chooseCompositionStrategy(changes); });
+}
+
+GpuCompositionResult Output::prepareFrameAsync(const CompositionRefreshArgs& refreshArgs) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+ auto& state = editState();
+ const auto& previousChanges = state.previousDeviceRequestedChanges;
+ std::optional<android::HWComposer::DeviceRequestedChanges> changes;
+ resetCompositionStrategy();
+ auto hwcResult = chooseCompositionStrategyAsync(&changes);
+ if (state.previousDeviceRequestedSuccess) {
+ 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 chooseCompositionSuccess = hwcResult.get();
+ const bool predictionSucceeded = dequeueSucceeded && changes == previousChanges;
+ compositionResult.succeeded = predictionSucceeded;
+ if (!predictionSucceeded) {
+ ATRACE_NAME("CompositionStrategyPredictionMiss");
+ resetCompositionStrategy();
+ if (chooseCompositionSuccess) {
+ 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);
+ state.previousDeviceRequestedSuccess = chooseCompositionSuccess;
+ return compositionResult;
}
void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& refreshArgs) {
@@ -975,7 +1037,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());
}
}
@@ -987,7 +1053,7 @@
prepareFrame();
}
-void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -995,9 +1061,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;
}
@@ -1006,16 +1088,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();
@@ -1037,29 +1111,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;
@@ -1088,6 +1181,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,
@@ -1095,16 +1190,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);
}
@@ -1356,7 +1454,7 @@
outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect());
}
-void Output::chooseCompositionStrategy() {
+void Output::resetCompositionStrategy() {
// The base output implementation can only do client composition
auto& outputState = editState();
outputState.usesClientComposition = true;
@@ -1376,5 +1474,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 d2c945c..9a61667 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>
@@ -43,6 +45,7 @@
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::Composition;
+using aidl::android::hardware::graphics::composer3::DimmingStage;
namespace android::compositionengine {
namespace {
@@ -113,8 +116,9 @@
return mCompositionEngine;
};
+ size_t getOutputLayerCount() const override { return 1u; }
+
// Mock implementation overrides
- MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
compositionengine::OutputLayer*(size_t));
MOCK_METHOD2(ensureOutputLayer,
@@ -197,6 +201,26 @@
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}},
+ {DEFAULT_DISPLAY_ID.value,
+ {aidl::android::hardware::graphics::common::PixelFormat::RGBA_8888,
+ aidl::android::hardware::graphics::common::Dataspace::UNKNOWN},
+ -1.f,
+ DimmingStage::NONE},
+ };
+
+ void chooseCompositionStrategy(Display* display) {
+ std::optional<android::HWComposer::DeviceRequestedChanges> changes;
+ bool success = display->chooseCompositionStrategy(&changes);
+ display->resetCompositionStrategy();
+ if (success) {
+ display->applyCompositionStrategy(changes);
+ }
+ }
};
struct FullDisplayImplTestCommon : public DisplayTestCommon {
@@ -213,6 +237,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;
@@ -221,6 +250,8 @@
StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
std::shared_ptr<Display> mDisplay =
createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
+ impl::GpuCompositionResult mResultWithBuffer;
+ impl::GpuCompositionResult mResultWithoutBuffer;
};
/*
@@ -553,7 +584,7 @@
createPartialMockDisplay<Display>(mCompositionEngine, args);
EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId()));
- gpuDisplay->chooseCompositionStrategy();
+ chooseCompositionStrategy(gpuDisplay.get());
auto& state = gpuDisplay->getState();
EXPECT_TRUE(state.usesClientComposition);
@@ -566,11 +597,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) {
@@ -587,10 +619,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);
@@ -602,45 +640,23 @@
// values, use a Sequence to control the matching so the values are returned in a known
// order.
constexpr float kDisplayBrightness = 0.5f;
- Sequence s;
- EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
- .InSequence(s)
- .WillOnce(Return(true));
- EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
- .InSequence(s)
- .WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
setDisplayBrightness(DEFAULT_DISPLAY_ID, kDisplayBrightness,
Hwc2::Composer::DisplayBrightnessOptions{.applyImmediately =
false}))
.WillOnce(Return(ByMove(ftl::yield<status_t>(NO_ERROR))));
- EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
- .WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
-
mDisplay->setNextBrightness(kDisplayBrightness);
- mDisplay->chooseCompositionStrategy();
+ mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+ EXPECT_CALL(*renderSurface, beginFrame(_)).Times(1);
+ mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+ mDisplay->beginFrame();
auto& state = mDisplay->getState();
- EXPECT_FALSE(state.usesClientComposition);
- EXPECT_TRUE(state.usesDeviceComposition);
EXPECT_FALSE(state.displayBrightness.has_value());
}
TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
- android::HWComposer::DeviceRequestedChanges changes{
- {{nullptr, Composition::CLIENT}},
- hal::DisplayRequest::FLIP_CLIENT_TARGET,
- {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
- {.clientTargetProperty =
- {aidl::android::hardware::graphics::common::PixelFormat::RGBA_8888,
- aidl::android::hardware::graphics::common::Dataspace::UNKNOWN},
- .brightness = -1.f,
- .dimmingStage = aidl::android::hardware::graphics::composer3::DimmingStage::NONE},
- };
-
// 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.
@@ -654,13 +670,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);
@@ -938,7 +956,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) {
@@ -956,7 +974,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) {
@@ -973,7 +991,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 66f3753..49be45e 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,9 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD0(chooseCompositionStrategy, void());
+ MOCK_METHOD1(chooseCompositionStrategy,
+ bool(std::optional<android::HWComposer::DeviceRequestedChanges>*));
+ MOCK_METHOD0(resetCompositionStrategy, void());
};
OutputPrepareFrameTest() {
@@ -1015,7 +1018,8 @@
mOutput.editState().usesClientComposition = false;
mOutput.editState().usesDeviceComposition = true;
- EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
+ EXPECT_CALL(mOutput, chooseCompositionStrategy(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput, resetCompositionStrategy()).Times(1);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
@@ -1037,6 +1041,147 @@
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_METHOD1(chooseCompositionStrategy,
+ bool(std::optional<android::HWComposer::DeviceRequestedChanges>*));
+ MOCK_METHOD0(updateProtectedContentState, void());
+ MOCK_METHOD2(dequeueRenderBuffer,
+ bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
+ MOCK_METHOD1(
+ chooseCompositionStrategyAsync,
+ std::future<bool>(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&));
+ MOCK_METHOD0(resetCompositionStrategy, void());
+ };
+
+ 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<bool> p;
+ p.set_value(true);
+
+ EXPECT_CALL(mOutput, resetCompositionStrategy()).Times(1);
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)).Times(1);
+ EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_))
+ .WillOnce(DoAll(SetArgPointee<0>(mOutput.editState().previousDeviceRequestedChanges),
+ Return(ByMove(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<bool> p;
+ p.set_value(true);
+
+ EXPECT_CALL(mOutput, resetCompositionStrategy()).Times(2);
+ 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(DoAll(SetArgPointee<0>(mOutput.editState().previousDeviceRequestedChanges),
+ Return(ByMove(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<bool> p;
+ p.set_value(false);
+ std::shared_ptr<renderengine::ExternalTexture> tex =
+ std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ 2);
+ EXPECT_CALL(mOutput, resetCompositionStrategy()).Times(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->displayRequests = static_cast<hal::DisplayRequest>(0);
+ std::promise<bool> p;
+ p.set_value(false);
+ std::shared_ptr<renderengine::ExternalTexture> tex =
+ std::make_shared<renderengine::mock::FakeExternalTexture>(1, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ 2);
+
+ EXPECT_CALL(mOutput, resetCompositionStrategy()).Times(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()
*/
@@ -1820,10 +1965,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;
@@ -1839,9 +1988,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)));
@@ -2739,11 +2909,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() {
@@ -2800,7 +2974,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());
@@ -2816,10 +2992,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() {
@@ -2837,27 +3017,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));
}
/*
@@ -3104,8 +3320,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>();
}
};
@@ -3728,7 +3951,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) {
@@ -3736,7 +3963,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) {
@@ -3748,7 +3979,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) {
@@ -3770,7 +4005,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) {
@@ -3780,7 +4019,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) {
@@ -3791,7 +4034,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) {
@@ -3802,7 +4049,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) {
@@ -3813,7 +4064,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 {
@@ -3843,7 +4098,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
@@ -3878,7 +4137,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) {
@@ -3888,7 +4152,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 9116fd3..3651c8b 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 389c3be..5e2ab78 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -280,6 +280,13 @@
virtual Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const = 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;
+}
+
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 ee6675c..b83b54a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -411,6 +411,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 4a6c0b8..ea2e71a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -343,6 +343,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();