ProgramCache.cpp revision ff2ed70fa30f04b90dd1a2c06ec2319e157152d7
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#include <GLES2/gl2.h>
18#include <GLES2/gl2ext.h>
19
20#include <utils/String8.h>
21
22#include "ProgramCache.h"
23#include "Program.h"
24#include "Description.h"
25
26namespace android {
27// -----------------------------------------------------------------------------------------------
28
29
30/*
31 * A simple formatter class to automatically add the endl and
32 * manage the indentation.
33 */
34
35class Formatter;
36static Formatter& indent(Formatter& f);
37static Formatter& dedent(Formatter& f);
38
39class Formatter {
40    String8 mString;
41    int mIndent;
42    typedef Formatter& (*FormaterManipFunc)(Formatter&);
43    friend Formatter& indent(Formatter& f);
44    friend Formatter& dedent(Formatter& f);
45public:
46    Formatter() : mIndent(0) {}
47
48    String8 getString() const {
49        return mString;
50    }
51
52    friend Formatter& operator << (Formatter& out, const char* in) {
53        for (int i=0 ; i<out.mIndent ; i++) {
54            out.mString.append("    ");
55        }
56        out.mString.append(in);
57        out.mString.append("\n");
58        return out;
59    }
60    friend inline Formatter& operator << (Formatter& out, const String8& in) {
61        return operator << (out, in.string());
62    }
63    friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) {
64        return (*func)(to);
65    }
66};
67Formatter& indent(Formatter& f) {
68    f.mIndent++;
69    return f;
70}
71Formatter& dedent(Formatter& f) {
72    f.mIndent--;
73    return f;
74}
75
76// -----------------------------------------------------------------------------------------------
77
78ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache)
79
80
81ProgramCache::ProgramCache() {
82}
83
84ProgramCache::~ProgramCache() {
85}
86
87ProgramCache::Key ProgramCache::computeKey(const Description& description) {
88    Key needs;
89    needs.set(Key::TEXTURE_MASK,
90            !description.mTextureEnabled ? Key::TEXTURE_OFF :
91            description.mTexture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES ? Key::TEXTURE_EXT :
92            description.mTexture.getTextureTarget() == GL_TEXTURE_2D           ? Key::TEXTURE_2D :
93            Key::TEXTURE_OFF)
94    .set(Key::PLANE_ALPHA_MASK,
95            (description.mPlaneAlpha < 1) ? Key::PLANE_ALPHA_LT_ONE : Key::PLANE_ALPHA_EQ_ONE)
96    .set(Key::BLEND_MASK,
97            description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
98    .set(Key::OPACITY_MASK,
99            description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
100    .set(Key::COLOR_MATRIX_MASK,
101            description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON :  Key::COLOR_MATRIX_OFF);
102    return needs;
103}
104
105String8 ProgramCache::generateVertexShader(const Key& needs) {
106    Formatter vs;
107    if (needs.isTexturing()) {
108        vs  << "attribute vec4 texCoords;"
109            << "varying vec2 outTexCoords;";
110    }
111    vs << "attribute vec4 position;"
112       << "uniform mat4 projection;"
113       << "uniform mat4 texture;"
114       << "void main(void) {" << indent
115       << "gl_Position = projection * position;";
116    if (needs.isTexturing()) {
117        vs << "outTexCoords = (texture * texCoords).st;";
118    }
119    vs << dedent << "}";
120    return vs.getString();
121}
122
123String8 ProgramCache::generateFragmentShader(const Key& needs) {
124    Formatter fs;
125    if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
126        fs << "#extension GL_OES_EGL_image_external : require";
127    }
128
129    // default precision is required-ish in fragment shaders
130    fs << "precision mediump float;";
131
132    if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
133        fs << "uniform samplerExternalOES sampler;"
134           << "varying vec2 outTexCoords;";
135    } else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
136        fs << "uniform sampler2D sampler;"
137           << "varying vec2 outTexCoords;";
138    } else if (needs.getTextureTarget() == Key::TEXTURE_OFF) {
139        fs << "uniform vec4 color;";
140    }
141    if (needs.hasPlaneAlpha()) {
142        fs << "uniform float alphaPlane;";
143    }
144    if (needs.hasColorMatrix()) {
145        fs << "uniform mat4 colorMatrix;";
146    }
147    fs << "void main(void) {" << indent;
148    if (needs.isTexturing()) {
149        fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
150    } else {
151        fs << "gl_FragColor = color;";
152    }
153    if (needs.isOpaque()) {
154        fs << "gl_FragColor.a = 1.0;";
155    }
156    if (needs.hasPlaneAlpha()) {
157        // modulate the alpha value with planeAlpha
158        if (needs.isPremultiplied()) {
159            // ... and the color too if we're premultiplied
160            fs << "gl_FragColor *= alphaPlane;";
161        } else {
162            fs << "gl_FragColor.a *= alphaPlane;";
163        }
164    }
165
166    if (needs.hasColorMatrix()) {
167        if (!needs.isOpaque() && needs.isPremultiplied()) {
168            // un-premultiply if needed before linearization
169            fs << "gl_FragColor.rgb = gl_FragColor.rgb/gl_FragColor.a;";
170        }
171        fs << "gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(2.2));";
172        fs << "gl_FragColor     = colorMatrix*gl_FragColor;";
173        fs << "gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0 / 2.2));";
174        if (!needs.isOpaque() && needs.isPremultiplied()) {
175            // and re-premultiply if needed after gamma correction
176            fs << "gl_FragColor.rgb = gl_FragColor.rgb*gl_FragColor.a;";
177        }
178    }
179
180    fs << dedent << "}";
181    return fs.getString();
182}
183
184Program* ProgramCache::generateProgram(const Key& needs) {
185    // vertex shader
186    String8 vs = generateVertexShader(needs);
187
188    // fragment shader
189    String8 fs = generateFragmentShader(needs);
190
191    Program* program = new Program(needs, vs.string(), fs.string());
192    return program;
193}
194
195void ProgramCache::useProgram(const Description& description) {
196
197    // generate the key for the shader based on the description
198    Key needs(computeKey(description));
199
200     // look-up the program in the cache
201    Program* program = mCache.valueFor(needs);
202    if (program == NULL) {
203        // we didn't find our program, so generate one...
204        nsecs_t time = -systemTime();
205        program = generateProgram(needs);
206        mCache.add(needs, program);
207        time += systemTime();
208
209        //ALOGD(">>> generated new program: needs=%08X, time=%u ms (%d programs)",
210        //        needs.mNeeds, uint32_t(ns2ms(time)), mCache.size());
211    }
212
213    // here we have a suitable program for this description
214    if (program->isValid()) {
215        program->use();
216        program->setUniforms(description);
217    }
218}
219
220
221} /* namespace android */
222