1#!/usr/bin/python 2# Copyright 2018 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# gen_vk_internal_shaders.py: 7# Code generation for internal Vulkan shaders. Should be run when an internal 8# shader program is changed, added or removed. 9# Because this script can be slow direct invocation is supported. But before 10# code upload please run scripts/run_code_generation.py. 11 12from datetime import date 13import io 14import json 15import multiprocessing 16import os 17import platform 18import re 19import subprocess 20import sys 21 22out_file_cpp = 'vk_internal_shaders_autogen.cpp' 23out_file_h = 'vk_internal_shaders_autogen.h' 24out_file_gni = 'vk_internal_shaders_autogen.gni' 25 26is_windows = platform.system() == 'Windows' 27is_linux = platform.system() == 'Linux' 28 29# Templates for the generated files: 30template_shader_library_cpp = u"""// GENERATED FILE - DO NOT EDIT. 31// Generated by {script_name} using data from {input_file_name} 32// 33// Copyright {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// {out_file_name}: 38// Pre-generated shader library for the ANGLE Vulkan back-end. 39 40#include "libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h" 41 42namespace rx 43{{ 44namespace vk 45{{ 46namespace 47{{ 48{internal_shader_includes} 49 50// This is SPIR-V binary blob and the size. 51struct ShaderBlob 52{{ 53 const uint32_t *code; 54 size_t codeSize; 55}}; 56 57{shader_tables_cpp} 58 59angle::Result GetShader(Context *context, 60 RefCounted<ShaderAndSerial> *shaders, 61 const ShaderBlob *shaderBlobs, 62 size_t shadersCount, 63 uint32_t shaderFlags, 64 RefCounted<ShaderAndSerial> **shaderOut) 65{{ 66 ASSERT(shaderFlags < shadersCount); 67 RefCounted<ShaderAndSerial> &shader = shaders[shaderFlags]; 68 *shaderOut = &shader; 69 70 if (shader.get().valid()) 71 {{ 72 return angle::Result::Continue; 73 }} 74 75 // Create shader lazily. Access will need to be locked for multi-threading. 76 const ShaderBlob &shaderCode = shaderBlobs[shaderFlags]; 77 ASSERT(shaderCode.code != nullptr); 78 79 return InitShaderAndSerial(context, &shader.get(), shaderCode.code, shaderCode.codeSize); 80}} 81}} // anonymous namespace 82 83 84ShaderLibrary::ShaderLibrary() 85{{ 86}} 87 88ShaderLibrary::~ShaderLibrary() 89{{ 90}} 91 92void ShaderLibrary::destroy(VkDevice device) 93{{ 94 {shader_destroy_calls} 95}} 96 97{shader_get_functions_cpp} 98}} // namespace vk 99}} // namespace rx 100""" 101 102template_shader_library_h = u"""// GENERATED FILE - DO NOT EDIT. 103// Generated by {script_name} using data from {input_file_name} 104// 105// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved. 106// Use of this source code is governed by a BSD-style license that can be 107// found in the LICENSE file. 108// 109// {out_file_name}: 110// Pre-generated shader library for the ANGLE Vulkan back-end. 111 112#ifndef LIBANGLE_RENDERER_VULKAN_VK_INTERNAL_SHADERS_AUTOGEN_H_ 113#define LIBANGLE_RENDERER_VULKAN_VK_INTERNAL_SHADERS_AUTOGEN_H_ 114 115#include "libANGLE/renderer/vulkan/vk_utils.h" 116 117namespace rx 118{{ 119namespace vk 120{{ 121namespace InternalShader 122{{ 123{shader_variation_definitions} 124}} // namespace InternalShader 125 126class ShaderLibrary final : angle::NonCopyable 127{{ 128 public: 129 ShaderLibrary(); 130 ~ShaderLibrary(); 131 132 void destroy(VkDevice device); 133 134 {shader_get_functions_h} 135 136 private: 137 {shader_tables_h} 138}}; 139}} // namespace vk 140}} // namespace rx 141 142#endif // LIBANGLE_RENDERER_VULKAN_VK_INTERNAL_SHADERS_AUTOGEN_H_ 143""" 144 145template_shader_includes_gni = u"""# GENERATED FILE - DO NOT EDIT. 146# Generated by {script_name} using data from {input_file_name} 147# 148# Copyright {copyright_year} The ANGLE Project Authors. All rights reserved. 149# Use of this source code is governed by a BSD-style license that can be 150# found in the LICENSE file. 151# 152# {out_file_name}: 153# List of generated shaders for inclusion in ANGLE's build process. 154 155angle_vulkan_internal_shaders = [ 156{shaders_list} 157] 158""" 159 160 161# Gets the constant variable name for a generated shader. 162def get_var_name(output, prefix='k'): 163 return prefix + output.replace(".", "_") 164 165 166# Gets the namespace name given to constants generated from shader_file 167def get_namespace_name(shader_file): 168 return get_var_name(os.path.basename(shader_file), '') 169 170 171# Gets the namespace name given to constants generated from shader_file 172def get_variation_table_name(shader_file, prefix='k'): 173 return get_var_name(os.path.basename(shader_file), prefix) + '_shaders' 174 175 176# Gets the internal ID string for a particular shader. 177def get_shader_id(shader): 178 file = os.path.splitext(os.path.basename(shader))[0] 179 return file.replace(".", "_") 180 181 182# Returns the name of the generated SPIR-V file for a shader. 183def get_output_path(name): 184 return os.path.join('shaders', 'gen', name + ".inc") 185 186 187# Finds a path to GN's out directory 188def get_linux_glslang_exe_path(): 189 return '../../../../tools/glslang/glslang_validator' 190 191 192def get_win_glslang_exe_path(): 193 return get_linux_glslang_exe_path() + '.exe' 194 195 196def get_glslang_exe_path(): 197 glslang_exe = get_win_glslang_exe_path() if is_windows else get_linux_glslang_exe_path() 198 if not os.path.isfile(glslang_exe): 199 raise Exception('Could not find %s' % glslang_exe) 200 return glslang_exe 201 202 203# Generates the code for a shader blob array entry. 204def gen_shader_blob_entry(shader): 205 var_name = get_var_name(os.path.basename(shader))[0:-4] 206 return "{%s, %s}" % (var_name, "sizeof(%s)" % var_name) 207 208 209def slash(s): 210 return s.replace('\\', '/') 211 212 213def gen_shader_include(shader): 214 return '#include "libANGLE/renderer/vulkan/%s"' % slash(shader) 215 216 217def get_shader_variations(shader): 218 variation_file = shader + '.json' 219 if not os.path.exists(variation_file): 220 # If there is no variation file, assume none. 221 return ({}, []) 222 223 with open(variation_file) as fin: 224 variations = json.loads(fin.read()) 225 flags = {} 226 enums = [] 227 228 for key, value in variations.iteritems(): 229 if key == "Description": 230 continue 231 elif key == "Flags": 232 flags = value 233 elif len(value) > 0: 234 enums.append((key, value)) 235 236 # sort enums so the ones with the most waste ends up last, reducing the table size 237 enums.sort(key=lambda enum: (1 << (len(enum[1]) - 1).bit_length()) / float(len(enum[1]))) 238 239 return (flags, enums) 240 241 242def get_variation_bits(flags, enums): 243 flags_bits = len(flags) 244 enum_bits = [(len(enum[1]) - 1).bit_length() for enum in enums] 245 return (flags_bits, enum_bits) 246 247 248def next_enum_variation(enums, enum_indices): 249 """Loop through indices from [0, 0, ...] to [L0-1, L1-1, ...] 250 where Li is len(enums[i]). The list can be thought of as a number with many 251 digits, where each digit is in [0, Li), and this function effectively implements 252 the increment operation, with the least-significant digit being the first item.""" 253 for i in range(len(enums)): 254 current = enum_indices[i] 255 # if current digit has room, increment it. 256 if current + 1 < len(enums[i][1]): 257 enum_indices[i] = current + 1 258 return True 259 # otherwise reset it to 0 and carry to the next digit. 260 enum_indices[i] = 0 261 262 # if this is reached, the number has overflowed and the loop is finished. 263 return False 264 265 266compact_newlines_regex = re.compile(r"\n\s*\n", re.MULTILINE) 267 268 269def cleanup_preprocessed_shader(shader_text): 270 return compact_newlines_regex.sub('\n\n', shader_text.strip()) 271 272 273class CompileQueue: 274 275 class AppendPreprocessorOutput: 276 277 def __init__(self, shader_file, preprocessor_args, output_path): 278 # Asynchronously launch the preprocessor job. 279 self.process = subprocess.Popen( 280 preprocessor_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 281 # Store the file name for output to be appended to. 282 self.output_path = output_path 283 # Store info for error description. 284 self.shader_file = shader_file 285 286 def wait(self, queue): 287 (out, err) = self.process.communicate() 288 if self.process.returncode == 0: 289 # Use unix line endings. 290 out = out.replace('\r\n', '\n') 291 # Clean up excessive empty lines. 292 out = cleanup_preprocessed_shader(out) 293 # Comment it out! 294 out = '\n'.join([('// ' + line).strip() for line in out.splitlines()]) 295 # Append preprocessor output to the output file. 296 with open(self.output_path, 'ab') as incfile: 297 incfile.write('\n\n// Generated from:\n//\n') 298 incfile.write(out + '\n') 299 out = None 300 return (out, err, self.process.returncode, None, 301 "Error running preprocessor on " + self.shader_file) 302 303 class CompileToSPIRV: 304 305 def __init__(self, shader_file, shader_basename, variation_string, output_path, 306 compile_args, preprocessor_args): 307 # Asynchronously launch the compile job. 308 self.process = subprocess.Popen( 309 compile_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 310 # Store info for launching the preprocessor. 311 self.preprocessor_args = preprocessor_args 312 self.output_path = output_path 313 # Store info for job and error description. 314 self.shader_file = shader_file 315 self.shader_basename = shader_basename 316 self.variation_string = variation_string 317 318 def wait(self, queue): 319 (out, err) = self.process.communicate() 320 if self.process.returncode == 0: 321 # Insert the preprocessor job in the queue. 322 queue.append( 323 CompileQueue.AppendPreprocessorOutput(self.shader_file, self.preprocessor_args, 324 self.output_path)) 325 # If all the output says is the source file name, don't bother printing it. 326 if out.strip() == self.shader_file: 327 out = None 328 description = self.output_path + ': ' + self.shader_basename + self.variation_string 329 return (out, err, self.process.returncode, description, 330 "Error compiling " + self.shader_file) 331 332 def __init__(self): 333 # Compile with as many CPU threads are detected. Once a shader is compiled, another job is 334 # automatically added to the queue to append the preprocessor output to the generated file. 335 self.queue = [] 336 self.thread_count = multiprocessing.cpu_count() 337 338 def _wait_first(self, ignore_output=False): 339 (out, err, returncode, description, exception_description) = self.queue[0].wait(self.queue) 340 self.queue.pop(0) 341 if not ignore_output: 342 if description: 343 print description 344 if out and out.strip(): 345 print out.strip() 346 if err and err.strip(): 347 print err 348 if returncode != 0: 349 return exception_description 350 return None 351 352 # Wait for all pending tasks. If called after error is detected, ignore_output can be used to 353 # make sure errors in later jobs are suppressed to avoid cluttering the output. This is 354 # because the same compile error is likely present in other variations of the same shader and 355 # outputting the same error multiple times is not useful. 356 def _wait_all(self, ignore_output=False): 357 exception_description = None 358 while len(self.queue) > 0: 359 this_job_exception = self._wait_first(ignore_output) 360 # If encountered an error, keep it to be raised, ignoring errors from following jobs. 361 if this_job_exception and not ignore_output: 362 exception_description = this_job_exception 363 ignore_output = True 364 365 return exception_description 366 367 def add_job(self, shader_file, shader_basename, variation_string, output_path, compile_args, 368 preprocessor_args): 369 # If the queue is full, wait until there is at least one slot available. 370 while len(self.queue) >= self.thread_count: 371 exception = self._wait_first(False) 372 # If encountered an exception, cleanup following jobs and raise it. 373 if exception: 374 self._wait_all(True) 375 raise Exception(exception) 376 377 # Add a compile job 378 self.queue.append( 379 CompileQueue.CompileToSPIRV(shader_file, shader_basename, variation_string, 380 output_path, compile_args, preprocessor_args)) 381 382 def finish(self): 383 exception = self._wait_all(False) 384 # If encountered an exception, cleanup following jobs and raise it. 385 if exception is not None: 386 raise Exception(exception) 387 388 389# If the option is just a string, that's the name. Otherwise, it could be 390# [ name, arg1, ..., argN ]. In that case, name is option[0] and option[1:] are extra arguments 391# that need to be passed to glslang_validator for this variation. 392def get_variation_name(option): 393 return option if isinstance(option, unicode) else option[0] 394 395 396def get_variation_args(option): 397 return [] if isinstance(option, unicode) else option[1:] 398 399 400def compile_variation(glslang_path, compile_queue, shader_file, shader_basename, flags, enums, 401 flags_active, enum_indices, flags_bits, enum_bits, output_shaders): 402 403 glslang_args = [glslang_path] 404 405 # generate -D defines and the output file name 406 # 407 # The variations are given a bit pattern to be able to OR different flags into a variation. The 408 # least significant bits are the flags, where there is one bit per flag. After that, each enum 409 # takes up as few bits as needed to count that many enum values. 410 variation_bits = 0 411 variation_string = '' 412 variation_extra_args = [] 413 for f in range(len(flags)): 414 if flags_active & (1 << f): 415 flag = flags[f] 416 flag_name = get_variation_name(flag) 417 variation_extra_args += get_variation_args(flag) 418 glslang_args.append('-D' + flag_name + '=1') 419 420 variation_bits |= 1 << f 421 variation_string += '|' + flag_name 422 423 current_bit_start = flags_bits 424 425 for e in range(len(enums)): 426 enum = enums[e][1][enum_indices[e]] 427 enum_name = get_variation_name(enum) 428 variation_extra_args += get_variation_args(enum) 429 glslang_args.append('-D' + enum_name + '=1') 430 431 variation_bits |= enum_indices[e] << current_bit_start 432 current_bit_start += enum_bits[e] 433 variation_string += '|' + enum_name 434 435 output_name = '%s.%08X' % (shader_basename, variation_bits) 436 output_path = get_output_path(output_name) 437 output_shaders.append(output_path) 438 439 if glslang_path is not None: 440 glslang_preprocessor_output_args = glslang_args + ['-E'] 441 glslang_preprocessor_output_args.append(shader_file) # Input GLSL shader 442 443 glslang_args += variation_extra_args 444 445 glslang_args += ['-V'] # Output mode is Vulkan 446 glslang_args += ['--variable-name', get_var_name(output_name)] # C-style variable name 447 glslang_args += ['-o', output_path] # Output file 448 glslang_args.append(shader_file) # Input GLSL shader 449 450 compile_queue.add_job(shader_file, shader_basename, variation_string, output_path, 451 glslang_args, glslang_preprocessor_output_args) 452 453 454class ShaderAndVariations: 455 456 def __init__(self, shader_file): 457 self.shader_file = shader_file 458 (self.flags, self.enums) = get_shader_variations(shader_file) 459 get_variation_bits(self.flags, self.enums) 460 (self.flags_bits, self.enum_bits) = get_variation_bits(self.flags, self.enums) 461 # Maximum index value has all flags set and all enums at max value. 462 max_index = (1 << self.flags_bits) - 1 463 current_bit_start = self.flags_bits 464 for (name, values), bits in zip(self.enums, self.enum_bits): 465 max_index |= (len(values) - 1) << current_bit_start 466 current_bit_start += bits 467 # Minimum array size is one more than the maximum value. 468 self.array_len = max_index + 1 469 470 471def get_variation_definition(shader_and_variation): 472 shader_file = shader_and_variation.shader_file 473 flags = shader_and_variation.flags 474 enums = shader_and_variation.enums 475 flags_bits = shader_and_variation.flags_bits 476 enum_bits = shader_and_variation.enum_bits 477 array_len = shader_and_variation.array_len 478 479 namespace_name = get_namespace_name(shader_file) 480 481 definition = 'namespace %s\n{\n' % namespace_name 482 if len(flags) > 0: 483 definition += 'enum flags\n{\n' 484 definition += ''.join([ 485 'k%s = 0x%08X,\n' % (get_variation_name(flags[f]), 1 << f) for f in range(len(flags)) 486 ]) 487 definition += '};\n' 488 489 current_bit_start = flags_bits 490 491 for e in range(len(enums)): 492 enum = enums[e] 493 enum_name = enum[0] 494 definition += 'enum %s\n{\n' % enum_name 495 definition += ''.join([ 496 'k%s = 0x%08X,\n' % (get_variation_name(enum[1][v]), v << current_bit_start) 497 for v in range(len(enum[1])) 498 ]) 499 definition += '};\n' 500 current_bit_start += enum_bits[e] 501 502 definition += 'constexpr size_t kArrayLen = 0x%08X;\n' % array_len 503 504 definition += '} // namespace %s\n' % namespace_name 505 return definition 506 507 508def get_shader_table_h(shader_and_variation): 509 shader_file = shader_and_variation.shader_file 510 flags = shader_and_variation.flags 511 enums = shader_and_variation.enums 512 513 table_name = get_variation_table_name(shader_file, 'm') 514 515 table = 'RefCounted<ShaderAndSerial> %s[' % table_name 516 517 namespace_name = "InternalShader::" + get_namespace_name(shader_file) 518 519 table += '%s::kArrayLen' % namespace_name 520 521 table += '];' 522 return table 523 524 525def get_shader_table_cpp(shader_and_variation): 526 shader_file = shader_and_variation.shader_file 527 enums = shader_and_variation.enums 528 flags_bits = shader_and_variation.flags_bits 529 enum_bits = shader_and_variation.enum_bits 530 array_len = shader_and_variation.array_len 531 532 # Cache max and mask value of each enum to quickly know when a possible variation is invalid 533 enum_maxes = [] 534 enum_masks = [] 535 current_bit_start = flags_bits 536 537 for e in range(len(enums)): 538 enum_values = enums[e][1] 539 enum_maxes.append((len(enum_values) - 1) << current_bit_start) 540 enum_masks.append(((1 << enum_bits[e]) - 1) << current_bit_start) 541 current_bit_start += enum_bits[e] 542 543 table_name = get_variation_table_name(shader_file) 544 var_name = get_var_name(os.path.basename(shader_file)) 545 546 table = 'constexpr ShaderBlob %s[] = {\n' % table_name 547 548 for variation in range(array_len): 549 # if any variation is invalid, output an empty entry 550 if any([(variation & enum_masks[e]) > enum_maxes[e] for e in range(len(enums))]): 551 table += '{nullptr, 0}, // 0x%08X\n' % variation 552 else: 553 entry = '%s_%08X' % (var_name, variation) 554 table += '{%s, sizeof(%s)},\n' % (entry, entry) 555 556 table += '};' 557 return table 558 559 560def get_get_function_h(shader_and_variation): 561 shader_file = shader_and_variation.shader_file 562 563 function_name = get_var_name(os.path.basename(shader_file), 'get') 564 565 definition = 'angle::Result %s' % function_name 566 definition += '(Context *context, uint32_t shaderFlags, RefCounted<ShaderAndSerial> **shaderOut);' 567 568 return definition 569 570 571def get_get_function_cpp(shader_and_variation): 572 shader_file = shader_and_variation.shader_file 573 enums = shader_and_variation.enums 574 575 function_name = get_var_name(os.path.basename(shader_file), 'get') 576 namespace_name = "InternalShader::" + get_namespace_name(shader_file) 577 member_table_name = get_variation_table_name(shader_file, 'm') 578 constant_table_name = get_variation_table_name(shader_file) 579 580 definition = 'angle::Result ShaderLibrary::%s' % function_name 581 definition += '(Context *context, uint32_t shaderFlags, RefCounted<ShaderAndSerial> **shaderOut)\n{\n' 582 definition += 'return GetShader(context, %s, %s, ArraySize(%s), shaderFlags, shaderOut);\n}\n' % ( 583 member_table_name, constant_table_name, constant_table_name) 584 585 return definition 586 587 588def get_destroy_call(shader_and_variation): 589 shader_file = shader_and_variation.shader_file 590 591 table_name = get_variation_table_name(shader_file, 'm') 592 593 destroy = 'for (RefCounted<ShaderAndSerial> &shader : %s)\n' % table_name 594 destroy += '{\nshader.get().destroy(device);\n}' 595 return destroy 596 597 598def shader_path(shader): 599 return '"%s"' % slash(shader) 600 601 602def main(): 603 # STEP 0: Handle inputs/outputs for run_code_generation.py's auto_script 604 shaders_dir = os.path.join('shaders', 'src') 605 if not os.path.isdir(shaders_dir): 606 raise Exception("Could not find shaders directory") 607 608 print_inputs = len(sys.argv) == 2 and sys.argv[1] == 'inputs' 609 print_outputs = len(sys.argv) == 2 and sys.argv[1] == 'outputs' 610 # If an argument X is given that's not inputs or outputs, compile shaders that match *X*. 611 # This is useful in development to build only the shader of interest. 612 shader_files_to_compile = os.listdir(shaders_dir) 613 if not (print_inputs or print_outputs or len(sys.argv) < 2): 614 shader_files_to_compile = [f for f in shader_files_to_compile if f.find(sys.argv[1]) != -1] 615 616 valid_extensions = ['.vert', '.frag', '.comp'] 617 input_shaders = sorted([ 618 os.path.join(shaders_dir, shader) 619 for shader in os.listdir(shaders_dir) 620 if any([os.path.splitext(shader)[1] == ext for ext in valid_extensions]) 621 ]) 622 if print_inputs: 623 glslang_binaries = [get_linux_glslang_exe_path(), get_win_glslang_exe_path()] 624 glslang_binary_hashes = [path + '.sha1' for path in glslang_binaries] 625 print(",".join(input_shaders + glslang_binary_hashes)) 626 return 0 627 628 # STEP 1: Call glslang to generate the internal shaders into small .inc files. 629 # Iterates over the shaders and call glslang with the right arguments. 630 631 glslang_path = None 632 if not print_outputs: 633 glslang_path = get_glslang_exe_path() 634 635 output_shaders = [] 636 637 input_shaders_and_variations = [ 638 ShaderAndVariations(shader_file) for shader_file in input_shaders 639 ] 640 641 compile_queue = CompileQueue() 642 643 for shader_and_variation in input_shaders_and_variations: 644 shader_file = shader_and_variation.shader_file 645 flags = shader_and_variation.flags 646 enums = shader_and_variation.enums 647 flags_bits = shader_and_variation.flags_bits 648 enum_bits = shader_and_variation.enum_bits 649 650 # an array where each element i is in [0, len(enums[i])), 651 # telling which enum is currently selected 652 enum_indices = [0] * len(enums) 653 654 output_name = os.path.basename(shader_file) 655 656 while True: 657 do_compile = not print_outputs and output_name in shader_files_to_compile 658 # a number where each bit says whether a flag is active or not, 659 # with values in [0, 2^len(flags)) 660 for flags_active in range(1 << len(flags)): 661 compile_variation(glslang_path if do_compile else None, compile_queue, shader_file, 662 output_name, flags, enums, flags_active, enum_indices, 663 flags_bits, enum_bits, output_shaders) 664 665 if not next_enum_variation(enums, enum_indices): 666 break 667 668 output_shaders = sorted(output_shaders) 669 outputs = output_shaders + [out_file_cpp, out_file_h] 670 671 if print_outputs: 672 print(','.join(outputs)) 673 return 0 674 675 compile_queue.finish() 676 677 # STEP 2: Consolidate the .inc files into an auto-generated cpp/h library. 678 with open(out_file_cpp, 'w') as outfile: 679 includes = "\n".join([gen_shader_include(shader) for shader in output_shaders]) 680 shader_tables_cpp = '\n'.join( 681 [get_shader_table_cpp(s) for s in input_shaders_and_variations]) 682 shader_destroy_calls = '\n'.join( 683 [get_destroy_call(s) for s in input_shaders_and_variations]) 684 shader_get_functions_cpp = '\n'.join( 685 [get_get_function_cpp(s) for s in input_shaders_and_variations]) 686 687 outcode = template_shader_library_cpp.format( 688 script_name=__file__, 689 copyright_year=date.today().year, 690 out_file_name=out_file_cpp, 691 input_file_name='shaders/src/*', 692 internal_shader_includes=includes, 693 shader_tables_cpp=shader_tables_cpp, 694 shader_destroy_calls=shader_destroy_calls, 695 shader_get_functions_cpp=shader_get_functions_cpp) 696 outfile.write(outcode) 697 outfile.close() 698 699 with open(out_file_h, 'w') as outfile: 700 shader_variation_definitions = '\n'.join( 701 [get_variation_definition(s) for s in input_shaders_and_variations]) 702 shader_get_functions_h = '\n'.join( 703 [get_get_function_h(s) for s in input_shaders_and_variations]) 704 shader_tables_h = '\n'.join([get_shader_table_h(s) for s in input_shaders_and_variations]) 705 outcode = template_shader_library_h.format( 706 script_name=__file__, 707 copyright_year=date.today().year, 708 out_file_name=out_file_h, 709 input_file_name='shaders/src/*', 710 shader_variation_definitions=shader_variation_definitions, 711 shader_get_functions_h=shader_get_functions_h, 712 shader_tables_h=shader_tables_h) 713 outfile.write(outcode) 714 outfile.close() 715 716 # STEP 3: Create a gni file with the generated files. 717 with io.open(out_file_gni, 'w', newline='\n') as outfile: 718 outcode = template_shader_includes_gni.format( 719 script_name=__file__, 720 copyright_year=date.today().year, 721 out_file_name=out_file_gni, 722 input_file_name='shaders/src/*', 723 shaders_list=',\n'.join([shader_path(shader) for shader in output_shaders])) 724 outfile.write(outcode) 725 outfile.close() 726 727 return 0 728 729 730if __name__ == '__main__': 731 sys.exit(main()) 732