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