1/*Gluint
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#include <stdint.h>
18
19#include <log/log.h>
20#include <utils/String8.h>
21
22#include <math/mat4.h>
23#include "Description.h"
24#include "Program.h"
25#include "ProgramCache.h"
26
27namespace android {
28
29Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment)
30      : mInitialized(false) {
31    GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER);
32    GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER);
33    GLuint programId = glCreateProgram();
34    glAttachShader(programId, vertexId);
35    glAttachShader(programId, fragmentId);
36    glBindAttribLocation(programId, position, "position");
37    glBindAttribLocation(programId, texCoords, "texCoords");
38    glLinkProgram(programId);
39
40    GLint status;
41    glGetProgramiv(programId, GL_LINK_STATUS, &status);
42    if (status != GL_TRUE) {
43        ALOGE("Error while linking shaders:");
44        GLint infoLen = 0;
45        glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen);
46        if (infoLen > 1) {
47            GLchar log[infoLen];
48            glGetProgramInfoLog(programId, infoLen, 0, &log[0]);
49            ALOGE("%s", log);
50        }
51        glDetachShader(programId, vertexId);
52        glDetachShader(programId, fragmentId);
53        glDeleteShader(vertexId);
54        glDeleteShader(fragmentId);
55        glDeleteProgram(programId);
56    } else {
57        mProgram = programId;
58        mVertexShader = vertexId;
59        mFragmentShader = fragmentId;
60        mInitialized = true;
61        mProjectionMatrixLoc = glGetUniformLocation(programId, "projection");
62        mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
63        mSamplerLoc = glGetUniformLocation(programId, "sampler");
64        mColorLoc = glGetUniformLocation(programId, "color");
65        mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
66        mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
67        mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
68
69        // set-up the default values for our uniforms
70        glUseProgram(programId);
71        glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, mat4().asArray());
72        glEnableVertexAttribArray(0);
73    }
74}
75
76Program::~Program() {}
77
78bool Program::isValid() const {
79    return mInitialized;
80}
81
82void Program::use() {
83    glUseProgram(mProgram);
84}
85
86GLuint Program::getAttrib(const char* name) const {
87    // TODO: maybe use a local cache
88    return glGetAttribLocation(mProgram, name);
89}
90
91GLint Program::getUniform(const char* name) const {
92    // TODO: maybe use a local cache
93    return glGetUniformLocation(mProgram, name);
94}
95
96GLuint Program::buildShader(const char* source, GLenum type) {
97    GLuint shader = glCreateShader(type);
98    glShaderSource(shader, 1, &source, 0);
99    glCompileShader(shader);
100    GLint status;
101    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
102    if (status != GL_TRUE) {
103        // Some drivers return wrong values for GL_INFO_LOG_LENGTH
104        // use a fixed size instead
105        GLchar log[512];
106        glGetShaderInfoLog(shader, sizeof(log), 0, log);
107        ALOGE("Error while compiling shader: \n%s\n%s", source, log);
108        glDeleteShader(shader);
109        return 0;
110    }
111    return shader;
112}
113
114String8& Program::dumpShader(String8& result, GLenum /*type*/) {
115    GLuint shader = GL_FRAGMENT_SHADER ? mFragmentShader : mVertexShader;
116    GLint l;
117    glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &l);
118    char* src = new char[l];
119    glGetShaderSource(shader, l, nullptr, src);
120    result.append(src);
121    delete[] src;
122    return result;
123}
124
125void Program::setUniforms(const Description& desc) {
126    // TODO: we should have a mechanism here to not always reset uniforms that
127    // didn't change for this program.
128
129    if (mSamplerLoc >= 0) {
130        glUniform1i(mSamplerLoc, 0);
131        glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.mTexture.getMatrix().asArray());
132    }
133    if (mColorLoc >= 0) {
134        const float color[4] = {desc.mColor.r, desc.mColor.g, desc.mColor.b, desc.mColor.a};
135        glUniform4fv(mColorLoc, 1, color);
136    }
137    if (mInputTransformMatrixLoc >= 0) {
138        // If the input transform matrix is not identity matrix, we want to merge
139        // the saturation matrix with input transform matrix so that the saturation
140        // matrix is applied at the correct stage.
141        mat4 inputTransformMatrix = mat4(desc.mInputTransformMatrix) * desc.mSaturationMatrix;
142        glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
143    }
144    if (mOutputTransformMatrixLoc >= 0) {
145        // The output transform matrix and color matrix can be combined as one matrix
146        // that is applied right before applying OETF.
147        mat4 outputTransformMatrix = desc.mColorMatrix * desc.mOutputTransformMatrix;
148        // If there is no input transform matrix, we want to merge the saturation
149        // matrix with output transform matrix to avoid extra matrix multiplication
150        // in shader.
151        if (mInputTransformMatrixLoc < 0) {
152            outputTransformMatrix *= desc.mSaturationMatrix;
153        }
154        glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE,
155                           outputTransformMatrix.asArray());
156    }
157    if (mDisplayMaxLuminanceLoc >= 0) {
158        glUniform1f(mDisplayMaxLuminanceLoc, desc.mDisplayMaxLuminance);
159    }
160    // these uniforms are always present
161    glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.mProjectionMatrix.asArray());
162}
163
164} /* namespace android */
165