Home | History | Annotate | Download | only in RenderEngine
      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