aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Stratiienko <r.stratiienko@gmail.com>2023-09-18 22:46:08 +0300
committerRoman Stratiienko <r.stratiienko@gmail.com>2023-10-01 15:00:59 +0300
commitf2c060f8be1ed1270728b43a2588d3b9eb00c292 (patch)
treefa79d38263dbfa983067bc2bded41265ea98ac6a
parent63762a9d5d131bb20b29d9ab92c2ddbd7dc1889e (diff)
downloaddrm_hwcomposer-f2c060f8be1ed1270728b43a2588d3b9eb00c292.tar.gz
drm_hwcomposer: Virtual display acceleration support
Some platforms like RaspberryPI-4 can benefit from Display Blender IP Core's ability to write back the composition into RAM, offloading the GPU in cases where the display content needs to be used (screen record, remote display, etc.). To enable this feature the following system property must be enabled: PRODUCT_VENDOR_PROPERTIES += debug.sf.enable_hwc_vds=1 The feature was requested by the Tesla Android project to improve UI performance. Closes: https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer/-/issues/4 Change-Id: I643f94551408bf218a0b889f1a031598646242f1 Signed-off-by: Roman Stratiienko <r.stratiienko@gmail.com>
-rw-r--r--drm/DrmAtomicStateManager.cpp22
-rw-r--r--drm/DrmAtomicStateManager.h3
-rw-r--r--drm/DrmConnector.h8
-rw-r--r--drm/DrmDevice.cpp5
-rw-r--r--drm/DrmDevice.h2
-rw-r--r--drm/ResourceManager.cpp26
-rw-r--r--drm/ResourceManager.h3
-rw-r--r--hwc2_device/DrmHwcTwo.cpp55
-rw-r--r--hwc2_device/HwcDisplay.cpp51
-rw-r--r--hwc2_device/HwcDisplay.h12
-rw-r--r--hwc2_device/HwcDisplayConfigs.cpp44
-rw-r--r--hwc2_device/HwcDisplayConfigs.h2
12 files changed, 202 insertions, 31 deletions
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp
index 4ff16e2..b1f8257 100644
--- a/drm/DrmAtomicStateManager.cpp
+++ b/drm/DrmAtomicStateManager.cpp
@@ -79,8 +79,26 @@ auto DrmAtomicStateManager::CommitFrame(AtomicCommitArgs &args) -> int {
}
int out_fence = -1;
- if (!crtc->GetOutFencePtrProperty().AtomicSet(*pset, uint64_t(&out_fence))) {
- return -EINVAL;
+ if (!args.writeback_fb) {
+ if (!crtc->GetOutFencePtrProperty(). //
+ AtomicSet(*pset, uint64_t(&out_fence))) {
+ return -EINVAL;
+ }
+ } else {
+ if (!connector->GetWritebackOutFenceProperty(). //
+ AtomicSet(*pset, uint64_t(&out_fence))) {
+ return -EINVAL;
+ }
+
+ if (!connector->GetWritebackFbIdProperty(). //
+ AtomicSet(*pset, args.writeback_fb->GetFbId())) {
+ return -EINVAL;
+ }
+
+ if (args.writeback_release_fence) {
+ sync_wait(*args.writeback_release_fence, -1);
+ args.writeback_release_fence.reset();
+ }
}
bool nonblock = true;
diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h
index 6e32a37..e5a0945 100644
--- a/drm/DrmAtomicStateManager.h
+++ b/drm/DrmAtomicStateManager.h
@@ -37,6 +37,9 @@ struct AtomicCommitArgs {
std::shared_ptr<DrmKmsPlan> composition;
std::shared_ptr<drm_color_ctm> color_matrix;
+ std::shared_ptr<DrmFbIdHandle> writeback_fb;
+ SharedFd writeback_release_fence;
+
/* out */
SharedFd out_fence;
diff --git a/drm/DrmConnector.h b/drm/DrmConnector.h
index f21f598..018c615 100644
--- a/drm/DrmConnector.h
+++ b/drm/DrmConnector.h
@@ -94,6 +94,14 @@ class DrmConnector : public PipelineBindable<DrmConnector> {
return edid_property_;
}
+ auto &GetWritebackFbIdProperty() const {
+ return writeback_fb_id_;
+ }
+
+ auto &GetWritebackOutFenceProperty() const {
+ return writeback_out_fence_;
+ }
+
auto IsConnected() const {
return connector_->connection == DRM_MODE_CONNECTED;
}
diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp
index 1d6b62e..f6f0b01 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -242,6 +242,11 @@ auto DrmDevice::GetConnectors()
return connectors_;
}
+auto DrmDevice::GetWritebackConnectors()
+ -> const std::vector<std::unique_ptr<DrmConnector>> & {
+ return writeback_connectors_;
+}
+
auto DrmDevice::GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> & {
return planes_;
}
diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h
index 39d0c88..cbaa536 100644
--- a/drm/DrmDevice.h
+++ b/drm/DrmDevice.h
@@ -47,6 +47,8 @@ class DrmDevice {
}
auto GetConnectors() -> const std::vector<std::unique_ptr<DrmConnector>> &;
+ auto GetWritebackConnectors()
+ -> const std::vector<std::unique_ptr<DrmConnector>> &;
auto GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> &;
auto GetCrtcs() -> const std::vector<std::unique_ptr<DrmCrtc>> &;
auto GetEncoders() -> const std::vector<std::unique_ptr<DrmEncoder>> &;
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index 634ccb7..a6e9fc2 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -187,4 +187,30 @@ auto ResourceManager::GetOrderedConnectors() -> std::vector<DrmConnector *> {
return ordered_connectors;
}
+
+auto ResourceManager::GetVirtualDisplayPipeline()
+ -> std::shared_ptr<DrmDisplayPipeline> {
+ for (auto &drm : drms_) {
+ for (const auto &conn : drm->GetWritebackConnectors()) {
+ auto pipeline = DrmDisplayPipeline::CreatePipeline(*conn);
+ if (!pipeline) {
+ ALOGE("Failed to create pipeline for writeback connector %s",
+ conn->GetName().c_str());
+ }
+ if (pipeline) {
+ return pipeline;
+ }
+ }
+ }
+ return {};
+}
+
+auto ResourceManager::GetWritebackConnectorsCount() -> uint32_t {
+ uint32_t count = 0;
+ for (auto &drm : drms_) {
+ count += drm->GetWritebackConnectors().size();
+ }
+ return count;
+}
+
} // namespace android
diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h
index 72ee3e2..20e84a9 100644
--- a/drm/ResourceManager.h
+++ b/drm/ResourceManager.h
@@ -65,6 +65,9 @@ class ResourceManager {
return main_lock_;
}
+ auto GetVirtualDisplayPipeline() -> std::shared_ptr<DrmDisplayPipeline>;
+ auto GetWritebackConnectorsCount() -> uint32_t;
+
static auto GetTimeMonotonicNs() -> int64_t;
private:
diff --git a/hwc2_device/DrmHwcTwo.cpp b/hwc2_device/DrmHwcTwo.cpp
index 99756eb..b151155 100644
--- a/hwc2_device/DrmHwcTwo.cpp
+++ b/hwc2_device/DrmHwcTwo.cpp
@@ -124,17 +124,47 @@ bool DrmHwcTwo::UnbindDisplay(std::shared_ptr<DrmDisplayPipeline> pipeline) {
return true;
}
-HWC2::Error DrmHwcTwo::CreateVirtualDisplay(uint32_t /*width*/,
- uint32_t /*height*/,
- int32_t * /*format*/,
- hwc2_display_t * /*display*/) {
- // TODO(nobody): Implement virtual display
- return HWC2::Error::Unsupported;
+HWC2::Error DrmHwcTwo::CreateVirtualDisplay(
+ uint32_t width, uint32_t height,
+ int32_t *format, // NOLINT(readability-non-const-parameter)
+ hwc2_display_t *display) {
+ ALOGI("Creating virtual display %dx%d format %d", width, height, *format);
+
+ auto virtual_pipeline = resource_manager_.GetVirtualDisplayPipeline();
+ if (!virtual_pipeline)
+ return HWC2::Error::Unsupported;
+
+ *display = ++last_display_handle_;
+ auto disp = std::make_unique<HwcDisplay>(*display, HWC2::DisplayType::Virtual,
+ this);
+
+ disp->SetVirtualDisplayResolution(width, height);
+ disp->SetPipeline(virtual_pipeline);
+ displays_[*display] = std::move(disp);
+ return HWC2::Error::None;
}
-HWC2::Error DrmHwcTwo::DestroyVirtualDisplay(hwc2_display_t /*display*/) {
- // TODO(nobody): Implement virtual display
- return HWC2::Error::Unsupported;
+HWC2::Error DrmHwcTwo::DestroyVirtualDisplay(hwc2_display_t display) {
+ ALOGI("Destroying virtual display %" PRIu64, display);
+
+ if (displays_.count(display) == 0) {
+ ALOGE("Trying to destroy non-existent display %" PRIu64, display);
+ return HWC2::Error::BadDisplay;
+ }
+
+ displays_[display]->SetPipeline({});
+
+ /* Wait 0.2s before removing the displays to flush pending HWC2 transactions
+ */
+ auto &mutex = GetResMan().GetMainLock();
+ mutex.unlock();
+ const int kTimeForSFToDisposeDisplayUs = 200000;
+ usleep(kTimeForSFToDisposeDisplayUs);
+ mutex.lock();
+
+ displays_.erase(display);
+
+ return HWC2::Error::None;
}
void DrmHwcTwo::Dump(uint32_t *outSize, char *outBuffer) {
@@ -156,8 +186,11 @@ void DrmHwcTwo::Dump(uint32_t *outSize, char *outBuffer) {
}
uint32_t DrmHwcTwo::GetMaxVirtualDisplayCount() {
- // TODO(nobody): Implement virtual display
- return 0;
+ auto writeback_count = resource_manager_.GetWritebackConnectorsCount();
+ writeback_count = std::min(writeback_count, 1U);
+ /* Currently, only 1 virtual display is supported. Other cases need testing */
+ ALOGI("Max virtual display count: %d", writeback_count);
+ return writeback_count;
}
HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor,
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 4ef98c3..c9d9a68 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -67,7 +67,11 @@ std::string HwcDisplay::Dump() {
HwcDisplay::HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type,
DrmHwcTwo *hwc2)
- : hwc2_(hwc2), handle_(handle), type_(type), client_layer_(this){};
+ : hwc2_(hwc2), handle_(handle), type_(type), client_layer_(this) {
+ if (type_ == HWC2::DisplayType::Virtual) {
+ writeback_layer_ = std::make_unique<HwcLayer>(this);
+ }
+}
void HwcDisplay::SetColorMarixToIdentity() {
color_matrix_ = std::make_shared<drm_color_ctm>();
@@ -160,10 +164,12 @@ HWC2::Error HwcDisplay::Init() {
},
};
- vsync_worker_ = VSyncWorker::CreateInstance(pipeline_, vsw_callbacks);
- if (!vsync_worker_) {
- ALOGE("Failed to create event worker for d=%d\n", int(handle_));
- return HWC2::Error::BadDisplay;
+ if (type_ != HWC2::DisplayType::Virtual) {
+ vsync_worker_ = VSyncWorker::CreateInstance(pipeline_, vsw_callbacks);
+ if (!vsync_worker_) {
+ ALOGE("Failed to create event worker for d=%d\n", int(handle_));
+ return HWC2::Error::BadDisplay;
+ }
}
if (!IsInHeadlessMode()) {
@@ -190,10 +196,12 @@ HWC2::Error HwcDisplay::Init() {
HWC2::Error HwcDisplay::ChosePreferredConfig() {
HWC2::Error err{};
- if (!IsInHeadlessMode()) {
+ if (type_ == HWC2::DisplayType::Virtual) {
+ configs_.GenFakeMode(virtual_disp_width_, virtual_disp_height_);
+ } else if (!IsInHeadlessMode()) {
err = configs_.Update(*pipeline_->connector->Get());
} else {
- configs_.FillHeadless();
+ configs_.GenFakeMode(0, 0);
}
if (!IsInHeadlessMode() && err != HWC2::Error::None) {
return HWC2::Error::BadDisplay;
@@ -528,6 +536,13 @@ HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
*/
current_plan_ = DrmKmsPlan::CreateDrmKmsPlan(GetPipe(),
std::move(composition_layers));
+
+ if (type_ == HWC2::DisplayType::Virtual) {
+ a_args.writeback_fb = writeback_layer_->GetLayerData().fb;
+ a_args.writeback_release_fence = writeback_layer_->GetLayerData()
+ .acquire_fence;
+ }
+
if (!current_plan_) {
if (!a_args.test_only) {
ALOGE("Failed to create DrmKmsPlan");
@@ -722,10 +737,16 @@ bool HwcDisplay::CtmByGpu() {
return true;
}
-HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t /*buffer*/,
- int32_t /*release_fence*/) {
- // TODO(nobody): Need virtual display support
- return HWC2::Error::Unsupported;
+HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t buffer,
+ int32_t release_fence) {
+ writeback_layer_->SetLayerBuffer(buffer, release_fence);
+ writeback_layer_->PopulateLayerData();
+ if (!writeback_layer_->IsLayerUsableAsDevice()) {
+ ALOGE("Output layer must be always usable by DRM/KMS");
+ return HWC2::Error::BadLayer;
+ }
+ /* TODO: Check if format is supported by writeback connector */
+ return HWC2::Error::None;
}
HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) {
@@ -773,6 +794,10 @@ HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) {
}
HWC2::Error HwcDisplay::SetVsyncEnabled(int32_t enabled) {
+ if (type_ == HWC2::DisplayType::Virtual) {
+ return HWC2::Error::None;
+ }
+
vsync_event_en_ = HWC2_VSYNC_ENABLE == enabled;
if (vsync_event_en_) {
vsync_worker_->VSyncControl(true);
@@ -845,6 +870,10 @@ HWC2::Error HwcDisplay::SetActiveConfigWithConstraints(
hwc2_config_t config,
hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints,
hwc_vsync_period_change_timeline_t *outTimeline) {
+ if (type_ == HWC2::DisplayType::Virtual) {
+ return HWC2::Error::None;
+ }
+
if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) {
return HWC2::Error::BadParameter;
}
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index d9dc4eb..aef029a 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -182,6 +182,15 @@ class HwcDisplay {
return flatcon_;
}
+ auto &GetWritebackLayer() {
+ return writeback_layer_;
+ }
+
+ void SetVirtualDisplayResolution(uint16_t width, uint16_t height) {
+ virtual_disp_width_ = width;
+ virtual_disp_height_ = height;
+ }
+
private:
HwcDisplayConfigs configs_;
@@ -210,6 +219,9 @@ class HwcDisplay {
std::map<hwc2_layer_t, HwcLayer> layers_;
HwcLayer client_layer_;
+ std::unique_ptr<HwcLayer> writeback_layer_;
+ uint16_t virtual_disp_width_{};
+ uint16_t virtual_disp_height_{};
int32_t color_mode_{};
static constexpr int kCtmRows = 3;
static constexpr int kCtmCols = 3;
diff --git a/hwc2_device/HwcDisplayConfigs.cpp b/hwc2_device/HwcDisplayConfigs.cpp
index 9727989..f6bf4a1 100644
--- a/hwc2_device/HwcDisplayConfigs.cpp
+++ b/hwc2_device/HwcDisplayConfigs.cpp
@@ -19,6 +19,7 @@
#include "HwcDisplayConfigs.h"
#include <cmath>
+#include <cstring>
#include "drm/DrmConnector.h"
#include "utils/log.h"
@@ -28,23 +29,53 @@ constexpr uint32_t kHeadlessModeDisplayHeightMm = 122;
constexpr uint32_t kHeadlessModeDisplayWidthPx = 1024;
constexpr uint32_t kHeadlessModeDisplayHeightPx = 768;
constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
+constexpr uint32_t kSyncLen = 10;
+constexpr uint32_t kBackPorch = 10;
+constexpr uint32_t kFrontPorch = 10;
+constexpr uint32_t kHzInKHz = 1000;
namespace android {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
uint32_t HwcDisplayConfigs::last_config_id = 1;
-void HwcDisplayConfigs::FillHeadless() {
+void HwcDisplayConfigs::GenFakeMode(uint16_t width, uint16_t height) {
hwc_configs.clear();
last_config_id++;
preferred_config_id = active_config_id = last_config_id;
auto headless_drm_mode_info = (drmModeModeInfo){
- .hdisplay = kHeadlessModeDisplayWidthPx,
- .vdisplay = kHeadlessModeDisplayHeightPx,
+ .hdisplay = width,
+ .vdisplay = height,
.vrefresh = kHeadlessModeDisplayVRefresh,
- .name = "HEADLESS-MODE",
+ .name = "VIRTUAL-MODE",
};
+
+ if (width == 0 || height == 0) {
+ strcpy(headless_drm_mode_info.name, "HEADLESS-MODE");
+ headless_drm_mode_info.hdisplay = kHeadlessModeDisplayWidthPx;
+ headless_drm_mode_info.vdisplay = kHeadlessModeDisplayHeightPx;
+ }
+
+ /* We need a valid mode to pass the kernel validation */
+
+ headless_drm_mode_info.hsync_start = headless_drm_mode_info.hdisplay +
+ kFrontPorch;
+ headless_drm_mode_info.hsync_end = headless_drm_mode_info.hsync_start +
+ kSyncLen;
+ headless_drm_mode_info.htotal = headless_drm_mode_info.hsync_end + kBackPorch;
+
+ headless_drm_mode_info.vsync_start = headless_drm_mode_info.vdisplay +
+ kFrontPorch;
+ headless_drm_mode_info.vsync_end = headless_drm_mode_info.vsync_start +
+ kSyncLen;
+ headless_drm_mode_info.vtotal = headless_drm_mode_info.vsync_end + kBackPorch;
+
+ headless_drm_mode_info.clock = (headless_drm_mode_info.htotal *
+ headless_drm_mode_info.vtotal *
+ headless_drm_mode_info.vrefresh) /
+ kHzInKHz;
+
hwc_configs[active_config_id] = (HwcDisplayConfig){
.id = active_config_id,
.group_id = 1,
@@ -58,8 +89,9 @@ void HwcDisplayConfigs::FillHeadless() {
// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
/* In case UpdateModes will fail we will still have one mode for headless
- * mode*/
- FillHeadless();
+ * mode
+ */
+ GenFakeMode(0, 0);
/* Read real configs */
auto ret = connector.UpdateModes();
if (ret != 0) {
diff --git a/hwc2_device/HwcDisplayConfigs.h b/hwc2_device/HwcDisplayConfigs.h
index 98067c1..33dcb81 100644
--- a/hwc2_device/HwcDisplayConfigs.h
+++ b/hwc2_device/HwcDisplayConfigs.h
@@ -39,7 +39,7 @@ struct HwcDisplayConfig {
struct HwcDisplayConfigs {
HWC2::Error Update(DrmConnector &conn);
- void FillHeadless();
+ void GenFakeMode(uint16_t width, uint16_t height);
std::map<uint32_t /*config_id*/, struct HwcDisplayConfig> hwc_configs;