/* ** 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.sdklib.util.SparseArray; import java.nio.ByteBuffer; class GLBuffer implements Cloneable { public final int name; public GLEnum usage; public GLEnum target; /** in SampleView.targetByteOrder */ public ByteBuffer data; public GLBuffer(final int name) { this.name = name; } /** deep copy */ @Override public GLBuffer clone() { try { GLBuffer copy = (GLBuffer) super.clone(); if (data != null) { copy.data = ByteBuffer.allocate(data.capacity()); copy.data.order(SampleView.targetByteOrder); data.position(0); copy.data.put(data); } return copy; } catch (CloneNotSupportedException e) { e.printStackTrace(); assert false; return null; } } } class GLAttribPointer implements Cloneable { public int size; // number of values per vertex public GLEnum type; // data type public int stride; // bytes /** * element stride in bytes, used when fetching from buffer; not for fetching * from user pointer since server already packed elements */ int elemStride; // in bytes /** element size in bytes */ int elemSize; public int ptr; // pointer in debugger server or byte offset into buffer public GLBuffer buffer; public boolean normalized; public boolean enabled; /** deep copy, re-maps buffer into copyBuffers */ public GLAttribPointer clone(SparseArray<GLBuffer> copyBuffers) { try { GLAttribPointer copy = (GLAttribPointer) super.clone(); if (buffer != null) copy.buffer = copyBuffers.get(buffer.name); return copy; } catch (CloneNotSupportedException e) { e.printStackTrace(); assert false; return null; } } } public class GLServerVertex implements Cloneable { public SparseArray<GLBuffer> buffers = new SparseArray<GLBuffer>(); public GLBuffer attribBuffer, indexBuffer; // current binding public GLAttribPointer attribPointers[]; public float defaultAttribs[][]; public GLServerVertex(final int MAX_VERTEX_ATTRIBS) { buffers.append(0, null); attribPointers = new GLAttribPointer[MAX_VERTEX_ATTRIBS]; for (int i = 0; i < attribPointers.length; i++) attribPointers[i] = new GLAttribPointer(); defaultAttribs = new float[MAX_VERTEX_ATTRIBS][4]; for (int i = 0; i < defaultAttribs.length; i++) { defaultAttribs[i][0] = 0; defaultAttribs[i][1] = 0; defaultAttribs[i][2] = 0; defaultAttribs[i][3] = 1; } } /** deep copy */ @Override public GLServerVertex clone() { try { GLServerVertex copy = (GLServerVertex) super.clone(); copy.buffers = new SparseArray<GLBuffer>(buffers.size()); for (int i = 0; i < buffers.size(); i++) if (buffers.valueAt(i) != null) copy.buffers.append(buffers.keyAt(i), buffers.valueAt(i).clone()); else copy.buffers.append(buffers.keyAt(i), null); if (attribBuffer != null) copy.attribBuffer = copy.buffers.get(attribBuffer.name); if (indexBuffer != null) copy.indexBuffer = copy.buffers.get(indexBuffer.name); copy.attribPointers = new GLAttribPointer[attribPointers.length]; for (int i = 0; i < attribPointers.length; i++) copy.attribPointers[i] = attribPointers[i].clone(copy.buffers); copy.defaultAttribs = defaultAttribs.clone(); return copy; } catch (CloneNotSupportedException e) { e.printStackTrace(); assert false; return null; } } /** returns true if processed */ public boolean process(final Message msg) { switch (msg.getFunction()) { case glBindBuffer: glBindBuffer(msg); return true; case glBufferData: glBufferData(msg); return true; case glBufferSubData: glBufferSubData(msg); return true; case glDeleteBuffers: glDeleteBuffers(msg); return true; case glDrawArrays: case glDrawElements: return true; case glDisableVertexAttribArray: glDisableVertexAttribArray(msg); return true; case glEnableVertexAttribArray: glEnableVertexAttribArray(msg); return true; case glGenBuffers: glGenBuffers(msg); return true; case glVertexAttribPointer: glVertexAttribPointer(msg); return true; case glVertexAttrib1f: glVertexAttrib1f(msg); return true; case glVertexAttrib1fv: glVertexAttrib1fv(msg); return true; case glVertexAttrib2f: glVertexAttrib2f(msg); return true; case glVertexAttrib2fv: glVertexAttrib2fv(msg); return true; case glVertexAttrib3f: glVertexAttrib3f(msg); return true; case glVertexAttrib3fv: glVertexAttrib3fv(msg); return true; case glVertexAttrib4f: glVertexAttrib4f(msg); return true; case glVertexAttrib4fv: glVertexAttrib4fv(msg); return true; default: return false; } } // void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) public void glBindBuffer(Message msg) { if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ARRAY_BUFFER) { attribBuffer = buffers.get(msg.getArg1()); if (null != attribBuffer) attribBuffer.target = GLEnum.GL_ARRAY_BUFFER; } else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ELEMENT_ARRAY_BUFFER) { indexBuffer = buffers.get(msg.getArg1()); if (null != indexBuffer) indexBuffer.target = GLEnum.GL_ELEMENT_ARRAY_BUFFER; } else assert false; } // void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const // GLvoid:size:in data, GLenum usage) public void glBufferData(Message msg) { if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ARRAY_BUFFER) { attribBuffer.usage = GLEnum.valueOf(msg.getArg3()); attribBuffer.data = msg.getData().asReadOnlyByteBuffer(); attribBuffer.data.order(SampleView.targetByteOrder); } else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ELEMENT_ARRAY_BUFFER) { indexBuffer.usage = GLEnum.valueOf(msg.getArg3()); indexBuffer.data = msg.getData().asReadOnlyByteBuffer(); indexBuffer.data.order(SampleView.targetByteOrder); } else assert false; } // void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, // GLsizeiptr size, const GLvoid:size:in data) public void glBufferSubData(Message msg) { if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ARRAY_BUFFER) { if (attribBuffer.data.isReadOnly()) { ByteBuffer buffer = ByteBuffer.allocate(attribBuffer.data.capacity()); buffer.order(SampleView.targetByteOrder); buffer.put(attribBuffer.data); attribBuffer.data = buffer; } attribBuffer.data.position(msg.getArg1()); attribBuffer.data.put(msg.getData().asReadOnlyByteBuffer()); } else if (GLEnum.valueOf(msg.getArg0()) == GLEnum.GL_ELEMENT_ARRAY_BUFFER) { if (indexBuffer.data.isReadOnly()) { ByteBuffer buffer = ByteBuffer.allocate(indexBuffer.data.capacity()); buffer.order(SampleView.targetByteOrder); buffer.put(indexBuffer.data); indexBuffer.data = buffer; } indexBuffer.data.position(msg.getArg1()); indexBuffer.data.put(msg.getData().asReadOnlyByteBuffer()); } else assert false; } // void glDeleteBuffers(GLsizei n, const GLuint* buffers) public void glDeleteBuffers(Message msg) { final int n = msg.getArg0(); final ByteBuffer names = msg.getData().asReadOnlyByteBuffer(); names.order(SampleView.targetByteOrder); for (int i = 0; i < n; i++) { final int name = names.getInt(); final GLBuffer buffer = buffers.get(name); for (int j = 0; j < attribPointers.length; j++) if (attribPointers[j].buffer == buffer) { attribPointers[j].buffer = null; attribPointers[j].enabled = false; } if (attribBuffer == buffer) attribBuffer = null; if (indexBuffer == buffer) indexBuffer = null; buffers.remove(name); } } // void glDisableVertexAttribArray(GLuint index) public void glDisableVertexAttribArray(Message msg) { if (msg.getArg0() >= 0 && msg.getArg0() < attribPointers.length) attribPointers[msg.getArg0()].enabled = false; } float fetchConvert(final ByteBuffer src, final GLEnum type, final boolean normalized) { if (GLEnum.GL_FLOAT == type) return Float.intBitsToFloat(src.getInt()); else if (GLEnum.GL_UNSIGNED_INT == type) if (normalized) return (src.getInt() & 0xffffffffL) / (2e32f - 1); else return src.getInt() & 0xffffffffL; else if (GLEnum.GL_INT == type) if (normalized) return (src.getInt() * 2 + 1) / (2e32f - 1); else return src.getInt(); else if (GLEnum.GL_UNSIGNED_SHORT == type) if (normalized) return (src.getShort() & 0xffff) / (2e16f - 1); else return src.getShort() & 0xffff; else if (GLEnum.GL_SHORT == type) if (normalized) return (src.getShort() * 2 + 1) / (2e16f - 1); else return src.getShort(); else if (GLEnum.GL_UNSIGNED_BYTE == type) if (normalized) return (src.get() & 0xff) / (2e8f - 1); else return src.get() & 0xff; else if (GLEnum.GL_BYTE == type) if (normalized) return (src.get() * 2 + 1) / (2e8f - 1); else return src.get(); else if (GLEnum.GL_FIXED == type) if (normalized) return (src.getInt() * 2 + 1) / (2e32f - 1); else return src.getInt() / (2e16f); else assert false; return 0; } static int typeSize(final GLEnum type) { switch (type) { case GL_FLOAT: case GL_UNSIGNED_INT: case GL_INT: case GL_FIXED: return 4; case GL_UNSIGNED_SHORT: case GL_SHORT: return 2; case GL_UNSIGNED_BYTE: case GL_BYTE: return 1; default: assert false; return 0; } } void fetch(final int maxAttrib, final int index, final int dstIdx, final ByteBuffer nonVBO, final float[][] fetchedAttribs) { for (int i = 0; i < maxAttrib; i++) { final GLAttribPointer attrib = attribPointers[i]; int size = 0; if (attrib.enabled) { size = attrib.size; if (null != attrib.buffer) { final ByteBuffer src = attrib.buffer.data; src.position(attrib.ptr + index * attrib.elemStride); for (int j = 0; j < size; j++) fetchedAttribs[i][dstIdx * 4 + j] = fetchConvert(src, attrib.type, attrib.normalized); } else for (int j = 0; j < size; j++) fetchedAttribs[i][dstIdx * 4 + j] = fetchConvert(nonVBO, attrib.type, attrib.normalized); } if (size < 1) fetchedAttribs[i][dstIdx * 4 + 0] = defaultAttribs[i][0]; if (size < 2) fetchedAttribs[i][dstIdx * 4 + 1] = defaultAttribs[i][1]; if (size < 3) fetchedAttribs[i][dstIdx * 4 + 2] = defaultAttribs[i][2]; if (size < 4) fetchedAttribs[i][dstIdx * 4 + 3] = defaultAttribs[i][3]; } } /** * fetches and converts vertex data from buffers, defaults and user pointers * into MessageData; mainly for display use */ public void glDrawArrays(MessageData msgData) { final Message msg = msgData.msg; if (!msg.hasArg7()) return; final int maxAttrib = msg.getArg7(); final int first = msg.getArg1(), count = msg.getArg2(); msgData.attribs = new float[maxAttrib][count * 4]; ByteBuffer arrays = null; if (msg.hasData()) // server sends user pointer attribs { arrays = msg.getData().asReadOnlyByteBuffer(); arrays.order(SampleView.targetByteOrder); } for (int i = 0; i < count; i++) fetch(maxAttrib, first + i, i, arrays, msgData.attribs); assert null == arrays || arrays.remaining() == 0; } // void glDrawElements(GLenum mode, GLsizei count, GLenum type, const // GLvoid* indices) /** * fetches and converts vertex data from buffers, defaults and user pointers * and indices from buffer/pointer into MessageData; mainly for display use */ public void glDrawElements(MessageData msgData) { final Message msg = msgData.msg; if (!msg.hasArg7()) return; final int maxAttrib = msg.getArg7(); final int count = msg.getArg1(); final GLEnum type = GLEnum.valueOf(msg.getArg2()); msgData.attribs = new float[maxAttrib][count * 4]; msgData.indices = new short[count]; ByteBuffer arrays = null, index = null; if (msg.hasData()) // server sends user pointer attribs { arrays = msg.getData().asReadOnlyByteBuffer(); arrays.order(SampleView.targetByteOrder); } if (null == indexBuffer) index = arrays; // server also interleaves user pointer indices else { index = indexBuffer.data; index.position(msg.getArg3()); } if (GLEnum.GL_UNSIGNED_SHORT == type) { for (int i = 0; i < count; i++) { msgData.indices[i] = index.getShort(); fetch(maxAttrib, msgData.indices[i] & 0xffff, i, arrays, msgData.attribs); } } else if (GLEnum.GL_UNSIGNED_BYTE == type) { for (int i = 0; i < count; i++) { msgData.indices[i] = (short) (index.get() & 0xff); fetch(maxAttrib, msgData.indices[i], i, arrays, msgData.attribs); } } else assert false; assert null == arrays || arrays.remaining() == 0; } // void glEnableVertexAttribArray(GLuint index) public void glEnableVertexAttribArray(Message msg) { if (msg.getArg0() >= 0 && msg.getArg0() < attribPointers.length) attribPointers[msg.getArg0()].enabled = true; } // void API_ENTRY(glGenBuffers)(GLsizei n, GLuint:n:out buffers) public void glGenBuffers(Message msg) { final int n = msg.getArg0(); final ByteBuffer buffer = msg.getData().asReadOnlyByteBuffer(); buffer.order(SampleView.targetByteOrder); for (int i = 0; i < n; i++) { final int name = buffer.getInt(); final int index = buffers.indexOfKey(name); if (index < 0) buffers.append(name, new GLBuffer(name)); } } // void glVertexAttribPointer(GLuint index, GLint size, GLenum type, // GLboolean normalized, GLsizei stride, const GLvoid* ptr) public void glVertexAttribPointer(Message msg) { GLAttribPointer attrib = attribPointers[msg.getArg0()]; attrib.size = msg.getArg1(); attrib.type = GLEnum.valueOf(msg.getArg2()); attrib.normalized = msg.getArg3() != 0; attrib.stride = msg.getArg4(); attrib.elemSize = attrib.size * typeSize(attrib.type); if (attrib.stride == 0) attrib.elemStride = attrib.elemSize; else attrib.elemStride = attrib.stride; attrib.ptr = msg.getArg5(); attrib.buffer = attribBuffer; } // void glVertexAttrib1f(GLuint indx, GLfloat x) public void glVertexAttrib1f(Message msg) { glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()), 0, 0, 1); } // void glVertexAttrib1fv(GLuint indx, const GLfloat* values) public void glVertexAttrib1fv(Message msg) { final ByteBuffer values = msg.getData().asReadOnlyByteBuffer(); values.order(SampleView.targetByteOrder); glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(values.getInt()), 0, 0, 1); } // void glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) public void glVertexAttrib2f(Message msg) { glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()), Float.intBitsToFloat(msg.getArg2()), 0, 1); } // void glVertexAttrib2fv(GLuint indx, const GLfloat* values) public void glVertexAttrib2fv(Message msg) { final ByteBuffer values = msg.getData().asReadOnlyByteBuffer(); values.order(SampleView.targetByteOrder); glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(values.getInt()), Float.intBitsToFloat(values.getInt()), 0, 1); } // void glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) public void glVertexAttrib3f(Message msg) { glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()), Float.intBitsToFloat(msg.getArg2()), Float.intBitsToFloat(msg.getArg3()), 1); } // void glVertexAttrib3fv(GLuint indx, const GLfloat* values) public void glVertexAttrib3fv(Message msg) { final ByteBuffer values = msg.getData().asReadOnlyByteBuffer(); values.order(SampleView.targetByteOrder); glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(values.getInt()), Float.intBitsToFloat(values.getInt()), Float.intBitsToFloat(values.getInt()), 1); } public void glVertexAttrib4f(Message msg) { glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(msg.getArg1()), Float.intBitsToFloat(msg.getArg2()), Float.intBitsToFloat(msg.getArg3()), Float.intBitsToFloat(msg.getArg4())); } void glVertexAttrib4f(int indx, float x, float y, float z, float w) { if (indx < 0 || indx >= defaultAttribs.length) return; defaultAttribs[indx][0] = x; defaultAttribs[indx][1] = y; defaultAttribs[indx][2] = z; defaultAttribs[indx][3] = w; } // void glVertexAttrib4fv(GLuint indx, const GLfloat* values) public void glVertexAttrib4fv(Message msg) { final ByteBuffer values = msg.getData().asReadOnlyByteBuffer(); values.order(SampleView.targetByteOrder); glVertexAttrib4f(msg.getArg0(), Float.intBitsToFloat(values.getInt()), Float.intBitsToFloat(values.getInt()), Float.intBitsToFloat(values.getInt()), Float.intBitsToFloat(values.getInt())); } }