|  | #!/usr/bin/env python3 | 
|  | # Copyright 2019 The Dawn & Tint Authors | 
|  | # | 
|  | # Redistribution and use in source and binary forms, with or without | 
|  | # modification, are permitted provided that the following conditions are met: | 
|  | # | 
|  | # 1. Redistributions of source code must retain the above copyright notice, this | 
|  | #    list of conditions and the following disclaimer. | 
|  | # | 
|  | # 2. Redistributions in binary form must reproduce the above copyright notice, | 
|  | #    this list of conditions and the following disclaimer in the documentation | 
|  | #    and/or other materials provided with the distribution. | 
|  | # | 
|  | # 3. Neither the name of the copyright holder nor the names of its | 
|  | #    contributors may be used to endorse or promote products derived from | 
|  | #    this software without specific prior written permission. | 
|  | # | 
|  | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
|  | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
|  | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
|  | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
|  | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
|  | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | import os, json, sys | 
|  | from collections import namedtuple | 
|  | import xml.etree.ElementTree as etree | 
|  |  | 
|  | from generator_lib import Generator, run_generator, FileRender, GeneratorOutput | 
|  |  | 
|  |  | 
|  | 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_outputs(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) | 
|  |  | 
|  | renders = [ | 
|  | 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]), | 
|  | ] | 
|  | return GeneratorOutput(renders=renders, imported_templates=[]) | 
|  |  | 
|  | 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())) |