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