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