/* * 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; }