Home | History | Annotate | Download | only in glgen2
      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