• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2# Copyright 2017 The ANGLE Project Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5#
6# generate_gl_dispatch_table.py:
7#  Generation script for OpenGL bindings with ANGLE.
8#  NOTE: don't run this script directly. Run scripts/run_code_generation.py.
9
10import sys
11import os
12import re
13import xml.etree.ElementTree as etree
14
15# Set the CWD to the script directory.
16os.chdir(os.path.dirname(os.path.abspath(sys.argv[0])))
17
18sys.path.append('..')
19import angle_format
20
21def safe_append(the_dict, key, element):
22    if key not in the_dict:
23        the_dict[key] = []
24    the_dict[key].append(element)
25
26
27# Template for the header declaration of the dispatch table.
28dispatch_table_header_template = """// GENERATED FILE - DO NOT EDIT.
29// Generated by {script_name} using data from {data_source_name} and gl.xml.
30//
31// Copyright 2017 The ANGLE Project Authors. All rights reserved.
32// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
34//
35// {file_name}:
36//   Defines the native binding interface for ANGLE's OpenGL back-end.
37
38#ifndef LIBGLESV2_RENDERER_GL_DISPATCH_TABLE_GL_AUTOGEN_H_
39#define LIBGLESV2_RENDERER_GL_DISPATCH_TABLE_GL_AUTOGEN_H_
40
41#include "common/angleutils.h"
42#include "libANGLE/renderer/gl/functionsgl_typedefs.h"
43
44#include <set>
45
46namespace gl
47{{
48struct Version;
49}}  // namespace gl
50
51namespace rx
52{{
53class DispatchTableGL : angle::NonCopyable
54{{
55  public:
56  // clang-format off
57{table_data}
58  // clang-format on
59
60  DispatchTableGL();
61  virtual ~DispatchTableGL() = default;
62
63  protected:
64    virtual void *loadProcAddress(const std::string &function) const = 0;
65
66    void initProcsDesktopGL(const gl::Version &version, const std::set<std::string> &extensions);
67    void initProcsGLES(const gl::Version &version, const std::set<std::string> &extensions);
68    void initProcsSharedExtensions(const std::set<std::string> &extensions);
69
70#if defined(ANGLE_ENABLE_OPENGL_NULL)
71    void initProcsDesktopGLNULL(const gl::Version &version, const std::set<std::string> &extensions);
72    void initProcsGLESNULL(const gl::Version &version, const std::set<std::string> &extensions);
73    void initProcsSharedExtensionsNULL(const std::set<std::string> &extensions);
74#endif  // defined(ANGLE_ENABLE_OPENGL_NULL)
75}};
76
77}}  // namespace rx
78
79#endif  // LIBGLESV2_RENDERER_GL_DISPATCH_TABLE_GL_AUTOGEN_H_
80"""
81
82
83def first_lower(str):
84    return str[:1].lower() + str[1:]
85
86
87def format_ep_decl(entry_point):
88    return "    PFNGL" + entry_point.upper() + "PROC " + first_lower(entry_point) + " = nullptr;"
89
90
91# Template for the initialization file of the dispatch table.
92dispatch_table_source_template = """// GENERATED FILE - DO NOT EDIT.
93// Generated by {script_name} using data from {data_source_name} and gl.xml.
94//
95// Copyright 2017 The ANGLE Project Authors. All rights reserved.
96// Use of this source code is governed by a BSD-style license that can be
97// found in the LICENSE file.
98//
99// {file_name}:
100//   Initialize the native bindings for ANGLE's OpenGL back-end.
101
102#include "libANGLE/renderer/gl/DispatchTableGL_autogen.h"
103
104#include "libANGLE/Version.h"
105#include "libANGLE/renderer/gl/FunctionsGL.h"
106
107#if defined(ANGLE_ENABLE_OPENGL_NULL)
108#include "libANGLE/renderer/gl/null_functions.h"
109#endif  // defined(ANGLE_ENABLE_OPENGL_NULL)
110
111#define ASSIGN(NAME, FP) do {{ FP = reinterpret_cast<decltype(FP)>(loadProcAddress(NAME)); }} while (0)
112
113namespace rx
114{{
115DispatchTableGL::DispatchTableGL() = default;
116
117void DispatchTableGL::initProcsDesktopGL(const gl::Version &version, const std::set<std::string> &extensions)
118{{
119#if defined(ANGLE_ENABLE_GL_DESKTOP_BACKEND)
120{gl_extensions_data}
121
122{gl_data}
123#endif  // defined(ANGLE_ENABLE_GL_DESKTOP_BACKEND)
124}}
125
126void DispatchTableGL::initProcsGLES(const gl::Version &version, const std::set<std::string> &extensions)
127{{
128{gles2_extensions_data}
129
130{gles2_data}
131}}
132
133void DispatchTableGL::initProcsSharedExtensions(const std::set<std::string> &extensions)
134{{
135{both_extensions_data}
136}}
137
138#if defined(ANGLE_ENABLE_OPENGL_NULL)
139void DispatchTableGL::initProcsDesktopGLNULL(const gl::Version &version, const std::set<std::string> &extensions)
140{{
141#if defined(ANGLE_ENABLE_GL_DESKTOP_BACKEND)
142{gl_null_extensions_data}
143
144{gl_null_data}
145#endif  // defined(ANGLE_ENABLE_GL_DESKTOP_BACKEND)
146}}
147
148void DispatchTableGL::initProcsGLESNULL(const gl::Version &version, const std::set<std::string> &extensions)
149{{
150{gles2_null_extensions_data}
151
152{gles2_null_data}
153}}
154
155void DispatchTableGL::initProcsSharedExtensionsNULL(const std::set<std::string> &extensions)
156{{
157{both_null_extensions_data}
158}}
159#endif  // defined(ANGLE_ENABLE_OPENGL_NULL)
160
161}}  // namespace rx
162"""
163
164
165def format_assign_ep(entry_point, ep):
166    return '        ASSIGN("' + ep + '", ' + first_lower(entry_point[2:]) + ');'
167
168
169def format_requirements_lines(required, entry_points):
170    major, minor = required
171    lines = ['    if (version >= gl::Version(' + major + ', ' + minor + '))', '    {']
172    lines += [format_assign_ep(entry_point, entry_point) for entry_point in sorted(entry_points)]
173    lines += ['    }']
174    return '\n'.join(lines)
175
176
177def format_extension_requirements_lines(extension, entry_points, api):
178    lines = ['    if (extensions.count("' + extension + '") != 0)', '    {']
179    lines += [format_assign_ep(entry_point, ep) for entry_point, ep in sorted(entry_points)]
180    lines += ['    }']
181    return '\n'.join(lines)
182
183
184def assign_null_line(line):
185    m = re.match(r'        ASSIGN\("gl.*", (.+)\);', line)
186    if m:
187        name = m.group(1)
188        return '        ' + name + ' = &gl' + name[0].upper() + name[1:] + 'NULL;'
189    else:
190        return line
191
192
193def assign_null(entry):
194    return '\n'.join([assign_null_line(line) for line in entry.split('\n')])
195
196
197def nullify(data):
198    return [assign_null(entry) for entry in data]
199
200
201def format_param(param):
202    return "".join(param.itertext())
203
204
205null_functions_header_template = """// GENERATED FILE - DO NOT EDIT.
206// Generated by {script_name} using data from {data_source_name} and gl.xml.
207//
208// Copyright 2017 The ANGLE Project Authors. All rights reserved.
209// Use of this source code is governed by a BSD-style license that can be
210// found in the LICENSE file.
211//
212// {file_name}:
213//   Declares the NULL/Stub bindings for the OpenGL back-end.
214
215#ifndef LIBGLESV2_RENDERER_GL_NULL_GL_FUNCTIONS_AUTOGEN_H_
216#define LIBGLESV2_RENDERER_GL_NULL_GL_FUNCTIONS_AUTOGEN_H_
217
218#include "libANGLE/renderer/gl/functionsgl_typedefs.h"
219
220namespace rx
221{{
222{table_data}
223}}  // namespace rx
224
225#endif  // LIBGLESV2_RENDERER_GL_NULL_GL_FUNCTIONS_AUTOGEN_H_
226"""
227
228null_functions_source_template = """// GENERATED FILE - DO NOT EDIT.
229// Generated by {script_name} using data from {data_source_name} and gl.xml.
230//
231// Copyright 2017 The ANGLE Project Authors. All rights reserved.
232// Use of this source code is governed by a BSD-style license that can be
233// found in the LICENSE file.
234//
235// {file_name}:
236//   Defines the NULL/Stub bindings for the OpenGL back-end.
237
238#include "libANGLE/renderer/gl/null_functions.h"
239
240namespace rx
241{{
242{table_data}
243}}  // namespace rx
244"""
245
246
247def main():
248
249    # auto_script parameters.
250    if len(sys.argv) > 1:
251        inputs = [
252            '../../../../third_party/OpenGL-Registry/src/xml/gl.xml',
253            '../angle_format.py',
254            'gl_bindings_data.json',
255        ]
256        outputs = [
257            'DispatchTableGL_autogen.cpp',
258            'DispatchTableGL_autogen.h',
259            'null_functions.cpp',
260            'null_functions.h',
261        ]
262
263        if sys.argv[1] == 'inputs':
264            print(','.join(inputs))
265        elif sys.argv[1] == 'outputs':
266            print(','.join(outputs))
267        else:
268            print('Invalid script parameters')
269            return 1
270        return 0
271
272    gl_xml_path = os.path.join('..', '..', '..', '..', 'third_party', 'OpenGL-Registry', 'src',
273                               'xml', 'gl.xml')
274    dispatch_header_path = 'DispatchTableGL_autogen.h'
275    dispatch_source_path = 'DispatchTableGL_autogen.cpp'
276    null_functions_header_path = 'null_functions.h'
277    null_functions_source_path = 'null_functions.cpp'
278
279    # Load the JSON and XML data.
280    data_source_name = 'gl_bindings_data.json'
281    json_data = angle_format.load_json(data_source_name)
282    xml_root = etree.parse(gl_xml_path).getroot()
283
284    api_feature_info = {}
285
286    core_removed_eps = []
287    for core_removed_ep in xml_root.findall('feature/remove'):
288        assert (core_removed_ep.attrib['profile'] == 'core')
289        for command in core_removed_ep.findall('./command'):
290            core_removed_eps.append(command.attrib['name'])
291
292    for feature in xml_root.findall('feature'):
293        api = feature.attrib['api']
294        name = feature.attrib['name']
295        number = feature.attrib['number']
296
297        # OpenGL ES 3.x versions are listed as api 'gles2'
298        if api != 'gl' and api != 'gles2':
299            continue
300
301        for command in feature.findall('./require/command'):
302            command_name = command.attrib['name']
303            safe_append(api_feature_info, command_name, (api, name, number))
304
305    gl_extension_commands = {}
306    gles2_extension_commands = {}
307    both_extension_commands = {}
308
309    for extension in xml_root.findall('extensions/extension'):
310        extension_name = extension.attrib['name']
311        support = extension.attrib['supported'].split('|')
312        for command in extension.findall('./require/command'):
313            command_name = command.attrib['name']
314            if 'gl' in support and 'gles2' in support:
315                # Special case for KHR extensions, since in GLES they are suffixed.
316                if '_KHR_' in extension_name and not command_name.endswith('KHR'):
317                    safe_append(gl_extension_commands, command_name, extension_name)
318                    safe_append(gles2_extension_commands, command_name, extension_name)
319                else:
320                    safe_append(both_extension_commands, command_name, extension_name)
321            elif 'gl' in support:
322                safe_append(gl_extension_commands, command_name, extension_name)
323            elif 'gles2' in support:
324                safe_append(gles2_extension_commands, command_name, extension_name)
325
326    gl_requirements = {}
327    gles2_requirements = {}
328    gl_extension_requirements = {}
329    gles2_extension_requirements = {}
330    both_extension_requirements = {}
331
332    # Used later in the NULL bindings.
333    all_entry_points = []
334
335    for comment, entry_points in sorted(json_data.items()):
336        for entry_point_no_prefix in entry_points:
337            entry_point = "gl" + entry_point_no_prefix
338
339            all_entry_points.append(entry_point)
340
341            gl_required = None
342            gles2_required = None
343
344            if entry_point in api_feature_info:
345                for api, name, number in api_feature_info[entry_point]:
346                    major, minor = number.split(".")
347                    reqs = (major, minor)
348                    if api == 'gl':
349                        if not gl_required:
350                            gl_required = reqs
351                        elif entry_point in core_removed_eps:
352                            print('Upgrade ' + entry_point + ' to ' + str(reqs) + ' instead of ' +
353                                  str(gl_required))
354                            gl_required = reqs
355                        else:
356                            print('Keep ' + entry_point + ' at ' + str(gl_required) +
357                                  ' instead of ' + str(reqs))
358                    elif api == 'gles2':
359                        if not gles2_required:
360                            gles2_required = reqs
361                        else:
362                            print("Duplicate for " + entry_point + ": " + str(reqs) + " and " +
363                                  str(gles2_required))
364                    else:
365                        raise Exception('Bad api type: ' + api)
366
367            if gl_required:
368                safe_append(gl_requirements, gl_required, entry_point)
369
370            if gles2_required:
371                safe_append(gles2_requirements, gles2_required, entry_point)
372
373            # Special case for finding extensions that overlap with core functions.
374
375            extension = False
376
377            for ep in [entry_point, entry_point + "EXT", entry_point + "ARB", entry_point + "OES"]:
378                if ep in both_extension_commands:
379                    extension = True
380                    for extension in both_extension_commands[ep]:
381                        safe_append(both_extension_requirements, extension, (entry_point, ep))
382
383                else:
384                    if ep in gl_extension_commands:
385                        extension = True
386                        for extension in gl_extension_commands[ep]:
387                            safe_append(gl_extension_requirements, extension, (entry_point, ep))
388
389                    if ep in gles2_extension_commands:
390                        extension = True
391                        for extension in gles2_extension_commands[ep]:
392                            full_ep = ep
393                            if '_KHR_' in extension:
394                                full_ep += 'KHR'
395                            safe_append(gles2_extension_requirements, extension,
396                                        (entry_point, full_ep))
397
398            if not (gl_required or gles2_required or extension):
399                raise Exception('Entry point ' + entry_point + ' not found in the xml.')
400
401    table_data = []
402    for comment, entry_points in sorted(json_data.items()):
403        formatted = ["    // " + comment]
404        formatted += [format_ep_decl(entry_point) for entry_point in sorted(entry_points)]
405
406        table_data.append("\n".join(formatted))
407
408    dispatch_table_header = dispatch_table_header_template.format(
409        script_name=os.path.basename(sys.argv[0]),
410        data_source_name=data_source_name,
411        file_name=dispatch_header_path,
412        table_data="\n\n".join(table_data))
413
414    with open(dispatch_header_path, "w") as out:
415        out.write(dispatch_table_header)
416
417    gl_data = []
418    for gl_required, entry_points in sorted(gl_requirements.items()):
419        gl_data.append(format_requirements_lines(gl_required, entry_points))
420
421    gl_extensions_data = []
422    for extension, entry_points in sorted(gl_extension_requirements.items()):
423        gl_extensions_data.append(
424            format_extension_requirements_lines(extension, entry_points, "gl"))
425
426    gles2_data = []
427    for gles2_required, entry_points in sorted(gles2_requirements.items()):
428        gles2_data.append(format_requirements_lines(gles2_required, entry_points))
429
430    gles2_extensions_data = []
431    for extension, entry_points in sorted(gles2_extension_requirements.items()):
432        gles2_extensions_data.append(
433            format_extension_requirements_lines(extension, entry_points, "gles2"))
434
435    both_extensions_data = []
436    for extension, entry_points in sorted(both_extension_requirements.items()):
437        both_extensions_data.append(
438            format_extension_requirements_lines(extension, entry_points, "gles2|gl"))
439
440    dispatch_table_source = dispatch_table_source_template.format(
441        script_name=os.path.basename(sys.argv[0]),
442        data_source_name=data_source_name,
443        file_name=dispatch_source_path,
444        gl_data="\n\n".join(gl_data),
445        gl_extensions_data="\n\n".join(gl_extensions_data),
446        gles2_data="\n\n".join(gles2_data),
447        gles2_extensions_data="\n\n".join(gles2_extensions_data),
448        both_extensions_data="\n\n".join(both_extensions_data),
449        gl_null_data="\n\n".join(nullify(gl_data)),
450        gl_null_extensions_data="\n\n".join(nullify(gl_extensions_data)),
451        gles2_null_data="\n\n".join(nullify(gles2_data)),
452        gles2_null_extensions_data="\n\n".join(nullify(gles2_extensions_data)),
453        both_null_extensions_data="\n\n".join(nullify(both_extensions_data)))
454
455    with open(dispatch_source_path, "w") as out:
456        out.write(dispatch_table_source)
457
458    # Generate the NULL/stub entry points.
459    # Process the whole set of commands
460
461    command_defs = {}
462    command_decls = {}
463
464    for command in xml_root.findall('commands/command'):
465        proto = command.find('proto')
466        command_name = proto.find('name').text
467        entry = ''.join(proto.itertext())
468        return_type = entry[:-len(command_name)]
469        entry = return_type + ' INTERNAL_GL_APIENTRY ' + entry[len(return_type):] + 'NULL('
470
471        param_text = [format_param(param) for param in command.findall('param')]
472        entry += ', '.join(param_text) + ')'
473
474        command_decls[command_name] = entry + ';'
475
476        entry += '\n{\n'
477        if return_type != 'void ':
478            entry += '    return static_cast<' + return_type + '>(0);\n'
479        entry += '}'
480
481        command_defs[command_name] = entry
482
483    null_decls = [command_decls[entry_point] for entry_point in sorted(all_entry_points)]
484    null_stubs = [command_defs[entry_point] for entry_point in sorted(all_entry_points)]
485
486    null_functions_header = null_functions_header_template.format(
487        script_name=os.path.basename(sys.argv[0]),
488        data_source_name=data_source_name,
489        file_name=null_functions_header_path,
490        table_data="\n".join(null_decls))
491
492    with open(null_functions_header_path, "w") as out:
493        out.write(null_functions_header)
494
495    null_functions_source = null_functions_source_template.format(
496        script_name=os.path.basename(sys.argv[0]),
497        data_source_name=data_source_name,
498        file_name=null_functions_source_path,
499        table_data="\n\n".join(null_stubs))
500
501    with open(null_functions_source_path, "w") as out:
502        out.write(null_functions_source)
503    return 0
504
505
506if __name__ == '__main__':
507    sys.exit(main())
508