1/*
2 * Copyright (C) 2007 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 <GLES2/gl2.h>
25#include <GLES2/gl2ext.h>
26
27#include <utils/Timers.h>
28
29#include <ui/FramebufferNativeWindow.h>
30#include "EGLUtils.h"
31
32using namespace android;
33
34static void printGLString(const char *name, GLenum s) {
35    // fprintf(stderr, "printGLString %s, %d\n", name, s);
36    const char *v = (const char *) glGetString(s);
37    // int error = glGetError();
38    // fprintf(stderr, "glGetError() = %d, result of glGetString = %x\n", error,
39    //        (unsigned int) v);
40    // if ((v < (const char*) 0) || (v > (const char*) 0x10000))
41    //    fprintf(stderr, "GL %s = %s\n", name, v);
42    // else
43    //    fprintf(stderr, "GL %s = (null) 0x%08x\n", name, (unsigned int) v);
44    fprintf(stderr, "GL %s = %s\n", name, v);
45}
46
47static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
48    if (returnVal != EGL_TRUE) {
49        fprintf(stderr, "%s() returned %d\n", op, returnVal);
50    }
51
52    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
53            = eglGetError()) {
54        fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
55                error);
56    }
57}
58
59static void checkGlError(const char* op) {
60    for (GLint error = glGetError(); error; error
61            = glGetError()) {
62        fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
63    }
64}
65
66static const char gVertexShader[] = "attribute vec4 vPosition;\n"
67    "void main() {\n"
68    "  gl_Position = vPosition;\n"
69    "}\n";
70
71static const char gFragmentShader[] = "precision mediump float;\n"
72    "void main() {\n"
73    "  gl_FragColor = vec4(0.0, 1.0, 0.0, 0.5);\n"
74    "}\n";
75
76GLuint loadShader(GLenum shaderType, const char* pSource) {
77    GLuint shader = glCreateShader(shaderType);
78    if (shader) {
79        glShaderSource(shader, 1, &pSource, NULL);
80        glCompileShader(shader);
81        GLint compiled = 0;
82        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
83        if (!compiled) {
84            GLint infoLen = 0;
85            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
86            if (infoLen) {
87                char* buf = (char*) malloc(infoLen);
88                if (buf) {
89                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
90                    fprintf(stderr, "Could not compile shader %d:\n%s\n",
91                            shaderType, buf);
92                    free(buf);
93                }
94                glDeleteShader(shader);
95                shader = 0;
96            }
97        }
98    }
99    return shader;
100}
101
102GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
103    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
104    if (!vertexShader) {
105        return 0;
106    }
107
108    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
109    if (!pixelShader) {
110        return 0;
111    }
112
113    GLuint program = glCreateProgram();
114    if (program) {
115        glAttachShader(program, vertexShader);
116        checkGlError("glAttachShader");
117        glAttachShader(program, pixelShader);
118        checkGlError("glAttachShader");
119        glLinkProgram(program);
120        GLint linkStatus = GL_FALSE;
121        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
122        if (linkStatus != GL_TRUE) {
123            GLint bufLength = 0;
124            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
125            if (bufLength) {
126                char* buf = (char*) malloc(bufLength);
127                if (buf) {
128                    glGetProgramInfoLog(program, bufLength, NULL, buf);
129                    fprintf(stderr, "Could not link program:\n%s\n", buf);
130                    free(buf);
131                }
132            }
133            glDeleteProgram(program);
134            program = 0;
135        }
136    }
137    return program;
138}
139
140GLuint gProgram;
141GLuint gTextureProgram;
142GLuint gvPositionHandle;
143GLuint gvTexturePositionHandle;
144GLuint gvTextureTexCoordsHandle;
145GLuint gvTextureSamplerHandle;
146GLuint gFbo;
147GLuint gTexture;
148GLuint gBufferTexture;
149
150static const char gSimpleVS[] =
151    "attribute vec4 position;\n"
152    "attribute vec2 texCoords;\n"
153    "varying vec2 outTexCoords;\n"
154    "\nvoid main(void) {\n"
155    "    outTexCoords = texCoords;\n"
156    "    gl_Position = position;\n"
157    "}\n\n";
158static const char gSimpleFS[] =
159    "precision mediump float;\n\n"
160    "varying vec2 outTexCoords;\n"
161    "uniform sampler2D texture;\n"
162    "\nvoid main(void) {\n"
163    "    gl_FragColor = texture2D(texture, outTexCoords);\n"
164    "}\n\n";
165
166bool setupGraphics(int w, int h) {
167    gProgram = createProgram(gVertexShader, gFragmentShader);
168    if (!gProgram) {
169        return false;
170    }
171    gvPositionHandle = glGetAttribLocation(gProgram, "vPosition");
172    checkGlError("glGetAttribLocation");
173    fprintf(stderr, "glGetAttribLocation(\"vPosition\") = %d\n", gvPositionHandle);
174
175    gTextureProgram = createProgram(gSimpleVS, gSimpleFS);
176    if (!gTextureProgram) {
177        return false;
178    }
179    gvTexturePositionHandle = glGetAttribLocation(gTextureProgram, "position");
180    checkGlError("glGetAttribLocation");
181    gvTextureTexCoordsHandle = glGetAttribLocation(gTextureProgram, "texCoords");
182    checkGlError("glGetAttribLocation");
183    gvTextureSamplerHandle = glGetUniformLocation(gTextureProgram, "texture");
184    checkGlError("glGetAttribLocation");
185
186    glActiveTexture(GL_TEXTURE0);
187
188    glGenTextures(1, &gTexture);
189    glBindTexture(GL_TEXTURE_2D, gTexture);
190    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
191    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
192    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
193    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
194    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
195
196    glGenTextures(1, &gBufferTexture);
197    glBindTexture(GL_TEXTURE_2D, gBufferTexture);
198    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
199    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
200    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
201    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
202    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
203
204    glGenFramebuffers(1, &gFbo);
205    glBindFramebuffer(GL_FRAMEBUFFER, gFbo);
206    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gTexture, 0);
207
208    glBindFramebuffer(GL_FRAMEBUFFER, 0);
209
210    glViewport(0, 0, w, h);
211    checkGlError("glViewport");
212    return true;
213}
214
215const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f,
216        0.5f, -0.5f };
217
218const GLint FLOAT_SIZE_BYTES = 4;
219const GLint TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
220const GLfloat gTriangleVerticesData[] = {
221    // X, Y, Z, U, V
222    -1.0f, -1.0f, 0, 0.f, 0.f,
223    1.0f, -1.0f, 0, 1.f, 0.f,
224    -1.0f,  1.0f, 0, 0.f, 1.f,
225    1.0f,   1.0f, 0, 1.f, 1.f,
226};
227
228void renderFrame(GLint w, GLint h) {
229    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
230    checkGlError("glClearColor");
231    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
232    checkGlError("glClear");
233
234    // Bind FBO and draw into it
235    glBindFramebuffer(GL_FRAMEBUFFER, gFbo);
236    checkGlError("glBindFramebuffer");
237
238    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
239    checkGlError("glClearColor");
240    glClear(GL_COLOR_BUFFER_BIT);
241    checkGlError("glClear");
242
243    glUseProgram(gProgram);
244    checkGlError("glUseProgram");
245
246    glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices);
247    checkGlError("glVertexAttribPointer");
248    glEnableVertexAttribArray(gvPositionHandle);
249    checkGlError("glEnableVertexAttribArray");
250    glDrawArrays(GL_TRIANGLES, 0, 3);
251    checkGlError("glDrawArrays");
252
253    // Copy content of FBO into a texture
254    glBindTexture(GL_TEXTURE_2D, gBufferTexture);
255    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, w / 2, h / 2);
256    checkGlError("glCopyTexSubImage2D");
257
258    // Back to the display
259    glBindFramebuffer(GL_FRAMEBUFFER, 0);
260    checkGlError("glBindFramebuffer");
261
262    // Draw copied content on the screen
263    glUseProgram(gTextureProgram);
264    checkGlError("glUseProgram");
265
266    glEnable(GL_BLEND);
267    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
268
269    glVertexAttribPointer(gvTexturePositionHandle, 3, GL_FLOAT, GL_FALSE,
270            TRIANGLE_VERTICES_DATA_STRIDE_BYTES, gTriangleVerticesData);
271    checkGlError("glVertexAttribPointer");
272    glVertexAttribPointer(gvTextureTexCoordsHandle, 2, GL_FLOAT, GL_FALSE,
273            TRIANGLE_VERTICES_DATA_STRIDE_BYTES, &gTriangleVerticesData[3]);
274    checkGlError("glVertexAttribPointer");
275    glEnableVertexAttribArray(gvTexturePositionHandle);
276    glEnableVertexAttribArray(gvTextureTexCoordsHandle);
277    checkGlError("glEnableVertexAttribArray");
278    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
279    checkGlError("glDrawArrays");
280}
281
282void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
283
284#define X(VAL) {VAL, #VAL}
285    struct {EGLint attribute; const char* name;} names[] = {
286    X(EGL_BUFFER_SIZE),
287    X(EGL_ALPHA_SIZE),
288    X(EGL_BLUE_SIZE),
289    X(EGL_GREEN_SIZE),
290    X(EGL_RED_SIZE),
291    X(EGL_DEPTH_SIZE),
292    X(EGL_STENCIL_SIZE),
293    X(EGL_CONFIG_CAVEAT),
294    X(EGL_CONFIG_ID),
295    X(EGL_LEVEL),
296    X(EGL_MAX_PBUFFER_HEIGHT),
297    X(EGL_MAX_PBUFFER_PIXELS),
298    X(EGL_MAX_PBUFFER_WIDTH),
299    X(EGL_NATIVE_RENDERABLE),
300    X(EGL_NATIVE_VISUAL_ID),
301    X(EGL_NATIVE_VISUAL_TYPE),
302    X(EGL_SAMPLES),
303    X(EGL_SAMPLE_BUFFERS),
304    X(EGL_SURFACE_TYPE),
305    X(EGL_TRANSPARENT_TYPE),
306    X(EGL_TRANSPARENT_RED_VALUE),
307    X(EGL_TRANSPARENT_GREEN_VALUE),
308    X(EGL_TRANSPARENT_BLUE_VALUE),
309    X(EGL_BIND_TO_TEXTURE_RGB),
310    X(EGL_BIND_TO_TEXTURE_RGBA),
311    X(EGL_MIN_SWAP_INTERVAL),
312    X(EGL_MAX_SWAP_INTERVAL),
313    X(EGL_LUMINANCE_SIZE),
314    X(EGL_ALPHA_MASK_SIZE),
315    X(EGL_COLOR_BUFFER_TYPE),
316    X(EGL_RENDERABLE_TYPE),
317    X(EGL_CONFORMANT),
318   };
319#undef X
320
321    for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
322        EGLint value = -1;
323        EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
324        EGLint error = eglGetError();
325        if (returnVal && error == EGL_SUCCESS) {
326            printf(" %s: ", names[j].name);
327            printf("%d (0x%x)", value, value);
328        }
329    }
330    printf("\n");
331}
332
333int printEGLConfigurations(EGLDisplay dpy) {
334    EGLint numConfig = 0;
335    EGLint returnVal = eglGetConfigs(dpy, NULL, 0, &numConfig);
336    checkEglError("eglGetConfigs", returnVal);
337    if (!returnVal) {
338        return false;
339    }
340
341    printf("Number of EGL configuration: %d\n", numConfig);
342
343    EGLConfig* configs = (EGLConfig*) malloc(sizeof(EGLConfig) * numConfig);
344    if (! configs) {
345        printf("Could not allocate configs.\n");
346        return false;
347    }
348
349    returnVal = eglGetConfigs(dpy, configs, numConfig, &numConfig);
350    checkEglError("eglGetConfigs", returnVal);
351    if (!returnVal) {
352        free(configs);
353        return false;
354    }
355
356    for(int i = 0; i < numConfig; i++) {
357        printf("Configuration %d\n", i);
358        printEGLConfiguration(dpy, configs[i]);
359    }
360
361    free(configs);
362    return true;
363}
364
365int main(int argc, char** argv) {
366    EGLBoolean returnValue;
367    EGLConfig myConfig = {0};
368
369    EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
370    EGLint s_configAttribs[] = {
371            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
372            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
373            EGL_RED_SIZE, 8,
374            EGL_GREEN_SIZE, 8,
375            EGL_BLUE_SIZE, 8,
376            EGL_ALPHA_SIZE, 8,
377            EGL_NONE };
378    EGLint majorVersion;
379    EGLint minorVersion;
380    EGLContext context;
381    EGLSurface surface;
382    EGLint w, h;
383
384    EGLDisplay dpy;
385
386    checkEglError("<init>");
387    dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
388    checkEglError("eglGetDisplay");
389    if (dpy == EGL_NO_DISPLAY) {
390        printf("eglGetDisplay returned EGL_NO_DISPLAY.\n");
391        return 0;
392    }
393
394    returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
395    checkEglError("eglInitialize", returnValue);
396    fprintf(stderr, "EGL version %d.%d\n", majorVersion, minorVersion);
397    if (returnValue != EGL_TRUE) {
398        printf("eglInitialize failed\n");
399        return 0;
400    }
401
402    if (!printEGLConfigurations(dpy)) {
403        printf("printEGLConfigurations failed\n");
404        return 0;
405    }
406
407    checkEglError("printEGLConfigurations");
408
409    EGLNativeWindowType window = android_createDisplaySurface();
410    EGLint numConfigs = -1, n = 0;
411    eglChooseConfig(dpy, s_configAttribs, 0, 0, &numConfigs);
412    if (numConfigs) {
413        EGLConfig* const configs = new EGLConfig[numConfigs];
414        eglChooseConfig(dpy, s_configAttribs, configs, numConfigs, &n);
415        myConfig = configs[0];
416        delete[] configs;
417    }
418
419    checkEglError("EGLUtils::selectConfigForNativeWindow");
420
421    printf("Chose this configuration:\n");
422    printEGLConfiguration(dpy, myConfig);
423
424    surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
425    checkEglError("eglCreateWindowSurface");
426    if (surface == EGL_NO_SURFACE) {
427        printf("gelCreateWindowSurface failed.\n");
428        return 0;
429    }
430
431    context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs);
432    checkEglError("eglCreateContext");
433    if (context == EGL_NO_CONTEXT) {
434        printf("eglCreateContext failed\n");
435        return 0;
436    }
437    returnValue = eglMakeCurrent(dpy, surface, surface, context);
438    checkEglError("eglMakeCurrent", returnValue);
439    if (returnValue != EGL_TRUE) {
440        return 0;
441    }
442    eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
443    checkEglError("eglQuerySurface");
444    eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
445    checkEglError("eglQuerySurface");
446    GLint dim = w < h ? w : h;
447
448    fprintf(stderr, "Window dimensions: %d x %d\n", w, h);
449
450    printGLString("Version", GL_VERSION);
451    printGLString("Vendor", GL_VENDOR);
452    printGLString("Renderer", GL_RENDERER);
453    printGLString("Extensions", GL_EXTENSIONS);
454
455    if(!setupGraphics(w, h)) {
456        fprintf(stderr, "Could not set up graphics.\n");
457        return 0;
458    }
459
460    for (;;) {
461        renderFrame(w, h);
462        eglSwapBuffers(dpy, surface);
463        checkEglError("eglSwapBuffers");
464    }
465
466    return 0;
467}
468