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