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/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();