1/*
2 * Copyright (C) 2012 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
17package com.android.ide.eclipse.gltrace.state.transforms;
18
19import com.android.ide.eclipse.gltrace.FileUtils;
20import com.android.ide.eclipse.gltrace.GLEnum;
21import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage;
22import com.android.ide.eclipse.gltrace.state.GLState;
23import com.android.ide.eclipse.gltrace.state.GLStateType;
24import com.android.ide.eclipse.gltrace.state.IGLProperty;
25import com.google.common.io.Files;
26import com.google.protobuf.ByteString;
27
28import java.io.File;
29import java.io.IOException;
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.Collections;
33import java.util.List;
34
35public class StateTransformFactory {
36    private static final String TEXTURE_DATA_FILE_PREFIX = "tex";   //$NON-NLS-1$
37    private static final String TEXTURE_DATA_FILE_SUFFIX = ".dat";  //$NON-NLS-1$
38
39    /** Construct a list of transformations to be applied for the provided OpenGL call. */
40    public static List<IStateTransform> getTransformsFor(GLMessage msg) {
41        switch (msg.getFunction()) {
42            case eglCreateContext:
43                return transformsForEglCreateContext(msg);
44            case glBindFramebuffer:
45                return transformsForGlBindFramebuffer(msg);
46
47            // vertex data
48            case glVertexAttribPointer:
49                return transformsForGlVertexAttribPointer(msg);
50            case glVertexAttrib1f:
51            case glVertexAttrib2f:
52            case glVertexAttrib3f:
53            case glVertexAttrib4f:
54                return transformsForGlVertexAttribxf(msg);
55            case glVertexAttrib1fv:
56            case glVertexAttrib2fv:
57            case glVertexAttrib3fv:
58            case glVertexAttrib4fv:
59                return transformsForGlVertexAttribxfv(msg);
60            case glEnableVertexAttribArray:
61                return transformsForGlEnableVertexAttribArray(msg);
62            case glDisableVertexAttribArray:
63                return transformsForGlDisableVertexAttribArray(msg);
64
65            // VBO's
66            case glBindBuffer:
67                return transformsForGlBindBuffer(msg);
68            case glGenBuffers:
69                return transformsForGlGenBuffers(msg);
70            case glDeleteBuffers:
71                return transformsForGlDeleteBuffers(msg);
72            case glBufferData:
73                return transformsForGlBufferData(msg);
74            case glBufferSubData:
75                return transformsForGlBufferSubData(msg);
76
77            // transformation state
78            case glViewport:
79                return transformsForGlViewport(msg);
80            case glDepthRangef:
81                return transformsForGlDepthRangef(msg);
82
83            // rasterization
84            case glLineWidth:
85                return transformsForGlLineWidth(msg);
86            case glCullFace:
87                return transformsForGlCullFace(msg);
88            case glFrontFace:
89                return transformsForGlFrontFace(msg);
90            case glPolygonOffset:
91                return transformsForGlPolygonOffset(msg);
92
93            // pixel operations
94            case glScissor:
95                return transformsForGlScissor(msg);
96            case glStencilFunc:
97                return transformsForGlStencilFunc(msg);
98            case glStencilFuncSeparate:
99                return transformsForGlStencilFuncSeparate(msg);
100            case glStencilOp:
101                return transformsForGlStencilOp(msg);
102            case glStencilOpSeparate:
103                return transformsForGlStencilOpSeparate(msg);
104            case glDepthFunc:
105                return transformsForGlDepthFunc(msg);
106            case glBlendEquation:
107                return transformsForGlBlendEquation(msg);
108            case glBlendEquationSeparate:
109                return transformsForGlBlendEquationSeparate(msg);
110            case glBlendFunc:
111                return transformsForGlBlendFunc(msg);
112            case glBlendFuncSeparate:
113                return transformsForGlBlendFuncSeparate(msg);
114            case glPixelStorei:
115                return transformsForGlPixelStorei(msg);
116
117            // Texture State Transformations
118            case glGenTextures:
119                return transformsForGlGenTextures(msg);
120            case glDeleteTextures:
121                return transformsForGlDeleteTextures(msg);
122            case glActiveTexture:
123                return transformsForGlActiveTexture(msg);
124            case glBindTexture:
125                return transformsForGlBindTexture(msg);
126            case glTexImage2D:
127                return transformsForGlTexImage2D(msg);
128            case glTexSubImage2D:
129                return transformsForGlTexSubImage2D(msg);
130            case glTexParameteri:
131                return transformsForGlTexParameter(msg);
132
133            // Program State Transformations
134            case glCreateProgram:
135                return transformsForGlCreateProgram(msg);
136            case glUseProgram:
137                return transformsForGlUseProgram(msg);
138            case glAttachShader:
139                return transformsForGlAttachShader(msg);
140            case glDetachShader:
141                return transformsForGlDetachShader(msg);
142            case glGetActiveAttrib:
143                return transformsForGlGetActiveAttrib(msg);
144            case glGetActiveUniform:
145                return transformsForGlGetActiveUniform(msg);
146            case glUniform1i:
147            case glUniform2i:
148            case glUniform3i:
149            case glUniform4i:
150                return transformsForGlUniform(msg, false);
151            case glUniform1f:
152            case glUniform2f:
153            case glUniform3f:
154            case glUniform4f:
155                return transformsForGlUniform(msg, true);
156            case glUniform1iv:
157            case glUniform2iv:
158            case glUniform3iv:
159            case glUniform4iv:
160                return transformsForGlUniformv(msg, false);
161            case glUniform1fv:
162            case glUniform2fv:
163            case glUniform3fv:
164            case glUniform4fv:
165                return transformsForGlUniformv(msg, true);
166            case glUniformMatrix2fv:
167            case glUniformMatrix3fv:
168            case glUniformMatrix4fv:
169                return transformsForGlUniformMatrix(msg);
170
171            // Shader State Transformations
172            case glCreateShader:
173                return transformsForGlCreateShader(msg);
174            case glDeleteShader:
175                return transformsForGlDeleteShader(msg);
176            case glShaderSource:
177                return transformsForGlShaderSource(msg);
178            default:
179                return Collections.emptyList();
180        }
181    }
182
183    private static List<IStateTransform> transformsForGlVertexAttribPointer(GLMessage msg) {
184        int index = msg.getArgs(0).getIntValue(0);
185
186        int size = msg.getArgs(1).getIntValue(0);
187        int type = msg.getArgs(2).getIntValue(0);
188        boolean normalized = msg.getArgs(3).getBoolValue(0);
189        int stride = msg.getArgs(4).getIntValue(0);
190        int pointer = msg.getArgs(5).getIntValue(0);
191
192        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
193        transforms.add(new PropertyChangeTransform(
194                GLPropertyAccessor.makeAccessor(msg.getContextId(),
195                                                GLStateType.VERTEX_ARRAY_DATA,
196                                                GLStateType.VERTEX_ATTRIB_ARRAY,
197                                                Integer.valueOf(index),
198                                                GLStateType.VERTEX_ATTRIB_ARRAY_SIZE),
199                Integer.valueOf(size)));
200        transforms.add(new PropertyChangeTransform(
201                GLPropertyAccessor.makeAccessor(msg.getContextId(),
202                                                GLStateType.VERTEX_ARRAY_DATA,
203                                                GLStateType.VERTEX_ATTRIB_ARRAY,
204                                                Integer.valueOf(index),
205                                                GLStateType.VERTEX_ATTRIB_ARRAY_TYPE),
206                GLEnum.valueOf(type)));
207        transforms.add(new PropertyChangeTransform(
208                GLPropertyAccessor.makeAccessor(msg.getContextId(),
209                                                GLStateType.VERTEX_ARRAY_DATA,
210                                                GLStateType.VERTEX_ATTRIB_ARRAY,
211                                                Integer.valueOf(index),
212                                                GLStateType.VERTEX_ATTRIB_ARRAY_NORMALIZED),
213                Boolean.valueOf(normalized)));
214        transforms.add(new PropertyChangeTransform(
215                GLPropertyAccessor.makeAccessor(msg.getContextId(),
216                                                GLStateType.VERTEX_ARRAY_DATA,
217                                                GLStateType.VERTEX_ATTRIB_ARRAY,
218                                                Integer.valueOf(index),
219                                                GLStateType.VERTEX_ATTRIB_ARRAY_STRIDE),
220                Integer.valueOf(stride)));
221        transforms.add(new PropertyChangeTransform(
222                GLPropertyAccessor.makeAccessor(msg.getContextId(),
223                                                GLStateType.VERTEX_ARRAY_DATA,
224                                                GLStateType.VERTEX_ATTRIB_ARRAY,
225                                                Integer.valueOf(index),
226                                                GLStateType.VERTEX_ATTRIB_ARRAY_POINTER),
227                Integer.valueOf(pointer)));
228        return transforms;
229    }
230
231    private static List<IStateTransform> transformsForGlVertexAttrib(int context,
232            int index, float v0, float v1, float v2, float v3) {
233        List<IStateTransform> transforms = new ArrayList<IStateTransform>(4);
234        transforms.add(new PropertyChangeTransform(
235                GLPropertyAccessor.makeAccessor(context,
236                                                GLStateType.VERTEX_ARRAY_DATA,
237                                                GLStateType.GENERIC_VERTEX_ATTRIBUTES,
238                                                Integer.valueOf(index),
239                                                GLStateType.GENERIC_VERTEX_ATTRIB_V0),
240                Float.valueOf(v0)));
241        transforms.add(new PropertyChangeTransform(
242                GLPropertyAccessor.makeAccessor(context,
243                                                GLStateType.VERTEX_ARRAY_DATA,
244                                                GLStateType.GENERIC_VERTEX_ATTRIBUTES,
245                                                Integer.valueOf(index),
246                                                GLStateType.GENERIC_VERTEX_ATTRIB_V1),
247                Float.valueOf(v1)));
248        transforms.add(new PropertyChangeTransform(
249                GLPropertyAccessor.makeAccessor(context,
250                                                GLStateType.VERTEX_ARRAY_DATA,
251                                                GLStateType.GENERIC_VERTEX_ATTRIBUTES,
252                                                Integer.valueOf(index),
253                                                GLStateType.GENERIC_VERTEX_ATTRIB_V2),
254                Float.valueOf(v2)));
255        transforms.add(new PropertyChangeTransform(
256                GLPropertyAccessor.makeAccessor(context,
257                                                GLStateType.VERTEX_ARRAY_DATA,
258                                                GLStateType.GENERIC_VERTEX_ATTRIBUTES,
259                                                Integer.valueOf(index),
260                                                GLStateType.GENERIC_VERTEX_ATTRIB_V3),
261                Float.valueOf(v3)));
262        return transforms;
263    }
264
265    private static List<IStateTransform> transformsForGlVertexAttribxf(GLMessage msg) {
266        // void glVertexAttrib1f(GLuint index, GLfloat v0);
267        // void glVertexAttrib2f(GLuint index, GLfloat v0, GLfloat v1);
268        // void glVertexAttrib3f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2);
269        // void glVertexAttrib4f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
270
271        int index = msg.getArgs(0).getIntValue(0);
272        float v0 = msg.getArgs(1).getFloatValue(0);
273        float v1 = msg.getArgsCount() > 2 ? msg.getArgs(2).getFloatValue(0) : 0;
274        float v2 = msg.getArgsCount() > 3 ? msg.getArgs(3).getFloatValue(0) : 0;
275        float v3 = msg.getArgsCount() > 4 ? msg.getArgs(4).getFloatValue(0) : 0;
276
277        return transformsForGlVertexAttrib(msg.getContextId(), index, v0, v1, v2, v3);
278    }
279
280    private static List<IStateTransform> transformsForGlVertexAttribxfv(GLMessage msg) {
281        // void glVertexAttrib1fv(GLuint index, const GLfloat *v);
282        // void glVertexAttrib2fv(GLuint index, const GLfloat *v);
283        // void glVertexAttrib3fv(GLuint index, const GLfloat *v);
284        // void glVertexAttrib4fv(GLuint index, const GLfloat *v);
285
286        int index = msg.getArgs(0).getIntValue(0);
287        float v[] = new float[4];
288
289        for (int i = 0; i < msg.getArgs(1).getFloatValueList().size(); i++) {
290            v[i] = msg.getArgs(1).getFloatValue(i);
291        }
292
293        return transformsForGlVertexAttrib(msg.getContextId(), index, v[0], v[1], v[2], v[3]);
294    }
295
296    private static List<IStateTransform> transformsForGlEnableVertexAttribArray(GLMessage msg) {
297        // void glEnableVertexAttribArray(GLuint index);
298        // void glDisableVertexAttribArray(GLuint index);
299
300        int index = msg.getArgs(0).getIntValue(0);
301        IStateTransform transform = new PropertyChangeTransform(
302                GLPropertyAccessor.makeAccessor(msg.getContextId(),
303                                                GLStateType.VERTEX_ARRAY_DATA,
304                                                GLStateType.VERTEX_ATTRIB_ARRAY,
305                                                Integer.valueOf(index),
306                                                GLStateType.VERTEX_ATTRIB_ARRAY_ENABLED),
307                Boolean.TRUE);
308        return Collections.singletonList(transform);
309    }
310
311    private static List<IStateTransform> transformsForGlDisableVertexAttribArray(GLMessage msg) {
312        // void glEnableVertexAttribArray(GLuint index);
313        // void glDisableVertexAttribArray(GLuint index);
314
315        int index = msg.getArgs(0).getIntValue(0);
316        IStateTransform transform = new PropertyChangeTransform(
317                GLPropertyAccessor.makeAccessor(msg.getContextId(),
318                                                GLStateType.VERTEX_ARRAY_DATA,
319                                                GLStateType.VERTEX_ATTRIB_ARRAY,
320                                                Integer.valueOf(index),
321                                                GLStateType.VERTEX_ATTRIB_ARRAY_ENABLED),
322                Boolean.FALSE);
323        return Collections.singletonList(transform);
324    }
325
326    private static List<IStateTransform> transformsForGlBindBuffer(GLMessage msg) {
327        // void glBindBuffer(GLenum target, GLuint buffer);
328        // target is one of GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER.
329
330        GLEnum target = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
331        int buffer = msg.getArgs(1).getIntValue(0);
332        GLStateType bufferType;
333
334        if (target == GLEnum.GL_ARRAY_BUFFER) {
335            bufferType = GLStateType.ARRAY_BUFFER_BINDING;
336        } else {
337            bufferType = GLStateType.ELEMENT_ARRAY_BUFFER_BINDING;
338        }
339
340        IStateTransform transform = new PropertyChangeTransform(
341                GLPropertyAccessor.makeAccessor(msg.getContextId(),
342                                                GLStateType.VERTEX_ARRAY_DATA,
343                                                GLStateType.BUFFER_BINDINGS,
344                                                bufferType),
345                Integer.valueOf(buffer));
346        return Collections.singletonList(transform);
347    }
348
349    private static List<IStateTransform> transformsForGlGenBuffers(GLMessage msg) {
350        // void glGenBuffers(GLsizei n, GLuint * buffers);
351        int n = msg.getArgs(0).getIntValue(0);
352        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
353
354        for (int i = 0; i < n; i++) {
355            transforms.add(new SparseArrayElementAddTransform(
356                    GLPropertyAccessor.makeAccessor(msg.getContextId(),
357                                                    GLStateType.VERTEX_ARRAY_DATA,
358                                                    GLStateType.VBO),
359                    msg.getArgs(1).getIntValue(i)));
360        }
361
362        return transforms;
363    }
364
365    private static List<IStateTransform> transformsForGlDeleteBuffers(GLMessage msg) {
366        // void glDeleteBuffers(GLsizei n, const GLuint * buffers);
367        int n = msg.getArgs(0).getIntValue(0);
368        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
369
370        for (int i = 0; i < n; i++) {
371            transforms.add(new SparseArrayElementRemoveTransform(
372                    GLPropertyAccessor.makeAccessor(msg.getContextId(),
373                                                    GLStateType.VERTEX_ARRAY_DATA,
374                                                    GLStateType.VBO),
375                    msg.getArgs(1).getIntValue(i)));
376        }
377
378        return transforms;
379    }
380
381    private static List<IStateTransform> transformsForGlBufferData(GLMessage msg) {
382        // void glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);
383        GLEnum target = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
384        int size = msg.getArgs(1).getIntValue(0);
385        byte[] data = null;
386        GLEnum usage = GLEnum.valueOf(msg.getArgs(3).getIntValue(0));
387
388        if (msg.getArgs(2).getRawBytesList().size() > 0) {
389            data = msg.getArgs(2).getRawBytesList().get(0).toByteArray();
390        } else {
391            data = new byte[size];
392        }
393
394        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
395
396        transforms.add(new PropertyChangeTransform(
397                new CurrentVboPropertyAccessor(msg.getContextId(),
398                                               target,
399                                               GLStateType.BUFFER_SIZE),
400                Integer.valueOf(size)));
401        transforms.add(new PropertyChangeTransform(
402                new CurrentVboPropertyAccessor(msg.getContextId(),
403                                               target,
404                                               GLStateType.BUFFER_DATA),
405                data));
406        transforms.add(new PropertyChangeTransform(
407                new CurrentVboPropertyAccessor(msg.getContextId(),
408                                               target,
409                                               GLStateType.BUFFER_USAGE),
410                usage));
411        transforms.add(new PropertyChangeTransform(
412                new CurrentVboPropertyAccessor(msg.getContextId(),
413                                               target,
414                                               GLStateType.BUFFER_TYPE),
415                target));
416        return transforms;
417    }
418
419    private static List<IStateTransform> transformsForGlBufferSubData(GLMessage msg) {
420        // void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data);
421        GLEnum target = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
422        int offset = msg.getArgs(1).getIntValue(0);
423        byte[] data = msg.getArgs(3).getRawBytesList().get(0).toByteArray();
424
425        IStateTransform transform = new BufferSubDataTransform(
426                new CurrentVboPropertyAccessor(msg.getContextId(),
427                        target,
428                        GLStateType.BUFFER_DATA),
429                offset, data);
430
431        return Collections.singletonList(transform);
432    }
433
434    private static List<IStateTransform> transformsForGlBindFramebuffer(GLMessage msg) {
435        // void glBindFramebuffer(GLenum target, GLuint framebuffer);
436        int fb = msg.getArgs(1).getIntValue(0);
437        IStateTransform transform = new PropertyChangeTransform(
438                GLPropertyAccessor.makeAccessor(msg.getContextId(),
439                        GLStateType.FRAMEBUFFER_STATE,
440                        GLStateType.FRAMEBUFFER_BINDING),
441                fb);
442        return Collections.singletonList(transform);
443    }
444
445    private static List<IStateTransform> transformsForGlLineWidth(GLMessage msg) {
446        // void glLineWidth(GLfloat width);
447        float width = msg.getArgs(0).getFloatValue(0);
448        IStateTransform transform = new PropertyChangeTransform(
449                GLPropertyAccessor.makeAccessor(msg.getContextId(),
450                        GLStateType.RASTERIZATION_STATE,
451                        GLStateType.LINE_WIDTH),
452                width);
453        return Collections.singletonList(transform);
454    }
455
456    private static List<IStateTransform> transformsForGlCullFace(GLMessage msg) {
457        // void glCullFace(GLenum mode);
458        int mode = msg.getArgs(0).getIntValue(0);
459        IStateTransform transform = new PropertyChangeTransform(
460                GLPropertyAccessor.makeAccessor(msg.getContextId(),
461                        GLStateType.RASTERIZATION_STATE,
462                        GLStateType.CULL_FACE_MODE),
463                GLEnum.valueOf(mode));
464        return Collections.singletonList(transform);
465    }
466
467    private static List<IStateTransform> transformsForGlFrontFace(GLMessage msg) {
468        // void glFrontFace(GLenum mode);
469        int mode = msg.getArgs(0).getIntValue(0);
470        IStateTransform transform = new PropertyChangeTransform(
471                GLPropertyAccessor.makeAccessor(msg.getContextId(),
472                        GLStateType.RASTERIZATION_STATE,
473                        GLStateType.FRONT_FACE),
474                GLEnum.valueOf(mode));
475        return Collections.singletonList(transform);
476    }
477
478    private static List<IStateTransform> transformsForGlPolygonOffset(GLMessage msg) {
479        // void glPolygonOffset(GLfloat factor, GLfloat units)
480        float factor = msg.getArgs(0).getFloatValue(0);
481        float units = msg.getArgs(1).getFloatValue(0);
482
483        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
484        transforms.add(new PropertyChangeTransform(
485                GLPropertyAccessor.makeAccessor(msg.getContextId(),
486                        GLStateType.RASTERIZATION_STATE,
487                        GLStateType.POLYGON_OFFSET_FACTOR),
488                Float.valueOf(factor)));
489        transforms.add(new PropertyChangeTransform(
490                GLPropertyAccessor.makeAccessor(msg.getContextId(),
491                        GLStateType.RASTERIZATION_STATE,
492                        GLStateType.POLYGON_OFFSET_UNITS),
493                Float.valueOf(units)));
494        return transforms;
495    }
496
497    private static List<IStateTransform> transformsForGlScissor(GLMessage msg) {
498        // void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
499        int x = msg.getArgs(0).getIntValue(0);
500        int y = msg.getArgs(1).getIntValue(0);
501        int w = msg.getArgs(2).getIntValue(0);
502        int h = msg.getArgs(3).getIntValue(0);
503
504        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
505        transforms.add(new PropertyChangeTransform(
506                GLPropertyAccessor.makeAccessor(msg.getContextId(),
507                        GLStateType.PIXEL_OPERATIONS,
508                        GLStateType.SCISSOR_BOX,
509                        GLStateType.SCISSOR_BOX_X),
510                Integer.valueOf(x)));
511        transforms.add(new PropertyChangeTransform(
512                GLPropertyAccessor.makeAccessor(msg.getContextId(),
513                        GLStateType.PIXEL_OPERATIONS,
514                        GLStateType.SCISSOR_BOX,
515                        GLStateType.SCISSOR_BOX_Y),
516                Integer.valueOf(y)));
517        transforms.add(new PropertyChangeTransform(
518                GLPropertyAccessor.makeAccessor(msg.getContextId(),
519                        GLStateType.PIXEL_OPERATIONS,
520                        GLStateType.SCISSOR_BOX,
521                        GLStateType.SCISSOR_BOX_WIDTH),
522                Integer.valueOf(w)));
523        transforms.add(new PropertyChangeTransform(
524                GLPropertyAccessor.makeAccessor(msg.getContextId(),
525                        GLStateType.PIXEL_OPERATIONS,
526                        GLStateType.SCISSOR_BOX,
527                        GLStateType.SCISSOR_BOX_HEIGHT),
528                Integer.valueOf(h)));
529        return transforms;
530    }
531
532    private static List<IStateTransform> transformsForGlStencilFuncFront(int contextId,
533            GLEnum func, int ref, int mask) {
534        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
535        transforms.add(new PropertyChangeTransform(
536                GLPropertyAccessor.makeAccessor(contextId,
537                        GLStateType.PIXEL_OPERATIONS,
538                        GLStateType.STENCIL,
539                        GLStateType.STENCIL_FUNC),
540                func));
541        transforms.add(new PropertyChangeTransform(
542                GLPropertyAccessor.makeAccessor(contextId,
543                        GLStateType.PIXEL_OPERATIONS,
544                        GLStateType.STENCIL,
545                        GLStateType.STENCIL_REF),
546                Integer.valueOf(ref)));
547        transforms.add(new PropertyChangeTransform(
548                GLPropertyAccessor.makeAccessor(contextId,
549                        GLStateType.PIXEL_OPERATIONS,
550                        GLStateType.STENCIL,
551                        GLStateType.STENCIL_VALUE_MASK),
552                Integer.valueOf(mask)));
553        return transforms;
554    }
555
556    private static List<IStateTransform> transformsForGlStencilFuncBack(int contextId,
557            GLEnum func, int ref, int mask) {
558        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
559        transforms.add(new PropertyChangeTransform(
560                GLPropertyAccessor.makeAccessor(contextId,
561                        GLStateType.PIXEL_OPERATIONS,
562                        GLStateType.STENCIL,
563                        GLStateType.STENCIL_BACK_FUNC),
564                func));
565        transforms.add(new PropertyChangeTransform(
566                GLPropertyAccessor.makeAccessor(contextId,
567                        GLStateType.PIXEL_OPERATIONS,
568                        GLStateType.STENCIL,
569                        GLStateType.STENCIL_BACK_REF),
570                Integer.valueOf(ref)));
571        transforms.add(new PropertyChangeTransform(
572                GLPropertyAccessor.makeAccessor(contextId,
573                        GLStateType.PIXEL_OPERATIONS,
574                        GLStateType.STENCIL,
575                        GLStateType.STENCIL_BACK_VALUE_MASK),
576                Integer.valueOf(mask)));
577        return transforms;
578    }
579
580    private static List<IStateTransform> transformsForGlStencilFunc(GLMessage msg) {
581        // void glStencilFunc(GLenum func, GLint ref, GLuint mask);
582        GLEnum func = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
583        int ref = msg.getArgs(1).getIntValue(0);
584        int mask = msg.getArgs(2).getIntValue(0);
585
586        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
587        transforms.addAll(transformsForGlStencilFuncFront(msg.getContextId(), func, ref, mask));
588        transforms.addAll(transformsForGlStencilFuncBack(msg.getContextId(), func, ref, mask));
589        return transforms;
590    }
591
592    private static List<IStateTransform> transformsForGlStencilFuncSeparate(GLMessage msg) {
593        // void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
594        GLEnum face = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
595        GLEnum func = GLEnum.valueOf(msg.getArgs(1).getIntValue(0));
596        int ref = msg.getArgs(2).getIntValue(0);
597        int mask = msg.getArgs(3).getIntValue(0);
598
599        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
600        if (face == GLEnum.GL_FRONT || face == GLEnum.GL_FRONT_AND_BACK) {
601            transforms.addAll(
602                    transformsForGlStencilFuncFront(msg.getContextId(), func, ref, mask));
603        }
604        if (face == GLEnum.GL_BACK || face == GLEnum.GL_FRONT_AND_BACK) {
605            transforms.addAll(
606                    transformsForGlStencilFuncBack(msg.getContextId(), func, ref, mask));
607        }
608
609        return transforms;
610    }
611
612    private static List<IStateTransform> transformsForGlStencilOpFront(int contextId,
613            GLEnum sfail, GLEnum dpfail, GLEnum dppass) {
614        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
615        transforms.add(new PropertyChangeTransform(
616                GLPropertyAccessor.makeAccessor(contextId,
617                        GLStateType.PIXEL_OPERATIONS,
618                        GLStateType.STENCIL,
619                        GLStateType.STENCIL_FAIL),
620                sfail));
621        transforms.add(new PropertyChangeTransform(
622                GLPropertyAccessor.makeAccessor(contextId,
623                        GLStateType.PIXEL_OPERATIONS,
624                        GLStateType.STENCIL,
625                        GLStateType.STENCIL_PASS_DEPTH_FAIL),
626                dpfail));
627        transforms.add(new PropertyChangeTransform(
628                GLPropertyAccessor.makeAccessor(contextId,
629                        GLStateType.PIXEL_OPERATIONS,
630                        GLStateType.STENCIL,
631                        GLStateType.STENCIL_PASS_DEPTH_PASS),
632                dppass));
633        return transforms;
634    }
635
636    private static List<IStateTransform> transformsForGlStencilOpBack(int contextId,
637            GLEnum sfail, GLEnum dpfail, GLEnum dppass) {
638        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
639        transforms.add(new PropertyChangeTransform(
640                GLPropertyAccessor.makeAccessor(contextId,
641                        GLStateType.PIXEL_OPERATIONS,
642                        GLStateType.STENCIL,
643                        GLStateType.STENCIL_BACK_FAIL),
644                sfail));
645        transforms.add(new PropertyChangeTransform(
646                GLPropertyAccessor.makeAccessor(contextId,
647                        GLStateType.PIXEL_OPERATIONS,
648                        GLStateType.STENCIL,
649                        GLStateType.STENCIL_BACK_PASS_DEPTH_FAIL),
650                dpfail));
651        transforms.add(new PropertyChangeTransform(
652                GLPropertyAccessor.makeAccessor(contextId,
653                        GLStateType.PIXEL_OPERATIONS,
654                        GLStateType.STENCIL,
655                        GLStateType.STENCIL_BACK_PASS_DEPTH_PASS),
656                dppass));
657        return transforms;
658    }
659
660    private static List<IStateTransform> transformsForGlStencilOp(GLMessage msg) {
661        // void glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);
662        GLEnum sfail = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
663        GLEnum dpfail = GLEnum.valueOf(msg.getArgs(1).getIntValue(0));
664        GLEnum dppass = GLEnum.valueOf(msg.getArgs(2).getIntValue(0));
665        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
666        transforms.addAll(
667                transformsForGlStencilOpFront(msg.getContextId(), sfail, dpfail, dppass));
668        transforms.addAll(
669                transformsForGlStencilOpBack(msg.getContextId(), sfail, dpfail, dppass));
670        return transforms;
671    }
672
673    private static List<IStateTransform> transformsForGlStencilOpSeparate(GLMessage msg) {
674        // void glStencilOp(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
675        GLEnum face = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
676        GLEnum sfail = GLEnum.valueOf(msg.getArgs(1).getIntValue(0));
677        GLEnum dpfail = GLEnum.valueOf(msg.getArgs(2).getIntValue(0));
678        GLEnum dppass = GLEnum.valueOf(msg.getArgs(3).getIntValue(0));
679        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
680
681        if (face == GLEnum.GL_FRONT || face == GLEnum.GL_FRONT_AND_BACK) {
682            transforms.addAll(
683                    transformsForGlStencilOpFront(msg.getContextId(), sfail, dpfail, dppass));
684        }
685
686        if (face == GLEnum.GL_BACK || face == GLEnum.GL_FRONT_AND_BACK) {
687            transforms.addAll(
688                    transformsForGlStencilOpBack(msg.getContextId(), sfail, dpfail, dppass));
689        }
690
691        return transforms;
692    }
693
694    private static List<IStateTransform> transformsForGlDepthFunc(GLMessage msg) {
695        // void glDepthFunc(GLenum func);
696        GLEnum func = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
697
698        IStateTransform transform = new PropertyChangeTransform(
699                GLPropertyAccessor.makeAccessor(msg.getContextId(),
700                        GLStateType.PIXEL_OPERATIONS,
701                        GLStateType.DEPTH_FUNC),
702                func);
703        return Collections.singletonList(transform);
704    }
705
706    private static IStateTransform transformForGlEquationRGB(int contextId, GLEnum mode) {
707        return new PropertyChangeTransform(
708                GLPropertyAccessor.makeAccessor(contextId,
709                        GLStateType.PIXEL_OPERATIONS,
710                        GLStateType.BLEND,
711                        GLStateType.BLEND_EQUATION_RGB),
712                mode);
713    }
714
715    private static IStateTransform transformForGlEquationAlpha(int contextId, GLEnum mode) {
716        return new PropertyChangeTransform(
717                GLPropertyAccessor.makeAccessor(contextId,
718                        GLStateType.PIXEL_OPERATIONS,
719                        GLStateType.BLEND,
720                        GLStateType.BLEND_EQUATION_ALPHA),
721                mode);
722    }
723
724    private static List<IStateTransform> transformsForGlBlendEquationSeparate(GLMessage msg) {
725        // void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);
726        GLEnum rgb = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
727        GLEnum alpha = GLEnum.valueOf(msg.getArgs(1).getIntValue(0));
728
729        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
730        transforms.add(transformForGlEquationRGB(msg.getContextId(), rgb));
731        transforms.add(transformForGlEquationAlpha(msg.getContextId(), alpha));
732        return transforms;
733    }
734
735    private static List<IStateTransform> transformsForGlBlendEquation(GLMessage msg) {
736        // void glBlendEquation(GLenum mode);
737        GLEnum mode = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
738
739        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
740        transforms.add(transformForGlEquationRGB(msg.getContextId(), mode));
741        transforms.add(transformForGlEquationAlpha(msg.getContextId(), mode));
742        return transforms;
743    }
744
745    private static List<IStateTransform> transformsForGlBlendFuncSrcDst(boolean src,
746            int contextId, GLEnum rgb, GLEnum alpha) {
747        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
748
749        GLStateType rgbAccessor = GLStateType.BLEND_DST_RGB;
750        GLStateType alphaAccessor = GLStateType.BLEND_DST_ALPHA;
751        if (src) {
752            rgbAccessor = GLStateType.BLEND_SRC_RGB;
753            alphaAccessor = GLStateType.BLEND_SRC_ALPHA;
754        }
755
756        transforms.add(new PropertyChangeTransform(
757                GLPropertyAccessor.makeAccessor(contextId,
758                        GLStateType.PIXEL_OPERATIONS,
759                        GLStateType.BLEND,
760                        rgbAccessor),
761                rgb));
762        transforms.add(new PropertyChangeTransform(
763                GLPropertyAccessor.makeAccessor(contextId,
764                        GLStateType.PIXEL_OPERATIONS,
765                        GLStateType.BLEND,
766                        alphaAccessor),
767                alpha));
768        return transforms;
769    }
770
771    private static List<IStateTransform> transformsForGlBlendFuncSeparate(GLMessage msg) {
772        // void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
773        GLEnum srcRgb = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
774        GLEnum dstRgb = GLEnum.valueOf(msg.getArgs(1).getIntValue(0));
775        GLEnum srcAlpha = GLEnum.valueOf(msg.getArgs(2).getIntValue(0));
776        GLEnum dstAlpha = GLEnum.valueOf(msg.getArgs(3).getIntValue(0));
777
778        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
779        transforms.addAll(transformsForGlBlendFuncSrcDst(true,
780                msg.getContextId(), srcRgb, srcAlpha));
781        transforms.addAll(transformsForGlBlendFuncSrcDst(false,
782                msg.getContextId(), dstRgb, dstAlpha));
783        return transforms;
784    }
785
786    private static List<IStateTransform> transformsForGlBlendFunc(GLMessage msg) {
787        // void glBlendFunc(GLenum sfactor, GLenum dfactor);
788        GLEnum sfactor = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
789        GLEnum dfactor = GLEnum.valueOf(msg.getArgs(1).getIntValue(0));
790
791        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
792        transforms.addAll(transformsForGlBlendFuncSrcDst(true,
793                msg.getContextId(), sfactor, sfactor));
794        transforms.addAll(transformsForGlBlendFuncSrcDst(false,
795                msg.getContextId(), dfactor, dfactor));
796        return transforms;
797    }
798
799    private static List<IStateTransform> transformsForGlPixelStorei(GLMessage msg) {
800        // void glPixelStorei(GLenum pname, GLint param);
801        GLEnum pname = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
802        Integer param = Integer.valueOf(msg.getArgs(1).getIntValue(0));
803
804        IStateTransform transform;
805        if (pname == GLEnum.GL_PACK_ALIGNMENT) {
806            transform = new PropertyChangeTransform(
807                    GLPropertyAccessor.makeAccessor(msg.getContextId(),
808                            GLStateType.PIXEL_PACKING,
809                            GLStateType.PACK_ALIGNMENT),
810                    param);
811        } else {
812            transform = new PropertyChangeTransform(
813                    GLPropertyAccessor.makeAccessor(msg.getContextId(),
814                            GLStateType.PIXEL_PACKING,
815                            GLStateType.UNPACK_ALIGNMENT),
816                    param);
817        }
818
819        return Collections.singletonList(transform);
820    }
821
822    private static List<IStateTransform> transformsForGlViewport(GLMessage msg) {
823        // void glViewport( GLint x, GLint y, GLsizei width, GLsizei height);
824        int x = msg.getArgs(0).getIntValue(0);
825        int y = msg.getArgs(1).getIntValue(0);
826        int w = msg.getArgs(2).getIntValue(0);
827        int h = msg.getArgs(3).getIntValue(0);
828
829        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
830        transforms.add(new PropertyChangeTransform(
831                GLPropertyAccessor.makeAccessor(msg.getContextId(),
832                                                GLStateType.TRANSFORMATION_STATE,
833                                                GLStateType.VIEWPORT,
834                                                GLStateType.VIEWPORT_X),
835                Integer.valueOf(x)));
836        transforms.add(new PropertyChangeTransform(
837                GLPropertyAccessor.makeAccessor(msg.getContextId(),
838                                                GLStateType.TRANSFORMATION_STATE,
839                                                GLStateType.VIEWPORT,
840                                                GLStateType.VIEWPORT_Y),
841                Integer.valueOf(y)));
842        transforms.add(new PropertyChangeTransform(
843                GLPropertyAccessor.makeAccessor(msg.getContextId(),
844                                                GLStateType.TRANSFORMATION_STATE,
845                                                GLStateType.VIEWPORT,
846                                                GLStateType.VIEWPORT_WIDTH),
847                Integer.valueOf(w)));
848        transforms.add(new PropertyChangeTransform(
849                GLPropertyAccessor.makeAccessor(msg.getContextId(),
850                                                GLStateType.TRANSFORMATION_STATE,
851                                                GLStateType.VIEWPORT,
852                                                GLStateType.VIEWPORT_HEIGHT),
853                Integer.valueOf(h)));
854        return transforms;
855    }
856
857    private static List<IStateTransform> transformsForGlDepthRangef(GLMessage msg) {
858        // void glDepthRangef(GLclampf nearVal, GLclampf farVal);
859        float near = msg.getArgs(0).getFloatValue(0);
860        float far = msg.getArgs(1).getFloatValue(0);
861
862        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
863        transforms.add(new PropertyChangeTransform(
864                GLPropertyAccessor.makeAccessor(msg.getContextId(),
865                                                GLStateType.TRANSFORMATION_STATE,
866                                                GLStateType.DEPTH_RANGE,
867                                                GLStateType.DEPTH_RANGE_NEAR),
868                Float.valueOf(near)));
869        transforms.add(new PropertyChangeTransform(
870                GLPropertyAccessor.makeAccessor(msg.getContextId(),
871                                                GLStateType.TRANSFORMATION_STATE,
872                                                GLStateType.DEPTH_RANGE,
873                                                GLStateType.DEPTH_RANGE_FAR),
874                Float.valueOf(far)));
875        return transforms;
876    }
877
878    private static List<IStateTransform> transformsForGlGenTextures(GLMessage msg) {
879        // void glGenTextures(GLsizei n, GLuint *textures);
880        int n = msg.getArgs(0).getIntValue(0);
881
882        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
883        for (int i = 0; i < n; i++) {
884            int texture = msg.getArgs(1).getIntValue(i);
885            transforms.add(new SparseArrayElementAddTransform(
886                    GLPropertyAccessor.makeAccessor(msg.getContextId(),
887                                                    GLStateType.TEXTURE_STATE,
888                                                    GLStateType.TEXTURES),
889                    texture));
890        }
891
892        return transforms;
893    }
894
895    /**
896     * Obtain a list of transforms that will reset any existing texture units
897     * that are bound to provided texture.
898     * @param contextId context to operate on
899     * @param texture texture that should be unbound
900     */
901    private static List<IStateTransform> transformsToResetBoundTextureUnits(int contextId,
902            int texture) {
903        List<IStateTransform> transforms = new ArrayList<IStateTransform>(
904                GLState.TEXTURE_UNIT_COUNT);
905
906        for (int i = 0; i < GLState.TEXTURE_UNIT_COUNT; i++) {
907            transforms.add(new PropertyChangeTransform(
908                    GLPropertyAccessor.makeAccessor(contextId,
909                                                    GLStateType.TEXTURE_STATE,
910                                                    GLStateType.TEXTURE_UNITS,
911                                                    Integer.valueOf(i),
912                                                    GLStateType.TEXTURE_BINDING_2D),
913                    Integer.valueOf(0), /* reset binding to texture 0 */
914                    Predicates.matchesInteger(texture) /* only if currently bound to @texture */ ));
915        }
916        return transforms;
917    }
918
919    private static List<IStateTransform> transformsForGlDeleteTextures(GLMessage msg) {
920        // void glDeleteTextures(GLsizei n, const GLuint * textures);
921        int n = msg.getArgs(0).getIntValue(0);
922
923        List<IStateTransform> transforms = new ArrayList<IStateTransform>(n);
924        for (int i = 0; i < n; i++) {
925            int texture = msg.getArgs(1).getIntValue(i);
926            transforms.add(new SparseArrayElementRemoveTransform(
927                    GLPropertyAccessor.makeAccessor(msg.getContextId(),
928                                                    GLStateType.TEXTURE_STATE,
929                                                    GLStateType.TEXTURES),
930                    texture));
931            transforms.addAll(transformsToResetBoundTextureUnits(msg.getContextId(), texture));
932        }
933
934        return transforms;
935    }
936
937    private static List<IStateTransform> transformsForGlActiveTexture(GLMessage msg) {
938        // void glActiveTexture(GLenum texture);
939        GLEnum texture = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
940        Integer textureIndex = Integer.valueOf(texture.value - GLEnum.GL_TEXTURE0.value);
941        IStateTransform transform = new PropertyChangeTransform(
942                GLPropertyAccessor.makeAccessor(msg.getContextId(),
943                                                GLStateType.TEXTURE_STATE,
944                                                GLStateType.ACTIVE_TEXTURE_UNIT),
945                textureIndex);
946        return Collections.singletonList(transform);
947    }
948
949    private static GLStateType getTextureUnitTargetName(GLEnum target) {
950        if (target == GLEnum.GL_TEXTURE_CUBE_MAP) {
951            return GLStateType.TEXTURE_BINDING_CUBE_MAP;
952        } else if (target == GLEnum.GL_TEXTURE_EXTERNAL) {
953            // added by OES_EGL_image_external
954            return GLStateType.TEXTURE_BINDING_EXTERNAL;
955        } else {
956            return GLStateType.TEXTURE_BINDING_2D;
957        }
958    }
959
960    private static GLStateType getTextureTargetName(GLEnum pname) {
961        switch (pname) {
962            case GL_TEXTURE_MIN_FILTER:
963                return GLStateType.TEXTURE_MIN_FILTER;
964            case GL_TEXTURE_MAG_FILTER:
965                return GLStateType.TEXTURE_MAG_FILTER;
966            case GL_TEXTURE_WRAP_S:
967                return GLStateType.TEXTURE_WRAP_S;
968            case GL_TEXTURE_WRAP_T:
969                return GLStateType.TEXTURE_WRAP_T;
970        }
971
972        assert false : "glTexParameter's pname argument does not support provided value.";
973        return GLStateType.TEXTURE_MIN_FILTER;
974    }
975
976    private static List<IStateTransform> transformsForGlBindTexture(GLMessage msg) {
977        // void glBindTexture(GLenum target, GLuint texture);
978        GLEnum target = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
979        Integer texture = Integer.valueOf(msg.getArgs(1).getIntValue(0));
980
981        IStateTransform transform = new PropertyChangeTransform(
982                new TextureUnitPropertyAccessor(msg.getContextId(),
983                                                getTextureUnitTargetName(target)),
984                texture);
985        return Collections.singletonList(transform);
986    }
987
988    /**
989     * Utility function used by both {@link #transformsForGlTexImage2D(GLMessage) and
990     * {@link #transformsForGlTexSubImage2D(GLMessage)}.
991     */
992    private static List<IStateTransform> transformsForGlTexImage(GLMessage msg, int widthArgIndex,
993            int heightArgIndex, int xOffsetIndex, int yOffsetIndex) {
994        GLEnum target = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
995        int level = msg.getArgs(1).getIntValue(0);
996        Integer width = Integer.valueOf(msg.getArgs(widthArgIndex).getIntValue(0));
997        Integer height = Integer.valueOf(msg.getArgs(heightArgIndex).getIntValue(0));
998        GLEnum format = GLEnum.valueOf(msg.getArgs(6).getIntValue(0));
999        GLEnum type = GLEnum.valueOf(msg.getArgs(7).getIntValue(0));
1000
1001        List<IStateTransform> transforms = new ArrayList<IStateTransform>();
1002        transforms.add(new PropertyChangeTransform(
1003                new TexturePropertyAccessor(msg.getContextId(),
1004                                            getTextureUnitTargetName(target),
1005                                            level,
1006                                            GLStateType.TEXTURE_WIDTH),
1007                width));
1008        transforms.add(new PropertyChangeTransform(
1009                new TexturePropertyAccessor(msg.getContextId(),
1010                                            getTextureUnitTargetName(target),
1011                                            level,
1012                                            GLStateType.TEXTURE_HEIGHT),
1013                height));
1014        transforms.add(new PropertyChangeTransform(
1015                new TexturePropertyAccessor(msg.getContextId(),
1016                                            getTextureUnitTargetName(target),
1017                                            level,
1018                                            GLStateType.TEXTURE_FORMAT),
1019                format));
1020        transforms.add(new PropertyChangeTransform(
1021                new TexturePropertyAccessor(msg.getContextId(),
1022                                            getTextureUnitTargetName(target),
1023                                            level,
1024                                            GLStateType.TEXTURE_IMAGE_TYPE),
1025                type));
1026
1027        // if texture data is available, extract and store it in the cache folder
1028        File f = null;
1029        if (msg.getArgs(8).getIsArray()) {
1030            ByteString data = msg.getArgs(8).getRawBytes(0);
1031            f = FileUtils.createTempFile(TEXTURE_DATA_FILE_PREFIX, TEXTURE_DATA_FILE_SUFFIX);
1032            try {
1033                Files.write(data.toByteArray(), f);
1034            } catch (IOException e) {
1035                throw new RuntimeException(e);
1036            }
1037        }
1038
1039        int xOffset = 0;
1040        int yOffset = 0;
1041
1042        if (xOffsetIndex >= 0) {
1043            xOffset = msg.getArgs(xOffsetIndex).getIntValue(0);
1044        }
1045
1046        if (yOffsetIndex >= 0) {
1047            yOffset = msg.getArgs(yOffsetIndex).getIntValue(0);
1048        }
1049
1050        transforms.add(new TexImageTransform(
1051                new TexturePropertyAccessor(msg.getContextId(),
1052                        getTextureUnitTargetName(target),
1053                        level,
1054                        GLStateType.TEXTURE_IMAGE),
1055                f, format, xOffset, yOffset, width, height));
1056
1057        return transforms;
1058    }
1059
1060    private static List<IStateTransform> transformsForGlTexImage2D(GLMessage msg) {
1061        // void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width,
1062        //          GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *data);
1063        return transformsForGlTexImage(msg, 3, 4, -1, -1);
1064    }
1065
1066    private static List<IStateTransform> transformsForGlTexSubImage2D(GLMessage msg) {
1067        // void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
1068        //          GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data);
1069        return transformsForGlTexImage(msg, 4, 5, 2, 3);
1070    }
1071
1072    private static List<IStateTransform> transformsForGlTexParameter(GLMessage msg) {
1073        // void glTexParameteri(GLenum target, GLenum pname, GLint param);
1074        GLEnum target = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
1075        GLEnum pname = GLEnum.valueOf(msg.getArgs(1).getIntValue(0));
1076        GLEnum pvalue = GLEnum.valueOf(msg.getArgs(2).getIntValue(0));
1077
1078        if (pname != GLEnum.GL_TEXTURE_MIN_FILTER
1079                && pname != GLEnum.GL_TEXTURE_MAG_FILTER
1080                && pname != GLEnum.GL_TEXTURE_WRAP_S
1081                && pname != GLEnum.GL_TEXTURE_WRAP_T) {
1082            throw new IllegalArgumentException(
1083                    String.format("Unsupported parameter (%s) for glTexParameter()", pname));
1084        }
1085
1086        IStateTransform transform = new PropertyChangeTransform(
1087                new TexturePropertyAccessor(msg.getContextId(),
1088                                            getTextureUnitTargetName(target),
1089                                            getTextureTargetName(pname)),
1090                pvalue);
1091        return Collections.singletonList(transform);
1092    }
1093
1094    private static List<IStateTransform> transformsForGlCreateProgram(GLMessage msg) {
1095        // GLuint glCreateProgram(void);
1096        int program = msg.getReturnValue().getIntValue(0);
1097
1098        IStateTransform transform = new SparseArrayElementAddTransform(
1099                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1100                                                GLStateType.PROGRAM_STATE,
1101                                                GLStateType.PROGRAMS),
1102                program);
1103        return Collections.singletonList(transform);
1104    }
1105
1106    private static List<IStateTransform> transformsForGlUseProgram(GLMessage msg) {
1107        // void glUseProgram(GLuint program);
1108        Integer program = Integer.valueOf(msg.getArgs(0).getIntValue(0));
1109
1110        IStateTransform transform = new PropertyChangeTransform(
1111                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1112                                                GLStateType.PROGRAM_STATE,
1113                                                GLStateType.CURRENT_PROGRAM),
1114                program);
1115        return Collections.singletonList(transform);
1116    }
1117
1118    private static List<IStateTransform> transformsForGlAttachShader(GLMessage msg) {
1119        // void glAttachShader(GLuint program, GLuint shader);
1120        int program = msg.getArgs(0).getIntValue(0);
1121        int shader = msg.getArgs(1).getIntValue(0);
1122
1123        IStateTransform transform = new SparseArrayElementAddTransform(
1124                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1125                                                GLStateType.PROGRAM_STATE,
1126                                                GLStateType.PROGRAMS,
1127                                                Integer.valueOf(program),
1128                                                GLStateType.ATTACHED_SHADERS),
1129                Integer.valueOf(shader));
1130        return Collections.singletonList(transform);
1131    }
1132
1133    private static List<IStateTransform> transformsForGlDetachShader(GLMessage msg) {
1134        // void glDetachShader(GLuint program, GLuint shader);
1135        int program = msg.getArgs(0).getIntValue(0);
1136        int shader = msg.getArgs(1).getIntValue(0);
1137
1138        IStateTransform transform = new SparseArrayElementRemoveTransform(
1139                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1140                                                GLStateType.PROGRAM_STATE,
1141                                                GLStateType.PROGRAMS,
1142                                                Integer.valueOf(program),
1143                                                GLStateType.ATTACHED_SHADERS),
1144                Integer.valueOf(shader));
1145        return Collections.singletonList(transform);
1146    }
1147
1148    private static List<IStateTransform> transformsForGlGetActiveAttribOrUniform(
1149            GLMessage msg, boolean isAttrib) {
1150        // void glGetActive[Attrib|Uniform](GLuint program, GLuint index, GLsizei bufsize,
1151        //                  GLsizei* length, GLint* size, GLenum* type, GLchar* name);
1152        int program = msg.getArgs(0).getIntValue(0);
1153        int size = msg.getArgs(4).getIntValue(0);
1154        GLEnum type = GLEnum.valueOf(msg.getArgs(5).getIntValue(0));
1155        String name = msg.getArgs(6).getCharValue(0).toStringUtf8();
1156
1157        // The 2nd argument (index) does not give the correct location of the
1158        // attribute/uniform in device. The actual location is obtained from
1159        // the getAttribLocation or getUniformLocation calls. The trace library
1160        // appends this value as an additional last argument to this call.
1161        int location = msg.getArgs(7).getIntValue(0);
1162
1163        GLStateType activeInput;
1164        GLStateType inputName;
1165        GLStateType inputType;
1166        GLStateType inputSize;
1167
1168        if (isAttrib) {
1169            activeInput = GLStateType.ACTIVE_ATTRIBUTES;
1170            inputName = GLStateType.ATTRIBUTE_NAME;
1171            inputType = GLStateType.ATTRIBUTE_TYPE;
1172            inputSize = GLStateType.ATTRIBUTE_SIZE;
1173        } else {
1174            activeInput = GLStateType.ACTIVE_UNIFORMS;
1175            inputName = GLStateType.UNIFORM_NAME;
1176            inputType = GLStateType.UNIFORM_TYPE;
1177            inputSize = GLStateType.UNIFORM_SIZE;
1178        }
1179
1180        IStateTransform addAttribute = new SparseArrayElementAddTransform(
1181                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1182                                                GLStateType.PROGRAM_STATE,
1183                                                GLStateType.PROGRAMS,
1184                                                Integer.valueOf(program),
1185                                                activeInput),
1186                Integer.valueOf(location));
1187        IStateTransform setAttributeName = new PropertyChangeTransform(
1188                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1189                                                GLStateType.PROGRAM_STATE,
1190                                                GLStateType.PROGRAMS,
1191                                                Integer.valueOf(program),
1192                                                activeInput,
1193                                                Integer.valueOf(location),
1194                                                inputName),
1195                name);
1196        IStateTransform setAttributeType = new PropertyChangeTransform(
1197                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1198                                                GLStateType.PROGRAM_STATE,
1199                                                GLStateType.PROGRAMS,
1200                                                Integer.valueOf(program),
1201                                                activeInput,
1202                                                Integer.valueOf(location),
1203                                                inputType),
1204                type);
1205        IStateTransform setAttributeSize = new PropertyChangeTransform(
1206                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1207                                                GLStateType.PROGRAM_STATE,
1208                                                GLStateType.PROGRAMS,
1209                                                Integer.valueOf(program),
1210                                                activeInput,
1211                                                Integer.valueOf(location),
1212                                                inputSize),
1213                Integer.valueOf(size));
1214        return Arrays.asList(addAttribute, setAttributeName, setAttributeType, setAttributeSize);
1215    }
1216
1217    private static List<IStateTransform> transformsForGlGetActiveAttrib(GLMessage msg) {
1218        return transformsForGlGetActiveAttribOrUniform(msg, true);
1219    }
1220
1221    private static List<IStateTransform> transformsForGlGetActiveUniform(GLMessage msg) {
1222        return transformsForGlGetActiveAttribOrUniform(msg, false);
1223    }
1224
1225    private static List<IStateTransform> transformsForGlUniformMatrix(GLMessage msg) {
1226        // void glUniformMatrix[2|3|4]fv(GLint location, GLsizei count, GLboolean transpose,
1227        //                                  const GLfloat *value);
1228        int location = msg.getArgs(0).getIntValue(0);
1229        List<Float> uniforms = msg.getArgs(3).getFloatValueList();
1230
1231        IStateTransform setValues = new PropertyChangeTransform(
1232                new CurrentProgramPropertyAccessor(msg.getContextId(),
1233                                                   GLStateType.ACTIVE_UNIFORMS,
1234                                                   location,
1235                                                   GLStateType.UNIFORM_VALUE),
1236                uniforms);
1237
1238        return Collections.singletonList(setValues);
1239    }
1240
1241    private static List<IStateTransform> transformsForGlUniformv(GLMessage msg, boolean isFloats) {
1242        // void glUniform1fv(GLint location, GLsizei count, const GLfloat *value);
1243        int location = msg.getArgs(0).getIntValue(0);
1244        List<?> uniforms;
1245        if (isFloats) {
1246            uniforms = msg.getArgs(2).getFloatValueList();
1247        } else {
1248            uniforms = msg.getArgs(2).getIntValueList();
1249        }
1250
1251        IStateTransform setValues = new PropertyChangeTransform(
1252                new CurrentProgramPropertyAccessor(msg.getContextId(),
1253                                                   GLStateType.ACTIVE_UNIFORMS,
1254                                                   location,
1255                                                   GLStateType.UNIFORM_VALUE),
1256                uniforms);
1257
1258        return Collections.singletonList(setValues);
1259    }
1260
1261    private static List<IStateTransform> transformsForGlUniform(GLMessage msg, boolean isFloats) {
1262        // void glUniform1f(GLint location, GLfloat v0);
1263        // void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
1264        // ..            3f
1265        // ..            4f
1266        // void glUniform1i(GLint location, GLfloat v0);
1267        // void glUniform2i(GLint location, GLfloat v0, GLfloat v1);
1268        // ..            3i
1269        // ..            4i
1270
1271        int location = msg.getArgs(0).getIntValue(0);
1272        if (location < 0) {
1273            throw new IllegalArgumentException("Argument location cannot be less than 0.");
1274        }
1275        List<?> uniforms;
1276        if (isFloats) {
1277            List<Float> args = new ArrayList<Float>(msg.getArgsCount() - 1);
1278            for (int i = 1; i < msg.getArgsCount(); i++) {
1279                args.add(Float.valueOf(msg.getArgs(1).getFloatValue(0)));
1280            }
1281            uniforms = args;
1282        } else {
1283            List<Integer> args = new ArrayList<Integer>(msg.getArgsCount() - 1);
1284            for (int i = 1; i < msg.getArgsCount(); i++) {
1285                args.add(Integer.valueOf(msg.getArgs(1).getIntValue(0)));
1286            }
1287            uniforms = args;
1288        }
1289
1290        IStateTransform setValues = new PropertyChangeTransform(
1291                new CurrentProgramPropertyAccessor(msg.getContextId(),
1292                                                   GLStateType.ACTIVE_UNIFORMS,
1293                                                   location,
1294                                                   GLStateType.UNIFORM_VALUE),
1295                uniforms);
1296
1297        return Collections.singletonList(setValues);
1298    }
1299
1300    private static List<IStateTransform> transformsForGlCreateShader(GLMessage msg) {
1301        // GLuint glCreateShader(GLenum shaderType);
1302        GLEnum shaderType = GLEnum.valueOf(msg.getArgs(0).getIntValue(0));
1303        int shader = msg.getReturnValue().getIntValue(0);
1304
1305        IStateTransform addShader = new SparseArrayElementAddTransform(
1306                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1307                                                GLStateType.SHADERS),
1308                shader);
1309        IStateTransform setShaderType = new PropertyChangeTransform(
1310                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1311                                                GLStateType.SHADERS,
1312                                                Integer.valueOf(shader),
1313                                                GLStateType.SHADER_TYPE),
1314                shaderType);
1315        return Arrays.asList(addShader, setShaderType);
1316    }
1317
1318    private static List<IStateTransform> transformsForGlDeleteShader(GLMessage msg) {
1319        // void glDeleteShader(GLuint shader);
1320        int shader = msg.getArgs(0).getIntValue(0);
1321
1322        IStateTransform transform = new SparseArrayElementRemoveTransform(
1323                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1324                        GLStateType.SHADERS),
1325                shader);
1326        return Collections.singletonList(transform);
1327    }
1328
1329    private static List<IStateTransform> transformsForGlShaderSource(GLMessage msg) {
1330        // void glShaderSource(GLuint shader, GLsizei count, const GLchar **string,
1331        //                                                          const GLint *length);
1332        // This message is patched up on the device to return a single string as opposed to a
1333        // list of strings
1334        int shader = msg.getArgs(0).getIntValue(0);
1335        String src = msg.getArgs(2).getCharValue(0).toStringUtf8();
1336
1337        IStateTransform transform = new PropertyChangeTransform(
1338                GLPropertyAccessor.makeAccessor(msg.getContextId(),
1339                                                GLStateType.SHADERS,
1340                                                Integer.valueOf(shader),
1341                                                GLStateType.SHADER_SOURCE),
1342                src);
1343        return Collections.singletonList(transform);
1344    }
1345
1346    private static List<IStateTransform> transformsForEglCreateContext(GLMessage msg) {
1347        // void eglCreateContext(int version, int context);
1348        int version = msg.getArgs(0).getIntValue(0);
1349        IGLProperty glState = null;
1350        if (version == 0) {
1351            glState = GLState.createDefaultES1State();
1352        } else {
1353            glState = GLState.createDefaultES2State();
1354        }
1355        IStateTransform transform = new ListElementAddTransform(null, glState);
1356        return Collections.singletonList(transform);
1357    }
1358}
1359