genapi.py 14 KB

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