SurfaceFlinger now uses GLES 2.x when available

Bug: 8679321

Change-Id: I2b152d01fb4e2de2ea9fe87f1ddbd6826d7520d7
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
new file mode 100644
index 0000000..835ed8a
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/String8.h>
+
+#include "ProgramCache.h"
+#include "Program.h"
+#include "Description.h"
+
+namespace android {
+// -----------------------------------------------------------------------------------------------
+
+
+/*
+ * A simple formatter class to automatically add the endl and
+ * manage the indentation.
+ */
+
+class Formatter;
+static Formatter& indent(Formatter& f);
+static Formatter& dedent(Formatter& f);
+
+class Formatter {
+    String8 mString;
+    int mIndent;
+    typedef Formatter& (*FormaterManipFunc)(Formatter&);
+    friend Formatter& indent(Formatter& f);
+    friend Formatter& dedent(Formatter& f);
+public:
+    String8 getString() const {
+        return mString;
+    }
+
+    friend Formatter& operator << (Formatter& out, const char* in) {
+        for (int i=0 ; i<out.mIndent ; i++) {
+            out.mString.append("    ");
+        }
+        out.mString.append(in);
+        out.mString.append("\n");
+        return out;
+    }
+    friend inline Formatter& operator << (Formatter& out, const String8& in) {
+        return operator << (out, in.string());
+    }
+    friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) {
+        return (*func)(to);
+    }
+};
+Formatter& indent(Formatter& f) {
+    f.mIndent++;
+    return f;
+}
+Formatter& dedent(Formatter& f) {
+    f.mIndent--;
+    return f;
+}
+
+// -----------------------------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache)
+
+
+ProgramCache::ProgramCache() {
+}
+
+ProgramCache::~ProgramCache() {
+}
+
+ProgramCache::Key ProgramCache::computeKey(const Description& description) {
+    Key needs;
+    needs.set(Key::TEXTURE_MASK,
+            (description.mTextureTarget == GL_TEXTURE_EXTERNAL_OES) ? Key::TEXTURE_EXT :
+            (description.mTextureTarget == GL_TEXTURE_2D)           ? Key::TEXTURE_2D :
+            Key::TEXTURE_OFF)
+    .set(Key::PLANE_ALPHA_MASK,
+            (description.mPlaneAlpha < 1) ? Key::PLANE_ALPHA_LT_ONE : Key::PLANE_ALPHA_EQ_ONE)
+    .set(Key::BLEND_MASK,
+            description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
+    .set(Key::OPACITY_MASK,
+            description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT);
+    return needs;
+}
+
+String8 ProgramCache::generateVertexShader(const Key& needs) {
+    Formatter vs;
+    if (needs.isTexturing()) {
+        vs  << "attribute vec4 texCoords;"
+            << "varying vec2 outTexCoords;";
+    }
+    vs << "attribute vec4 position;"
+       << "uniform mat4 projection;"
+       << "uniform mat4 texture;"
+       << "void main(void) {" << indent
+       << "gl_Position = projection * position;";
+    if (needs.isTexturing()) {
+        vs << "outTexCoords = (texture * texCoords).st;";
+    }
+    vs << dedent << "}";
+    return vs.getString();
+}
+
+String8 ProgramCache::generateFragmentShader(const Key& needs) {
+    Formatter fs;
+    if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
+        fs << "#extension GL_OES_EGL_image_external : require";
+    }
+    if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
+        fs << "uniform samplerExternalOES sampler;"
+           << "varying vec2 outTexCoords;";
+    } else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
+        fs << "uniform sampler2D sampler;"
+           << "varying vec2 outTexCoords;";
+    } else if (needs.getTextureTarget() == Key::TEXTURE_OFF) {
+        fs << "uniform vec4 color;";
+    }
+    if (needs.hasPlaneAlpha()) {
+        fs << "uniform float alphaPlane;";
+    }
+    fs << "void main(void) {" << indent;
+    if (needs.isTexturing()) {
+        fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
+    } else {
+        fs << "gl_FragColor = color;";
+    }
+    if (needs.hasPlaneAlpha()) {
+        // modulate the alpha value with planeAlpha
+        if (needs.isPremultiplied()) {
+            // ... and the color too if we're premultiplied
+            if (needs.isOpaque()) {
+                // ... we're opaque, only premultiply the color component
+                fs << "gl_FragColor.rgb *= alphaPlane;"
+                   << "gl_FragColor.a = alphaPlane;";
+            } else {
+                fs << "gl_FragColor *= alphaPlane;";
+            }
+        } else {
+            // not premultiplied
+            if (needs.isOpaque()) {
+                fs << "gl_FragColor.a = alphaPlane;";
+            } else {
+                fs << "gl_FragColor.a *= alphaPlane;";
+            }
+        }
+    } else {
+        if (needs.isOpaque()) {
+            fs << "gl_FragColor.a = 1.0;";
+        }
+    }
+    fs << dedent << "}";
+    return fs.getString();
+}
+
+Program* ProgramCache::generateProgram(const Key& needs) {
+    // vertex shader
+    String8 vs = generateVertexShader(needs);
+
+    // fragment shader
+    String8 fs = generateFragmentShader(needs);
+
+    Program* program = new Program(needs, vs.string(), fs.string());
+    return program;
+}
+
+void ProgramCache::useProgram(const Description& description) {
+
+    // generate the key for the shader based on the description
+    Key needs(computeKey(description));
+
+     // look-up the program in the cache
+    Program* program = mCache.valueFor(needs);
+    if (program == NULL) {
+        // we didn't find our program, so generate one...
+        nsecs_t time = -systemTime();
+        program = generateProgram(needs);
+        mCache.add(needs, program);
+        time += systemTime();
+
+        //ALOGD(">>> generated new program: needs=%08X, time=%u ms (%d programs)",
+        //        needs.mNeeds, uint32_t(ns2ms(time)), mCache.size());
+    }
+
+    // here we have a suitable program for this description
+    if (program->isValid()) {
+        program->use();
+        program->setUniforms(description);
+    }
+}
+
+
+} /* namespace android */