summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Kovalivskyi <roman.kovalivskyi@globallogic.com>2020-01-30 20:20:47 +0200
committerRoman Kovalivskyi <roman.kovalivskyi@globallogic.com>2020-02-19 17:08:20 +0200
commit8fae156cf9127c61fc3ba998e71de85b737ac3e8 (patch)
tree3b4b8d5f0ac8e2f62f6ffa7b95c99610a0fb5d08
parent65f2ba80df03635a75c7ecd70f8a39c0d76bd425 (diff)
drm_hwcomposer: Add fallback to client for layers flattening
Google recommends to delegate composition to GLES instead of HWC when screen isn't updating to conserve power, as stated on page https://source.android.com/devices/graphics/implement-hwc. Current implementation of hwcomposer has flattening of layers if after some time there were no updates of frames, but it uses writeback connector. Not every device has a support of writeback feature, so some sort of fallback should be provided. It is possible to fallback to client composition in case if writeback isn't available. This is used to reduce power consumption since squashing layers into a single layer on GPU and then using that buffer is more efficient than loading drm device. Signed-off-by: Roman Kovalivskyi <roman.kovalivskyi@globallogic.com>
-rw-r--r--compositor/drmdisplaycompositor.cpp59
-rw-r--r--drmhwctwo.cpp97
-rw-r--r--include/drmdisplaycompositor.h24
-rw-r--r--include/drmhwctwo.h3
4 files changed, 140 insertions, 43 deletions
diff --git a/compositor/drmdisplaycompositor.cpp b/compositor/drmdisplaycompositor.cpp
index e6f6922..401af48 100644
--- a/compositor/drmdisplaycompositor.cpp
+++ b/compositor/drmdisplaycompositor.cpp
@@ -63,7 +63,8 @@ DrmDisplayCompositor::DrmDisplayCompositor()
dump_frames_composited_(0),
dump_last_timestamp_ns_(0),
flatten_countdown_(FLATTEN_COUNTDOWN_INIT),
- writeback_fence_(-1) {
+ writeback_fence_(-1),
+ flattening_state_(FlatteningState::kNone) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
return;
@@ -143,6 +144,15 @@ DrmDisplayCompositor::CreateInitializedComposition() const {
return comp;
}
+FlatteningState DrmDisplayCompositor::GetFlatteningState() const {
+ return flattening_state_;
+}
+
+bool DrmDisplayCompositor::ShouldFlattenOnClient() const {
+ return flattening_state_ == FlatteningState::kClientRequested ||
+ flattening_state_ == FlatteningState::kClientDone;
+}
+
std::tuple<uint32_t, uint32_t, int>
DrmDisplayCompositor::GetActiveModeResolution() {
DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
@@ -605,6 +615,11 @@ void DrmDisplayCompositor::ApplyFrame(
active_composition_.swap(composition);
flatten_countdown_ = FLATTEN_COUNTDOWN_INIT;
+ if (flattening_state_ != FlatteningState::kClientRequested) {
+ flattening_state_ = FlatteningState::kNone;
+ } else {
+ flattening_state_ = FlatteningState::kClientDone;
+ }
vsync_worker_.VSyncControl(!writeback);
}
@@ -761,6 +776,35 @@ int DrmDisplayCompositor::FlattenOnDisplay(
return 0;
}
+bool DrmDisplayCompositor::IsFlatteningNeeded() const {
+ return CountdownExpired() && active_composition_->layers().size() >= 2;
+}
+
+int DrmDisplayCompositor::FlattenOnClient() {
+ if (refresh_display_cb_) {
+ {
+ AutoLock lock(&lock_, __func__);
+ if (!IsFlatteningNeeded()) {
+ if (flattening_state_ != FlatteningState::kClientDone) {
+ ALOGV("Flattening is not needed");
+ flattening_state_ = FlatteningState::kNotNeeded;
+ }
+ return -EALREADY;
+ }
+ }
+
+ ALOGV(
+ "No writeback connector available, "
+ "falling back to client composition");
+ flattening_state_ = FlatteningState::kClientRequested;
+ refresh_display_cb_(display_);
+ return 0;
+ } else {
+ ALOGV("No writeback connector available");
+ return -EINVAL;
+ }
+}
+
// Flatten a scene by enabling the writeback connector attached
// to the same CRTC as the one driving the display.
int DrmDisplayCompositor::FlattenSerial(DrmConnector *writeback_conn) {
@@ -776,8 +820,9 @@ int DrmDisplayCompositor::FlattenSerial(DrmConnector *writeback_conn) {
int ret = lock.Lock();
if (ret)
return ret;
- if (!CountdownExpired() || active_composition_->layers().size() < 2) {
+ if (!IsFlatteningNeeded()) {
ALOGV("Flattening is not needed");
+ flattening_state_ = FlatteningState::kNotNeeded;
return -EALREADY;
}
@@ -884,8 +929,9 @@ int DrmDisplayCompositor::FlattenConcurrent(DrmConnector *writeback_conn) {
ret = lock.Lock();
if (ret)
return ret;
- if (!CountdownExpired() || active_composition_->layers().size() < 2) {
+ if (!IsFlatteningNeeded()) {
ALOGV("Flattening is not needed");
+ flattening_state_ = FlatteningState::kNotNeeded;
return -EALREADY;
}
DrmCrtc *crtc = active_composition_->crtc();
@@ -955,13 +1001,16 @@ int DrmDisplayCompositor::FlattenActiveComposition() {
DrmConnector *writeback_conn = resource_manager_->AvailableWritebackConnector(
display_);
if (!active_composition_ || !writeback_conn) {
- ALOGV("No writeback connector available");
- return -EINVAL;
+ // Try to fallback to GPU composition on client, since it is more
+ // power-efficient than composition on device side
+ return FlattenOnClient();
}
if (writeback_conn->display() != display_) {
+ flattening_state_ = FlatteningState::kConcurrent;
return FlattenConcurrent(writeback_conn);
} else {
+ flattening_state_ = FlatteningState::kSerial;
return FlattenSerial(writeback_conn);
}
diff --git a/drmhwctwo.cpp b/drmhwctwo.cpp
index e2c943a..a4a8187 100644
--- a/drmhwctwo.cpp
+++ b/drmhwctwo.cpp
@@ -208,6 +208,12 @@ HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor,
HandleInitialHotplugState(device.get());
break;
}
+ case HWC2::Callback::Refresh: {
+ for (std::pair<const hwc2_display_t, DrmHwcTwo::HwcDisplay> &d :
+ displays_)
+ d.second.RegisterRefreshCallback(data, function);
+ break;
+ }
case HWC2::Callback::Vsync: {
for (std::pair<const hwc2_display_t, DrmHwcTwo::HwcDisplay> &d :
displays_)
@@ -310,6 +316,15 @@ HWC2::Error DrmHwcTwo::HwcDisplay::RegisterVsyncCallback(
return HWC2::Error::None;
}
+void DrmHwcTwo::HwcDisplay::RegisterRefreshCallback(
+ hwc2_callback_data_t data, hwc2_function_pointer_t func) {
+ supported(__func__);
+ auto hook = reinterpret_cast<HWC2_PFN_REFRESH>(func);
+ compositor_.SetRefreshCallback([data, hook](int display) {
+ hook(data, static_cast<hwc2_display_t>(display));
+ });
+}
+
HWC2::Error DrmHwcTwo::HwcDisplay::AcceptDisplayChanges() {
supported(__func__);
for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_)
@@ -900,51 +915,57 @@ HWC2::Error DrmHwcTwo::HwcDisplay::ValidateDisplay(uint32_t *num_types,
int client_start = -1, client_size = 0;
- for (std::pair<const uint32_t, DrmHwcTwo::HwcLayer *> &l : z_map) {
- if (!HardwareSupportsLayerType(l.second->sf_type()) ||
- !importer_->CanImportBuffer(l.second->buffer()) ||
- color_transform_hint_ != HAL_COLOR_TRANSFORM_IDENTITY ||
- (l.second->RequireScalingOrPhasing() &&
- resource_manager_->ForcedScalingWithGpu())) {
- if (client_start < 0)
- client_start = l.first;
- client_size = (l.first - client_start) + 1;
+ if (compositor_.ShouldFlattenOnClient()) {
+ client_start = 0;
+ client_size = z_map.size();
+ MarkValidated(z_map, client_start, client_size);
+ } else {
+ for (std::pair<const uint32_t, DrmHwcTwo::HwcLayer *> &l : z_map) {
+ if (!HardwareSupportsLayerType(l.second->sf_type()) ||
+ !importer_->CanImportBuffer(l.second->buffer()) ||
+ color_transform_hint_ != HAL_COLOR_TRANSFORM_IDENTITY ||
+ (l.second->RequireScalingOrPhasing() &&
+ resource_manager_->ForcedScalingWithGpu())) {
+ if (client_start < 0)
+ client_start = l.first;
+ client_size = (l.first - client_start) + 1;
+ }
}
- }
- int extra_client = (z_map.size() - client_size) - avail_planes;
- if (extra_client > 0) {
- int start = 0, steps;
- if (client_size != 0) {
- int prepend = std::min(client_start, extra_client);
- int append = std::min(int(z_map.size() - (client_start + client_size)),
- extra_client);
- start = client_start - prepend;
- client_size += extra_client;
- steps = 1 + std::min(std::min(append, prepend),
- int(z_map.size()) - (start + client_size));
- } else {
- client_size = extra_client;
- steps = 1 + z_map.size() - extra_client;
- }
+ int extra_client = (z_map.size() - client_size) - avail_planes;
+ if (extra_client > 0) {
+ int start = 0, steps;
+ if (client_size != 0) {
+ int prepend = std::min(client_start, extra_client);
+ int append = std::min(int(z_map.size() - (client_start + client_size)),
+ extra_client);
+ start = client_start - prepend;
+ client_size += extra_client;
+ steps = 1 + std::min(std::min(append, prepend),
+ int(z_map.size()) - (start + client_size));
+ } else {
+ client_size = extra_client;
+ steps = 1 + z_map.size() - extra_client;
+ }
- gpu_pixops = INT_MAX;
- for (int i = 0; i < steps; i++) {
- uint32_t po = CalcPixOps(z_map, start + i, client_size);
- if (po < gpu_pixops) {
- gpu_pixops = po;
- client_start = start + i;
+ gpu_pixops = INT_MAX;
+ for (int i = 0; i < steps; i++) {
+ uint32_t po = CalcPixOps(z_map, start + i, client_size);
+ if (po < gpu_pixops) {
+ gpu_pixops = po;
+ client_start = start + i;
+ }
}
}
- }
- MarkValidated(z_map, client_start, client_size);
+ MarkValidated(z_map, client_start, client_size);
- if (CreateComposition(true) != HWC2::Error::None) {
- ++total_stats_.failed_kms_validate_;
- gpu_pixops = total_pixops;
- client_size = z_map.size();
- MarkValidated(z_map, 0, client_size);
+ if (CreateComposition(true) != HWC2::Error::None) {
+ ++total_stats_.failed_kms_validate_;
+ gpu_pixops = total_pixops;
+ client_size = z_map.size();
+ MarkValidated(z_map, 0, client_size);
+ }
}
*num_types = client_size;
diff --git a/include/drmdisplaycompositor.h b/include/drmdisplaycompositor.h
index 477f226..cfd8f4a 100644
--- a/include/drmdisplaycompositor.h
+++ b/include/drmdisplaycompositor.h
@@ -41,6 +41,15 @@
namespace android {
+enum class FlatteningState {
+ kNone,
+ kNotNeeded,
+ kClientRequested,
+ kClientDone,
+ kSerial,
+ kConcurrent
+};
+
class DrmDisplayCompositor {
public:
DrmDisplayCompositor();
@@ -48,6 +57,11 @@ class DrmDisplayCompositor {
int Init(ResourceManager *resource_manager, int display);
+ template <typename Fn>
+ void SetRefreshCallback(Fn &&refresh_cb) {
+ refresh_display_cb_ = std::forward<Fn>(refresh_cb);
+ }
+
std::unique_ptr<DrmDisplayComposition> CreateComposition() const;
std::unique_ptr<DrmDisplayComposition> CreateInitializedComposition() const;
int ApplyComposition(std::unique_ptr<DrmDisplayComposition> composition);
@@ -62,6 +76,9 @@ class DrmDisplayCompositor {
return active_composition_->take_out_fence();
}
+ FlatteningState GetFlatteningState() const;
+ bool ShouldFlattenOnClient() const;
+
std::tuple<uint32_t, uint32_t, int> GetActiveModeResolution();
private:
@@ -90,7 +107,10 @@ class DrmDisplayCompositor {
void ApplyFrame(std::unique_ptr<DrmDisplayComposition> composition,
int status, bool writeback = false);
+
+ bool IsFlatteningNeeded() const;
int FlattenActiveComposition();
+ int FlattenOnClient();
int FlattenSerial(DrmConnector *writeback_conn);
int FlattenConcurrent(DrmConnector *writeback_conn);
int FlattenOnDisplay(std::unique_ptr<DrmDisplayComposition> &src,
@@ -126,6 +146,10 @@ class DrmDisplayCompositor {
int64_t flatten_countdown_;
std::unique_ptr<Planner> planner_;
int writeback_fence_;
+
+ FlatteningState flattening_state_;
+
+ std::function<void(int)> refresh_display_cb_;
};
} // namespace android
diff --git a/include/drmhwctwo.h b/include/drmhwctwo.h
index 444c6ed..43f1fce 100644
--- a/include/drmhwctwo.h
+++ b/include/drmhwctwo.h
@@ -163,6 +163,9 @@ class DrmHwcTwo : public hwc2_device_t {
HWC2::Error RegisterVsyncCallback(hwc2_callback_data_t data,
hwc2_function_pointer_t func);
+ void RegisterRefreshCallback(hwc2_callback_data_t data,
+ hwc2_function_pointer_t func);
+
void ClearDisplay();
std::string Dump();