summaryrefslogtreecommitdiffstats
path: root/services/surfaceflinger/RenderEngine/ProgramCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/surfaceflinger/RenderEngine/ProgramCache.cpp')
-rw-r--r--services/surfaceflinger/RenderEngine/ProgramCache.cpp206
1 files changed, 206 insertions, 0 deletions
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 */