xref: /aosp_15_r20/external/angle/scripts/gen_gl_enum_utils.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1#!/usr/bin/python3
2#
3# Copyright 2019 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_gl_enum_utils.py:
8#   Generates GLenum value to string mapping for ANGLE
9#   NOTE: don't run this script directly. Run scripts/run_code_generation.py.
10
11import sys
12import os
13
14import registry_xml
15
16template_gl_enums_header = """// GENERATED FILE - DO NOT EDIT.
17// Generated by {script_name} using data from {data_source_name}.
18//
19// Copyright 2019 The ANGLE Project Authors. All rights reserved.
20// Use of this source code is governed by a BSD-style license that can be
21// found in the LICENSE file.
22//
23// gl_enum_utils_autogen.h:
24//   mapping of GLenum value to string.
25
26# ifndef COMMON_GL_ENUM_UTILS_AUTOGEN_H_
27# define COMMON_GL_ENUM_UTILS_AUTOGEN_H_
28
29namespace gl
30{{
31enum class GLESEnum
32{{
33    {gles_enum_groups}
34}};
35
36enum class BigGLEnum
37{{
38    {gl_enum_groups}
39}};
40}}  // namespace gl
41
42# endif  // COMMON_GL_ENUM_UTILS_AUTOGEN_H_
43"""
44
45template_gl_enums_source = """// GENERATED FILE - DO NOT EDIT.
46// Generated by {script_name} using data from {data_source_name}.
47//
48// Copyright 2019 The ANGLE Project Authors. All rights reserved.
49// Use of this source code is governed by a BSD-style license that can be
50// found in the LICENSE file.
51//
52// gl_enum_utils_autogen.cpp:
53//   mapping of GLenum value to string.
54
55#include "common/gl_enum_utils_autogen.h"
56
57#include "common/debug.h"
58#include "common/gl_enum_utils.h"
59
60#include <algorithm>
61#include <cstring>
62
63namespace gl
64{{
65namespace
66{{
67const char *UnknownEnumToString(unsigned int value)
68{{
69    constexpr size_t kBufferSize = 64;
70    static thread_local char sBuffer[kBufferSize];
71    snprintf(sBuffer, kBufferSize, "0x%04X", value);
72    return sBuffer;
73}}
74}}  // anonymous namespace
75
76const char *GLenumToString(GLESEnum enumGroup, unsigned int value)
77{{
78    switch (enumGroup)
79    {{
80        {gles_enums_value_to_string_table}
81        default:
82            return UnknownEnumToString(value);
83    }}
84}}
85
86const char *GLenumToString(BigGLEnum enumGroup, unsigned int value)
87{{
88    switch (enumGroup)
89    {{
90        {gl_enums_value_to_string_table}
91        default:
92            return UnknownEnumToString(value);
93    }}
94}}
95
96namespace
97{{
98using StringEnumEntry = std::pair<const char*, unsigned int>;
99static StringEnumEntry g_stringEnumTable[] = {{
100    {string_to_enum_table}
101}};
102
103const size_t g_numStringEnums = std::size(g_stringEnumTable);
104}}  // anonymous namespace
105
106unsigned int StringToGLenum(const char *str)
107{{
108    auto it = std::lower_bound(
109        &g_stringEnumTable[0], &g_stringEnumTable[g_numStringEnums], str,
110        [](const StringEnumEntry& a, const char* b) {{ return strcmp(a.first, b) < 0; }});
111
112    if (strcmp(it->first, str) == 0)
113    {{
114        return it->second;
115    }}
116
117    UNREACHABLE();
118    return 0;
119}}
120}}  // namespace gl
121
122"""
123
124template_enum_group_case = """case {api_enum}::{group_name}: {{
125    switch (value) {{
126        {inner_group_cases}
127        default:
128            return UnknownEnumToString(value);
129    }}
130}}
131"""
132
133template_enum_value_to_string_case = """case {value}: return {name};"""
134exclude_enum_groups = {'SpecialNumbers'}
135
136# Special enum groups that don't have any enum values.
137# Add groups here if you get missing group compile errors.
138empty_enum_groups = ['SemaphoreParameterName', 'ShaderBinaryFormat']
139
140
141def dump_value_to_string_mapping(enum_groups, api_enum):
142    exporting_groups = list()
143    for group_name, inner_mapping in enum_groups.items():
144        # Convert to pairs and strip out-of-range values.
145        string_value_pairs = list(
146            filter(lambda x: x[1] >= 0 and x[1] <= 0xFFFFFFFFF, inner_mapping.items()))
147        if not string_value_pairs:
148            continue
149
150        # sort according values
151        string_value_pairs.sort(key=lambda x: (x[1], len(x[0]), x[0]))
152
153        # remove all duplicate values from the pairs list
154        # some value may have more than one GLenum mapped to them, such as:
155        #     GL_DRAW_FRAMEBUFFER_BINDING and GL_FRAMEBUFFER_BINDING
156        #     GL_BLEND_EQUATION_RGB and GL_BLEND_EQUATION
157        # it is safe to output either one of them, for simplity here just
158        # choose the shorter one which comes first in the sorted list
159        exporting_string_value_pairs = list()
160        for index, pair in enumerate(string_value_pairs):
161            if index == 0 or pair[1] != string_value_pairs[index - 1][1]:
162                exporting_string_value_pairs.append(pair)
163
164        inner_code_block = "\n".join([
165            template_enum_value_to_string_case.format(
166                value='0x%X' % value,
167                name='"%s"' % name,
168            ) for name, value in exporting_string_value_pairs
169        ])
170
171        exporting_groups.append((group_name, inner_code_block))
172
173    return "\n".join([
174        template_enum_group_case.format(
175            api_enum=api_enum,
176            group_name=group_name,
177            inner_group_cases=inner_code_block,
178        ) for group_name, inner_code_block in sorted(exporting_groups, key=lambda x: x[0])
179    ])
180
181
182def dump_string_to_value_mapping(enums_and_values):
183
184    def f(value):
185        if value < 0:
186            return str(value)
187        if value < 0xFFFF:
188            return "0x%04X" % value
189        if value <= 0xFFFFFFFF:
190            return "0x%X" % value
191        else:
192            return "0xFFFFFFFF"
193
194    return '\n'.join('{"%s", %s},' % (k, f(v)) for k, v in sorted(enums_and_values))
195
196
197def main(header_output_path, source_output_path):
198    xml = registry_xml.RegistryXML('gl.xml', 'gl_angle_ext.xml')
199
200    # Compute a list of all GLES enums.
201    gles_enums = set()
202    bigl_enums = set()
203    for feature in xml.root.findall('feature'):
204        for require in feature.findall('require'):
205            assert 'api' not in require.attrib
206            if 'gles' in feature.attrib['api']:
207                for enum in require.findall('enum'):
208                    gles_enums.add(enum.attrib['name'])
209            if feature.attrib['api'] == 'gl':
210                for enum in require.findall('enum'):
211                    bigl_enums.add(enum.attrib['name'])
212
213    for extensions in xml.root.findall('extensions'):
214        for extension in extensions.findall('extension'):
215            if extension.attrib['name'] in registry_xml.supported_extensions:
216                for require in extension.findall('require'):
217                    ext_apis = extension.attrib['supported'].split('|')
218                    if ('api' not in require.attrib or 'gles' in require.attrib['api']) and (
219                            'gles' in extension.attrib['supported']):
220                        for enum in require.findall('enum'):
221                            gles_enums.add(enum.attrib['name'])
222                    if ('api' not in require.attrib or
223                            feature.attrib['api'] == 'gl') and ('gl' in ext_apis):
224                        for enum in require.findall('enum'):
225                            bigl_enums.add(enum.attrib['name'])
226
227    # Build a map from GLenum name to its value
228    gl_enum_groups = dict()
229    gles_enum_groups = dict()
230
231    # Add all enums to default groups
232    gl_default_enums = dict()
233    gles_default_enums = dict()
234    gl_enum_groups[registry_xml.default_enum_group_name] = gl_default_enums
235    gles_enum_groups[registry_xml.default_enum_group_name] = gles_default_enums
236    enums_and_values = []
237
238    for enums_node in xml.root.findall('enums'):
239        for enum in enums_node.findall('enum'):
240            enum_name = enum.attrib['name']
241            enum_value = int(enum.attrib['value'], base=16)
242            enums_and_values.append((enum_name, enum_value))
243
244            if enum_name in gles_enums:
245                gles_default_enums[enum_name] = enum_value
246            if enum_name in bigl_enums:
247                gl_default_enums[enum_name] = enum_value
248
249            if 'group' in enum.attrib:
250                for enum_group in enum.attrib['group'].split(','):
251                    if enum_group in exclude_enum_groups:
252                        continue
253                    if enum_name in gles_enums:
254                        if enum_group not in gles_enum_groups:
255                            gles_enum_groups[enum_group] = dict()
256                        gles_enum_groups[enum_group][enum_name] = enum_value
257                    if enum_name in bigl_enums:
258                        if enum_group not in gl_enum_groups:
259                            gl_enum_groups[enum_group] = dict()
260                        gl_enum_groups[enum_group][enum_name] = enum_value
261
262    for empty_group in empty_enum_groups:
263        assert not empty_group in gles_enum_groups or not empty_group in gl_enum_groups, 'Remove %s from the empty groups list, it has enums now.' % empty_group
264        if empty_group not in gles_enum_groups:
265            gles_enum_groups[empty_group] = dict()
266        if empty_group not in gl_enum_groups:
267            gl_enum_groups[empty_group] = dict()
268
269    # Write GLenum groups into the header file.
270    header_content = template_gl_enums_header.format(
271        script_name=os.path.basename(sys.argv[0]),
272        data_source_name="gl.xml and gl_angle_ext.xml",
273        gles_enum_groups=',\n'.join(sorted(gles_enum_groups.keys())),
274        gl_enum_groups=',\n'.join(sorted(gl_enum_groups.keys())))
275
276    header_output_path = registry_xml.script_relative(header_output_path)
277    with open(header_output_path, 'w') as f:
278        f.write(header_content)
279
280    # Write mapping to source file
281    gles_enums_value_to_string_table = dump_value_to_string_mapping(gles_enum_groups, 'GLESEnum')
282    gl_enums_value_to_string_table = dump_value_to_string_mapping(gl_enum_groups, 'BigGLEnum')
283    string_to_enum_table = dump_string_to_value_mapping(enums_and_values)
284    source_content = template_gl_enums_source.format(
285        script_name=os.path.basename(sys.argv[0]),
286        data_source_name="gl.xml and gl_angle_ext.xml",
287        gles_enums_value_to_string_table=gles_enums_value_to_string_table,
288        gl_enums_value_to_string_table=gl_enums_value_to_string_table,
289        string_to_enum_table=string_to_enum_table,
290    )
291
292    source_output_path = registry_xml.script_relative(source_output_path)
293    with open(source_output_path, 'w') as f:
294        f.write(source_content)
295
296    return 0
297
298
299if __name__ == '__main__':
300    inputs = [
301        '../third_party/OpenGL-Registry/src/xml/gl.xml',
302        'gl_angle_ext.xml',
303        'registry_xml.py',
304    ]
305
306    gl_enum_utils_autogen_base_path = '../src/common/gl_enum_utils_autogen'
307    outputs = [
308        gl_enum_utils_autogen_base_path + '.h',
309        gl_enum_utils_autogen_base_path + '.cpp',
310    ]
311
312    if len(sys.argv) > 1:
313        if sys.argv[1] == 'inputs':
314            print(','.join(inputs))
315        elif sys.argv[1] == 'outputs':
316            print(','.join(outputs))
317    else:
318        sys.exit(
319            main(
320                registry_xml.script_relative(outputs[0]),
321                registry_xml.script_relative(outputs[1])))
322