X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=hwcomposer.cpp;h=b2e96433e37f126c32ac277564dcdc78a313a435;hb=637699644d49ef96e2243ddab67ee6a795800b1b;hp=891955d7c17a522e6ce39abbac1994402247fe08;hpb=e42febfa7feedb74ec239413820008bb38804505;p=android-x86%2Fexternal-drm_hwcomposer.git diff --git a/hwcomposer.cpp b/hwcomposer.cpp index 891955d..b2e9643 100644 --- a/hwcomposer.cpp +++ b/hwcomposer.cpp @@ -14,17 +14,26 @@ * limitations under the License. */ +#define ATRACE_TAG ATRACE_TAG_GRAPHICS #define LOG_TAG "hwcomposer-drm" -#include "drm_hwcomposer.h" +#include "drmhwcomposer.h" +#include "drmeventlistener.h" #include "drmresources.h" +#include "platform.h" +#include "virtualcompositorworker.h" +#include "vsyncworker.h" + +#include + +#include +#include +#include +#include #include #include -#include -#include #include -#include #include #include #include @@ -36,517 +45,598 @@ #include #include #include - -#define ARRAY_SIZE(arr) (int)(sizeof(arr) / sizeof((arr)[0])) +#include #define UM_PER_INCH 25400 namespace android { -struct hwc_worker { - pthread_t thread; - pthread_mutex_t lock; - pthread_cond_t cond; - bool exit; -}; - -typedef struct hwc_drm_display { - struct hwc_context_t *ctx; - int display; +class DummySwSyncTimeline { + public: + int Init() { + int ret = timeline_fd_.Set(sw_sync_timeline_create()); + if (ret < 0) + return ret; + return 0; + } - std::vector config_ids; + UniqueFd CreateDummyFence() { + int ret = sw_sync_fence_create(timeline_fd_.get(), "dummy fence", + timeline_pt_ + 1); + if (ret < 0) { + ALOGE("Failed to create dummy fence %d", ret); + return ret; + } - struct hwc_worker set_worker; + UniqueFd ret_fd(ret); - std::list buf_queue; - struct hwc_drm_bo front; - pthread_mutex_t flip_lock; - pthread_cond_t flip_cond; + ret = sw_sync_timeline_inc(timeline_fd_.get(), 1); + if (ret) { + ALOGE("Failed to increment dummy sync timeline %d", ret); + return ret; + } - int timeline_fd; - unsigned timeline_next; + ++timeline_pt_; + return ret_fd; + } - bool enable_vsync_events; - unsigned int vsync_sequence; -} hwc_drm_display_t; + private: + UniqueFd timeline_fd_; + int timeline_pt_ = 0; +}; -struct hwc_context_t { - // map of display:hwc_drm_display_t - typedef std::map DisplayMap; - typedef DisplayMap::iterator DisplayMapIter; +struct CheckedOutputFd { + CheckedOutputFd(int *fd, const char *description, + DummySwSyncTimeline &timeline) + : fd_(fd), description_(description), timeline_(timeline) { + } + CheckedOutputFd(CheckedOutputFd &&rhs) + : description_(rhs.description_), timeline_(rhs.timeline_) { + std::swap(fd_, rhs.fd_); + } - hwc_composer_device_1_t device; - hwc_procs_t const *procs; - struct hwc_import_context *import_ctx; + CheckedOutputFd &operator=(const CheckedOutputFd &rhs) = delete; - struct hwc_worker event_worker; + ~CheckedOutputFd() { + if (fd_ == NULL) + return; - DisplayMap displays; - DrmResources drm; -}; + if (*fd_ >= 0) + return; -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"); + *fd_ = timeline_.CreateDummyFence().Release(); - /* TODO: Support sideband compositions */ - } else if (layer->compositionType == HWC_SIDEBAND) { - layer->compositionType = HWC_FRAMEBUFFER; - ALOGV("Can't handle sideband content yet"); + if (*fd_ < 0) + ALOGE("Failed to fill %s (%p == %d) before destruction", + description_.c_str(), fd_, *fd_); } - layer->hints = 0; + private: + int *fd_ = NULL; + std::string description_; + DummySwSyncTimeline &timeline_; +}; - /* TODO: Handle cursor by setting compositionType=HWC_CURSOR_OVERLAY */ - if (layer->flags & HWC_IS_CURSOR_LAYER) { - ALOGV("Can't handle async cursors yet"); - } +typedef struct hwc_drm_display { + struct hwc_context_t *ctx; + int display; - /* TODO: Handle transformations */ - if (layer->transform) { - ALOGV("Can't handle transformations yet"); - } + std::vector config_ids; + + VSyncWorker vsync_worker; +} hwc_drm_display_t; - /* TODO: Handle blending & plane alpha*/ - if (layer->blending == HWC_BLENDING_PREMULT || - layer->blending == HWC_BLENDING_COVERAGE) { - ALOGV("Can't handle blending yet"); +class DrmHotplugHandler : public DrmEventHandler { + public: + void Init(DrmResources *drm, const struct hwc_procs *procs) { + drm_ = drm; + procs_ = procs; } - /* TODO: Handle cropping & scaling */ + void HandleEvent(uint64_t timestamp_us) { + for (auto &conn : drm_->connectors()) { + drmModeConnection old_state = conn->state(); - return 0; -} + conn->UpdateModes(); -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 */ + drmModeConnection cur_state = conn->state(); - for (int i = 0; i < (int)num_displays; ++i) { - if (!display_contents[i]) - continue; + if (cur_state == old_state) + 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; + ALOGI("%s event @%" PRIu64 " for connector %u\n", + cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", timestamp_us, + conn->id()); + + if (cur_state == DRM_MODE_CONNECTED) { + // Take the first one, then look for the preferred + DrmMode mode = *(conn->modes().begin()); + for (auto &m : conn->modes()) { + if (m.type() & DRM_MODE_TYPE_PREFERRED) { + mode = m; + break; + } + } + ALOGI("Setting mode %dx%d for connector %d\n", mode.h_display(), + mode.v_display(), conn->id()); + int ret = drm_->SetDisplayActiveMode(conn->display(), mode); + if (ret) { + ALOGE("Failed to set active config %d", ret); + return; + } + } else { + int ret = drm_->SetDpmsMode(conn->display(), DRM_MODE_DPMS_OFF); + if (ret) { + ALOGE("Failed to set dpms mode off %d", ret); + return; + } } + + procs_->hotplug(procs_, conn->display(), + cur_state == DRM_MODE_CONNECTED ? 1 : 0); } } - return 0; -} + private: + DrmResources *drm_ = NULL; + const struct hwc_procs *procs_ = NULL; +}; -static int hwc_queue_vblank_event(struct hwc_drm_display *hd) { - DrmCrtc *crtc = hd->ctx->drm.GetCrtcForDisplay(hd->display); - if (!crtc) { - ALOGE("Failed to get crtc for display"); - return -ENODEV; +struct hwc_context_t { + // map of display:hwc_drm_display_t + typedef std::map DisplayMap; + + ~hwc_context_t() { + virtual_compositor_worker.Exit(); } - drmVBlank vblank; - memset(&vblank, 0, sizeof(vblank)); + hwc_composer_device_1_t device; + hwc_procs_t const *procs = NULL; - uint32_t high_crtc = (crtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT); - vblank.request.type = (drmVBlankSeqType)( - DRM_VBLANK_ABSOLUTE | DRM_VBLANK_NEXTONMISS | DRM_VBLANK_EVENT | - (high_crtc & DRM_VBLANK_HIGH_CRTC_MASK)); - vblank.request.signal = (unsigned long)hd; - vblank.request.sequence = hd->vsync_sequence + 1; + DisplayMap displays; + DrmResources drm; + std::unique_ptr importer; + const gralloc_module_t *gralloc; + DummySwSyncTimeline dummy_timeline; + VirtualCompositorWorker virtual_compositor_worker; + DrmHotplugHandler hotplug_handler; +}; - int ret = drmWaitVBlank(hd->ctx->drm.fd(), &vblank); - if (ret) { - ALOGE("Failed to wait for vblank %d", ret); - return ret; +static native_handle_t *dup_buffer_handle(buffer_handle_t handle) { + native_handle_t *new_handle = + native_handle_create(handle->numFds, handle->numInts); + if (new_handle == NULL) + return NULL; + + const int *old_data = handle->data; + int *new_data = new_handle->data; + for (int i = 0; i < handle->numFds; i++) { + *new_data = dup(*old_data); + old_data++; + new_data++; } + memcpy(new_data, old_data, sizeof(int) * handle->numInts); - return 0; + return new_handle; } -static void hwc_vblank_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; - - if (!hd->enable_vsync_events || !hd->ctx->procs->vsync) - return; - - /* - * Discard duplicate vsync (can happen when enabling vsync events while - * already processing vsyncs). - */ - if (sequence <= hd->vsync_sequence) - return; - - hd->vsync_sequence = sequence; - int ret = hwc_queue_vblank_event(hd); +static void free_buffer_handle(native_handle_t *handle) { + int ret = native_handle_close(handle); if (ret) - ALOGE("Failed to queue vblank event ret=%d", ret); - - int64_t timestamp = - (int64_t)tv_sec * 1000 * 1000 * 1000 + (int64_t)tv_usec * 1000; - hd->ctx->procs->vsync(hd->ctx->procs, hd->display, timestamp); + ALOGE("Failed to close native handle %d", ret); + ret = native_handle_delete(handle); + if (ret) + ALOGE("Failed to delete native handle %d", ret); } -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; +const hwc_drm_bo *DrmHwcBuffer::operator->() const { + if (importer_ == NULL) { + ALOGE("Access of non-existent BO"); + exit(1); + return NULL; + } + return &bo_; +} - int ret = pthread_mutex_lock(&hd->flip_lock); - if (ret) { - ALOGE("Failed to lock flip lock ret=%d", ret); - return; +void DrmHwcBuffer::Clear() { + if (importer_ != NULL) { + importer_->ReleaseBuffer(&bo_); + importer_ = NULL; } +} - ret = pthread_cond_signal(&hd->flip_cond); +int DrmHwcBuffer::ImportBuffer(buffer_handle_t handle, Importer *importer) { + hwc_drm_bo tmp_bo; + + int ret = importer->ImportBuffer(handle, &tmp_bo); if (ret) - ALOGE("Failed to signal flip condition ret=%d", ret); + return ret; - ret = pthread_mutex_unlock(&hd->flip_lock); - if (ret) { - ALOGE("Failed to unlock flip lock ret=%d", ret); - return; + if (importer_ != NULL) { + importer_->ReleaseBuffer(&bo_); } -} -static void *hwc_event_worker(void *arg) { - setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); + importer_ = importer; - struct hwc_context_t *ctx = (struct hwc_context_t *)arg; - do { - fd_set fds; - FD_ZERO(&fds); - FD_SET(ctx->drm.fd(), &fds); + bo_ = tmp_bo; - drmEventContext event_context; - event_context.version = DRM_EVENT_CONTEXT_VERSION; - event_context.page_flip_handler = hwc_flip_event_handler; - event_context.vblank_handler = hwc_vblank_event_handler; + return 0; +} - int ret; - do { - ret = select(ctx->drm.fd() + 1, &fds, NULL, NULL, NULL); - } while (ret == -1 && errno == EINTR); +int DrmHwcNativeHandle::CopyBufferHandle(buffer_handle_t handle, + const gralloc_module_t *gralloc) { + native_handle_t *handle_copy = dup_buffer_handle(handle); + if (handle_copy == NULL) { + ALOGE("Failed to duplicate handle"); + return -ENOMEM; + } - if (ret != 1) { - ALOGE("Failed waiting for drm event\n"); - continue; - } + int ret = gralloc->registerBuffer(gralloc, handle_copy); + if (ret) { + ALOGE("Failed to register buffer handle %d", ret); + free_buffer_handle(handle_copy); + return ret; + } - drmHandleEvent(ctx->drm.fd(), &event_context); - } while (true); + Clear(); - return NULL; + gralloc_ = gralloc; + handle_ = handle_copy; + + return 0; } -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); +DrmHwcNativeHandle::~DrmHwcNativeHandle() { + Clear(); } -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; +void DrmHwcNativeHandle::Clear() { + if (gralloc_ != NULL && handle_ != NULL) { + gralloc_->unregisterBuffer(gralloc_, handle_); + free_buffer_handle(handle_); + gralloc_ = NULL; + handle_ = NULL; } +} - DrmConnector *connector = hd->ctx->drm.GetConnectorForDisplay(hd->display); - if (!connector) { - ALOGE("Failed to get connector for display %d", hd->display); - return -ENODEV; +int DrmHwcLayer::InitFromHwcLayer(hwc_layer_1_t *sf_layer, Importer *importer, + const gralloc_module_t *gralloc) { + sf_handle = sf_layer->handle; + alpha = sf_layer->planeAlpha; + + source_crop = DrmHwcRect( + sf_layer->sourceCropf.left, sf_layer->sourceCropf.top, + sf_layer->sourceCropf.right, sf_layer->sourceCropf.bottom); + display_frame = DrmHwcRect( + sf_layer->displayFrame.left, sf_layer->displayFrame.top, + sf_layer->displayFrame.right, sf_layer->displayFrame.bottom); + + transform = 0; + // 270* and 180* cannot be combined with flips. More specifically, they + // already contain both horizontal and vertical flips, so those fields are + // redundant in this case. 90* rotation can be combined with either horizontal + // flip or vertical flip, so treat it differently + if (sf_layer->transform == HWC_TRANSFORM_ROT_270) { + transform = DrmHwcTransform::kRotate270; + } else if (sf_layer->transform == HWC_TRANSFORM_ROT_180) { + transform = DrmHwcTransform::kRotate180; + } else { + if (sf_layer->transform & HWC_TRANSFORM_FLIP_H) + transform |= DrmHwcTransform::kFlipH; + if (sf_layer->transform & HWC_TRANSFORM_FLIP_V) + transform |= DrmHwcTransform::kFlipV; + if (sf_layer->transform & HWC_TRANSFORM_ROT_90) + transform |= DrmHwcTransform::kRotate90; } - 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; - } - return 0; + switch (sf_layer->blending) { + case HWC_BLENDING_NONE: + blending = DrmHwcBlending::kNone; + break; + case HWC_BLENDING_PREMULT: + blending = DrmHwcBlending::kPreMult; + break; + case HWC_BLENDING_COVERAGE: + blending = DrmHwcBlending::kCoverage; + break; + default: + ALOGE("Invalid blending in hwc_layer_1_t %d", sf_layer->blending); + return -EINVAL; } - 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()); + int ret = buffer.ImportBuffer(sf_layer->handle, importer); + if (ret) return ret; - } - ret = pthread_cond_wait(&hd->flip_cond, &hd->flip_lock); + ret = handle.CopyBufferHandle(sf_layer->handle, gralloc); + if (ret) + return ret; + + ret = gralloc->perform(gralloc, GRALLOC_MODULE_PERFORM_GET_USAGE, + handle.get(), &gralloc_buffer_usage); if (ret) { - ALOGE("Failed to wait on condition %d", ret); + ALOGE("Failed to get usage for buffer %p (%d)", handle.get(), 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; +static void hwc_dump(struct hwc_composer_device_1 *dev, char *buff, + int buff_len) { + struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common; + std::ostringstream out; + + ctx->drm.compositor()->Dump(&out); + std::string out_str = out.str(); + strncpy(buff, out_str.c_str(), + std::min((size_t)buff_len, out_str.length() + 1)); + buff[buff_len - 1] = '\0'; +} + +static bool hwc_skip_layer(const std::pair &indices, int i) { + return indices.first >= 0 && i >= indices.first && i <= indices.second; +} + +static int hwc_prepare(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; + + for (int i = 0; i < (int)num_displays; ++i) { + if (!display_contents[i]) + continue; + + bool use_framebuffer_target = false; + DrmMode mode; + if (i == HWC_DISPLAY_VIRTUAL) { + use_framebuffer_target = true; + } else { + DrmConnector *c = ctx->drm.GetConnectorForDisplay(i); + if (!c) { + ALOGE("Failed to get DrmConnector for display %d", i); + return -ENODEV; + } + mode = c->active_mode(); } - } - ret = hwc_flip(hd, buf); - if (ret) { - ALOGE("Failed to perform flip\n"); - return ret; - } + // Since we can't composite HWC_SKIP_LAYERs by ourselves, we'll let SF + // handle all layers in between the first and last skip layers. So find the + // outer indices and mark everything in between as HWC_FRAMEBUFFER + std::pair skip_layer_indices(-1, -1); + int num_layers = display_contents[i]->numHwLayers; + for (int j = 0; !use_framebuffer_target && j < num_layers; ++j) { + hwc_layer_1_t *layer = &display_contents[i]->hwLayers[j]; - if (hwc_import_bo_release(hd->ctx->drm.fd(), hd->ctx->import_ctx, - &hd->front)) { - 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]) + if (!(layer->flags & HWC_SKIP_LAYER)) continue; - ret = pthread_mutex_lock(&hd->set_worker.lock); - if (ret) { - ALOGE("Failed to lock set lock in wait_and_set() %d", ret); - continue; - } + if (skip_layer_indices.first == -1) + skip_layer_indices.first = j; + skip_layer_indices.second = j; + } - /* check for duplicate handle in buf_queue */ - bool found = false; - for (std::list::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); + for (int j = 0; j < num_layers; ++j) { + hwc_layer_1_t *layer = &display_contents[i]->hwLayers[j]; + + if (!use_framebuffer_target && !hwc_skip_layer(skip_layer_indices, j)) { + // If the layer is off the screen, don't earmark it for an overlay. + // We'll leave it as-is, which effectively just drops it from the frame + const hwc_rect_t *frame = &layer->displayFrame; + if ((frame->right - frame->left) <= 0 || + (frame->bottom - frame->top) <= 0 || + frame->right <= 0 || frame->bottom <= 0 || + frame->left >= (int)mode.h_display() || + frame->top >= (int)mode.v_display()) + continue; + + if (layer->compositionType == HWC_FRAMEBUFFER) + layer->compositionType = HWC_OVERLAY; + } else { + switch (layer->compositionType) { + case HWC_OVERLAY: + case HWC_BACKGROUND: + case HWC_SIDEBAND: + case HWC_CURSOR_OVERLAY: + layer->compositionType = HWC_FRAMEBUFFER; + break; + } } - 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; + return 0; } -static void *hwc_set_worker(void *arg) { - setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); +static void hwc_add_layer_to_retire_fence( + hwc_layer_1_t *layer, hwc_display_contents_1_t *display_contents) { + if (layer->releaseFenceFd < 0) + return; - 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; + if (display_contents->retireFenceFd >= 0) { + int old_retire_fence = display_contents->retireFenceFd; + display_contents->retireFenceFd = + sync_merge("dc_retire", old_retire_fence, layer->releaseFenceFd); + close(old_retire_fence); + } else { + display_contents->retireFenceFd = dup(layer->releaseFenceFd); } +} - do { - ret = pthread_mutex_lock(&hd->set_worker.lock); - if (ret) { - ALOGE("Failed to lock set lock %d", ret); - return NULL; - } +static int hwc_set(hwc_composer_device_1_t *dev, size_t num_displays, + hwc_display_contents_1_t **sf_display_contents) { + ATRACE_CALL(); + struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common; + int ret = 0; - if (hd->set_worker.exit) - break; + std::vector checked_output_fences; + std::vector displays_contents; + std::vector layers_map; + std::vector> layers_indices; + displays_contents.reserve(num_displays); + // layers_map.reserve(num_displays); + layers_indices.reserve(num_displays); + + // Phase one does nothing that would cause errors. Only take ownership of FDs. + for (size_t i = 0; i < num_displays; ++i) { + hwc_display_contents_1_t *dc = sf_display_contents[i]; + displays_contents.emplace_back(); + DrmHwcDisplayContents &display_contents = displays_contents.back(); + layers_indices.emplace_back(); + std::vector &indices_to_composite = layers_indices.back(); + + if (!sf_display_contents[i]) + continue; - 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); + if (i == HWC_DISPLAY_VIRTUAL) { + ctx->virtual_compositor_worker.QueueComposite(dc); + continue; + } + + std::ostringstream display_index_formatter; + display_index_formatter << "retire fence for display " << i; + std::string display_fence_description(display_index_formatter.str()); + checked_output_fences.emplace_back(&dc->retireFenceFd, + display_fence_description.c_str(), + ctx->dummy_timeline); + display_contents.retire_fence = OutputFd(&dc->retireFenceFd); + + size_t num_dc_layers = dc->numHwLayers; + int framebuffer_target_index = -1; + for (size_t j = 0; j < num_dc_layers; ++j) { + hwc_layer_1_t *sf_layer = &dc->hwLayers[j]; + if (sf_layer->compositionType == HWC_FRAMEBUFFER_TARGET) { + framebuffer_target_index = j; break; } } - struct hwc_drm_bo buf; - buf = hd->buf_queue.front(); - hd->buf_queue.pop_front(); + for (size_t j = 0; j < num_dc_layers; ++j) { + hwc_layer_1_t *sf_layer = &dc->hwLayers[j]; + + display_contents.layers.emplace_back(); + DrmHwcLayer &layer = display_contents.layers.back(); + + // In prepare() we marked all layers FRAMEBUFFER between SKIP_LAYER's. + // This means we should insert the FB_TARGET layer in the composition + // stack at the location of the first skip layer, and ignore the rest. + if (sf_layer->flags & HWC_SKIP_LAYER) { + if (framebuffer_target_index < 0) + continue; + int idx = framebuffer_target_index; + framebuffer_target_index = -1; + hwc_layer_1_t *fbt_layer = &dc->hwLayers[idx]; + if (!fbt_layer->handle || (fbt_layer->flags & HWC_SKIP_LAYER)) { + ALOGE("Invalid HWC_FRAMEBUFFER_TARGET with HWC_SKIP_LAYER present"); + continue; + } + indices_to_composite.push_back(idx); + continue; + } - ret = pthread_mutex_unlock(&hd->set_worker.lock); - if (ret) { - ALOGE("Failed to unlock set lock %d", ret); - return NULL; - } + if (sf_layer->compositionType == HWC_OVERLAY) + indices_to_composite.push_back(j); - ret = hwc_wait_and_set(hd, &buf); - if (ret) - ALOGE("Failed to wait and set %d", ret); + layer.acquire_fence.Set(sf_layer->acquireFenceFd); + sf_layer->acquireFenceFd = -1; - ret = sw_sync_timeline_inc(hd->timeline_fd, 1); - if (ret) - ALOGE("Failed to increment sync timeline %d", ret); - } while (true); + std::ostringstream layer_fence_formatter; + layer_fence_formatter << "release fence for layer " << j << " of display " + << i; + std::string layer_fence_description(layer_fence_formatter.str()); + checked_output_fences.emplace_back(&sf_layer->releaseFenceFd, + layer_fence_description.c_str(), + ctx->dummy_timeline); + layer.release_fence = OutputFd(&sf_layer->releaseFenceFd); + } - ret = pthread_mutex_unlock(&hd->set_worker.lock); - if (ret) - ALOGE("Failed to unlock set lock while exiting %d", ret); + // This is a catch-all in case we get a frame without any overlay layers, or + // skip layers, but with a value fb_target layer. This _shouldn't_ happen, + // but it's not ruled out by the hwc specification + if (indices_to_composite.empty() && framebuffer_target_index >= 0) { + hwc_layer_1_t *sf_layer = &dc->hwLayers[framebuffer_target_index]; + if (!sf_layer->handle || (sf_layer->flags & HWC_SKIP_LAYER)) { + ALOGE( + "Expected valid layer with HWC_FRAMEBUFFER_TARGET when all " + "HWC_OVERLAY layers are skipped."); + ret = -EINVAL; + } + indices_to_composite.push_back(framebuffer_target_index); + } + } - ret = pthread_mutex_unlock(&hd->flip_lock); if (ret) - ALOGE("Failed to unlock flip lock ret=%d", ret); + return ret; - return NULL; -} + for (size_t i = 0; i < num_displays; ++i) { + hwc_display_contents_1_t *dc = sf_display_contents[i]; + DrmHwcDisplayContents &display_contents = displays_contents[i]; + if (!sf_display_contents[i] || i == HWC_DISPLAY_VIRTUAL) + continue; -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; - } -} + layers_map.emplace_back(); + DrmCompositionDisplayLayersMap &map = layers_map.back(); + map.display = i; + map.geometry_changed = + (dc->flags & HWC_GEOMETRY_CHANGED) == HWC_GEOMETRY_CHANGED; + std::vector &indices_to_composite = layers_indices[i]; + for (size_t j : indices_to_composite) { + hwc_layer_1_t *sf_layer = &dc->hwLayers[j]; -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; - } + DrmHwcLayer &layer = display_contents.layers[j]; - /* - * 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); + ret = layer.InitFromHwcLayer(sf_layer, ctx->importer.get(), ctx->gralloc); + if (ret) { + ALOGE("Failed to init composition from layer %d", ret); + return ret; + } + map.layers.emplace_back(std::move(layer)); } } - 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; + std::unique_ptr composition( + ctx->drm.compositor()->CreateComposition(ctx->importer.get())); + if (!composition) { + ALOGE("Drm composition init failed"); + return -EINVAL; } - struct hwc_drm_bo buf; - memset(&buf, 0, sizeof(buf)); - ret = - hwc_import_bo_create(ctx->drm.fd(), ctx->import_ctx, layer->handle, &buf); + ret = composition->SetLayers(layers_map.size(), layers_map.data()); if (ret) { - ALOGE("Failed to import handle to drm bo %d", ret); - hwc_close_fences(display_contents); - return ret; + return -EINVAL; } - 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; -} + ret = ctx->drm.compositor()->QueueComposition(std::move(composition)); + if (ret) { + return -EINVAL; + } -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; + for (size_t i = 0; i < num_displays; ++i) { + hwc_display_contents_1_t *dc = sf_display_contents[i]; + if (!dc) + continue; - 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]); + size_t num_dc_layers = dc->numHwLayers; + for (size_t j = 0; j < num_dc_layers; ++j) { + hwc_layer_1_t *layer = &dc->hwLayers[j]; + if (layer->flags & HWC_SKIP_LAYER) + continue; + hwc_add_layer_to_retire_fence(layer, dc); + } } + composition.reset(NULL); + return ret; } static int hwc_event_control(struct hwc_composer_device_1 *dev, int display, int event, int enabled) { - struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common; - struct hwc_drm_display *hd = &ctx->displays[display]; if (event != HWC_EVENT_VSYNC || (enabled != 0 && enabled != 1)) return -EINVAL; - DrmCrtc *crtc = ctx->drm.GetCrtcForDisplay(display); - if (!crtc) { - ALOGD("Can't service events for display %d, no crtc", display); - return -EINVAL; - } - - hd->enable_vsync_events = !!enabled; - - if (!hd->enable_vsync_events) - return 0; - - /* - * Note that it's possible that the event worker is already waiting for - * a vsync, and this will be a duplicate request. In that event, we'll - * end up firing the event handler twice, and it will discard the second - * event. Not ideal, but not worth introducing a bunch of additional - * logic/locks/state for. - */ - int ret = hwc_queue_vblank_event(hd); - if (ret) { - ALOGE("Failed to queue vblank event ret=%d", ret); - return ret; - } - - return 0; + struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common; + hwc_drm_display_t *hd = &ctx->displays[display]; + return hd->vsync_worker.VSyncControl(enabled); } static int hwc_set_power_mode(struct hwc_composer_device_1 *dev, int display, @@ -580,7 +670,8 @@ static int hwc_query(struct hwc_composer_device_1 * /* dev */, int what, *value = 1000 * 1000 * 1000 / 60; break; case HWC_DISPLAY_TYPES_SUPPORTED: - *value = HWC_DISPLAY_PRIMARY | HWC_DISPLAY_EXTERNAL; + *value = HWC_DISPLAY_PRIMARY_BIT | HWC_DISPLAY_EXTERNAL_BIT | + HWC_DISPLAY_VIRTUAL_BIT; break; } return 0; @@ -591,6 +682,12 @@ static void hwc_register_procs(struct hwc_composer_device_1 *dev, struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common; ctx->procs = procs; + + for (std::pair &display_entry : ctx->displays) + display_entry.second.vsync_worker.SetProcs(procs); + + ctx->hotplug_handler.Init(&ctx->drm, procs); + ctx->drm.event_listener()->RegisterHotplugHandler(&ctx->hotplug_handler); } static int hwc_get_display_configs(struct hwc_composer_device_1 *dev, @@ -615,13 +712,12 @@ static int hwc_get_display_configs(struct hwc_composer_device_1 *dev, return ret; } - for (DrmConnector::ModeIter iter = connector->begin_modes(); - iter != connector->end_modes(); ++iter) { + for (const DrmMode &mode : connector->modes()) { size_t idx = hd->config_ids.size(); if (idx == *num_configs) break; - hd->config_ids.push_back(iter->id()); - configs[idx] = iter->id(); + hd->config_ids.push_back(mode.id()); + configs[idx] = mode.id(); } *num_configs = hd->config_ids.size(); return *num_configs == 0 ? -1 : 0; @@ -638,10 +734,9 @@ static int hwc_get_display_attributes(struct hwc_composer_device_1 *dev, return -ENODEV; } DrmMode mode; - for (DrmConnector::ModeIter iter = c->begin_modes(); iter != c->end_modes(); - ++iter) { - if (iter->id() == config) { - mode = *iter; + for (const DrmMode &conn_mode : c->modes()) { + if (conn_mode.id() == config) { + mode = conn_mode; break; } } @@ -704,88 +799,42 @@ static int hwc_set_active_config(struct hwc_composer_device_1 *dev, int display, return -EINVAL; } - int ret = - ctx->drm.SetDisplayActiveMode(display, hd->config_ids[index]); - if (ret) { - ALOGE("Failed to set config for display %d", display); - return ret; + DrmConnector *c = ctx->drm.GetConnectorForDisplay(display); + if (!c) { + ALOGE("Failed to get connector for display %d", display); + return -ENODEV; } - return ret; -} + if (c->state() != DRM_MODE_CONNECTED) + return -ENODEV; -static int hwc_destroy_worker(struct hwc_worker *worker) { - int ret = pthread_mutex_lock(&worker->lock); + DrmMode mode; + for (const DrmMode &conn_mode : c->modes()) { + if (conn_mode.id() == hd->config_ids[index]) { + mode = conn_mode; + break; + } + } + if (mode.id() != hd->config_ids[index]) { + ALOGE("Could not find active mode for %d/%d", index, hd->config_ids[index]); + return -ENOENT; + } + int ret = ctx->drm.SetDisplayActiveMode(display, mode); if (ret) { - ALOGE("Failed to lock in destroy() %d", ret); + ALOGE("Failed to set active config %d", ret); + return ret; + } + ret = ctx->drm.SetDpmsMode(display, DRM_MODE_DPMS_ON); + if (ret) { + ALOGE("Failed to set dpms mode on %d", ret); return ret; } - - worker->exit = true; - - ret |= pthread_cond_signal(&worker->cond); - if (ret) - ALOGE("Failed to signal cond in destroy() %d", ret); - - ret |= pthread_mutex_unlock(&worker->lock); - if (ret) - ALOGE("Failed to unlock in destroy() %d", ret); - - ret |= pthread_join(worker->thread, NULL); - if (ret && ret != ESRCH) - ALOGE("Failed to join thread in destroy() %d", ret); - 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"); - - int ret = hwc_import_destroy(ctx->import_ctx); - if (ret) - ALOGE("Could not destroy import %d", ret); - delete ctx; - - return 0; -} - -static int hwc_initialize_worker(struct hwc_worker *worker, - void *(*routine)(void *), void *arg) { - int ret = pthread_cond_init(&worker->cond, NULL); - if (ret) { - ALOGE("Failed to create worker condition %d", ret); - return ret; - } - - ret = pthread_mutex_init(&worker->lock, NULL); - if (ret) { - ALOGE("Failed to initialize worker lock %d", ret); - pthread_cond_destroy(&worker->cond); - return ret; - } - - worker->exit = false; - - ret = pthread_create(&worker->thread, NULL, routine, arg); - if (ret) { - ALOGE("Could not create worker thread %d", ret); - pthread_mutex_destroy(&worker->lock); - pthread_cond_destroy(&worker->cond); - return ret; - } return 0; } @@ -815,79 +864,37 @@ static int hwc_initialize_display(struct hwc_context_t *ctx, int display) { hwc_drm_display_t *hd = &ctx->displays[display]; hd->ctx = ctx; hd->display = display; - 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); + ret = hd->vsync_worker.Init(&ctx->drm, display); 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); + ALOGE("Failed to create event worker for display %d %d\n", display, ret); 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(); - c != ctx->drm.end_connectors(); ++c) { - ret = hwc_initialize_display(ctx, (*c)->display()); + for (auto &conn : ctx->drm.connectors()) { + ret = hwc_initialize_display(ctx, conn->display()); if (ret) { - ALOGE("Failed to initialize display %d", (*c)->display()); + ALOGE("Failed to initialize display %d", conn->display()); return ret; } } + ret = ctx->virtual_compositor_worker.Init(); + if (ret) { + ALOGE("Failed to initialize virtual compositor worker"); + return ret; + } return 0; } @@ -898,7 +905,7 @@ static int hwc_device_open(const struct hw_module_t *module, const char *name, return -EINVAL; } - struct hwc_context_t *ctx = new hwc_context_t(); + std::unique_ptr ctx(new hwc_context_t()); if (!ctx) { ALOGE("Failed to allocate hwc context"); return -ENOMEM; @@ -907,28 +914,31 @@ static int hwc_device_open(const struct hw_module_t *module, const char *name, int ret = ctx->drm.Init(); if (ret) { ALOGE("Can't initialize Drm object %d", ret); - delete ctx; return ret; } - ret = hwc_import_init(&ctx->import_ctx); + ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, + (const hw_module_t **)&ctx->gralloc); if (ret) { - ALOGE("Failed to initialize import context"); - delete ctx; + ALOGE("Failed to open gralloc module %d", ret); return ret; } - ret = hwc_enumerate_displays(ctx); + ret = ctx->dummy_timeline.Init(); if (ret) { - ALOGE("Failed to enumerate displays: %s", strerror(ret)); - delete ctx; + ALOGE("Failed to create dummy sw sync timeline %d", ret); return ret; } - ret = hwc_initialize_worker(&ctx->event_worker, hwc_event_worker, ctx); + ctx->importer.reset(Importer::CreateInstance(&ctx->drm)); + if (!ctx->importer) { + ALOGE("Failed to create importer instance"); + return ret; + } + + ret = hwc_enumerate_displays(ctx.get()); if (ret) { - ALOGE("Failed to create event worker %d\n", ret); - delete ctx; + ALOGE("Failed to enumerate displays: %s", strerror(ret)); return ret; } @@ -937,6 +947,7 @@ static int hwc_device_open(const struct hw_module_t *module, const char *name, ctx->device.common.module = const_cast(module); ctx->device.common.close = hwc_device_close; + ctx->device.dump = hwc_dump; ctx->device.prepare = hwc_prepare; ctx->device.set = hwc_set; ctx->device.eventControl = hwc_event_control; @@ -950,25 +961,26 @@ static int hwc_device_open(const struct hw_module_t *module, const char *name, ctx->device.setCursorPositionAsync = NULL; /* TODO: Add cursor */ *dev = &ctx->device.common; + ctx.release(); return 0; } } static struct hw_module_methods_t hwc_module_methods = { - open : android::hwc_device_open + .open = android::hwc_device_open }; hwc_module_t HAL_MODULE_INFO_SYM = { - common : { - tag : HARDWARE_MODULE_TAG, - version_major : 1, - version_minor : 0, - id : HWC_HARDWARE_MODULE_ID, - name : "DRM hwcomposer module", - author : "The Android Open Source Project", - methods : &hwc_module_methods, - dso : NULL, - reserved : {0}, + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = HWC_HARDWARE_MODULE_ID, + .name = "DRM hwcomposer module", + .author = "The Android Open Source Project", + .methods = &hwc_module_methods, + .dso = NULL, + .reserved = {0}, } };