1 #!/usr/bin/env python 2 # 3 # Copyright 2014 The Android Open Source Project 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 from __future__ import print_function 18 from operator import itemgetter 19 import collections 20 import os.path 21 import re 22 import sys 23 24 25 # Avoid endlessly adding to the path if this module is imported multiple 26 # times, e.g. in an interactive session 27 regpath = os.path.join(sys.path[0], "registry") 28 if sys.path[1] != regpath: 29 sys.path.insert(1, regpath) 30 import reg 31 32 33 AEP_EXTENSIONS = [ 34 'GL_KHR_blend_equation_advanced', 35 'GL_KHR_debug', 36 'GL_KHR_texture_compression_astc_ldr', 37 'GL_OES_sample_shading', 38 'GL_OES_sample_variables', 39 'GL_OES_shader_image_atomic', 40 'GL_OES_shader_multisample_interpolation', 41 'GL_OES_texture_stencil8', 42 'GL_OES_texture_storage_multisample_2d_array', 43 'GL_EXT_copy_image', 44 'GL_EXT_draw_buffers_indexed', 45 'GL_EXT_geometry_shader', 46 'GL_EXT_gpu_shader5', 47 'GL_EXT_primitive_bounding_box', 48 'GL_EXT_shader_io_blocks', 49 'GL_EXT_tessellation_shader', 50 'GL_EXT_texture_border_clamp', 51 'GL_EXT_texture_buffer', 52 'GL_EXT_texture_cube_map_array', 53 'GL_EXT_texture_sRGB_decode'] 54 55 56 def nonestr(s): 57 return s if s else "" 58 59 60 def parseTypedName(elem): 61 type = [nonestr(elem.text)] 62 name = None 63 for subelem in elem: 64 text = nonestr(subelem.text) 65 tail = nonestr(subelem.tail) 66 if subelem.tag == 'name': 67 name = text 68 break 69 else: 70 type.extend([text, tail]) 71 return (''.join(type).strip(), name) 72 73 74 # Format a list of (type, name) tuples as a C-style parameter list 75 def fmtParams(params): 76 if not params: 77 return 'void' 78 return ', '.join(['%s %s' % (p[0], p[1]) for p in params]) 79 80 # Format a list of (type, name) tuples as a C-style argument list 81 def fmtArgs(params): 82 return ', '.join(p[1] for p in params) 83 84 # Format a list of (type, name) tuples as comma-separated '"type", name' 85 def fmtTypeNameList(params): 86 return ', '.join(['"%s", %s' % (p[0], p[1]) for p in params]) 87 88 89 def overrideSymbolName(sym): 90 # The wrapper intercepts glGetString and (sometimes) calls the generated 91 # __glGetString thunk which dispatches to the driver's glGetString 92 if sym == 'glGetString': 93 return '__glGetString' 94 else: 95 return sym 96 97 98 # Generate API trampoline templates: 99 # <rtype> API_ENTRY(<name>)(<params>) { 100 # CALL_GL_API(<name>, <args>); 101 # // or 102 # CALL_GL_API_RETURN(<name>, <args>); 103 # } 104 class TrampolineGen(reg.OutputGenerator): 105 def __init__(self): 106 reg.OutputGenerator.__init__(self, sys.stderr, sys.stderr, None) 107 108 def genCmd(self, cmd, name): 109 reg.OutputGenerator.genCmd(self, cmd, name) 110 111 rtype, fname = parseTypedName(cmd.elem.find('proto')) 112 params = [parseTypedName(p) for p in cmd.elem.findall('param')] 113 114 call = 'CALL_GL_API' if rtype == 'void' else 'CALL_GL_API_RETURN' 115 print('%s API_ENTRY(%s)(%s) {\n' 116 ' %s(%s%s%s);\n' 117 '}' 118 % (rtype, overrideSymbolName(fname), fmtParams(params), 119 call, fname, 120 ', ' if len(params) > 0 else '', 121 fmtArgs(params)), 122 file=self.outFile) 123 124 125 126 # Collect all API prototypes across all families, remove duplicates, 127 # emit to entries.in and trace.in files. 128 class ApiGenerator(reg.OutputGenerator): 129 def __init__(self): 130 reg.OutputGenerator.__init__(self, sys.stderr, sys.stderr, None) 131 self.cmds = [] 132 self.enums = collections.OrderedDict() 133 134 def genCmd(self, cmd, name): 135 reg.OutputGenerator.genCmd(self, cmd, name) 136 rtype, fname = parseTypedName(cmd.elem.find('proto')) 137 params = [parseTypedName(p) for p in cmd.elem.findall('param')] 138 self.cmds.append({'rtype': rtype, 'name': fname, 'params': params}) 139 140 def genEnum(self, enuminfo, name): 141 reg.OutputGenerator.genEnum(self, enuminfo, name) 142 value = enuminfo.elem.get('value') 143 144 # Skip bitmask enums. Pattern matches: 145 # - GL_DEPTH_BUFFER_BIT 146 # - GL_MAP_INVALIDATE_BUFFER_BIT_EXT 147 # - GL_COLOR_BUFFER_BIT1_QCOM 148 # but not 149 # - GL_DEPTH_BITS 150 # - GL_QUERY_COUNTER_BITS_EXT 151 # 152 # TODO: Assuming a naming pattern and using a regex is what the 153 # old glenumsgen script did. But the registry XML knows which enums are 154 # parts of bitmask groups, so we should just use that. I'm not sure how 155 # to get the information out though, and it's not critical right now, 156 # so leaving for later. 157 if re.search('_BIT($|\d*_)', name): 158 return 159 160 # Skip non-hex values (GL_TRUE, GL_FALSE, header guard junk) 161 if not re.search('0x[0-9A-Fa-f]+', value): 162 return 163 164 # Append 'u' or 'ull' type suffix if present 165 type = enuminfo.elem.get('type') 166 if type and type != 'i': 167 value += type 168 169 if value not in self.enums: 170 self.enums[value] = name 171 172 def finish(self): 173 # sort by function name, remove duplicates 174 self.cmds.sort(key=itemgetter('name')) 175 cmds = [] 176 for cmd in self.cmds: 177 if len(cmds) == 0 or cmd != cmds[-1]: 178 cmds.append(cmd) 179 self.cmds = cmds 180 181 # Write entries.in 182 def writeEntries(self, outfile): 183 for cmd in self.cmds: 184 print('GL_ENTRY(%s, %s, %s)' 185 % (cmd['rtype'], cmd['name'], fmtParams(cmd['params'])), 186 file=outfile) 187 188 # Write traces.in 189 def writeTrace(self, outfile): 190 for cmd in self.cmds: 191 if cmd['rtype'] == 'void': 192 ret = '_VOID(' 193 else: 194 ret = '(%s, ' % cmd['rtype'] 195 196 params = cmd['params'] 197 if len(params) > 0: 198 typeNameList = ', ' + fmtTypeNameList(params) 199 else: 200 typeNameList = '' 201 202 print('TRACE_GL%s%s, (%s), (%s), %d%s)' 203 % (ret, cmd['name'], 204 fmtParams(params), fmtArgs(params), 205 len(params), typeNameList), 206 file=outfile) 207 208 # Write enums.in 209 def writeEnums(self, outfile): 210 for enum in self.enums.iteritems(): 211 print('GL_ENUM(%s,%s)' % (enum[0], enum[1]), file=outfile) 212 213 214 # Generate .spec entries for use by legacy 'gen' script 215 class SpecGenerator(reg.OutputGenerator): 216 def __init__(self): 217 reg.OutputGenerator.__init__(self, sys.stderr, sys.stderr, None) 218 219 def genCmd(self, cmd, name): 220 reg.OutputGenerator.genCmd(self, cmd, name) 221 rtype, fname = parseTypedName(cmd.elem.find('proto')) 222 params = [parseTypedName(p) for p in cmd.elem.findall('param')] 223 224 print('%s %s ( %s )' % (rtype, fname, fmtParams(params)), 225 file=self.outFile) 226 227 228 if __name__ == '__main__': 229 registry = reg.Registry() 230 registry.loadFile('registry/gl.xml') 231 232 registry.setGenerator(TrampolineGen()) 233 TRAMPOLINE_OPTIONS = [ 234 reg.GeneratorOptions( 235 apiname = 'gles1', 236 profile = 'common', 237 filename = '../../libs/GLES_CM/gl_api.in'), 238 reg.GeneratorOptions( 239 apiname = 'gles1', 240 profile = 'common', 241 emitversions = None, 242 defaultExtensions = 'gles1', 243 filename = '../../libs/GLES_CM/glext_api.in'), 244 reg.GeneratorOptions( 245 apiname = 'gles2', 246 profile = 'common', 247 filename = '../../libs/GLES2/gl2_api.in'), 248 reg.GeneratorOptions( 249 apiname = 'gles2', 250 profile = 'common', 251 emitversions = None, 252 defaultExtensions = 'gles2', 253 filename = '../../libs/GLES2/gl2ext_api.in')] 254 for opts in TRAMPOLINE_OPTIONS: 255 registry.apiGen(opts) 256 257 apigen = ApiGenerator() 258 registry.setGenerator(apigen) 259 API_OPTIONS = [ 260 # Generate non-extension versions of each API first, then extensions, 261 # so that if an extension enum was later standardized, we see the non- 262 # suffixed version first. 263 reg.GeneratorOptions( 264 apiname = 'gles1', 265 profile = 'common'), 266 reg.GeneratorOptions( 267 apiname = 'gles2', 268 profile = 'common'), 269 reg.GeneratorOptions( 270 apiname = 'gles1', 271 profile = 'common', 272 emitversions = None, 273 defaultExtensions = 'gles1'), 274 reg.GeneratorOptions( 275 apiname = 'gles2', 276 profile = 'common', 277 emitversions = None, 278 defaultExtensions = 'gles2')] 279 for opts in API_OPTIONS: 280 registry.apiGen(opts) 281 apigen.finish() 282 with open('../../libs/entries.in', 'w') as f: 283 apigen.writeEntries(f) 284 with open('../../libs/trace.in', 'w') as f: 285 apigen.writeTrace(f) 286 with open('../../libs/enums.in', 'w') as f: 287 apigen.writeEnums(f) 288 289 registry.setGenerator(SpecGenerator()) 290 SPEC_OPTIONS = [ 291 reg.GeneratorOptions( 292 apiname = 'gles2', 293 profile = 'common', 294 versions = '3\.1', 295 filename = '../glgen/specs/gles11/GLES31.spec'), 296 reg.GeneratorOptions( 297 apiname = 'gles2', 298 profile = 'common', 299 emitversions = None, 300 defaultExtensions = None, 301 addExtensions = '^({})$'.format('|'.join(AEP_EXTENSIONS)), 302 filename = '../glgen/specs/gles11/GLES31Ext.spec')] 303 # SpecGenerator creates a good starting point, but the CFunc.java parser is 304 # so terrible that the .spec file needs a lot of manual massaging before 305 # it works. Commenting this out to avoid accidentally overwriting all the 306 # manual modifications. 307 # 308 # Eventually this script should generate the Java and JNI code directly, 309 # skipping the intermediate .spec step, and obsoleting the existing 310 # ../glgen system. 311 # 312 # for opts in SPEC_OPTIONS: 313 # registry.apiGen(opts) 314 315