| #!/usr/bin/env python3 | 
 | # Copyright 2019 The Dawn Authors | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #     http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 |  | 
 | import os, json, sys | 
 | from collections import namedtuple | 
 | import xml.etree.ElementTree as etree | 
 |  | 
 | from generator_lib import Generator, run_generator, FileRender | 
 |  | 
 |  | 
 | class ProcName: | 
 |     def __init__(self, gl_name, proc_name=None): | 
 |         assert gl_name.startswith('gl') | 
 |         if proc_name == None: | 
 |             proc_name = gl_name[2:] | 
 |  | 
 |         self.gl_name = gl_name | 
 |         self.proc_name = proc_name | 
 |  | 
 |     def glProcName(self): | 
 |         return self.gl_name | 
 |  | 
 |     def ProcName(self): | 
 |         return self.proc_name | 
 |  | 
 |     def PFNPROCNAME(self): | 
 |         return 'PFN' + self.gl_name.upper() + 'PROC' | 
 |  | 
 |     def __repr__(self): | 
 |         return 'Proc("{}", "{}")'.format(self.gl_name, self.proc_name) | 
 |  | 
 |  | 
 | ProcParam = namedtuple('ProcParam', ['name', 'type']) | 
 |  | 
 |  | 
 | class Proc: | 
 |     def __init__(self, element): | 
 |         # Type declaration for return values and arguments all have the same | 
 |         # (weird) format. | 
 |         # <element>[A][<ptype>B</ptype>][C]<other stuff.../></element> | 
 |         # | 
 |         # Some examples are: | 
 |         #   <proto>void <name>glFinish</name></proto> | 
 |         #   <proto><ptype>GLenum</ptype><name>glFenceSync</name></proto> | 
 |         #   <proto>const <ptype>GLubyte</ptype> *<name>glGetString</name></proto> | 
 |         # | 
 |         # This handles all the shapes found in gl.xml except for this one that | 
 |         # has an array specifier after </name>: | 
 |         #   <param><ptype>GLuint</ptype> <name>baseAndCount</name>[2]</param> | 
 |         def parse_type_declaration(element): | 
 |             result = '' | 
 |             if element.text != None: | 
 |                 result += element.text | 
 |             ptype = element.find('ptype') | 
 |             if ptype != None: | 
 |                 result += ptype.text | 
 |                 if ptype.tail != None: | 
 |                     result += ptype.tail | 
 |             return result.strip() | 
 |  | 
 |         proto = element.find('proto') | 
 |  | 
 |         self.return_type = parse_type_declaration(proto) | 
 |  | 
 |         self.params = [] | 
 |         for param in element.findall('./param'): | 
 |             self.params.append( | 
 |                 ProcParam( | 
 |                     param.find('name').text, parse_type_declaration(param))) | 
 |  | 
 |         self.gl_name = proto.find('name').text | 
 |         self.alias = None | 
 |         if element.find('alias') != None: | 
 |             self.alias = element.find('alias').attrib['name'] | 
 |  | 
 |     def glProcName(self): | 
 |         return self.gl_name | 
 |  | 
 |     def ProcName(self): | 
 |         assert self.gl_name.startswith('gl') | 
 |         return self.gl_name[2:] | 
 |  | 
 |     def PFNGLPROCNAME(self): | 
 |         return 'PFN' + self.gl_name.upper() + 'PROC' | 
 |  | 
 |     def __repr__(self): | 
 |         return 'Proc("{}")'.format(self.gl_name) | 
 |  | 
 |  | 
 | EnumDefine = namedtuple('EnumDefine', ['name', 'value']) | 
 | Version = namedtuple('Version', ['major', 'minor']) | 
 | VersionBlock = namedtuple('VersionBlock', ['version', 'procs', 'enums']) | 
 | HeaderBlock = namedtuple('HeaderBlock', ['description', 'procs', 'enums']) | 
 | ExtensionBlock = namedtuple('ExtensionBlock', | 
 |                             ['extension', 'procs', 'enums', 'supported_specs']) | 
 |  | 
 |  | 
 | def parse_version(version): | 
 |     return Version(*map(int, version.split('.'))) | 
 |  | 
 |  | 
 | def compute_params(root, supported_extensions): | 
 |     # Parse all the commands and enums | 
 |     all_procs = {} | 
 |     for command in root.findall('''commands[@namespace='GL']/command'''): | 
 |         proc = Proc(command) | 
 |         assert proc.gl_name not in all_procs | 
 |         all_procs[proc.gl_name] = proc | 
 |  | 
 |     all_enums = {} | 
 |     for enum in root.findall('''enums[@namespace='GL']/enum'''): | 
 |         enum_name = enum.attrib['name'] | 
 |         # Special case an enum we'll never use that has different values in GL and GLES | 
 |         if enum_name == 'GL_ACTIVE_PROGRAM_EXT': | 
 |             continue | 
 |  | 
 |         assert enum_name not in all_enums | 
 |         all_enums[enum_name] = EnumDefine(enum_name, enum.attrib['value']) | 
 |  | 
 |     # Get the list of all Desktop OpenGL function removed by the Core Profile. | 
 |     core_removed_procs = set() | 
 |     for proc in root.findall('''feature/remove[@profile='core']/command'''): | 
 |         core_removed_procs.add(proc.attrib['name']) | 
 |  | 
 |     # Get list of enums and procs per OpenGL ES/Desktop OpenGL version | 
 |     def parse_version_blocks(api, removed_procs=set()): | 
 |         blocks = [] | 
 |         for section in root.findall('''feature[@api='{}']'''.format(api)): | 
 |             section_procs = [] | 
 |             for command in section.findall('./require/command'): | 
 |                 proc_name = command.attrib['name'] | 
 |                 assert all_procs[proc_name].alias == None | 
 |                 if proc_name not in removed_procs: | 
 |                     section_procs.append(all_procs[proc_name]) | 
 |  | 
 |             section_enums = [] | 
 |             for enum in section.findall('./require/enum'): | 
 |                 section_enums.append(all_enums[enum.attrib['name']]) | 
 |  | 
 |             blocks.append( | 
 |                 VersionBlock(parse_version(section.attrib['number']), | 
 |                              section_procs, section_enums)) | 
 |  | 
 |         return blocks | 
 |  | 
 |     gles_blocks = parse_version_blocks('gles2') | 
 |     desktop_gl_blocks = parse_version_blocks('gl', core_removed_procs) | 
 |  | 
 |     def parse_extension_block(extension): | 
 |         section = root.find( | 
 |             '''extensions/extension[@name='{}']'''.format(extension)) | 
 |         supported_specs = section.attrib['supported'].split('|') | 
 |         section_procs = [] | 
 |         for command in section.findall('./require/command'): | 
 |             proc_name = command.attrib['name'] | 
 |             assert all_procs[proc_name].alias == None | 
 |             section_procs.append(all_procs[proc_name]) | 
 |  | 
 |         section_enums = [] | 
 |         for enum in section.findall('./require/enum'): | 
 |             section_enums.append(all_enums[enum.attrib['name']]) | 
 |  | 
 |         return ExtensionBlock(extension, section_procs, section_enums, | 
 |                               supported_specs) | 
 |  | 
 |     extension_desktop_gl_blocks = [] | 
 |     extension_gles_blocks = [] | 
 |     for extension in supported_extensions: | 
 |         extension_block = parse_extension_block(extension) | 
 |         if 'gl' in extension_block.supported_specs: | 
 |             extension_desktop_gl_blocks.append(extension_block) | 
 |         if 'gles2' in extension_block.supported_specs: | 
 |             extension_gles_blocks.append(extension_block) | 
 |  | 
 |     # Compute the blocks for headers such that there is no duplicate definition | 
 |     already_added_header_procs = set() | 
 |     already_added_header_enums = set() | 
 |     header_blocks = [] | 
 |  | 
 |     def add_header_block(description, block): | 
 |         block_procs = [] | 
 |         for proc in block.procs: | 
 |             if not proc.glProcName() in already_added_header_procs: | 
 |                 already_added_header_procs.add(proc.glProcName()) | 
 |                 block_procs.append(proc) | 
 |  | 
 |         block_enums = [] | 
 |         for enum in block.enums: | 
 |             if not enum.name in already_added_header_enums: | 
 |                 already_added_header_enums.add(enum.name) | 
 |                 block_enums.append(enum) | 
 |  | 
 |         if len(block_procs) > 0 or len(block_enums) > 0: | 
 |             header_blocks.append( | 
 |                 HeaderBlock(description, block_procs, block_enums)) | 
 |  | 
 |     for block in gles_blocks: | 
 |         add_header_block( | 
 |             'OpenGL ES {}.{}'.format(block.version.major, block.version.minor), | 
 |             block) | 
 |  | 
 |     for block in desktop_gl_blocks: | 
 |         add_header_block( | 
 |             'Desktop OpenGL {}.{}'.format(block.version.major, | 
 |                                           block.version.minor), block) | 
 |  | 
 |     for block in extension_desktop_gl_blocks: | 
 |         add_header_block(block.extension, block) | 
 |  | 
 |     for block in extension_gles_blocks: | 
 |         add_header_block(block.extension, block) | 
 |  | 
 |     return { | 
 |         'gles_blocks': gles_blocks, | 
 |         'desktop_gl_blocks': desktop_gl_blocks, | 
 |         'extension_desktop_gl_blocks': extension_desktop_gl_blocks, | 
 |         'extension_gles_blocks': extension_gles_blocks, | 
 |         'header_blocks': header_blocks, | 
 |     } | 
 |  | 
 |  | 
 | class OpenGLLoaderGenerator(Generator): | 
 |     def get_description(self): | 
 |         return 'Generates code to load OpenGL function pointers' | 
 |  | 
 |     def add_commandline_arguments(self, parser): | 
 |         parser.add_argument('--gl-xml', | 
 |                             required=True, | 
 |                             type=str, | 
 |                             help='The Khronos gl.xml to use.') | 
 |         parser.add_argument( | 
 |             '--supported-extensions', | 
 |             required=True, | 
 |             type=str, | 
 |             help= | 
 |             'The JSON file that defines the OpenGL and GLES extensions to use.' | 
 |         ) | 
 |  | 
 |     def get_file_renders(self, args): | 
 |         supported_extensions = [] | 
 |         with open(args.supported_extensions) as f: | 
 |             supported_extensions_json = json.loads(f.read()) | 
 |             supported_extensions = supported_extensions_json[ | 
 |                 'supported_extensions'] | 
 |  | 
 |         params = compute_params( | 
 |             etree.parse(args.gl_xml).getroot(), supported_extensions) | 
 |  | 
 |         return [ | 
 |             FileRender( | 
 |                 'opengl/OpenGLFunctionsBase.cpp', | 
 |                 'src/dawn/native/opengl/OpenGLFunctionsBase_autogen.cpp', | 
 |                 [params]), | 
 |             FileRender('opengl/OpenGLFunctionsBase.h', | 
 |                        'src/dawn/native/opengl/OpenGLFunctionsBase_autogen.h', | 
 |                        [params]), | 
 |             FileRender('opengl/opengl_platform.h', | 
 |                        'src/dawn/native/opengl/opengl_platform_autogen.h', | 
 |                        [params]), | 
 |         ] | 
 |  | 
 |     def get_dependencies(self, args): | 
 |         return [ | 
 |             os.path.abspath(args.gl_xml), | 
 |             os.path.abspath(args.supported_extensions) | 
 |         ] | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     sys.exit(run_generator(OpenGLLoaderGenerator())) |