| /* |
| * Copyright (C) 2010 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 <time.h> |
| #include <sched.h> |
| #include <sys/resource.h> |
| |
| #include <EGL/egl.h> |
| #include <EGL/eglext.h> |
| #include <GLES/gl.h> |
| #include <GLES/glext.h> |
| |
| #include <utils/Timers.h> |
| |
| #include <ui/FramebufferNativeWindow.h> |
| #include <ui/GraphicBuffer.h> |
| #include "EGLUtils.h" |
| |
| using namespace android; |
| |
| static void printGLString(const char *name, GLenum s) { |
| // fprintf(stderr, "printGLString %s, %d\n", name, s); |
| const char *v = (const char *) glGetString(s); |
| // int error = glGetError(); |
| // fprintf(stderr, "glGetError() = %d, result of glGetString = %x\n", error, |
| // (unsigned int) v); |
| // if ((v < (const char*) 0) || (v > (const char*) 0x10000)) |
| // fprintf(stderr, "GL %s = %s\n", name, v); |
| // else |
| // fprintf(stderr, "GL %s = (null) 0x%08x\n", name, (unsigned int) v); |
| fprintf(stderr, "GL %s = %s\n", name, v); |
| } |
| |
| static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) { |
| if (returnVal != EGL_TRUE) { |
| fprintf(stderr, "%s() returned %d\n", op, returnVal); |
| } |
| |
| for (EGLint error = eglGetError(); error != EGL_SUCCESS; error |
| = eglGetError()) { |
| fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error), |
| error); |
| } |
| } |
| |
| static void checkGlError(const char* op) { |
| for (GLint error = glGetError(); error; error |
| = glGetError()) { |
| fprintf(stderr, "after %s() glError (0x%x)\n", op, error); |
| } |
| } |
| |
| bool setupGraphics(int w, int h) { |
| glViewport(0, 0, w, h); |
| checkGlError("glViewport"); |
| return true; |
| } |
| |
| int align(int x, int a) { |
| return (x + (a-1)) & (~(a-1)); |
| } |
| |
| const int yuvTexWidth = 600; |
| const int yuvTexHeight = 480; |
| const int yuvTexUsage = GraphicBuffer::USAGE_HW_TEXTURE | |
| GraphicBuffer::USAGE_SW_WRITE_RARELY; |
| const int yuvTexFormat = HAL_PIXEL_FORMAT_YV12; |
| const int yuvTexOffsetY = 0; |
| const int yuvTexStrideY = (yuvTexWidth + 0xf) & ~0xf; |
| const int yuvTexOffsetV = yuvTexStrideY * yuvTexHeight; |
| const int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; |
| const int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * yuvTexHeight/2; |
| const int yuvTexStrideU = yuvTexStrideV; |
| const bool yuvTexSameUV = false; |
| static sp<GraphicBuffer> yuvTexBuffer; |
| static GLuint yuvTex; |
| |
| bool setupYuvTexSurface(EGLDisplay dpy, EGLContext context) { |
| int blockWidth = yuvTexWidth > 16 ? yuvTexWidth / 16 : 1; |
| int blockHeight = yuvTexHeight > 16 ? yuvTexHeight / 16 : 1; |
| yuvTexBuffer = new GraphicBuffer(yuvTexWidth, yuvTexHeight, yuvTexFormat, |
| yuvTexUsage); |
| char* buf = NULL; |
| status_t err = yuvTexBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf)); |
| if (err != 0) { |
| fprintf(stderr, "yuvTexBuffer->lock(...) failed: %d\n", err); |
| return false; |
| } |
| for (int x = 0; x < yuvTexWidth; x++) { |
| for (int y = 0; y < yuvTexHeight; y++) { |
| int parityX = (x / blockWidth) & 1; |
| int parityY = (y / blockHeight) & 1; |
| unsigned char intensity = (parityX ^ parityY) ? 63 : 191; |
| buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; |
| if (x < yuvTexWidth / 2 && y < yuvTexHeight / 2) { |
| buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; |
| if (yuvTexSameUV) { |
| buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = intensity; |
| } else if (x < yuvTexWidth / 4 && y < yuvTexHeight / 4) { |
| buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = |
| buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = |
| buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = |
| buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = intensity; |
| } |
| } |
| } |
| } |
| |
| err = yuvTexBuffer->unlock(); |
| if (err != 0) { |
| fprintf(stderr, "yuvTexBuffer->unlock() failed: %d\n", err); |
| return false; |
| } |
| |
| EGLClientBuffer clientBuffer = (EGLClientBuffer)yuvTexBuffer->getNativeBuffer(); |
| EGLImageKHR img = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, |
| clientBuffer, 0); |
| checkEglError("eglCreateImageKHR"); |
| if (img == EGL_NO_IMAGE_KHR) { |
| return false; |
| } |
| |
| glGenTextures(1, &yuvTex); |
| checkGlError("glGenTextures"); |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTex); |
| checkGlError("glBindTexture"); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)img); |
| checkGlError("glEGLImageTargetTexture2DOES"); |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| checkGlError("glTexParameteri"); |
| glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| checkGlError("glTexParameteri"); |
| glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); |
| checkGlError("glTexEnvx"); |
| |
| GLint crop[4] = { 0, 0, yuvTexWidth, yuvTexHeight }; |
| glTexParameteriv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_CROP_RECT_OES, crop); |
| checkGlError("glTexParameteriv"); |
| |
| return true; |
| } |
| |
| void renderFrame(int w, int h) { |
| glClearColor(0.0f, 0.0f, 1.0f, 1.0f); |
| checkGlError("glClearColor"); |
| glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); |
| checkGlError("glClear"); |
| |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTex); |
| checkGlError("glBindTexture"); |
| glEnable(GL_TEXTURE_EXTERNAL_OES); |
| checkGlError("glEnable"); |
| |
| glDrawTexiOES(0, 0, 0, w, h); |
| checkGlError("glDrawTexiOES"); |
| } |
| |
| void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { |
| |
| #define X(VAL) {VAL, #VAL} |
| struct {EGLint attribute; const char* name;} names[] = { |
| X(EGL_BUFFER_SIZE), |
| X(EGL_ALPHA_SIZE), |
| X(EGL_BLUE_SIZE), |
| X(EGL_GREEN_SIZE), |
| X(EGL_RED_SIZE), |
| X(EGL_DEPTH_SIZE), |
| X(EGL_STENCIL_SIZE), |
| X(EGL_CONFIG_CAVEAT), |
| X(EGL_CONFIG_ID), |
| X(EGL_LEVEL), |
| X(EGL_MAX_PBUFFER_HEIGHT), |
| X(EGL_MAX_PBUFFER_PIXELS), |
| X(EGL_MAX_PBUFFER_WIDTH), |
| X(EGL_NATIVE_RENDERABLE), |
| X(EGL_NATIVE_VISUAL_ID), |
| X(EGL_NATIVE_VISUAL_TYPE), |
| X(EGL_SAMPLES), |
| X(EGL_SAMPLE_BUFFERS), |
| X(EGL_SURFACE_TYPE), |
| X(EGL_TRANSPARENT_TYPE), |
| X(EGL_TRANSPARENT_RED_VALUE), |
| X(EGL_TRANSPARENT_GREEN_VALUE), |
| X(EGL_TRANSPARENT_BLUE_VALUE), |
| X(EGL_BIND_TO_TEXTURE_RGB), |
| X(EGL_BIND_TO_TEXTURE_RGBA), |
| X(EGL_MIN_SWAP_INTERVAL), |
| X(EGL_MAX_SWAP_INTERVAL), |
| X(EGL_LUMINANCE_SIZE), |
| X(EGL_ALPHA_MASK_SIZE), |
| X(EGL_COLOR_BUFFER_TYPE), |
| X(EGL_RENDERABLE_TYPE), |
| X(EGL_CONFORMANT), |
| }; |
| #undef X |
| |
| for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) { |
| EGLint value = -1; |
| EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value); |
| EGLint error = eglGetError(); |
| if (returnVal && error == EGL_SUCCESS) { |
| printf(" %s: ", names[j].name); |
| printf("%d (0x%x)", value, value); |
| } |
| } |
| printf("\n"); |
| } |
| |
| int main(int argc, char** argv) { |
| EGLBoolean returnValue; |
| EGLConfig myConfig = {0}; |
| |
| EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE }; |
| EGLint s_configAttribs[] = { |
| EGL_SURFACE_TYPE, EGL_WINDOW_BIT, |
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, |
| EGL_NONE }; |
| EGLint majorVersion; |
| EGLint minorVersion; |
| EGLContext context; |
| EGLSurface surface; |
| EGLint w, h; |
| |
| EGLDisplay dpy; |
| |
| checkEglError("<init>"); |
| dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
| checkEglError("eglGetDisplay"); |
| if (dpy == EGL_NO_DISPLAY) { |
| printf("eglGetDisplay returned EGL_NO_DISPLAY.\n"); |
| return 0; |
| } |
| |
| returnValue = eglInitialize(dpy, &majorVersion, &minorVersion); |
| checkEglError("eglInitialize", returnValue); |
| fprintf(stderr, "EGL version %d.%d\n", majorVersion, minorVersion); |
| if (returnValue != EGL_TRUE) { |
| printf("eglInitialize failed\n"); |
| return 0; |
| } |
| |
| EGLNativeWindowType window = android_createDisplaySurface(); |
| returnValue = EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &myConfig); |
| if (returnValue) { |
| printf("EGLUtils::selectConfigForNativeWindow() returned %d", returnValue); |
| return 1; |
| } |
| |
| checkEglError("EGLUtils::selectConfigForNativeWindow"); |
| |
| printf("Chose this configuration:\n"); |
| printEGLConfiguration(dpy, myConfig); |
| |
| surface = eglCreateWindowSurface(dpy, myConfig, window, NULL); |
| checkEglError("eglCreateWindowSurface"); |
| if (surface == EGL_NO_SURFACE) { |
| printf("gelCreateWindowSurface failed.\n"); |
| return 1; |
| } |
| |
| context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs); |
| checkEglError("eglCreateContext"); |
| if (context == EGL_NO_CONTEXT) { |
| printf("eglCreateContext failed\n"); |
| return 1; |
| } |
| returnValue = eglMakeCurrent(dpy, surface, surface, context); |
| checkEglError("eglMakeCurrent", returnValue); |
| if (returnValue != EGL_TRUE) { |
| return 1; |
| } |
| eglQuerySurface(dpy, surface, EGL_WIDTH, &w); |
| checkEglError("eglQuerySurface"); |
| eglQuerySurface(dpy, surface, EGL_HEIGHT, &h); |
| checkEglError("eglQuerySurface"); |
| GLint dim = w < h ? w : h; |
| |
| fprintf(stderr, "Window dimensions: %d x %d\n", w, h); |
| |
| printGLString("Version", GL_VERSION); |
| printGLString("Vendor", GL_VENDOR); |
| printGLString("Renderer", GL_RENDERER); |
| printGLString("Extensions", GL_EXTENSIONS); |
| |
| if(!setupYuvTexSurface(dpy, context)) { |
| fprintf(stderr, "Could not set up texture surface.\n"); |
| return 1; |
| } |
| |
| if(!setupGraphics(w, h)) { |
| fprintf(stderr, "Could not set up graphics.\n"); |
| return 1; |
| } |
| |
| for (;;) { |
| static int dir = -1; |
| |
| renderFrame(w, h); |
| eglSwapBuffers(dpy, surface); |
| checkEglError("eglSwapBuffers"); |
| |
| if (w <= 10 || h <= 10) |
| { |
| dir = -dir; |
| } |
| |
| if (w >= 1300 || h >= 900) |
| { |
| dir = -dir; |
| } |
| |
| |
| w += dir; |
| h += dir; |
| } |
| |
| return 0; |
| } |