• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright 2019 The Dawn Authors
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import os, json, sys
17from collections import namedtuple
18import xml.etree.ElementTree as etree
19
20from generator_lib import Generator, run_generator, FileRender
21
22
23class ProcName:
24    def __init__(self, gl_name, proc_name=None):
25        assert gl_name.startswith('gl')
26        if proc_name == None:
27            proc_name = gl_name[2:]
28
29        self.gl_name = gl_name
30        self.proc_name = proc_name
31
32    def glProcName(self):
33        return self.gl_name
34
35    def ProcName(self):
36        return self.proc_name
37
38    def PFNPROCNAME(self):
39        return 'PFN' + self.gl_name.upper() + 'PROC'
40
41    def __repr__(self):
42        return 'Proc("{}", "{}")'.format(self.gl_name, self.proc_name)
43
44
45ProcParam = namedtuple('ProcParam', ['name', 'type'])
46
47
48class Proc:
49    def __init__(self, element):
50        # Type declaration for return values and arguments all have the same
51        # (weird) format.
52        # <element>[A][<ptype>B</ptype>][C]<other stuff.../></element>
53        #
54        # Some examples are:
55        #   <proto>void <name>glFinish</name></proto>
56        #   <proto><ptype>GLenum</ptype><name>glFenceSync</name></proto>
57        #   <proto>const <ptype>GLubyte</ptype> *<name>glGetString</name></proto>
58        #
59        # This handles all the shapes found in gl.xml except for this one that
60        # has an array specifier after </name>:
61        #   <param><ptype>GLuint</ptype> <name>baseAndCount</name>[2]</param>
62        def parse_type_declaration(element):
63            result = ''
64            if element.text != None:
65                result += element.text
66            ptype = element.find('ptype')
67            if ptype != None:
68                result += ptype.text
69                if ptype.tail != None:
70                    result += ptype.tail
71            return result.strip()
72
73        proto = element.find('proto')
74
75        self.return_type = parse_type_declaration(proto)
76
77        self.params = []
78        for param in element.findall('./param'):
79            self.params.append(
80                ProcParam(
81                    param.find('name').text, parse_type_declaration(param)))
82
83        self.gl_name = proto.find('name').text
84        self.alias = None
85        if element.find('alias') != None:
86            self.alias = element.find('alias').attrib['name']
87
88    def glProcName(self):
89        return self.gl_name
90
91    def ProcName(self):
92        assert self.gl_name.startswith('gl')
93        return self.gl_name[2:]
94
95    def PFNGLPROCNAME(self):
96        return 'PFN' + self.gl_name.upper() + 'PROC'
97
98    def __repr__(self):
99        return 'Proc("{}")'.format(self.gl_name)
100
101
102EnumDefine = namedtuple('EnumDefine', ['name', 'value'])
103Version = namedtuple('Version', ['major', 'minor'])
104VersionBlock = namedtuple('VersionBlock', ['version', 'procs', 'enums'])
105HeaderBlock = namedtuple('HeaderBlock', ['description', 'procs', 'enums'])
106ExtensionBlock = namedtuple('ExtensionBlock',
107                            ['extension', 'procs', 'enums', 'supported_specs'])
108
109
110def parse_version(version):
111    return Version(*map(int, version.split('.')))
112
113
114def compute_params(root, supported_extensions):
115    # Parse all the commands and enums
116    all_procs = {}
117    for command in root.findall('''commands[@namespace='GL']/command'''):
118        proc = Proc(command)
119        assert proc.gl_name not in all_procs
120        all_procs[proc.gl_name] = proc
121
122    all_enums = {}
123    for enum in root.findall('''enums[@namespace='GL']/enum'''):
124        enum_name = enum.attrib['name']
125        # Special case an enum we'll never use that has different values in GL and GLES
126        if enum_name == 'GL_ACTIVE_PROGRAM_EXT':
127            continue
128
129        assert enum_name not in all_enums
130        all_enums[enum_name] = EnumDefine(enum_name, enum.attrib['value'])
131
132    # Get the list of all Desktop OpenGL function removed by the Core Profile.
133    core_removed_procs = set()
134    for proc in root.findall('''feature/remove[@profile='core']/command'''):
135        core_removed_procs.add(proc.attrib['name'])
136
137    # Get list of enums and procs per OpenGL ES/Desktop OpenGL version
138    def parse_version_blocks(api, removed_procs=set()):
139        blocks = []
140        for section in root.findall('''feature[@api='{}']'''.format(api)):
141            section_procs = []
142            for command in section.findall('./require/command'):
143                proc_name = command.attrib['name']
144                assert all_procs[proc_name].alias == None
145                if proc_name not in removed_procs:
146                    section_procs.append(all_procs[proc_name])
147
148            section_enums = []
149            for enum in section.findall('./require/enum'):
150                section_enums.append(all_enums[enum.attrib['name']])
151
152            blocks.append(
153                VersionBlock(parse_version(section.attrib['number']),
154                             section_procs, section_enums))
155
156        return blocks
157
158    gles_blocks = parse_version_blocks('gles2')
159    desktop_gl_blocks = parse_version_blocks('gl', core_removed_procs)
160
161    def parse_extension_block(extension):
162        section = root.find(
163            '''extensions/extension[@name='{}']'''.format(extension))
164        supported_specs = section.attrib['supported'].split('|')
165        section_procs = []
166        for command in section.findall('./require/command'):
167            proc_name = command.attrib['name']
168            assert all_procs[proc_name].alias == None
169            section_procs.append(all_procs[proc_name])
170
171        section_enums = []
172        for enum in section.findall('./require/enum'):
173            section_enums.append(all_enums[enum.attrib['name']])
174
175        return ExtensionBlock(extension, section_procs, section_enums,
176                              supported_specs)
177
178    extension_desktop_gl_blocks = []
179    extension_gles_blocks = []
180    for extension in supported_extensions:
181        extension_block = parse_extension_block(extension)
182        if 'gl' in extension_block.supported_specs:
183            extension_desktop_gl_blocks.append(extension_block)
184        if 'gles2' in extension_block.supported_specs:
185            extension_gles_blocks.append(extension_block)
186
187    # Compute the blocks for headers such that there is no duplicate definition
188    already_added_header_procs = set()
189    already_added_header_enums = set()
190    header_blocks = []
191
192    def add_header_block(description, block):
193        block_procs = []
194        for proc in block.procs:
195            if not proc.glProcName() in already_added_header_procs:
196                already_added_header_procs.add(proc.glProcName())
197                block_procs.append(proc)
198
199        block_enums = []
200        for enum in block.enums:
201            if not enum.name in already_added_header_enums:
202                already_added_header_enums.add(enum.name)
203                block_enums.append(enum)
204
205        if len(block_procs) > 0 or len(block_enums) > 0:
206            header_blocks.append(
207                HeaderBlock(description, block_procs, block_enums))
208
209    for block in gles_blocks:
210        add_header_block(
211            'OpenGL ES {}.{}'.format(block.version.major, block.version.minor),
212            block)
213
214    for block in desktop_gl_blocks:
215        add_header_block(
216            'Desktop OpenGL {}.{}'.format(block.version.major,
217                                          block.version.minor), block)
218
219    for block in extension_desktop_gl_blocks:
220        add_header_block(block.extension, block)
221
222    for block in extension_gles_blocks:
223        add_header_block(block.extension, block)
224
225    return {
226        'gles_blocks': gles_blocks,
227        'desktop_gl_blocks': desktop_gl_blocks,
228        'extension_desktop_gl_blocks': extension_desktop_gl_blocks,
229        'extension_gles_blocks': extension_gles_blocks,
230        'header_blocks': header_blocks,
231    }
232
233
234class OpenGLLoaderGenerator(Generator):
235    def get_description(self):
236        return 'Generates code to load OpenGL function pointers'
237
238    def add_commandline_arguments(self, parser):
239        parser.add_argument('--gl-xml',
240                            required=True,
241                            type=str,
242                            help='The Khronos gl.xml to use.')
243        parser.add_argument(
244            '--supported-extensions',
245            required=True,
246            type=str,
247            help=
248            'The JSON file that defines the OpenGL and GLES extensions to use.'
249        )
250
251    def get_file_renders(self, args):
252        supported_extensions = []
253        with open(args.supported_extensions) as f:
254            supported_extensions_json = json.loads(f.read())
255            supported_extensions = supported_extensions_json[
256                'supported_extensions']
257
258        params = compute_params(
259            etree.parse(args.gl_xml).getroot(), supported_extensions)
260
261        return [
262            FileRender(
263                'opengl/OpenGLFunctionsBase.cpp',
264                'src/dawn_native/opengl/OpenGLFunctionsBase_autogen.cpp',
265                [params]),
266            FileRender('opengl/OpenGLFunctionsBase.h',
267                       'src/dawn_native/opengl/OpenGLFunctionsBase_autogen.h',
268                       [params]),
269            FileRender('opengl/opengl_platform.h',
270                       'src/dawn_native/opengl/opengl_platform_autogen.h',
271                       [params]),
272        ]
273
274    def get_dependencies(self, args):
275        return [
276            os.path.abspath(args.gl_xml),
277            os.path.abspath(args.supported_extensions)
278        ]
279
280
281if __name__ == '__main__':
282    sys.exit(run_generator(OpenGLLoaderGenerator()))
283