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 81 ProgramCache::ProgramCache() { 82 } 83 84 ProgramCache::~ProgramCache() { 85 } 86 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 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 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 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 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