/* * Copyright (C) 2013 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. */ #include #include #include #include #include #include #include #include #include #include #include // normalize and shorten type names typedef struct android_native_base_t aBase; typedef struct ANativeWindowBuffer aBuffer; typedef struct ANativeWindow aWindow; static int trace_level = 1; #define _TRACE(n,fmt...) \ do { if (trace_level >= (n)) fprintf(stderr, "CNW: " fmt); } while (0) #define ERROR(fmt...) _TRACE(0, fmt) #define INFO(fmt...) _TRACE(1, fmt) #define LOG(fmt...) _TRACE(2, fmt) #define TRACE(fmt...) _TRACE(3, fmt) #define QCT_WORKAROUND 1 typedef struct CNativeBuffer { aBuffer base; struct CNativeBuffer *next; struct CNativeBuffer *prev; int ffd; } CNativeBuffer; typedef struct CNativeWindow { aWindow base; hwc_composer_device_1_t *hwc; framebuffer_device_t *fb; alloc_device_t *gr; pthread_mutex_t lock; pthread_cond_t cvar; aBuffer *front; aBuffer *spare; CNativeBuffer free_buffer_queue; unsigned width; unsigned height; unsigned xdpi; unsigned ydpi; unsigned format; hwc_display_contents_1_t *dclist[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; hwc_display_contents_1_t dc; hwc_layer_1_t layer[4]; } CNativeWindow; static inline CNativeBuffer *from_abuffer(aBuffer *buf) { return (CNativeBuffer*) buf; } static CNativeBuffer *get_front(struct CNativeBuffer *queue) { CNativeBuffer *buf = queue->next; if (buf == queue) return 0; buf->next->prev = queue; queue->next = buf->next; buf->next = buf->prev = 0; return buf; } static void put_front(struct CNativeBuffer *queue, aBuffer *_buf) { struct CNativeBuffer *buf = (struct CNativeBuffer *) _buf; buf->prev = queue; buf->next = queue->next; queue->next->prev = buf; queue->next = buf; } static void put_back(struct CNativeBuffer *queue, aBuffer *_buf) { struct CNativeBuffer *buf = (struct CNativeBuffer *) _buf; buf->next = queue; buf->prev = queue->prev; queue->prev->next = buf; queue->prev = buf; } static void cnw_inc_ref(aBase *base) { TRACE("buf %p ref++\n",base); } static void cnw_dec_ref(aBase *base) { TRACE("buf %p ref--\n",base); } static inline CNativeWindow *from_base(aWindow *base) { return (CNativeWindow *) base; } static inline CNativeWindow *from_base_const(const aWindow *base) { return (CNativeWindow *) base; } static int cnw_set_swap_interval(aWindow *base, int interval) { CNativeWindow *win = from_base(base); if (win->fb && win->fb->setSwapInterval) return win->fb->setSwapInterval(win->fb, interval); return 0; } static int cnw_dequeue_buffer1(aWindow *base, aBuffer **buf, int *ffd) { CNativeWindow *win = from_base(base); CNativeBuffer *cnb; pthread_mutex_lock(&win->lock); while ((cnb = get_front(&win->free_buffer_queue)) == 0) { pthread_cond_wait(&win->cvar, &win->lock); } *ffd = cnb->ffd; *buf = &cnb->base; cnb->ffd = -1; LOG("<< dequeue buffer %p %d\n", *buf, *ffd); pthread_mutex_unlock(&win->lock); return 0; } static int cnw_lock_buffer0(aWindow *base, aBuffer *buffer) { return 0; } static void set_layer(hwc_layer_1_t *dl, aBuffer *buf, int ffd) { int right = buf->width; int bottom = buf->height; dl->compositionType = HWC_FRAMEBUFFER; dl->hints = 0; dl->flags = 0; dl->handle = buf->handle; dl->transform = 0; dl->blending = HWC_BLENDING_NONE; dl->sourceCrop.left = 0; dl->sourceCrop.top = 0; dl->sourceCrop.right = right; dl->sourceCrop.bottom = bottom; dl->displayFrame.left = 0; dl->displayFrame.top = 0; dl->displayFrame.right = right; dl->displayFrame.bottom = bottom; dl->visibleRegionScreen.numRects = 1; dl->visibleRegionScreen.rects = &dl->displayFrame; dl->acquireFenceFd = ffd; dl->releaseFenceFd = -1; } static void hwc_post(CNativeWindow *win, aBuffer *buf, int ffd) { hwc_composer_device_1_t *hwc = win->hwc; hwc_display_contents_1_t *dc = &(win->dc); hwc_layer_1_t *dl = win->dc.hwLayers; int r, i; dc->retireFenceFd = -1; dc->outbufAcquireFenceFd = -1; dc->flags = HWC_GEOMETRY_CHANGED; dc->numHwLayers = 1; // some hwcomposers fail if these are NULL dc->dpy = (void*) 0xdeadbeef; dc->sur = (void*) 0xdeadbeef; set_layer(&dl[0], buf, ffd); if (QCT_WORKAROUND) { set_layer(&dl[1], win->spare, -1); dl[1].compositionType = HWC_FRAMEBUFFER_TARGET; dc->numHwLayers++; } r = hwc->prepare(hwc, HWC_NUM_PHYSICAL_DISPLAY_TYPES, win->dclist); if (r) { ERROR("hwc->prepare failed r=%d\n",r); return; } // for (i = 0; i < dc->numHwLayers; i++) // LOG("dl[%d] ctype=0x%08x hints=0x%08x flags=0x%08x\n", i, // dl[i].compositionType, dl[0].hints, dl[0].flags); r = hwc->set(hwc, HWC_NUM_PHYSICAL_DISPLAY_TYPES, win->dclist); if (r) { ERROR("hwc->set failed, r=%d\n", r); return; } if (dc->retireFenceFd != -1) close(dc->retireFenceFd); if (dl->releaseFenceFd != -1) { CNativeBuffer *cnb = from_abuffer(buf); cnb->ffd = dl->releaseFenceFd; } if (QCT_WORKAROUND) if (dl[1].releaseFenceFd != -1) close(dl[1].releaseFenceFd); } static int cnw_queue_buffer1(aWindow *base, aBuffer *buffer, int ffd) { CNativeWindow *win = from_base(base); int res; LOG(">> queue buffer %p %d\n", buffer, ffd); if (win->fb) { res = win->fb->post(win->fb, buffer->handle); if (ffd != -1) close(ffd); } else { hwc_post(win, buffer, ffd); res = 0; } pthread_mutex_lock(&win->lock); if (win->front) put_back(&win->free_buffer_queue, win->front); win->front = buffer; pthread_cond_signal(&win->cvar); pthread_mutex_unlock(&win->lock); return res; } static int cnw_cancel_buffer1(aWindow *base, aBuffer *buf, int ffd) { CNativeWindow *win = from_base(base); CNativeBuffer *cnb = from_abuffer(buf); LOG("<< cancel buffer %p %d\n", buf, ffd); cnb->ffd = ffd; pthread_mutex_lock(&win->lock); put_front(&win->free_buffer_queue, buf); pthread_mutex_unlock(&win->lock); return 0; } static int cnw_dequeue_buffer0(aWindow *base, aBuffer **buf) { int ffd = -1; int r; r = cnw_dequeue_buffer1(base, buf, &ffd); if (ffd != -1) close(ffd); return r; } static int cnw_queue_buffer0(aWindow *base, aBuffer *buf) { return cnw_queue_buffer1(base, buf, -1); } static int cnw_cancel_buffer0(aWindow *base, aBuffer *buf) { return cnw_cancel_buffer1(base, buf, -1); } static int cnw_query(const aWindow *base, int what, int *value) { CNativeWindow *win = from_base_const(base); switch (what) { case NATIVE_WINDOW_WIDTH: case NATIVE_WINDOW_DEFAULT_WIDTH: *value = win->width; TRACE("query window width: %d\n", *value); return 0; case NATIVE_WINDOW_HEIGHT: case NATIVE_WINDOW_DEFAULT_HEIGHT: *value = win->height; TRACE("query window height: %d\n", *value); return 0; case NATIVE_WINDOW_FORMAT: *value = win->format; TRACE("query window format: %d\n", *value); return 0; case NATIVE_WINDOW_TRANSFORM_HINT: TRACE("query transform hint: 0\n"); *value = 0; return 0; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: TRACE("query min undequeued buffers: 1\n"); *value = 1; return 0; default: *value = 0; ERROR("query %d unknown!\n", what); return -EINVAL; } } static int cnw_perform(aWindow *base, int op, ...) { CNativeWindow *win = from_base(base); va_list ap; va_start(ap, op); switch (op) { case NATIVE_WINDOW_SET_USAGE: TRACE("set usage %d\n", va_arg(ap,int)); return 0; case NATIVE_WINDOW_CONNECT: case NATIVE_WINDOW_DISCONNECT: case NATIVE_WINDOW_API_CONNECT: case NATIVE_WINDOW_API_DISCONNECT: return 0; case NATIVE_WINDOW_SET_BUFFERS_FORMAT: TRACE("set buffers format %d\n", va_arg(ap,int)); return 0; case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: TRACE("set buffers transform %d\n", va_arg(ap,int)); return 0; case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: TRACE("set buffers timestamp %lld\n", va_arg(ap,long long)); return 0; case NATIVE_WINDOW_SET_SCALING_MODE: TRACE("set scaling mode %d\n", va_arg(ap,int)); return 0; case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: { unsigned int w = va_arg(ap,unsigned int); unsigned int h = va_arg(ap,unsigned int); if ((w == win->width) && (h == win->height)) { TRACE("set buffers dimensions %d x %d\n", w, h); return 0; } ERROR("cannot resize buffers to %d x %d\n", w, h); return -1; } default: ERROR("perform %d unknown!\n", op); return -ENODEV; } } static void hwc_invalidate(const struct hwc_procs *procs) {} static void hwc_vsync(const struct hwc_procs *procs, int disp, int64_t ts) {} static void hwc_hotplug(const struct hwc_procs *procs, int disp, int conn) {} struct hwc_procs hprocs = { .invalidate = hwc_invalidate, .vsync = hwc_vsync, .hotplug = hwc_hotplug, }; uint32_t attrs[] = { HWC_DISPLAY_WIDTH, HWC_DISPLAY_HEIGHT, HWC_DISPLAY_VSYNC_PERIOD, HWC_DISPLAY_DPI_X, HWC_DISPLAY_DPI_Y, HWC_DISPLAY_NO_ATTRIBUTE, }; static int hwc_init(CNativeWindow *win) { hw_module_t const* module; hwc_composer_device_1_t *hwc; unsigned i; int r; uint32_t configs[32]; size_t numconfigs = 32; int32_t values[8]; if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) { ERROR("cannot open hw composer module\n"); return -ENODEV; } if (hwc_open_1(module, &hwc)) { ERROR("cannot open hwc device\n"); return -ENODEV; } win->hwc = hwc; LOG("hwc version 0x%08x\n", hwc->common.version); if ((hwc->common.version & 0xFFFF0000) < 0x01010000) { ERROR("hwc version less than 1.1\n"); hwc_close_1(hwc); return -ENODEV; } hwc->registerProcs(hwc, &hprocs); if (hwc->getDisplayConfigs(hwc, 0, configs, &numconfigs)) { ERROR("cannot get configs\n"); return -ENODEV; } for (i = 0; i < numconfigs; i++) LOG("cfg[%d] = 0x%08x\n", i, configs[i]); if ((r = hwc->getDisplayAttributes(hwc, 0, configs[0], attrs, values))) { ERROR("cannot get attributes %d\n", r); return -ENODEV; } win->width = values[0]; win->height = values[1]; win->xdpi = values[3]; win->ydpi = values[4]; win->format = HAL_PIXEL_FORMAT_RGBA_8888; hwc->blank(hwc, 0, 0); win->dclist[0] = &(win->dc); return 0; } static aBuffer *cnw_alloc(CNativeWindow *win, unsigned format, unsigned usage) { CNativeBuffer *cnb; aBuffer *buf; int err; if (!(cnb = malloc(sizeof(CNativeBuffer)))) return 0; buf = &cnb->base; cnb->ffd = -1; buf->common.magic = ANDROID_NATIVE_BUFFER_MAGIC; buf->common.version = sizeof(aBuffer); buf->common.incRef = cnw_inc_ref; buf->common.decRef = cnw_dec_ref; buf->width = win->width; buf->height = win->height; buf->format = format; buf->usage = usage; err = win->gr->alloc(win->gr, win->width, win->height, format, usage, &buf->handle, &buf->stride); if (err) { ERROR("gralloc of %d x %d failed: err=%d\n", win->width, win->height, err); free(buf); return 0; } INFO("alloc buffer %p %d x %d\n", buf, win->width, win->height); return buf; } static int cnw_init(CNativeWindow *win) { hw_module_t const* module; framebuffer_device_t *fb = NULL; alloc_device_t *gr; int err, i, n; unsigned usage, format; memset(win, 0, sizeof(CNativeWindow)); win->free_buffer_queue.next = &(win->free_buffer_queue); win->free_buffer_queue.prev = &(win->free_buffer_queue); if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) != 0) { ERROR("cannot open gralloc module\n"); return -ENODEV; } if (hwc_init(win)) { ERROR("cannot open hwcomposer, trying legacy fb HAL\n"); err = framebuffer_open(module, &fb); if (err) { ERROR("cannot open fb HAL (%s)", strerror(-err)); return -ENODEV; } win->width = fb->width; win->height = fb->height; win->format = fb->format; win->xdpi = fb->xdpi; win->ydpi = fb->ydpi; win->fb = fb; } INFO("display %d x %d fmt=%d\n", win->width, win->height, win->format); err = gralloc_open(module, &gr); if (err) { ERROR("couldn't open gralloc HAL (%s)", strerror(-err)); return -ENODEV; } win->gr = gr; usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER; for (i = 0; i < 2; i++) { aBuffer *buf = cnw_alloc(win, win->format, usage); if (!buf) return -ENOMEM; put_back(&win->free_buffer_queue, buf); } if (!win->fb && QCT_WORKAROUND) { win->spare = cnw_alloc(win, win->format, usage); if (!win->spare) return -ENOMEM; } // Disgusting, but we need to init these "const" fields // and unlike C++ we can't use const_cast<> *((float*) &win->base.xdpi) = win->xdpi; *((float*) &win->base.ydpi) = win->ydpi; *((int*) &win->base.minSwapInterval) = 1; *((int*) &win->base.maxSwapInterval) = 1; win->base.common.magic = ANDROID_NATIVE_WINDOW_MAGIC; win->base.common.version = sizeof(aWindow); win->base.common.incRef = cnw_inc_ref; win->base.common.decRef = cnw_dec_ref; win->base.setSwapInterval = cnw_set_swap_interval; win->base.dequeueBuffer_DEPRECATED = cnw_dequeue_buffer0; win->base.lockBuffer_DEPRECATED = cnw_lock_buffer0; win->base.queueBuffer_DEPRECATED = cnw_queue_buffer0; win->base.query = cnw_query; win->base.perform = cnw_perform; win->base.cancelBuffer_DEPRECATED = cnw_cancel_buffer0; win->base.dequeueBuffer = cnw_dequeue_buffer1; win->base.queueBuffer = cnw_queue_buffer1; win->base.cancelBuffer = cnw_cancel_buffer1; pthread_mutex_init(&win->lock, NULL); pthread_cond_init(&win->cvar, NULL); return 0; } void cnw_destroy(CNativeWindow *win) { if (win->fb) framebuffer_close(win->fb); if (win->hwc) hwc_close_1(win->hwc); if (win->gr) gralloc_close(win->gr); free(win); } CNativeWindow *cnw_create(void) { CNativeWindow *win; char *x; if ((x = getenv("CNWDEBUG"))) trace_level = atoi(x); if (!(win = malloc(sizeof(CNativeWindow)))) return NULL; if (cnw_init(win)) { cnw_destroy(win); return NULL; } return win; } void cnw_info(CNativeWindow *win, unsigned *w, unsigned *h, unsigned *fmt) { *w = win->width; *h = win->height; *fmt = win->format; }