You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
578 lines
14 KiB
578 lines
14 KiB
/* |
|
* 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_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; |
|
} |
|
|
|
|