aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Stratiienko <r.stratiienko@gmail.com>2023-01-17 21:22:29 +0200
committerRoman Stratiienko <r.stratiienko@gmail.com>2023-01-18 19:25:08 +0200
commit22fe9617dab9c0568ce625143b1d064c95df8fa2 (patch)
tree7c452c3b576fccf3db96538ae4c36b14cc2baa67
parent0da91bf5bb72323a0f0d1928a1fc36a01c1b7941 (diff)
downloaddrm_hwcomposer-22fe9617dab9c0568ce625143b1d064c95df8fa2.tar.gz
drm_hwcomposer: Make flattening thread-based instead of vsync-based
Using vsync means consume some CPU time every frame for IRQ -> Kernel_Thread -> UserSpace_listener thread transitions. Framework tries to reduce vsync usage to the minimum, by using timelines instead. New flattening controller thread wakes-up only once per second. This commit also removes flattening info from dumpsys. Practice shows that it is almost useless. Instead debugging can be done using ALOGV dumps. Signed-off-by: Roman Stratiienko <r.stratiienko@gmail.com>
-rw-r--r--Android.bp1
-rw-r--r--backend/Backend.cpp43
-rw-r--r--compositor/FlatteningController.cpp98
-rw-r--r--compositor/FlatteningController.h71
-rw-r--r--hwc2_device/HwcDisplay.cpp70
-rw-r--r--hwc2_device/HwcDisplay.h21
-rw-r--r--meson.build1
7 files changed, 215 insertions, 90 deletions
diff --git a/Android.bp b/Android.bp
index 1e78729..0e313ea 100644
--- a/Android.bp
+++ b/Android.bp
@@ -59,6 +59,7 @@ filegroup {
"bufferinfo/BufferInfoMapperMetadata.cpp",
"compositor/DrmKmsPlan.cpp",
+ "compositor/FlatteningController.cpp",
"drm/DrmAtomicStateManager.cpp",
"drm/DrmConnector.cpp",
diff --git a/backend/Backend.cpp b/backend/Backend.cpp
index f3156ed..3ca6e92 100644
--- a/backend/Backend.cpp
+++ b/backend/Backend.cpp
@@ -33,27 +33,36 @@ HWC2::Error Backend::ValidateDisplay(HwcDisplay *display, uint32_t *num_types,
int client_start = -1;
size_t client_size = 0;
- if (display->ProcessClientFlatteningState(layers.size() <= 1)) {
- display->total_stats().frames_flattened_++;
- client_start = 0;
- client_size = layers.size();
- MarkValidated(layers, client_start, client_size);
- } else {
- std::tie(client_start, client_size) = GetClientLayers(display, layers);
+ auto flatcon = display->GetFlatCon();
+ if (flatcon) {
+ bool should_flatten = false;
+ if (layers.size() <= 1)
+ flatcon->Disable();
+ else
+ should_flatten = flatcon->NewFrame();
+
+ if (should_flatten) {
+ display->total_stats().frames_flattened_++;
+ MarkValidated(layers, 0, layers.size());
+ *num_types = layers.size();
+ return HWC2::Error::HasChanges;
+ }
+ }
- MarkValidated(layers, client_start, client_size);
+ std::tie(client_start, client_size) = GetClientLayers(display, layers);
- auto testing_needed = client_start != 0 || client_size != layers.size();
+ MarkValidated(layers, client_start, client_size);
- AtomicCommitArgs a_args = {.test_only = true};
+ auto testing_needed = client_start != 0 || client_size != layers.size();
- if (testing_needed &&
- display->CreateComposition(a_args) != HWC2::Error::None) {
- ++display->total_stats().failed_kms_validate_;
- client_start = 0;
- client_size = layers.size();
- MarkValidated(layers, 0, client_size);
- }
+ AtomicCommitArgs a_args = {.test_only = true};
+
+ if (testing_needed &&
+ display->CreateComposition(a_args) != HWC2::Error::None) {
+ ++display->total_stats().failed_kms_validate_;
+ client_start = 0;
+ client_size = layers.size();
+ MarkValidated(layers, 0, client_size);
}
*num_types = client_size;
diff --git a/compositor/FlatteningController.cpp b/compositor/FlatteningController.cpp
new file mode 100644
index 0000000..257f8a0
--- /dev/null
+++ b/compositor/FlatteningController.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+/*
+ * Usually, display controllers do not use intermediate buffer for composition
+ * results. Instead, they scan-out directly from the input buffers, composing
+ * the planes on the fly every VSYNC.
+ *
+ * Flattening is a technique that reduces memory bandwidth and power consumption
+ * by converting non-updating multi-plane composition into a single-plane.
+ * Additionally, flattening also makes more shared planes available for use by
+ * other CRTCs.
+ *
+ * If the client is not updating layers for 1 second, FlatCon triggers a
+ * callback to refresh the screen. The compositor should mark all layers to be
+ * composed by the client into a single framebuffer using GPU.
+ */
+
+#define LOG_TAG "hwc-flatcon"
+
+#include "FlatteningController.h"
+
+#include "utils/log.h"
+
+namespace android {
+
+auto FlatteningController::CreateInstance(FlatConCallbacks &cbks)
+ -> std::shared_ptr<FlatteningController> {
+ auto fc = std::shared_ptr<FlatteningController>(new FlatteningController());
+
+ fc->cbks_ = cbks;
+
+ std::thread(&FlatteningController::ThreadFn, fc).detach();
+
+ return fc;
+}
+
+/* Compositor should call this every frame */
+bool FlatteningController::NewFrame() {
+ bool wake_it = false;
+ auto lock = std::lock_guard<std::mutex>(mutex_);
+
+ if (flatten_next_frame_) {
+ flatten_next_frame_ = false;
+ return true;
+ }
+
+ sleep_until_ = std::chrono::system_clock::now() + kTimeout;
+ if (disabled_) {
+ wake_it = true;
+ disabled_ = false;
+ }
+
+ if (wake_it)
+ cv_.notify_all();
+
+ return false;
+}
+
+void FlatteningController::ThreadFn(
+ const std::shared_ptr<FlatteningController> &fc) {
+ for (;;) {
+ std::unique_lock<std::mutex> lock(fc->mutex_);
+ if (fc.use_count() == 1 || !fc->cbks_.trigger)
+ break;
+
+ if (fc->sleep_until_ <= std::chrono::system_clock::now() &&
+ !fc->disabled_) {
+ fc->disabled_ = true;
+ fc->flatten_next_frame_ = true;
+ ALOGV("Timeout. Sending an event to compositor");
+ fc->cbks_.trigger();
+ }
+
+ if (fc->disabled_) {
+ ALOGV("Wait");
+ fc->cv_.wait(lock);
+ } else {
+ ALOGV("Wait_until");
+ fc->cv_.wait_until(lock, fc->sleep_until_);
+ }
+ }
+}
+
+} // namespace android
diff --git a/compositor/FlatteningController.h b/compositor/FlatteningController.h
new file mode 100644
index 0000000..2be6ce4
--- /dev/null
+++ b/compositor/FlatteningController.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 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 <chrono>
+#include <condition_variable>
+#include <functional>
+#include <thread>
+
+namespace android {
+
+// NOLINTNEXTLINE(misc-unused-using-decls): False positive
+using std::chrono_literals::operator""s;
+
+struct FlatConCallbacks {
+ std::function<void()> trigger;
+};
+
+class FlatteningController {
+ public:
+ static auto CreateInstance(FlatConCallbacks &cbks)
+ -> std::shared_ptr<FlatteningController>;
+
+ void Disable() {
+ auto lock = std::lock_guard<std::mutex>(mutex_);
+ flatten_next_frame_ = false;
+ disabled_ = true;
+ }
+
+ /* Compositor should call this every frame */
+ bool NewFrame();
+
+ auto ShouldFlatten() const {
+ return flatten_next_frame_;
+ }
+
+ void StopThread() {
+ auto lock = std::lock_guard<std::mutex>(mutex_);
+ cbks_ = {};
+ cv_.notify_all();
+ }
+
+ static constexpr auto kTimeout = 1s;
+
+ private:
+ FlatteningController() = default;
+ static void ThreadFn(const std::shared_ptr<FlatteningController> &fc);
+
+ bool flatten_next_frame_{};
+ bool disabled_{};
+ decltype(std::chrono::system_clock::now()) sleep_until_{};
+ std::mutex mutex_;
+ std::condition_variable cv_;
+ FlatConCallbacks cbks_;
+};
+
+} // namespace android
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index cdcf518..13da016 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -50,32 +50,12 @@ std::string HwcDisplay::DumpDelta(HwcDisplay::Stats delta) {
}
std::string HwcDisplay::Dump() {
- std::string flattening_state_str;
- switch (flattenning_state_) {
- case ClientFlattenningState::Disabled:
- flattening_state_str = "Disabled";
- break;
- case ClientFlattenningState::NotRequired:
- flattening_state_str = "Not needed";
- break;
- case ClientFlattenningState::Flattened:
- flattening_state_str = "Active";
- break;
- case ClientFlattenningState::ClientRefreshRequested:
- flattening_state_str = "Refresh requested";
- break;
- default:
- flattening_state_str = std::to_string(flattenning_state_) +
- " VSync remains";
- }
-
auto connector_name = IsInHeadlessMode()
? std::string("NULL-DISPLAY")
: GetPipe().connector->Get()->GetName();
std::stringstream ss;
ss << "- Display on: " << connector_name << "\n"
- << " Flattening state: " << flattening_state_str << "\n"
<< "Statistics since system boot:\n"
<< DumpDelta(total_stats_) << "\n\n"
<< "Statistics since last dumpsys request:\n"
@@ -140,6 +120,10 @@ void HwcDisplay::Deinit() {
current_plan_.reset();
backend_.reset();
+ if (flatcon_) {
+ flatcon_->StopThread();
+ flatcon_.reset();
+ }
}
if (vsync_worker_) {
@@ -162,14 +146,10 @@ HWC2::Error HwcDisplay::Init() {
GetDisplayVsyncPeriod(&period_ns);
hwc2_->SendVsyncEventToClient(handle_, timestamp, period_ns);
}
- if (vsync_flattening_en_) {
- ProcessFlatenningVsyncInternal();
- }
if (vsync_tracking_en_) {
last_vsync_ts_ = timestamp;
}
- if (!vsync_event_en_ && !vsync_flattening_en_ &&
- !vsync_tracking_en_) {
+ if (!vsync_event_en_ && !vsync_tracking_en_) {
vsync_worker_->VSyncControl(false);
}
},
@@ -192,6 +172,13 @@ HWC2::Error HwcDisplay::Init() {
ALOGE("Failed to set backend for d=%d %d\n", int(handle_), ret);
return HWC2::Error::BadDisplay;
}
+ auto flatcbk = (struct FlatConCallbacks){.trigger = [this]() {
+ if (hwc2_->refresh_callback_.first != nullptr &&
+ hwc2_->refresh_callback_.second != nullptr)
+ hwc2_->refresh_callback_.first(hwc2_->refresh_callback_.second,
+ handle_);
+ }};
+ flatcon_ = FlatteningController::CreateInstance(flatcbk);
}
client_layer_.SetLayerBlendMode(HWC2_BLEND_MODE_PREMULTIPLIED);
@@ -1022,37 +1009,4 @@ void HwcDisplay::set_backend(std::unique_ptr<Backend> backend) {
backend_ = std::move(backend);
}
-/* returns true if composition should be sent to client */
-bool HwcDisplay::ProcessClientFlatteningState(bool skip) {
- const int flattenning_state = flattenning_state_;
- if (flattenning_state == ClientFlattenningState::Disabled) {
- return false;
- }
-
- if (skip) {
- flattenning_state_ = ClientFlattenningState::NotRequired;
- return false;
- }
-
- if (flattenning_state == ClientFlattenningState::ClientRefreshRequested) {
- flattenning_state_ = ClientFlattenningState::Flattened;
- return true;
- }
-
- vsync_flattening_en_ = true;
- vsync_worker_->VSyncControl(true);
- flattenning_state_ = ClientFlattenningState::VsyncCountdownMax;
- return false;
-}
-
-void HwcDisplay::ProcessFlatenningVsyncInternal() {
- if (flattenning_state_ > ClientFlattenningState::ClientRefreshRequested &&
- --flattenning_state_ == ClientFlattenningState::ClientRefreshRequested &&
- hwc2_->refresh_callback_.first != nullptr &&
- hwc2_->refresh_callback_.second != nullptr) {
- hwc2_->refresh_callback_.first(hwc2_->refresh_callback_.second, handle_);
- vsync_flattening_en_ = false;
- }
-}
-
} // namespace android
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index 653aa4a..bf95c3e 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -23,6 +23,7 @@
#include <sstream>
#include "HwcDisplayConfigs.h"
+#include "compositor/FlatteningController.h"
#include "compositor/LayerData.h"
#include "drm/DrmAtomicStateManager.h"
#include "drm/ResourceManager.h"
@@ -165,10 +166,6 @@ class HwcDisplay {
return total_stats_;
}
- /* returns true if composition should be sent to client */
- bool ProcessClientFlatteningState(bool skip);
- void ProcessFlatenningVsyncInternal();
-
/* Headless mode required to keep SurfaceFlinger alive when all display are
* disconnected, Without headless mode Android will continuously crash.
* Only single internal (primary) display is required to be in HEADLESS mode
@@ -181,17 +178,11 @@ class HwcDisplay {
void Deinit();
- private:
- enum ClientFlattenningState : int32_t {
- Disabled = -3,
- NotRequired = -2,
- Flattened = -1,
- ClientRefreshRequested = 0,
- VsyncCountdownMax = 60, /* 1 sec @ 60FPS */
- };
-
- std::atomic_int flattenning_state_{ClientFlattenningState::NotRequired};
+ auto GetFlatCon() {
+ return flatcon_;
+ }
+ private:
HwcDisplayConfigs configs_;
DrmHwcTwo *const hwc2_;
@@ -205,10 +196,10 @@ class HwcDisplay {
DrmDisplayPipeline *pipeline_{};
std::unique_ptr<Backend> backend_;
+ std::shared_ptr<FlatteningController> flatcon_;
std::shared_ptr<VSyncWorker> vsync_worker_;
bool vsync_event_en_{};
- bool vsync_flattening_en_{};
bool vsync_tracking_en_{};
int64_t last_vsync_ts_{};
diff --git a/meson.build b/meson.build
index 4328a1e..c2e5fb6 100644
--- a/meson.build
+++ b/meson.build
@@ -11,6 +11,7 @@ inc_include = [include_directories('.')]
src_common = files(
'compositor/DrmKmsPlan.cpp',
+ 'compositor/FlatteningController.cpp',
'backend/BackendManager.cpp',
'backend/Backend.cpp',
'backend/BackendClient.cpp',