Merge "Sync with new API to start HDCP" into main
diff --git a/Android.bp b/Android.bp
index 7ff063d..24d4d99 100644
--- a/Android.bp
+++ b/Android.bp
@@ -118,6 +118,7 @@
"hwc2_device/hwc2_device.cpp",
"utils/fd.cpp",
+ "utils/properties.cpp",
],
}
diff --git a/compositor/DisplayInfo.h b/compositor/DisplayInfo.h
index bbcbff8..6ddc66f 100644
--- a/compositor/DisplayInfo.h
+++ b/compositor/DisplayInfo.h
@@ -46,3 +46,12 @@
kModePanelOrientationLeftUp,
kModePanelOrientationRightUp
};
+
+struct QueuedConfigTiming {
+ // In order for the new config to be applied, the client must send a new frame
+ // at this time.
+ int64_t refresh_time_ns;
+
+ // The time when the display will start to refresh at the new vsync period.
+ int64_t new_vsync_time_ns;
+};
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp
index bb26189..9a8769a 100644
--- a/drm/DrmAtomicStateManager.cpp
+++ b/drm/DrmAtomicStateManager.cpp
@@ -101,7 +101,7 @@
}
}
- bool nonblock = true;
+ bool nonblock = !args.blocking;
if (args.active) {
nonblock = false;
diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h
index 20896ed..8d22b99 100644
--- a/drm/DrmAtomicStateManager.h
+++ b/drm/DrmAtomicStateManager.h
@@ -33,6 +33,7 @@
struct AtomicCommitArgs {
/* inputs. All fields are optional, but at least one has to be specified */
bool test_only = false;
+ bool blocking = false;
std::optional<DrmMode> display_mode;
std::optional<bool> active;
std::shared_ptr<DrmKmsPlan> composition;
diff --git a/drm/DrmHwc.cpp b/drm/DrmHwc.cpp
index 57293c2..aaba506 100644
--- a/drm/DrmHwc.cpp
+++ b/drm/DrmHwc.cpp
@@ -22,6 +22,7 @@
#include "backend/Backend.h"
#include "utils/log.h"
+#include "utils/properties.h"
namespace android {
@@ -196,6 +197,13 @@
}
uint32_t DrmHwc::GetMaxVirtualDisplayCount() {
+ /* Virtual display is an experimental feature.
+ * Unless explicitly set to true, return 0 for no support.
+ */
+ if (0 == property_get_bool("vendor.hwc.drm.enable_virtual_display", 0)) {
+ 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 */
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 6797c56..43564d4 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -21,6 +21,11 @@
#include <cinttypes>
+#include <hardware/gralloc.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+#include <ui/PixelFormat.h>
+
#include "backend/Backend.h"
#include "backend/BackendManager.h"
#include "bufferinfo/BufferInfoGetter.h"
@@ -35,6 +40,71 @@
namespace android {
+namespace {
+// Allocate a black buffer that can be used for an initial modeset when there.
+// is no appropriate client buffer available to be used.
+// Caller must free the returned buffer with GraphicBufferAllocator::free.
+auto GetModesetBuffer(uint32_t width, uint32_t height) -> buffer_handle_t {
+ constexpr PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+ constexpr uint64_t usage = GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
+
+ constexpr uint32_t layer_count = 1;
+ const std::string name = "drm-hwcomposer";
+
+ buffer_handle_t handle = nullptr;
+ uint32_t stride = 0;
+ status_t status = GraphicBufferAllocator::get().allocate(width, height,
+ format, layer_count,
+ usage, &handle,
+ &stride, name);
+ if (status != OK) {
+ ALOGE("Failed to allocate modeset buffer.");
+ return nullptr;
+ }
+
+ void *data = nullptr;
+ Rect bounds = {0, 0, static_cast<int32_t>(width),
+ static_cast<int32_t>(height)};
+ status = GraphicBufferMapper::get().lock(handle, usage, bounds, &data);
+ if (status != OK) {
+ ALOGE("Failed to map modeset buffer.");
+ GraphicBufferAllocator::get().free(handle);
+ return nullptr;
+ }
+
+ // Cast one of the multiplicands to ensure that the multiplication happens
+ // in a wider type (size_t).
+ const size_t buffer_size = static_cast<size_t>(height) * stride *
+ bytesPerPixel(format);
+ memset(data, 0, buffer_size);
+ status = GraphicBufferMapper::get().unlock(handle);
+ ALOGW_IF(status != OK, "Failed to unmap buffer.");
+ return handle;
+}
+
+auto GetModesetLayerProperties(buffer_handle_t buffer, uint32_t width,
+ uint32_t height) -> HwcLayer::LayerProperties {
+ HwcLayer::LayerProperties properties;
+ properties.buffer = {.buffer_handle = buffer, .acquire_fence = {}};
+ properties.display_frame = {
+ .left = 0,
+ .top = 0,
+ .right = int(width),
+ .bottom = int(height),
+ };
+ properties.source_crop = (hwc_frect_t){
+ .left = 0.0F,
+ .top = 0.0F,
+ .right = static_cast<float>(width),
+ .bottom = static_cast<float>(height),
+ };
+ properties.blend_mode = BufferBlendMode::kNone;
+ return properties;
+}
+} // namespace
+
std::string HwcDisplay::DumpDelta(HwcDisplay::Stats delta) {
if (delta.total_pixops_ == 0)
return "No stats yet";
@@ -96,6 +166,109 @@
Deinit();
};
+auto HwcDisplay::GetConfig(hwc2_config_t config_id) const
+ -> const HwcDisplayConfig * {
+ auto config_iter = configs_.hwc_configs.find(config_id);
+ if (config_iter == configs_.hwc_configs.end()) {
+ return nullptr;
+ }
+ return &config_iter->second;
+}
+
+auto HwcDisplay::GetCurrentConfig() const -> const HwcDisplayConfig * {
+ return GetConfig(configs_.active_config_id);
+}
+
+auto HwcDisplay::GetLastRequestedConfig() const -> const HwcDisplayConfig * {
+ return GetConfig(staged_mode_config_id_.value_or(configs_.active_config_id));
+}
+
+HwcDisplay::ConfigError HwcDisplay::SetConfig(hwc2_config_t config) {
+ const HwcDisplayConfig *new_config = GetConfig(config);
+ if (new_config == nullptr) {
+ ALOGE("Could not find active mode for %u", config);
+ return ConfigError::kBadConfig;
+ }
+
+ const HwcDisplayConfig *current_config = GetCurrentConfig();
+
+ const uint32_t width = new_config->mode.GetRawMode().hdisplay;
+ const uint32_t height = new_config->mode.GetRawMode().hdisplay;
+
+ std::optional<LayerData> modeset_layer_data;
+ // If a client layer has already been provided, and its size matches the
+ // new config, use it for the modeset.
+ if (client_layer_.IsLayerUsableAsDevice() && current_config &&
+ current_config->mode.GetRawMode().hdisplay == width &&
+ current_config->mode.GetRawMode().vdisplay == height) {
+ ALOGV("Use existing client_layer for blocking config.");
+ modeset_layer_data = client_layer_.GetLayerData();
+ } else {
+ ALOGV("Allocate modeset buffer.");
+ buffer_handle_t modeset_buffer = GetModesetBuffer(width, height);
+ if (modeset_buffer != nullptr) {
+ auto modeset_layer = std::make_unique<HwcLayer>(this);
+ modeset_layer->SetLayerProperties(
+ GetModesetLayerProperties(modeset_buffer, width, height));
+ modeset_layer->PopulateLayerData();
+ modeset_layer_data = modeset_layer->GetLayerData();
+ GraphicBufferAllocator::get().free(modeset_buffer);
+ }
+ }
+
+ ALOGV("Create modeset commit.");
+ // Create atomic commit args for a blocking modeset. There's no need to do a
+ // separate test commit, since the commit does a test anyways.
+ AtomicCommitArgs commit_args = CreateModesetCommit(new_config,
+ modeset_layer_data);
+ commit_args.blocking = true;
+ int ret = GetPipe().atomic_state_manager->ExecuteAtomicCommit(commit_args);
+
+ if (ret) {
+ ALOGE("Blocking config failed: %d", ret);
+ return HwcDisplay::ConfigError::kBadConfig;
+ }
+
+ ALOGV("Blocking config succeeded.");
+ configs_.active_config_id = config;
+ return ConfigError::kNone;
+}
+
+auto HwcDisplay::QueueConfig(hwc2_config_t config, int64_t desired_time,
+ bool seamless, QueuedConfigTiming *out_timing)
+ -> ConfigError {
+ if (configs_.hwc_configs.count(config) == 0) {
+ ALOGE("Could not find active mode for %u", config);
+ return ConfigError::kBadConfig;
+ }
+
+ // TODO: Add support for seamless configuration changes.
+ if (seamless) {
+ return ConfigError::kSeamlessNotAllowed;
+ }
+
+ // Request a refresh from the client one vsync period before the desired
+ // time, or simply at the desired time if there is no active configuration.
+ const HwcDisplayConfig *current_config = GetCurrentConfig();
+ out_timing->refresh_time_ns = desired_time -
+ (current_config
+ ? current_config->mode.GetVSyncPeriodNs()
+ : 0);
+ out_timing->new_vsync_time_ns = desired_time;
+
+ // Queue the config change timing to be consistent with the requested
+ // refresh time.
+ staged_mode_change_time_ = out_timing->refresh_time_ns;
+ staged_mode_config_id_ = config;
+
+ // Enable vsync events until the mode has been applied.
+ last_vsync_ts_ = 0;
+ vsync_tracking_en_ = true;
+ vsync_worker_->VSyncControl(true);
+
+ return ConfigError::kNone;
+}
+
void HwcDisplay::SetPipeline(std::shared_ptr<DrmDisplayPipeline> pipeline) {
Deinit();
@@ -114,22 +287,9 @@
AtomicCommitArgs a_args{};
a_args.composition = std::make_shared<DrmKmsPlan>();
GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args);
-/*
- * TODO:
- * Unfortunately the following causes regressions on db845c
- * with VtsHalGraphicsComposerV2_3TargetTest due to the display
- * never coming back. Patches to avoiding that issue on the
- * the kernel side unfortunately causes further crashes in
- * drm_hwcomposer, because the client detach takes longer then the
- * 1 second max VTS expects. So for now as a workaround, lets skip
- * deactivating the display on deinit, which matches previous
- * behavior prior to commit d0494d9b8097
- */
-#if 0
a_args.composition = {};
a_args.active = false;
GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args);
-#endif
current_plan_.reset();
backend_.reset();
@@ -257,10 +417,12 @@
}
HWC2::Error HwcDisplay::GetActiveConfig(hwc2_config_t *config) const {
- if (configs_.hwc_configs.count(staged_mode_config_id_) == 0)
+ // If a config has been queued, it is considered the "active" config.
+ const HwcDisplayConfig *hwc_config = GetLastRequestedConfig();
+ if (hwc_config == nullptr)
return HWC2::Error::BadConfig;
- *config = staged_mode_config_id_;
+ *config = hwc_config->id;
return HWC2::Error::None;
}
@@ -479,6 +641,34 @@
return HWC2::Error::None;
}
+AtomicCommitArgs HwcDisplay::CreateModesetCommit(
+ const HwcDisplayConfig *config,
+ const std::optional<LayerData> &modeset_layer) {
+ AtomicCommitArgs args{};
+
+ args.color_matrix = color_matrix_;
+ args.content_type = content_type_;
+ args.colorspace = colorspace_;
+
+ std::vector<LayerData> composition_layers;
+ if (modeset_layer) {
+ composition_layers.emplace_back(modeset_layer.value());
+ }
+
+ if (composition_layers.empty()) {
+ ALOGW("Attempting to create a modeset commit without a layer.");
+ }
+
+ args.display_mode = config->mode;
+ args.active = true;
+ args.composition = DrmKmsPlan::CreateDrmKmsPlan(GetPipe(),
+ std::move(
+ composition_layers));
+ ALOGW_IF(!args.composition, "No composition for blocking modeset");
+
+ return args;
+}
+
HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
if (IsInHeadlessMode()) {
ALOGE("%s: Display is in headless mode, should never reach here", __func__);
@@ -493,17 +683,22 @@
GetDisplayVsyncPeriod(&prev_vperiod_ns);
auto mode_update_commited_ = false;
- if (staged_mode_ &&
+ if (staged_mode_config_id_ &&
staged_mode_change_time_ <= ResourceManager::GetTimeMonotonicNs()) {
+ const HwcDisplayConfig *staged_config = GetConfig(
+ staged_mode_config_id_.value());
+ if (staged_config == nullptr) {
+ return HWC2::Error::BadConfig;
+ }
client_layer_.SetLayerDisplayFrame(
(hwc_rect_t){.left = 0,
.top = 0,
- .right = int(staged_mode_->GetRawMode().hdisplay),
- .bottom = int(staged_mode_->GetRawMode().vdisplay)});
+ .right = int(staged_config->mode.GetRawMode().hdisplay),
+ .bottom = int(staged_config->mode.GetRawMode().vdisplay)});
- configs_.active_config_id = staged_mode_config_id_;
+ configs_.active_config_id = staged_mode_config_id_.value();
- a_args.display_mode = *staged_mode_;
+ a_args.display_mode = staged_config->mode;
if (!a_args.test_only) {
mode_update_commited_ = true;
}
@@ -568,9 +763,7 @@
}
if (!current_plan_) {
- if (!a_args.test_only) {
- ALOGE("Failed to create DrmKmsPlan");
- }
+ ALOGE_IF(!a_args.test_only, "Failed to create DrmKmsPlan");
return HWC2::Error::BadConfig;
}
@@ -579,13 +772,12 @@
auto ret = GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args);
if (ret) {
- if (!a_args.test_only)
- ALOGE("Failed to apply the frame composition ret=%d", ret);
+ ALOGE_IF(!a_args.test_only, "Failed to apply the frame composition ret=%d", ret);
return HWC2::Error::BadParameter;
}
if (mode_update_commited_) {
- staged_mode_.reset();
+ staged_mode_config_id_.reset();
vsync_tracking_en_ = false;
if (last_vsync_ts_ != 0) {
hwc_->SendVsyncPeriodTimingChangedEventToClient(handle_,
@@ -630,6 +822,7 @@
color_matrix_ = {};
++frame_no_;
+
return HWC2::Error::None;
}
@@ -640,7 +833,6 @@
return HWC2::Error::BadConfig;
}
- staged_mode_ = configs_.hwc_configs[config].mode;
staged_mode_change_time_ = change_time;
staged_mode_config_id_ = config;
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index 4680ca9..ecca514 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -41,6 +41,13 @@
// NOLINTNEXTLINE
class HwcDisplay {
public:
+ enum ConfigError {
+ kNone,
+ kBadConfig,
+ kSeamlessNotAllowed,
+ kSeamlessNotPossible
+ };
+
HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type, DrmHwc *hwc);
HwcDisplay(const HwcDisplay &) = delete;
~HwcDisplay();
@@ -59,6 +66,26 @@
return configs_;
}
+ // Get the config representing the mode that has been committed to KMS.
+ auto GetCurrentConfig() const -> const HwcDisplayConfig *;
+
+ // Get the config that was last requested through SetActiveConfig and similar
+ // functions. This may differ from the GetCurrentConfig if the config change
+ // is queued up to take effect in the future.
+ auto GetLastRequestedConfig() const -> const HwcDisplayConfig *;
+
+ // Set a config synchronously. If the requested config fails to be committed,
+ // this will return with an error. Otherwise, the config will have been
+ // committed to the kernel on successful return.
+ ConfigError SetConfig(hwc2_config_t config);
+
+ // Queue a configuration change to take effect in the future.
+ auto QueueConfig(hwc2_config_t config, int64_t desired_time, bool seamless,
+ QueuedConfigTiming *out_timing) -> ConfigError;
+
+ // Get the HwcDisplayConfig, or nullptor if none.
+ auto GetConfig(hwc2_config_t config_id) const -> const HwcDisplayConfig *;
+
// HWC2 Hooks - these should not be used outside of the hwc2 device.
HWC2::Error AcceptDisplayChanges();
HWC2::Error CreateLayer(hwc2_layer_t *layer);
@@ -201,15 +228,18 @@
auto getDisplayPhysicalOrientation() -> std::optional<PanelOrientation>;
private:
+ AtomicCommitArgs CreateModesetCommit(
+ const HwcDisplayConfig *config,
+ const std::optional<LayerData> &modeset_layer);
+
HwcDisplayConfigs configs_;
DrmHwc *const hwc_;
SharedFd present_fence_;
- std::optional<DrmMode> staged_mode_;
int64_t staged_mode_change_time_{};
- uint32_t staged_mode_config_id_{};
+ std::optional<uint32_t> staged_mode_config_id_{};
std::shared_ptr<DrmDisplayPipeline> pipeline_;
diff --git a/hwc3/Composer.cpp b/hwc3/Composer.cpp
index 4977a14..124380d 100644
--- a/hwc3/Composer.cpp
+++ b/hwc3/Composer.cpp
@@ -25,6 +25,7 @@
#include "hwc3/ComposerClient.h"
#include "hwc3/Utils.h"
#include "utils/log.h"
+#include "utils/properties.h"
namespace aidl::android::hardware::graphics::composer3::impl {
@@ -71,6 +72,11 @@
DEBUG_FUNC();
/* No capabilities advertised */
caps->clear();
+
+ if (Properties::IsPresentFenceNotReliable()) {
+ caps->emplace_back(Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
+ }
+
return ndk::ScopedAStatus::ok();
}
diff --git a/hwc3/ComposerClient.cpp b/hwc3/ComposerClient.cpp
index 8320997..124f5ea 100644
--- a/hwc3/ComposerClient.cpp
+++ b/hwc3/ComposerClient.cpp
@@ -109,6 +109,8 @@
case static_cast<int32_t>(
common::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE):
return BufferColorSpace::kItuRec2020;
+ case static_cast<int32_t>(common::Dataspace::UNKNOWN):
+ return BufferColorSpace::kUndefined;
default:
ALOGE("Unsupported standard: %d", standard);
return std::nullopt;
@@ -128,6 +130,8 @@
return BufferSampleRange::kFullRange;
case static_cast<int32_t>(common::Dataspace::RANGE_LIMITED):
return BufferSampleRange::kLimitedRange;
+ case static_cast<int32_t>(common::Dataspace::UNKNOWN):
+ return BufferSampleRange::kUndefined;
default:
ALOGE("Unsupported sample range: %d", sample_range);
return std::nullopt;
@@ -603,11 +607,16 @@
return;
}
+ if (command.brightness) {
+ // TODO: Implement support for display brightness.
+ cmd_result_writer_->AddError(hwc3::Error::kUnsupported);
+ return;
+ }
+
for (const auto& layer_cmd : command.layers) {
DispatchLayerCommand(command.display, layer_cmd);
}
- // TODO: Implement support for display brightness.
if (command.colorTransformMatrix) {
ExecuteSetDisplayColorTransform(command.display,
*command.colorTransformMatrix);
@@ -650,7 +659,7 @@
}
ndk::ScopedAStatus ComposerClient::getActiveConfig(int64_t display_id,
- int32_t* config) {
+ int32_t* config_id) {
DEBUG_FUNC();
const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
HwcDisplay* display = GetDisplay(display_id);
@@ -658,13 +667,12 @@
return ToBinderStatus(hwc3::Error::kBadDisplay);
}
- uint32_t hwc2_config = 0;
- const hwc3::Error error = Hwc2toHwc3Error(
- display->GetActiveConfig(&hwc2_config));
- if (error != hwc3::Error::kNone) {
- return ToBinderStatus(error);
+ const HwcDisplayConfig* config = display->GetLastRequestedConfig();
+ if (config == nullptr) {
+ return ToBinderStatus(hwc3::Error::kBadConfig);
}
- *config = Hwc2ConfigIdToHwc3(hwc2_config);
+
+ *config_id = Hwc2ConfigIdToHwc3(config->id);
return ndk::ScopedAStatus::ok();
}
@@ -887,14 +895,15 @@
return ToBinderStatus(hwc3::Error::kBadDisplay);
}
- uint32_t hwc2_vsync_period = 0;
- auto error = Hwc2toHwc3Error(
- display->GetDisplayVsyncPeriod(&hwc2_vsync_period));
- if (error != hwc3::Error::kNone) {
- return ToBinderStatus(error);
+ // getDisplayVsyncPeriod should return the vsync period of the config that
+ // is currently committed to the kernel. If a config change is pending due to
+ // setActiveConfigWithConstraints, return the pre-change vsync period.
+ const HwcDisplayConfig* config = display->GetCurrentConfig();
+ if (config == nullptr) {
+ return ToBinderStatus(hwc3::Error::kBadConfig);
}
- *vsync_period = static_cast<int32_t>(hwc2_vsync_period);
+ *vsync_period = config->mode.GetVSyncPeriodNs();
return ndk::ScopedAStatus::ok();
}
@@ -1055,13 +1064,14 @@
ndk::ScopedAStatus ComposerClient::setActiveConfig(int64_t display_id,
int32_t config) {
DEBUG_FUNC();
- const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
- HwcDisplay* display = GetDisplay(display_id);
- if (display == nullptr) {
- return ToBinderStatus(hwc3::Error::kBadDisplay);
- }
- return ToBinderStatus(Hwc2toHwc3Error(display->SetActiveConfig(config)));
+ VsyncPeriodChangeTimeline timeline;
+ VsyncPeriodChangeConstraints constraints = {
+ .desiredTimeNanos = ::android::ResourceManager::GetTimeMonotonicNs(),
+ .seamlessRequired = false,
+ };
+ return setActiveConfigWithConstraints(display_id, config, constraints,
+ &timeline);
}
ndk::ScopedAStatus ComposerClient::setActiveConfigWithConstraints(
@@ -1075,23 +1085,47 @@
return ToBinderStatus(hwc3::Error::kBadDisplay);
}
- hwc_vsync_period_change_constraints_t hwc2_constraints;
- hwc2_constraints.desiredTimeNanos = constraints.desiredTimeNanos;
- hwc2_constraints.seamlessRequired = static_cast<uint8_t>(
- constraints.seamlessRequired);
-
- hwc_vsync_period_change_timeline_t hwc2_timeline{};
- auto error = Hwc2toHwc3Error(
- display->SetActiveConfigWithConstraints(config, &hwc2_constraints,
- &hwc2_timeline));
- if (error != hwc3::Error::kNone) {
- return ToBinderStatus(error);
+ if (constraints.seamlessRequired) {
+ return ToBinderStatus(hwc3::Error::kSeamlessNotAllowed);
}
- timeline->refreshTimeNanos = hwc2_timeline.refreshTimeNanos;
- timeline->newVsyncAppliedTimeNanos = hwc2_timeline.newVsyncAppliedTimeNanos;
- timeline->refreshRequired = static_cast<bool>(hwc2_timeline.refreshRequired);
- return ndk::ScopedAStatus::ok();
+ const bool future_config = constraints.desiredTimeNanos >
+ ::android::ResourceManager::GetTimeMonotonicNs();
+ const HwcDisplayConfig* current_config = display->GetCurrentConfig();
+ const HwcDisplayConfig* next_config = display->GetConfig(config);
+ const bool same_config_group = current_config != nullptr &&
+ next_config != nullptr &&
+ current_config->group_id ==
+ next_config->group_id;
+ // If the contraints dictate that this is to be applied in the future, it
+ // must be queued. If the new config is in the same config group as the
+ // current one, then queue it to reduce jank.
+ HwcDisplay::ConfigError result{};
+ if (future_config || same_config_group) {
+ QueuedConfigTiming timing = {};
+ result = display->QueueConfig(config, constraints.desiredTimeNanos,
+ constraints.seamlessRequired, &timing);
+ timeline->newVsyncAppliedTimeNanos = timing.new_vsync_time_ns;
+ timeline->refreshTimeNanos = timing.refresh_time_ns;
+ timeline->refreshRequired = true;
+ } else {
+ // Fall back to a blocking commit, which may modeset.
+ result = display->SetConfig(config);
+ timeline->newVsyncAppliedTimeNanos = ::android::ResourceManager::
+ GetTimeMonotonicNs();
+ timeline->refreshRequired = false;
+ }
+
+ switch (result) {
+ case HwcDisplay::ConfigError::kBadConfig:
+ return ToBinderStatus(hwc3::Error::kBadConfig);
+ case HwcDisplay::ConfigError::kSeamlessNotAllowed:
+ return ToBinderStatus(hwc3::Error::kSeamlessNotAllowed);
+ case HwcDisplay::ConfigError::kSeamlessNotPossible:
+ return ToBinderStatus(hwc3::Error::kSeamlessNotPossible);
+ case HwcDisplay::ConfigError::kNone:
+ return ndk::ScopedAStatus::ok();
+ }
}
ndk::ScopedAStatus ComposerClient::setBootDisplayConfig(int64_t /*display_id*/,
@@ -1266,6 +1300,10 @@
#endif
+ndk::ScopedAStatus ComposerClient::getMaxLayerPictureProfiles(int64_t, int32_t*) {
+ return ToBinderStatus(hwc3::Error::kUnsupported);
+}
+
std::string ComposerClient::Dump() {
uint32_t size = 0;
hwc_->Dump(&size, nullptr);
diff --git a/hwc3/ComposerClient.h b/hwc3/ComposerClient.h
index 8ca6cfb..e287d58 100644
--- a/hwc3/ComposerClient.h
+++ b/hwc3/ComposerClient.h
@@ -155,6 +155,9 @@
#endif
+ ndk::ScopedAStatus getMaxLayerPictureProfiles(
+ int64_t display, int32_t* maxProfiles) override;
+
protected:
::ndk::SpAIBinder createBinder() override;
diff --git a/meson.build b/meson.build
index e9a86ec..8cfbbc8 100644
--- a/meson.build
+++ b/meson.build
@@ -16,6 +16,7 @@
'backend/Backend.cpp',
'backend/BackendClient.cpp',
'utils/fd.cpp',
+ 'utils/properties.cpp',
)
srcs_hwc2_device = [
diff --git a/utils/properties.cpp b/utils/properties.cpp
new file mode 100644
index 0000000..4547e74
--- /dev/null
+++ b/utils/properties.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#include "properties.h"
+
+/**
+ * @brief Determine if the "Present Not Reliable" property is enabled.
+ *
+ * @return boolean
+ */
+auto Properties::IsPresentFenceNotReliable() -> bool {
+ return (property_get_bool("ro.vendor.hwc.drm.present_fence_not_reliable",
+ 0) != 0);
+}
diff --git a/utils/properties.h b/utils/properties.h
index e400236..f5816cb 100644
--- a/utils/properties.h
+++ b/utils/properties.h
@@ -39,4 +39,42 @@
return static_cast<int>(strlen(value));
}
+/**
+ * Bluntly copied from system/core/libcutils/properties.cpp,
+ * which is part of the Android Project and licensed under Apache 2.
+ * Source:
+ * https://cs.android.com/android/platform/superproject/main/+/main:system/core/libcutils/properties.cpp;l=27
+ */
+auto inline property_get_bool(const char *key, int8_t default_value) -> int8_t {
+ if (!key)
+ return default_value;
+
+ int8_t result = default_value;
+ char buf[PROPERTY_VALUE_MAX] = {};
+
+ int len = property_get(key, buf, "");
+ if (len == 1) {
+ char ch = buf[0];
+ if (ch == '0' || ch == 'n') {
+ result = false;
+ } else if (ch == '1' || ch == 'y') {
+ result = true;
+ }
+ } else if (len > 1) {
+ if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
+ result = false;
+ } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") ||
+ !strcmp(buf, "on")) {
+ result = true;
+ }
+ }
+
+ return result;
+}
+
#endif
+
+class Properties {
+ public:
+ static auto IsPresentFenceNotReliable() -> bool;
+};