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
26 namespace android {
27 // -----------------------------------------------------------------------------------------------
28
29
30 /*
31 * A simple formatter class to automatically add the endl and
32 * manage the indentation.
33 */
34
35 class Formatter;
36 static Formatter& indent(Formatter& f);
37 static Formatter& dedent(Formatter& f);
38
39 class Formatter {
40 String8 mString;
41 int mIndent;
42 typedef Formatter& (*FormaterManipFunc)(Formatter&);
43 friend Formatter& indent(Formatter& f);
44 friend Formatter& dedent(Formatter& f);
45 public:
Formatter()46 Formatter() : mIndent(0) {}
47
getString() const48 String8 getString() const {
49 return mString;
50 }
51
operator <<(Formatter & out,const char * in)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 }
operator <<(Formatter & out,const String8 & in)60 friend inline Formatter& operator << (Formatter& out, const String8& in) {
61 return operator << (out, in.string());
62 }
operator <<(Formatter & to,FormaterManipFunc func)63 friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) {
64 return (*func)(to);
65 }
66 };
indent(Formatter & f)67 Formatter& indent(Formatter& f) {
68 f.mIndent++;
69 return f;
70 }
dedent(Formatter & f)71 Formatter& dedent(Formatter& f) {
72 f.mIndent--;
73 return f;
74 }
75
76 // -----------------------------------------------------------------------------------------------
77
ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache)78 ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache)
79
80
81 ProgramCache::ProgramCache() {
82 }
83
~ProgramCache()84 ProgramCache::~ProgramCache() {
85 }
86
computeKey(const Description & description)87 ProgramCache::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
generateVertexShader(const Key & needs)105 String8 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
generateFragmentShader(const Key & needs)123 String8 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
generateProgram(const Key & needs)184 Program* 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
useProgram(const Description & description)195 void 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