blob: 3c47a5f14d41692aa17e6b324b5327ff3524ff12 [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
107HEADER_TEXT = """/*
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#include <cutils/log.h>
126#include <GLES2/gl2.h>
127
128#include "gltrace.pb.h"
129#include "gltrace_context.h"
130#include "gltrace_fixup.h"
131#include "gltrace_transport.h"
132
133namespace android {
134namespace gltrace {
135
136"""
137
138FOOTER_TEXT = """
139
140}; // namespace gltrace
141}; // namespace android
142"""
143
144TRACE_CALL_TEMPLATE = pyratemp.Template(
145"""$!retType!$ GLTrace_$!func!$($!inputArgList!$) {
146 GLMessage glmsg;
147 GLTraceContext *glContext = getGLTraceContext();
148
149 glmsg.set_context_id(1);
150 glmsg.set_function(GLMessage::$!func!$);
151<!--(if len(parsedArgs) > 0)-->
152 <!--(for argname, argtype in parsedArgs)-->
153
154 // copy argument $!argname!$
155 GLMessage_DataType *arg_$!argname!$ = glmsg.add_args();
156 arg_$!argname!$->set_isarray(false);
157 arg_$!argname!$->set_type(GLMessage::DataType::$!argtype!$);
158 arg_$!argname!$->$!argtype.getProtobufCall()!$$!argname!$);
159 <!--(end)-->
160<!--(end)-->
161
162 // call function
163<!--(if retType != "void")-->
164 $!retType!$ retValue = glContext->hooks->gl.$!callsite!$;
165<!--(else)-->
166 glContext->hooks->gl.$!callsite!$;
167<!--(end)-->
168<!--(if retType != "void")-->
169
170 // set return value
171 GLMessage_DataType *rt = glmsg.mutable_returnvalue();
172 rt->set_isarray(false);
173 rt->set_type(GLMessage::DataType::$!retDataType!$);
174 rt->$!retDataType.getProtobufCall()!$retValue);
175<!--(end)-->
176
177 fixupGLMessage(&glmsg);
178 traceGLMessage(&glmsg);
179<!--(if retType != "void")-->
180
181 return retValue;
182<!--(end)-->
183}
184""")
185
186def getDataTypeFromKw(kw):
187 """ Get the data type given declaration.
188 All pointer declarations are of type DataType.POINTER
189
190 e.g.: GLvoid -> DataType.VOID"""
191
192 if kw.count('*') > 0:
193 return DataType.POINTER
194 return GL2PROTOBUF_TYPE_MAP.get(kw)
195
196def getNameTypePair(decl):
197 """ Split declaration of a variable to a tuple of (variable name, DataType).
198 e.g. "const GLChar* varName" -> (varName, POINTER) """
199 elements = decl.strip().split(' ')
200 name = None
201 if len(elements) > 1:
202 name = " ".join(elements[-1:]).strip() # last element is the name
203 dataType = " ".join(elements[:-1]).strip() # everything else is the data type
204
205 # if name is a pointer (e.g. "*ptr"), then remove the "*" from the name
206 # and add it to the data type
207 pointersInName = name.count("*")
208 if pointersInName > 0:
209 name = name.replace("*", "")
210 dataType += "*" * pointersInName
211
212 # if name is an array (e.g. "array[10]"), then remove the "[X]" from the name
213 # and make the datatype to be a pointer
214 arraysInName = name.count("[")
215 if arraysInName > 0:
216 name = name.split('[')[0]
217 dataType += "*"
218 else:
219 dataType = elements[0]
220 return (name, getDataTypeFromKw(dataType))
221
222def 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
232 return argtypelist
233
234class ApiCall(object):
235 """An ApiCall models all information about a single OpenGL API"""
236
237 # Regex to match API_ENTRY specification:
238 # e.g. void API_ENTRY(glActiveTexture)(GLenum texture) {
239 # the regex uses a non greedy match (?) to match the first closing paren
240 API_ENTRY_REGEX = "(.*)API_ENTRY\(.*?\)\((.*?)\)"
241
242 # Regex to match CALL_GL_API specification:
243 # e.g. CALL_GL_API(glCullFace, mode);
244 # CALL_GL_API_RETURN(glCreateProgram);
245 CALL_GL_API_REGEX = "CALL_GL_API(_RETURN)?\((.*)\);"
246
247 def __init__(self, prefix, apientry, callsite):
248 """Construct an ApiCall from its specification.
249
250 The specification is provided by the two arguments:
251 prefix: prefix to use for function names
252 defn: specification line containing API_ENTRY macro
253 e.g: void API_ENTRY(glActiveTexture)(GLenum texture) {
254 callsite: specification line containing CALL_GL_API macro
255 e.g: CALL_GL_API(glActiveTexture, texture);
256 """
257 self.prefix = prefix
258 self.ret = self.getReturnType(apientry)
259 self.arglist = self.getArgList(apientry)
260
261 # some functions (e.g. __glEGLImageTargetRenderbufferStorageOES), define their
262 # names one way in the API_ENTRY and another way in the CALL_GL_API macros.
263 # so self.func is reassigned based on what is there in the call site
264 self.func = self.getFunc(callsite)
265 self.callsite = self.getCallSite(callsite)
266
267 def getReturnType(self, apientry):
268 '''Extract the return type from the API_ENTRY specification'''
269 m = re.search(self.API_ENTRY_REGEX, apientry)
270 if not m:
271 raise ValueError("%s does not match API_ENTRY specification %s"
272 % (apientry, self.API_ENTRY_REGEX))
273
274 return m.group(1).strip()
275
276 def getArgList(self, apientry):
277 '''Extract the argument list from the API_ENTRY specification'''
278 m = re.search(self.API_ENTRY_REGEX, apientry)
279 if not m:
280 raise ValueError("%s does not match API_ENTRY specification %s"
281 % (apientry, self.API_ENTRY_REGEX))
282
283 return m.group(2).strip()
284
285 def parseCallSite(self, callsite):
286 m = re.search(self.CALL_GL_API_REGEX, callsite)
287 if not m:
288 raise ValueError("%s does not match CALL_GL_API specification (%s)"
289 % (callsite, self.CALL_GL_API_REGEX))
290
291 arglist = m.group(2)
292 args = arglist.split(',')
293 args = map(lambda x: x.strip(), args)
294
295 return args
296
297 def getCallSite(self, callsite):
298 '''Extract the callsite from the CALL_GL_API specification'''
299 args = self.parseCallSite(callsite)
300 return "%s(%s)" % (args[0], ", ".join(args[1:]))
301
302 def getFunc(self, callsite):
303 '''Extract the function name from the CALL_GL_API specification'''
304 args = self.parseCallSite(callsite)
305 return args[0]
306
307 def genDeclaration(self):
308 return "%s GLTrace_%s(%s);" % (self.ret, self.func, self.arglist)
309
310 def genCode(self):
311 return TRACE_CALL_TEMPLATE(func = self.func,
312 retType = self.ret,
313 retDataType = getDataTypeFromKw(self.ret),
314 inputArgList = self.arglist,
315 callsite = self.callsite,
316 parsedArgs = parseArgs(self.arglist),
317 DataType=DataType)
318
319def getApis(apiEntryFile, prefix):
320 '''Get a list of all ApiCalls in provided specification file'''
321 lines = open(apiEntryFile).readlines()
322
323 apis = []
324 for i in range(0, len(lines)/3):
325 apis.append(ApiCall(prefix, lines[i*3], lines[i*3+1]))
326
327 return apis
328
329def parseAllSpecs(specs):
330 apis = []
331 for name, specfile in specs:
332 a = getApis(specfile, name)
333 print 'Parsed %s APIs from %s, # of entries = %d' % (name, specfile, len(a))
334 apis.extend(a)
335 return apis
336
337def removeDuplicates(apis):
338 '''Remove all duplicate function entries.
339
340 The input list contains functions declared in GL1 and GL2 APIs.
341 This will return a list that contains only the first function if there are
342 multiple functions with the same name.'''
343 uniqs = []
344 funcs = set()
345 for api in apis:
346 if api.func not in funcs:
347 uniqs.append(api)
348 funcs.add(api.func)
349
350 return uniqs
351
352def genHeaders(apis, fname):
353 lines = []
354 lines.append(HEADER_TEXT)
355 prefix = ""
356 for api in apis:
357 if prefix != api.prefix:
358 lines.append("\n// Declarations for %s APIs\n\n" % api.prefix)
359 prefix = api.prefix
360 lines.append(api.genDeclaration())
361 lines.append("\n")
362 lines.append(FOOTER_TEXT)
363
364 with open(fname, "w") as f:
365 f.writelines(lines)
366
367def genSrcs(apis, fname):
368 lines = []
369 lines.append(HEADER_TEXT)
370 prefix = ""
371 for api in apis:
372 if prefix != api.prefix:
373 lines.append("\n// Definitions for %s APIs\n\n" % api.prefix)
374 prefix = api.prefix
375 lines.append(api.genCode())
376 lines.append("\n")
377 lines.append(FOOTER_TEXT)
378
379 with open(fname, "w") as f:
380 f.writelines(lines)
381
382if __name__ == '__main__':
383 apis = parseAllSpecs(API_SPECS) # read in all the specfiles
384 apis = removeDuplicates(apis) # remove duplication of functions common to GL1 and GL2
385 genHeaders(apis, 'gltrace_api.h') # generate header file
386 genSrcs(apis, 'gltrace_api.cpp') # generate source file