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 389def compile_variation(glslang_path, compile_queue, shader_file, shader_basename, flags, enums, 390 flags_active, enum_indices, flags_bits, enum_bits, output_shaders): 391 392 glslang_args = [glslang_path] 393 394 # generate -D defines and the output file name 395 # 396 # The variations are given a bit pattern to be able to OR different flags into a variation. The 397 # least significant bits are the flags, where there is one bit per flag. After that, each enum 398 # takes up as few bits as needed to count that many enum values. 399 variation_bits = 0 400 variation_string = '' 401 for f in range(len(flags)): 402 if flags_active & (1 << f): 403 flag_name = flags[f] 404 glslang_args.append('-D' + flag_name + '=1') 405 406 variation_bits |= 1 << f 407 variation_string += '|' + flag_name 408 409 current_bit_start = flags_bits 410 411 for e in range(len(enums)): 412 enum_name = enums[e][1][enum_indices[e]] 413 glslang_args.append('-D' + enum_name + '=1') 414 415 variation_bits |= enum_indices[e] << current_bit_start 416 current_bit_start += enum_bits[e] 417 variation_string += '|' + enum_name 418 419 output_name = '%s.%08X' % (shader_basename, variation_bits) 420 output_path = get_output_path(output_name) 421 output_shaders.append(output_path) 422 423 if glslang_path is not None: 424 glslang_preprocessor_output_args = glslang_args + ['-E'] 425 glslang_preprocessor_output_args.append(shader_file) # Input GLSL shader 426 427 glslang_args += ['-V'] # Output mode is Vulkan 428 glslang_args += ['--variable-name', get_var_name(output_name)] # C-style variable name 429 glslang_args += ['-o', output_path] # Output file 430 glslang_args.append(shader_file) # Input GLSL shader 431 432 compile_queue.add_job(shader_file, shader_basename, variation_string, output_path, 433 glslang_args, glslang_preprocessor_output_args) 434 435 436class ShaderAndVariations: 437 438 def __init__(self, shader_file): 439 self.shader_file = shader_file 440 (self.flags, self.enums) = get_shader_variations(shader_file) 441 get_variation_bits(self.flags, self.enums) 442 (self.flags_bits, self.enum_bits) = get_variation_bits(self.flags, self.enums) 443 # Maximum index value has all flags set and all enums at max value. 444 max_index = (1 << self.flags_bits) - 1 445 current_bit_start = self.flags_bits 446 for (name, values), bits in zip(self.enums, self.enum_bits): 447 max_index |= (len(values) - 1) << current_bit_start 448 current_bit_start += bits 449 # Minimum array size is one more than the maximum value. 450 self.array_len = max_index + 1 451 452 453def get_variation_definition(shader_and_variation): 454 shader_file = shader_and_variation.shader_file 455 flags = shader_and_variation.flags 456 enums = shader_and_variation.enums 457 flags_bits = shader_and_variation.flags_bits 458 enum_bits = shader_and_variation.enum_bits 459 array_len = shader_and_variation.array_len 460 461 namespace_name = get_namespace_name(shader_file) 462 463 definition = 'namespace %s\n{\n' % namespace_name 464 if len(flags) > 0: 465 definition += 'enum flags\n{\n' 466 definition += ''.join(['k%s = 0x%08X,\n' % (flags[f], 1 << f) for f in range(len(flags))]) 467 definition += '};\n' 468 469 current_bit_start = flags_bits 470 471 for e in range(len(enums)): 472 enum = enums[e] 473 enum_name = enum[0] 474 definition += 'enum %s\n{\n' % enum_name 475 definition += ''.join([ 476 'k%s = 0x%08X,\n' % (enum[1][v], v << current_bit_start) for v in range(len(enum[1])) 477 ]) 478 definition += '};\n' 479 current_bit_start += enum_bits[e] 480 481 definition += 'constexpr size_t kArrayLen = 0x%08X;\n' % array_len 482 483 definition += '} // namespace %s\n' % namespace_name 484 return definition 485 486 487def get_shader_table_h(shader_and_variation): 488 shader_file = shader_and_variation.shader_file 489 flags = shader_and_variation.flags 490 enums = shader_and_variation.enums 491 492 table_name = get_variation_table_name(shader_file, 'm') 493 494 table = 'RefCounted<ShaderAndSerial> %s[' % table_name 495 496 namespace_name = "InternalShader::" + get_namespace_name(shader_file) 497 498 table += '%s::kArrayLen' % namespace_name 499 500 table += '];' 501 return table 502 503 504def get_shader_table_cpp(shader_and_variation): 505 shader_file = shader_and_variation.shader_file 506 enums = shader_and_variation.enums 507 flags_bits = shader_and_variation.flags_bits 508 enum_bits = shader_and_variation.enum_bits 509 array_len = shader_and_variation.array_len 510 511 # Cache max and mask value of each enum to quickly know when a possible variation is invalid 512 enum_maxes = [] 513 enum_masks = [] 514 current_bit_start = flags_bits 515 516 for e in range(len(enums)): 517 enum_values = enums[e][1] 518 enum_maxes.append((len(enum_values) - 1) << current_bit_start) 519 enum_masks.append(((1 << enum_bits[e]) - 1) << current_bit_start) 520 current_bit_start += enum_bits[e] 521 522 table_name = get_variation_table_name(shader_file) 523 var_name = get_var_name(os.path.basename(shader_file)) 524 525 table = 'constexpr ShaderBlob %s[] = {\n' % table_name 526 527 for variation in range(array_len): 528 # if any variation is invalid, output an empty entry 529 if any([(variation & enum_masks[e]) > enum_maxes[e] for e in range(len(enums))]): 530 table += '{nullptr, 0}, // 0x%08X\n' % variation 531 else: 532 entry = '%s_%08X' % (var_name, variation) 533 table += '{%s, sizeof(%s)},\n' % (entry, entry) 534 535 table += '};' 536 return table 537 538 539def get_get_function_h(shader_and_variation): 540 shader_file = shader_and_variation.shader_file 541 542 function_name = get_var_name(os.path.basename(shader_file), 'get') 543 544 definition = 'angle::Result %s' % function_name 545 definition += '(Context *context, uint32_t shaderFlags, RefCounted<ShaderAndSerial> **shaderOut);' 546 547 return definition 548 549 550def get_get_function_cpp(shader_and_variation): 551 shader_file = shader_and_variation.shader_file 552 enums = shader_and_variation.enums 553 554 function_name = get_var_name(os.path.basename(shader_file), 'get') 555 namespace_name = "InternalShader::" + get_namespace_name(shader_file) 556 member_table_name = get_variation_table_name(shader_file, 'm') 557 constant_table_name = get_variation_table_name(shader_file) 558 559 definition = 'angle::Result ShaderLibrary::%s' % function_name 560 definition += '(Context *context, uint32_t shaderFlags, RefCounted<ShaderAndSerial> **shaderOut)\n{\n' 561 definition += 'return GetShader(context, %s, %s, ArraySize(%s), shaderFlags, shaderOut);\n}\n' % ( 562 member_table_name, constant_table_name, constant_table_name) 563 564 return definition 565 566 567def get_destroy_call(shader_and_variation): 568 shader_file = shader_and_variation.shader_file 569 570 table_name = get_variation_table_name(shader_file, 'm') 571 572 destroy = 'for (RefCounted<ShaderAndSerial> &shader : %s)\n' % table_name 573 destroy += '{\nshader.get().destroy(device);\n}' 574 return destroy 575 576 577def shader_path(shader): 578 return '"src/libANGLE/renderer/vulkan/%s"' % slash(shader) 579 580 581def main(): 582 # STEP 0: Handle inputs/outputs for run_code_generation.py's auto_script 583 shaders_dir = os.path.join('shaders', 'src') 584 if not os.path.isdir(shaders_dir): 585 raise Exception("Could not find shaders directory") 586 587 print_inputs = len(sys.argv) == 2 and sys.argv[1] == 'inputs' 588 print_outputs = len(sys.argv) == 2 and sys.argv[1] == 'outputs' 589 # If an argument X is given that's not inputs or outputs, compile shaders that match *X*. 590 # This is useful in development to build only the shader of interest. 591 shader_files_to_compile = os.listdir(shaders_dir) 592 if not (print_inputs or print_outputs or len(sys.argv) < 2): 593 shader_files_to_compile = [f for f in shader_files_to_compile if f.find(sys.argv[1]) != -1] 594 595 valid_extensions = ['.vert', '.frag', '.comp'] 596 input_shaders = sorted([ 597 os.path.join(shaders_dir, shader) 598 for shader in os.listdir(shaders_dir) 599 if any([os.path.splitext(shader)[1] == ext for ext in valid_extensions]) 600 ]) 601 if print_inputs: 602 glslang_binaries = [get_linux_glslang_exe_path(), get_win_glslang_exe_path()] 603 glslang_binary_hashes = [path + '.sha1' for path in glslang_binaries] 604 print(",".join(input_shaders + glslang_binary_hashes)) 605 return 0 606 607 # STEP 1: Call glslang to generate the internal shaders into small .inc files. 608 # Iterates over the shaders and call glslang with the right arguments. 609 610 glslang_path = None 611 if not print_outputs: 612 glslang_path = get_glslang_exe_path() 613 614 output_shaders = [] 615 616 input_shaders_and_variations = [ 617 ShaderAndVariations(shader_file) for shader_file in input_shaders 618 ] 619 620 compile_queue = CompileQueue() 621 622 for shader_and_variation in input_shaders_and_variations: 623 shader_file = shader_and_variation.shader_file 624 flags = shader_and_variation.flags 625 enums = shader_and_variation.enums 626 flags_bits = shader_and_variation.flags_bits 627 enum_bits = shader_and_variation.enum_bits 628 629 # an array where each element i is in [0, len(enums[i])), 630 # telling which enum is currently selected 631 enum_indices = [0] * len(enums) 632 633 output_name = os.path.basename(shader_file) 634 635 while True: 636 do_compile = not print_outputs and output_name in shader_files_to_compile 637 # a number where each bit says whether a flag is active or not, 638 # with values in [0, 2^len(flags)) 639 for flags_active in range(1 << len(flags)): 640 compile_variation(glslang_path if do_compile else None, compile_queue, shader_file, 641 output_name, flags, enums, flags_active, enum_indices, 642 flags_bits, enum_bits, output_shaders) 643 644 if not next_enum_variation(enums, enum_indices): 645 break 646 647 output_shaders = sorted(output_shaders) 648 outputs = output_shaders + [out_file_cpp, out_file_h] 649 650 if print_outputs: 651 print(','.join(outputs)) 652 return 0 653 654 compile_queue.finish() 655 656 # STEP 2: Consolidate the .inc files into an auto-generated cpp/h library. 657 with open(out_file_cpp, 'w') as outfile: 658 includes = "\n".join([gen_shader_include(shader) for shader in output_shaders]) 659 shader_tables_cpp = '\n'.join( 660 [get_shader_table_cpp(s) for s in input_shaders_and_variations]) 661 shader_destroy_calls = '\n'.join( 662 [get_destroy_call(s) for s in input_shaders_and_variations]) 663 shader_get_functions_cpp = '\n'.join( 664 [get_get_function_cpp(s) for s in input_shaders_and_variations]) 665 666 outcode = template_shader_library_cpp.format( 667 script_name=__file__, 668 copyright_year=date.today().year, 669 out_file_name=out_file_cpp, 670 input_file_name='shaders/src/*', 671 internal_shader_includes=includes, 672 shader_tables_cpp=shader_tables_cpp, 673 shader_destroy_calls=shader_destroy_calls, 674 shader_get_functions_cpp=shader_get_functions_cpp) 675 outfile.write(outcode) 676 outfile.close() 677 678 with open(out_file_h, 'w') as outfile: 679 shader_variation_definitions = '\n'.join( 680 [get_variation_definition(s) for s in input_shaders_and_variations]) 681 shader_get_functions_h = '\n'.join( 682 [get_get_function_h(s) for s in input_shaders_and_variations]) 683 shader_tables_h = '\n'.join([get_shader_table_h(s) for s in input_shaders_and_variations]) 684 outcode = template_shader_library_h.format( 685 script_name=__file__, 686 copyright_year=date.today().year, 687 out_file_name=out_file_h, 688 input_file_name='shaders/src/*', 689 shader_variation_definitions=shader_variation_definitions, 690 shader_get_functions_h=shader_get_functions_h, 691 shader_tables_h=shader_tables_h) 692 outfile.write(outcode) 693 outfile.close() 694 695 # STEP 3: Create a gni file with the generated files. 696 with io.open(out_file_gni, 'w', newline='\n') as outfile: 697 outcode = template_shader_includes_gni.format( 698 script_name=__file__, 699 copyright_year=date.today().year, 700 out_file_name=out_file_gni, 701 input_file_name='shaders/src/*', 702 shaders_list=',\n'.join([shader_path(shader) for shader in output_shaders])) 703 outfile.write(outcode) 704 outfile.close() 705 706 return 0 707 708 709if __name__ == '__main__': 710 sys.exit(main()) 711