drm_hwcomposer: Use hw planes + drm atomic interface
Replace the basic, single overlay, modeset/flip implementation with
a new Compositor instantiation for drm. The new compositor uses the
drm atomic interface to composite layers to multiple hardware planes.
This change also introduces an importer argument in
Compositor::CreateComposition. By specifying the importer, Compositor
instances are responsible for cleaning up buffer objects submitted
via Composition::AddLayer.
Change-Id: Ic292829cd93475d754294b00428de132301092b2
Signed-off-by: Sean Paul <seanpaul@chromium.org>
diff --git a/Android.mk b/Android.mk
index 30dfb5f..262ddfe 100644
--- a/Android.mk
+++ b/Android.mk
@@ -36,6 +36,9 @@
LOCAL_SRC_FILES := \
compositor.cpp \
drmresources.cpp \
+ drmcomposition.cpp \
+ drmcompositor.cpp \
+ drmcompositorworker.cpp \
drmconnector.cpp \
drmcrtc.cpp \
drmencoder.cpp \
diff --git a/drmcomposition.cpp b/drmcomposition.cpp
new file mode 100644
index 0000000..8fcb12d
--- /dev/null
+++ b/drmcomposition.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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 LOG_TAG "hwc-drm-composition"
+
+#include "drmcomposition.h"
+#include "drmcrtc.h"
+#include "drmplane.h"
+#include "drmresources.h"
+
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <sw_sync.h>
+#include <sync/sync.h>
+
+namespace android {
+
+static const bool kUseOverlayPlanes = false;
+
+DrmCompositionLayer::DrmCompositionLayer() : crtc(NULL), plane(NULL) {
+ memset(&layer, 0, sizeof(layer));
+ layer.acquireFenceFd = -1;
+ memset(&bo, 0, sizeof(bo));
+}
+
+DrmCompositionLayer::~DrmCompositionLayer() {
+}
+
+DrmComposition::DrmComposition(DrmResources *drm, Importer *importer,
+ uint64_t frame_no)
+ : drm_(drm),
+ importer_(importer),
+ frame_no_(frame_no),
+ timeline_fd_(-1),
+ timeline_(0) {
+ for (DrmResources::PlaneIter iter = drm_->begin_planes();
+ iter != drm_->end_planes(); ++iter) {
+ if ((*iter)->type() == DRM_PLANE_TYPE_PRIMARY)
+ primary_planes_.push_back(*iter);
+ else if (kUseOverlayPlanes && (*iter)->type() == DRM_PLANE_TYPE_OVERLAY)
+ overlay_planes_.push_back(*iter);
+ }
+}
+
+DrmComposition::~DrmComposition() {
+ for (DrmCompositionLayerMap_t::iterator iter = composition_map_.begin();
+ iter != composition_map_.end(); ++iter) {
+ importer_->ReleaseBuffer(&iter->second.bo);
+
+ if (iter->second.layer.acquireFenceFd >= 0)
+ close(iter->second.layer.acquireFenceFd);
+ }
+
+ if (timeline_fd_ >= 0)
+ close(timeline_fd_);
+}
+
+int DrmComposition::Init() {
+ int ret = sw_sync_timeline_create();
+ if (ret < 0) {
+ ALOGE("Failed to create sw sync timeline %d", ret);
+ return ret;
+ }
+ timeline_fd_ = ret;
+ return 0;
+}
+
+unsigned DrmComposition::GetRemainingLayers(int display,
+ unsigned num_needed) const {
+ DrmCrtc *crtc = drm_->GetCrtcForDisplay(display);
+ if (!crtc) {
+ ALOGW("Failed to find crtc for display %d", display);
+ return 0;
+ }
+
+ unsigned num_planes = 0;
+ for (std::vector<DrmPlane *>::const_iterator iter = primary_planes_.begin();
+ iter != primary_planes_.end(); ++iter) {
+ if ((*iter)->GetCrtcSupported(*crtc))
+ ++num_planes;
+ }
+ for (std::deque<DrmPlane *>::const_iterator iter = overlay_planes_.begin();
+ iter != overlay_planes_.end(); ++iter) {
+ if ((*iter)->GetCrtcSupported(*crtc))
+ ++num_planes;
+ }
+ return std::min(num_planes, num_needed);
+}
+
+int DrmComposition::AddLayer(int display, hwc_layer_1_t *layer,
+ hwc_drm_bo *bo) {
+ if (layer->transform != 0)
+ return -EINVAL;
+
+ DrmCrtc *crtc = drm_->GetCrtcForDisplay(display);
+ if (!crtc) {
+ ALOGE("Could not find crtc for display %d", display);
+ return -ENODEV;
+ }
+
+ ++timeline_;
+ layer->releaseFenceFd =
+ sw_sync_fence_create(timeline_fd_, "drm_fence", timeline_);
+ if (layer->releaseFenceFd < 0) {
+ ALOGE("Could not create release fence %d", layer->releaseFenceFd);
+ return layer->releaseFenceFd;
+ }
+
+ DrmCompositionLayer_t c_layer;
+ c_layer.layer = *layer;
+ c_layer.bo = *bo;
+ c_layer.crtc = crtc;
+
+ // First try to find a primary plane for the layer, then fallback on overlays
+ for (std::vector<DrmPlane *>::iterator iter = primary_planes_.begin();
+ iter != primary_planes_.end(); ++iter) {
+ if ((*iter)->GetCrtcSupported(*crtc)) {
+ c_layer.plane = (*iter);
+ primary_planes_.erase(iter);
+ break;
+ }
+ }
+ for (std::deque<DrmPlane *>::iterator iter = overlay_planes_.begin();
+ !c_layer.plane && iter != overlay_planes_.end(); ++iter) {
+ if ((*iter)->GetCrtcSupported(*crtc)) {
+ c_layer.plane = (*iter);
+ overlay_planes_.erase(iter);
+ break;
+ }
+ }
+ if (!c_layer.plane) {
+ close(layer->releaseFenceFd);
+ layer->releaseFenceFd = -1;
+ return -ENOENT;
+ }
+
+ layer->acquireFenceFd = -1; // We own this now
+ composition_map_.insert(DrmCompositionLayerPair_t(display, c_layer));
+ return 0;
+}
+
+int DrmComposition::FinishComposition() {
+ int ret = sw_sync_timeline_inc(timeline_fd_, timeline_);
+ if (ret)
+ ALOGE("Failed to increment sync timeline %d", ret);
+
+ return ret;
+}
+
+DrmCompositionLayerMap_t *DrmComposition::GetCompositionMap() {
+ return &composition_map_;
+}
+}
diff --git a/drmcomposition.h b/drmcomposition.h
new file mode 100644
index 0000000..5c47fbf
--- /dev/null
+++ b/drmcomposition.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_DRM_COMPOSITION_H_
+#define ANDROID_DRM_COMPOSITION_H_
+
+#include "compositor.h"
+#include "drm_hwcomposer.h"
+#include "drmplane.h"
+#include "importer.h"
+
+#include <deque>
+#include <map>
+#include <vector>
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+namespace android {
+
+typedef struct DrmCompositionLayer {
+ DrmCompositionLayer();
+ ~DrmCompositionLayer();
+
+ hwc_layer_1_t layer;
+ hwc_drm_bo_t bo;
+ DrmCrtc *crtc;
+ DrmPlane *plane;
+} DrmCompositionLayer_t;
+
+typedef std::multimap<int, DrmCompositionLayer> DrmCompositionLayerMap_t;
+typedef std::pair<int, DrmCompositionLayer> DrmCompositionLayerPair_t;
+
+class DrmComposition : public Composition {
+ public:
+ DrmComposition(DrmResources *drm, Importer *importer, uint64_t frame_no);
+ ~DrmComposition();
+
+ virtual int Init();
+
+ virtual unsigned GetRemainingLayers(int display, unsigned num_needed) const;
+ virtual int AddLayer(int display, hwc_layer_1_t *layer, hwc_drm_bo_t *bo);
+
+ int FinishComposition();
+
+ DrmCompositionLayerMap_t *GetCompositionMap();
+
+ private:
+ DrmResources *drm_;
+ Importer *importer_;
+
+ uint64_t frame_no_;
+
+ int timeline_fd_;
+ int timeline_;
+
+ std::vector<DrmPlane *> primary_planes_;
+ std::deque<DrmPlane *> overlay_planes_;
+ DrmCompositionLayerMap_t composition_map_;
+};
+}
+
+#endif // ANDROID_DRM_COMPOSITION_H_
diff --git a/drmcompositor.cpp b/drmcompositor.cpp
new file mode 100644
index 0000000..ffca4df
--- /dev/null
+++ b/drmcompositor.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 LOG_TAG "hwc-drm-compositor"
+
+#include "drmcompositor.h"
+#include "drmcrtc.h"
+#include "drmplane.h"
+#include "drmresources.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <sync/sync.h>
+
+namespace android {
+
+DrmCompositor::DrmCompositor(DrmResources *drm)
+ : drm_(drm),
+ worker_(this),
+ active_composition_(NULL),
+ frame_no_(0),
+ initialized_(false) {
+}
+
+DrmCompositor::~DrmCompositor() {
+ if (initialized_)
+ pthread_mutex_destroy(&lock_);
+}
+
+int DrmCompositor::Init() {
+ 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;
+}
+
+Composition *DrmCompositor::CreateComposition(Importer *importer) {
+ DrmComposition *composition = new DrmComposition(drm_, importer, frame_no_++);
+ if (!composition) {
+ ALOGE("Failed to allocate drm composition");
+ return NULL;
+ }
+ int ret = composition->Init();
+ if (ret) {
+ ALOGE("Failed to initialize drm composition %d", ret);
+ delete composition;
+ return NULL;
+ }
+ return composition;
+}
+
+int DrmCompositor::QueueComposition(Composition *composition) {
+ int ret = pthread_mutex_lock(&lock_);
+ if (ret) {
+ ALOGE("Failed to acquire compositor lock %d", ret);
+ return ret;
+ }
+
+ composite_queue_.push((DrmComposition *)composition);
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret) {
+ ALOGE("Failed to release compositor lock %d", ret);
+ return ret;
+ }
+
+ worker_.Signal();
+ return 0;
+}
+
+int DrmCompositor::PerformModeset(DrmCompositionLayerMap_t::iterator begin,
+ DrmCompositionLayerMap_t::iterator end) {
+ DrmCompositionLayer *layer = NULL;
+ for (DrmCompositionLayerMap_t::iterator iter = begin; iter != end; ++iter) {
+ if (iter->second.layer.compositionType == HWC_FRAMEBUFFER_TARGET) {
+ layer = &iter->second;
+ break;
+ }
+ }
+ int display = begin->first;
+ if (!layer) {
+ ALOGE("Could not find target framebuffer for display %d", display);
+ return -ENOENT;
+ }
+
+ drmModeModeInfo m;
+ DrmConnector *connector = drm_->GetConnectorForDisplay(display);
+ connector->active_mode().ToModeModeInfo(&m);
+
+ uint32_t connectors = connector->id();
+ int ret = drmModeSetCrtc(drm_->fd(), layer->crtc->id(), layer->bo.fb_id, 0, 0,
+ &connectors, 1, &m);
+ if (ret)
+ ALOGE("Failed set crtc for disp %d/%d", display, ret);
+ else
+ layer->crtc->set_requires_modeset(false);
+
+ return ret;
+}
+
+int DrmCompositor::CompositeDisplay(DrmCompositionLayerMap_t::iterator begin,
+ DrmCompositionLayerMap_t::iterator end) {
+ int ret = 0;
+ // Wait for all acquire fences to signal
+ for (DrmCompositionLayerMap_t::iterator iter = begin; iter != end; ++iter) {
+ hwc_layer_1_t *layer = &iter->second.layer;
+
+ if (layer->acquireFenceFd < 0)
+ continue;
+
+ ret = sync_wait(layer->acquireFenceFd, -1);
+ if (ret) {
+ ALOGE("Failed to wait for acquire %d/%d", layer->acquireFenceFd, ret);
+ return ret;
+ }
+ close(layer->acquireFenceFd);
+ layer->acquireFenceFd = -1;
+ }
+
+ DrmCrtc *crtc = begin->second.crtc;
+ if (crtc->requires_modeset()) {
+ ret = PerformModeset(begin, end);
+ if (ret)
+ ALOGE("Failed modeset on display %d", begin->first);
+ return ret;
+ }
+
+ drmModePropertySetPtr pset = drmModePropertySetAlloc();
+ if (!pset) {
+ ALOGE("Failed to allocate property set");
+ return -ENOMEM;
+ }
+
+ for (DrmCompositionLayerMap_t::iterator iter = begin; iter != end; ++iter) {
+ DrmCompositionLayer_t *comp = &iter->second;
+ hwc_layer_1_t *layer = &comp->layer;
+ DrmPlane *plane = comp->plane;
+
+ ret =
+ drmModePropertySetAdd(pset, plane->id(), plane->crtc_property().id(),
+ crtc->id()) ||
+ drmModePropertySetAdd(pset, plane->id(), plane->fb_property().id(),
+ comp->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 DrmCompositor::Composite() {
+ 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;
+ }
+
+ DrmComposition *composition = composite_queue_.front();
+ composite_queue_.pop();
+
+ ret = pthread_mutex_unlock(&lock_);
+ if (ret) {
+ ALOGE("Failed to release compositor lock %d", ret);
+ return ret;
+ }
+
+ DrmCompositionLayerMap_t *map = composition->GetCompositionMap();
+ for (DrmResources::ConnectorIter iter = drm_->begin_connectors();
+ iter != drm_->end_connectors(); ++iter) {
+ int display = (*iter)->display();
+ std::pair<DrmCompositionLayerMap_t::iterator,
+ DrmCompositionLayerMap_t::iterator> layer_iters =
+ map->equal_range(display);
+
+ if (layer_iters.first != layer_iters.second) {
+ ret = CompositeDisplay(layer_iters.first, layer_iters.second);
+ if (ret) {
+ ALOGE("Composite failed for display %d:", display);
+ break;
+ }
+ }
+ }
+
+ if (active_composition_) {
+ active_composition_->FinishComposition();
+ delete active_composition_;
+ }
+ active_composition_ = composition;
+ return ret;
+}
+
+bool DrmCompositor::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;
+}
+}
diff --git a/drmcompositor.h b/drmcompositor.h
new file mode 100644
index 0000000..6b92a22
--- /dev/null
+++ b/drmcompositor.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_DRM_COMPOSITOR_H_
+#define ANDROID_DRM_COMPOSITOR_H_
+
+#include "compositor.h"
+#include "drm_hwcomposer.h"
+#include "drmcomposition.h"
+#include "drmcompositorworker.h"
+#include "drmplane.h"
+#include "importer.h"
+
+#include <pthread.h>
+#include <queue>
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+namespace android {
+
+class Drm;
+
+class DrmCompositor : public Compositor {
+ public:
+ DrmCompositor(DrmResources *drm);
+ ~DrmCompositor();
+
+ virtual int Init();
+
+ virtual Targeting *targeting() {
+ return NULL;
+ }
+
+ virtual Composition *CreateComposition(Importer *importer);
+
+ virtual int QueueComposition(Composition *composition);
+ virtual int Composite();
+
+ bool HaveQueuedComposites() const;
+
+ private:
+ DrmCompositor(const DrmCompositor &);
+
+ int CompositeDisplay(DrmCompositionLayerMap_t::iterator begin,
+ DrmCompositionLayerMap_t::iterator end);
+ int PerformModeset(DrmCompositionLayerMap_t::iterator begin,
+ DrmCompositionLayerMap_t::iterator end);
+
+ DrmResources *drm_;
+
+ DrmCompositorWorker worker_;
+
+ std::queue<DrmComposition *> composite_queue_;
+ DrmComposition *active_composition_;
+
+ uint64_t frame_no_;
+
+ bool initialized_;
+
+ // mutable since we need to acquire in HaveQueuedComposites
+ mutable pthread_mutex_t lock_;
+};
+}
+
+#endif // ANDROID_DRM_COMPOSITOR_H_
diff --git a/drmcompositorworker.cpp b/drmcompositorworker.cpp
new file mode 100644
index 0000000..9406194
--- /dev/null
+++ b/drmcompositorworker.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 LOG_TAG "hwc-drm-compositor-worker"
+
+#include "drmcompositor.h"
+#include "drmcompositorworker.h"
+#include "worker.h"
+
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <hardware/hardware.h>
+
+namespace android {
+
+DrmCompositorWorker::DrmCompositorWorker(DrmCompositor *compositor)
+ : Worker("drm-compositor", HAL_PRIORITY_URGENT_DISPLAY),
+ compositor_(compositor) {
+}
+
+DrmCompositorWorker::~DrmCompositorWorker() {
+}
+
+int DrmCompositorWorker::Init() {
+ return InitWorker();
+}
+
+void DrmCompositorWorker::Routine() {
+ int ret;
+ if (!compositor_->HaveQueuedComposites()) {
+ ret = Lock();
+ if (ret) {
+ ALOGE("Failed to lock worker, %d", ret);
+ return;
+ }
+
+ int wait_ret = WaitForSignalOrExitLocked();
+
+ ret = Unlock();
+ if (ret) {
+ ALOGE("Failed to unlock worker, %d", ret);
+ return;
+ }
+
+ if (wait_ret == -EINTR) {
+ return;
+ } else if (wait_ret) {
+ ALOGE("Failed to wait for signal, %d", wait_ret);
+ return;
+ }
+ }
+
+ ret = compositor_->Composite();
+ if (ret)
+ ALOGE("Failed to composite! %d", ret);
+}
+}
diff --git a/drmcompositorworker.h b/drmcompositorworker.h
new file mode 100644
index 0000000..8b485bf
--- /dev/null
+++ b/drmcompositorworker.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_DRM_COMPOSITOR_WORKER_H_
+#define ANDROID_DRM_COMPOSITOR_WORKER_H_
+
+#include "worker.h"
+
+namespace android {
+
+class DrmCompositor;
+
+class DrmCompositorWorker : public Worker {
+ public:
+ DrmCompositorWorker(DrmCompositor *compositor);
+ ~DrmCompositorWorker();
+
+ int Init();
+
+ protected:
+ virtual void Routine();
+
+ DrmCompositor *compositor_;
+};
+}
+
+#endif
diff --git a/drmresources.cpp b/drmresources.cpp
index 3da0f04..3429abc 100644
--- a/drmresources.cpp
+++ b/drmresources.cpp
@@ -33,7 +33,7 @@
namespace android {
-DrmResources::DrmResources() : fd_(-1), mode_id_(0) {
+DrmResources::DrmResources() : fd_(-1), mode_id_(0), compositor_(this) {
}
DrmResources::~DrmResources() {
@@ -235,7 +235,7 @@
if (ret)
return ret;
- return 0;
+ return compositor_.Init();
}
int DrmResources::fd() const {
@@ -383,6 +383,10 @@
return 0;
}
+DrmCompositor *DrmResources::compositor() {
+ return &compositor_;
+}
+
int DrmResources::GetProperty(uint32_t obj_id, uint32_t obj_type,
const char *prop_name, DrmProperty *property) {
drmModeObjectPropertiesPtr props;
diff --git a/drmresources.h b/drmresources.h
index f329bd9..1261956 100644
--- a/drmresources.h
+++ b/drmresources.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_DRM_H_
#define ANDROID_DRM_H_
+#include "drmcompositor.h"
#include "drmconnector.h"
#include "drmcrtc.h"
#include "drmencoder.h"
@@ -46,6 +47,7 @@
DrmConnector *GetConnectorForDisplay(int display) const;
DrmCrtc *GetCrtcForDisplay(int display) const;
DrmPlane *GetPlane(uint32_t id) const;
+ DrmCompositor *compositor();
int GetPlaneProperty(const DrmPlane &plane, const char *prop_name,
DrmProperty *property);
@@ -68,6 +70,7 @@
std::vector<DrmEncoder *> encoders_;
std::vector<DrmCrtc *> crtcs_;
std::vector<DrmPlane *> planes_;
+ DrmCompositor compositor_;
};
}
diff --git a/hwcomposer.cpp b/hwcomposer.cpp
index 06c0931..a809c8b 100644
--- a/hwcomposer.cpp
+++ b/hwcomposer.cpp
@@ -38,8 +38,6 @@
#include <sw_sync.h>
#include <sync/sync.h>
-#define ARRAY_SIZE(arr) (int)(sizeof(arr) / sizeof((arr)[0]))
-
#define UM_PER_INCH 25400
namespace android {
@@ -57,16 +55,6 @@
std::vector<uint32_t> config_ids;
- struct hwc_worker set_worker;
-
- std::list<struct hwc_drm_bo> buf_queue;
- struct hwc_drm_bo front;
- pthread_mutex_t flip_lock;
- pthread_cond_t flip_cond;
-
- int timeline_fd;
- unsigned timeline_next;
-
bool enable_vsync_events;
unsigned int vsync_sequence;
} hwc_drm_display_t;
@@ -93,61 +81,72 @@
Importer *importer;
};
-static int hwc_prepare_layer(hwc_layer_1_t *layer) {
- /* TODO: We can't handle background right now, defer to sufaceFlinger */
- if (layer->compositionType == HWC_BACKGROUND) {
- layer->compositionType = HWC_FRAMEBUFFER;
- ALOGV("Can't handle background layers yet");
-
- /* TODO: Support sideband compositions */
- } else if (layer->compositionType == HWC_SIDEBAND) {
- layer->compositionType = HWC_FRAMEBUFFER;
- ALOGV("Can't handle sideband content yet");
- }
-
- layer->hints = 0;
-
- /* TODO: Handle cursor by setting compositionType=HWC_CURSOR_OVERLAY */
- if (layer->flags & HWC_IS_CURSOR_LAYER) {
- ALOGV("Can't handle async cursors yet");
- }
-
- /* TODO: Handle transformations */
- if (layer->transform) {
- ALOGV("Can't handle transformations yet");
- }
-
- /* TODO: Handle blending & plane alpha*/
- if (layer->blending == HWC_BLENDING_PREMULT ||
- layer->blending == HWC_BLENDING_COVERAGE) {
- ALOGV("Can't handle blending yet");
- }
-
- /* TODO: Handle cropping & scaling */
-
- return 0;
-}
-
-static int hwc_prepare(hwc_composer_device_1_t * /* dev */, size_t num_displays,
+static int hwc_prepare(hwc_composer_device_1_t *dev, size_t num_displays,
hwc_display_contents_1_t **display_contents) {
- /* TODO: Check flags for HWC_GEOMETRY_CHANGED */
+ // XXX: Once we have a GL compositor, just make everything HWC_OVERLAY
+ struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
+ Composition *composition =
+ ctx->drm.compositor()->CreateComposition(ctx->importer);
+ if (!composition) {
+ ALOGE("Drm composition init failed");
+ return -EINVAL;
+ }
for (int i = 0; i < (int)num_displays; ++i) {
if (!display_contents[i])
continue;
- for (int j = 0; j < (int)display_contents[i]->numHwLayers; ++j) {
- int ret = hwc_prepare_layer(&display_contents[i]->hwLayers[j]);
- if (ret) {
- ALOGE("Failed to prepare layer %d:%d", j, i);
- return ret;
- }
+ int num_layers = display_contents[i]->numHwLayers;
+ int num_planes = composition->GetRemainingLayers(i, num_layers);
+
+ // XXX: Should go away with atomic modeset
+ DrmCrtc *crtc = ctx->drm.GetCrtcForDisplay(i);
+ if (!crtc) {
+ ALOGE("No crtc for display %d", i);
+ delete composition;
+ return -ENODEV;
+ }
+ if (crtc->requires_modeset())
+ num_planes = 0;
+
+ for (int j = std::max(0, num_layers - num_planes); j < num_layers; j++) {
+ if (j >= num_planes)
+ break;
+
+ hwc_layer_1_t *layer = &display_contents[i]->hwLayers[j];
+ if (layer->compositionType == HWC_FRAMEBUFFER)
+ layer->compositionType = HWC_OVERLAY;
}
}
+ delete composition;
return 0;
}
+static void hwc_set_cleanup(size_t num_displays,
+ hwc_display_contents_1_t **display_contents,
+ Composition *composition) {
+ for (int i = 0; i < (int)num_displays; ++i) {
+ if (!display_contents[i])
+ continue;
+
+ hwc_display_contents_1_t *dc = display_contents[i];
+ for (size_t j = 0; j < dc->numHwLayers; ++j) {
+ hwc_layer_1_t *layer = &dc->hwLayers[j];
+ if (layer->acquireFenceFd >= 0) {
+ close(layer->acquireFenceFd);
+ layer->acquireFenceFd = -1;
+ }
+ }
+ if (dc->outbufAcquireFenceFd >= 0) {
+ close(dc->outbufAcquireFenceFd);
+ dc->outbufAcquireFenceFd = -1;
+ }
+ }
+
+ delete composition;
+}
+
static int hwc_queue_vblank_event(struct hwc_drm_display *hd) {
DrmCrtc *crtc = hd->ctx->drm.GetCrtcForDisplay(hd->display);
if (!crtc) {
@@ -199,29 +198,6 @@
hd->ctx->procs->vsync(hd->ctx->procs, hd->display, timestamp);
}
-static void hwc_flip_event_handler(int /* fd */, unsigned int /* sequence */,
- unsigned int /* tv_sec */,
- unsigned int /* tv_usec */,
- void *user_data) {
- struct hwc_drm_display *hd = (struct hwc_drm_display *)user_data;
-
- int ret = pthread_mutex_lock(&hd->flip_lock);
- if (ret) {
- ALOGE("Failed to lock flip lock ret=%d", ret);
- return;
- }
-
- ret = pthread_cond_signal(&hd->flip_cond);
- if (ret)
- ALOGE("Failed to signal flip condition ret=%d", ret);
-
- ret = pthread_mutex_unlock(&hd->flip_lock);
- if (ret) {
- ALOGE("Failed to unlock flip lock ret=%d", ret);
- return;
- }
-}
-
static void *hwc_event_worker(void *arg) {
setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
@@ -233,7 +209,7 @@
drmEventContext event_context;
event_context.version = DRM_EVENT_CONTEXT_VERSION;
- event_context.page_flip_handler = hwc_flip_event_handler;
+ event_context.page_flip_handler = NULL;
event_context.vblank_handler = hwc_vblank_event_handler;
int ret;
@@ -252,272 +228,95 @@
return NULL;
}
-static bool hwc_mode_is_equal(drmModeModeInfoPtr a, drmModeModeInfoPtr b) {
- return a->clock == b->clock && a->hdisplay == b->hdisplay &&
- a->hsync_start == b->hsync_start && a->hsync_end == b->hsync_end &&
- a->htotal == b->htotal && a->hskew == b->hskew &&
- a->vdisplay == b->vdisplay && a->vsync_start == b->vsync_start &&
- a->vsync_end == b->vsync_end && a->vtotal == b->vtotal &&
- a->vscan == b->vscan && a->vrefresh == b->vrefresh &&
- a->flags == b->flags && a->type == b->type &&
- !strcmp(a->name, b->name);
-}
-
-static int hwc_flip(struct hwc_drm_display *hd, struct hwc_drm_bo *buf) {
- DrmCrtc *crtc = hd->ctx->drm.GetCrtcForDisplay(hd->display);
- if (!crtc) {
- ALOGE("Failed to get crtc for display %d", hd->display);
- return -ENODEV;
+static int hwc_add_layer(int display, hwc_context_t *ctx, hwc_layer_1_t *layer,
+ Composition *composition) {
+ hwc_drm_bo_t bo;
+ int ret = ctx->importer->ImportBuffer(layer->handle, &bo);
+ if (ret) {
+ ALOGE("Failed to import handle to bo %d", ret);
+ return ret;
}
- DrmConnector *connector = hd->ctx->drm.GetConnectorForDisplay(hd->display);
- if (!connector) {
- ALOGE("Failed to get connector for display %d", hd->display);
- return -ENODEV;
- }
-
- int ret;
- if (crtc->requires_modeset()) {
- drmModeModeInfo drm_mode;
- connector->active_mode().ToModeModeInfo(&drm_mode);
- uint32_t connector_id = connector->id();
- ret = drmModeSetCrtc(hd->ctx->drm.fd(), crtc->id(), buf->fb_id, 0, 0,
- &connector_id, 1, &drm_mode);
- if (ret) {
- ALOGE("Modeset failed for crtc %d", crtc->id());
- return ret;
- }
- crtc->set_requires_modeset(false);
+ ret = composition->AddLayer(display, layer, &bo);
+ if (!ret)
return 0;
- }
- ret = drmModePageFlip(hd->ctx->drm.fd(), crtc->id(), buf->fb_id,
- DRM_MODE_PAGE_FLIP_EVENT, hd);
- if (ret) {
- ALOGE("Failed to flip buffer for crtc %d", crtc->id());
- return ret;
- }
+ int destroy_ret = ctx->importer->ReleaseBuffer(&bo);
+ if (destroy_ret)
+ ALOGE("Failed to destroy buffer %d", destroy_ret);
- ret = pthread_cond_wait(&hd->flip_cond, &hd->flip_lock);
- if (ret) {
- ALOGE("Failed to wait on condition %d", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int hwc_wait_and_set(struct hwc_drm_display *hd,
- struct hwc_drm_bo *buf) {
- int ret;
- if (buf->acquire_fence_fd >= 0) {
- ret = sync_wait(buf->acquire_fence_fd, -1);
- close(buf->acquire_fence_fd);
- buf->acquire_fence_fd = -1;
- if (ret) {
- ALOGE("Failed to wait for acquire %d", ret);
- return ret;
- }
- }
-
- ret = hwc_flip(hd, buf);
- if (ret) {
- ALOGE("Failed to perform flip\n");
- return ret;
- }
-
- if (!hd->ctx->importer->ReleaseBuffer(buf)) {
- struct drm_gem_close args;
- memset(&args, 0, sizeof(args));
- for (int i = 0; i < ARRAY_SIZE(hd->front.gem_handles); ++i) {
- if (!hd->front.gem_handles[i])
- continue;
-
- ret = pthread_mutex_lock(&hd->set_worker.lock);
- if (ret) {
- ALOGE("Failed to lock set lock in wait_and_set() %d", ret);
- continue;
- }
-
- /* check for duplicate handle in buf_queue */
- bool found = false;
- for (std::list<struct hwc_drm_bo>::iterator bi = hd->buf_queue.begin();
- bi != hd->buf_queue.end(); ++bi)
- for (int j = 0; j < ARRAY_SIZE(bi->gem_handles); ++j)
- if (hd->front.gem_handles[i] == bi->gem_handles[j])
- found = true;
-
- for (int j = 0; j < ARRAY_SIZE(buf->gem_handles); ++j)
- if (hd->front.gem_handles[i] == buf->gem_handles[j])
- found = true;
-
- if (!found) {
- args.handle = hd->front.gem_handles[i];
- drmIoctl(hd->ctx->drm.fd(), DRM_IOCTL_GEM_CLOSE, &args);
- }
- if (pthread_mutex_unlock(&hd->set_worker.lock))
- ALOGE("Failed to unlock set lock in wait_and_set() %d", ret);
- }
- }
-
- hd->front = *buf;
-
- return ret;
-}
-
-static void *hwc_set_worker(void *arg) {
- setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
-
- struct hwc_drm_display *hd = (struct hwc_drm_display *)arg;
- int ret = pthread_mutex_lock(&hd->flip_lock);
- if (ret) {
- ALOGE("Failed to lock flip lock ret=%d", ret);
- return NULL;
- }
-
- do {
- ret = pthread_mutex_lock(&hd->set_worker.lock);
- if (ret) {
- ALOGE("Failed to lock set lock %d", ret);
- return NULL;
- }
-
- if (hd->set_worker.exit)
- break;
-
- if (hd->buf_queue.empty()) {
- ret = pthread_cond_wait(&hd->set_worker.cond, &hd->set_worker.lock);
- if (ret) {
- ALOGE("Failed to wait on condition %d", ret);
- break;
- }
- }
-
- struct hwc_drm_bo buf;
- buf = hd->buf_queue.front();
- hd->buf_queue.pop_front();
-
- ret = pthread_mutex_unlock(&hd->set_worker.lock);
- if (ret) {
- ALOGE("Failed to unlock set lock %d", ret);
- return NULL;
- }
-
- ret = hwc_wait_and_set(hd, &buf);
- if (ret)
- ALOGE("Failed to wait and set %d", ret);
-
- ret = sw_sync_timeline_inc(hd->timeline_fd, 1);
- if (ret)
- ALOGE("Failed to increment sync timeline %d", ret);
- } while (true);
-
- ret = pthread_mutex_unlock(&hd->set_worker.lock);
- if (ret)
- ALOGE("Failed to unlock set lock while exiting %d", ret);
-
- ret = pthread_mutex_unlock(&hd->flip_lock);
- if (ret)
- ALOGE("Failed to unlock flip lock ret=%d", ret);
-
- return NULL;
-}
-
-static void hwc_close_fences(hwc_display_contents_1_t *display_contents) {
- for (int i = 0; i < (int)display_contents->numHwLayers; ++i) {
- hwc_layer_1_t *layer = &display_contents->hwLayers[i];
- if (layer->acquireFenceFd >= 0) {
- close(layer->acquireFenceFd);
- layer->acquireFenceFd = -1;
- }
- }
- if (display_contents->outbufAcquireFenceFd >= 0) {
- close(display_contents->outbufAcquireFenceFd);
- display_contents->outbufAcquireFenceFd = -1;
- }
-}
-
-static int hwc_set_display(hwc_context_t *ctx, int display,
- hwc_display_contents_1_t *display_contents) {
- struct hwc_drm_display *hd = &ctx->displays[display];
- DrmCrtc *crtc = hd->ctx->drm.GetCrtcForDisplay(display);
- if (!crtc) {
- ALOGE("There is no active crtc for display %d", display);
- hwc_close_fences(display_contents);
- return -ENOENT;
- }
-
- /*
- * TODO: We can only support one hw layer atm, so choose either the
- * first one or the framebuffer target.
- */
- hwc_layer_1_t *layer = NULL;
- if (!display_contents->numHwLayers) {
- return 0;
- } else if (display_contents->numHwLayers == 1) {
- layer = &display_contents->hwLayers[0];
- } else {
- int i;
- for (i = 0; i < (int)display_contents->numHwLayers; ++i) {
- layer = &display_contents->hwLayers[i];
- if (layer->compositionType == HWC_FRAMEBUFFER_TARGET)
- break;
- }
- if (i == (int)display_contents->numHwLayers) {
- ALOGE("Could not find a suitable layer for display %d", display);
- }
- }
-
- int ret = pthread_mutex_lock(&hd->set_worker.lock);
- if (ret) {
- ALOGE("Failed to lock set lock in set() %d", ret);
- hwc_close_fences(display_contents);
- return ret;
- }
-
- struct hwc_drm_bo buf;
- ret = ctx->importer->ImportBuffer(layer->handle, &buf);
- if (ret) {
- ALOGE("Failed to import handle to drm bo %d", ret);
- hwc_close_fences(display_contents);
- return ret;
- }
- buf.acquire_fence_fd = layer->acquireFenceFd;
- layer->acquireFenceFd = -1;
-
- /*
- * TODO: Retire and release can use the same sync point here b/c hwc is
- * restricted to one layer. Once that is no longer true, this will need
- * to change
- */
- ++hd->timeline_next;
- display_contents->retireFenceFd = sw_sync_fence_create(
- hd->timeline_fd, "drm_hwc_retire", hd->timeline_next);
- layer->releaseFenceFd = sw_sync_fence_create(
- hd->timeline_fd, "drm_hwc_release", hd->timeline_next);
- hd->buf_queue.push_back(buf);
-
- ret = pthread_cond_signal(&hd->set_worker.cond);
- if (ret)
- ALOGE("Failed to signal set worker %d", ret);
-
- if (pthread_mutex_unlock(&hd->set_worker.lock))
- ALOGE("Failed to unlock set lock in set()");
-
- hwc_close_fences(display_contents);
return ret;
}
static int hwc_set(hwc_composer_device_1_t *dev, size_t num_displays,
hwc_display_contents_1_t **display_contents) {
struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
-
- int ret = 0;
- for (int i = 0; i < (int)num_displays; ++i) {
- if (display_contents[i])
- ret = hwc_set_display(ctx, i, display_contents[i]);
+ Composition *composition =
+ ctx->drm.compositor()->CreateComposition(ctx->importer);
+ if (!composition) {
+ ALOGE("Drm composition init failed");
+ hwc_set_cleanup(num_displays, display_contents, NULL);
+ return -EINVAL;
}
+ int ret;
+ for (int i = 0; i < (int)num_displays; ++i) {
+ if (!display_contents[i])
+ continue;
+
+ DrmCrtc *crtc = ctx->drm.GetCrtcForDisplay(i);
+ if (!crtc) {
+ ALOGE("No crtc for display %d", i);
+ hwc_set_cleanup(num_displays, display_contents, composition);
+ return -ENODEV;
+ }
+
+ hwc_display_contents_1_t *dc = display_contents[i];
+ unsigned num_layers = dc->numHwLayers;
+ unsigned num_planes = composition->GetRemainingLayers(i, num_layers);
+ bool use_target = false;
+ // XXX: We don't need to check for modeset required with atomic modeset
+ if (crtc->requires_modeset() || num_layers > num_planes)
+ use_target = true;
+
+ // XXX: Won't need to worry about FB_TARGET with GL Compositor
+ for (int j = 0; use_target && j < (int)num_layers; ++j) {
+ hwc_layer_1_t *layer = &dc->hwLayers[j];
+ if (layer->compositionType != HWC_FRAMEBUFFER_TARGET)
+ continue;
+
+ ret = hwc_add_layer(i, ctx, layer, composition);
+ if (ret) {
+ ALOGE("Add layer failed %d", ret);
+ hwc_set_cleanup(num_displays, display_contents, composition);
+ return ret;
+ }
+ --num_planes;
+ break;
+ }
+
+ for (int j = 0; num_planes && j < (int)num_layers; ++j) {
+ hwc_layer_1_t *layer = &dc->hwLayers[j];
+ if (layer->compositionType != HWC_OVERLAY)
+ continue;
+
+ ret = hwc_add_layer(i, ctx, layer, composition);
+ if (ret) {
+ ALOGE("Add layer failed %d", ret);
+ hwc_set_cleanup(num_displays, display_contents, composition);
+ return ret;
+ }
+ --num_planes;
+ }
+ }
+
+ ret = ctx->drm.compositor()->QueueComposition(composition);
+ if (ret) {
+ ALOGE("Failed to queue the composition");
+ hwc_set_cleanup(num_displays, display_contents, composition);
+ return ret;
+ }
+ hwc_set_cleanup(num_displays, display_contents, NULL);
return ret;
}
@@ -744,18 +543,9 @@
return ret;
}
-static void hwc_destroy_display(struct hwc_drm_display *hd) {
- if (hwc_destroy_worker(&hd->set_worker))
- ALOGE("Destroy set worker failed");
-}
-
static int hwc_device_close(struct hw_device_t *dev) {
struct hwc_context_t *ctx = (struct hwc_context_t *)dev;
- for (hwc_context_t::DisplayMapIter iter = ctx->displays.begin();
- iter != ctx->displays.end(); ++iter)
- hwc_destroy_display(&iter->second);
-
if (hwc_destroy_worker(&ctx->event_worker))
ALOGE("Destroy event worker failed");
@@ -819,65 +609,15 @@
hd->enable_vsync_events = false;
hd->vsync_sequence = 0;
- int ret = pthread_mutex_init(&hd->flip_lock, NULL);
- if (ret) {
- ALOGE("Failed to initialize flip lock %d", ret);
- return ret;
- }
-
- ret = pthread_cond_init(&hd->flip_cond, NULL);
- if (ret) {
- ALOGE("Failed to intiialize flip condition %d", ret);
- pthread_mutex_destroy(&hd->flip_lock);
- return ret;
- }
-
- ret = sw_sync_timeline_create();
- if (ret < 0) {
- ALOGE("Failed to create sw sync timeline %d", ret);
- pthread_cond_destroy(&hd->flip_cond);
- pthread_mutex_destroy(&hd->flip_lock);
- return ret;
- }
- hd->timeline_fd = ret;
-
- /*
- * Initialize timeline_next to 1, because point 0 will be the very first
- * set operation. Since we increment every time set() is called,
- * initializing to 0 would cause an off-by-one error where
- * surfaceflinger would composite on the front buffer.
- */
- hd->timeline_next = 1;
-
- ret = hwc_set_initial_config(hd);
+ int ret = hwc_set_initial_config(hd);
if (ret) {
ALOGE("Failed to set initial config for d=%d ret=%d", display, ret);
- close(hd->timeline_fd);
- pthread_cond_destroy(&hd->flip_cond);
- pthread_mutex_destroy(&hd->flip_lock);
- return ret;
- }
-
- ret = hwc_initialize_worker(&hd->set_worker, hwc_set_worker, hd);
- if (ret) {
- ALOGE("Failed to create set worker %d\n", ret);
- close(hd->timeline_fd);
- pthread_cond_destroy(&hd->flip_cond);
- pthread_mutex_destroy(&hd->flip_lock);
return ret;
}
return 0;
}
-static void hwc_free_conn_list(drmModeConnectorPtr *conn_list, int num_conn) {
- for (int i = 0; i < num_conn; ++i) {
- if (conn_list[i])
- drmModeFreeConnector(conn_list[i]);
- }
- free(conn_list);
-}
-
static int hwc_enumerate_displays(struct hwc_context_t *ctx) {
int ret;
for (DrmResources::ConnectorIter c = ctx->drm.begin_connectors();