summaryrefslogtreecommitdiffstats
path: root/opengl/libs/GLES_trace/tools/genapi.py
diff options
context:
space:
mode:
authorSiva Velusamy <vsiva@google.com>2011-11-30 15:05:37 -0800
committerSiva Velusamy <vsiva@google.com>2011-12-02 15:30:17 -0800
commit0469dd6d55fa331bfd7de9431da98b6340d82271 (patch)
tree02185748d9cfeafb5fed64e715b11abff37eaeb9 /opengl/libs/GLES_trace/tools/genapi.py
parent484bc2727c7ce5098ad229ce4feb3c230dfd109d (diff)
downloadframeworks_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-xopengl/libs/GLES_trace/tools/genapi.py386
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