OSDN Git Service

drm_hwcomposer: Split the drm compositor into per-display threads
authorSean Paul <seanpaul@chromium.org>
Wed, 24 Jun 2015 21:38:49 +0000 (14:38 -0700)
committerSean Paul <seanpaul@chromium.org>
Fri, 10 Jul 2015 20:09:10 +0000 (16:09 -0400)
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

Android.mk
drmcomposition.cpp
drmcomposition.h
drmcompositor.cpp
drmcompositor.h
drmcompositorworker.cpp
drmcompositorworker.h
drmdisplaycomposition.cpp [new file with mode: 0644]
drmdisplaycomposition.h [new file with mode: 0644]
drmdisplaycompositor.cpp [new file with mode: 0644]
drmdisplaycompositor.h [new file with mode: 0644]

index e20615e..eadfe2b 100644 (file)
@@ -44,6 +44,8 @@ LOCAL_SRC_FILES := \
         drmcompositorworker.cpp \
        drmconnector.cpp \
        drmcrtc.cpp \
+        drmdisplaycomposition.cpp \
+        drmdisplaycompositor.cpp \
        drmencoder.cpp \
        drmmode.cpp \
        drmplane.cpp \
index 07e08da..37e3bad 100644 (file)
@@ -31,22 +31,8 @@ namespace android {
 
 static const bool kUseOverlayPlanes = true;
 
-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) {
+DrmComposition::DrmComposition(DrmResources *drm, Importer *importer)
+    : drm_(drm), importer_(importer) {
   for (DrmResources::PlaneIter iter = drm_->begin_planes();
        iter != drm_->end_planes(); ++iter) {
     if ((*iter)->type() == DRM_PLANE_TYPE_PRIMARY)
@@ -57,25 +43,23 @@ DrmComposition::DrmComposition(DrmResources *drm, Importer *importer,
 }
 
 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;
+  for (DrmResources::ConnectorIter iter = drm_->begin_connectors();
+       iter != drm_->end_connectors(); ++iter) {
+    int display = (*iter)->display();
+    composition_map_[display].reset(new DrmDisplayComposition());
+    if (!composition_map_[display]) {
+      ALOGE("Failed to allocate new display composition\n");
+      return -ENOMEM;
+    }
+    int ret = composition_map_[(*iter)->display()]->Init(drm_, importer_);
+    if (ret) {
+      ALOGE("Failed to init display composition for %d", (*iter)->display());
+      return ret;
+    }
   }
-  timeline_fd_ = ret;
   return 0;
 }
 
@@ -83,7 +67,7 @@ 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);
+    ALOGE("Failed to find crtc for display %d", display);
     return 0;
   }
 
@@ -103,65 +87,39 @@ unsigned DrmComposition::GetRemainingLayers(int display,
 
 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);
+    ALOGE("Failed to 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
+  // Find a plane for the layer
+  DrmPlane *plane = NULL;
   for (std::vector<DrmPlane *>::iterator iter = primary_planes_.begin();
        iter != primary_planes_.end(); ++iter) {
     if ((*iter)->GetCrtcSupported(*crtc)) {
-      c_layer.plane = (*iter);
+      plane = *iter;
       primary_planes_.erase(iter);
       break;
     }
   }
   for (std::deque<DrmPlane *>::iterator iter = overlay_planes_.begin();
-       !c_layer.plane && iter != overlay_planes_.end(); ++iter) {
+       !plane && iter != overlay_planes_.end(); ++iter) {
     if ((*iter)->GetCrtcSupported(*crtc)) {
-      c_layer.plane = (*iter);
+      plane = *iter;
       overlay_planes_.erase(iter);
       break;
     }
   }
-  if (!c_layer.plane) {
-    close(layer->releaseFenceFd);
-    layer->releaseFenceFd = -1;
+  if (!plane) {
+    ALOGE("Failed to find plane for display %d", display);
     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;
+  return composition_map_[display]->AddLayer(layer, bo, crtc, plane);
 }
 
-DrmCompositionLayerMap_t *DrmComposition::GetCompositionMap() {
-  return &composition_map_;
+std::unique_ptr<DrmDisplayComposition> DrmComposition::TakeDisplayComposition(
+    int display) {
+  return std::move(composition_map_[display]);
 }
 }
index 5c47fbf..a8f9836 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "compositor.h"
 #include "drm_hwcomposer.h"
+#include "drmdisplaycomposition.h"
 #include "drmplane.h"
 #include "importer.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(DrmResources *drm, Importer *importer);
   ~DrmComposition();
 
   virtual int Init();
@@ -54,23 +42,24 @@ class DrmComposition : public Composition {
   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();
+  std::unique_ptr<DrmDisplayComposition> TakeDisplayComposition(int display);
 
  private:
+  DrmComposition(const DrmComposition &) = delete;
+
   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_;
+
+  /*
+   * This _must_ be read-only after it's passed to QueueComposition. Otherwise
+   * locking is required to maintain consistency across the compositor threads.
+   */
+  std::map<int, std::unique_ptr<DrmDisplayComposition>> composition_map_;
 };
+
 }
 
 #endif  // ANDROID_DRM_COMPOSITION_H_
index 0c6d445..4268ab5 100644 (file)
  * limitations under the License.
  */
 
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #define LOG_TAG "hwc-drm-compositor"
 
 #include "drmcompositor.h"
-#include "drmcrtc.h"
-#include "drmplane.h"
+#include "drmdisplaycompositor.h"
 #include "drmresources.h"
 
-#include <pthread.h>
 #include <sstream>
 #include <stdlib.h>
-#include <time.h>
-#include <utils/Trace.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),
-      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;
+DrmCompositor::DrmCompositor(DrmResources *drm) : drm_(drm) {
 }
 
 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;
+  for (DrmResources::ConnectorIter iter = drm_->begin_connectors();
+       iter != drm_->end_connectors(); ++iter) {
+    int display = (*iter)->display();
+    int ret = compositor_map_[display].Init(drm_, display);
+    if (ret) {
+      ALOGE("Failed to initialize display compositor for %d", display);
+      return ret;
+    }
   }
 
-  initialized_ = true;
   return 0;
 }
 
 Composition *DrmCompositor::CreateComposition(Importer *importer) {
-  DrmComposition *composition = new DrmComposition(drm_, importer, frame_no_++);
+  DrmComposition *composition = new DrmComposition(drm_, importer);
   if (!composition) {
     ALOGE("Failed to allocate drm composition");
     return NULL;
@@ -85,187 +63,34 @@ Composition *DrmCompositor::CreateComposition(Importer *importer) {
 }
 
 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::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;
-  }
-
-  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(),
-                              begin->second.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() {
-  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;
-  }
-
-  DrmComposition *composition = composite_queue_.front();
-  composite_queue_.pop();
-  ++dump_frames_composited_;
-
-  ret = pthread_mutex_unlock(&lock_);
-  if (ret) {
-    ALOGE("Failed to release compositor lock %d", ret);
-    return ret;
-  }
-
-  DrmCompositionLayerMap_t *map = composition->GetCompositionMap();
+  DrmComposition *drm_composition = (DrmComposition *)composition;
   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;
-      }
+    int ret = compositor_map_[display].QueueComposition(
+        drm_composition->TakeDisplayComposition(display));
+    if (ret) {
+      ALOGE("Failed to queue composition for display %d", display);
+      return ret;
     }
   }
 
-  if (active_composition_) {
-    active_composition_->FinishComposition();
-    delete active_composition_;
-  }
-  active_composition_ = composition;
-  return ret;
+  return 0;
 }
 
-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;
+int DrmCompositor::Composite() {
+  /*
+   * This shouldn't be called, we should be calling Composite() on the display
+   * compositors directly.
+   */
+  ALOGE("Calling base drm compositor Composite() function");
+  return -EINVAL;
 }
 
 void DrmCompositor::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 << "DrmCompositor: num_frames=" << num_frames << " num_ms=" << num_ms <<
-          " fps=" << fps << "\n";
-
-  dump_last_timestamp_ns_ = cur_ts;
+  *out << "DrmCompositor stats:\n";
+  for (DrmResources::ConnectorIter iter = drm_->begin_connectors();
+       iter != drm_->end_connectors(); ++iter)
+    compositor_map_[(*iter)->display()].Dump(out);
 }
 }
index 2d4eb69..5f47034 100644 (file)
 #define ANDROID_DRM_COMPOSITOR_H_
 
 #include "compositor.h"
-#include "drm_hwcomposer.h"
-#include "drmcomposition.h"
-#include "drmcompositorworker.h"
-#include "drmplane.h"
+#include "drmdisplaycompositor.h"
 #include "importer.h"
 
-#include <pthread.h>
-#include <queue>
+#include <map>
 #include <sstream>
 
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
 namespace android {
 
-class Drm;
-
 class DrmCompositor : public Compositor {
  public:
   DrmCompositor(DrmResources *drm);
@@ -52,32 +43,13 @@ class DrmCompositor : public Compositor {
   virtual int Composite();
   virtual void Dump(std::ostringstream *out) const;
 
-  bool HaveQueuedComposites() const;
-
  private:
-  DrmCompositor(const DrmCompositor &);
-
-  int CompositeDisplay(DrmCompositionLayerMap_t::iterator begin,
-                       DrmCompositionLayerMap_t::iterator end);
+  DrmCompositor(const DrmCompositor &) = delete;
 
   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_;
-
-  // State tracking progress since our last Dump(). These are mutable since
-  // we need to reset them on every Dump() call.
-  mutable uint64_t dump_frames_composited_;
-  mutable uint64_t dump_last_timestamp_ns_;
+  // mutable for Dump() propagation
+  mutable std::map<int, DrmDisplayCompositor> compositor_map_;
 };
 }
 
index 9406194..c8eae5f 100644 (file)
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "hwc-drm-compositor-worker"
 
-#include "drmcompositor.h"
+#include "drmdisplaycompositor.h"
 #include "drmcompositorworker.h"
 #include "worker.h"
 
@@ -27,7 +27,7 @@
 
 namespace android {
 
-DrmCompositorWorker::DrmCompositorWorker(DrmCompositor *compositor)
+DrmCompositorWorker::DrmCompositorWorker(DrmDisplayCompositor *compositor)
     : Worker("drm-compositor", HAL_PRIORITY_URGENT_DISPLAY),
       compositor_(compositor) {
 }
index 8b485bf..9f74fb6 100644 (file)
 
 namespace android {
 
-class DrmCompositor;
+class DrmDisplayCompositor;
 
 class DrmCompositorWorker : public Worker {
  public:
-  DrmCompositorWorker(DrmCompositor *compositor);
+  DrmCompositorWorker(DrmDisplayCompositor *compositor);
   ~DrmCompositorWorker();
 
   int Init();
@@ -33,7 +33,7 @@ class DrmCompositorWorker : public Worker {
  protected:
   virtual void Routine();
 
-  DrmCompositor *compositor_;
+  DrmDisplayCompositor *compositor_;
 };
 }
 
diff --git a/drmdisplaycomposition.cpp b/drmdisplaycomposition.cpp
new file mode 100644 (file)
index 0000000..75046fb
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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-display-composition"
+
+#include "drmdisplaycomposition.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 {
+
+DrmCompositionLayer::DrmCompositionLayer() : crtc(NULL), plane(NULL) {
+  memset(&layer, 0, sizeof(layer));
+  layer.acquireFenceFd = -1;
+  memset(&bo, 0, sizeof(bo));
+}
+
+DrmCompositionLayer::~DrmCompositionLayer() {
+}
+
+DrmDisplayComposition::DrmDisplayComposition()
+    : drm_(NULL), importer_(NULL), timeline_fd_(-1), timeline_(0) {
+}
+
+DrmDisplayComposition::~DrmDisplayComposition() {
+  for (DrmCompositionLayerVector_t::iterator iter = layers_.begin();
+       iter != layers_.end(); ++iter) {
+    if (importer_)
+      importer_->ReleaseBuffer(&iter->bo);
+
+    if (iter->layer.acquireFenceFd >= 0)
+      close(iter->layer.acquireFenceFd);
+  }
+
+  if (timeline_fd_ >= 0)
+    close(timeline_fd_);
+}
+
+int DrmDisplayComposition::Init(DrmResources *drm, Importer *importer) {
+  drm_ = drm;
+  importer_ = importer;
+
+  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;
+}
+
+int DrmDisplayComposition::AddLayer(hwc_layer_1_t *layer, hwc_drm_bo_t *bo,
+                                    DrmCrtc *crtc, DrmPlane *plane) {
+  if (layer->transform != 0)
+    return -EINVAL;
+
+  ++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;
+  c_layer.plane = plane;
+
+  layer->acquireFenceFd = -1;  // We own this now
+  layers_.push_back(c_layer);
+  return 0;
+}
+
+int DrmDisplayComposition::FinishComposition() {
+  int ret = sw_sync_timeline_inc(timeline_fd_, timeline_);
+  if (ret)
+    ALOGE("Failed to increment sync timeline %d", ret);
+
+  return ret;
+}
+
+DrmCompositionLayerVector_t *DrmDisplayComposition::GetCompositionLayers() {
+  return &layers_;
+}
+}
diff --git a/drmdisplaycomposition.h b/drmdisplaycomposition.h
new file mode 100644 (file)
index 0000000..cf03360
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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_DISPLAY_COMPOSITION_H_
+#define ANDROID_DRM_DISPLAY_COMPOSITION_H_
+
+#include "drm_hwcomposer.h"
+#include "drmplane.h"
+#include "importer.h"
+
+#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::vector<DrmCompositionLayer_t> DrmCompositionLayerVector_t;
+
+class DrmDisplayComposition {
+ public:
+  DrmDisplayComposition();
+  ~DrmDisplayComposition();
+
+  int Init(DrmResources *drm, Importer *importer);
+
+  int AddLayer(hwc_layer_1_t *layer, hwc_drm_bo_t *bo, DrmCrtc *crtc,
+               DrmPlane *plane);
+
+  int FinishComposition();
+
+  DrmCompositionLayerVector_t *GetCompositionLayers();
+
+ private:
+  DrmDisplayComposition(const DrmDisplayComposition &) = delete;
+
+  DrmResources *drm_;
+  Importer *importer_;
+
+  int timeline_fd_;
+  int timeline_;
+
+  DrmCompositionLayerVector_t layers_;
+};
+}
+
+#endif  // ANDROID_DRM_DISPLAY_COMPOSITION_H_
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
new file mode 100644 (file)
index 0000000..2b4b4df
--- /dev/null
@@ -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;
+}
+}
diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
new file mode 100644 (file)
index 0000000..928ce46
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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_DISPLAY_COMPOSITOR_H_
+#define ANDROID_DRM_DISPLAY_COMPOSITOR_H_
+
+#include "drm_hwcomposer.h"
+#include "drmcomposition.h"
+#include "drmcompositorworker.h"
+
+#include <pthread.h>
+#include <queue>
+#include <sstream>
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+
+namespace android {
+
+class DrmDisplayCompositor {
+ public:
+  DrmDisplayCompositor();
+  ~DrmDisplayCompositor();
+
+  int Init(DrmResources *drm, int display);
+
+  int QueueComposition(std::unique_ptr<DrmDisplayComposition> composition);
+  int Composite();
+  void Dump(std::ostringstream *out) const;
+
+  bool HaveQueuedComposites() const;
+
+ private:
+  DrmDisplayCompositor(const DrmDisplayCompositor &) = delete;
+
+  int ApplyFrame(DrmDisplayComposition *display_comp);
+
+  DrmResources *drm_;
+  int display_;
+
+  DrmCompositorWorker worker_;
+
+  std::queue<std::unique_ptr<DrmDisplayComposition>> composite_queue_;
+  std::unique_ptr<DrmDisplayComposition> active_composition_;
+
+  uint64_t frame_no_;
+
+  bool initialized_;
+
+  // mutable since we need to acquire in HaveQueuedComposites
+  mutable pthread_mutex_t lock_;
+
+  // State tracking progress since our last Dump(). These are mutable since
+  // we need to reset them on every Dump() call.
+  mutable uint64_t dump_frames_composited_;
+  mutable uint64_t dump_last_timestamp_ns_;
+};
+}
+
+#endif  // ANDROID_DRM_DISPLAY_COMPOSITOR_H_