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         reg.OutputGenerator.genCmd(self, cmd, name)
    121 
    122         rtype, fname = parseProto(cmd.elem.find('proto'))
    123         params = [parseParam(p) for p in cmd.elem.findall('param')]
    124         call = 'CALL_GL_API' if rtype == 'void' else 'CALL_GL_API_RETURN'
    125         print('%s API_ENTRY(%s)(%s) {\n'
    126               '    %s(%s%s%s);\n'
    127               '}'
    128               % (rtype, overrideSymbolName(fname, self.genOpts.apiname),
    129                  fmtParams(params), call, fname,
    130                  ', ' if len(params) > 0 else '',
    131                  fmtArgs(params)),
    132               file=self.outFile)
    133 
    134 
    135 
    136 # Collect all API prototypes across all families, remove duplicates,
    137 # emit to entries.in and enums.in files.
    138 class ApiGenerator(reg.OutputGenerator):
    139     def __init__(self):
    140         reg.OutputGenerator.__init__(self, sys.stderr, sys.stderr, None)
    141         self.cmds = []
    142         self.enums = collections.OrderedDict()
    143 
    144     def genCmd(self, cmd, name):
    145         reg.OutputGenerator.genCmd(self, cmd, name)
    146         rtype, fname = parseProto(cmd.elem.find('proto'))
    147         params = [parseParam(p) for p in cmd.elem.findall('param')]
    148         self.cmds.append({'rtype': rtype, 'name': fname, 'params': params})
    149 
    150     def genEnum(self, enuminfo, name):
    151         reg.OutputGenerator.genEnum(self, enuminfo, name)
    152         value = enuminfo.elem.get('value')
    153 
    154         # Skip bitmask enums. Pattern matches:
    155         # - GL_DEPTH_BUFFER_BIT
    156         # - GL_MAP_INVALIDATE_BUFFER_BIT_EXT
    157         # - GL_COLOR_BUFFER_BIT1_QCOM
    158         # but not
    159         # - GL_DEPTH_BITS
    160         # - GL_QUERY_COUNTER_BITS_EXT
    161         #
    162         # TODO: Assuming a naming pattern and using a regex is what the
    163         # old glenumsgen script did. But the registry XML knows which enums are
    164         # parts of bitmask groups, so we should just use that. I'm not sure how
    165         # to get the information out though, and it's not critical right now,
    166         # so leaving for later.
    167         if re.search('_BIT($|\d*_)', name):
    168             return
    169 
    170         # Skip non-hex values (GL_TRUE, GL_FALSE, header guard junk)
    171         if not re.search('0x[0-9A-Fa-f]+', value):
    172             return
    173 
    174         # Append 'u' or 'ull' type suffix if present
    175         type = enuminfo.elem.get('type')
    176         if type and type != 'i':
    177             value += type
    178 
    179         if value not in self.enums:
    180             self.enums[value] = name
    181 
    182     def finish(self):
    183         # sort by function name, remove duplicates
    184         self.cmds.sort(key=itemgetter('name'))
    185         cmds = []
    186         for cmd in self.cmds:
    187             if len(cmds) == 0 or cmd != cmds[-1]:
    188                 cmds.append(cmd)
    189         self.cmds = cmds
    190 
    191     # Write entries.in
    192     def writeEntries(self, outfile):
    193         for cmd in self.cmds:
    194             print('GL_ENTRY(%s, %s, %s)'
    195                   % (cmd['rtype'], cmd['name'], fmtParams(cmd['params'])),
    196                   file=outfile)
    197 
    198     # Write enums.in
    199     def writeEnums(self, outfile):
    200         for enum in self.enums.iteritems():
    201             print('GL_ENUM(%s,%s)' % (enum[0], enum[1]), file=outfile)
    202 
    203 
    204 # Generate .spec entries for use by legacy 'gen' script
    205 class SpecGenerator(reg.OutputGenerator):
    206     def __init__(self):
    207         reg.OutputGenerator.__init__(self, sys.stderr, sys.stderr, None)
    208 
    209     def genCmd(self, cmd, name):
    210         reg.OutputGenerator.genCmd(self, cmd, name)
    211         rtype, fname = parseProto(cmd.elem.find('proto'))
    212         params = [parseParam(p) for p in cmd.elem.findall('param')]
    213 
    214         print('%s %s ( %s )' % (rtype, fname, fmtParams(params)),
    215               file=self.outFile)
    216 
    217 
    218 if __name__ == '__main__':
    219     registry = reg.Registry()
    220     registry.loadFile('registry/gl.xml')
    221 
    222     registry.setGenerator(TrampolineGen())
    223     TRAMPOLINE_OPTIONS = [
    224         reg.GeneratorOptions(
    225             apiname             = 'gles1',
    226             profile             = 'common',
    227             filename            = '../../libs/GLES_CM/gl_api.in'),
    228         reg.GeneratorOptions(
    229             apiname             = 'gles1',
    230             profile             = 'common',
    231             emitversions        = None,
    232             defaultExtensions   = 'gles1',
    233             filename            = '../../libs/GLES_CM/glext_api.in'),
    234         reg.GeneratorOptions(
    235             apiname             = 'gles2',
    236             profile             = 'common',
    237             filename            = '../../libs/GLES2/gl2_api.in'),
    238         reg.GeneratorOptions(
    239             apiname             = 'gles2',
    240             profile             = 'common',
    241             emitversions        = None,
    242             defaultExtensions   = 'gles2',
    243             filename            = '../../libs/GLES2/gl2ext_api.in')]
    244     for opts in TRAMPOLINE_OPTIONS:
    245         registry.apiGen(opts)
    246 
    247     apigen = ApiGenerator()
    248     registry.setGenerator(apigen)
    249     API_OPTIONS = [
    250         # Generate non-extension versions of each API first, then extensions,
    251         # so that if an extension enum was later standardized, we see the non-
    252         # suffixed version first.
    253         reg.GeneratorOptions(
    254             apiname             = 'gles1',
    255             profile             = 'common'),
    256         reg.GeneratorOptions(
    257             apiname             = 'gles2',
    258             profile             = 'common'),
    259         reg.GeneratorOptions(
    260             apiname             = 'gles1',
    261             profile             = 'common',
    262             emitversions        = None,
    263             defaultExtensions   = 'gles1'),
    264         reg.GeneratorOptions(
    265             apiname             = 'gles2',
    266             profile             = 'common',
    267             emitversions        = None,
    268             defaultExtensions   = 'gles2')]
    269     for opts in API_OPTIONS:
    270         registry.apiGen(opts)
    271     apigen.finish()
    272     with open('../../libs/entries.in', 'w') as f:
    273         apigen.writeEntries(f)
    274     with open('../../libs/enums.in', 'w') as f:
    275         apigen.writeEnums(f)
    276 
    277     registry.setGenerator(SpecGenerator())
    278     SPEC_OPTIONS = [
    279         reg.GeneratorOptions(
    280             apiname             = 'gles2',
    281             profile             = 'common',
    282             versions            = '3\.1',
    283             filename            = '../glgen/specs/gles11/GLES31.spec'),
    284         reg.GeneratorOptions(
    285             apiname             = 'gles2',
    286             profile             = 'common',
    287             emitversions        = None,
    288             defaultExtensions   = None,
    289             addExtensions       = '^({})$'.format('|'.join(AEP_EXTENSIONS)),
    290             filename            = '../glgen/specs/gles11/GLES31Ext.spec'),
    291         reg.GeneratorOptions(
    292             apiname             = 'gles2',
    293             profile             = 'common',
    294             versions            = '3\.2',
    295             filename            = '../glgen/specs/gles11/GLES32.spec')]
    296     # SpecGenerator creates a good starting point, but the CFunc.java parser is
    297     # so terrible that the .spec file needs a lot of manual massaging before
    298     # it works. Commenting this out to avoid accidentally overwriting all the
    299     # manual modifications.
    300     #
    301     # Eventually this script should generate the Java and JNI code directly,
    302     # skipping the intermediate .spec step, and obsoleting the existing
    303     # ../glgen system.
    304     #
    305     # for opts in SPEC_OPTIONS:
    306     #     registry.apiGen(opts)
    307 
    308