1/*
2 * Copyright 2013 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#define LOG_TAG "ScreenRecord"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include "Program.h"
22
23#include <GLES2/gl2.h>
24#include <GLES2/gl2ext.h>
25
26#include <assert.h>
27
28using namespace android;
29
30// 4x4 identity matrix
31const float Program::kIdentity[] = {
32        1.0f, 0.0f, 0.0f, 0.0f,
33        0.0f, 1.0f, 0.0f, 0.0f,
34        0.0f, 0.0f, 1.0f, 0.0f,
35        0.0f, 0.0f, 0.0f, 1.0f
36};
37
38// Simple vertex shader.  Texture coord calc includes matrix for GLConsumer
39// transform.
40static const char* kVertexShader =
41        "uniform mat4 uMVPMatrix;\n"
42        "uniform mat4 uGLCMatrix;\n"
43        "attribute vec4 aPosition;\n"
44        "attribute vec4 aTextureCoord;\n"
45        "varying vec2 vTextureCoord;\n"
46        "void main() {\n"
47        "    gl_Position = uMVPMatrix * aPosition;\n"
48        "    vTextureCoord = (uGLCMatrix * aTextureCoord).xy;\n"
49        "}\n";
50
51// Trivial fragment shader for external texture.
52static const char* kExtFragmentShader =
53        "#extension GL_OES_EGL_image_external : require\n"
54        "precision mediump float;\n"
55        "varying vec2 vTextureCoord;\n"
56        "uniform samplerExternalOES uTexture;\n"
57        "void main() {\n"
58        "    gl_FragColor = texture2D(uTexture, vTextureCoord);\n"
59        "}\n";
60
61// Trivial fragment shader for mundane texture.
62static const char* kFragmentShader =
63        "precision mediump float;\n"
64        "varying vec2 vTextureCoord;\n"
65        "uniform sampler2D uTexture;\n"
66        "void main() {\n"
67        "    gl_FragColor = texture2D(uTexture, vTextureCoord);\n"
68        //"    gl_FragColor = vec4(0.2, 1.0, 0.2, 1.0);\n"
69        "}\n";
70
71status_t Program::setup(ProgramType type) {
72    ALOGV("Program::setup type=%d", type);
73    status_t err;
74
75    mProgramType = type;
76
77    GLuint program;
78    if (type == PROGRAM_TEXTURE_2D) {
79        err = createProgram(&program, kVertexShader, kFragmentShader);
80    } else {
81        err = createProgram(&program, kVertexShader, kExtFragmentShader);
82    }
83    if (err != NO_ERROR) {
84        return err;
85    }
86    assert(program != 0);
87
88    maPositionLoc = glGetAttribLocation(program, "aPosition");
89    maTextureCoordLoc = glGetAttribLocation(program, "aTextureCoord");
90    muMVPMatrixLoc = glGetUniformLocation(program, "uMVPMatrix");
91    muGLCMatrixLoc = glGetUniformLocation(program, "uGLCMatrix");
92    muTextureLoc = glGetUniformLocation(program, "uTexture");
93    if ((maPositionLoc | maTextureCoordLoc | muMVPMatrixLoc |
94            muGLCMatrixLoc | muTextureLoc) == -1) {
95        ALOGE("Attrib/uniform lookup failed: %#x", glGetError());
96        glDeleteProgram(program);
97        return UNKNOWN_ERROR;
98    }
99
100    mProgram = program;
101    return NO_ERROR;
102}
103
104void Program::release() {
105    ALOGV("Program::release");
106    if (mProgram != 0) {
107        glDeleteProgram(mProgram);
108        mProgram = 0;
109    }
110}
111
112status_t Program::createProgram(GLuint* outPgm, const char* vertexShader,
113        const char* fragmentShader) {
114    GLuint vs, fs;
115    status_t err;
116
117    err = compileShader(GL_VERTEX_SHADER, vertexShader, &vs);
118    if (err != NO_ERROR) {
119        return err;
120    }
121    err = compileShader(GL_FRAGMENT_SHADER, fragmentShader, &fs);
122    if (err != NO_ERROR) {
123        glDeleteShader(vs);
124        return err;
125    }
126
127    GLuint program;
128    err = linkShaderProgram(vs, fs, &program);
129    glDeleteShader(vs);
130    glDeleteShader(fs);
131    if (err == NO_ERROR) {
132        *outPgm = program;
133    }
134    return err;
135}
136
137status_t Program::compileShader(GLenum shaderType, const char* src,
138        GLuint* outShader) {
139    GLuint shader = glCreateShader(shaderType);
140    if (shader == 0) {
141        ALOGE("glCreateShader error: %#x", glGetError());
142        return UNKNOWN_ERROR;
143    }
144
145    glShaderSource(shader, 1, &src, NULL);
146    glCompileShader(shader);
147
148    GLint compiled = 0;
149    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
150    if (!compiled) {
151        ALOGE("Compile of shader type %d failed", shaderType);
152        GLint infoLen = 0;
153        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
154        if (infoLen) {
155            char* buf = new char[infoLen];
156            if (buf) {
157                glGetShaderInfoLog(shader, infoLen, NULL, buf);
158                ALOGE("Compile log: %s", buf);
159                delete[] buf;
160            }
161        }
162        glDeleteShader(shader);
163        return UNKNOWN_ERROR;
164    }
165    *outShader = shader;
166    return NO_ERROR;
167}
168
169status_t Program::linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm) {
170    GLuint program = glCreateProgram();
171    if (program == 0) {
172        ALOGE("glCreateProgram error: %#x", glGetError());
173        return UNKNOWN_ERROR;
174    }
175
176    glAttachShader(program, vs);
177    glAttachShader(program, fs);
178    glLinkProgram(program);
179    GLint linkStatus = GL_FALSE;
180    glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
181    if (linkStatus != GL_TRUE) {
182        ALOGE("glLinkProgram failed");
183        GLint bufLength = 0;
184        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
185        if (bufLength) {
186            char* buf = new char[bufLength];
187            if (buf) {
188                glGetProgramInfoLog(program, bufLength, NULL, buf);
189                ALOGE("Link log: %s", buf);
190                delete[] buf;
191            }
192        }
193        glDeleteProgram(program);
194        return UNKNOWN_ERROR;
195    }
196
197    *outPgm = program;
198    return NO_ERROR;
199}
200
201
202
203status_t Program::blit(GLuint texName, const float* texMatrix,
204        int32_t x, int32_t y, int32_t w, int32_t h, bool invert) const {
205    ALOGV("Program::blit %d xy=%d,%d wh=%d,%d", texName, x, y, w, h);
206
207    const float pos[] = {
208        float(x),   float(y+h),
209        float(x+w), float(y+h),
210        float(x),   float(y),
211        float(x+w), float(y),
212    };
213    const float uv[] = {
214        0.0f, 0.0f,
215        1.0f, 0.0f,
216        0.0f, 1.0f,
217        1.0f, 1.0f,
218    };
219    status_t err;
220
221    err = beforeDraw(texName, texMatrix, pos, uv, invert);
222    if (err == NO_ERROR) {
223        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
224        err = afterDraw();
225    }
226    return err;
227}
228
229status_t Program::drawTriangles(GLuint texName, const float* texMatrix,
230        const float* vertices, const float* texes, size_t count) const {
231    ALOGV("Program::drawTriangles texName=%d", texName);
232
233    status_t err;
234
235    err = beforeDraw(texName, texMatrix, vertices, texes, false);
236    if (err == NO_ERROR) {
237        glDrawArrays(GL_TRIANGLES, 0, count);
238        err = afterDraw();
239    }
240    return err;
241}
242
243status_t Program::beforeDraw(GLuint texName, const float* texMatrix,
244        const float* vertices, const float* texes, bool invert) const {
245    // Create an orthographic projection matrix based on viewport size.
246    GLint vp[4];
247    glGetIntegerv(GL_VIEWPORT, vp);
248    float screenToNdc[16] = {
249        2.0f/float(vp[2]),  0.0f,               0.0f,   0.0f,
250        0.0f,               -2.0f/float(vp[3]), 0.0f,   0.0f,
251        0.0f,               0.0f,               1.0f,   0.0f,
252        -1.0f,              1.0f,               0.0f,   1.0f,
253    };
254    if (invert) {
255        screenToNdc[5] = -screenToNdc[5];
256        screenToNdc[13] = -screenToNdc[13];
257    }
258
259    glUseProgram(mProgram);
260
261    glVertexAttribPointer(maPositionLoc, 2, GL_FLOAT, GL_FALSE, 0, vertices);
262    glVertexAttribPointer(maTextureCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, texes);
263    glEnableVertexAttribArray(maPositionLoc);
264    glEnableVertexAttribArray(maTextureCoordLoc);
265
266    glUniformMatrix4fv(muMVPMatrixLoc, 1, GL_FALSE, screenToNdc);
267    glUniformMatrix4fv(muGLCMatrixLoc, 1, GL_FALSE, texMatrix);
268
269    glActiveTexture(GL_TEXTURE0);
270
271    switch (mProgramType) {
272    case PROGRAM_EXTERNAL_TEXTURE:
273        glBindTexture(GL_TEXTURE_EXTERNAL_OES, texName);
274        break;
275    case PROGRAM_TEXTURE_2D:
276        glBindTexture(GL_TEXTURE_2D, texName);
277        break;
278    default:
279        ALOGE("unexpected program type %d", mProgramType);
280        return UNKNOWN_ERROR;
281    }
282
283    glUniform1i(muTextureLoc, 0);
284
285    GLenum glErr;
286    if ((glErr = glGetError()) != GL_NO_ERROR) {
287        ALOGE("GL error before draw: %#x", glErr);
288        glDisableVertexAttribArray(maPositionLoc);
289        glDisableVertexAttribArray(maTextureCoordLoc);
290        return UNKNOWN_ERROR;
291    }
292
293    return NO_ERROR;
294}
295
296status_t Program::afterDraw() const {
297    glDisableVertexAttribArray(maPositionLoc);
298    glDisableVertexAttribArray(maTextureCoordLoc);
299
300    GLenum glErr;
301    if ((glErr = glGetError()) != GL_NO_ERROR) {
302        ALOGE("GL error after draw: %#x", glErr);
303        return UNKNOWN_ERROR;
304    }
305
306    return NO_ERROR;
307}
308