diff options
author | Roman Stratiienko <r.stratiienko@gmail.com> | 2023-01-17 21:22:29 +0200 |
---|---|---|
committer | Roman Stratiienko <r.stratiienko@gmail.com> | 2023-01-18 19:25:08 +0200 |
commit | 22fe9617dab9c0568ce625143b1d064c95df8fa2 (patch) | |
tree | 7c452c3b576fccf3db96538ae4c36b14cc2baa67 | |
parent | 0da91bf5bb72323a0f0d1928a1fc36a01c1b7941 (diff) | |
download | drm_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.bp | 1 | ||||
-rw-r--r-- | backend/Backend.cpp | 43 | ||||
-rw-r--r-- | compositor/FlatteningController.cpp | 98 | ||||
-rw-r--r-- | compositor/FlatteningController.h | 71 | ||||
-rw-r--r-- | hwc2_device/HwcDisplay.cpp | 70 | ||||
-rw-r--r-- | hwc2_device/HwcDisplay.h | 21 | ||||
-rw-r--r-- | meson.build | 1 |
7 files changed, 215 insertions, 90 deletions
@@ -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', |