| /* | |
| * Copyright 2013 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. | |
| */ | |
| #define LOG_TAG "ScreenRecord" | |
| //#define LOG_NDEBUG 0 | |
| #include <utils/Log.h> | |
| #include "Program.h" | |
| #include <GLES2/gl2.h> | |
| #include <GLES2/gl2ext.h> | |
| #include <assert.h> | |
| using namespace android; | |
| // 4x4 identity matrix | |
| const float Program::kIdentity[] = { | |
| 1.0f, 0.0f, 0.0f, 0.0f, | |
| 0.0f, 1.0f, 0.0f, 0.0f, | |
| 0.0f, 0.0f, 1.0f, 0.0f, | |
| 0.0f, 0.0f, 0.0f, 1.0f | |
| }; | |
| // Simple vertex shader. Texture coord calc includes matrix for GLConsumer | |
| // transform. | |
| static const char* kVertexShader = | |
| "uniform mat4 uMVPMatrix;\n" | |
| "uniform mat4 uGLCMatrix;\n" | |
| "attribute vec4 aPosition;\n" | |
| "attribute vec4 aTextureCoord;\n" | |
| "varying vec2 vTextureCoord;\n" | |
| "void main() {\n" | |
| " gl_Position = uMVPMatrix * aPosition;\n" | |
| " vTextureCoord = (uGLCMatrix * aTextureCoord).xy;\n" | |
| "}\n"; | |
| // Trivial fragment shader for external texture. | |
| static const char* kExtFragmentShader = | |
| "#extension GL_OES_EGL_image_external : require\n" | |
| "precision mediump float;\n" | |
| "varying vec2 vTextureCoord;\n" | |
| "uniform samplerExternalOES uTexture;\n" | |
| "void main() {\n" | |
| " gl_FragColor = texture2D(uTexture, vTextureCoord);\n" | |
| "}\n"; | |
| // Trivial fragment shader for mundane texture. | |
| static const char* kFragmentShader = | |
| "precision mediump float;\n" | |
| "varying vec2 vTextureCoord;\n" | |
| "uniform sampler2D uTexture;\n" | |
| "void main() {\n" | |
| " gl_FragColor = texture2D(uTexture, vTextureCoord);\n" | |
| //" gl_FragColor = vec4(0.2, 1.0, 0.2, 1.0);\n" | |
| "}\n"; | |
| status_t Program::setup(ProgramType type) { | |
| ALOGV("Program::setup type=%d", type); | |
| status_t err; | |
| mProgramType = type; | |
| GLuint program; | |
| if (type == PROGRAM_TEXTURE_2D) { | |
| err = createProgram(&program, kVertexShader, kFragmentShader); | |
| } else { | |
| err = createProgram(&program, kVertexShader, kExtFragmentShader); | |
| } | |
| if (err != NO_ERROR) { | |
| return err; | |
| } | |
| assert(program != 0); | |
| maPositionLoc = glGetAttribLocation(program, "aPosition"); | |
| maTextureCoordLoc = glGetAttribLocation(program, "aTextureCoord"); | |
| muMVPMatrixLoc = glGetUniformLocation(program, "uMVPMatrix"); | |
| muGLCMatrixLoc = glGetUniformLocation(program, "uGLCMatrix"); | |
| muTextureLoc = glGetUniformLocation(program, "uTexture"); | |
| if ((maPositionLoc | maTextureCoordLoc | muMVPMatrixLoc | | |
| muGLCMatrixLoc | muTextureLoc) == -1) { | |
| ALOGE("Attrib/uniform lookup failed: %#x", glGetError()); | |
| glDeleteProgram(program); | |
| return UNKNOWN_ERROR; | |
| } | |
| mProgram = program; | |
| return NO_ERROR; | |
| } | |
| void Program::release() { | |
| ALOGV("Program::release"); | |
| if (mProgram != 0) { | |
| glDeleteProgram(mProgram); | |
| mProgram = 0; | |
| } | |
| } | |
| status_t Program::createProgram(GLuint* outPgm, const char* vertexShader, | |
| const char* fragmentShader) { | |
| GLuint vs, fs; | |
| status_t err; | |
| err = compileShader(GL_VERTEX_SHADER, vertexShader, &vs); | |
| if (err != NO_ERROR) { | |
| return err; | |
| } | |
| err = compileShader(GL_FRAGMENT_SHADER, fragmentShader, &fs); | |
| if (err != NO_ERROR) { | |
| glDeleteShader(vs); | |
| return err; | |
| } | |
| GLuint program; | |
| err = linkShaderProgram(vs, fs, &program); | |
| glDeleteShader(vs); | |
| glDeleteShader(fs); | |
| if (err == NO_ERROR) { | |
| *outPgm = program; | |
| } | |
| return err; | |
| } | |
| status_t Program::compileShader(GLenum shaderType, const char* src, | |
| GLuint* outShader) { | |
| GLuint shader = glCreateShader(shaderType); | |
| if (shader == 0) { | |
| ALOGE("glCreateShader error: %#x", glGetError()); | |
| return UNKNOWN_ERROR; | |
| } | |
| glShaderSource(shader, 1, &src, NULL); | |
| glCompileShader(shader); | |
| GLint compiled = 0; | |
| glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); | |
| if (!compiled) { | |
| ALOGE("Compile of shader type %d failed", shaderType); | |
| GLint infoLen = 0; | |
| glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); | |
| if (infoLen) { | |
| char* buf = new char[infoLen]; | |
| if (buf) { | |
| glGetShaderInfoLog(shader, infoLen, NULL, buf); | |
| ALOGE("Compile log: %s", buf); | |
| delete[] buf; | |
| } | |
| } | |
| glDeleteShader(shader); | |
| return UNKNOWN_ERROR; | |
| } | |
| *outShader = shader; | |
| return NO_ERROR; | |
| } | |
| status_t Program::linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm) { | |
| GLuint program = glCreateProgram(); | |
| if (program == 0) { | |
| ALOGE("glCreateProgram error: %#x", glGetError()); | |
| return UNKNOWN_ERROR; | |
| } | |
| glAttachShader(program, vs); | |
| glAttachShader(program, fs); | |
| glLinkProgram(program); | |
| GLint linkStatus = GL_FALSE; | |
| glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); | |
| if (linkStatus != GL_TRUE) { | |
| ALOGE("glLinkProgram failed"); | |
| GLint bufLength = 0; | |
| glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); | |
| if (bufLength) { | |
| char* buf = new char[bufLength]; | |
| if (buf) { | |
| glGetProgramInfoLog(program, bufLength, NULL, buf); | |
| ALOGE("Link log: %s", buf); | |
| delete[] buf; | |
| } | |
| } | |
| glDeleteProgram(program); | |
| return UNKNOWN_ERROR; | |
| } | |
| *outPgm = program; | |
| return NO_ERROR; | |
| } | |
| status_t Program::blit(GLuint texName, const float* texMatrix, | |
| int32_t x, int32_t y, int32_t w, int32_t h, bool invert) const { | |
| ALOGV("Program::blit %d xy=%d,%d wh=%d,%d", texName, x, y, w, h); | |
| const float pos[] = { | |
| float(x), float(y+h), | |
| float(x+w), float(y+h), | |
| float(x), float(y), | |
| float(x+w), float(y), | |
| }; | |
| const float uv[] = { | |
| 0.0f, 0.0f, | |
| 1.0f, 0.0f, | |
| 0.0f, 1.0f, | |
| 1.0f, 1.0f, | |
| }; | |
| status_t err; | |
| err = beforeDraw(texName, texMatrix, pos, uv, invert); | |
| if (err == NO_ERROR) { | |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
| err = afterDraw(); | |
| } | |
| return err; | |
| } | |
| status_t Program::drawTriangles(GLuint texName, const float* texMatrix, | |
| const float* vertices, const float* texes, size_t count) const { | |
| ALOGV("Program::drawTriangles texName=%d", texName); | |
| status_t err; | |
| err = beforeDraw(texName, texMatrix, vertices, texes, false); | |
| if (err == NO_ERROR) { | |
| glDrawArrays(GL_TRIANGLES, 0, count); | |
| err = afterDraw(); | |
| } | |
| return err; | |
| } | |
| status_t Program::beforeDraw(GLuint texName, const float* texMatrix, | |
| const float* vertices, const float* texes, bool invert) const { | |
| // Create an orthographic projection matrix based on viewport size. | |
| GLint vp[4]; | |
| glGetIntegerv(GL_VIEWPORT, vp); | |
| float screenToNdc[16] = { | |
| 2.0f/float(vp[2]), 0.0f, 0.0f, 0.0f, | |
| 0.0f, -2.0f/float(vp[3]), 0.0f, 0.0f, | |
| 0.0f, 0.0f, 1.0f, 0.0f, | |
| -1.0f, 1.0f, 0.0f, 1.0f, | |
| }; | |
| if (invert) { | |
| screenToNdc[5] = -screenToNdc[5]; | |
| screenToNdc[13] = -screenToNdc[13]; | |
| } | |
| glUseProgram(mProgram); | |
| glVertexAttribPointer(maPositionLoc, 2, GL_FLOAT, GL_FALSE, 0, vertices); | |
| glVertexAttribPointer(maTextureCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, texes); | |
| glEnableVertexAttribArray(maPositionLoc); | |
| glEnableVertexAttribArray(maTextureCoordLoc); | |
| glUniformMatrix4fv(muMVPMatrixLoc, 1, GL_FALSE, screenToNdc); | |
| glUniformMatrix4fv(muGLCMatrixLoc, 1, GL_FALSE, texMatrix); | |
| glActiveTexture(GL_TEXTURE0); | |
| switch (mProgramType) { | |
| case PROGRAM_EXTERNAL_TEXTURE: | |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, texName); | |
| break; | |
| case PROGRAM_TEXTURE_2D: | |
| glBindTexture(GL_TEXTURE_2D, texName); | |
| break; | |
| default: | |
| ALOGE("unexpected program type %d", mProgramType); | |
| return UNKNOWN_ERROR; | |
| } | |
| glUniform1i(muTextureLoc, 0); | |
| GLenum glErr; | |
| if ((glErr = glGetError()) != GL_NO_ERROR) { | |
| ALOGE("GL error before draw: %#x", glErr); | |
| glDisableVertexAttribArray(maPositionLoc); | |
| glDisableVertexAttribArray(maTextureCoordLoc); | |
| return UNKNOWN_ERROR; | |
| } | |
| return NO_ERROR; | |
| } | |
| status_t Program::afterDraw() const { | |
| glDisableVertexAttribArray(maPositionLoc); | |
| glDisableVertexAttribArray(maTextureCoordLoc); | |
| GLenum glErr; | |
| if ((glErr = glGetError()) != GL_NO_ERROR) { | |
| ALOGE("GL error after draw: %#x", glErr); | |
| return UNKNOWN_ERROR; | |
| } | |
| return NO_ERROR; | |
| } |