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: 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 }; 67 Formatter& indent(Formatter& f) { 68 f.mIndent++; 69 return f; 70 } 71 Formatter& dedent(Formatter& f) { 72 f.mIndent--; 73 return f; 74 } 75 76 // ----------------------------------------------------------------------------------------------- 77 78 ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache) 79 80 ProgramCache::ProgramCache() { 81 // Until surfaceflinger has a dependable blob cache on the filesystem, 82 // generate shaders on initialization so as to avoid jank. 83 primeCache(); 84 } 85 86 ProgramCache::~ProgramCache() { 87 } 88 89 void ProgramCache::primeCache() { 90 uint32_t shaderCount = 0; 91 uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | 92 Key::PLANE_ALPHA_MASK | Key::TEXTURE_MASK; 93 // Prime the cache for all combinations of the above masks, 94 // leaving off the experimental color matrix mask options. 95 96 nsecs_t timeBefore = systemTime(); 97 for (uint32_t keyVal = 0; keyVal <= keyMask; keyVal++) { 98 Key shaderKey; 99 shaderKey.set(keyMask, keyVal); 100 uint32_t tex = shaderKey.getTextureTarget(); 101 if (tex != Key::TEXTURE_OFF && 102 tex != Key::TEXTURE_EXT && 103 tex != Key::TEXTURE_2D) { 104 continue; 105 } 106 Program* program = mCache.valueFor(shaderKey); 107 if (program == NULL) { 108 program = generateProgram(shaderKey); 109 mCache.add(shaderKey, program); 110 shaderCount++; 111 } 112 } 113 nsecs_t timeAfter = systemTime(); 114 float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; 115 ALOGD("shader cache generated - %u shaders in %f ms\n", shaderCount, compileTimeMs); 116 } 117 118 ProgramCache::Key ProgramCache::computeKey(const Description& description) { 119 Key needs; 120 needs.set(Key::TEXTURE_MASK, 121 !description.mTextureEnabled ? Key::TEXTURE_OFF : 122 description.mTexture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES ? Key::TEXTURE_EXT : 123 description.mTexture.getTextureTarget() == GL_TEXTURE_2D ? Key::TEXTURE_2D : 124 Key::TEXTURE_OFF) 125 .set(Key::PLANE_ALPHA_MASK, 126 (description.mPlaneAlpha < 1) ? Key::PLANE_ALPHA_LT_ONE : Key::PLANE_ALPHA_EQ_ONE) 127 .set(Key::BLEND_MASK, 128 description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) 129 .set(Key::OPACITY_MASK, 130 description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) 131 .set(Key::COLOR_MATRIX_MASK, 132 description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON : Key::COLOR_MATRIX_OFF); 133 return needs; 134 } 135 136 String8 ProgramCache::generateVertexShader(const Key& needs) { 137 Formatter vs; 138 if (needs.isTexturing()) { 139 vs << "attribute vec4 texCoords;" 140 << "varying vec2 outTexCoords;"; 141 } 142 vs << "attribute vec4 position;" 143 << "uniform mat4 projection;" 144 << "uniform mat4 texture;" 145 << "void main(void) {" << indent 146 << "gl_Position = projection * position;"; 147 if (needs.isTexturing()) { 148 vs << "outTexCoords = (texture * texCoords).st;"; 149 } 150 vs << dedent << "}"; 151 return vs.getString(); 152 } 153 154 String8 ProgramCache::generateFragmentShader(const Key& needs) { 155 Formatter fs; 156 if (needs.getTextureTarget() == Key::TEXTURE_EXT) { 157 fs << "#extension GL_OES_EGL_image_external : require"; 158 } 159 160 // default precision is required-ish in fragment shaders 161 fs << "precision mediump float;"; 162 163 if (needs.getTextureTarget() == Key::TEXTURE_EXT) { 164 fs << "uniform samplerExternalOES sampler;" 165 << "varying vec2 outTexCoords;"; 166 } else if (needs.getTextureTarget() == Key::TEXTURE_2D) { 167 fs << "uniform sampler2D sampler;" 168 << "varying vec2 outTexCoords;"; 169 } else if (needs.getTextureTarget() == Key::TEXTURE_OFF) { 170 fs << "uniform vec4 color;"; 171 } 172 if (needs.hasPlaneAlpha()) { 173 fs << "uniform float alphaPlane;"; 174 } 175 if (needs.hasColorMatrix()) { 176 fs << "uniform mat4 colorMatrix;"; 177 } 178 fs << "void main(void) {" << indent; 179 if (needs.isTexturing()) { 180 fs << "gl_FragColor = texture2D(sampler, outTexCoords);"; 181 } else { 182 fs << "gl_FragColor = color;"; 183 } 184 if (needs.isOpaque()) { 185 fs << "gl_FragColor.a = 1.0;"; 186 } 187 if (needs.hasPlaneAlpha()) { 188 // modulate the alpha value with planeAlpha 189 if (needs.isPremultiplied()) { 190 // ... and the color too if we're premultiplied 191 fs << "gl_FragColor *= alphaPlane;"; 192 } else { 193 fs << "gl_FragColor.a *= alphaPlane;"; 194 } 195 } 196 197 if (needs.hasColorMatrix()) { 198 if (!needs.isOpaque() && needs.isPremultiplied()) { 199 // un-premultiply if needed before linearization 200 fs << "gl_FragColor.rgb = gl_FragColor.rgb/gl_FragColor.a;"; 201 } 202 fs << "vec4 transformed = colorMatrix * vec4(gl_FragColor.rgb, 1);"; 203 fs << "gl_FragColor.rgb = transformed.rgb/transformed.a;"; 204 if (!needs.isOpaque() && needs.isPremultiplied()) { 205 // and re-premultiply if needed after gamma correction 206 fs << "gl_FragColor.rgb = gl_FragColor.rgb*gl_FragColor.a;"; 207 } 208 } 209 210 fs << dedent << "}"; 211 return fs.getString(); 212 } 213 214 Program* ProgramCache::generateProgram(const Key& needs) { 215 // vertex shader 216 String8 vs = generateVertexShader(needs); 217 218 // fragment shader 219 String8 fs = generateFragmentShader(needs); 220 221 Program* program = new Program(needs, vs.string(), fs.string()); 222 return program; 223 } 224 225 void ProgramCache::useProgram(const Description& description) { 226 227 // generate the key for the shader based on the description 228 Key needs(computeKey(description)); 229 230 // look-up the program in the cache 231 Program* program = mCache.valueFor(needs); 232 if (program == NULL) { 233 // we didn't find our program, so generate one... 234 nsecs_t time = -systemTime(); 235 program = generateProgram(needs); 236 mCache.add(needs, program); 237 time += systemTime(); 238 239 //ALOGD(">>> generated new program: needs=%08X, time=%u ms (%d programs)", 240 // needs.mNeeds, uint32_t(ns2ms(time)), mCache.size()); 241 } 242 243 // here we have a suitable program for this description 244 if (program->isValid()) { 245 program->use(); 246 program->setUniforms(description); 247 } 248 } 249 250 251 } /* namespace android */ 252