diff options
Diffstat (limited to 'tests/hwc/cnativewindow.c')
-rw-r--r-- | tests/hwc/cnativewindow.c | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/tests/hwc/cnativewindow.c b/tests/hwc/cnativewindow.c new file mode 100644 index 0000000..474ceec --- /dev/null +++ b/tests/hwc/cnativewindow.c @@ -0,0 +1,578 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> + +#include <pthread.h> + +#include <hardware/hardware.h> +#include <hardware/gralloc.h> +#include <hardware/hwcomposer.h> + +#include <system/window.h> +#include <cutils/native_handle.h> + +// 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_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_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_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: { + int w = va_arg(ap,int); + int h = va_arg(ap,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]; + uint32_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; +} + |