diff options
author | Siva Velusamy <vsiva@google.com> | 2011-11-30 15:05:37 -0800 |
---|---|---|
committer | Siva Velusamy <vsiva@google.com> | 2011-12-02 15:30:17 -0800 |
commit | 0469dd6d55fa331bfd7de9431da98b6340d82271 (patch) | |
tree | 02185748d9cfeafb5fed64e715b11abff37eaeb9 /opengl/libs/GLES_trace/tools/genapi.py | |
parent | 484bc2727c7ce5098ad229ce4feb3c230dfd109d (diff) | |
download | frameworks_native-0469dd6d55fa331bfd7de9431da98b6340d82271.zip frameworks_native-0469dd6d55fa331bfd7de9431da98b6340d82271.tar.gz frameworks_native-0469dd6d55fa331bfd7de9431da98b6340d82271.tar.bz2 |
glestrace: Framework for GLES tracing library
This patch provides a framework for tracing GLES 1.0 and 2.0
functions. It is missing a lot of features, but here are the
things it accomplishes:
- Stop building the glesv2dbg library, and build the
glestrace library instead.
- Replace the hooks for glesv2dbg with the ones for glestrace.
- Add the basics for the trace library. Currently, this
traces all GL functions, but not all required data is
sent for all the functions. As a result, it will not
be possible to reconstruct the entire GL state on the
host side.
The files gltrace.pb.* and gltrace_api.* are both generated
using the tools/genapi.py script.
Change-Id: Id60a468f7278657f008bc6ea1df01f9bdfecfdd3
Diffstat (limited to 'opengl/libs/GLES_trace/tools/genapi.py')
-rwxr-xr-x | opengl/libs/GLES_trace/tools/genapi.py | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/opengl/libs/GLES_trace/tools/genapi.py b/opengl/libs/GLES_trace/tools/genapi.py new file mode 100755 index 0000000..3c47a5f --- /dev/null +++ b/opengl/libs/GLES_trace/tools/genapi.py @@ -0,0 +1,386 @@ +#!/usr/bin/env python +# +# Copyright (C) 2011 Google Inc. +# +# 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. +# +# ABOUT +# This script is used to generate the trace implementations of all +# OpenGL calls. When executed, it reads the specs for the OpenGL calls +# from the files GLES2/gl2_api.in, GLES2/gl2ext_api.in, GLES_CM/gl_api.in, +# and GLES_CM/glext_api.in, and generates trace versions for all the +# defined functions. +# +# PREREQUISITES +# To generate C++ files, this script uses the 'pyratemp' template +# module. The only reason to use pyratemp is that it is extremly +# simple to install: +# $ wget http://www.simple-is-better.org/template/pyratemp-current/pyratemp.py +# Put the file in the GLES2_trace/tools folder, or update PYTHONPATH +# to point to wherever it was downloaded. +# +# USAGE +# $ cd GLES2_trace - run the program from GLES2_trace folder +# $ ./tools/genapi.py - generates a .cpp and .h file +# $ mv *.cpp *.h src/ - move the generated files into the src folder + +import sys +import re +import pyratemp + +# Constants corresponding to the protobuf DataType.Type +class DataType: + def __init__(self, name): + self.name = name + + def __str__(self): + if self.name == "pointer": # pointers map to the INT DataType + return "INT" + return self.name.upper() + + def getProtobufCall(self): + if self.name == "void": + raise ValueError("Attempt to set void value") + elif self.name == "char" or self.name == "byte" \ + or self.name == "pointer" or self.name == "enum": + return "add_intvalue((int)" + elif self.name == "int": + return "add_intvalue(" + elif self.name == "float": + return "add_floatvalue(" + elif self.name == "bool": + return "add_boolvalue(" + else: + raise ValueError("Unknown value type %s" % self.name) + +DataType.VOID = DataType("void") +DataType.CHAR = DataType("char") +DataType.BYTE = DataType("byte") +DataType.ENUM = DataType("enum") +DataType.BOOL = DataType("bool") +DataType.INT = DataType("int") +DataType.FLOAT = DataType("float") +DataType.POINTER = DataType("pointer") + +# mapping of GL types to protobuf DataType +GL2PROTOBUF_TYPE_MAP = { + "GLvoid":DataType.VOID, + "void":DataType.VOID, + "GLchar":DataType.CHAR, + "GLenum":DataType.ENUM, + "GLboolean":DataType.BOOL, + "GLbitfield":DataType.INT, + "GLbyte":DataType.BYTE, + "GLshort":DataType.INT, + "GLint":DataType.INT, + "int":DataType.INT, + "GLsizei":DataType.INT, + "GLubyte":DataType.BYTE, + "GLushort":DataType.INT, + "GLuint":DataType.INT, + "GLfloat":DataType.FLOAT, + "GLclampf":DataType.FLOAT, + "GLfixed":DataType.INT, + "GLclampx":DataType.INT, + "GLsizeiptr":DataType.POINTER, + "GLintptr":DataType.POINTER, + "GLeglImageOES":DataType.POINTER, +} + +API_SPECS = [ + ('GL2','../GLES2/gl2_api.in'), + ('GL2Ext','../GLES2/gl2ext_api.in'), + ('GL1','../GLES_CM/gl_api.in'), + ('GL1Ext','../GLES_CM/glext_api.in'), +] + +HEADER_TEXT = """/* + * 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. + * + * THIS FILE WAS GENERATED BY A SCRIPT. DO NOT EDIT. + */ + +#include <cutils/log.h> +#include <GLES2/gl2.h> + +#include "gltrace.pb.h" +#include "gltrace_context.h" +#include "gltrace_fixup.h" +#include "gltrace_transport.h" + +namespace android { +namespace gltrace { + +""" + +FOOTER_TEXT = """ + +}; // namespace gltrace +}; // namespace android +""" + +TRACE_CALL_TEMPLATE = pyratemp.Template( +"""$!retType!$ GLTrace_$!func!$($!inputArgList!$) { + GLMessage glmsg; + GLTraceContext *glContext = getGLTraceContext(); + + glmsg.set_context_id(1); + glmsg.set_function(GLMessage::$!func!$); +<!--(if len(parsedArgs) > 0)--> + <!--(for argname, argtype in parsedArgs)--> + + // copy argument $!argname!$ + GLMessage_DataType *arg_$!argname!$ = glmsg.add_args(); + arg_$!argname!$->set_isarray(false); + arg_$!argname!$->set_type(GLMessage::DataType::$!argtype!$); + arg_$!argname!$->$!argtype.getProtobufCall()!$$!argname!$); + <!--(end)--> +<!--(end)--> + + // call function +<!--(if retType != "void")--> + $!retType!$ retValue = glContext->hooks->gl.$!callsite!$; +<!--(else)--> + glContext->hooks->gl.$!callsite!$; +<!--(end)--> +<!--(if retType != "void")--> + + // set return value + GLMessage_DataType *rt = glmsg.mutable_returnvalue(); + rt->set_isarray(false); + rt->set_type(GLMessage::DataType::$!retDataType!$); + rt->$!retDataType.getProtobufCall()!$retValue); +<!--(end)--> + + fixupGLMessage(&glmsg); + traceGLMessage(&glmsg); +<!--(if retType != "void")--> + + return retValue; +<!--(end)--> +} +""") + +def getDataTypeFromKw(kw): + """ Get the data type given declaration. + All pointer declarations are of type DataType.POINTER + + e.g.: GLvoid -> DataType.VOID""" + + if kw.count('*') > 0: + return DataType.POINTER + return GL2PROTOBUF_TYPE_MAP.get(kw) + +def getNameTypePair(decl): + """ Split declaration of a variable to a tuple of (variable name, DataType). + e.g. "const GLChar* varName" -> (varName, POINTER) """ + elements = decl.strip().split(' ') + name = None + if len(elements) > 1: + name = " ".join(elements[-1:]).strip() # last element is the name + dataType = " ".join(elements[:-1]).strip() # everything else is the data type + + # if name is a pointer (e.g. "*ptr"), then remove the "*" from the name + # and add it to the data type + pointersInName = name.count("*") + if pointersInName > 0: + name = name.replace("*", "") + dataType += "*" * pointersInName + + # if name is an array (e.g. "array[10]"), then remove the "[X]" from the name + # and make the datatype to be a pointer + arraysInName = name.count("[") + if arraysInName > 0: + name = name.split('[')[0] + dataType += "*" + else: + dataType = elements[0] + return (name, getDataTypeFromKw(dataType)) + +def parseArgs(arglist): + """ Parse the argument list into a list of (var name, DataType) tuples """ + args = arglist.split(',') + args = map(lambda x: x.strip(), args) # remove unnecessary whitespaces + argtypelist = map(getNameTypePair, args) # split arg into arg type and arg name + if len(argtypelist) == 1: + (name, argtype) = argtypelist[0] + if argtype == DataType.VOID: + return [] + + return argtypelist + +class ApiCall(object): + """An ApiCall models all information about a single OpenGL API""" + + # Regex to match API_ENTRY specification: + # e.g. void API_ENTRY(glActiveTexture)(GLenum texture) { + # the regex uses a non greedy match (?) to match the first closing paren + API_ENTRY_REGEX = "(.*)API_ENTRY\(.*?\)\((.*?)\)" + + # Regex to match CALL_GL_API specification: + # e.g. CALL_GL_API(glCullFace, mode); + # CALL_GL_API_RETURN(glCreateProgram); + CALL_GL_API_REGEX = "CALL_GL_API(_RETURN)?\((.*)\);" + + def __init__(self, prefix, apientry, callsite): + """Construct an ApiCall from its specification. + + The specification is provided by the two arguments: + prefix: prefix to use for function names + defn: specification line containing API_ENTRY macro + e.g: void API_ENTRY(glActiveTexture)(GLenum texture) { + callsite: specification line containing CALL_GL_API macro + e.g: CALL_GL_API(glActiveTexture, texture); + """ + self.prefix = prefix + self.ret = self.getReturnType(apientry) + self.arglist = self.getArgList(apientry) + + # some functions (e.g. __glEGLImageTargetRenderbufferStorageOES), define their + # names one way in the API_ENTRY and another way in the CALL_GL_API macros. + # so self.func is reassigned based on what is there in the call site + self.func = self.getFunc(callsite) + self.callsite = self.getCallSite(callsite) + + def getReturnType(self, apientry): + '''Extract the return type from the API_ENTRY specification''' + m = re.search(self.API_ENTRY_REGEX, apientry) + if not m: + raise ValueError("%s does not match API_ENTRY specification %s" + % (apientry, self.API_ENTRY_REGEX)) + + return m.group(1).strip() + + def getArgList(self, apientry): + '''Extract the argument list from the API_ENTRY specification''' + m = re.search(self.API_ENTRY_REGEX, apientry) + if not m: + raise ValueError("%s does not match API_ENTRY specification %s" + % (apientry, self.API_ENTRY_REGEX)) + + return m.group(2).strip() + + def parseCallSite(self, callsite): + m = re.search(self.CALL_GL_API_REGEX, callsite) + if not m: + raise ValueError("%s does not match CALL_GL_API specification (%s)" + % (callsite, self.CALL_GL_API_REGEX)) + + arglist = m.group(2) + args = arglist.split(',') + args = map(lambda x: x.strip(), args) + + return args + + def getCallSite(self, callsite): + '''Extract the callsite from the CALL_GL_API specification''' + args = self.parseCallSite(callsite) + return "%s(%s)" % (args[0], ", ".join(args[1:])) + + def getFunc(self, callsite): + '''Extract the function name from the CALL_GL_API specification''' + args = self.parseCallSite(callsite) + return args[0] + + def genDeclaration(self): + return "%s GLTrace_%s(%s);" % (self.ret, self.func, self.arglist) + + def genCode(self): + return TRACE_CALL_TEMPLATE(func = self.func, + retType = self.ret, + retDataType = getDataTypeFromKw(self.ret), + inputArgList = self.arglist, + callsite = self.callsite, + parsedArgs = parseArgs(self.arglist), + DataType=DataType) + +def getApis(apiEntryFile, prefix): + '''Get a list of all ApiCalls in provided specification file''' + lines = open(apiEntryFile).readlines() + + apis = [] + for i in range(0, len(lines)/3): + apis.append(ApiCall(prefix, lines[i*3], lines[i*3+1])) + + return apis + +def parseAllSpecs(specs): + apis = [] + for name, specfile in specs: + a = getApis(specfile, name) + print 'Parsed %s APIs from %s, # of entries = %d' % (name, specfile, len(a)) + apis.extend(a) + return apis + +def removeDuplicates(apis): + '''Remove all duplicate function entries. + + The input list contains functions declared in GL1 and GL2 APIs. + This will return a list that contains only the first function if there are + multiple functions with the same name.''' + uniqs = [] + funcs = set() + for api in apis: + if api.func not in funcs: + uniqs.append(api) + funcs.add(api.func) + + return uniqs + +def genHeaders(apis, fname): + lines = [] + lines.append(HEADER_TEXT) + prefix = "" + for api in apis: + if prefix != api.prefix: + lines.append("\n// Declarations for %s APIs\n\n" % api.prefix) + prefix = api.prefix + lines.append(api.genDeclaration()) + lines.append("\n") + lines.append(FOOTER_TEXT) + + with open(fname, "w") as f: + f.writelines(lines) + +def genSrcs(apis, fname): + lines = [] + lines.append(HEADER_TEXT) + prefix = "" + for api in apis: + if prefix != api.prefix: + lines.append("\n// Definitions for %s APIs\n\n" % api.prefix) + prefix = api.prefix + lines.append(api.genCode()) + lines.append("\n") + lines.append(FOOTER_TEXT) + + with open(fname, "w") as f: + f.writelines(lines) + +if __name__ == '__main__': + apis = parseAllSpecs(API_SPECS) # read in all the specfiles + apis = removeDuplicates(apis) # remove duplication of functions common to GL1 and GL2 + genHeaders(apis, 'gltrace_api.h') # generate header file + genSrcs(apis, 'gltrace_api.cpp') # generate source file |