Upgrade drm_hwcomposer to 7009cc1909804ef707376441b5b3f986b492d66a
This project was upgraded with external_updater.
Usage: tools/external_updater/updater.sh update external/drm_hwcomposer
For more info, check https://cs.android.com/android/platform/superproject/main/+/main:tools/external_updater/README.md
Test: TreeHugger
Change-Id: Iccce48405a92ce7e469f1c60b04fb576e2702838
diff --git a/.ci/Makefile b/.ci/Makefile
index 0dba6cc..d57ea86 100644
--- a/.ci/Makefile
+++ b/.ci/Makefile
@@ -9,7 +9,7 @@
SRC_DIR := .
CXXFLAGS := -Wall -Wextra -Werror -Wno-missing-designated-field-initializers
-CXXFLAGS += -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS
+CXXFLAGS += -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Wthread-safety
CXXFLAGS += -fvisibility-inlines-hidden -std=gnu++17 -DHWC2_USE_CPP11 -DHWC2_INCLUDE_STRINGIFICATION -fno-rtti
CXXARGS := $(shell cat $(BASE_DIR)/toolchain_wrapper/sharedlib.cppflags)
diff --git a/.ci/container/.gitlab-ci.yml b/.ci/container/.gitlab-ci.yml
new file mode 100644
index 0000000..85024f8
--- /dev/null
+++ b/.ci/container/.gitlab-ci.yml
@@ -0,0 +1,37 @@
+
+.incorporate-templates-commit:
+ variables:
+ FDO_DISTRIBUTION_TAG: "${HWC_TAG}--${CI_TEMPLATES_COMMIT}"
+
+.container:
+ stage: container
+ extends:
+ - .incorporate-templates-commit
+ variables:
+ FDO_REPO_SUFFIX: $CI_JOB_NAME
+ # no need to pull the whole repo to build the container image
+ GIT_STRATEGY: none
+
+ubuntu/x86_64_hwc:
+ extends:
+ - .fdo.container-build@ubuntu
+ - .container
+ variables:
+ FDO_DISTRIBUTION_VERSION: "24.10"
+ FDO_DISTRIBUTION_EXEC: 'bash .ci/container/ubuntu/x86_64_aospless.sh'
+ HWC_TAG: $UBUNTU_HWC_TAG
+
+.set-image:
+ extends:
+ - .incorporate-templates-commit
+ image: "$CI_REGISTRY_IMAGE/${HWC_IMAGE}:${FDO_DISTRIBUTION_TAG}"
+
+.use-ubuntu/x86_64_hwc:
+ extends:
+ - .fdo.container-build@ubuntu
+ - .set-image
+ variables:
+ HWC_TAG: $UBUNTU_HWC_TAG
+ HWC_IMAGE: $UBUNTU_HWC_IMAGE
+ needs:
+ - ubuntu/x86_64_hwc
diff --git a/.ci/container/ubuntu/x86_64_aospless.sh b/.ci/container/ubuntu/x86_64_aospless.sh
new file mode 100644
index 0000000..9b6e49d
--- /dev/null
+++ b/.ci/container/ubuntu/x86_64_aospless.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+# For any changes to this file to take effect, the UBUNTU_HWC_TAG has
+# to be bumped to generate a new image.
+
+set -ex
+
+DEPS=(
+ clang
+ llvm
+ clang-19
+ clang-tidy-19
+ clang-format-19
+ ca-certificates
+ git
+ libdrm-dev
+ blueprint-tools
+ libgtest-dev
+ make
+ python3
+ wget
+ sudo
+ rsync
+ lld
+ pkg-config
+ ninja-build
+ meson
+ python3-mako
+ python3-jinja2
+ python3-ply
+ python3-yaml
+ wget
+ gnupg
+ xz-utils
+)
+
+export DEBIAN_FRONTEND=noninteractive
+
+apt-get update
+apt-get upgrade -y
+
+apt-get install -y --no-remove --no-install-recommends "${DEPS[@]}"
+
+wget https://gitlab.freedesktop.org/-/project/5/uploads/cafa930dad28acf7ee44d50101d5e8f0/aospless_drm_hwcomposer_arm64.tar.xz
+
+sha256sum aospless_drm_hwcomposer_arm64.tar.xz
+if echo f792b1140861112f80c8a3a22e1af8e3eccf4910fe4449705e62d2032b713bf9 aospless_drm_hwcomposer_arm64.tar.xz | sha256sum --check; then
+ tar --no-same-owner -xf aospless_drm_hwcomposer_arm64.tar.xz -C /
+else
+ echo "Tar file check failed"
+ exit 1
+fi
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fbe2a85..3d39f17 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,5 +1,3 @@
-image: ubuntu:24.10
-
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
@@ -7,52 +5,53 @@
variables:
DEBIAN_FRONTEND: noninteractive
+ CI_TEMPLATES_COMMIT: &ci-templates-commit e195d80f35b45cc73668be3767b923fd76c70ed5
+ FDO_UPSTREAM_REPO: "drm-hwcomposer/drm-hwcomposer"
+ UBUNTU_HWC_IMAGE: "ubuntu/x86_64_hwc"
+ UBUNTU_HWC_TAG: "20250218_hwc"
-before_script:
- - apt-get --quiet update --yes >/dev/null
- - apt-get --quiet install --yes clang-19 clang-tidy-19 clang-format-19 git libdrm-dev blueprint-tools libgtest-dev make >/dev/null
- - apt-get --quiet install --yes clang llvm make python3 wget sudo rsync lld pkg-config ninja-build meson >/dev/null
- - apt-get --quiet install --yes python3-mako python3-jinja2 python3-ply python3-yaml >/dev/null
+include:
+ - project: 'freedesktop/ci-templates'
+ ref: *ci-templates-commit
+ file:
+ - '/templates/ubuntu.yml'
+ - local: '.ci/container/.gitlab-ci.yml'
stages:
+ - container
- build
- tidy
- style
build:
stage: build
+ extends:
+ .use-ubuntu/x86_64_hwc
script:
- - mkdir -p install/arm64
- - cd ..
- - rm -f aospless_drm_hwcomposer_arm64.tar.xz
- - rm -rf aospless/*
- - wget https://gitlab.freedesktop.org/-/project/5/uploads/cafa930dad28acf7ee44d50101d5e8f0/aospless_drm_hwcomposer_arm64.tar.xz
- - tar xf aospless_drm_hwcomposer_arm64.tar.xz
- - rm -rf aospless/src
- - ln -s ../drm-hwcomposer/ aospless/src
- - make -C ./aospless install
- - cp -r aospless/install/* drm-hwcomposer/install/arm64
- - ls drm-hwcomposer/install/arm64
-
+ - ln -s "${CI_PROJECT_DIR}" "/aospless/src"
+ - make -C /aospless install
+ - mkdir -p "${CI_PROJECT_DIR}/install/arm64"
+ - cp -r /aospless/install/* "${CI_PROJECT_DIR}/install/arm64"
artifacts:
paths:
- - install/
+ - "${CI_PROJECT_DIR}/install/arm64"
expire_in: 1 week
tidy:
stage: tidy
+ extends:
+ .use-ubuntu/x86_64_hwc
script:
- - cd ..
- - rm -f aospless_drm_hwcomposer_arm64.tar.xz
- - rm -rf aospless/*
- - wget https://gitlab.freedesktop.org/-/project/5/uploads/cafa930dad28acf7ee44d50101d5e8f0/aospless_drm_hwcomposer_arm64.tar.xz
- - tar xf aospless_drm_hwcomposer_arm64.tar.xz
- - cd -
- - make -j$(nproc) -k -f .ci/Makefile
+ - cp -r /aospless ../
+ - make -j$(nproc) -k -f "${CI_PROJECT_DIR}/.ci/Makefile"
checkstyle:
stage: style
+ extends:
+ .use-ubuntu/x86_64_hwc
script: "./.ci/.gitlab-ci-checkcommit.sh"
artifacts:
when: on_failure
untracked: true
+ tags:
+ - placeholder-job
diff --git a/METADATA b/METADATA
index afe2045..2d137a9 100644
--- a/METADATA
+++ b/METADATA
@@ -8,12 +8,12 @@
license_type: NOTICE
last_upgrade_date {
year: 2025
- month: 2
- day: 18
+ month: 3
+ day: 13
}
identifier {
type: "Git"
value: "https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer"
- version: "75855a6ab877011a2e26ae95da7ddf06dd3a04b2"
+ version: "7009cc1909804ef707376441b5b3f986b492d66a"
}
}
diff --git a/backend/Backend.cpp b/backend/Backend.cpp
index bf45c08..650e29e 100644
--- a/backend/Backend.cpp
+++ b/backend/Backend.cpp
@@ -130,7 +130,7 @@
std::tuple<int, int> Backend::GetExtraClientRange(
HwcDisplay *display, const std::vector<HwcLayer *> &layers,
int client_start, size_t client_size) {
- auto planes = display->GetPipe().GetUsablePlanes();
+ auto [planes, cursor_plane] = display->GetPipe().GetUsablePlanes();
size_t avail_planes = planes.size();
/*
diff --git a/compositor/DrmKmsPlan.cpp b/compositor/DrmKmsPlan.cpp
index 4f75e89..443b515 100644
--- a/compositor/DrmKmsPlan.cpp
+++ b/compositor/DrmKmsPlan.cpp
@@ -28,7 +28,7 @@
-> std::unique_ptr<DrmKmsPlan> {
auto plan = std::make_unique<DrmKmsPlan>();
- auto avail_planes = pipe.GetUsablePlanes();
+ auto [avail_planes, cursor_plane] = pipe.GetUsablePlanes();
int z_pos = 0;
for (auto &dhl : composition) {
diff --git a/compositor/LayerData.h b/compositor/LayerData.h
index 962141f..90ae7be 100644
--- a/compositor/LayerData.h
+++ b/compositor/LayerData.h
@@ -30,6 +30,8 @@
class DrmFbIdHandle;
+using ILayerId = int64_t;
+
/* Rotation is defined in the clockwise direction */
/* The flip is done before rotation */
struct LayerTransform {
diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp
index 6fd5c0c..f6141d4 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -90,6 +90,14 @@
}
HasAddFb2ModifiersSupport_ = cap_value != 0;
+ uint64_t cursor_width = 0;
+ uint64_t cursor_height = 0;
+ if (drmGetCap(*GetFd(), DRM_CAP_CURSOR_WIDTH, &cursor_width) == 0 &&
+ drmGetCap(*GetFd(), DRM_CAP_CURSOR_HEIGHT, &cursor_height) == 0) {
+ cap_cursor_size_ = std::pair<uint64_t, uint64_t>(cursor_width,
+ cursor_height);
+ }
+
drmSetMaster(*GetFd());
if (drmIsMaster(*GetFd()) == 0) {
ALOGE("DRM/KMS master access required");
@@ -200,7 +208,7 @@
drmModePropertyPtr p = drmModeGetProperty(*GetFd(), props->props[i]);
if (strcmp(p->name, prop_name) == 0) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
- property->Init(obj_id, p, props->prop_values[i]);
+ property->Init(GetFd(), obj_id, p, props->prop_values[i]);
found = true;
}
drmModeFreeProperty(p);
diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h
index baa719d..ac20855 100644
--- a/drm/DrmDevice.h
+++ b/drm/DrmDevice.h
@@ -106,6 +106,10 @@
int GetProperty(uint32_t obj_id, uint32_t obj_type, const char *prop_name,
DrmProperty *property) const;
+ const std::optional<std::pair<uint64_t, uint64_t>> &GetCapCursorSize() const {
+ return cap_cursor_size_;
+ }
+
private:
explicit DrmDevice(ResourceManager *res_man, uint32_t index);
auto Init(const char *path) -> int;
@@ -123,6 +127,7 @@
std::pair<uint32_t, uint32_t> min_resolution_;
std::pair<uint32_t, uint32_t> max_resolution_;
+ std::optional<std::pair<uint64_t, uint64_t>> cap_cursor_size_;
bool HasAddFb2ModifiersSupport_{};
@@ -130,4 +135,5 @@
ResourceManager *const res_man_;
};
+
} // namespace android
diff --git a/drm/DrmDisplayPipeline.cpp b/drm/DrmDisplayPipeline.cpp
index 7588ee2..8062b2c 100644
--- a/drm/DrmDisplayPipeline.cpp
+++ b/drm/DrmDisplayPipeline.cpp
@@ -65,18 +65,21 @@
}
std::vector<DrmPlane *> primary_planes;
- std::vector<DrmPlane *> overlay_planes;
/* Attach necessary resources */
auto display_planes = std::vector<DrmPlane *>();
for (const auto &plane : dev.GetPlanes()) {
if (plane->IsCrtcSupported(crtc)) {
- if (plane->GetType() == DRM_PLANE_TYPE_PRIMARY) {
- primary_planes.emplace_back(plane.get());
- } else if (plane->GetType() == DRM_PLANE_TYPE_OVERLAY) {
- overlay_planes.emplace_back(plane.get());
- } else {
- ALOGI("Ignoring cursor plane %d", plane->GetId());
+ switch (plane->GetType()) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ primary_planes.emplace_back(plane.get());
+ break;
+ case DRM_PLANE_TYPE_OVERLAY:
+ case DRM_PLANE_TYPE_CURSOR:
+ break;
+ default:
+ ALOGE("Unknown type for plane %d", plane->GetId());
+ break;
}
}
}
@@ -158,25 +161,34 @@
return {};
}
-auto DrmDisplayPipeline::GetUsablePlanes()
- -> std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> {
- std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> planes;
+auto DrmDisplayPipeline::GetUsablePlanes() -> UsablePlanes {
+ UsablePlanes pair;
+ auto &[planes, cursor] = pair;
+
planes.emplace_back(primary_plane);
- if (Properties::UseOverlayPlanes()) {
- for (const auto &plane : device->GetPlanes()) {
- if (plane->IsCrtcSupported(*crtc->Get())) {
- if (plane->GetType() == DRM_PLANE_TYPE_OVERLAY) {
- auto op = plane->BindPipeline(this, true);
- if (op) {
- planes.emplace_back(op);
- }
+ for (const auto &plane : device->GetPlanes()) {
+ if (plane->IsCrtcSupported(*crtc->Get())) {
+ if (Properties::UseOverlayPlanes() &&
+ plane->GetType() == DRM_PLANE_TYPE_OVERLAY) {
+ auto op = plane->BindPipeline(this, true);
+ if (op) {
+ planes.emplace_back(op);
+ }
+ } else if (plane->GetType() == DRM_PLANE_TYPE_CURSOR) {
+ if (cursor) {
+ ALOGW(
+ "Encountered multiple cursor planes for CRTC %d. Ignoring "
+ "plane %d",
+ crtc->Get()->GetId(), plane->GetId());
+ } else {
+ cursor = plane->BindPipeline(this, true);
}
}
}
}
- return planes;
+ return pair;
}
DrmDisplayPipeline::~DrmDisplayPipeline() {
diff --git a/drm/DrmDisplayPipeline.h b/drm/DrmDisplayPipeline.h
index cf64a36..0d05288 100644
--- a/drm/DrmDisplayPipeline.h
+++ b/drm/DrmDisplayPipeline.h
@@ -67,12 +67,15 @@
B *const bindable_;
};
+using UsablePlanes = std::pair<
+ std::vector<std::shared_ptr<BindingOwner<DrmPlane>>>,
+ std::shared_ptr<BindingOwner<DrmPlane>>>;
+
struct DrmDisplayPipeline {
static auto CreatePipeline(DrmConnector &connector)
-> std::unique_ptr<DrmDisplayPipeline>;
- auto GetUsablePlanes()
- -> std::vector<std::shared_ptr<BindingOwner<DrmPlane>>>;
+ auto GetUsablePlanes() -> UsablePlanes;
~DrmDisplayPipeline();
diff --git a/drm/DrmPlane.cpp b/drm/DrmPlane.cpp
index 68887dd..0fe060d 100644
--- a/drm/DrmPlane.cpp
+++ b/drm/DrmPlane.cpp
@@ -132,6 +132,12 @@
}
}
+ if (type_ == DRM_PLANE_TYPE_CURSOR &&
+ GetPlaneProperty("SIZE_HINTS", size_hints_property_,
+ Presence::kOptional)) {
+ size_hints_property_.GetBlobData(size_hints_);
+ }
+
return 0;
}
@@ -208,6 +214,13 @@
return false;
}
+ if (type_ == DRM_PLANE_TYPE_CURSOR &&
+ !IsBufferValidForCursorPlane(layer->bi.value())) {
+ ALOGV("Buffer size %dx%d is not supported by cursor plane %d",
+ layer->bi->width, layer->bi->height, GetId());
+ return false;
+ }
+
return true;
}
@@ -342,4 +355,21 @@
return true;
}
+bool DrmPlane::HasCursorSizeConstraints() const {
+ return drm_->GetCapCursorSize().has_value() || !size_hints_.empty();
+}
+
+bool DrmPlane::IsBufferValidForCursorPlane(const BufferInfo &bi) const {
+ if (std::find_if(size_hints_.begin(), size_hints_.end(),
+ [&](const auto &hint) -> bool {
+ return bi.width == hint.width && bi.height == hint.height;
+ }) != size_hints_.end()) {
+ return true;
+ }
+
+ const auto &cap_size = drm_->GetCapCursorSize();
+ return cap_size.has_value() && bi.width == cap_size->first &&
+ bi.height == cap_size->second;
+}
+
} // namespace android
diff --git a/drm/DrmPlane.h b/drm/DrmPlane.h
index 81306d9..2fa6388 100644
--- a/drm/DrmPlane.h
+++ b/drm/DrmPlane.h
@@ -30,6 +30,12 @@
class DrmDevice;
struct LayerData;
+// NOLINTNEXTLINE(readability-identifier-naming)
+struct drm_plane_size_hint_local {
+ __u16 width;
+ __u16 height;
+};
+
class DrmPlane : public PipelineBindable<DrmPlane> {
public:
DrmPlane(const DrmPlane &) = delete;
@@ -59,6 +65,8 @@
return plane_->plane_id;
}
+ bool HasCursorSizeConstraints() const;
+
private:
DrmPlane(DrmDevice &dev, DrmModePlaneUnique plane)
: drm_(&dev), plane_(std::move(plane)){};
@@ -70,6 +78,7 @@
auto Init() -> int;
auto GetPlaneProperty(const char *prop_name, DrmProperty &property,
Presence presence = Presence::kMandatory) -> bool;
+ bool IsBufferValidForCursorPlane(const BufferInfo &bi) const;
uint32_t type_{};
@@ -92,10 +101,12 @@
DrmProperty in_fence_fd_property_;
DrmProperty color_encoding_property_;
DrmProperty color_range_property_;
+ DrmProperty size_hints_property_;
std::map<BufferBlendMode, uint64_t> blending_enum_map_;
std::map<BufferColorSpace, uint64_t> color_encoding_enum_map_;
std::map<BufferSampleRange, uint64_t> color_range_enum_map_;
uint64_t transform_enum_mask_ = DRM_MODE_ROTATE_0;
+ std::vector<drm_plane_size_hint_local> size_hints_;
};
} // namespace android
diff --git a/drm/DrmProperty.cpp b/drm/DrmProperty.cpp
index 8dc62f6..24a67bc 100644
--- a/drm/DrmProperty.cpp
+++ b/drm/DrmProperty.cpp
@@ -35,12 +35,14 @@
: value(e->value), name(e->name) {
}
-DrmProperty::DrmProperty(uint32_t obj_id, drmModePropertyPtr p,
- uint64_t value) {
- Init(obj_id, p, value);
+DrmProperty::DrmProperty(const SharedFd &fd, uint32_t obj_id,
+ drmModePropertyPtr p, uint64_t value) {
+ Init(fd, obj_id, p, value);
}
-void DrmProperty::Init(uint32_t obj_id, drmModePropertyPtr p, uint64_t value) {
+void DrmProperty::Init(const SharedFd &fd, uint32_t obj_id,
+ drmModePropertyPtr p, uint64_t value) {
+ fd_ = fd;
obj_id_ = obj_id;
id_ = p->prop_id;
flags_ = p->flags;
diff --git a/drm/DrmProperty.h b/drm/DrmProperty.h
index 99bddb1..c0f6161 100644
--- a/drm/DrmProperty.h
+++ b/drm/DrmProperty.h
@@ -18,22 +18,29 @@
#include <xf86drmMode.h>
+#include <cinttypes>
#include <cstdint>
#include <map>
#include <optional>
#include <string>
#include <vector>
+#include "drm/DrmUnique.h"
+#include "utils/fd.h"
+#include "utils/log.h"
+
namespace android {
class DrmProperty {
public:
DrmProperty() = default;
- DrmProperty(uint32_t obj_id, drmModePropertyPtr p, uint64_t value);
+ DrmProperty(const SharedFd &fd, uint32_t obj_id, drmModePropertyPtr p,
+ uint64_t value);
DrmProperty(const DrmProperty &) = delete;
DrmProperty &operator=(const DrmProperty &) = delete;
- auto Init(uint32_t obj_id, drmModePropertyPtr p, uint64_t value) -> void;
+ auto Init(const SharedFd &fd, uint32_t obj_id, drmModePropertyPtr p,
+ uint64_t value) -> void;
std::tuple<uint64_t, int> GetEnumValueWithName(const std::string &name) const;
auto GetId() const {
@@ -80,6 +87,12 @@
auto GetEnumNameFromValue(uint64_t value) const -> std::optional<std::string>;
+ bool IsBlob() const {
+ return id_ != 0 && (flags_ & DRM_MODE_PROP_BLOB) != 0;
+ }
+ template <typename T>
+ bool GetBlobData(std::vector<T> &data_out) const;
+
private:
class DrmPropertyEnum {
public:
@@ -90,6 +103,7 @@
std::string name;
};
+ SharedFd fd_ = nullptr;
uint32_t obj_id_ = 0;
uint32_t id_ = 0;
@@ -130,4 +144,43 @@
return false;
}
+template <typename T>
+bool DrmProperty::GetBlobData(std::vector<T> &data_out) const {
+ auto value = GetValue();
+ if (!fd_) {
+ ALOGE("Could not read blob data from property %s: No fd", name_.c_str());
+ return false;
+ }
+ if (!IsBlob()) {
+ ALOGE("Property %s is not blob type", name_.c_str());
+ return false;
+ }
+ if (!value.has_value()) {
+ ALOGE("Could not read blob data from property %s: No blob id",
+ name_.c_str());
+ return false;
+ }
+
+ auto blob = MakeDrmModePropertyBlobUnique(*fd_, value.value());
+ if (blob == nullptr) {
+ ALOGE("Failed to read blob with id=%" PRIu64 " from property %s",
+ value.value(), name_.c_str());
+ return false;
+ }
+
+ if (blob->length % sizeof(T) != 0) {
+ ALOGE(
+ "Property %s blob size of %u bytes is not divisible by type argument "
+ "size of %zu bytes",
+ name_.c_str(), blob->length, sizeof(T));
+ return false;
+ }
+
+ auto cast_data = static_cast<T *>(blob->data);
+ size_t cast_data_length = blob->length / sizeof(T);
+ data_out.assign(cast_data, cast_data + cast_data_length);
+
+ return true;
+}
+
} // namespace android
diff --git a/drm/VSyncWorker.cpp b/drm/VSyncWorker.cpp
index defbe42..7ec1c28 100644
--- a/drm/VSyncWorker.cpp
+++ b/drm/VSyncWorker.cpp
@@ -55,7 +55,6 @@
{
const std::lock_guard<std::mutex> lock(mutex_);
enabled_ = ShouldEnable();
- last_timestamp_ = -1;
}
cv_.notify_all();
@@ -64,6 +63,7 @@
void VSyncWorker::SetVsyncPeriodNs(uint32_t vsync_period_ns) {
const std::lock_guard<std::mutex> lock(mutex_);
vsync_period_ns_ = vsync_period_ns;
+ last_timestamp_ = std::nullopt;
}
void VSyncWorker::SetVsyncTimestampTracking(bool enabled) {
@@ -71,9 +71,9 @@
const std::lock_guard<std::mutex> lock(mutex_);
enable_vsync_timestamps_ = enabled;
if (enabled) {
- // Reset the last timestamp so the caller knows if a vsync timestamp is
- // fresh or not.
- last_vsync_timestamp_ = 0;
+ // Reset the freshness flag to ensure that only a fresh timestamp is
+ // returned from GetLastVsyncTimestamp.
+ last_timestamp_is_fresh_ = false;
}
}
UpdateVSyncControl();
@@ -81,7 +81,7 @@
uint32_t VSyncWorker::GetLastVsyncTimestamp() {
const std::lock_guard<std::mutex> lock(mutex_);
- return last_vsync_timestamp_;
+ return last_timestamp_is_fresh_ ? last_timestamp_.value_or(0) : 0;
}
void VSyncWorker::SetTimestampCallback(
@@ -121,19 +121,23 @@
* timestamp.
*/
int64_t VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t current) const {
- if (last_timestamp_ < 0)
+ if (!last_timestamp_.has_value())
return current + frame_ns;
- return (frame_ns * ((current - last_timestamp_) / frame_ns + 1)) +
- last_timestamp_;
+ return (frame_ns * ((current - *last_timestamp_) / frame_ns + 1)) +
+ *last_timestamp_;
}
static const int64_t kOneSecondNs = 1LL * 1000 * 1000 * 1000;
int VSyncWorker::SyntheticWaitVBlank(int64_t *timestamp) {
- auto time_now = ResourceManager::GetTimeMonotonicNs();
+ int64_t phased_timestamp = 0;
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ int64_t time_now = ResourceManager::GetTimeMonotonicNs();
+ phased_timestamp = GetPhasedVSync(vsync_period_ns_, time_now);
+ }
- auto phased_timestamp = GetPhasedVSync(vsync_period_ns_, time_now);
struct timespec vsync {};
vsync.tv_sec = int(phased_timestamp / kOneSecondNs);
vsync.tv_nsec = int(phased_timestamp - (vsync.tv_sec * kOneSecondNs));
@@ -155,6 +159,9 @@
for (;;) {
{
std::unique_lock<std::mutex> lock(mutex_);
+ // Thread safety analysis doesn't understand std::unique_lock.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wthread-safety-analysis"
if (thread_exit_)
break;
@@ -163,6 +170,7 @@
if (!enabled_)
continue;
+#pragma clang diagnostic pop
}
ret = -EAGAIN;
@@ -191,21 +199,23 @@
}
std::optional<VsyncTimestampCallback> vsync_callback;
+ int64_t vsync_period_ns = 0;
{
const std::lock_guard<std::mutex> lock(mutex_);
if (!enabled_)
continue;
if (enable_vsync_timestamps_) {
- last_vsync_timestamp_ = timestamp;
+ last_timestamp_is_fresh_ = true;
}
vsync_callback = callback_;
+ vsync_period_ns = vsync_period_ns_;
+ last_timestamp_ = timestamp;
}
if (vsync_callback) {
- vsync_callback.value()(timestamp, vsync_period_ns_);
+ vsync_callback.value()(timestamp, vsync_period_ns);
}
- last_timestamp_ = timestamp;
}
ALOGI("VSyncWorker thread exit");
diff --git a/drm/VSyncWorker.h b/drm/VSyncWorker.h
index c76dd14..8e410f4 100644
--- a/drm/VSyncWorker.h
+++ b/drm/VSyncWorker.h
@@ -23,6 +23,7 @@
#include <thread>
#include "DrmDevice.h"
+#include "utils/thread_annotations.h"
namespace android {
@@ -36,7 +37,8 @@
auto static CreateInstance(std::shared_ptr<DrmDisplayPipeline> &pipe)
-> std::unique_ptr<VSyncWorker>;
- // Set the expected vsync period.
+ // Set the expected vsync period. Resets internal timestamp tracking until the
+ // next vsync event is tracked.
void SetVsyncPeriodNs(uint32_t vsync_period_ns);
// Set or clear a callback to be fired on vsync.
@@ -55,27 +57,26 @@
void ThreadFn();
- int64_t GetPhasedVSync(int64_t frame_ns, int64_t current) const;
+ int64_t GetPhasedVSync(int64_t frame_ns, int64_t current) const
+ REQUIRES(mutex_);
int SyntheticWaitVBlank(int64_t *timestamp);
- // Must hold the lock before calling these.
void UpdateVSyncControl();
- bool ShouldEnable() const;
+ bool ShouldEnable() const REQUIRES(mutex_);
SharedFd drm_fd_;
uint32_t high_crtc_ = 0;
- bool enabled_ = false;
- bool thread_exit_ = false;
- int64_t last_timestamp_ = -1;
+ bool enabled_ GUARDED_BY(mutex_) = false;
+ bool thread_exit_ GUARDED_BY(mutex_) = false;
+ std::optional<int64_t> last_timestamp_ GUARDED_BY(mutex_);
// Default to 60Hz refresh rate
static constexpr uint32_t kDefaultVSPeriodNs = 16666666;
- // Needs to be threadsafe.
- uint32_t vsync_period_ns_ = kDefaultVSPeriodNs;
- bool enable_vsync_timestamps_ = false;
- uint32_t last_vsync_timestamp_ = 0;
- std::optional<VsyncTimestampCallback> callback_;
+ uint32_t vsync_period_ns_ GUARDED_BY(mutex_) = kDefaultVSPeriodNs;
+ bool enable_vsync_timestamps_ GUARDED_BY(mutex_) = false;
+ bool last_timestamp_is_fresh_ GUARDED_BY(mutex_) = false;
+ std::optional<VsyncTimestampCallback> callback_ GUARDED_BY(mutex_);
std::condition_variable cv_;
std::thread vswt_;
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 13287ef..15a4c6e 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -216,7 +216,7 @@
case 2: // SDR
[[fallthrough]];
default:
- hdr_metadata_.reset();
+ hdr_metadata_ = std::make_shared<hdr_output_metadata>();
min_bpc_ = 6;
colorspace_ = Colorspace::kDefault;
}
@@ -355,9 +355,22 @@
return changed_layers;
}
+auto HwcDisplay::GetDisplayBoundsMm() -> std::pair<int32_t, int32_t> {
+
+ const auto bounds = GetEdid()->GetBoundsMm();
+ if (bounds.first > 0 || bounds.second > 0) {
+ return bounds;
+ }
+
+ ALOGE("Failed to get display bounds for d=%d\n", int(handle_));
+ // mm_width and mm_height are unreliable. so only provide mm_width to avoid
+ // wrong dpi computations or other use of the values.
+ return {configs_.mm_width, -1};
+}
+
auto HwcDisplay::AcceptValidatedComposition() -> void {
- for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) {
- l.second.AcceptTypeChange();
+ for (auto &[_, layer] : layers_) {
+ layer.AcceptTypeChange();
}
}
@@ -506,20 +519,18 @@
return SetActiveConfig(configs_.preferred_config_id);
}
-HWC2::Error HwcDisplay::CreateLayer(hwc2_layer_t *layer) {
- layers_.emplace(static_cast<hwc2_layer_t>(layer_idx_), HwcLayer(this));
- *layer = static_cast<hwc2_layer_t>(layer_idx_);
- ++layer_idx_;
- return HWC2::Error::None;
+auto HwcDisplay::CreateLayer(ILayerId new_layer_id) -> bool {
+ if (layers_.count(new_layer_id) > 0)
+ return false;
+
+ layers_.emplace(new_layer_id, HwcLayer(this));
+
+ return true;
}
-HWC2::Error HwcDisplay::DestroyLayer(hwc2_layer_t layer) {
- if (!get_layer(layer)) {
- return HWC2::Error::BadLayer;
- }
-
- layers_.erase(layer);
- return HWC2::Error::None;
+auto HwcDisplay::DestroyLayer(ILayerId layer_id) -> bool {
+ auto count = layers_.erase(layer_id);
+ return count != 0;
}
HWC2::Error HwcDisplay::GetActiveConfig(hwc2_config_t *config) const {
@@ -588,15 +599,25 @@
*value = hwc_config.mode.GetVSyncPeriodNs();
break;
case HWC2::Attribute::DpiY:
- // ideally this should be vdisplay/mm_heigth, however mm_height
- // comes from edid parsing and is highly unreliable. Viewing the
- // rarity of anisotropic displays, falling back to a single value
- // for dpi yield more correct output.
+ *value = GetEdid()->GetDpiY();
+ if (*value < 0) {
+ // default to raw mode DpiX for both x and y when no good value
+ // can be provided from edid.
+ *value = mm_width ? int(hwc_config.mode.GetRawMode().hdisplay *
+ kUmPerInch / mm_width)
+ : -1;
+ }
+ break;
case HWC2::Attribute::DpiX:
// Dots per 1000 inches
- *value = mm_width ? int(hwc_config.mode.GetRawMode().hdisplay *
- kUmPerInch / mm_width)
- : -1;
+ *value = GetEdid()->GetDpiX();
+ if (*value < 0) {
+ // default to raw mode DpiX for both x and y when no good value
+ // can be provided from edid.
+ *value = mm_width ? int(hwc_config.mode.GetRawMode().hdisplay *
+ kUmPerInch / mm_width)
+ : -1;
+ }
break;
#if __ANDROID_API__ > 29
case HWC2::Attribute::ConfigGroup:
@@ -760,15 +781,15 @@
bool use_client_layer = false;
uint32_t client_z_order = UINT32_MAX;
std::map<uint32_t, HwcLayer *> z_map;
- for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) {
- switch (l.second.GetValidatedType()) {
+ for (auto &[_, layer] : layers_) {
+ switch (layer.GetValidatedType()) {
case HWC2::Composition::Device:
- z_map.emplace(l.second.GetZOrder(), &l.second);
+ z_map.emplace(layer.GetZOrder(), &layer);
break;
case HWC2::Composition::Client:
// Place it at the z_order of the lowest client layer
use_client_layer = true;
- client_z_order = std::min(client_z_order, l.second.GetZOrder());
+ client_z_order = std::min(client_z_order, layer.GetZOrder());
break;
default:
continue;
@@ -842,7 +863,6 @@
}
if (new_vsync_period_ns) {
- vsync_worker_->SetVsyncPeriodNs(new_vsync_period_ns.value());
staged_mode_config_id_.reset();
vsync_worker_->SetVsyncTimestampTracking(false);
@@ -852,6 +872,7 @@
last_vsync_ts +
prev_vperiod_ns);
}
+ vsync_worker_->SetVsyncPeriodNs(new_vsync_period_ns.value());
}
return HWC2::Error::None;
@@ -1287,4 +1308,12 @@
backend_ = std::move(backend);
}
+bool HwcDisplay::NeedsClientLayerUpdate() const {
+ return std::any_of(layers_.begin(), layers_.end(), [](const auto &pair) {
+ const auto &layer = pair.second;
+ return layer.GetSfType() == HWC2::Composition::Client ||
+ layer.GetValidatedType() == HWC2::Composition::Client;
+ });
+}
+
} // namespace android
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index 94f3f14..b8c52fe 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -97,10 +97,12 @@
// Get the HwcDisplayConfig, or nullptor if none.
auto GetConfig(hwc2_config_t config_id) const -> const HwcDisplayConfig *;
+ auto GetDisplayBoundsMm() -> std::pair<int32_t, int32_t>;
+
// To be called after SetDisplayProperties. Returns an empty vector if the
// requested layers have been validated, otherwise the vector describes
// the requested composition type changes.
- using ChangedLayer = std::pair<hwc2_layer_t, HWC2::Composition>;
+ using ChangedLayer = std::pair<ILayerId, HWC2::Composition>;
auto ValidateStagedComposition() -> std::vector<ChangedLayer>;
// Mark previously validated properties as ready to present.
@@ -109,7 +111,7 @@
// Present previously staged properties, and return fences to indicate when
// the new content has been presented, and when the previous buffers have
// been released.
- using ReleaseFence = std::pair<hwc2_layer_t, SharedFd>;
+ using ReleaseFence = std::pair<ILayerId, SharedFd>;
auto PresentStagedComposition(SharedFd &out_present_fence,
std::vector<ReleaseFence> &out_release_fences)
-> bool;
@@ -122,9 +124,10 @@
frontend_private_data_ = std::move(data);
}
+ auto CreateLayer(ILayerId new_layer_id) -> bool;
+ auto DestroyLayer(ILayerId layer_id) -> bool;
+
// HWC2 Hooks - these should not be used outside of the hwc2 device.
- HWC2::Error CreateLayer(hwc2_layer_t *layer);
- HWC2::Error DestroyLayer(hwc2_layer_t layer);
HWC2::Error GetActiveConfig(hwc2_config_t *config) const;
HWC2::Error GetColorModes(uint32_t *num_modes, int32_t *modes);
HWC2::Error GetDisplayAttribute(hwc2_config_t config, int32_t attribute,
@@ -167,7 +170,7 @@
HWC2::Error SetColorTransform(const float *matrix, int32_t hint);
HWC2::Error SetPowerMode(int32_t mode);
HWC2::Error SetVsyncEnabled(int32_t enabled);
- HwcLayer *get_layer(hwc2_layer_t layer) {
+ HwcLayer *get_layer(ILayerId layer) {
auto it = layers_.find(layer);
if (it == layers_.end())
return nullptr;
@@ -200,7 +203,7 @@
return hwc_;
}
- std::map<hwc2_layer_t, HwcLayer> &layers() {
+ auto layers() -> std::map<ILayerId, HwcLayer> & {
return layers_;
}
@@ -245,6 +248,8 @@
auto getDisplayPhysicalOrientation() -> std::optional<PanelOrientation>;
+ bool NeedsClientLayerUpdate() const;
+
private:
AtomicCommitArgs CreateModesetCommit(
const HwcDisplayConfig *config,
@@ -268,9 +273,7 @@
const hwc2_display_t handle_;
HWC2::DisplayType type_;
- uint32_t layer_idx_{};
-
- std::map<hwc2_layer_t, HwcLayer> layers_;
+ std::map<ILayerId, HwcLayer> layers_;
HwcLayer client_layer_;
std::unique_ptr<HwcLayer> writeback_layer_;
uint16_t virtual_disp_width_{};
diff --git a/hwc2_device/HwcLayer.cpp b/hwc2_device/HwcLayer.cpp
index d0ff216..400ac9b 100644
--- a/hwc2_device/HwcLayer.cpp
+++ b/hwc2_device/HwcLayer.cpp
@@ -101,6 +101,10 @@
return;
}
+ if (slots_.count(*active_slot_id_) == 0) {
+ return;
+ }
+
layer_data_.bi = slots_[*active_slot_id_].bi;
layer_data_.fb = slots_[*active_slot_id_].fb;
diff --git a/hwc2_device/hwc2_device.cpp b/hwc2_device/hwc2_device.cpp
index 72d0aa9..c1c5157 100644
--- a/hwc2_device/hwc2_device.cpp
+++ b/hwc2_device/hwc2_device.cpp
@@ -53,6 +53,8 @@
public:
std::vector<HwcDisplay::ReleaseFence> release_fences;
std::vector<HwcDisplay::ChangedLayer> changed_layers;
+
+ int64_t next_layer_id = 1;
};
static auto GetHwc2DeviceDisplay(HwcDisplay &display)
@@ -267,6 +269,37 @@
}
/* Display functions */
+static int32_t CreateLayer(hwc2_device_t *device, hwc2_display_t display,
+ hwc2_layer_t *out_layer) {
+ ALOGV("CreateLayer");
+ LOCK_COMPOSER(device);
+ GET_DISPLAY(display);
+
+ auto hwc2display = GetHwc2DeviceDisplay(*idisplay);
+
+ if (!idisplay->CreateLayer(hwc2display->next_layer_id)) {
+ return static_cast<int32_t>(HWC2::Error::BadDisplay);
+ }
+
+ *out_layer = (hwc2_layer_t)hwc2display->next_layer_id;
+ hwc2display->next_layer_id++;
+
+ return 0;
+}
+
+static int32_t DestroyLayer(hwc2_device_t *device, hwc2_display_t display,
+ hwc2_layer_t layer) {
+ ALOGV("DestroyLayer");
+ LOCK_COMPOSER(device);
+ GET_DISPLAY(display);
+
+ if (!idisplay->DestroyLayer((ILayerId)layer)) {
+ return static_cast<int32_t>(HWC2::Error::BadLayer);
+ }
+
+ return 0;
+}
+
static int32_t GetDisplayRequests(hwc2_device_t * /*device*/,
hwc2_display_t /*display*/,
int32_t * /* out_display_requests */,
@@ -763,13 +796,9 @@
case HWC2::FunctionDescriptor::AcceptDisplayChanges:
return (hwc2_function_pointer_t)AcceptDisplayChanges;
case HWC2::FunctionDescriptor::CreateLayer:
- return ToHook<HWC2_PFN_CREATE_LAYER>(
- DisplayHook<decltype(&HwcDisplay::CreateLayer),
- &HwcDisplay::CreateLayer, hwc2_layer_t *>);
+ return (hwc2_function_pointer_t)CreateLayer;
case HWC2::FunctionDescriptor::DestroyLayer:
- return ToHook<HWC2_PFN_DESTROY_LAYER>(
- DisplayHook<decltype(&HwcDisplay::DestroyLayer),
- &HwcDisplay::DestroyLayer, hwc2_layer_t>);
+ return (hwc2_function_pointer_t)DestroyLayer;
case HWC2::FunctionDescriptor::GetActiveConfig:
return ToHook<HWC2_PFN_GET_ACTIVE_CONFIG>(
DisplayHook<decltype(&HwcDisplay::GetActiveConfig),
diff --git a/hwc3/Composer.cpp b/hwc3/Composer.cpp
index 28d096c..de875db 100644
--- a/hwc3/Composer.cpp
+++ b/hwc3/Composer.cpp
@@ -79,6 +79,10 @@
caps->emplace_back(Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
}
+#if __ANDROID_API__ >= 35
+ caps->emplace_back(Capability::LAYER_LIFECYCLE_BATCH_COMMAND);
+#endif
+
return ndk::ScopedAStatus::ok();
}
diff --git a/hwc3/ComposerClient.cpp b/hwc3/ComposerClient.cpp
index d6efe07..c7ec522 100644
--- a/hwc3/ComposerClient.cpp
+++ b/hwc3/ComposerClient.cpp
@@ -273,7 +273,7 @@
#endif
DisplayConfiguration HwcDisplayConfigToAidlConfiguration(
- const HwcDisplayConfigs& configs, const HwcDisplayConfig& config) {
+ int32_t width, int32_t height, const HwcDisplayConfig& config) {
DisplayConfiguration aidl_configuration =
{.configId = static_cast<int32_t>(config.id),
.width = config.mode.GetRawMode().hdisplay,
@@ -282,15 +282,14 @@
.vsyncPeriod = config.mode.GetVSyncPeriodNs(),
.hdrOutputType = static_cast<OutputType>(config.output_type)};
- if (configs.mm_width != 0) {
- // ideally this should be vdisplay/mm_heigth, however mm_height
- // comes from edid parsing and is highly unreliable. Viewing the
- // rarity of anisotropic displays, falling back to a single value
- // for dpi yield more correct output.
+ if (width > 0) {
static const float kMmPerInch = 25.4;
- float dpi = float(config.mode.GetRawMode().hdisplay) * kMmPerInch /
- float(configs.mm_width);
- aidl_configuration.dpi = {.x = dpi, .y = dpi};
+ float dpi_x = float(config.mode.GetRawMode().hdisplay) * kMmPerInch /
+ float(width);
+ float dpi_y = height <= 0 ? dpi_x :
+ float(config.mode.GetRawMode().vdisplay) * kMmPerInch /
+ float(height);
+ aidl_configuration.dpi = {.x = dpi_x, .y = dpi_y};
}
// TODO: Populate vrrConfig.
return aidl_configuration;
@@ -361,9 +360,14 @@
-> std::shared_ptr<Hwc3BufferHandle> {
auto hwc3 = std::shared_ptr<Hwc3BufferHandle>(new Hwc3BufferHandle());
- ::android::GraphicBufferMapper::get()
+ auto result = ::android::GraphicBufferMapper::get()
.importBufferNoValidate(handle, &hwc3->imported_handle_);
+ if (result != ::android::NO_ERROR) {
+ ALOGE("Failed to import buffer handle: %d", result);
+ return nullptr;
+ }
+
return hwc3;
}
@@ -407,17 +411,15 @@
auto bi = ::android::BufferInfoGetter::GetInstance()->GetBoInfo(
hwc3->GetHandle());
- if (!bi) {
- return std::nullopt;
+ if (bi) {
+ bi->fds_shared = hwc3;
+
+ lp.slot_buffer = {
+ .slot_id = slot_id,
+ .bi = bi,
+ };
}
- bi->fds_shared = hwc3;
-
- lp.slot_buffer = {
- .slot_id = slot_id,
- .bi = bi,
- };
-
lp.active_slot = {
.slot_id = slot_id,
.fence = std::move(fence_fd),
@@ -493,13 +495,16 @@
return ToBinderStatus(hwc3::Error::kBadDisplay);
}
- hwc2_layer_t hwc2_layer_id = 0;
- auto err = Hwc2toHwc3Error(display->CreateLayer(&hwc2_layer_id));
- if (err != hwc3::Error::kNone) {
- return ToBinderStatus(err);
+ auto hwc3display = DrmHwcThree::GetHwc3Display(*display);
+
+ if (!display->CreateLayer(hwc3display->next_layer_id)) {
+ return ToBinderStatus(hwc3::Error::kBadDisplay);
}
- *layer_id = Hwc2LayerToHwc3(hwc2_layer_id);
+ *layer_id = hwc3display->next_layer_id;
+
+ hwc3display->next_layer_id++;
+
return ndk::ScopedAStatus::ok();
}
@@ -533,12 +538,11 @@
return ToBinderStatus(hwc3::Error::kBadDisplay);
}
- auto err = Hwc2toHwc3Error(display->DestroyLayer(Hwc3LayerToHwc2(layer_id)));
- if (err != hwc3::Error::kNone) {
- return ToBinderStatus(err);
+ if (!display->DestroyLayer(layer_id)) {
+ return ToBinderStatus(hwc3::Error::kBadLayer);
}
- return ToBinderStatus(err);
+ return ToBinderStatus(hwc3::Error::kNone);
}
ndk::ScopedAStatus ComposerClient::destroyVirtualDisplay(int64_t display_id) {
@@ -560,6 +564,24 @@
return;
}
+#if __ANDROID_API__ >= 35
+ auto batch_command = command.layerLifecycleBatchCommandType;
+ if (batch_command == LayerLifecycleBatchCommandType::CREATE) {
+ if (!display->CreateLayer(command.layer)) {
+ cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
+ return;
+ }
+ }
+
+ if (batch_command == LayerLifecycleBatchCommandType::DESTROY) {
+ if (!display->DestroyLayer(command.layer)) {
+ cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
+ }
+
+ return;
+ }
+#endif
+
auto* layer = display->get_layer(command.layer);
if (layer == nullptr) {
cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
@@ -695,13 +717,14 @@
display->SetColorTransformMatrix(ctm.value());
}
+ bool shall_present_now = false;
+
+ DisplayChanges changes{};
if (command.validateDisplay || command.presentOrValidateDisplay) {
std::vector<HwcDisplay::ChangedLayer>
changed_layers = display->ValidateStagedComposition();
- DisplayChanges changes{};
for (auto [layer_id, composition_type] : changed_layers) {
- changes.AddLayerCompositionChange(command.display,
- Hwc2LayerToHwc3(layer_id),
+ changes.AddLayerCompositionChange(command.display, layer_id,
static_cast<Composition>(
composition_type));
}
@@ -710,21 +733,23 @@
hwc3_display->must_validate = false;
// TODO: DisplayRequests are not implemented.
+ }
- /* TODO: Add check if it's possible to skip display validation for
- * presentOrValidateDisplay */
- if (command.presentOrValidateDisplay) {
- cmd_result_writer_
- ->AddPresentOrValidateResult(display_id,
- PresentOrValidate::Result::Validated);
+ if (command.presentOrValidateDisplay) {
+ auto result = PresentOrValidate::Result::Validated;
+ if (!display->NeedsClientLayerUpdate() && !changes.HasAnyChanges()) {
+ ALOGV("Skipping SF roundtrip for display %" PRId64, display_id);
+ result = PresentOrValidate::Result::Presented;
+ shall_present_now = true;
}
+ cmd_result_writer_->AddPresentOrValidateResult(display_id, result);
}
if (command.acceptDisplayChanges) {
display->AcceptValidatedComposition();
}
- if (command.presentDisplay) {
+ if (command.presentDisplay || shall_present_now) {
auto hwc3_display = DrmHwcThree::GetHwc3Display(*display);
if (hwc3_display->must_validate) {
cmd_result_writer_->AddError(hwc3::Error::kNotValidated);
@@ -745,8 +770,7 @@
std::unordered_map<int64_t, unique_fd> hal_release_fences;
for (const auto& [layer_id, release_fence] : release_fences) {
- hal_release_fences[Hwc2LayerToHwc3(layer_id)] = //
- unique_fd(::android::DupFd(release_fence));
+ hal_release_fences[layer_id] = unique_fd(::android::DupFd(release_fence));
}
cmd_result_writer_->AddReleaseFence(display_id, hal_release_fences);
}
@@ -844,9 +868,11 @@
return ToBinderStatus(hwc3::Error::kBadConfig);
}
- DisplayConfiguration
- aidl_configuration = HwcDisplayConfigToAidlConfiguration(configs,
- config->second);
+ const auto bounds = display->GetDisplayBoundsMm();
+ DisplayConfiguration aidl_configuration =
+ HwcDisplayConfigToAidlConfiguration(/*width =*/ bounds.first,
+ /*height =*/bounds.second,
+ config->second);
// Legacy API for querying DPI uses units of dots per 1000 inches.
static const int kLegacyDpiUnit = 1000;
switch (attribute) {
@@ -1427,9 +1453,12 @@
}
const HwcDisplayConfigs& configs = display->GetDisplayConfigs();
+ const auto bounds = display->GetDisplayBoundsMm();
for (const auto& [id, config] : configs.hwc_configs) {
configurations->push_back(
- HwcDisplayConfigToAidlConfiguration(configs, config));
+ HwcDisplayConfigToAidlConfiguration(/*width =*/ bounds.first,
+ /*height =*/ bounds.second,
+ config));
}
return ndk::ScopedAStatus::ok();
}
diff --git a/hwc3/DrmHwcThree.h b/hwc3/DrmHwcThree.h
index 44168aa..10470ea 100644
--- a/hwc3/DrmHwcThree.h
+++ b/hwc3/DrmHwcThree.h
@@ -26,6 +26,8 @@
class Hwc3Display : public ::android::FrontendDisplayBase {
public:
bool must_validate = false;
+
+ int64_t next_layer_id = 1;
};
class DrmHwcThree : public ::android::DrmHwc {
diff --git a/hwc3/Utils.h b/hwc3/Utils.h
index aa956f6..89767c8 100644
--- a/hwc3/Utils.h
+++ b/hwc3/Utils.h
@@ -58,12 +58,6 @@
return ToBinderStatus(Hwc2toHwc3Error(error));
}
-// ID conversion. HWC2 uses typedef'd unsigned integer types while HWC3 uses
-// signed integer types. static_cast in between these.
-inline int64_t Hwc2LayerToHwc3(hwc2_layer_t layer) {
- return static_cast<int64_t>(layer);
-}
-
inline int64_t Hwc2DisplayToHwc3(hwc2_display_t display) {
return static_cast<int64_t>(display);
}
@@ -72,10 +66,6 @@
return static_cast<int32_t>(config_id);
}
-inline hwc2_layer_t Hwc3LayerToHwc2(int64_t layer) {
- return static_cast<hwc2_layer_t>(layer);
-}
-
inline hwc2_display_t Hwc3DisplayToHwc2(int64_t display) {
return static_cast<hwc2_display_t>(display);
}
diff --git a/utils/EdidWrapper.h b/utils/EdidWrapper.h
index 30124d7..651c284 100644
--- a/utils/EdidWrapper.h
+++ b/utils/EdidWrapper.h
@@ -18,6 +18,7 @@
#if HAS_LIBDISPLAY_INFO
extern "C" {
+#include <libdisplay-info/edid.h>
#include <libdisplay-info/info.h>
}
#endif
@@ -40,14 +41,24 @@
types.clear();
};
virtual void GetHdrCapabilities(std::vector<ui::Hdr> &types,
- const float * /*max_luminance*/,
- const float * /*max_average_luminance*/,
- const float * /*min_luminance*/) {
+ float * /*max_luminance*/,
+ float * /*max_average_luminance*/,
+ float * /*min_luminance*/) {
GetSupportedHdrTypes(types);
};
virtual void GetColorModes(std::vector<Colormode> &color_modes) {
color_modes.clear();
};
+ virtual int GetDpiX() {
+ return -1;
+ }
+ virtual int GetDpiY() {
+ return -1;
+ }
+
+ virtual auto GetBoundsMm() -> std::pair<int32_t, int32_t> {
+ return {-1, -1};
+ }
};
#if HAS_LIBDISPLAY_INFO
@@ -64,16 +75,23 @@
void GetSupportedHdrTypes(std::vector<ui::Hdr> &types) override;
void GetHdrCapabilities(std::vector<ui::Hdr> &types,
- const float *max_luminance,
- const float *max_average_luminance,
- const float *min_luminance) override;
+ float *max_luminance,
+ float *max_average_luminance,
+ float *min_luminance) override;
void GetColorModes(std::vector<Colormode> &color_modes) override;
+ auto GetDpiX() -> int override;
+ auto GetDpiY() -> int override;
+
+ auto GetBoundsMm() -> std::pair<int32_t, int32_t> override;
+
private:
LibdisplayEdidWrapper(di_info *info) : info_(std::move(info)) {
}
+ std::pair<int32_t, int32_t> GetDpi();
+
di_info *info_{};
};
#endif
diff --git a/utils/LibdisplayEdidWrapper.cpp b/utils/LibdisplayEdidWrapper.cpp
index d2d2a1c..ad737a2 100644
--- a/utils/LibdisplayEdidWrapper.cpp
+++ b/utils/LibdisplayEdidWrapper.cpp
@@ -53,15 +53,15 @@
}
void LibdisplayEdidWrapper::GetHdrCapabilities(
- std::vector<ui::Hdr> &types, const float *max_luminance,
- const float *max_average_luminance, const float *min_luminance) {
+ std::vector<ui::Hdr> &types, float *max_luminance,
+ float *max_average_luminance, float *min_luminance) {
GetSupportedHdrTypes(types);
const auto *hdr_static_meta = di_info_get_hdr_static_metadata(info_);
- max_luminance = &hdr_static_meta->desired_content_max_luminance;
- max_average_luminance = &hdr_static_meta
+ *max_luminance = hdr_static_meta->desired_content_max_luminance;
+ *max_average_luminance = hdr_static_meta
->desired_content_max_frame_avg_luminance;
- min_luminance = &hdr_static_meta->desired_content_min_luminance;
+ *min_luminance = hdr_static_meta->desired_content_min_luminance;
}
void LibdisplayEdidWrapper::GetColorModes(std::vector<Colormode> &color_modes) {
@@ -95,5 +95,51 @@
}
}
+auto LibdisplayEdidWrapper::GetDpiX() -> int {
+ return GetDpi().first;
+}
+
+auto LibdisplayEdidWrapper::GetDpiY() -> int {
+ return GetDpi().second;
+}
+
+auto LibdisplayEdidWrapper::GetBoundsMm() -> std::pair<int32_t, int32_t> {
+ const auto edid = di_info_get_edid(info_);
+ const auto detailed_timing_defs = di_edid_get_detailed_timing_defs(edid);
+ const auto dtd = detailed_timing_defs[0];
+ if (dtd == nullptr || dtd->horiz_image_mm == 0 || dtd->vert_image_mm == 0) {
+ // try to fallback on display size if no dtd.
+ // However since edid screen size are vastly unreliable only provide a valid
+ // width to avoid invalid dpi computation.
+ const auto screen_size = di_edid_get_screen_size(edid);
+ return {screen_size->width_cm * 10, -1};
+ }
+
+ return {dtd->horiz_image_mm, dtd->vert_image_mm};
+}
+
+auto LibdisplayEdidWrapper::GetDpi() -> std::pair<int32_t, int32_t> {
+ static const int32_t kUmPerInch = 25400;
+ const auto edid = di_info_get_edid(info_);
+ const auto detailed_timing_defs = di_edid_get_detailed_timing_defs(edid);
+ const auto dtd = detailed_timing_defs[0];
+ if (dtd == nullptr || dtd->horiz_image_mm == 0 || dtd->vert_image_mm == 0) {
+ // try to fallback on display size if no dtd.
+ const auto screen_size = di_edid_get_screen_size(edid);
+ const auto standard_timings = di_edid_get_standard_timings(edid);
+ if (screen_size->width_cm <= 0 || standard_timings == nullptr) {
+ return {-1, -1};
+ }
+
+ // display size is more unreliable so use only horizontal dpi.
+ int32_t horiz_video = standard_timings[0]->horiz_video;
+ int32_t dpi = horiz_video * kUmPerInch / (screen_size->width_cm * 10);
+ return {dpi, dpi};
+ }
+
+ return {dtd->horiz_video * kUmPerInch / dtd->horiz_image_mm,
+ dtd->vert_video * kUmPerInch / dtd->vert_image_mm};
+}
+
} // namespace android
#endif
diff --git a/utils/thread_annotations.h b/utils/thread_annotations.h
new file mode 100644
index 0000000..f3bd2e0
--- /dev/null
+++ b/utils/thread_annotations.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2025 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
+
+// Enable thread safety attributes only with clang.
+#if defined(__clang__)
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
+#endif
+
+#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define RELEASE_GENERIC(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS \
+ THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
\ No newline at end of file