| /* |
| * Copyright (C) 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.ide.eclipse.gltrace; |
| |
| import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage; |
| import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.Function; |
| import com.android.ide.eclipse.gltrace.format.GLAPISpec; |
| import com.android.ide.eclipse.gltrace.format.GLMessageFormatter; |
| import com.android.ide.eclipse.gltrace.model.GLCall; |
| import com.android.ide.eclipse.gltrace.model.GLFrame; |
| import com.android.ide.eclipse.gltrace.model.GLTrace; |
| import com.android.ide.eclipse.gltrace.state.transforms.StateTransformFactory; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.RandomAccessFile; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| public class TraceFileParserTask implements IRunnableWithProgress { |
| private static final TraceFileReader sReader = new TraceFileReader(); |
| |
| private static final GLMessageFormatter sGLMessageFormatter = |
| new GLMessageFormatter(GLAPISpec.getSpecs()); |
| |
| private String mTraceFilePath; |
| private RandomAccessFile mFile; |
| |
| private List<GLCall> mGLCalls; |
| private Set<Integer> mGLContextIds; |
| |
| private GLTrace mTrace; |
| |
| /** |
| * Construct a GL Trace file parser. |
| * @param path path to trace file |
| */ |
| public TraceFileParserTask(String path) { |
| try { |
| mFile = new RandomAccessFile(path, "r"); //$NON-NLS-1$ |
| } catch (FileNotFoundException e) { |
| throw new IllegalArgumentException(e); |
| } |
| |
| mTraceFilePath = path; |
| mGLCalls = new ArrayList<GLCall>(); |
| mGLContextIds = new TreeSet<Integer>(); |
| } |
| |
| private void addMessage(int index, long traceFileOffset, GLMessage msg, long startTime) { |
| String formattedMsg; |
| try { |
| formattedMsg = sGLMessageFormatter.formatGLMessage(msg); |
| } catch (Exception e) { |
| formattedMsg = String.format("%s()", msg.getFunction().toString()); //$NON-NLS-1$ |
| } |
| |
| GLCall c = new GLCall(index, |
| startTime, |
| traceFileOffset, |
| formattedMsg, |
| msg.getFunction(), |
| msg.hasFb(), |
| msg.getContextId(), |
| msg.getDuration(), |
| msg.getThreadtime()); |
| |
| addProperties(c, msg); |
| |
| try { |
| c.setStateTransformations(StateTransformFactory.getTransformsFor(msg)); |
| } catch (Exception e) { |
| c.setStateTransformationCreationError(e.getMessage()); |
| GlTracePlugin.getDefault().logMessage("Error while creating transformations for " |
| + c.toString() + ":"); |
| GlTracePlugin.getDefault().logMessage(e.getMessage()); |
| } |
| |
| mGLCalls.add(c); |
| mGLContextIds.add(Integer.valueOf(c.getContextId())); |
| } |
| |
| /** Save important values from the {@link GLMessage} in the {@link GLCall} as properties. */ |
| private void addProperties(GLCall c, GLMessage msg) { |
| switch (msg.getFunction()) { |
| case glPushGroupMarkerEXT: |
| // void PushGroupMarkerEXT(sizei length, const char *marker); |
| // save the marker name |
| c.addProperty(GLCall.PROPERTY_MARKERNAME, |
| msg.getArgs(1).getCharValue(0).toStringUtf8()); |
| break; |
| case glVertexAttribPointerData: |
| // void glVertexAttribPointerData(GLuint indx, GLint size, GLenum type, |
| // GLboolean normalized, GLsizei stride, const GLvoid* ptr, |
| // int minIndex, int maxIndex) |
| c.addProperty(GLCall.PROPERTY_VERTEX_ATTRIB_POINTER_SIZE, |
| Integer.valueOf(msg.getArgs(1).getIntValue(0))); |
| c.addProperty(GLCall.PROPERTY_VERTEX_ATTRIB_POINTER_TYPE, |
| GLEnum.valueOf(msg.getArgs(2).getIntValue(0))); |
| c.addProperty(GLCall.PROPERTY_VERTEX_ATTRIB_POINTER_DATA, |
| msg.getArgs(5).getRawBytes(0).toByteArray()); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * Parse the entire file and create a {@link GLTrace} object that can be retrieved |
| * using {@link #getTrace()}. |
| */ |
| @Override |
| public void run(IProgressMonitor monitor) throws InvocationTargetException, |
| InterruptedException { |
| long fileLength; |
| try { |
| fileLength = mFile.length(); |
| } catch (IOException e1) { |
| fileLength = 0; |
| } |
| |
| monitor.beginTask("Parsing OpenGL Trace File", |
| fileLength > 0 ? 100 : IProgressMonitor.UNKNOWN); |
| |
| List<GLFrame> glFrames = null; |
| |
| try { |
| GLMessage msg = null; |
| int msgCount = 0; |
| long filePointer = mFile.getFilePointer(); |
| int percentParsed = 0; |
| |
| // counters that maintain some statistics about the trace messages |
| long minTraceStartTime = Long.MAX_VALUE; |
| |
| while ((msg = sReader.getMessageAtOffset(mFile, -1)) != null) { |
| if (minTraceStartTime > msg.getStartTime()) { |
| minTraceStartTime = msg.getStartTime(); |
| } |
| |
| addMessage(msgCount, filePointer, msg, msg.getStartTime() - minTraceStartTime); |
| |
| filePointer = mFile.getFilePointer(); |
| msgCount++; |
| |
| if (monitor.isCanceled()) { |
| throw new InterruptedException(); |
| } |
| |
| if (fileLength > 0) { |
| int percentParsedNow = (int)((filePointer * 100) / fileLength); |
| monitor.worked(percentParsedNow - percentParsed); |
| percentParsed = percentParsedNow; |
| } |
| } |
| |
| if (mGLContextIds.size() > 1) { |
| // if there are multiple contexts, then the calls may arrive at the |
| // host out of order. So we perform a sort based on the invocation time. |
| Collections.sort(mGLCalls, new Comparator<GLCall>() { |
| @Override |
| public int compare(GLCall c1, GLCall c2) { |
| long diff = (c1.getStartTime() - c2.getStartTime()); |
| |
| // We could return diff casted to an int. But in Java, casting |
| // from a long to an int truncates the bits and will not preserve |
| // the sign. So we resort to comparing the diff to 0 and returning |
| // the sign. |
| if (diff == 0) { |
| return 0; |
| } else if (diff > 0) { |
| return 1; |
| } else { |
| return -1; |
| } |
| } |
| }); |
| |
| // reassign indices after sorting |
| for (int i = 0; i < mGLCalls.size(); i++) { |
| mGLCalls.get(i).setIndex(i); |
| } |
| } |
| |
| glFrames = createFrames(mGLCalls); |
| } catch (Exception e) { |
| throw new InvocationTargetException(e); |
| } finally { |
| try { |
| mFile.close(); |
| } catch (IOException e) { |
| // ignore exception while closing file |
| } |
| monitor.done(); |
| } |
| |
| File f = new File(mTraceFilePath); |
| TraceFileInfo fileInfo = new TraceFileInfo(mTraceFilePath, f.length(), f.lastModified()); |
| mTrace = new GLTrace(fileInfo, glFrames, mGLCalls, new ArrayList<Integer>(mGLContextIds)); |
| } |
| |
| /** Assign GL calls to GL Frames. */ |
| private List<GLFrame> createFrames(List<GLCall> calls) { |
| List<GLFrame> glFrames = new ArrayList<GLFrame>(); |
| int startCallIndex = 0; |
| int frameIndex = 0; |
| |
| for (int i = 0; i < calls.size(); i++) { |
| GLCall c = calls.get(i); |
| if (c.getFunction() == Function.eglSwapBuffers) { |
| glFrames.add(new GLFrame(frameIndex, startCallIndex, i + 1)); |
| startCallIndex = i + 1; |
| frameIndex++; |
| } |
| } |
| |
| // assign left over calls at the end to the last frame |
| if (startCallIndex != mGLCalls.size()) { |
| glFrames.add(new GLFrame(frameIndex, startCallIndex, mGLCalls.size())); |
| } |
| |
| return glFrames; |
| } |
| |
| /** |
| * Retrieve the trace object constructed from messages in the trace file. |
| */ |
| public GLTrace getTrace() { |
| return mTrace; |
| } |
| } |