diff options
author | Brian Swetland <swetland@google.com> | 2013-03-06 17:16:31 -0800 |
---|---|---|
committer | Brian Swetland <swetland@google.com> | 2013-03-06 17:48:19 -0800 |
commit | 2c7b08ad8aeedef1807560ea16d54d073bede2b2 (patch) | |
tree | 72bc899344b5d953ddc1c80b7d80c59dec947536 /tests/hwc | |
parent | 20452866560ff73ff8bae62e0789ef1f8745e955 (diff) | |
download | hardware_libhardware-2c7b08ad8aeedef1807560ea16d54d073bede2b2.zip hardware_libhardware-2c7b08ad8aeedef1807560ea16d54d073bede2b2.tar.gz hardware_libhardware-2c7b08ad8aeedef1807560ea16d54d073bede2b2.tar.bz2 |
hwcomposer commandline test(s)
Add cnativewindow which provides a minimal bridge between EGL and hwcomposer or fbhal.
Add test-arrows, a trivial GLES2 test program to exercise this.
Change-Id: I60ece4378def6b6eda55437c215fc46a180ad7a3
Diffstat (limited to 'tests/hwc')
-rw-r--r-- | tests/hwc/Android.mk | 14 | ||||
-rw-r--r-- | tests/hwc/cnativewindow.c | 578 | ||||
-rw-r--r-- | tests/hwc/test-arrows.c | 164 | ||||
-rw-r--r-- | tests/hwc/util.c | 236 | ||||
-rw-r--r-- | tests/hwc/util.h | 38 |
5 files changed, 1030 insertions, 0 deletions
diff --git a/tests/hwc/Android.mk b/tests/hwc/Android.mk new file mode 100644 index 0000000..4cae9eb --- /dev/null +++ b/tests/hwc/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := libcnativewindow +LOCAL_SRC_FILES := cnativewindow.c util.c +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := hwc-test-arrows +LOCAL_SRC_FILES := test-arrows.c +LOCAL_STATIC_LIBRARIES := libcnativewindow +LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libdl libhardware +LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES +include $(BUILD_EXECUTABLE) 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; +} + diff --git a/tests/hwc/test-arrows.c b/tests/hwc/test-arrows.c new file mode 100644 index 0000000..a35faa7 --- /dev/null +++ b/tests/hwc/test-arrows.c @@ -0,0 +1,164 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> + +#include <EGL/egl.h> +#include <GLES2/gl2.h> + +#include "util.h" + +static const char gVertexShader[] = + "attribute vec4 aPosition;\n" + "uniform mat4 uTransform;\n" + "varying vec4 vTexCoord;\n" + "void main() {\n" + " gl_Position = aPosition * uTransform;\n" + " vTexCoord = aPosition * vec4(1.0/16.0,-1.0/16.0,0.0,0.0);\n" + "}\n"; + +static const char gFragmentShader[] = + "precision mediump float;\n" + "uniform sampler2D uTexture;\n" + "uniform float uAnim;\n" + "varying vec4 vTexCoord;\n" + "void main() {\n" + " vec2 tc = vec2(vTexCoord.x, uAnim + vTexCoord.y);\n" + " gl_FragColor = texture2D(uTexture, tc);\n" + "}\n"; + +static GLuint pgm; +static GLint aPosition, uTransform, uTexture, uAnim; + +static GLfloat vtx[2 * 3 * 2]; +static GLfloat mtx[16]; + +//#define R (0xFF0000FF) +#define R (0xFF000000) +#define G (0xFF00FF00) +uint32_t t32[] = { + R, R, R, R, R, R, R, G, G, R, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, G, G, G, G, G, G, R, R, R, R, R, + R, R, R, R, G, G, G, G, G, G, G, G, R, R, R, R, + R, R, R, G, G, G, G, G, G, G, G, G, G, R, R, R, + R, R, G, G, G, G, G, G, G, G, G, G, G, G, R, R, + R, R, G, G, G, G, G, G, G, G, G, G, G, G, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, G, G, G, G, R, R, R, R, R, R, + R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, + R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, + R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, R, +}; +#undef R +#undef G + +int prepare(int w, int h) { + GLuint texid; + + int left = w / 4; + int top = h / 4; + int right = (w / 4) * 3; + int bottom = (h / 4) * 3; + + vtx[0] = left; + vtx[1] = top; + vtx[2] = left; + vtx[3] = bottom; + vtx[4] = right; + vtx[5] = bottom; + + vtx[6] = right; + vtx[7] = bottom; + vtx[8] = right; + vtx[9] = top; + vtx[10] = left; + vtx[11] = top; + + matrix_init_ortho(mtx, w, h); + + pgm = load_program(gVertexShader, gFragmentShader); + if (!pgm) + return -1; + + aPosition = glGetAttribLocation(pgm, "aPosition"); + uTexture = glGetUniformLocation(pgm, "uTexture"); + uTransform = glGetUniformLocation(pgm, "uTransform"); + uAnim = glGetUniformLocation(pgm, "uAnim"); + + glViewport(0, 0, w, h); + + glGenTextures(1, &texid); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glEnable(GL_TEXTURE_2D); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, + GL_RGBA, GL_UNSIGNED_BYTE, t32); + + return 0; +} + +static float anim = 0.0; + +void render() { + anim += 0.1; + if (anim >= 16.0) anim = 0.0; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glUseProgram(pgm); + glUniform1i(uTexture, 0); + glUniform1f(uAnim, anim); + glUniformMatrix4fv(uTransform, 1, 0, mtx); + glVertexAttribPointer(aPosition, 2, GL_FLOAT, GL_FALSE, 0, vtx); + glEnableVertexAttribArray(aPosition); + glDrawArrays(GL_TRIANGLES, 0, 6); +} + +int main(int argc, char **argv) { + EGLDisplay display; + EGLSurface surface; + int w, h, count; + + if (argc > 1) + count = atoi(argv[1]); + + if (egl_create(&display, &surface, &w, &h)) + return -1; + + if (prepare(w, h)) + return -1; + + for (;;) { + render(); + eglSwapBuffers(display, surface); + if (count > 0) + if (--count == 0) + break; + } + + egl_destroy(display, surface); + return 0; +} diff --git a/tests/hwc/util.c b/tests/hwc/util.c new file mode 100644 index 0000000..8931305 --- /dev/null +++ b/tests/hwc/util.c @@ -0,0 +1,236 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <stdarg.h> + +#include <EGL/egl.h> +#include <GLES2/gl2.h> + +#include <system/graphics.h> + +#include "util.h" + +void matrix_init_ortho(GLfloat *m, float w, float h) { + m[0] = 2.0 / w; + m[1] = 0.0; + m[2] = 0.0; + m[3] = -1.0; + m[4] = 0.0; + m[5] = 2.0 / h; + m[6] = 0.0; + m[7] = -1.0; + m[8] = 0.0; + m[9] = 0.0; + m[10] -1.0; + m[11] = 0.0; + m[12] = 0.0; + m[13] = 0.0; + m[14] = 0.0; + m[15] = 1.0; +} + +static GLuint load_shader(GLenum shaderType, const char *src) { + GLint status = 0, len = 0; + GLuint shader; + + if (!(shader = glCreateShader(shaderType))) + return 0; + + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + + if (status) + return shader; + + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); + if (len) { + char *msg = malloc(len); + if (msg) { + glGetShaderInfoLog(shader, len, NULL, msg); + msg[len-1] = 0; + fprintf(stderr, "error compiling shader:\n%s\n", msg); + free(msg); + } + } + glDeleteShader(shader); + return 0; +} + +GLuint load_program(const char *vert_src, const char *frag_src) { + GLuint vert, frag, prog; + GLint status = 0, len = 0; + + if (!(vert = load_shader(GL_VERTEX_SHADER, vert_src))) + return 0; + if (!(frag = load_shader(GL_FRAGMENT_SHADER, frag_src))) + goto fail_frag; + if (!(prog = glCreateProgram())) + goto fail_prog; + + glAttachShader(prog, vert); + glAttachShader(prog, frag); + glLinkProgram(prog); + + glGetProgramiv(prog, GL_LINK_STATUS, &status); + if (status) + return prog; + + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len); + if (len) { + char *buf = (char*) malloc(len); + if (buf) { + glGetProgramInfoLog(prog, len, NULL, buf); + buf[len-1] = 0; + fprintf(stderr, "error linking program:\n%s\n", buf); + free(buf); + } + } + glDeleteProgram(prog); +fail_prog: + glDeleteShader(frag); +fail_frag: + glDeleteShader(vert); + return 0; +} + +int select_config_for_window(EGLDisplay dpy, EGLint *attr, + unsigned format, EGLConfig *config) { + EGLint R,G,B,A,r,g,b,a; + EGLint i, n, max; + EGLConfig *cfg; + + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + R = G = B = A = 8; + break; + case HAL_PIXEL_FORMAT_RGB_565: + R = 5; G = 6; B = 5; A = 0; + break; + default: + fprintf(stderr, "unknown fb pixel format %d\n", format); + return -1; + } + + if (eglGetConfigs(dpy, NULL, 0, &max) == EGL_FALSE) { + fprintf(stderr, "no EGL configurations available?!\n"); + return -1; + } + + cfg = (EGLConfig*) malloc(sizeof(EGLConfig) * max); + if (!cfg) + return -1; + + if (eglChooseConfig(dpy, attr, cfg, max, &n) == EGL_FALSE) { + fprintf(stderr, "eglChooseConfig failed\n"); + return -1; + } + + for (i = 0; i < n; i++) { + EGLint r,g,b,a; + eglGetConfigAttrib(dpy, cfg[i], EGL_RED_SIZE, &r); + eglGetConfigAttrib(dpy, cfg[i], EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(dpy, cfg[i], EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(dpy, cfg[i], EGL_ALPHA_SIZE, &a); + if (r == R && g == G && b == B && a == A) { + *config = cfg[i]; + free(cfg); + return 0; + } + } + + fprintf(stderr, "cannot find matching config\n"); + free(cfg); + return -1; +} + +static struct CNativeWindow *_cnw = 0; + +int egl_create(EGLDisplay *_display, EGLSurface *_surface, int *_w, int *_h) { + EGLBoolean res; + EGLConfig config = { 0 }; + EGLint context_attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + EGLint config_attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE }; + EGLint major, minor; + EGLContext context; + EGLSurface surface; + EGLint w, h; + EGLDisplay display; + EGLNativeWindowType window; + unsigned width, height, format; + struct CNativeWindow *cnw; + + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) + return -1; + + if (!(res = eglInitialize(display, &major, &minor))) + return -1; + + fprintf(stderr, "egl version: %d.%d\n", major, minor); + + if ((cnw = cnw_create()) == 0) + return -1; + + cnw_info(cnw, &width, &height, &format); + window = (EGLNativeWindowType) cnw; + + if ((res = select_config_for_window(display, config_attrs, format, &config))) + goto fail; + + surface = eglCreateWindowSurface(display, config, window, NULL); + if (surface == EGL_NO_SURFACE) + goto fail; + + context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attrs); + if (context == EGL_NO_CONTEXT) + goto fail; + + if (!(res = eglMakeCurrent(display, surface, surface, context))) + goto fail; + + eglQuerySurface(display, surface, EGL_WIDTH, &w); + eglQuerySurface(display, surface, EGL_HEIGHT, &h); + + fprintf(stderr, "window: %d x %d\n", w, h); + + *_display = display; + *_surface = surface; + *_w = w; + *_h = h; + + _cnw = cnw; + return 0; + +fail: + cnw_destroy(cnw); + return -1; +} + +void egl_destroy(EGLDisplay display, EGLSurface surface) { + if (_cnw) { + eglDestroySurface(display, surface); + eglTerminate(display); + cnw_destroy(_cnw); + _cnw = 0; + } +} diff --git a/tests/hwc/util.h b/tests/hwc/util.h new file mode 100644 index 0000000..a0d38ce --- /dev/null +++ b/tests/hwc/util.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef _GL_UTIL_H_ +#define _GL_UTIL_H_ + +/* convenience */ + +GLuint load_program(const char *vert_src, const char *frag_src); +void matrix_init_ortho(GLfloat *m, float w, float h); + +/* context setup / teardown */ + +int egl_create(EGLDisplay *_display, EGLSurface *_surface, int *_w, int *_h); +void egl_destroy(EGLDisplay display, EGLSurface surface); + +/* internals needed by util.c */ + +struct CNativeWindow; +struct CNativeWindow *cnw_create(void); +void cnw_destroy(struct CNativeWindow *win); +void cnw_info(struct CNativeWindow *win, + unsigned *w, unsigned *h, unsigned *fmt); + +#endif |