com.android.glesv2debugger.CodeGen.java Source code

Java tutorial

Introduction

Here is the source code for com.android.glesv2debugger.CodeGen.java

Source

/*
 ** Copyright 2011, 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.
 */

package com.android.glesv2debugger;

import com.android.glesv2debugger.DebuggerMessage.Message;
import com.android.glesv2debugger.DebuggerMessage.Message.Function;
import com.android.sdklib.util.SparseIntArray;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Shell;

import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;

public class CodeGen implements IRunnableWithProgress {
    private FileWriter codeFile, makeFile, namesHeaderFile, namesSourceFile;
    private PrintWriter code, make, namesHeader, namesSource;
    private FileOutputStream dataOut;
    private SparseIntArray bufferNames, framebufferNames, programNames, textureNames, shaderNames,
            renderbufferNames;

    /** return true if msg was a texture upload */
    private boolean codeGenTextureUpload(final Message msg, final boolean replaceCopy) {
        String s = null;
        switch (msg.getFunction()) {
        case glCompressedTexImage2D:
            s = MessageFormatter.format(msg, true).replace("arg7", "texData");
            break;
        case glCompressedTexSubImage2D:
        case glTexImage2D:
        case glTexSubImage2D:
            s = MessageFormatter.format(msg, true).replace("arg8", "texData");
            break;
        case glCopyTexImage2D:
            if (!replaceCopy) {
                code.write(MessageFormatter.format(msg, true));
                code.write(";CHKERR;\n");
                return true;
            }
            assert msg.getArg2() == msg.getPixelFormat(); // TODO
            s = "//" + MessageFormatter.format(msg, true) + "\n";
            s += String.format("glTexImage2D(%s, %d, %s, %d, %d, %d, %s, %s, texData);CHKERR;",
                    GLEnum.valueOf(msg.getArg0()), msg.getArg1(), GLEnum.valueOf(msg.getArg2()), msg.getArg5(),
                    msg.getArg6(), msg.getArg7(), GLEnum.valueOf(msg.getPixelFormat()),
                    GLEnum.valueOf(msg.getPixelType()));
            break;
        case glCopyTexSubImage2D:
            if (!replaceCopy) {
                code.write(MessageFormatter.format(msg, true));
                code.write(";CHKERR;\n");
                return true;
            }
            // FIXME: check the texture format & type, and convert
            s = "//" + MessageFormatter.format(msg, true) + "\n";
            s += String.format("glTexSubImage2D(%s, %d, %d, %d, %d, %d, %s, %s, texData);CHKERR;",
                    GLEnum.valueOf(msg.getArg0()), msg.getArg1(), msg.getArg2(), msg.getArg3(), msg.getArg6(),
                    msg.getArg7(), GLEnum.valueOf(msg.getPixelFormat()), GLEnum.valueOf(msg.getPixelType()));
            break;
        default:
            return false;
        }

        if (msg.hasData()) {
            final byte[] data = MessageProcessor.lzfDecompressChunks(msg.getData());
            try {
                code.write("{\n");
                code.format("    void * texData = malloc(%d);CHKERR;\n", data.length);
                code.format("    FILE * texFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");CHKERR;\n");
                code.format("    assert(texFile);CHKERR;\n");
                code.format("    fseek(texFile, %d, SEEK_SET);CHKERR;\n", dataOut.getChannel().position());
                dataOut.write(data);
                code.format("    fread(texData, %d, 1, texFile);CHKERR;\n", data.length);
                code.format("    fclose(texFile);CHKERR;\n");
                code.format("    " + s + ";\n");
                code.format("    free(texData);CHKERR;\n");
                code.format("}\n");
            } catch (IOException e) {
                e.printStackTrace();
                assert false;
            }
        } else
            code.write(s.replace("texData", "NULL") + ";\n");
        return true;
    }

    private void codeGenServerState(final GLServerState serverState) {
        code.write("// CodeGenServerState\n");
        for (int i = 0; i < serverState.enableDisables.size(); i++) {
            final GLEnum key = GLEnum.valueOf(serverState.enableDisables.keyAt(i));
            if (serverState.enableDisables.valueAt(i) == 0)
                code.format("glDisable(%s);CHKERR;\n", key);
            else
                code.format("glEnable(%s);CHKERR;\n", key);
        }
        for (int i = 0; i < serverState.lastSetter.size(); i++) {
            final Function key = Function.valueOf(serverState.lastSetter.keyAt(i));
            final Message msg = serverState.lastSetter.valueAt(i);
            if (msg == null) {
                code.format("// %s is default\n", key);
                continue;
            }
            final String s = MessageFormatter.format(msg, true);
            code.write(s);
            code.write(";\n");
        }
        // TODO: stencil and integers
    }

    private void codeGenServerShader(final GLServerShader serverShader) {
        code.write("// CodeGenServerShader\n");
        for (int i = 0; i < serverShader.shaders.size(); i++) {
            final int name = serverShader.shaders.keyAt(i);
            final GLShader shader = serverShader.shaders.valueAt(i);
            final String id = "shader_" + name;
            if (shaderNames.indexOfKey(name) < 0) {
                namesSource.format("GLuint %s = 0;\n", id);
                namesHeader.format("extern GLuint %s;\n", id);
            }
            code.format("%s = glCreateShader(%s);CHKERR;\n", id, shader.type);
            shaderNames.put(name, name);

            if (shader.source != null) {
                final String src = shader.source.replace("\r", "").replace("\n", "\\n\\\n").replace("\"", "\\\"");
                code.format("glShaderSource(%s, 1, (const GLchar *[]){\"%s\"}, NULL);CHKERR;\n", id, src);
                code.format("glCompileShader(%s);CHKERR;\n", id);
            }
        }

        for (int i = 0; i < serverShader.programs.size(); i++) {
            final int name = serverShader.programs.keyAt(i);
            final GLProgram program = serverShader.programs.valueAt(i);
            final String id = "program_" + name;
            if (programNames.indexOfKey(name) < 0) {
                namesSource.format("GLuint %s = 0;\n", id);
                namesHeader.format("extern GLuint %s;\n", id);
            }
            code.format("%s = glCreateProgram();CHKERR;\n", id);
            programNames.put(name, name);
            code.format("glAttachShader(%s, shader_%d);CHKERR;\n", id, program.vert);
            code.format("glAttachShader(%s, shader_%d);CHKERR;\n", id, program.frag);
            code.format("glLinkProgram(%s);CHKERR;\n", id);
            if (serverShader.current == program)
                code.format("glUseProgram(%s);CHKERR;\n", id);
        }
    }

    private void codeGenServerTexture(final GLServerTexture serverTexture, final boolean replaceCopy) {
        code.write("// CodeGenServerTexture\n");
        for (int i = 0; i < serverTexture.textures.size(); i++) {
            final int name = serverTexture.textures.keyAt(i);
            final GLTexture tex = serverTexture.textures.valueAt(i);
            final String id = "texture_" + name;
            if (textureNames.indexOfKey(name) < 0) {
                namesHeader.format("extern GLuint %s;\n", id);
                namesSource.format("GLuint %s = 0;\n", id);
            }
            code.format("%s = 0;\n", id);
            textureNames.put(name, name);

            if (name == 0)
                continue;
            code.format("glGenTextures(1, &%s);CHKERR;\n", id);
            String s = String.format("glBindTexture(%s, texture_%d);CHKERR;\n", tex.target, tex.name);
            code.write(s);
            for (final Message msg : tex.contentChanges) {
                if (codeGenTextureUpload(msg, replaceCopy))
                    continue;
                switch (msg.getFunction()) {
                case glGenerateMipmap:
                    s = MessageFormatter.format(msg, true);
                    break;
                default:
                    assert false;
                }
                code.write(s + ";\n");
            }
            code.format("glTexParameteriv(%s, GL_TEXTURE_WRAP_S, (GLint[]){%s});CHKERR;\n", tex.target, tex.wrapS);
            code.format("glTexParameteriv(%s, GL_TEXTURE_WRAP_T, (GLint[]){%s});CHKERR;\n", tex.target, tex.wrapT);
            code.format("glTexParameteriv(%s, GL_TEXTURE_MIN_FILTER, (GLint[]){%s});CHKERR;\n", tex.target,
                    tex.min);
            code.format("glTexParameteriv(%s, GL_TEXTURE_MAG_FILTER, (GLint[]){%s});CHKERR;\n", tex.target,
                    tex.mag);
        }
        for (int i = 0; i < serverTexture.tmu2D.length; i++) {
            code.format("glActiveTexture(%s);CHKERR;\n", GLEnum.valueOf(GLEnum.GL_TEXTURE0.value + i));
            code.format("glBindTexture(GL_TEXTURE_2D, texture_%d);CHKERR;\n", serverTexture.tmu2D[i]);
        }
        for (int i = 0; i < serverTexture.tmuCube.length; i++) {
            code.format("glActiveTexture(%s);CHKERR;\n", GLEnum.valueOf(GLEnum.GL_TEXTURE0.value + i));
            code.format("glBindTexture(GL_TEXTURE_CUBE_MAP, texture_%d);CHKERR;\n", serverTexture.tmuCube[i]);
        }
        code.format("glActiveTexture(%s);CHKERR;\n", serverTexture.activeTexture);
        if (serverTexture.tex2D == null)
            code.format("glBindTexture(GL_TEXTURE_2D, 0);CHKERR;\n");
        else
            code.format("glBindTexture(GL_TEXTURE_2D, texture_%d);CHKERR;\n", serverTexture.tex2D.name);
        if (serverTexture.texCube == null)
            code.format("glBindTexture(GL_TEXTURE_CUBE_MAP, 0);CHKERR;\n");
        else
            code.format("glBindTexture(GL_TEXTURE_CUBE_MAP, texture_%d);CHKERR;\n", serverTexture.texCube.name);
    }

    private void codeGenBufferData(final ByteBuffer buffer, final String call) {
        ByteBuffer bfr = buffer;
        if (buffer.isReadOnly()) {
            bfr = ByteBuffer.allocate(buffer.capacity());
            bfr.put(buffer);
        }
        final byte[] data = bfr.array();
        try {
            code.write("{\n");
            code.format("    void * bufferData = malloc(%d);\n", data.length);
            code.format("    FILE * bufferFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");\n");
            code.format("    assert(bufferFile);\n");
            code.format("    fseek(bufferFile, %d, SEEK_SET);\n", dataOut.getChannel().position());
            dataOut.write(data);
            code.format("    fread(bufferData, %d, 1, bufferFile);\n", data.length);
            code.format("    fclose(bufferFile);\n");
            code.format("    " + call + ";CHKERR;\n");
            code.format("    free(bufferData);\n");
            code.format("}\n");
        } catch (IOException e) {
            e.printStackTrace();
            assert false;
        }
    }

    private void codeGenServerVertex(final GLServerVertex v) {
        code.write("// CodeGenServerVertex\n");
        for (int i = 0; i < v.buffers.size(); i++) {
            final int name = v.buffers.keyAt(i);
            final String id = "buffer_" + name;
            final GLBuffer buffer = v.buffers.valueAt(i);
            if (bufferNames.indexOfKey(name) < 0) {
                namesHeader.format("extern GLuint %s;\n", id);
                namesSource.format("GLuint %s = 0;\n", id);
            }
            code.format("%s = 0;\n", id);
            bufferNames.put(name, name);
            if (name == 0)
                continue;
            code.format("glGenBuffers(1, &%s);CHKERR;\n", id);
            if (buffer.target != null) {
                code.format("glBindBuffer(%s, %s);CHKERR;\n", buffer.target, id);
                if (buffer.data != null) {
                    String s = String.format("glBufferData(%s, %d, bufferData, %s)", buffer.target,
                            buffer.data.capacity(), buffer.usage);
                    codeGenBufferData(buffer.data, s);
                }
            }
        }
        // TODO: use MAX_VERTEX_ATTRIBS
        for (int i = 0; i < v.defaultAttribs.length; i++)
            code.format("glVertexAttrib4f(%d, %f, %f, %f, %f);CHKERR;\n", i, v.defaultAttribs[i][0],
                    v.defaultAttribs[i][1], v.defaultAttribs[i][2], v.defaultAttribs[i][3]);
        for (int i = 0; i < v.attribPointers.length; i++) {
            final GLAttribPointer att = v.attribPointers[i];
            if (att.type == null)
                continue;
            if (att.buffer != null)
                code.format("glBindBuffer(GL_ARRAY_BUFFER, buffer_%d);CHKERR;\n", att.buffer.name);
            else
                code.format("glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
            code.format("glVertexAttribPointer(%d, %d, %s, %b, %d, (const GLvoid *)%d);CHKERR;\n", i, att.size,
                    att.type, att.normalized, att.stride, att.ptr);
        }
        if (v.attribBuffer != null)
            code.format("glBindBuffer(GL_ARRAY_BUFFER, buffer_%d);CHKERR;\n", v.attribBuffer.name);
        else
            code.write("glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
        if (v.indexBuffer != null)
            code.format("glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_%d);CHKERR;\n", v.indexBuffer.name);
        else
            code.write("glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);CHKERR;\n");
    }

    private void codeGenGenNames(final Message msg) {
        final ByteBuffer names = msg.getData().asReadOnlyByteBuffer();
        names.order(SampleView.targetByteOrder);
        SparseIntArray namesArray = null;
        for (int i = 0; i < msg.getArg0(); i++) {
            String id = "";
            final int name = names.getInt();
            switch (msg.getFunction()) {
            case glGenBuffers:
                id = "buffer";
                namesArray = bufferNames;
                break;
            case glGenFramebuffers:
                id = "framebuffer";
                namesArray = framebufferNames;
                break;
            case glGenRenderbuffers:
                id = "renderbuffer";
                namesArray = renderbufferNames;
                break;
            case glGenTextures:
                id = "texture";
                namesArray = textureNames;
                break;
            default:
                assert false;
            }
            id += "_" + name;
            if (namesArray.indexOfKey(name) < 0) {
                namesHeader.format("extern GLuint %s;\n", id);
                namesSource.format("GLuint %s = 0;\n", id);
            }
            code.format("%s = 0;\n", id);
            namesArray.put(name, name);
            code.format("%s(1, &%s);CHKERR;\n", msg.getFunction(), id);
        }
    }

    private void codeGenDeleteNames(final Message msg) {
        final ByteBuffer names = msg.getData().asReadOnlyByteBuffer();
        names.order(SampleView.targetByteOrder);
        SparseIntArray namesArray = null;
        for (int i = 0; i < msg.getArg0(); i++) {
            String id = null;
            final int name = names.getInt();
            switch (msg.getFunction()) {
            case glDeleteBuffers:
                id = "buffer";
                namesArray = bufferNames;
                break;
            case glDeleteFramebuffers:
                id = "framebuffer";
                namesArray = framebufferNames;
                break;
            case glDeleteRenderbuffers:
                id = "renderbuffer";
                namesArray = renderbufferNames;
                break;
            case glDeleteTextures:
                id = "texture";
                namesArray = textureNames;
                break;
            default:
                assert false;
            }
            id += "_" + name;
            code.format("%s = 0;\n", id);
            namesArray.put(name, 0);
            code.format("%s(1, &%s);CHKERR;\n", msg.getFunction(), id);
        }
    }

    private void codeGenBindNames(final Message msg) {
        String id = null;
        SparseIntArray namesArray = null;
        final int name = msg.getArg1();
        switch (msg.getFunction()) {
        case glBindBuffer:
            id = "buffer";
            namesArray = bufferNames;
            break;
        case glBindFramebuffer:
            id = "framebuffer";
            namesArray = framebufferNames;
            break;
        case glBindRenderbuffer:
            id = "renderbuffer";
            namesArray = renderbufferNames;
            break;
        case glBindTexture:
            id = "texture";
            namesArray = textureNames;
            break;
        default:
            assert false;
        }
        id += "_" + name;
        if (namesArray.indexOfKey(name) < 0) {
            namesHeader.format("extern GLuint %s;\n", id);
            namesSource.format("GLuint %s = 0;\n", id);
        } else if (namesArray.get(name) != name)
            code.format("%s = %d;\n", id, name); // name was deleted
        namesArray.put(name, name);
        code.write(MessageFormatter.format(msg, true));
        code.write(";CHKERR;\n");
    }

    private void codeGenDrawArrays(final GLServerVertex v, final MessageData msgData) throws IOException {
        final int maxAttrib = msgData.msg.getArg7();
        if (maxAttrib < 1) {
            code.write("// no vertex data\n");
            return;
        }
        final byte[] data = msgData.msg.getData().toByteArray();
        final GLEnum mode = GLEnum.valueOf(msgData.msg.getArg0());
        final int first = msgData.msg.getArg1(), count = msgData.msg.getArg2();
        int attribDataStride = 0;
        for (int i = 0; i < maxAttrib; i++) {
            final GLAttribPointer att = v.attribPointers[i];
            if (!att.enabled)
                continue;
            if (att.buffer != null)
                continue;
            attribDataStride += att.elemSize;
        }
        assert attribDataStride * count == data.length;
        code.write("{\n");
        if (attribDataStride > 0) {
            code.format("    FILE * attribFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");CHKERR;\n");
            code.format("    assert(attribFile);CHKERR;\n");
            code.format("    fseek(attribFile, %d, SEEK_SET);CHKERR;\n", dataOut.getChannel().position());
            dataOut.write(data);
            code.format("    char * const attribData = (char *)malloc(%d);\n",
                    first * attribDataStride + data.length);
            code.format("    assert(attribData);\n");
            code.format("    fread(attribData + %d, %d, 1, attribFile);\n", first * attribDataStride, data.length);
            code.format("    fclose(attribFile);\n");
            code.format("    glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
            int attribDataOffset = 0;
            for (int i = 0; i < maxAttrib; i++) {
                final GLAttribPointer att = v.attribPointers[i];
                if (!att.enabled)
                    continue;
                if (att.buffer != null)
                    continue;
                code.format("    glVertexAttribPointer(%d, %d, %s, %b, %d, attribData + %d);CHKERR;\n", i, att.size,
                        att.type, att.normalized, attribDataStride, attribDataOffset);
                attribDataOffset += att.elemSize;
            }
            if (v.attribBuffer != null)
                code.format("    glBindBuffer(GL_ARRAY_BUFFER, %d);CHKERR;\n", v.attribBuffer.name);
        }
        code.format("    glDrawArrays(%s, %d, %d);CHKERR;\n", mode, first, count);
        if (attribDataStride > 0)
            code.format("    free(attribData);CHKERR;\n");
        code.write("};\n");
    }

    private void codeGenDrawElements(final GLServerVertex v, final MessageData msgData) throws IOException {
        final int maxAttrib = msgData.msg.getArg7();
        if (maxAttrib < 1) {
            code.write("// no vertex data\n");
            return;
        }
        final GLEnum mode = GLEnum.valueOf(msgData.msg.getArg0());
        final int count = msgData.msg.getArg1();
        final GLEnum type = GLEnum.valueOf(msgData.msg.getArg2());
        String typeName = "GLubyte";
        if (type == GLEnum.GL_UNSIGNED_SHORT)
            typeName = "GLushort";
        int attribDataStride = 0;
        for (int i = 0; i < maxAttrib; i++) {
            final GLAttribPointer att = v.attribPointers[i];
            if (!att.enabled)
                continue;
            if (att.buffer != null)
                continue;
            attribDataStride += att.elemSize;
        }
        code.write("{\n");
        if (v.indexBuffer == null || attribDataStride > 0) {
            // need to load user pointer indices and/or attributes
            final byte[] element = new byte[attribDataStride];
            final ByteBuffer data = msgData.msg.getData().asReadOnlyByteBuffer();
            data.order(SampleView.targetByteOrder);
            final ByteBuffer indexData = ByteBuffer.allocate(count * GLServerVertex.typeSize(type));
            indexData.order(SampleView.targetByteOrder);
            final ByteBuffer attribData = ByteBuffer.allocate(count * attribDataStride);
            attribData.order(SampleView.targetByteOrder);
            int maxIndex = -1;
            ByteBuffer indexSrc = data;
            if (v.indexBuffer != null) {
                indexSrc = v.indexBuffer.data;
                indexSrc.position(msgData.msg.getArg3());
            }
            indexSrc.order(SampleView.targetByteOrder);
            for (int i = 0; i < count; i++) {
                int index = -1;
                if (type == GLEnum.GL_UNSIGNED_BYTE) {
                    byte idx = indexSrc.get();
                    index = idx & 0xff;
                    indexData.put(idx);
                } else if (type == GLEnum.GL_UNSIGNED_SHORT) {
                    short idx = indexSrc.getShort();
                    index = idx & 0xffff;
                    indexData.putShort(idx);
                } else
                    assert false;
                data.get(element);
                attribData.put(element);
                if (index > maxIndex)
                    maxIndex = index;
            }
            code.format("    FILE * attribFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");CHKERR;\n");
            code.format("    assert(attribFile);CHKERR;\n");
            code.format("    fseek(attribFile, 0x%X, SEEK_SET);CHKERR;\n", dataOut.getChannel().position());
            dataOut.write(indexData.array());
            code.format("    %s * const indexData = (%s *)malloc(%d);\n", typeName, typeName, indexData.capacity());
            code.format("    assert(indexData);\n");
            code.format("    fread(indexData, %d, 1, attribFile);\n", indexData.capacity());
            if (attribDataStride > 0) {
                code.format("    glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
                for (int i = 0; i < maxAttrib; i++) {
                    final GLAttribPointer att = v.attribPointers[i];
                    if (!att.enabled)
                        continue;
                    if (att.buffer != null)
                        continue;
                    code.format("    char * const attrib%d = (char *)malloc(%d);\n", i,
                            att.elemSize * (maxIndex + 1));
                    code.format("    assert(attrib%d);\n", i);
                    code.format("    glVertexAttribPointer(%d, %d, %s, %b, %d, attrib%d);CHKERR;\n", i, att.size,
                            att.type, att.normalized, att.elemSize, i);
                }
                dataOut.write(attribData.array());
                code.format("    for (%s i = 0; i < %d; i++) {\n", typeName, count);
                for (int i = 0; i < maxAttrib; i++) {
                    final GLAttribPointer att = v.attribPointers[i];
                    if (!att.enabled)
                        continue;
                    if (att.buffer != null)
                        continue;
                    code.format("        fread(attrib%d + indexData[i] * %d, %d, 1, attribFile);\n", i,
                            att.elemSize, att.elemSize);
                }
                code.format("    }\n");
                if (v.attribBuffer != null)
                    code.format("    glBindBuffer(GL_ARRAY_BUFFER, %d);CHKERR;\n", v.attribBuffer.name);
            }
            code.format("    fclose(attribFile);\n");
        }
        if (v.indexBuffer != null)
            code.format("    glDrawElements(%s, %d, %s, (const void *)%d);CHKERR;\n", mode, count, type,
                    msgData.msg.getArg3());
        else {
            code.format("    glDrawElements(%s, %d, %s, indexData);CHKERR;\n", mode, count, type);
            code.format("    free(indexData);\n");
        }
        for (int i = 0; i < maxAttrib; i++) {
            final GLAttribPointer att = v.attribPointers[i];
            if (!att.enabled)
                continue;
            if (att.buffer != null)
                continue;
            code.format("    free(attrib%d);\n", i);
        }
        code.write("};\n");
    }

    private void codeGenDraw(final GLServerVertex v, final MessageData msgData) throws IOException {
        final int maxAttrib = msgData.msg.getArg7();
        if (maxAttrib < 1) {
            code.write("// no vertex data\n");
            return;
        }
        final int count = msgData.attribs[0].length / 4;
        final GLEnum mode = GLEnum.valueOf(msgData.msg.getArg0());
        final ByteBuffer attribData = ByteBuffer.allocate(maxAttrib * count * 16);
        attribData.order(SampleView.targetByteOrder);
        for (int i = 0; i < count; i++)
            for (int j = 0; j < maxAttrib; j++)
                for (int k = 0; k < 4; k++)
                    attribData.putFloat(msgData.attribs[j][i * 4 + k]);
        assert attribData.remaining() == 0;
        code.write("{\n");
        code.format("    FILE * attribFile = fopen(\"/sdcard/frame_data.bin\", \"rb\");CHKERR;\n");
        code.format("    assert(attribFile);CHKERR;\n");
        code.format("    fseek(attribFile, 0x%X, SEEK_SET);CHKERR;\n", dataOut.getChannel().position());
        dataOut.write(attribData.array());
        code.format("    char * const attribData = (char *)malloc(%d);\n", attribData.capacity());
        code.format("    assert(attribData);\n");
        code.format("    fread(attribData, %d, 1, attribFile);\n", attribData.capacity());
        code.format("    fclose(attribFile);\n");
        code.format("    glBindBuffer(GL_ARRAY_BUFFER, 0);CHKERR;\n");
        for (int i = 0; i < maxAttrib; i++) {
            final GLAttribPointer att = v.attribPointers[i];
            assert msgData.attribs[i].length == count * 4;
            code.format("    glVertexAttribPointer(%d, %d, GL_FLOAT, GL_FALSE, %d, attribData + %d);CHKERR;\n", i,
                    att.size, maxAttrib * 16, i * 16);
        }
        code.format("    glDrawArrays(%s, 0, %d);CHKERR;\n", mode, count);
        code.format("    free(attribData);\n");
        if (v.attribBuffer != null)
            code.format("    glBindBuffer(GL_ARRAY_BUFFER, %d);CHKERR;\n", v.attribBuffer.name);
        code.write("};\n");
    }

    private void codeGenFunction(final Context ctx, final MessageData msgData) throws IOException {
        final Message msg = msgData.msg;
        String call = MessageFormatter.format(msg, true);
        switch (msg.getFunction()) {
        case glActiveTexture:
        case glAttachShader:
        case glBindAttribLocation:
            break;
        case glBindBuffer:
        case glBindFramebuffer:
        case glBindRenderbuffer:
        case glBindTexture:
            codeGenBindNames(msg);
            return;
        case glBlendColor:
        case glBlendEquation:
        case glBlendEquationSeparate:
        case glBlendFunc:
        case glBlendFuncSeparate:
            break;
        case glBufferData:
            call = MessageFormatter.format(msg, true).replace("arg2", "bufferData");
            codeGenBufferData(msg.getData().asReadOnlyByteBuffer(), call);
            return;
        case glBufferSubData:
            call = MessageFormatter.format(msg, true).replace("arg3", "bufferData");
            codeGenBufferData(msg.getData().asReadOnlyByteBuffer(), call);
            return;
        case glCheckFramebufferStatus:
        case glClear:
        case glClearColor:
        case glClearDepthf:
        case glClearStencil:
        case glColorMask:
        case glCompileShader:
            break;
        case glCompressedTexImage2D:
        case glCompressedTexSubImage2D:
        case glCopyTexImage2D:
        case glCopyTexSubImage2D:
            codeGenTextureUpload(msg, false);
            return;
        case glCreateProgram:
            namesHeader.format("extern GLuint program_%d;\n", msg.getRet());
            namesSource.format("GLuint program_%d = 0;\n", msg.getRet());
            code.format("program_%d = glCreateProgram();CHKERR;\n", msg.getRet());
            return;
        case glCreateShader:
            namesHeader.format("extern GLuint shader_%d;\n", msg.getRet());
            namesSource.format("GLuint shader_%d = 0;\n", msg.getRet());
            code.format("shader_%d = %s;\n", msg.getRet(), call);
            return;
        case glCullFace:
            break;
        case glDeleteBuffers:
        case glDeleteFramebuffers:
        case glDeleteProgram:
            programNames.put(msg.getArg0(), 0);
            break;
        case glDeleteRenderbuffers:
            codeGenDeleteNames(msg);
            return;
        case glDeleteShader:
            shaderNames.put(msg.getArg0(), 0);
            return;
        case glDeleteTextures:
            codeGenDeleteNames(msg);
            return;
        case glDepthFunc:
        case glDepthMask:
        case glDepthRangef:
        case glDetachShader:
        case glDisable:
        case glDisableVertexAttribArray:
            break;
        case glDrawArrays:
            // CodeGenDraw(ctx.serverVertex, msgData);
            codeGenDrawArrays(ctx.serverVertex, msgData);
            return;
        case glDrawElements:
            // CodeGenDraw(ctx.serverVertex, msgData);
            codeGenDrawElements(ctx.serverVertex, msgData);
            return;
        case glEnable:
        case glEnableVertexAttribArray:
        case glFinish:
        case glFlush:
        case glFramebufferRenderbuffer:
        case glFramebufferTexture2D:
        case glFrontFace:
            break;
        case glGenBuffers:
            codeGenGenNames(msg);
            return;
        case glGenerateMipmap:
            break;
        case glGenFramebuffers:
        case glGenRenderbuffers:
        case glGenTextures:
            codeGenGenNames(msg);
            return;
        case glGetActiveAttrib:
        case glGetActiveUniform:
        case glGetAttachedShaders:
            break;
        case glGetAttribLocation:
            call = String.format("assert(%d == %s)", msg.getRet(), call);
            break;
        case glGetBooleanv:
        case glGetBufferParameteriv:
            return; // TODO
        case glGetError:
            code.write("CHKERR;\n");
            return;
        case glGetFloatv:
        case glGetFramebufferAttachmentParameteriv:
        case glGetIntegerv:
        case glGetProgramiv:
        case glGetProgramInfoLog:
        case glGetRenderbufferParameteriv:
        case glGetShaderiv:
        case glGetShaderInfoLog:
        case glGetShaderPrecisionFormat:
        case glGetShaderSource:
        case glGetString:
        case glGetTexParameterfv:
        case glGetTexParameteriv:
        case glGetUniformfv:
        case glGetUniformiv:
            return;
        case glGetUniformLocation:
            call = String.format("assert(%d == %s)", msg.getRet(), call);
            break;
        case glGetVertexAttribfv:
        case glGetVertexAttribiv:
        case glGetVertexAttribPointerv:
            return; // TODO
        case glHint:
        case glIsBuffer:
        case glIsEnabled:
        case glIsFramebuffer:
        case glIsProgram:
        case glIsRenderbuffer:
        case glIsShader:
        case glIsTexture:
        case glLineWidth:
        case glLinkProgram:
        case glPixelStorei:
        case glPolygonOffset:
            break;
        case glReadPixels:
            return; // TODO
        case glReleaseShaderCompiler:
        case glRenderbufferStorage:
        case glSampleCoverage:
        case glScissor:
            break;
        case glShaderBinary:
            return; // TODO
        case glShaderSource:
            call = String.format("glShaderSource(shader_%d, 1, (const char * []){\"%s\"}, NULL)", msg.getArg0(),
                    msg.getData().toStringUtf8().replace("\r", "").replace("\n", "\\n\\\n").replace("\"", "\\\""));
            break;
        case glStencilFunc:
        case glStencilFuncSeparate:
        case glStencilMask:
        case glStencilMaskSeparate:
        case glStencilOp:
        case glStencilOpSeparate:
            break;
        case glTexImage2D:
            codeGenTextureUpload(msg, false);
            return;
        case glTexParameterf:
            break;
        case glTexParameterfv:
            return; // TODO
        case glTexParameteri:
            break;
        case glTexParameteriv:
            return; // TODO
        case glTexSubImage2D:
            codeGenTextureUpload(msg, false);
            return;
        case glUniform1f:
        case glUniform1fv:
        case glUniform1i:
        case glUniform1iv:
        case glUniform2f:
        case glUniform2fv:
        case glUniform2i:
        case glUniform2iv:
        case glUniform3f:
        case glUniform3fv:
        case glUniform3i:
        case glUniform3iv:
        case glUniform4f:
        case glUniform4fv:
        case glUniform4i:
        case glUniform4iv:
        case glUniformMatrix2fv:
        case glUniformMatrix3fv:
        case glUniformMatrix4fv:
        case glUseProgram:
        case glValidateProgram:
        case glVertexAttrib1f:
        case glVertexAttrib1fv:
        case glVertexAttrib2f:
        case glVertexAttrib2fv:
        case glVertexAttrib3f:
        case glVertexAttrib3fv:
        case glVertexAttrib4f:
        case glVertexAttrib4fv:
            break;
        case glVertexAttribPointer:
            // if it's user pointer, then CodeGenDrawArrays/Elements will
            // replace it with loaded data just before the draw
            call = call.replace("arg5", "(const void *)0x" + Integer.toHexString(msg.getArg5()));
            break;
        case glViewport:
            break;
        case eglSwapBuffers:
            return;
        default:
            assert false;
            return;
        }
        if (call.indexOf("glEnable(/*cap*/ GL_TEXTURE_2D)") >= 0)
            return;
        else if (call.indexOf("glDisable(/*cap*/ GL_TEXTURE_2D)") >= 0)
            return;
        else if (call.indexOf("glActiveTexture(/*texture*/ GL_TEXTURE_2D)") >= 0)
            return;
        code.write(call + ";CHKERR;\n");
    }

    private void codeGenSetup(final Context ctx) {
        try {
            codeFile = new FileWriter("frame_setup.cpp", false);
            code = new PrintWriter(codeFile);
            dataOut = new FileOutputStream("frame_data.bin", false);
            namesHeaderFile = new FileWriter("frame_names.h", false);
            namesHeader = new PrintWriter(namesHeaderFile);
            namesSourceFile = new FileWriter("frame_names.cpp", false);
            namesSource = new PrintWriter(namesSourceFile);
        } catch (IOException e) {
            e.printStackTrace();
            assert false;
        }
        bufferNames = new SparseIntArray();
        framebufferNames = new SparseIntArray();
        programNames = new SparseIntArray();
        textureNames = new SparseIntArray();
        shaderNames = new SparseIntArray();
        renderbufferNames = new SparseIntArray();

        namesHeader.write("#include <stdlib.h>\n");
        namesHeader.write("#include <stdio.h>\n");
        namesHeader.write("#include <assert.h>\n");
        namesHeader.write("#include <GLES2/gl2.h>\n");
        namesHeader.write("#include <GLES2/gl2ext.h>\n");
        namesHeader.write("#define CHKERR assert(GL_NO_ERROR == glGetError());/**/\n");
        namesHeader.write("void FrameSetup();\n");
        namesHeader.write("extern const unsigned int FrameCount;\n");
        namesHeader.write("extern const GLuint program_0;\n");

        namesSource.write("/*\n" + " * Copyright (C) 2011 The Android Open Source Project\n" + " *\n"
                + " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"
                + " * you may not use this file except in compliance with the License.\n"
                + " * You may obtain a copy of the License at\n" + " *\n"
                + " *      http://www.apache.org/licenses/LICENSE-2.0\n" + " *\n"
                + " * Unless required by applicable law or agreed to in writing, software\n"
                + " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"
                + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
                + " * See the License for the specific language governing permissions and\n"
                + " * limitations under the License.\n" + " */\n" + "\n" + "#include <stdlib.h>\n"
                + "#include <stdio.h>\n" + "\n" + "#include <EGL/egl.h>\n" + "#include <GLES2/gl2.h>\n"
                + "#include <GLES2/gl2ext.h>\n" + "\n" + "#include <ui/FramebufferNativeWindow.h>\n"
                + "#include <ui/EGLUtils.h>\n" + "\n" + "#include <private/ui/android_natives_priv.h>\n" + "\n"
                + "#include <surfaceflinger/Surface.h>\n" + "#include <surfaceflinger/ISurface.h>\n"
                + "#include <surfaceflinger/SurfaceComposerClient.h>\n" + "\n" + "using namespace android;\n" + "\n"
                + "static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE)\n" + "{\n"
                + "    if (returnVal != EGL_TRUE) {\n"
                + "        fprintf(stderr, \"%s() returned %d\\n\", op, returnVal);\n" + "    }\n" + "\n"
                + "    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error\n"
                + "            = eglGetError()) {\n"
                + "        fprintf(stderr, \"after %s() eglError %s (0x%x)\\n\", op, EGLUtils::strerror(error),\n"
                + "                error);\n" + "    }\n" + "}\n" + "\n" + "static EGLDisplay dpy;\n"
                + "static EGLSurface surface;\n" + "\n" + "#include \"frame_names.h\"\n"
                + "const GLuint program_0 = 0;\n" + "int main(int argc, char** argv)\n" + "{\n"
                + "    EGLBoolean returnValue;\n" + "    EGLConfig myConfig = {0};\n" + "\n"
                + "    EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };\n"
                + "    EGLint majorVersion;\n" + "    EGLint minorVersion;\n" + "    EGLContext context;\n"
                + "    EGLint w, h;\n" + "\n" + "\n" + "    checkEglError(\"<init>\");\n"
                + "    dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);\n" + "    checkEglError(\"eglGetDisplay\");\n"
                + "    if (dpy == EGL_NO_DISPLAY) {\n"
                + "        printf(\"eglGetDisplay returned EGL_NO_DISPLAY.\\n\");\n" + "        return 0;\n"
                + "    }\n" + "\n" + "    returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);\n"
                + "    checkEglError(\"eglInitialize\", returnValue);\n" + "    if (returnValue != EGL_TRUE) {\n"
                + "        printf(\"eglInitialize failed\\n\");\n" + "        return 0;\n" + "    }\n" + "\n"
                + "    sp<SurfaceComposerClient> spClient;\n" + "    sp<SurfaceControl> spControl;\n"
                + "    sp<Surface> spSurface;\n" + "\n" + "    // create a client to surfaceflinger\n"
                + "    spClient = new SurfaceComposerClient();\n" + "\n"
                + "    spControl = spClient->createSurface(getpid(), 0, 1280, 752, PIXEL_FORMAT_RGBX_8888);\n"
                + "    spClient->openTransaction();\n" + "    spControl->setLayer(350000);\n"
                + "    spControl->show();\n" + "    spClient->closeTransaction();\n" + "\n"
                + "    spSurface = spControl->getSurface();\n"
                + "    EGLNativeWindowType window = spSurface.get();\n" + "\n"
                + "    printf(\"window=%p\\n\", window);\n" + "    EGLint attrib_list[] = {\n"
                + "        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,\n"
                + "        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,\n" + "        EGL_BUFFER_SIZE, 32,\n"
                + "        EGL_RED_SIZE, 8,\n" + "        EGL_GREEN_SIZE, 8,\n" + "        EGL_BLUE_SIZE, 8,\n"
                + "        EGL_NONE\n" + "    };\n" + "\n" + "    EGLConfig configs[12] = {0};\n"
                + "    int num_config = -1;\n"
                + "    eglChooseConfig(dpy, attrib_list, configs, sizeof(configs) / sizeof(*configs), &num_config);\n"
                + "    printf(\"eglChooseConfig %d \\n\", num_config);\n" + "\n"
                + "    surface = eglCreateWindowSurface(dpy, configs[0], window, NULL);\n"
                + "    checkEglError(\"eglCreateWindowSurface\");\n" + "    if (surface == EGL_NO_SURFACE) {\n"
                + "        printf(\"gelCreateWindowSurface failed.\\n\");\n" + "        return 0;\n" + "    }\n"
                + "\n" + "    context = eglCreateContext(dpy, configs[0], EGL_NO_CONTEXT, context_attribs);\n"
                + "    checkEglError(\"eglCreateContext\");\n" + "    if (context == EGL_NO_CONTEXT) {\n"
                + "        printf(\"eglCreateContext failed\\n\");\n" + "        return 0;\n" + "    }\n"
                + "    printf(\"context=%p \\n\", context);\n" + "\n"
                + "    returnValue = eglMakeCurrent(dpy, surface, surface, context);\n"
                + "    checkEglError(\"eglMakeCurrent\", returnValue);\n" + "    if (returnValue != EGL_TRUE) {\n"
                + "        return 0;\n" + "    }\n" + "\n" + "    glClearColor(1,1,1,1);\n"
                + "    glClear(GL_COLOR_BUFFER_BIT);\n" + "\n" + "    FrameSetup();\n" + "    while (true)\n"
                + "        for (unsigned int i = 0; i < FrameCount; i++) {\n" + "            Frames[i]();\n"
                + "            eglSwapBuffers(dpy, surface);\n"
                + "            printf(\"press ENTER after Frame%d \\n\", i);\n" + "            getchar();\n"
                + "        }\n" + "\n" + "    return 0;\n" + "}");

        code.write("#include \"frame_names.h\"\n");
        code.write("void FrameSetup(){\n");

        codeGenServerState(ctx.serverState);
        codeGenServerShader(ctx.serverShader);
        codeGenServerTexture(ctx.serverTexture, true);
        codeGenServerVertex(ctx.serverVertex);

        code.write("}\n");

        try {
            codeFile.close();
            makeFile = new FileWriter("Android.mk", false);
            make = new PrintWriter(makeFile);
            make.write("LOCAL_PATH:= $(call my-dir)\n" + "include $(CLEAR_VARS)\n" + "LOCAL_SRC_FILES := \\\n");
        } catch (IOException e) {
            e.printStackTrace();
            assert false;
        }
    }

    private void codeGenCleanup() {
        make.write("    frame_setup.cpp \\\n");
        make.write("    frame_names.cpp \\\n");
        make.write("#\n");
        make.write("LOCAL_SHARED_LIBRARIES := \\\n" + "    libcutils \\\n" + "    libutils \\\n" + "    libEGL \\\n"
                + "    libGLESv2 \\\n" + "    libui \\\n" + "    libhardware \\\n" + "    libgui\n" + "\n"
                + "LOCAL_MODULE:= gles2dbg\n" + "\n" + "LOCAL_MODULE_TAGS := optional\n" + "\n"
                + "LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -O0 -g -DDEBUG -UNDEBUG\n" + "\n"
                + "include $(BUILD_EXECUTABLE)");
        try {
            dataOut.flush();
            dataOut.close();
            codeFile.close();
            makeFile.close();
            namesHeaderFile.close();
            namesSourceFile.close();
        } catch (IOException e) {
            e.printStackTrace();
            assert false;
        }
        dataOut = null;
        code = null;
        codeFile = null;
        make = null;
        makeFile = null;

        bufferNames = null;
        framebufferNames = null;
        programNames = null;
        textureNames = null;
        shaderNames = null;
        renderbufferNames = null;
    }

    private DebugContext dbgCtx;
    private int count;
    private IProgressMonitor progress;

    @Override
    public void run(IProgressMonitor monitor) {
        progress.beginTask("CodeGenFrames", count + 2);
        Context ctx = dbgCtx.getFrame(0).startContext.clone();
        codeGenSetup(ctx);
        progress.worked(1);
        for (int i = 0; i < count; i++) {
            try {
                codeFile = new FileWriter("frame" + i + ".cpp", false);
                code = new PrintWriter(codeFile);
            } catch (IOException e1) {
                e1.printStackTrace();
                assert false;
            }
            make.format("    frame%d.cpp \\\n", i);

            code.write("#include \"frame_names.h\"\n");
            code.format("void Frame%d(){\n", i);
            final Frame frame = dbgCtx.getFrame(i);
            for (int j = 0; j < frame.size(); j++) {
                final MessageData msgData = frame.get(j);
                code.format("/* frame function %d: %s %s*/\n", j, msgData.msg.getFunction(),
                        MessageFormatter.format(msgData.msg, false));
                ctx.processMessage(msgData.msg);
                try {
                    codeGenFunction(ctx, msgData);
                } catch (IOException e) {
                    e.printStackTrace();
                    assert false;
                }
            }
            code.write("}\n");
            try {
                codeFile.close();
            } catch (IOException e) {
                e.printStackTrace();
                assert false;
            }
            progress.worked(1);
        }
        for (int i = 0; i < count; i++)
            namesHeader.format("void Frame%d();\n", i);
        namesHeader.format("extern void (* Frames[%d])();\n", count);
        namesSource.format("void (* Frames[%d])() = {\n", count);
        for (int i = 0; i < count; i++) {
            namesSource.format("    Frame%d,\n", i);
        }
        namesSource.write("};\n");
        namesSource.format("const unsigned int FrameCount = %d;\n", count);
        codeGenCleanup();
        progress.worked(1);
    }

    void codeGenFrames(final DebugContext dbgCtx, int count, final Shell shell) {
        this.dbgCtx = dbgCtx;
        this.count = count;
        ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell);
        this.progress = dialog.getProgressMonitor();
        try {
            dialog.run(false, true, this);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            assert false;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.dbgCtx = null;
        this.count = 0;
        progress = null;
    }

    void codeGenFrame(final Frame frame) {
        Context ctx = frame.startContext.clone();
        codeGenSetup(ctx);
        try {
            codeFile = new FileWriter("frame0.cpp", false);
            code = new PrintWriter(codeFile);
        } catch (IOException e1) {
            e1.printStackTrace();
            assert false;
        }
        make.format("    frame0.cpp \\\n");
        code.write("#include \"frame_names.h\"\n");
        code.format("void Frame0(){\n");
        for (int i = 0; i < frame.size(); i++) {
            final MessageData msgData = frame.get(i);
            code.format("/* frame function %d: %s %s*/\n", i, msgData.msg.getFunction(),
                    MessageFormatter.format(msgData.msg, false));
            ctx.processMessage(msgData.msg);
            try {
                codeGenFunction(ctx, msgData);
            } catch (IOException e) {
                e.printStackTrace();
                assert false;
            }
        }
        code.write("}\n");
        namesHeader.write("void Frame0();\n");
        namesHeader.write("extern void (* Frames[1])();\n");
        namesSource.write("void (* Frames[1])() = {Frame0};\n");
        namesSource.write("const unsigned int FrameCount = 1;\n");
        codeGenCleanup();
    }
}