1/*
2 * Copyright (C) 2011 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
18package android.filterfw.core;
19
20import android.filterfw.core.Frame;
21import android.filterfw.core.NativeAllocatorTag;
22import android.filterfw.core.Program;
23import android.filterfw.core.StopWatchMap;
24import android.filterfw.core.VertexFrame;
25import android.filterfw.geometry.Quad;
26import android.opengl.GLES20;
27
28/**
29 * @hide
30 */
31public class ShaderProgram extends Program {
32
33    private int shaderProgramId;
34
35    private int mMaxTileSize = 0;
36
37    // Keep a reference to the GL environment, so that it does not get deallocated while there
38    // are still programs living in it.
39    private GLEnvironment mGLEnvironment;
40
41    private StopWatchMap mTimer = null;
42
43    private void setTimer() {
44        mTimer = new StopWatchMap();
45    }
46
47    // Used from native layer for creating empty wrapper only!
48    private ShaderProgram() {
49    }
50
51    private ShaderProgram(NativeAllocatorTag tag) {
52    }
53
54    public ShaderProgram(FilterContext context, String fragmentShader) {
55        mGLEnvironment = getGLEnvironment(context);
56        allocate(mGLEnvironment, null, fragmentShader);
57        if (!compileAndLink()) {
58            throw new RuntimeException("Could not compile and link shader!");
59        }
60        this.setTimer();
61    }
62
63    public ShaderProgram(FilterContext context, String vertexShader, String fragmentShader) {
64        mGLEnvironment = getGLEnvironment(context);
65        allocate(mGLEnvironment, vertexShader, fragmentShader);
66        if (!compileAndLink()) {
67            throw new RuntimeException("Could not compile and link shader!");
68        }
69        this.setTimer();
70    }
71
72    public static ShaderProgram createIdentity(FilterContext context) {
73        ShaderProgram program = nativeCreateIdentity(getGLEnvironment(context));
74        program.setTimer();
75        return program;
76    }
77
78    @Override
79    protected void finalize() throws Throwable {
80        deallocate();
81    }
82
83    public GLEnvironment getGLEnvironment() {
84        return mGLEnvironment;
85    }
86
87    @Override
88    public void process(Frame[] inputs, Frame output) {
89        if (mTimer.LOG_MFF_RUNNING_TIMES) {
90          mTimer.start("glFinish");
91          GLES20.glFinish();
92          mTimer.stop("glFinish");
93        }
94
95        // Get the GL input frames
96        // TODO: We do the same in the NativeProgram... can we find a better way?!
97        GLFrame[] glInputs = new GLFrame[inputs.length];
98        for (int i = 0; i < inputs.length; ++i) {
99            if (inputs[i] instanceof GLFrame) {
100                glInputs[i] = (GLFrame)inputs[i];
101            } else {
102                throw new RuntimeException("ShaderProgram got non-GL frame as input " + i + "!");
103            }
104        }
105
106        // Get the GL output frame
107        GLFrame glOutput = null;
108        if (output instanceof GLFrame) {
109            glOutput = (GLFrame)output;
110        } else {
111            throw new RuntimeException("ShaderProgram got non-GL output frame!");
112        }
113
114        // Adjust tiles to meet maximum tile size requirement
115        if (mMaxTileSize > 0) {
116            int xTiles = (output.getFormat().getWidth() + mMaxTileSize - 1) / mMaxTileSize;
117            int yTiles = (output.getFormat().getHeight() + mMaxTileSize - 1) / mMaxTileSize;
118            setShaderTileCounts(xTiles, yTiles);
119        }
120
121        // Process!
122        if (!shaderProcess(glInputs, glOutput)) {
123            throw new RuntimeException("Error executing ShaderProgram!");
124        }
125
126        if (mTimer.LOG_MFF_RUNNING_TIMES) {
127          GLES20.glFinish();
128        }
129    }
130
131    @Override
132    public void setHostValue(String variableName, Object value) {
133        if (!setUniformValue(variableName, value)) {
134            throw new RuntimeException("Error setting uniform value for variable '" +
135                                       variableName + "'!");
136        }
137    }
138
139    @Override
140    public Object getHostValue(String variableName) {
141        return getUniformValue(variableName);
142    }
143
144    public void setAttributeValues(String attributeName, float[] data, int componentCount) {
145        if (!setShaderAttributeValues(attributeName, data, componentCount)) {
146            throw new RuntimeException("Error setting attribute value for attribute '" +
147                                       attributeName + "'!");
148        }
149    }
150
151    public void setAttributeValues(String attributeName,
152                                   VertexFrame vertexData,
153                                   int type,
154                                   int componentCount,
155                                   int strideInBytes,
156                                   int offsetInBytes,
157                                   boolean normalize) {
158        if (!setShaderAttributeVertexFrame(attributeName,
159                                           vertexData,
160                                           type,
161                                           componentCount,
162                                           strideInBytes,
163                                           offsetInBytes,
164                                           normalize)) {
165            throw new RuntimeException("Error setting attribute value for attribute '" +
166                                       attributeName + "'!");
167        }
168    }
169
170    public void setSourceRegion(Quad region) {
171        setSourceRegion(region.p0.x, region.p0.y,
172                        region.p1.x, region.p1.y,
173                        region.p2.x, region.p2.y,
174                        region.p3.x, region.p3.y);
175    }
176
177    public void setTargetRegion(Quad region) {
178        setTargetRegion(region.p0.x, region.p0.y,
179                        region.p1.x, region.p1.y,
180                        region.p2.x, region.p2.y,
181                        region.p3.x, region.p3.y);
182    }
183
184    public void setSourceRect(float x, float y, float width, float height) {
185        setSourceRegion(x, y, x + width, y, x, y + height, x + width, y + height);
186    }
187
188    public void setTargetRect(float x, float y, float width, float height) {
189        setTargetRegion(x, y, x + width, y, x, y + height, x + width, y + height);
190    }
191
192    public void setClearsOutput(boolean clears) {
193        if (!setShaderClearsOutput(clears)) {
194            throw new RuntimeException("Could not set clears-output flag to " + clears + "!");
195        }
196    }
197
198    public void setClearColor(float r, float g, float b) {
199        if (!setShaderClearColor(r, g, b)) {
200            throw new RuntimeException("Could not set clear color to " + r + "," + g + "," + b + "!");
201        }
202    }
203
204    public void setBlendEnabled(boolean enable) {
205        if (!setShaderBlendEnabled(enable)) {
206            throw new RuntimeException("Could not set Blending " + enable + "!");
207        }
208    }
209
210    public void setBlendFunc(int sfactor, int dfactor) {
211        if (!setShaderBlendFunc(sfactor, dfactor)) {
212            throw new RuntimeException("Could not set BlendFunc " + sfactor +","+ dfactor + "!");
213        }
214    }
215
216    public void setDrawMode(int drawMode) {
217        if (!setShaderDrawMode(drawMode)) {
218            throw new RuntimeException("Could not set GL draw-mode to " + drawMode + "!");
219        }
220    }
221
222    public void setVertexCount(int count) {
223        if (!setShaderVertexCount(count)) {
224            throw new RuntimeException("Could not set GL vertex count to " + count + "!");
225        }
226    }
227
228    public void setMaximumTileSize(int size) {
229        mMaxTileSize = size;
230    }
231
232    public void beginDrawing() {
233        if (!beginShaderDrawing()) {
234            throw new RuntimeException("Could not prepare shader-program for drawing!");
235        }
236    }
237
238    private static GLEnvironment getGLEnvironment(FilterContext context) {
239        GLEnvironment result = context != null ? context.getGLEnvironment() : null;
240        if (result == null) {
241            throw new NullPointerException("Attempting to create ShaderProgram with no GL "
242                + "environment in place!");
243        }
244        return result;
245    }
246
247    static {
248        System.loadLibrary("filterfw");
249    }
250
251    private native boolean allocate(GLEnvironment glEnv,
252                                    String vertexShader,
253                                    String fragmentShader);
254
255    private native boolean deallocate();
256
257    private native boolean compileAndLink();
258
259    private native boolean shaderProcess(GLFrame[] inputs, GLFrame output);
260
261    private native boolean setUniformValue(String name, Object value);
262
263    private native Object getUniformValue(String name);
264
265    public native boolean setSourceRegion(float x0, float y0, float x1, float y1,
266                                          float x2, float y2, float x3, float y3);
267
268    private native boolean setTargetRegion(float x0, float y0, float x1, float y1,
269                                           float x2, float y2, float x3, float y3);
270
271    private static native ShaderProgram nativeCreateIdentity(GLEnvironment glEnv);
272
273    private native boolean setShaderClearsOutput(boolean clears);
274
275    private native boolean setShaderBlendEnabled(boolean enable);
276
277    private native boolean setShaderBlendFunc(int sfactor, int dfactor);
278
279    private native boolean setShaderClearColor(float r, float g, float b);
280
281    private native boolean setShaderDrawMode(int drawMode);
282
283    private native boolean setShaderTileCounts(int xCount, int yCount);
284
285    private native boolean setShaderVertexCount(int vertexCount);
286
287    private native boolean beginShaderDrawing();
288
289    private native boolean setShaderAttributeValues(String attributeName,
290                                                    float[] data,
291                                                    int componentCount);
292
293    private native boolean setShaderAttributeVertexFrame(String attributeName,
294                                                         VertexFrame vertexData,
295                                                         int type,
296                                                         int componentCount,
297                                                         int strideInBytes,
298                                                         int offsetInBytes,
299                                                         boolean normalize);
300
301}
302