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_OPENGL_DESKTOP) 120{gl_extensions_data} 121 122{gl_data} 123#endif // defined(ANGLE_ENABLE_OPENGL_DESKTOP) 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_OPENGL_DESKTOP) 142{gl_null_extensions_data} 143 144{gl_null_data} 145#endif // defined(ANGLE_ENABLE_OPENGL_DESKTOP) 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 '../../../../scripts/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('..', '..', '..', '..', 'scripts', 'gl.xml') 273 dispatch_header_path = 'DispatchTableGL_autogen.h' 274 dispatch_source_path = 'DispatchTableGL_autogen.cpp' 275 null_functions_header_path = 'null_functions.h' 276 null_functions_source_path = 'null_functions.cpp' 277 278 # Load the JSON and XML data. 279 data_source_name = 'gl_bindings_data.json' 280 json_data = angle_format.load_json(data_source_name) 281 xml_root = etree.parse(gl_xml_path).getroot() 282 283 api_feature_info = {} 284 285 core_removed_eps = [] 286 for core_removed_ep in xml_root.findall('feature/remove'): 287 assert (core_removed_ep.attrib['profile'] == 'core') 288 for command in core_removed_ep.findall('./command'): 289 core_removed_eps.append(command.attrib['name']) 290 291 for feature in xml_root.findall('feature'): 292 api = feature.attrib['api'] 293 name = feature.attrib['name'] 294 number = feature.attrib['number'] 295 296 # OpenGL ES 3.x versions are listed as api 'gles2' 297 if api != 'gl' and api != 'gles2': 298 continue 299 300 for command in feature.findall('./require/command'): 301 command_name = command.attrib['name'] 302 safe_append(api_feature_info, command_name, (api, name, number)) 303 304 gl_extension_commands = {} 305 gles2_extension_commands = {} 306 both_extension_commands = {} 307 308 for extension in xml_root.findall('extensions/extension'): 309 extension_name = extension.attrib['name'] 310 support = extension.attrib['supported'].split('|') 311 for command in extension.findall('./require/command'): 312 command_name = command.attrib['name'] 313 if 'gl' in support and 'gles2' in support: 314 # Special case for KHR extensions, since in GLES they are suffixed. 315 if '_KHR_' in extension_name and not command_name.endswith('KHR'): 316 safe_append(gl_extension_commands, command_name, extension_name) 317 safe_append(gles2_extension_commands, command_name, extension_name) 318 else: 319 safe_append(both_extension_commands, command_name, extension_name) 320 elif 'gl' in support: 321 safe_append(gl_extension_commands, command_name, extension_name) 322 elif 'gles2' in support: 323 safe_append(gles2_extension_commands, command_name, extension_name) 324 325 gl_requirements = {} 326 gles2_requirements = {} 327 gl_extension_requirements = {} 328 gles2_extension_requirements = {} 329 both_extension_requirements = {} 330 331 # Used later in the NULL bindings. 332 all_entry_points = [] 333 334 for comment, entry_points in sorted(json_data.items()): 335 for entry_point_no_prefix in entry_points: 336 entry_point = "gl" + entry_point_no_prefix 337 338 all_entry_points.append(entry_point) 339 340 gl_required = None 341 gles2_required = None 342 343 if entry_point in api_feature_info: 344 for api, name, number in api_feature_info[entry_point]: 345 major, minor = number.split(".") 346 reqs = (major, minor) 347 if api == 'gl': 348 if not gl_required: 349 gl_required = reqs 350 elif entry_point in core_removed_eps: 351 print('Upgrade ' + entry_point + ' to ' + str(reqs) + ' instead of ' + 352 str(gl_required)) 353 gl_required = reqs 354 else: 355 print('Keep ' + entry_point + ' at ' + str(gl_required) + 356 ' instead of ' + str(reqs)) 357 elif api == 'gles2': 358 if not gles2_required: 359 gles2_required = reqs 360 else: 361 print("Duplicate for " + entry_point + ": " + str(reqs) + " and " + 362 str(gles2_required)) 363 else: 364 raise Exception('Bad api type: ' + api) 365 366 if gl_required: 367 safe_append(gl_requirements, gl_required, entry_point) 368 369 if gles2_required: 370 safe_append(gles2_requirements, gles2_required, entry_point) 371 372 # Special case for finding extensions that overlap with core functions. 373 374 extension = False 375 376 for ep in [entry_point, entry_point + "EXT", entry_point + "ARB", entry_point + "OES"]: 377 if ep in both_extension_commands: 378 extension = True 379 for extension in both_extension_commands[ep]: 380 safe_append(both_extension_requirements, extension, (entry_point, ep)) 381 382 else: 383 if ep in gl_extension_commands: 384 extension = True 385 for extension in gl_extension_commands[ep]: 386 safe_append(gl_extension_requirements, extension, (entry_point, ep)) 387 388 if ep in gles2_extension_commands: 389 extension = True 390 for extension in gles2_extension_commands[ep]: 391 full_ep = ep 392 if '_KHR_' in extension: 393 full_ep += 'KHR' 394 safe_append(gles2_extension_requirements, extension, 395 (entry_point, full_ep)) 396 397 if not (gl_required or gles2_required or extension): 398 raise Exception('Entry point ' + entry_point + ' not found in the xml.') 399 400 table_data = [] 401 for comment, entry_points in sorted(json_data.items()): 402 formatted = [" // " + comment] 403 formatted += [format_ep_decl(entry_point) for entry_point in sorted(entry_points)] 404 405 table_data.append("\n".join(formatted)) 406 407 dispatch_table_header = dispatch_table_header_template.format( 408 script_name=os.path.basename(sys.argv[0]), 409 data_source_name=data_source_name, 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.items()): 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.items()): 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.items()): 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.items()): 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.items()): 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 file_name=dispatch_source_path, 443 gl_data="\n\n".join(gl_data), 444 gl_extensions_data="\n\n".join(gl_extensions_data), 445 gles2_data="\n\n".join(gles2_data), 446 gles2_extensions_data="\n\n".join(gles2_extensions_data), 447 both_extensions_data="\n\n".join(both_extensions_data), 448 gl_null_data="\n\n".join(nullify(gl_data)), 449 gl_null_extensions_data="\n\n".join(nullify(gl_extensions_data)), 450 gles2_null_data="\n\n".join(nullify(gles2_data)), 451 gles2_null_extensions_data="\n\n".join(nullify(gles2_extensions_data)), 452 both_null_extensions_data="\n\n".join(nullify(both_extensions_data))) 453 454 with open(dispatch_source_path, "w") as out: 455 out.write(dispatch_table_source) 456 457 # Generate the NULL/stub entry points. 458 # Process the whole set of commands 459 460 command_defs = {} 461 command_decls = {} 462 463 for command in xml_root.findall('commands/command'): 464 proto = command.find('proto') 465 command_name = proto.find('name').text 466 entry = ''.join(proto.itertext()) 467 return_type = entry[:-len(command_name)] 468 entry = return_type + ' INTERNAL_GL_APIENTRY ' + entry[len(return_type):] + 'NULL(' 469 470 param_text = [format_param(param) for param in command.findall('param')] 471 entry += ', '.join(param_text) + ')' 472 473 command_decls[command_name] = entry + ';' 474 475 entry += '\n{\n' 476 if return_type != 'void ': 477 entry += ' return static_cast<' + return_type + '>(0);\n' 478 entry += '}' 479 480 command_defs[command_name] = entry 481 482 null_decls = [command_decls[entry_point] for entry_point in sorted(all_entry_points)] 483 null_stubs = [command_defs[entry_point] for entry_point in sorted(all_entry_points)] 484 485 null_functions_header = null_functions_header_template.format( 486 script_name=os.path.basename(sys.argv[0]), 487 data_source_name=data_source_name, 488 file_name=null_functions_header_path, 489 table_data="\n".join(null_decls)) 490 491 with open(null_functions_header_path, "w") as out: 492 out.write(null_functions_header) 493 494 null_functions_source = null_functions_source_template.format( 495 script_name=os.path.basename(sys.argv[0]), 496 data_source_name=data_source_name, 497 file_name=null_functions_source_path, 498 table_data="\n\n".join(null_stubs)) 499 500 with open(null_functions_source_path, "w") as out: 501 out.write(null_functions_source) 502 return 0 503 504 505if __name__ == '__main__': 506 sys.exit(main()) 507