1/*
2 * Copyright (C) 2017 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 "GlWrapper.h"
18
19#include <stdio.h>
20#include <fcntl.h>
21#include <sys/ioctl.h>
22
23#include <ui/DisplayInfo.h>
24#include <ui/GraphicBuffer.h>
25#include <ui/GraphicBufferAllocator.h>
26#include <ui/GraphicBufferMapper.h>
27
28
29using namespace android;
30
31
32// TODO:  Consider dropping direct use of GraphicsBufferAllocator and Mapper?
33using android::GraphicBuffer;
34using android::GraphicBufferAllocator;
35using android::GraphicBufferMapper;
36using android::sp;
37
38
39const char vertexShaderSource[] = ""
40        "#version 300 es                    \n"
41        "layout(location = 0) in vec4 pos;  \n"
42        "layout(location = 1) in vec2 tex;  \n"
43        "out vec2 uv;                       \n"
44        "void main()                        \n"
45        "{                                  \n"
46        "   gl_Position = pos;              \n"
47        "   uv = tex;                       \n"
48        "}                                  \n";
49
50const char pixelShaderSource[] =
51        "#version 300 es                            \n"
52        "precision mediump float;                   \n"
53        "uniform sampler2D tex;                     \n"
54        "in vec2 uv;                                \n"
55        "out vec4 color;                            \n"
56        "void main()                                \n"
57        "{                                          \n"
58        "    vec4 texel = texture(tex, uv);         \n"
59        "    color = texel;                         \n"
60        "}                                          \n";
61
62
63static const char *getEGLError(void) {
64    switch (eglGetError()) {
65        case EGL_SUCCESS:
66            return "EGL_SUCCESS";
67        case EGL_NOT_INITIALIZED:
68            return "EGL_NOT_INITIALIZED";
69        case EGL_BAD_ACCESS:
70            return "EGL_BAD_ACCESS";
71        case EGL_BAD_ALLOC:
72            return "EGL_BAD_ALLOC";
73        case EGL_BAD_ATTRIBUTE:
74            return "EGL_BAD_ATTRIBUTE";
75        case EGL_BAD_CONTEXT:
76            return "EGL_BAD_CONTEXT";
77        case EGL_BAD_CONFIG:
78            return "EGL_BAD_CONFIG";
79        case EGL_BAD_CURRENT_SURFACE:
80            return "EGL_BAD_CURRENT_SURFACE";
81        case EGL_BAD_DISPLAY:
82            return "EGL_BAD_DISPLAY";
83        case EGL_BAD_SURFACE:
84            return "EGL_BAD_SURFACE";
85        case EGL_BAD_MATCH:
86            return "EGL_BAD_MATCH";
87        case EGL_BAD_PARAMETER:
88            return "EGL_BAD_PARAMETER";
89        case EGL_BAD_NATIVE_PIXMAP:
90            return "EGL_BAD_NATIVE_PIXMAP";
91        case EGL_BAD_NATIVE_WINDOW:
92            return "EGL_BAD_NATIVE_WINDOW";
93        case EGL_CONTEXT_LOST:
94            return "EGL_CONTEXT_LOST";
95        default:
96            return "Unknown error";
97    }
98}
99
100
101// Given shader source, load and compile it
102static GLuint loadShader(GLenum type, const char *shaderSrc) {
103    // Create the shader object
104    GLuint shader = glCreateShader (type);
105    if (shader == 0) {
106        return 0;
107    }
108
109    // Load and compile the shader
110    glShaderSource(shader, 1, &shaderSrc, nullptr);
111    glCompileShader(shader);
112
113    // Verify the compilation worked as expected
114    GLint compiled = 0;
115    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
116    if (!compiled) {
117        ALOGE("Error compiling shader\n");
118
119        GLint size = 0;
120        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
121        if (size > 0)
122        {
123            // Get and report the error message
124            char *infoLog = (char*)malloc(size);
125            glGetShaderInfoLog(shader, size, nullptr, infoLog);
126            ALOGE("  msg:\n%s\n", infoLog);
127            free(infoLog);
128        }
129
130        glDeleteShader(shader);
131        return 0;
132    }
133
134    return shader;
135}
136
137
138// Create a program object given vertex and pixels shader source
139static GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
140    GLuint program = glCreateProgram();
141    if (program == 0) {
142        ALOGE("Failed to allocate program object\n");
143        return 0;
144    }
145
146    // Compile the shaders and bind them to this program
147    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vtxSrc);
148    if (vertexShader == 0) {
149        ALOGE("Failed to load vertex shader\n");
150        glDeleteProgram(program);
151        return 0;
152    }
153    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pxlSrc);
154    if (pixelShader == 0) {
155        ALOGE("Failed to load pixel shader\n");
156        glDeleteProgram(program);
157        glDeleteShader(vertexShader);
158        return 0;
159    }
160    glAttachShader(program, vertexShader);
161    glAttachShader(program, pixelShader);
162
163    // Link the program
164    glLinkProgram(program);
165    GLint linked = 0;
166    glGetProgramiv(program, GL_LINK_STATUS, &linked);
167    if (!linked)
168    {
169        ALOGE("Error linking program.\n");
170        GLint size = 0;
171        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
172        if (size > 0)
173        {
174            // Get and report the error message
175            char *infoLog = (char*)malloc(size);
176            glGetProgramInfoLog(program, size, nullptr, infoLog);
177            ALOGE("  msg:  %s\n", infoLog);
178            free(infoLog);
179        }
180
181        glDeleteProgram(program);
182        glDeleteShader(vertexShader);
183        glDeleteShader(pixelShader);
184        return 0;
185    }
186
187    return program;
188}
189
190
191// Main entry point
192bool GlWrapper::initialize() {
193    //
194    //  Create the native full screen window and get a suitable configuration to match it
195    //
196    status_t err;
197
198    mFlinger = new SurfaceComposerClient();
199    if (mFlinger == nullptr) {
200        ALOGE("SurfaceComposerClient couldn't be allocated");
201        return false;
202    }
203    err = mFlinger->initCheck();
204    if (err != NO_ERROR) {
205        ALOGE("SurfaceComposerClient::initCheck error: %#x", err);
206        return false;
207    }
208
209    // Get main display parameters.
210    sp <IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
211            ISurfaceComposer::eDisplayIdMain);
212    DisplayInfo mainDpyInfo;
213    err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
214    if (err != NO_ERROR) {
215        ALOGE("ERROR: unable to get display characteristics");
216        return false;
217    }
218
219    if (mainDpyInfo.orientation != DISPLAY_ORIENTATION_0 &&
220        mainDpyInfo.orientation != DISPLAY_ORIENTATION_180) {
221        // rotated
222        mWidth = mainDpyInfo.h;
223        mHeight = mainDpyInfo.w;
224    } else {
225        mWidth = mainDpyInfo.w;
226        mHeight = mainDpyInfo.h;
227    }
228
229    mFlingerSurfaceControl = mFlinger->createSurface(
230            String8("Evs Display"), mWidth, mHeight,
231            PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
232    if (mFlingerSurfaceControl == nullptr || !mFlingerSurfaceControl->isValid()) {
233        ALOGE("Failed to create SurfaceControl");
234        return false;
235    }
236    mFlingerSurface = mFlingerSurfaceControl->getSurface();
237
238
239    // Set up our OpenGL ES context associated with the default display
240    mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
241    if (mDisplay == EGL_NO_DISPLAY) {
242        ALOGE("Failed to get egl display");
243        return false;
244    }
245
246    EGLint major = 3;
247    EGLint minor = 0;
248    if (!eglInitialize(mDisplay, &major, &minor)) {
249        ALOGE("Failed to initialize EGL: %s", getEGLError());
250        return false;
251    }
252
253
254    const EGLint config_attribs[] = {
255            // Tag                  Value
256            EGL_RED_SIZE,           8,
257            EGL_GREEN_SIZE,         8,
258            EGL_BLUE_SIZE,          8,
259            EGL_DEPTH_SIZE,         0,
260            EGL_NONE
261    };
262
263    // Pick the default configuration without constraints (is this good enough?)
264    EGLConfig egl_config = {0};
265    EGLint numConfigs = -1;
266    eglChooseConfig(mDisplay, config_attribs, &egl_config, 1, &numConfigs);
267    if (numConfigs != 1) {
268        ALOGE("Didn't find a suitable format for our display window");
269        return false;
270    }
271
272    // Create the EGL render target surface
273    mSurface = eglCreateWindowSurface(mDisplay, egl_config, mFlingerSurface.get(), nullptr);
274    if (mSurface == EGL_NO_SURFACE) {
275        ALOGE("gelCreateWindowSurface failed.");
276        return false;
277    }
278
279    // Create the EGL context
280    // NOTE:  Our shader is (currently at least) written to require version 3, so this
281    //        is required.
282    const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
283    mContext = eglCreateContext(mDisplay, egl_config, EGL_NO_CONTEXT, context_attribs);
284    if (mContext == EGL_NO_CONTEXT) {
285        ALOGE("Failed to create OpenGL ES Context: %s", getEGLError());
286        return false;
287    }
288
289
290    // Activate our render target for drawing
291    if (!eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
292        ALOGE("Failed to make the OpenGL ES Context current: %s", getEGLError());
293        return false;
294    }
295
296
297    // Create the shader program for our simple pipeline
298    mShaderProgram = buildShaderProgram(vertexShaderSource, pixelShaderSource);
299    if (!mShaderProgram) {
300        ALOGE("Failed to build shader program: %s", getEGLError());
301        return false;
302    }
303
304    // Create a GL texture that will eventually wrap our externally created texture surface(s)
305    glGenTextures(1, &mTextureMap);
306    if (mTextureMap <= 0) {
307        ALOGE("Didn't get a texture handle allocated: %s", getEGLError());
308        return false;
309    }
310
311
312    return true;
313}
314
315
316void GlWrapper::shutdown() {
317
318    // Drop our device textures
319    if (mKHRimage != EGL_NO_IMAGE_KHR) {
320        eglDestroyImageKHR(mDisplay, mKHRimage);
321        mKHRimage = EGL_NO_IMAGE_KHR;
322    }
323
324    // Release all GL resources
325    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
326    eglDestroySurface(mDisplay, mSurface);
327    eglDestroyContext(mDisplay, mContext);
328    eglTerminate(mDisplay);
329    mSurface = EGL_NO_SURFACE;
330    mContext = EGL_NO_CONTEXT;
331    mDisplay = EGL_NO_DISPLAY;
332
333    // Let go of our SurfaceComposer resources
334    mFlingerSurface.clear();
335    mFlingerSurfaceControl.clear();
336    mFlinger.clear();
337}
338
339
340void GlWrapper::showWindow() {
341    if (mFlingerSurfaceControl != nullptr) {
342        SurfaceComposerClient::openGlobalTransaction();
343        mFlingerSurfaceControl->setLayer(0x7FFFFFFF);     // always on top
344        mFlingerSurfaceControl->show();
345        SurfaceComposerClient::closeGlobalTransaction();
346    }
347}
348
349
350void GlWrapper::hideWindow() {
351    if (mFlingerSurfaceControl != nullptr) {
352        SurfaceComposerClient::openGlobalTransaction();
353        mFlingerSurfaceControl->hide();
354        SurfaceComposerClient::closeGlobalTransaction();
355    }
356}
357
358
359bool GlWrapper::updateImageTexture(const BufferDesc& buffer) {
360
361    // If we haven't done it yet, create an "image" object to wrap the gralloc buffer
362    if (mKHRimage == EGL_NO_IMAGE_KHR) {
363        // create a temporary GraphicBuffer to wrap the provided handle
364        sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(
365                buffer.width,
366                buffer.height,
367                buffer.format,
368                1,      /* layer count */
369                buffer.usage,
370                buffer.stride,
371                const_cast<native_handle_t*>(buffer.memHandle.getNativeHandle()),
372                false   /* keep ownership */
373        );
374        if (pGfxBuffer.get() == nullptr) {
375            ALOGE("Failed to allocate GraphicsBuffer to wrap our native handle");
376            return false;
377        }
378
379
380        // Get a GL compatible reference to the graphics buffer we've been given
381        EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
382        EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
383// TODO:  If we pass in a context, we get "bad context" back
384#if 0
385        mKHRimage = eglCreateImageKHR(mDisplay, mContext,
386                                      EGL_NATIVE_BUFFER_ANDROID, cbuf,
387                                      eglImageAttributes);
388#else
389        mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT,
390                                      EGL_NATIVE_BUFFER_ANDROID, cbuf,
391                                      eglImageAttributes);
392#endif
393        if (mKHRimage == EGL_NO_IMAGE_KHR) {
394            ALOGE("error creating EGLImage: %s", getEGLError());
395            return false;
396        }
397
398
399        // Update the texture handle we already created to refer to this gralloc buffer
400        glActiveTexture(GL_TEXTURE0);
401        glBindTexture(GL_TEXTURE_2D, mTextureMap);
402        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
403
404    }
405
406    return true;
407}
408
409
410void GlWrapper::renderImageToScreen() {
411    // Set the viewport
412    glViewport(0, 0, mWidth, mHeight);
413
414    // Clear the color buffer
415    glClearColor(0.1f, 0.5f, 0.1f, 1.0f);
416    glClear(GL_COLOR_BUFFER_BIT);
417
418    // Select our screen space simple texture shader
419    glUseProgram(mShaderProgram);
420
421    // Bind the texture and assign it to the shader's sampler
422    glActiveTexture(GL_TEXTURE0);
423    glBindTexture(GL_TEXTURE_2D, mTextureMap);
424    GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
425    glUniform1i(sampler, 0);
426
427    // We want our image to show up opaque regardless of alpha values
428    glDisable(GL_BLEND);
429
430
431    // Draw a rectangle on the screen
432    // TODO:  We pulled in from the edges for now for diagnostic purposes...
433#if 0
434    GLfloat vertsCarPos[] = { -1.0,  1.0, 0.0f,   // left top in window space
435                               1.0,  1.0, 0.0f,   // right top
436                              -1.0, -1.0, 0.0f,   // left bottom
437                               1.0, -1.0, 0.0f    // right bottom
438    };
439#else
440    GLfloat vertsCarPos[] = { -0.8,  0.8, 0.0f,   // left top in window space
441                               0.8,  0.8, 0.0f,   // right top
442                              -0.8, -0.8, 0.0f,   // left bottom
443                               0.8, -0.8, 0.0f    // right bottom
444    };
445#endif
446    // NOTE:  We didn't flip the image in the texture, so V=0 is actually the top of the image
447    GLfloat vertsCarTex[] = { 0.0f, 0.0f,   // left top
448                              1.0f, 0.0f,   // right top
449                              0.0f, 1.0f,   // left bottom
450                              1.0f, 1.0f    // right bottom
451    };
452    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
453    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
454    glEnableVertexAttribArray(0);
455    glEnableVertexAttribArray(1);
456
457    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
458
459
460    // Clean up and flip the rendered result to the front so it is visible
461    glDisableVertexAttribArray(0);
462    glDisableVertexAttribArray(1);
463
464    glFinish();
465
466    eglSwapBuffers(mDisplay, mSurface);
467}
468
469