Merge remote-tracking branch 'aosp/upstream-main' into uprev_drm_hwc am: 1c44556c79 am: 4cadc91bc2
Original change: undetermined
Change-Id: I37a3011baffaab42a5e0311c967636880692b2b2
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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/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/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index ffe6b11..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,20 +166,72 @@
Deinit();
};
-auto HwcDisplay::GetCurrentConfig() const -> const HwcDisplayConfig * {
- auto config_iter = configs_.hwc_configs.find(configs_.active_config_id);
+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 * {
- auto config_iter = configs_.hwc_configs.find(staged_mode_config_id_);
- if (config_iter == configs_.hwc_configs.end()) {
- return nullptr;
+ 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;
}
- return &config_iter->second;
+
+ 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,
@@ -136,7 +258,6 @@
// Queue the config change timing to be consistent with the requested
// refresh time.
- staged_mode_ = configs_.hwc_configs[config].mode;
staged_mode_change_time_ = out_timing->refresh_time_ns;
staged_mode_config_id_ = config;
@@ -296,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;
}
@@ -518,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__);
@@ -532,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;
}
@@ -607,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;
}
@@ -618,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_,
@@ -669,6 +822,7 @@
color_matrix_ = {};
++frame_no_;
+
return HWC2::Error::None;
}
@@ -679,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 91e5df7..ecca514 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -74,10 +74,18 @@
// 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);
@@ -220,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 c0b340e..fd4dedc 100644
--- a/hwc3/ComposerClient.cpp
+++ b/hwc3/ComposerClient.cpp
@@ -1085,13 +1085,36 @@
return ToBinderStatus(hwc3::Error::kBadDisplay);
}
- ::QueuedConfigTiming timing{};
- HwcDisplay::ConfigError
- 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;
+ if (constraints.seamlessRequired) {
+ return ToBinderStatus(hwc3::Error::kSeamlessNotAllowed);
+ }
+
+ 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:
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 641df91..f5816cb 100644
--- a/utils/properties.h
+++ b/utils/properties.h
@@ -73,3 +73,8 @@
}
#endif
+
+class Properties {
+ public:
+ static auto IsPresentFenceNotReliable() -> bool;
+};