1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdlib.h> 18#include <stdio.h> 19#include <time.h> 20#include <sched.h> 21#include <sys/resource.h> 22 23#include <EGL/egl.h> 24#include <EGL/eglext.h> 25#include <GLES2/gl2.h> 26#include <GLES2/gl2ext.h> 27 28#include <utils/Timers.h> 29 30#include <WindowSurface.h> 31#include <ui/GraphicBuffer.h> 32#include <EGLUtils.h> 33 34using namespace android; 35 36static void printGLString(const char *name, GLenum s) { 37 // fprintf(stderr, "printGLString %s, %d\n", name, s); 38 const char *v = (const char *) glGetString(s); 39 // int error = glGetError(); 40 // fprintf(stderr, "glGetError() = %d, result of glGetString = %x\n", error, 41 // (unsigned int) v); 42 // if ((v < (const char*) 0) || (v > (const char*) 0x10000)) 43 // fprintf(stderr, "GL %s = %s\n", name, v); 44 // else 45 // fprintf(stderr, "GL %s = (null) 0x%08x\n", name, (unsigned int) v); 46 fprintf(stderr, "GL %s = %s\n", name, v); 47} 48 49static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) { 50 if (returnVal != EGL_TRUE) { 51 fprintf(stderr, "%s() returned %d\n", op, returnVal); 52 } 53 54 for (EGLint error = eglGetError(); error != EGL_SUCCESS; error 55 = eglGetError()) { 56 fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error), 57 error); 58 } 59} 60 61static void checkGlError(const char* op) { 62 for (GLint error = glGetError(); error; error 63 = glGetError()) { 64 fprintf(stderr, "after %s() glError (0x%x)\n", op, error); 65 } 66} 67 68static const char gVertexShader[] = "attribute vec4 vPosition;\n" 69 "varying vec2 yuvTexCoords;\n" 70 "void main() {\n" 71 " yuvTexCoords = vPosition.xy + vec2(0.5, 0.5);\n" 72 " gl_Position = vPosition;\n" 73 "}\n"; 74 75static const char gFragmentShader[] = "#extension GL_OES_EGL_image_external : require\n" 76 "precision mediump float;\n" 77 "uniform samplerExternalOES yuvTexSampler;\n" 78 "varying vec2 yuvTexCoords;\n" 79 "void main() {\n" 80 " gl_FragColor = texture2D(yuvTexSampler, yuvTexCoords);\n" 81 "}\n"; 82 83GLuint loadShader(GLenum shaderType, const char* pSource) { 84 GLuint shader = glCreateShader(shaderType); 85 if (shader) { 86 glShaderSource(shader, 1, &pSource, NULL); 87 glCompileShader(shader); 88 GLint compiled = 0; 89 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 90 if (!compiled) { 91 GLint infoLen = 0; 92 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 93 if (infoLen) { 94 char* buf = (char*) malloc(infoLen); 95 if (buf) { 96 glGetShaderInfoLog(shader, infoLen, NULL, buf); 97 fprintf(stderr, "Could not compile shader %d:\n%s\n", 98 shaderType, buf); 99 free(buf); 100 } 101 } else { 102 fprintf(stderr, "Guessing at GL_INFO_LOG_LENGTH size\n"); 103 char* buf = (char*) malloc(0x1000); 104 if (buf) { 105 glGetShaderInfoLog(shader, 0x1000, NULL, buf); 106 fprintf(stderr, "Could not compile shader %d:\n%s\n", 107 shaderType, buf); 108 free(buf); 109 } 110 } 111 glDeleteShader(shader); 112 shader = 0; 113 } 114 } 115 return shader; 116} 117 118GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) { 119 GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); 120 if (!vertexShader) { 121 return 0; 122 } 123 124 GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); 125 if (!pixelShader) { 126 return 0; 127 } 128 129 GLuint program = glCreateProgram(); 130 if (program) { 131 glAttachShader(program, vertexShader); 132 checkGlError("glAttachShader"); 133 glAttachShader(program, pixelShader); 134 checkGlError("glAttachShader"); 135 glLinkProgram(program); 136 GLint linkStatus = GL_FALSE; 137 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); 138 if (linkStatus != GL_TRUE) { 139 GLint bufLength = 0; 140 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); 141 if (bufLength) { 142 char* buf = (char*) malloc(bufLength); 143 if (buf) { 144 glGetProgramInfoLog(program, bufLength, NULL, buf); 145 fprintf(stderr, "Could not link program:\n%s\n", buf); 146 free(buf); 147 } 148 } 149 glDeleteProgram(program); 150 program = 0; 151 } 152 } 153 return program; 154} 155 156GLuint gProgram; 157GLint gvPositionHandle; 158GLint gYuvTexSamplerHandle; 159 160bool setupGraphics(int w, int h) { 161 gProgram = createProgram(gVertexShader, gFragmentShader); 162 if (!gProgram) { 163 return false; 164 } 165 gvPositionHandle = glGetAttribLocation(gProgram, "vPosition"); 166 checkGlError("glGetAttribLocation"); 167 fprintf(stderr, "glGetAttribLocation(\"vPosition\") = %d\n", 168 gvPositionHandle); 169 gYuvTexSamplerHandle = glGetUniformLocation(gProgram, "yuvTexSampler"); 170 checkGlError("glGetUniformLocation"); 171 fprintf(stderr, "glGetUniformLocation(\"yuvTexSampler\") = %d\n", 172 gYuvTexSamplerHandle); 173 174 glViewport(0, 0, w, h); 175 checkGlError("glViewport"); 176 return true; 177} 178 179int align(int x, int a) { 180 return (x + (a-1)) & (~(a-1)); 181} 182 183const int yuvTexWidth = 608; 184const int yuvTexHeight = 480; 185const int yuvTexUsage = GraphicBuffer::USAGE_HW_TEXTURE | 186 GraphicBuffer::USAGE_SW_WRITE_RARELY; 187const int yuvTexFormat = HAL_PIXEL_FORMAT_YV12; 188const int yuvTexOffsetY = 0; 189const bool yuvTexSameUV = false; 190static sp<GraphicBuffer> yuvTexBuffer; 191static GLuint yuvTex; 192 193bool setupYuvTexSurface(EGLDisplay dpy, EGLContext context) { 194 int blockWidth = yuvTexWidth > 16 ? yuvTexWidth / 16 : 1; 195 int blockHeight = yuvTexHeight > 16 ? yuvTexHeight / 16 : 1; 196 yuvTexBuffer = new GraphicBuffer(yuvTexWidth, yuvTexHeight, yuvTexFormat, 197 yuvTexUsage); 198 int yuvTexStrideY = yuvTexBuffer->getStride(); 199 int yuvTexOffsetV = yuvTexStrideY * yuvTexHeight; 200 int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; 201 int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * yuvTexHeight/2; 202 int yuvTexStrideU = yuvTexStrideV; 203 char* buf = NULL; 204 status_t err = yuvTexBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf)); 205 if (err != 0) { 206 fprintf(stderr, "yuvTexBuffer->lock(...) failed: %d\n", err); 207 return false; 208 } 209 for (int x = 0; x < yuvTexWidth; x++) { 210 for (int y = 0; y < yuvTexHeight; y++) { 211 int parityX = (x / blockWidth) & 1; 212 int parityY = (y / blockHeight) & 1; 213 unsigned char intensity = (parityX ^ parityY) ? 63 : 191; 214 buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; 215 if (x < yuvTexWidth / 2 && y < yuvTexHeight / 2) { 216 buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; 217 if (yuvTexSameUV) { 218 buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = intensity; 219 } else if (x < yuvTexWidth / 4 && y < yuvTexHeight / 4) { 220 buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = 221 buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = 222 buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = 223 buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = intensity; 224 } 225 } 226 } 227 } 228 229 err = yuvTexBuffer->unlock(); 230 if (err != 0) { 231 fprintf(stderr, "yuvTexBuffer->unlock() failed: %d\n", err); 232 return false; 233 } 234 235 EGLClientBuffer clientBuffer = (EGLClientBuffer)yuvTexBuffer->getNativeBuffer(); 236 EGLImageKHR img = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, 237 clientBuffer, 0); 238 checkEglError("eglCreateImageKHR"); 239 if (img == EGL_NO_IMAGE_KHR) { 240 return false; 241 } 242 243 glGenTextures(1, &yuvTex); 244 checkGlError("glGenTextures"); 245 glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTex); 246 checkGlError("glBindTexture"); 247 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)img); 248 checkGlError("glEGLImageTargetTexture2DOES"); 249 250 return true; 251} 252 253const GLfloat gTriangleVertices[] = { 254 -0.5f, 0.5f, 255 -0.5f, -0.5f, 256 0.5f, -0.5f, 257 0.5f, 0.5f, 258}; 259 260void renderFrame() { 261 glClearColor(0.0f, 0.0f, 1.0f, 1.0f); 262 checkGlError("glClearColor"); 263 glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 264 checkGlError("glClear"); 265 266 glUseProgram(gProgram); 267 checkGlError("glUseProgram"); 268 269 glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices); 270 checkGlError("glVertexAttribPointer"); 271 glEnableVertexAttribArray(gvPositionHandle); 272 checkGlError("glEnableVertexAttribArray"); 273 274 glUniform1i(gYuvTexSamplerHandle, 0); 275 checkGlError("glUniform1i"); 276 glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTex); 277 checkGlError("glBindTexture"); 278 279 glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 280 checkGlError("glDrawArrays"); 281} 282 283void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { 284 285#define X(VAL) {VAL, #VAL} 286 struct {EGLint attribute; const char* name;} names[] = { 287 X(EGL_BUFFER_SIZE), 288 X(EGL_ALPHA_SIZE), 289 X(EGL_BLUE_SIZE), 290 X(EGL_GREEN_SIZE), 291 X(EGL_RED_SIZE), 292 X(EGL_DEPTH_SIZE), 293 X(EGL_STENCIL_SIZE), 294 X(EGL_CONFIG_CAVEAT), 295 X(EGL_CONFIG_ID), 296 X(EGL_LEVEL), 297 X(EGL_MAX_PBUFFER_HEIGHT), 298 X(EGL_MAX_PBUFFER_PIXELS), 299 X(EGL_MAX_PBUFFER_WIDTH), 300 X(EGL_NATIVE_RENDERABLE), 301 X(EGL_NATIVE_VISUAL_ID), 302 X(EGL_NATIVE_VISUAL_TYPE), 303 X(EGL_SAMPLES), 304 X(EGL_SAMPLE_BUFFERS), 305 X(EGL_SURFACE_TYPE), 306 X(EGL_TRANSPARENT_TYPE), 307 X(EGL_TRANSPARENT_RED_VALUE), 308 X(EGL_TRANSPARENT_GREEN_VALUE), 309 X(EGL_TRANSPARENT_BLUE_VALUE), 310 X(EGL_BIND_TO_TEXTURE_RGB), 311 X(EGL_BIND_TO_TEXTURE_RGBA), 312 X(EGL_MIN_SWAP_INTERVAL), 313 X(EGL_MAX_SWAP_INTERVAL), 314 X(EGL_LUMINANCE_SIZE), 315 X(EGL_ALPHA_MASK_SIZE), 316 X(EGL_COLOR_BUFFER_TYPE), 317 X(EGL_RENDERABLE_TYPE), 318 X(EGL_CONFORMANT), 319 }; 320#undef X 321 322 for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) { 323 EGLint value = -1; 324 EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value); 325 EGLint error = eglGetError(); 326 if (returnVal && error == EGL_SUCCESS) { 327 printf(" %s: ", names[j].name); 328 printf("%d (0x%x)", value, value); 329 } 330 } 331 printf("\n"); 332} 333 334int main(int argc, char** argv) { 335 EGLBoolean returnValue; 336 EGLConfig myConfig = {0}; 337 338 EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; 339 EGLint s_configAttribs[] = { 340 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 341 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 342 EGL_NONE }; 343 EGLint majorVersion; 344 EGLint minorVersion; 345 EGLContext context; 346 EGLSurface surface; 347 EGLint w, h; 348 349 EGLDisplay dpy; 350 351 checkEglError("<init>"); 352 dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); 353 checkEglError("eglGetDisplay"); 354 if (dpy == EGL_NO_DISPLAY) { 355 printf("eglGetDisplay returned EGL_NO_DISPLAY.\n"); 356 return 0; 357 } 358 359 returnValue = eglInitialize(dpy, &majorVersion, &minorVersion); 360 checkEglError("eglInitialize", returnValue); 361 fprintf(stderr, "EGL version %d.%d\n", majorVersion, minorVersion); 362 if (returnValue != EGL_TRUE) { 363 printf("eglInitialize failed\n"); 364 return 0; 365 } 366 367 WindowSurface windowSurface; 368 EGLNativeWindowType window = windowSurface.getSurface(); 369 returnValue = EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &myConfig); 370 if (returnValue) { 371 printf("EGLUtils::selectConfigForNativeWindow() returned %d", returnValue); 372 return 1; 373 } 374 375 checkEglError("EGLUtils::selectConfigForNativeWindow"); 376 377 printf("Chose this configuration:\n"); 378 printEGLConfiguration(dpy, myConfig); 379 380 surface = eglCreateWindowSurface(dpy, myConfig, window, NULL); 381 checkEglError("eglCreateWindowSurface"); 382 if (surface == EGL_NO_SURFACE) { 383 printf("gelCreateWindowSurface failed.\n"); 384 return 1; 385 } 386 387 context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs); 388 checkEglError("eglCreateContext"); 389 if (context == EGL_NO_CONTEXT) { 390 printf("eglCreateContext failed\n"); 391 return 1; 392 } 393 returnValue = eglMakeCurrent(dpy, surface, surface, context); 394 checkEglError("eglMakeCurrent", returnValue); 395 if (returnValue != EGL_TRUE) { 396 return 1; 397 } 398 eglQuerySurface(dpy, surface, EGL_WIDTH, &w); 399 checkEglError("eglQuerySurface"); 400 eglQuerySurface(dpy, surface, EGL_HEIGHT, &h); 401 checkEglError("eglQuerySurface"); 402 GLint dim = w < h ? w : h; 403 404 fprintf(stderr, "Window dimensions: %d x %d\n", w, h); 405 406 printGLString("Version", GL_VERSION); 407 printGLString("Vendor", GL_VENDOR); 408 printGLString("Renderer", GL_RENDERER); 409 printGLString("Extensions", GL_EXTENSIONS); 410 411 if(!setupYuvTexSurface(dpy, context)) { 412 fprintf(stderr, "Could not set up texture surface.\n"); 413 return 1; 414 } 415 416 if(!setupGraphics(w, h)) { 417 fprintf(stderr, "Could not set up graphics.\n"); 418 return 1; 419 } 420 421 for (;;) { 422 renderFrame(); 423 eglSwapBuffers(dpy, surface); 424 checkEglError("eglSwapBuffers"); 425 } 426 427 return 0; 428} 429