drm_hwcomposer: Split the drm compositor into per-display threads
This patch splits out the current single drm compositor with
per-display compositors, each with their own thread.
The per-display compositors are hidden behind a singleton
drm compositor. This allows us to maintain a whole-world view
of all displays involved in a frame. This becomes useful if
we start switching up crtcs/encoders for the displays.
This also allows us to issue one DrmComposition when the
frame is being assembled.
The single DrmComposition handles the plane allocation (since they
might switch between displays), and contains per-display compositions
which are used to store the layer->plane/crtc information for each
frame. The display compositors use the per-display compositions to
display the frame on their output.
Each display compositor receives a shared pointer to the frame's
DrmComposition on QueueComposition. As a result, both the composition,
and the per-display compositions, live for as long as any one
display is still using it. While this is sub-optimal (since a display
might never update again), this is probably fine for now.
Finally, splitting things up per-display will allow us to inject
non-compositing jobs into the composite queue. An example would be
turning the display off, or setting the mode. This ensures that all
frames in the composite queue are displayed before the mode changes
or the display is disabled.
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Change-Id: I8a233ea64710b238f70acbcde1f6d771e297b069
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
new file mode 100644
index 0000000..2b4b4df
--- /dev/null
+++ b/drmdisplaycompositor.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#define LOG_TAG "hwc-drm-display-compositor"
+
+#include "drmdisplaycompositor.h"
+#include "drmcrtc.h"
+#include "drmplane.h"
+#include "drmresources.h"
+
+#include <pthread.h>
+#include <sstream>
+#include <stdlib.h>
+#include <time.h>
+#include <vector>
+
+#include <cutils/log.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+DrmDisplayCompositor::DrmDisplayCompositor()
+ : drm_(NULL),
+ display_(-1),
+ worker_(this),
+ frame_no_(0),
+ initialized_(false),
+ dump_frames_composited_(0),
+ dump_last_timestamp_ns_(0) {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ return;
+ dump_last_timestamp_ns_ = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+}
+
+DrmDisplayCompositor::~DrmDisplayCompositor() {
+ if (!initialized_)
+ return;
+
+ worker_.Exit();
+
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret)
+ ALOGE("Failed to acquire compositor lock %d", ret);
+
+ while (!composite_queue_.empty()) {
+ composite_queue_.front().reset();
+ composite_queue_.pop();
+ }
+ active_composition_.reset();
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret)
+ ALOGE("Failed to acquire compositor lock %d", ret);
+
+ pthread_mutex_destroy(&lock_);
+}
+
+int DrmDisplayCompositor::Init(DrmResources *drm, int display) {
+ drm_ = drm;
+ display_ = display;
+
+ int ret = pthread_mutex_init(&lock_, NULL);
+ if (ret) {
+ ALOGE("Failed to initialize drm compositor lock %d\n", ret);
+ return ret;
+ }
+ ret = worker_.Init();
+ if (ret) {
+ pthread_mutex_destroy(&lock_);
+ ALOGE("Failed to initialize compositor worker %d\n", ret);
+ return ret;
+ }
+
+ initialized_ = true;
+ return 0;
+}
+
+int DrmDisplayCompositor::QueueComposition(
+ std::unique_ptr<DrmDisplayComposition> composition) {
+ if (composition->GetCompositionLayers()->empty())
+ return 0;
+
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret) {
+ ALOGE("Failed to acquire compositor lock %d", ret);
+ return ret;
+ }
+
+ composite_queue_.push(std::move(composition));
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret) {
+ ALOGE("Failed to release compositor lock %d", ret);
+ return ret;
+ }
+
+ worker_.Signal();
+ return 0;
+}
+
+int DrmDisplayCompositor::ApplyFrame(DrmDisplayComposition *display_comp) {
+ int ret = 0;
+
+ drmModePropertySetPtr pset = drmModePropertySetAlloc();
+ if (!pset) {
+ ALOGE("Failed to allocate property set");
+ return -ENOMEM;
+ }
+
+ DrmCompositionLayerVector_t *layers = display_comp->GetCompositionLayers();
+ for (DrmCompositionLayerVector_t::iterator iter = layers->begin();
+ iter != layers->end(); ++iter) {
+ hwc_layer_1_t *layer = &iter->layer;
+
+ if (layer->acquireFenceFd >= 0) {
+ ret = sync_wait(layer->acquireFenceFd, -1);
+ if (ret) {
+ ALOGE("Failed to wait for acquire %d/%d", layer->acquireFenceFd, ret);
+ drmModePropertySetFree(pset);
+ return ret;
+ }
+ close(layer->acquireFenceFd);
+ layer->acquireFenceFd = -1;
+ }
+
+ DrmPlane *plane = iter->plane;
+ ret =
+ drmModePropertySetAdd(pset, plane->id(), plane->crtc_property().id(),
+ iter->crtc->id()) ||
+ drmModePropertySetAdd(pset, plane->id(), plane->fb_property().id(),
+ iter->bo.fb_id) ||
+ drmModePropertySetAdd(pset, plane->id(), plane->crtc_x_property().id(),
+ layer->displayFrame.left) ||
+ drmModePropertySetAdd(pset, plane->id(), plane->crtc_y_property().id(),
+ layer->displayFrame.top) ||
+ drmModePropertySetAdd(
+ pset, plane->id(), plane->crtc_w_property().id(),
+ layer->displayFrame.right - layer->displayFrame.left) ||
+ drmModePropertySetAdd(
+ pset, plane->id(), plane->crtc_h_property().id(),
+ layer->displayFrame.bottom - layer->displayFrame.top) ||
+ drmModePropertySetAdd(pset, plane->id(), plane->src_x_property().id(),
+ layer->sourceCropf.left) ||
+ drmModePropertySetAdd(pset, plane->id(), plane->src_y_property().id(),
+ layer->sourceCropf.top) ||
+ drmModePropertySetAdd(
+ pset, plane->id(), plane->src_w_property().id(),
+ (int)(layer->sourceCropf.right - layer->sourceCropf.left) << 16) ||
+ drmModePropertySetAdd(
+ pset, plane->id(), plane->src_h_property().id(),
+ (int)(layer->sourceCropf.bottom - layer->sourceCropf.top) << 16);
+ if (ret) {
+ ALOGE("Failed to add plane %d to set", plane->id());
+ break;
+ }
+ }
+
+ if (!ret) {
+ ret = drmModePropertySetCommit(drm_->fd(), 0, drm_, pset);
+ if (ret)
+ ALOGE("Failed to commit pset ret=%d\n", ret);
+ }
+ if (pset)
+ drmModePropertySetFree(pset);
+
+ return ret;
+}
+
+int DrmDisplayCompositor::Composite() {
+ ATRACE_CALL();
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret) {
+ ALOGE("Failed to acquire compositor lock %d", ret);
+ return ret;
+ }
+ if (composite_queue_.empty()) {
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret)
+ ALOGE("Failed to release compositor lock %d", ret);
+ return ret;
+ }
+
+ std::unique_ptr<DrmDisplayComposition> composition(
+ std::move(composite_queue_.front()));
+ composite_queue_.pop();
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret) {
+ ALOGE("Failed to release compositor lock %d", ret);
+ return ret;
+ }
+
+ ret = ApplyFrame(composition.get());
+ if (ret) {
+ ALOGE("Composite failed for display %d", display_);
+ return ret;
+ }
+ ++dump_frames_composited_;
+
+ if (active_composition_)
+ active_composition_->FinishComposition();
+
+ active_composition_.swap(composition);
+ return ret;
+}
+
+bool DrmDisplayCompositor::HaveQueuedComposites() const {
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret) {
+ ALOGE("Failed to acquire compositor lock %d", ret);
+ return false;
+ }
+
+ bool empty_ret = !composite_queue_.empty();
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret) {
+ ALOGE("Failed to release compositor lock %d", ret);
+ return false;
+ }
+
+ return empty_ret;
+}
+
+void DrmDisplayCompositor::Dump(std::ostringstream *out) const {
+ uint64_t cur_ts;
+
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret)
+ return;
+
+ uint64_t num_frames = dump_frames_composited_;
+ dump_frames_composited_ = 0;
+
+ struct timespec ts;
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ ret |= pthread_mutex_unlock(&lock_);
+ if (ret)
+ return;
+
+ cur_ts = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+ uint64_t num_ms = (cur_ts - dump_last_timestamp_ns_) / (1000 * 1000);
+ unsigned fps = num_ms ? (num_frames * 1000) / (num_ms) : 0;
+
+ *out << "--DrmDisplayCompositor[" << display_
+ << "]: num_frames=" << num_frames << " num_ms=" << num_ms
+ << " fps=" << fps << "\n";
+
+ dump_last_timestamp_ns_ = cur_ts;
+}
+}