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