blob: 9b7a1cfe459caab4d63c8a785f972e0d26cd19ea [file] [log] [blame]
Siva Velusamydb974682011-11-30 15:05:37 -08001#!/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 GLES2_trace/tools folder, or update PYTHONPATH
30# to point to wherever it was downloaded.
31#
32# USAGE
33# $ cd GLES2_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.POINTER,
96 "GLintptr":DataType.POINTER,
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
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800107HEADER_LICENSE = """/*
Siva Velusamydb974682011-11-30 15:05:37 -0800108 * 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 */
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800124"""
Siva Velusamydb974682011-11-30 15:05:37 -0800125
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800126HEADER_INCLUDES = """
Siva Velusamydb974682011-11-30 15:05:37 -0800127#include <cutils/log.h>
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800128#include <utils/Timers.h>
Siva Velusamydb974682011-11-30 15:05:37 -0800129#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"
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800135"""
Siva Velusamydb974682011-11-30 15:05:37 -0800136
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800137HEADER_NAMESPACE_START = """
Siva Velusamydb974682011-11-30 15:05:37 -0800138namespace android {
139namespace gltrace {
Siva Velusamydb974682011-11-30 15:05:37 -0800140"""
141
142FOOTER_TEXT = """
Siva Velusamydb974682011-11-30 15:05:37 -0800143}; // 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
Siva Velusamydb974682011-11-30 15:05:37 -0800152 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
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800165 nsecs_t start_time = systemTime();
Siva Velusamydb974682011-11-30 15:05:37 -0800166<!--(if retType != "void")-->
167 $!retType!$ retValue = glContext->hooks->gl.$!callsite!$;
168<!--(else)-->
169 glContext->hooks->gl.$!callsite!$;
170<!--(end)-->
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800171 nsecs_t end_time = systemTime();
Siva Velusamydb974682011-11-30 15:05:37 -0800172<!--(if retType != "void")-->
173
174 // set return value
175 GLMessage_DataType *rt = glmsg.mutable_returnvalue();
176 rt->set_isarray(false);
177 rt->set_type(GLMessage::DataType::$!retDataType!$);
178 rt->$!retDataType.getProtobufCall()!$retValue);
179<!--(end)-->
180
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800181 fixupGLMessage(glContext, start_time, end_time, &glmsg);
Siva Velusamy1e81e712011-12-14 12:19:56 -0800182 glContext->traceGLMessage(&glmsg);
Siva Velusamydb974682011-11-30 15:05:37 -0800183<!--(if retType != "void")-->
184
185 return retValue;
186<!--(end)-->
187}
188""")
189
190def getDataTypeFromKw(kw):
191 """ Get the data type given declaration.
192 All pointer declarations are of type DataType.POINTER
193
194 e.g.: GLvoid -> DataType.VOID"""
195
196 if kw.count('*') > 0:
197 return DataType.POINTER
198 return GL2PROTOBUF_TYPE_MAP.get(kw)
199
200def getNameTypePair(decl):
201 """ Split declaration of a variable to a tuple of (variable name, DataType).
202 e.g. "const GLChar* varName" -> (varName, POINTER) """
203 elements = decl.strip().split(' ')
204 name = None
205 if len(elements) > 1:
206 name = " ".join(elements[-1:]).strip() # last element is the name
207 dataType = " ".join(elements[:-1]).strip() # everything else is the data type
208
209 # if name is a pointer (e.g. "*ptr"), then remove the "*" from the name
210 # and add it to the data type
211 pointersInName = name.count("*")
212 if pointersInName > 0:
213 name = name.replace("*", "")
214 dataType += "*" * pointersInName
215
216 # if name is an array (e.g. "array[10]"), then remove the "[X]" from the name
217 # and make the datatype to be a pointer
218 arraysInName = name.count("[")
219 if arraysInName > 0:
220 name = name.split('[')[0]
221 dataType += "*"
222 else:
223 dataType = elements[0]
224 return (name, getDataTypeFromKw(dataType))
225
226def parseArgs(arglist):
227 """ Parse the argument list into a list of (var name, DataType) tuples """
228 args = arglist.split(',')
229 args = map(lambda x: x.strip(), args) # remove unnecessary whitespaces
230 argtypelist = map(getNameTypePair, args) # split arg into arg type and arg name
231 if len(argtypelist) == 1:
232 (name, argtype) = argtypelist[0]
233 if argtype == DataType.VOID:
234 return []
235
236 return argtypelist
237
238class ApiCall(object):
239 """An ApiCall models all information about a single OpenGL API"""
240
241 # Regex to match API_ENTRY specification:
242 # e.g. void API_ENTRY(glActiveTexture)(GLenum texture) {
243 # the regex uses a non greedy match (?) to match the first closing paren
244 API_ENTRY_REGEX = "(.*)API_ENTRY\(.*?\)\((.*?)\)"
245
246 # Regex to match CALL_GL_API specification:
247 # e.g. CALL_GL_API(glCullFace, mode);
248 # CALL_GL_API_RETURN(glCreateProgram);
249 CALL_GL_API_REGEX = "CALL_GL_API(_RETURN)?\((.*)\);"
250
251 def __init__(self, prefix, apientry, callsite):
252 """Construct an ApiCall from its specification.
253
254 The specification is provided by the two arguments:
255 prefix: prefix to use for function names
256 defn: specification line containing API_ENTRY macro
257 e.g: void API_ENTRY(glActiveTexture)(GLenum texture) {
258 callsite: specification line containing CALL_GL_API macro
259 e.g: CALL_GL_API(glActiveTexture, texture);
260 """
261 self.prefix = prefix
262 self.ret = self.getReturnType(apientry)
263 self.arglist = self.getArgList(apientry)
264
265 # some functions (e.g. __glEGLImageTargetRenderbufferStorageOES), define their
266 # names one way in the API_ENTRY and another way in the CALL_GL_API macros.
267 # so self.func is reassigned based on what is there in the call site
268 self.func = self.getFunc(callsite)
269 self.callsite = self.getCallSite(callsite)
270
271 def getReturnType(self, apientry):
272 '''Extract the return type from the API_ENTRY specification'''
273 m = re.search(self.API_ENTRY_REGEX, apientry)
274 if not m:
275 raise ValueError("%s does not match API_ENTRY specification %s"
276 % (apientry, self.API_ENTRY_REGEX))
277
278 return m.group(1).strip()
279
280 def getArgList(self, apientry):
281 '''Extract the argument list from the API_ENTRY specification'''
282 m = re.search(self.API_ENTRY_REGEX, apientry)
283 if not m:
284 raise ValueError("%s does not match API_ENTRY specification %s"
285 % (apientry, self.API_ENTRY_REGEX))
286
287 return m.group(2).strip()
288
289 def parseCallSite(self, callsite):
290 m = re.search(self.CALL_GL_API_REGEX, callsite)
291 if not m:
292 raise ValueError("%s does not match CALL_GL_API specification (%s)"
293 % (callsite, self.CALL_GL_API_REGEX))
294
295 arglist = m.group(2)
296 args = arglist.split(',')
297 args = map(lambda x: x.strip(), args)
298
299 return args
300
301 def getCallSite(self, callsite):
302 '''Extract the callsite from the CALL_GL_API specification'''
303 args = self.parseCallSite(callsite)
304 return "%s(%s)" % (args[0], ", ".join(args[1:]))
305
306 def getFunc(self, callsite):
307 '''Extract the function name from the CALL_GL_API specification'''
308 args = self.parseCallSite(callsite)
309 return args[0]
310
311 def genDeclaration(self):
312 return "%s GLTrace_%s(%s);" % (self.ret, self.func, self.arglist)
313
314 def genCode(self):
315 return TRACE_CALL_TEMPLATE(func = self.func,
316 retType = self.ret,
317 retDataType = getDataTypeFromKw(self.ret),
318 inputArgList = self.arglist,
319 callsite = self.callsite,
320 parsedArgs = parseArgs(self.arglist),
321 DataType=DataType)
322
323def getApis(apiEntryFile, prefix):
324 '''Get a list of all ApiCalls in provided specification file'''
325 lines = open(apiEntryFile).readlines()
326
327 apis = []
328 for i in range(0, len(lines)/3):
329 apis.append(ApiCall(prefix, lines[i*3], lines[i*3+1]))
330
331 return apis
332
333def parseAllSpecs(specs):
334 apis = []
335 for name, specfile in specs:
336 a = getApis(specfile, name)
337 print 'Parsed %s APIs from %s, # of entries = %d' % (name, specfile, len(a))
338 apis.extend(a)
339 return apis
340
341def removeDuplicates(apis):
342 '''Remove all duplicate function entries.
343
344 The input list contains functions declared in GL1 and GL2 APIs.
345 This will return a list that contains only the first function if there are
346 multiple functions with the same name.'''
347 uniqs = []
348 funcs = set()
349 for api in apis:
350 if api.func not in funcs:
351 uniqs.append(api)
352 funcs.add(api.func)
353
354 return uniqs
355
356def genHeaders(apis, fname):
357 lines = []
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800358 lines.append(HEADER_LICENSE)
359 lines.append(HEADER_NAMESPACE_START)
Siva Velusamydb974682011-11-30 15:05:37 -0800360 prefix = ""
361 for api in apis:
362 if prefix != api.prefix:
363 lines.append("\n// Declarations for %s APIs\n\n" % api.prefix)
364 prefix = api.prefix
365 lines.append(api.genDeclaration())
366 lines.append("\n")
367 lines.append(FOOTER_TEXT)
368
369 with open(fname, "w") as f:
370 f.writelines(lines)
371
372def genSrcs(apis, fname):
373 lines = []
Siva Velusamy5ed919e2011-12-15 17:32:05 -0800374 lines.append(HEADER_LICENSE)
375 lines.append(HEADER_INCLUDES)
376 lines.append(HEADER_NAMESPACE_START)
Siva Velusamydb974682011-11-30 15:05:37 -0800377 prefix = ""
378 for api in apis:
379 if prefix != api.prefix:
380 lines.append("\n// Definitions for %s APIs\n\n" % api.prefix)
381 prefix = api.prefix
382 lines.append(api.genCode())
383 lines.append("\n")
384 lines.append(FOOTER_TEXT)
385
386 with open(fname, "w") as f:
387 f.writelines(lines)
388
389if __name__ == '__main__':
390 apis = parseAllSpecs(API_SPECS) # read in all the specfiles
391 apis = removeDuplicates(apis) # remove duplication of functions common to GL1 and GL2
392 genHeaders(apis, 'gltrace_api.h') # generate header file
393 genSrcs(apis, 'gltrace_api.cpp') # generate source file