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