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 def parseProto(elem):
     60     type = nonestr(elem.text)
     61     name = None
     62     for subelem in elem:
     63         text = nonestr(subelem.text)
     64         if subelem.tag == 'name':
     65             name = text
     66         else:
     67             type += text
     68             type += nonestr(subelem.tail)
     69     return (type.strip(), name)
     70 
     71 def parseParam(elem):
     72     name = elem.find('name').text
     73     declaration = ''.join(elem.itertext())
     74     return (name, declaration)
     75 
     76 # Format a list of (type, declaration) tuples as a C-style parameter list
     77 def fmtParams(params):
     78     if not params:
     79         return 'void'
     80     return ', '.join(p[1] for p in params)
     81 
     82 # Format a list of (type, declaration) tuples as a C-style argument list
     83 def fmtArgs(params):
     84     return ', '.join(p[0] for p in params)
     85 
     86 def overrideSymbolName(sym, apiname):
     87     # The wrapper intercepts various glGet and glGetString functions and
     88     # (sometimes) calls the generated thunk which dispatches to the
     89     # driver's implementation
     90     wrapped_get_syms = {
     91         'gles1' : [
     92             'glGetString'
     93         ],
     94         'gles2' : [
     95             'glGetString',
     96             'glGetStringi',
     97             'glGetBooleanv',
     98             'glGetFloatv',
     99             'glGetIntegerv',
    100             'glGetInteger64v',
    101         ],
    102     }
    103     if sym in wrapped_get_syms.get(apiname):
    104         return '__' + sym
    105     else:
    106         return sym
    107 
    108 
    109 # Generate API trampoline templates:
    110 #   <rtype> API_ENTRY(<name>)(<params>) {
    111 #       CALL_GL_API(<name>, <args>);
    112 #       // or
    113 #       CALL_GL_API_RETURN(<name>, <args>);
    114 #   }
    115 class TrampolineGen(reg.OutputGenerator):
    116     def __init__(self):
    117         reg.OutputGenerator.__init__(self, sys.stderr, sys.stderr, None)
    118 
    119     def genCmd(self, cmd, name):
    120         if re.search('Win32', name):
    121             return
    122         reg.OutputGenerator.genCmd(self, cmd, name)
    123 
    124         rtype, fname = parseProto(cmd.elem.find('proto'))
    125         params = [parseParam(p) for p in cmd.elem.findall('param')]
    126         call = 'CALL_GL_API' if rtype == 'void' else 'CALL_GL_API_RETURN'
    127         print('%s API_ENTRY(%s)(%s) {\n'
    128               '    %s(%s%s%s);\n'
    129               '}'
    130               % (rtype, overrideSymbolName(fname, self.genOpts.apiname),
    131                  fmtParams(params), call, fname,
    132                  ', ' if len(params) > 0 else '',
    133                  fmtArgs(params)),
    134               file=self.outFile)
    135 
    136 
    137 
    138 # Collect all API prototypes across all families, remove duplicates,
    139 # emit to entries.in and enums.in files.
    140 class ApiGenerator(reg.OutputGenerator):
    141     def __init__(self):
    142         reg.OutputGenerator.__init__(self, sys.stderr, sys.stderr, None)
    143         self.cmds = []
    144         self.enums = collections.OrderedDict()
    145 
    146     def genCmd(self, cmd, name):
    147         if re.search('Win32', name):
    148             return
    149         reg.OutputGenerator.genCmd(self, cmd, name)
    150         rtype, fname = parseProto(cmd.elem.find('proto'))
    151         params = [parseParam(p) for p in cmd.elem.findall('param')]
    152         self.cmds.append({'rtype': rtype, 'name': fname, 'params': params})
    153 
    154     def genEnum(self, enuminfo, name):
    155         reg.OutputGenerator.genEnum(self, enuminfo, name)
    156         value = enuminfo.elem.get('value')
    157 
    158         # Skip bitmask enums. Pattern matches:
    159         # - GL_DEPTH_BUFFER_BIT
    160         # - GL_MAP_INVALIDATE_BUFFER_BIT_EXT
    161         # - GL_COLOR_BUFFER_BIT1_QCOM
    162         # but not
    163         # - GL_DEPTH_BITS
    164         # - GL_QUERY_COUNTER_BITS_EXT
    165         #
    166         # TODO: Assuming a naming pattern and using a regex is what the
    167         # old glenumsgen script did. But the registry XML knows which enums are
    168         # parts of bitmask groups, so we should just use that. I'm not sure how
    169         # to get the information out though, and it's not critical right now,
    170         # so leaving for later.
    171         if re.search('_BIT($|\d*_)', name):
    172             return
    173         if re.search('D3D|WIN32', name):
    174             return
    175 
    176         # Skip non-hex values (GL_TRUE, GL_FALSE, header guard junk)
    177         if not re.search('0x[0-9A-Fa-f]+', value):
    178             return
    179 
    180         # Append 'u' or 'ull' type suffix if present
    181         type = enuminfo.elem.get('type')
    182         if type and type != 'i':
    183             value += type
    184 
    185         if value not in self.enums:
    186             self.enums[value] = name
    187 
    188     def finish(self):
    189         # sort by function name, remove duplicates
    190         self.cmds.sort(key=itemgetter('name'))
    191         cmds = []
    192         for cmd in self.cmds:
    193             if len(cmds) == 0 or cmd != cmds[-1]:
    194                 cmds.append(cmd)
    195         self.cmds = cmds
    196 
    197     # Write entries.in
    198     def writeEntries(self, outfile):
    199         for cmd in self.cmds:
    200             print('GL_ENTRY(%s, %s, %s)'
    201                   % (cmd['rtype'], cmd['name'], fmtParams(cmd['params'])),
    202                   file=outfile)
    203 
    204     # Write enums.in
    205     def writeEnums(self, outfile):
    206         for enum in self.enums.iteritems():
    207             print('GL_ENUM(%s,%s)' % (enum[0], enum[1]), file=outfile)
    208 
    209 
    210 # Generate .spec entries for use by legacy 'gen' script
    211 class SpecGenerator(reg.OutputGenerator):
    212     def __init__(self):
    213         reg.OutputGenerator.__init__(self, sys.stderr, sys.stderr, None)
    214 
    215     def genCmd(self, cmd, name):
    216         reg.OutputGenerator.genCmd(self, cmd, name)
    217         rtype, fname = parseProto(cmd.elem.find('proto'))
    218         params = [parseParam(p) for p in cmd.elem.findall('param')]
    219 
    220         print('%s %s ( %s )' % (rtype, fname, fmtParams(params)),
    221               file=self.outFile)
    222 
    223 
    224 if __name__ == '__main__':
    225     registry = reg.Registry()
    226     registry.loadFile('registry/gl.xml')
    227 
    228     registry.setGenerator(TrampolineGen())
    229     TRAMPOLINE_OPTIONS = [
    230         reg.GeneratorOptions(
    231             apiname             = 'gles1',
    232             profile             = 'common',
    233             filename            = '../../libs/GLES_CM/gl_api.in'),
    234         reg.GeneratorOptions(
    235             apiname             = 'gles1',
    236             profile             = 'common',
    237             emitversions        = None,
    238             defaultExtensions   = 'gles1',
    239             filename            = '../../libs/GLES_CM/glext_api.in'),
    240         reg.GeneratorOptions(
    241             apiname             = 'gles2',
    242             profile             = 'common',
    243             filename            = '../../libs/GLES2/gl2_api.in'),
    244         reg.GeneratorOptions(
    245             apiname             = 'gles2',
    246             profile             = 'common',
    247             emitversions        = None,
    248             defaultExtensions   = 'gles2',
    249             filename            = '../../libs/GLES2/gl2ext_api.in')]
    250     for opts in TRAMPOLINE_OPTIONS:
    251         registry.apiGen(opts)
    252 
    253     # Generate a GLESv1_CM entries separately to avoid extra driver loading time
    254     apigen = ApiGenerator()
    255     registry.setGenerator(apigen)
    256     API_OPTIONS = [
    257         # Generate non-extension versions of each API first, then extensions,
    258         # so that if an extension enum was later standardized, we see the non-
    259         # suffixed version first.
    260         reg.GeneratorOptions(
    261             apiname             = 'gles1',
    262             profile             = 'common'),
    263         reg.GeneratorOptions(
    264             apiname             = 'gles1',
    265             profile             = 'common',
    266             emitversions        = None,
    267             defaultExtensions   = 'gles1')]
    268     for opts in API_OPTIONS:
    269         registry.apiGen(opts)
    270     apigen.finish()
    271     with open('../../libs/entries_gles1.in', 'w') as f:
    272         apigen.writeEntries(f)
    273 
    274     apigen = ApiGenerator()
    275     registry.setGenerator(apigen)
    276     API_OPTIONS = [
    277         # Generate non-extension versions of each API first, then extensions,
    278         # so that if an extension enum was later standardized, we see the non-
    279         # suffixed version first.
    280         reg.GeneratorOptions(
    281             apiname             = 'gles1',
    282             profile             = 'common'),
    283         reg.GeneratorOptions(
    284             apiname             = 'gles2',
    285             profile             = 'common'),
    286         reg.GeneratorOptions(
    287             apiname             = 'gles1',
    288             profile             = 'common',
    289             emitversions        = None,
    290             defaultExtensions   = 'gles1'),
    291         reg.GeneratorOptions(
    292             apiname             = 'gles2',
    293             profile             = 'common',
    294             emitversions        = None,
    295             defaultExtensions   = 'gles2')]
    296     for opts in API_OPTIONS:
    297         registry.apiGen(opts)
    298     apigen.finish()
    299     with open('../../libs/entries.in', 'w') as f:
    300         apigen.writeEntries(f)
    301     with open('../../libs/enums.in', 'w') as f:
    302         apigen.writeEnums(f)
    303 
    304     registry.setGenerator(SpecGenerator())
    305     SPEC_OPTIONS = [
    306         reg.GeneratorOptions(
    307             apiname             = 'gles2',
    308             profile             = 'common',
    309             versions            = '3\.1',
    310             filename            = '../glgen/specs/gles11/GLES31.spec'),
    311         reg.GeneratorOptions(
    312             apiname             = 'gles2',
    313             profile             = 'common',
    314             emitversions        = None,
    315             defaultExtensions   = None,
    316             addExtensions       = '^({})$'.format('|'.join(AEP_EXTENSIONS)),
    317             filename            = '../glgen/specs/gles11/GLES31Ext.spec'),
    318         reg.GeneratorOptions(
    319             apiname             = 'gles2',
    320             profile             = 'common',
    321             versions            = '3\.2',
    322             filename            = '../glgen/specs/gles11/GLES32.spec')]
    323     # SpecGenerator creates a good starting point, but the CFunc.java parser is
    324     # so terrible that the .spec file needs a lot of manual massaging before
    325     # it works. Commenting this out to avoid accidentally overwriting all the
    326     # manual modifications.
    327     #
    328     # Eventually this script should generate the Java and JNI code directly,
    329     # skipping the intermediate .spec step, and obsoleting the existing
    330     # ../glgen system.
    331     #
    332     # for opts in SPEC_OPTIONS:
    333     #     registry.apiGen(opts)
    334 
    335