Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 #
      3 # Copyright (C) 2011 Google Inc.
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #      http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 #
     17 # ABOUT
     18 #   This script is used to generate the trace implementations of all
     19 #   OpenGL calls. When executed, it reads the specs for the OpenGL calls
     20 #   from the files GLES2/gl2_api.in, GLES2/gl2ext_api.in, GLES_CM/gl_api.in,
     21 #   and GLES_CM/glext_api.in, and generates trace versions for all the 
     22 #   defined functions.
     23 #
     24 # PREREQUISITES
     25 #   To generate C++ files, this script uses the 'pyratemp' template
     26 #   module. The only reason to use pyratemp is that it is extremly
     27 #   simple to install:
     28 #   $ wget http://www.simple-is-better.org/template/pyratemp-current/pyratemp.py
     29 #   Put the file in the GLES_trace/tools folder, or update PYTHONPATH
     30 #   to point to wherever it was downloaded.
     31 #
     32 # USAGE
     33 #   $ cd GLES_trace       - run the program from GLES2_trace folder
     34 #   $ ./tools/genapi.py   - generates a .cpp and .h file
     35 #   $ mv *.cpp *.h src/   - move the generated files into the src folder
     36 
     37 import sys
     38 import re
     39 import pyratemp
     40 
     41 # Constants corresponding to the protobuf DataType.Type
     42 class DataType:
     43     def __init__(self, name):
     44         self.name = name
     45 
     46     def __str__(self):
     47         if self.name == "pointer":  # pointers map to the INT DataType
     48             return "INT"
     49         return self.name.upper()
     50 
     51     def getProtobufCall(self):
     52         if self.name == "void":
     53             raise ValueError("Attempt to set void value")
     54         elif self.name == "char" or self.name == "byte" \
     55                 or self.name == "pointer" or self.name == "enum":
     56             return "add_intvalue((int)"
     57         elif self.name == "int":
     58             return "add_intvalue("
     59         elif self.name == "float":
     60             return "add_floatvalue("
     61         elif self.name == "bool":
     62             return "add_boolvalue("
     63         elif self.name == "int64":
     64             return "add_int64value("
     65         else:
     66             raise ValueError("Unknown value type %s" % self.name)
     67 
     68 DataType.VOID = DataType("void")
     69 DataType.CHAR = DataType("char")
     70 DataType.BYTE = DataType("byte")
     71 DataType.ENUM = DataType("enum")
     72 DataType.BOOL = DataType("bool")
     73 DataType.INT = DataType("int")
     74 DataType.FLOAT = DataType("float")
     75 DataType.POINTER = DataType("pointer")
     76 DataType.INT64 = DataType("int64")
     77 
     78 # mapping of GL types to protobuf DataType
     79 GLPROTOBUF_TYPE_MAP = {
     80     "GLvoid":DataType.VOID,
     81     "void":DataType.VOID,
     82     "GLchar":DataType.CHAR,
     83     "GLenum":DataType.ENUM,
     84     "GLboolean":DataType.BOOL,
     85     "GLbitfield":DataType.INT,
     86     "GLbyte":DataType.BYTE,
     87     "GLshort":DataType.INT,
     88     "GLint":DataType.INT,
     89     "int":DataType.INT,
     90     "GLsizei":DataType.INT,
     91     "GLubyte":DataType.BYTE,
     92     "GLushort":DataType.INT,
     93     "GLuint":DataType.INT,
     94     "GLfloat":DataType.FLOAT,
     95     "GLclampf":DataType.FLOAT,
     96     "GLfixed":DataType.INT,
     97     "GLclampx":DataType.INT,
     98     "GLsizeiptr":DataType.INT,
     99     "GLintptr":DataType.INT,
    100     "GLeglImageOES":DataType.POINTER,
    101     "GLint64":DataType.INT64,
    102     "GLuint64":DataType.INT64,
    103     "GLsync":DataType.POINTER,
    104 }
    105 
    106 API_SPECS = [
    107     ('GL3','../GLES2/gl3_api.in'),
    108     ('GL3Ext','../GLES2/gl3ext_api.in'),
    109     ('GL2','../GLES2/gl2_api.in'),
    110     ('GL2Ext','../GLES2/gl2ext_api.in'),
    111     ('GL1','../GLES_CM/gl_api.in'),
    112     ('GL1Ext','../GLES_CM/glext_api.in'),
    113 ]
    114 
    115 HEADER_LICENSE = """/*
    116  * Copyright 2011, The Android Open Source Project
    117  *
    118  * Licensed under the Apache License, Version 2.0 (the "License");
    119  * you may not use this file except in compliance with the License.
    120  * You may obtain a copy of the License at
    121  *
    122  *     http://www.apache.org/licenses/LICENSE-2.0
    123  *
    124  * Unless required by applicable law or agreed to in writing, software
    125  * distributed under the License is distributed on an "AS IS" BASIS,
    126  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    127  * See the License for the specific language governing permissions and
    128  * limitations under the License.
    129  *
    130  * THIS FILE WAS GENERATED BY A SCRIPT. DO NOT EDIT.
    131  */
    132 """
    133 
    134 HEADER_INCLUDES = """
    135 #include <cutils/log.h>
    136 #include <utils/Timers.h>
    137 #include <GLES3/gl3.h>
    138 
    139 #include "gltrace.pb.h"
    140 #include "gltrace_context.h"
    141 #include "gltrace_fixup.h"
    142 #include "gltrace_transport.h"
    143 """
    144 
    145 HEADER_NAMESPACE_START = """
    146 namespace android {
    147 namespace gltrace {
    148 """
    149 
    150 FOOTER_TEXT = """
    151 }; // namespace gltrace
    152 }; // namespace android
    153 """
    154 
    155 TRACE_CALL_TEMPLATE = pyratemp.Template(
    156 """$!retType!$ GLTrace_$!func!$($!inputArgList!$) {
    157     GLMessage glmsg;
    158     GLTraceContext *glContext = getGLTraceContext();
    159 
    160     glmsg.set_function(GLMessage::$!func!$);
    161 <!--(if len(parsedArgs) > 0)-->
    162     <!--(for argname, argtype in parsedArgs)-->
    163 
    164     // copy argument $!argname!$
    165     GLMessage_DataType *arg_$!argname!$ = glmsg.add_args();
    166     arg_$!argname!$->set_isarray(false);
    167     arg_$!argname!$->set_type(GLMessage::DataType::$!argtype!$);
    168     arg_$!argname!$->$!argtype.getProtobufCall()!$$!argname!$);
    169     <!--(end)-->
    170 <!--(end)-->
    171 
    172     // call function
    173     nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
    174     nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD);
    175 <!--(if retType != "void")-->
    176     $!retType!$ retValue = glContext->hooks->gl.$!callsite!$;
    177 <!--(else)-->
    178     glContext->hooks->gl.$!callsite!$;
    179 <!--(end)-->
    180     nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD);
    181     nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
    182 <!--(if retType != "void")-->
    183 
    184     // set return value
    185     GLMessage_DataType *rt = glmsg.mutable_returnvalue();
    186     rt->set_isarray(false);
    187     rt->set_type(GLMessage::DataType::$!retDataType!$);
    188     rt->$!retDataType.getProtobufCall()!$retValue);
    189 <!--(end)-->
    190 
    191     void *pointerArgs[] = {
    192 <!--(for argname, argtype in parsedArgs)-->
    193     <!--(if argtype == DataType.POINTER)-->
    194         (void *) $!argname!$,
    195     <!--(end)-->
    196 <!--(end)-->
    197 <!--(if retDataType == DataType.POINTER)-->
    198         (void *) retValue,
    199 <!--(end)-->
    200     };
    201 
    202     fixupGLMessage(glContext, wallStartTime, wallEndTime,
    203                               threadStartTime, threadEndTime,
    204                               &glmsg, pointerArgs);
    205     glContext->traceGLMessage(&glmsg);
    206 <!--(if retType != "void")-->
    207 
    208     return retValue;
    209 <!--(end)-->
    210 }
    211 """)
    212 
    213 def getDataTypeFromKw(kw):
    214     """ Get the data type given declaration.
    215     All pointer declarations are of type DataType.POINTER
    216 
    217     e.g.: GLvoid -> DataType.VOID"""
    218 
    219     if kw.count('*') > 0:
    220         return DataType.POINTER
    221     return GLPROTOBUF_TYPE_MAP.get(kw)
    222 
    223 def getNameTypePair(decl):
    224     """ Split declaration of a variable to a tuple of (variable name, DataType).
    225     e.g. "const GLChar* varName" -> (varName, POINTER) """
    226     elements = decl.strip().split(' ')
    227     name = None
    228     if len(elements) > 1:
    229         name = " ".join(elements[-1:]).strip()      # last element is the name
    230         dataType = " ".join(elements[:-1]).strip()  # everything else is the data type
    231 
    232         # if name is a pointer (e.g. "*ptr"), then remove the "*" from the name
    233         # and add it to the data type
    234         pointersInName = name.count("*")            
    235         if pointersInName > 0:
    236             name = name.replace("*", "")
    237             dataType += "*" * pointersInName
    238 
    239         # if name is an array (e.g. "array[10]"), then remove the "[X]" from the name
    240         # and make the datatype to be a pointer
    241         arraysInName = name.count("[")
    242         if arraysInName > 0:
    243             name = name.split('[')[0]
    244             dataType += "*"
    245     else:
    246         dataType = elements[0]
    247     return (name, getDataTypeFromKw(dataType))
    248 
    249 def parseArgs(arglist):
    250     """ Parse the argument list into a list of (var name, DataType) tuples """
    251     args = arglist.split(',')
    252     args = map(lambda x: x.strip(), args)    # remove unnecessary whitespaces
    253     argtypelist = map(getNameTypePair, args) # split arg into arg type and arg name
    254     if len(argtypelist) == 1:
    255         (name, argtype) = argtypelist[0]
    256         if argtype == DataType.VOID:
    257             return []
    258 
    259     return argtypelist
    260 
    261 class ApiCall(object):
    262     """An ApiCall models all information about a single OpenGL API"""
    263 
    264     # Regex to match API_ENTRY specification:
    265     #       e.g. void API_ENTRY(glActiveTexture)(GLenum texture) {
    266     # the regex uses a non greedy match (?) to match the first closing paren
    267     API_ENTRY_REGEX = "(.*)API_ENTRY\(.*?\)\((.*?)\)"
    268 
    269     # Regex to match CALL_GL_API specification:
    270     #       e.g. CALL_GL_API(glCullFace, mode); 
    271     #            CALL_GL_API_RETURN(glCreateProgram);
    272     CALL_GL_API_REGEX = "CALL_GL_API(_RETURN)?\((.*)\);"
    273 
    274     def __init__(self, prefix, apientry, callsite):
    275         """Construct an ApiCall from its specification.
    276 
    277         The specification is provided by the two arguments:
    278         prefix: prefix to use for function names
    279         defn: specification line containing API_ENTRY macro
    280               e.g: void API_ENTRY(glActiveTexture)(GLenum texture) {
    281         callsite: specification line containing CALL_GL_API macro
    282               e.g: CALL_GL_API(glActiveTexture, texture);        
    283         """
    284         self.prefix = prefix
    285         self.ret = self.getReturnType(apientry)
    286         self.arglist = self.getArgList(apientry)
    287 
    288         # some functions (e.g. __glEGLImageTargetRenderbufferStorageOES), define their
    289         # names one way in the API_ENTRY and another way in the CALL_GL_API macros.
    290         # so self.func is reassigned based on what is there in the call site
    291         self.func = self.getFunc(callsite)
    292         self.callsite = self.getCallSite(callsite)
    293 
    294     def getReturnType(self, apientry):
    295         '''Extract the return type from the API_ENTRY specification'''
    296         m = re.search(self.API_ENTRY_REGEX, apientry)
    297         if not m:
    298             raise ValueError("%s does not match API_ENTRY specification %s" 
    299                              % (apientry, self.API_ENTRY_REGEX))
    300 
    301         return m.group(1).strip()
    302 
    303     def getArgList(self, apientry):
    304         '''Extract the argument list from the API_ENTRY specification'''
    305         m = re.search(self.API_ENTRY_REGEX, apientry)
    306         if not m:
    307             raise ValueError("%s does not match API_ENTRY specification %s" 
    308                              % (apientry, self.API_ENTRY_REGEX))
    309 
    310         return m.group(2).strip()
    311 
    312     def parseCallSite(self, callsite):
    313         m = re.search(self.CALL_GL_API_REGEX, callsite)
    314         if not m:
    315             raise ValueError("%s does not match CALL_GL_API specification (%s)"
    316                              % (callsite, self.CALL_GL_API_REGEX))
    317 
    318         arglist = m.group(2)
    319         args = arglist.split(',')
    320         args = map(lambda x: x.strip(), args)
    321 
    322         return args
    323 
    324     def getCallSite(self, callsite):
    325         '''Extract the callsite from the CALL_GL_API specification'''
    326         args = self.parseCallSite(callsite)
    327         return "%s(%s)" % (args[0], ", ".join(args[1:]))
    328 
    329     def getFunc(self, callsite):
    330         '''Extract the function name from the CALL_GL_API specification'''
    331         args = self.parseCallSite(callsite)
    332         return args[0]
    333 
    334     def genDeclaration(self):
    335         return "%s GLTrace_%s(%s);" % (self.ret, self.func, self.arglist)
    336 
    337     def genCode(self):
    338         return TRACE_CALL_TEMPLATE(func = self.func, 
    339                                    retType = self.ret,
    340                                    retDataType = getDataTypeFromKw(self.ret),
    341                                    inputArgList = self.arglist,
    342                                    callsite = self.callsite,
    343                                    parsedArgs = parseArgs(self.arglist),
    344                                    DataType=DataType)
    345 
    346 def getApis(apiEntryFile, prefix):
    347     '''Get a list of all ApiCalls in provided specification file'''
    348     lines = open(apiEntryFile).readlines()
    349 
    350     apis = []
    351     for i in range(0, len(lines)/3):
    352         apis.append(ApiCall(prefix, lines[i*3], lines[i*3+1]))
    353 
    354     return apis
    355 
    356 def parseAllSpecs(specs):
    357     apis = []
    358     for name, specfile in specs:
    359         a = getApis(specfile, name)
    360         print 'Parsed %s APIs from %s, # of entries = %d' % (name, specfile, len(a))
    361         apis.extend(a)
    362     return apis
    363 
    364 def removeDuplicates(apis):
    365     '''Remove all duplicate function entries.
    366 
    367     The input list contains functions declared in GL1, GL2, and GL3 APIs.
    368     This will return a list that contains only the first function if there are
    369     multiple functions with the same name.'''
    370     uniqs = []
    371     funcs = set()
    372     for api in apis:
    373         if api.func not in funcs:
    374             uniqs.append(api)
    375             funcs.add(api.func)
    376 
    377     return uniqs
    378 
    379 def genHeaders(apis, fname):
    380     lines = []
    381     lines.append(HEADER_LICENSE)
    382     lines.append(HEADER_NAMESPACE_START)
    383     prefix = ""
    384     for api in apis:
    385         if prefix != api.prefix:
    386             lines.append("\n// Declarations for %s APIs\n\n" % api.prefix)
    387             prefix = api.prefix
    388         lines.append(api.genDeclaration())
    389         lines.append("\n")
    390     lines.append(FOOTER_TEXT)
    391 
    392     with open(fname, "w") as f:
    393         f.writelines(lines)
    394 
    395 def genSrcs(apis, fname):
    396     lines = []
    397     lines.append(HEADER_LICENSE)
    398     lines.append(HEADER_INCLUDES)
    399     lines.append(HEADER_NAMESPACE_START)
    400     prefix = ""
    401     for api in apis:
    402         if prefix != api.prefix:
    403             lines.append("\n// Definitions for %s APIs\n\n" % api.prefix)
    404             prefix = api.prefix
    405         lines.append(api.genCode())
    406         lines.append("\n")
    407     lines.append(FOOTER_TEXT)
    408 
    409     with open(fname, "w") as f:
    410         f.writelines(lines)
    411 
    412 if __name__ == '__main__':
    413     apis = parseAllSpecs(API_SPECS)    # read in all the specfiles
    414     apis = removeDuplicates(apis)      # remove duplication of functions common to multiple versions
    415     genHeaders(apis, 'gltrace_api.h')  # generate header file
    416     genSrcs(apis, 'gltrace_api.cpp')   # generate source file
    417