OSDN Git Service

Generate the SurfaceFlinger shader cache on initialization
[android-x86/frameworks-native.git] / services / surfaceflinger / RenderEngine / ProgramCache.cpp
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 << "gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(2.2));";
203         fs << "vec4 transformed = colorMatrix * vec4(gl_FragColor.rgb, 1);";
204         fs << "gl_FragColor.rgb = transformed.rgb/transformed.a;";
205         fs << "gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0 / 2.2));";
206         if (!needs.isOpaque() && needs.isPremultiplied()) {
207             // and re-premultiply if needed after gamma correction
208             fs << "gl_FragColor.rgb = gl_FragColor.rgb*gl_FragColor.a;";
209         }
210     }
211
212     fs << dedent << "}";
213     return fs.getString();
214 }
215
216 Program* ProgramCache::generateProgram(const Key& needs) {
217     // vertex shader
218     String8 vs = generateVertexShader(needs);
219
220     // fragment shader
221     String8 fs = generateFragmentShader(needs);
222
223     Program* program = new Program(needs, vs.string(), fs.string());
224     return program;
225 }
226
227 void ProgramCache::useProgram(const Description& description) {
228
229     // generate the key for the shader based on the description
230     Key needs(computeKey(description));
231
232      // look-up the program in the cache
233     Program* program = mCache.valueFor(needs);
234     if (program == NULL) {
235         // we didn't find our program, so generate one...
236         nsecs_t time = -systemTime();
237         program = generateProgram(needs);
238         mCache.add(needs, program);
239         time += systemTime();
240
241         //ALOGD(">>> generated new program: needs=%08X, time=%u ms (%d programs)",
242         //        needs.mNeeds, uint32_t(ns2ms(time)), mCache.size());
243     }
244
245     // here we have a suitable program for this description
246     if (program->isValid()) {
247         program->use();
248         program->setUniforms(description);
249     }
250 }
251
252
253 } /* namespace android */