xref: /aosp_15_r20/external/angle/src/libANGLE/gen_extensions.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1#!/usr/bin/python3
2#
3# Copyright 2021 The ANGLE Project Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6#
7# gen_extensions.py:
8#   Generates files from supported extensions data.
9#   NOTE: don't run this script directly. Run scripts/run_code_generation.py.
10
11import json
12import os
13import re
14import sys
15
16d = os.path.dirname
17THIS_DIR = d(os.path.abspath(__file__))
18ANGLE_SRC_DIR = d(d(THIS_DIR))
19SCRIPTS_DIR = os.path.join(ANGLE_SRC_DIR, 'scripts')
20sys.path.insert(0, SCRIPTS_DIR)
21
22import registry_xml
23
24_MD_GLES_GPU_CONFIGS = [
25    'NVIDIA 1660 Win10',
26    'Intel 630 Win10',
27    'NVIDIA 1660 Linux',
28    'Intel 630 Linux',
29    'SwiftShader Win10',
30    'Pixel 4 Android 11',
31    'Pixel 6 Android 13',
32]
33
34_MD_GLES1_GPU_CONFIGS = [
35    'SwiftShader Win10',
36]
37
38_GLES_EXTENSIONS_TEMPLATE = """\
39// GENERATED FILE - DO NOT EDIT.
40// Generated by {script_name} using data from {data_source_name}
41//
42// Copyright 2021 The ANGLE Project Authors. All rights reserved.
43// Use of this source code is governed by a BSD-style license that can be
44// found in the LICENSE file.
45//
46// {filename}: GLES extension information.
47
48#ifndef LIBANGLE_GLES_EXTENSIONS_AUTOGEN_H_
49#define LIBANGLE_GLES_EXTENSIONS_AUTOGEN_H_
50
51namespace gl
52{{
53class TextureCapsMap;
54
55struct Extensions
56{{
57    Extensions();
58    Extensions(const Extensions &other);
59
60    Extensions &operator=(const Extensions &other);
61
62    // Generate a vector of supported extension strings
63    std::vector<std::string> getStrings() const;
64
65    // Set all texture related extension support based on the supported textures.
66    // Determines support for:
67    // GL_OES_packed_depth_stencil
68    // GL_OES_rgb8_rgba8
69    // GL_EXT_texture_format_BGRA8888
70    // GL_EXT_color_buffer_half_float,
71    // GL_OES_texture_half_float, GL_OES_texture_half_float_linear
72    // GL_OES_texture_float, GL_OES_texture_float_linear
73    // GL_EXT_texture_rg
74    // GL_EXT_texture_type_2_10_10_10_REV
75    // GL_EXT_texture_compression_dxt1, GL_ANGLE_texture_compression_dxt3,
76    // GL_ANGLE_texture_compression_dxt5
77    // GL_KHR_texture_compression_astc_ldr, GL_OES_texture_compression_astc.
78    //     NOTE: GL_KHR_texture_compression_astc_hdr must be enabled separately. Support for the
79    //           HDR profile cannot be determined from the format enums alone.
80    // GL_OES_compressed_ETC1_RGB8_texture
81    // GL_EXT_sRGB
82    // GL_ANGLE_depth_texture, GL_OES_depth32
83    // GL_EXT_color_buffer_float
84    // GL_EXT_texture_norm16
85    // GL_EXT_texture_compression_bptc
86    // GL_EXT_texture_compression_rgtc
87    void setTextureExtensionSupport(const TextureCapsMap &textureCaps);
88
89    // Helper functions
90{helper_functions}
91
92    // GLES 2.0+ extensions
93    // --------------------
94
95{gles_extensions}
96    // ANGLE unofficial extensions
97    // ---------------------------
98
99{angle_extensions}
100    // GLES 1.0 and 1.1 extensions
101    // ---------------------------
102
103{gles1_extensions}}};
104}}  // namespace gl
105
106#endif  // LIBANGLE_GLES_EXTENSIONS_AUTOGEN_H_
107"""
108
109_EXT_MEMBER_TEMPLATE = """\
110    // {full_name}
111    bool {name_camel_case}{vendor} = false;
112"""
113
114_HELPER_TEMPLATE = """    bool {ext_name}Any() const {{ return ({expression}); }}"""
115
116_GLES_EXT_STRINGS_TEMPLATE = """\
117// GENERATED FILE - DO NOT EDIT.
118// Generated by {script_name} using data from {data_source_name}
119//
120// Copyright 2021 The ANGLE Project Authors. All rights reserved.
121// Use of this source code is governed by a BSD-style license that can be
122// found in the LICENSE file.
123//
124// {filename}: GLES extension strings information.
125
126#include "anglebase/no_destructor.h"
127#include "libANGLE/Caps.h"
128
129namespace gl
130{{
131const ExtensionInfoMap &GetExtensionInfoMap()
132{{
133    auto buildExtensionInfoMap = []() {{
134        auto enableableExtension = [](ExtensionBool member) {{
135            ExtensionInfo info;
136            info.Requestable      = true;
137            info.ExtensionsMember = member;
138            return info;
139        }};
140
141        auto enableableDisablableExtension = [&](ExtensionBool member) {{
142            ExtensionInfo info = enableableExtension(member);
143            info.Disablable    = true;
144            return info;
145        }};
146
147        auto esOnlyExtension = [](ExtensionBool member) {{
148            ExtensionInfo info;
149            info.ExtensionsMember = member;
150            return info;
151        }};
152
153        // clang-format off
154        ExtensionInfoMap map;
155
156        // GLES 2.0 extension strings
157        // --------------------------
158{gles_strings}
159
160        // ANGLE unofficial extension strings
161        // ----------------------------------
162{angle_strings}
163
164        // GLES 1.0 and 1.1 extension strings
165        // ----------------------------------
166{gles1_strings}
167        // clang-format on
168
169#if defined(ANGLE_ENABLE_ASSERTS)
170        // Verify all extension strings start with GL_
171        for (const auto &extension : map)
172        {{
173            ASSERT(extension.first.rfind("GL_", 0) == 0);
174        }}
175#endif
176
177        return map;
178    }};
179
180    static const angle::base::NoDestructor<ExtensionInfoMap> extensionInfo(buildExtensionInfoMap());
181    return *extensionInfo;
182}}
183}} // namespace gl
184"""
185
186_EXT_STRING_TEMPLATE = """\
187        map["{full_name}"] = {mode}Extension(&Extensions::{name_camel_case}{vendor});"""
188
189ESONLY = 'esOnly'
190REQUESTABLE = 'enableable'
191TOGGLEABLE = 'enableableDisablable'
192
193_MARKDOWN_TEMPLATE = """\
194# ANGLE Supported Extensions
195
196This is a list of all extensions currently supported by ANGLE's front-end, and
197support listed for some of the tested targets for ANGLE's Vulkan back-end. To
198produce a list of all supported extensions in the Vulkan back-end, run
199`angle_end2end_tests` with `--gtest_filter EGLPrintEGLinfoTest.PrintGLInfo/ES*_Vulkan`.
200
201Specifications for GLES extensions can be found in the [Khronos OpenGL ES API
202Registry](http://www.khronos.org/registry/gles/)
203
204Specifications for EGL extensions can be found in the [Khronos EGL API
205Registry](http://www.khronos.org/registry/egl/)
206
207Specifications for ANGLE-specific extensions can be found in the [ANGLE
208extension registry](../extensions)
209
210This list is automatically generated by [`{script_name}`](../src/libANGLE/gen_extensions.py)
211using data from {data_source_name}.
212
213## GLES 2.0, 3.0, 3.1 and 3.2 extension support
214
215*Note: some data is sampled from older drivers, so might not represent the latest driver support.*
216
217{gles_md_table_header}
218{gles_md_exts}
219
220## ANGLE unofficial extension support
221
222*Note: some ANGLE extensions are currently missing specifications.*
223
224{angle_md_table_header}
225{angle_md_exts}
226
227## GLES 1.0 and 1.1 extension support
228
229{gles1_md_table_header}
230{gles1_md_exts}
231
232## EGL extension support
233
234Currently EGL extensions are not automatically tracked by our scripting. For a
235list of supported EGL extensions in ANGLE's front-end see
236[`src/libANGLE/Caps.h`](../src/libANGLE/Caps.h).
237
238## Configuration information
239
240{md_gpu_info}
241## How to update supported extension data
242
243Supported extension data is stored in the ANGLE repo as JSON files in
244[`scripts/extension_data`](../scripts/extension_data). The JSON data is
245sourced from public ANGLE test runs. Look for `angle_end2end_tests` in a bot
246run: [example link](https://ci.chromium.org/ui/p/angle/builders/ci/win-test/520/overview).
247Search for "`angle_end2end_tests`", then click on the "cas output" and find
248`GLinfo_ES3_2_Vulkan.json` or `GLinfo_ES3_1_Vulkan_SwiftShader.json` for
249SwiftShader.
250
251All data except for GLES 1 is automatically updated using
252the [`update_extension_data.py`](../scripts/update_extension_data.py) script.
253To use it first authenticate to the `bb` and `luci-go` tools by running `bb
254auth-login` and `./tools/luci-go/swarming login`. Then run the script and
255re-run [code generation][CodeGen].
256
257The GLES 1 data is currently manually updated. Find the relevant
258file from the task output (see above) and overwrite the correspoding file.
259Re-run [code generation][CodeGen] and create a CL as per our normal process.
260
261To add a new configuration, first retrieve the JSON data, modify
262[`gen_extensions.py`](../src/libANGLE/gen_extensions.py) as necessary, then
263run [`scripts/run_code_generation.py`][CodeGen] to refresh generated files.
264Also update `update_extension_data.py` as necessary.
265
266[CodeGen]: ../scripts/run_code_generation.py
267"""
268
269_MD_TABLE_HEADER_TEMPLATE = """\
270| Extension Name | {configs} |
271| -------------- | {dashes} |"""
272
273_MD_CONFIG_INFO_TEMPLATE = """\
274{config}:
275
276 * `GL_RENDERER` is `{Renderer}`
277 * `GL_VENDOR` is `{Vendor}`
278 * `GL_VERSION` is `{Version}`
279 * Data updated {DateRecorded}
280"""
281
282_MD_GLES_EXT_LINK_TEMPLATE = """[{full_name}](https://khronos.org/registry/OpenGL/extensions/{vendor}/{vendor}_{link}.txt)"""
283_MD_ANGLE_EXT_LINK_TEMPLATE = """[{full_name}](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/{vendor}_{link}.txt)"""
284
285# Some extensions are defined in documents that have different names.
286_LINK_OVERRIDES = {
287    'GL_ANGLE_shader_pixel_local_storage_coherent': 'shader_pixel_local_storage',
288    'GL_EXT_memory_object_fd': 'external_objects_fd',
289    'GL_EXT_semaphore_fd': 'external_objects_fd',
290}
291
292
293def get_camel_case(name_with_underscores):
294    """ To follow ANGLE naming for member variables, we convert the canonical extension:
295    0. Delete the API and vendor prefix.
296    1. Capitalize letters after underscores.
297    2. Delete underscores.
298    3. Add back the vendor prefix to the end. """
299    words = name_with_underscores.split('_')
300    words = [words[0]] + [(word[0].upper() + word[1:]) for word in words[1:]]
301    return ''.join(words)
302
303
304def break_down_ext(ext, expr, mode):
305    """ This splits an extension name like GL_EXT_buffer_storage into string components. """
306    m = expr.match(ext)
307    return {
308        'full_name': ext,
309        'api_prefix': m.group(1),
310        'vendor': m.group(2),
311        'name_with_underscores': m.group(3),
312        'name_camel_case': get_camel_case(m.group(3)),
313        'mode': mode,
314        'link': _LINK_OVERRIDES.get(ext, m.group(3)),
315    }
316
317
318def break_down_exts(exts, expr, mode):
319    return [break_down_ext(ext, expr, mode) for ext in exts]
320
321
322def format_exts(ext_infos):
323    return '\n'.join([_EXT_MEMBER_TEMPLATE.format(**ext_info) for ext_info in ext_infos])
324
325
326def format_helper_function(ext_name, vendors):
327    return _HELPER_TEMPLATE.format(
328        ext_name=ext_name,
329        expression=' || '.join(['%s%s' % (ext_name, vendor) for vendor in vendors]),
330    )
331
332
333def format_ext_strings(ext_infos):
334    return '\n'.join([_EXT_STRING_TEMPLATE.format(**ext_info) for ext_info in ext_infos])
335
336
337def write_file(fname, template, format_args):
338    with open(fname, 'w') as f:
339        formatted = template.format(**format_args)
340        f.write(formatted)
341        f.close()
342
343
344def sort_by_ext_name(ext_infos):
345    return sorted(ext_infos, key=lambda e: e['name_camel_case'].lower())
346
347
348def get_ext_support(ext_name, gpu_data):
349
350    def s(ext, support):
351        SUPPORT_SYM = '&#x2714;'
352        NOSUPPORT_SYM = ''
353        return SUPPORT_SYM if ext in support['Extensions'] else NOSUPPORT_SYM
354
355    return ' | '.join([s(ext_name, support) for support in gpu_data])
356
357
358def get_md_table_header(md_gpu_configs):
359    configs = ' | '.join(md_gpu_configs)
360    dashes = ' | '.join([(':%s:' % ('-' * (len(config) - 2))) for config in md_gpu_configs])
361    return _MD_TABLE_HEADER_TEMPLATE.format(configs=configs, dashes=dashes)
362
363
364def format_md_gpu_info(gpu_data):
365    return _MD_CONFIG_INFO_TEMPLATE.format(**gpu_data)
366
367
368def format_md_link(ext_info, link_template):
369    return link_template.format(**ext_info)
370
371
372def format_md_ext(ext_info, gpu_json_data, link_template):
373    return '| %s | %s |' % (format_md_link(
374        ext_info, link_template), get_ext_support(ext_info['full_name'], gpu_json_data))
375
376
377def format_md_exts(ext_infos, gpu_json_data, link_template):
378    return '\n'.join(
379        [format_md_ext(ext_info, gpu_json_data, link_template) for ext_info in ext_infos])
380
381
382def main():
383    # auto_script parameters.
384    data_source_name = 'registry_xml.py and gl.xml'
385    gles_h_output_name = 'gles_extensions_autogen.h'
386    gles_cpp_output_name = 'gles_extensions_autogen.cpp'
387    md_output_name = '../../doc/ExtensionSupport.md'
388    ext_jsons = [
389        '../../scripts/extension_data/%s.json' % s.lower().replace(' ', '_')
390        for s in _MD_GLES_GPU_CONFIGS
391    ]
392    gles1_ext_jsons = [
393        '../../scripts/extension_data/%s_gles1.json' % s.lower().replace(' ', '_')
394        for s in _MD_GLES1_GPU_CONFIGS
395    ]
396    if len(sys.argv) > 1:
397        inputs = ['../../scripts/%s' % xml_input for xml_input in registry_xml.xml_inputs
398                 ] + ext_jsons + gles1_ext_jsons
399        outputs = [gles_h_output_name, gles_cpp_output_name, md_output_name]
400        if sys.argv[1] == 'inputs':
401            print(','.join(inputs))
402        elif sys.argv[1] == 'outputs':
403            print(','.join(outputs))
404        else:
405            print('Invalid script parameters.')
406            return 1
407        return 0
408
409    expr = re.compile(r'^([A-Z]+)_([A-Z]+)_(\w+)$')
410
411    angle_ext_infos = (
412        break_down_exts(registry_xml.angle_requestable_extensions, expr, REQUESTABLE) +
413        break_down_exts(registry_xml.angle_es_only_extensions, expr, ESONLY) +
414        break_down_exts(registry_xml.angle_toggleable_extensions, expr, TOGGLEABLE))
415
416    angle_ext_infos = sort_by_ext_name(angle_ext_infos)
417
418    gles_ext_infos = (
419        break_down_exts(registry_xml.gles_requestable_extensions, expr, REQUESTABLE) +
420        break_down_exts(registry_xml.gles_es_only_extensions, expr, ESONLY))
421
422    gles_ext_infos = sort_by_ext_name(gles_ext_infos)
423
424    gles1_ext_infos = break_down_exts(registry_xml.gles1_extensions, expr, REQUESTABLE)
425
426    gles1_ext_infos = sort_by_ext_name(gles1_ext_infos)
427
428    ext_infos = angle_ext_infos + gles_ext_infos + gles1_ext_infos
429
430    ext_name_to_vendors = {}
431    for info in ext_infos:
432        ext_name = info['name_camel_case']
433        if ext_name in ext_name_to_vendors:
434            ext_name_to_vendors[ext_name] += [info['vendor']]
435        else:
436            ext_name_to_vendors[ext_name] = [info['vendor']]
437
438    helper_function_data = []
439    for (ext_name, vendors) in sorted(ext_name_to_vendors.items()):
440        if len(vendors) > 1:
441            helper_function_data += [format_helper_function(ext_name, vendors)]
442
443    helper_functions = '\n'.join(helper_function_data)
444
445    gles_gpu_data = []
446    for (gpu_config, ext_json) in zip(_MD_GLES_GPU_CONFIGS, ext_jsons):
447        with open(ext_json) as f:
448            config_support = json.loads(f.read())
449            config_support['config'] = gpu_config
450            gles_gpu_data.append(config_support)
451
452    gles1_gpu_data = []
453    for (gpu_config, ext_json) in zip(_MD_GLES1_GPU_CONFIGS, gles1_ext_jsons):
454        with open(ext_json) as f:
455            config_support = json.loads(f.read())
456            config_support['config'] = gpu_config
457            gles1_gpu_data.append(config_support)
458
459    gles_md_exts = format_md_exts(gles_ext_infos, gles_gpu_data, _MD_GLES_EXT_LINK_TEMPLATE)
460    angle_md_exts = format_md_exts(angle_ext_infos, gles_gpu_data, _MD_ANGLE_EXT_LINK_TEMPLATE)
461    gles1_md_exts = format_md_exts(gles1_ext_infos, gles1_gpu_data, _MD_GLES_EXT_LINK_TEMPLATE)
462    md_gpu_info = [format_md_gpu_info(gpu_data) for gpu_data in gles_gpu_data]
463
464    format_args = {
465        'script_name': os.path.basename(__file__),
466        'data_source_name': os.path.basename(data_source_name),
467        'filename': gles_h_output_name,
468        'gles_extensions': format_exts(gles_ext_infos),
469        'angle_extensions': format_exts(angle_ext_infos),
470        'gles1_extensions': format_exts(gles1_ext_infos),
471        'helper_functions': helper_functions,
472        'angle_strings': format_ext_strings(angle_ext_infos),
473        'gles_strings': format_ext_strings(gles_ext_infos),
474        'gles1_strings': format_ext_strings(gles1_ext_infos),
475        'gles_md_table_header': get_md_table_header(_MD_GLES_GPU_CONFIGS),
476        'gles_md_exts': gles_md_exts,
477        'angle_md_table_header': get_md_table_header(_MD_GLES_GPU_CONFIGS),
478        'angle_md_exts': angle_md_exts,
479        'gles1_md_table_header': get_md_table_header(_MD_GLES1_GPU_CONFIGS),
480        'gles1_md_exts': gles1_md_exts,
481        'md_gpu_info': '\n'.join(md_gpu_info),
482    }
483
484    write_file(gles_h_output_name, _GLES_EXTENSIONS_TEMPLATE, format_args)
485    write_file(gles_cpp_output_name, _GLES_EXT_STRINGS_TEMPLATE, format_args)
486    write_file(md_output_name, _MARKDOWN_TEMPLATE, format_args)
487
488    return 0
489
490
491if __name__ == '__main__':
492    sys.exit(main())
493