1#!/usr/bin/python3 2# Copyright 2019 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_mtl_internal_shaders.py: 7# Code generation for Metal backend's default shaders. 8# NOTE: don't run this script directly. Run scripts/run_code_generation.py. 9 10import json 11import os 12import subprocess 13import sys 14 15sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) 16import angle_format 17import gen_angle_format_table 18 19metal_source_output_header = "mtl_internal_shaders_src_autogen.h" 20 21metal_shader_output_file = "mtl_internal_shaders_autogen.metal" 22 23template_header_boilerplate = """// GENERATED FILE - DO NOT EDIT. 24// Generated by {script_name} 25// 26// Copyright 2020 The ANGLE Project Authors. All rights reserved. 27// Use of this source code is governed by a BSD-style license that can be 28// found in the LICENSE file. 29// 30""" 31 32def gen_shader_enums_code(angle_formats): 33 34 code = """// This file is similar to src/libANGLE/renderer/FormatID_autogen.h but is used by Metal default 35// shaders instead of C++ code. 36// 37""" 38 39 code += "namespace rx\n" 40 code += "{\n" 41 code += "namespace mtl_shader\n" 42 code += "{\n" 43 code += "\n" 44 code += "namespace FormatID\n" 45 code += "{\n" 46 code += "enum\n" 47 code += "{\n" 48 code += gen_angle_format_table.gen_enum_string(angle_formats) + '\n' 49 code += "};\n\n" 50 code += "}\n" 51 code += "\n" 52 code += "}\n" 53 code += "}\n" 54 55 return code 56 57 58def find_clang(): 59 if os.name == 'nt': 60 binary = 'clang-cl.exe' 61 else: 62 binary = 'clang++' 63 64 clang = os.path.join('..', '..', '..', '..', '..', 'third_party', 'llvm-build', 65 'Release+Asserts', 'bin', binary) 66 67 if not os.path.isfile(clang): 68 xcrun_clang = subprocess.run(["xcrun", "-f", binary], stdout=subprocess.PIPE, text=True) 69 if xcrun_clang.returncode == 0: 70 clang = xcrun_clang.stdout.strip() 71 if (not os.path.isfile(clang)): 72 raise Exception('Cannot find clang') 73 74 return clang 75 76 77def generate_metal_autogen_header(dest_metal_header): 78 angle_to_gl = angle_format.load_inverse_table('../../angle_format_map.json') 79 shader_autogen_header = gen_shader_enums_code(angle_to_gl.keys()) 80 81 with open(dest_metal_header, 'wt') as out_file: 82 out_file.write(shader_autogen_header) 83 out_file.close() 84 85 86def generate_combined_metal_src(metal_src_files): 87 autogen_header_file = "format_autogen.h" 88 generate_metal_autogen_header(autogen_header_file) 89 90 clang = find_clang() 91 92 # Use clang to preprocess the combination source. "@@" token is used to prevent clang from 93 # expanding the preprocessor directive 94 temp_fname = 'temp_master_source.metal' 95 with open(temp_fname, 'wb') as temp_file: 96 for src_file in metal_src_files: 97 include_str = '#include "' + src_file + '" \n' 98 temp_file.write(include_str.encode('utf-8')) 99 100 args = [clang] 101 if not os.name == 'nt': 102 args += ['-xc++'] 103 104 # Run clang with these options: 105 # '-E' - Only run the preprocessor 106 # '-P' - Disable linemarker output in -E mode 107 # Line markers are removed from the checked-in code 108 # as they are only useful for shader debugging. 109 args += ['-E', '-P', temp_fname] 110 111 combined_source = subprocess.check_output(args) 112 os.remove(temp_fname) 113 os.remove(autogen_header_file) 114 115 # Remove '@@' tokens 116 final_combined_src_string = combined_source.replace('@@'.encode('utf-8'), ''.encode('utf-8')) 117 118 return final_combined_src_string 119 120 121def generate_combined_metal_src_header(combined_metal_src, dest_header): 122 boilerplate_code = template_header_boilerplate.format( 123 script_name=os.path.basename(sys.argv[0])) 124 125 with open(dest_header, 'wt') as out_file: 126 out_file.write(boilerplate_code) 127 out_file.write('\n') 128 out_file.write('// C++ string version of combined Metal default shaders.\n\n') 129 out_file.write('\n\nstatic char gDefaultMetallibSrc[] = R"(\n') 130 out_file.write(combined_metal_src.decode("utf-8")) 131 out_file.write('\n') 132 out_file.write(')";\n') 133 out_file.close() 134 135 136def generate_combined_metal_shader_file(combined_metal_src, dest_file): 137 boilerplate_code = template_header_boilerplate.format( 138 script_name=os.path.basename(sys.argv[0])) 139 140 with open(dest_file, 'wt') as out_file: 141 out_file.write(boilerplate_code) 142 out_file.write('\n') 143 out_file.write('// Combined Metal default shaders.\n\n') 144 out_file.write(combined_metal_src.decode("utf-8")) 145 out_file.write('\n') 146 out_file.close() 147 148 149def main(): 150 angle_format_script_files = [ 151 '../../angle_format_map.json', '../../angle_format.py', '../../gen_angle_format_table.py' 152 ] 153 src_files = [ 154 'blit.metal', 'clear.metal', 'gen_indices.metal', 'gen_mipmap.metal', 'copy_buffer.metal', 155 'visibility.metal', 'rewrite_indices.metal' 156 ] 157 158 # auto_script parameters. 159 if len(sys.argv) > 1: 160 inputs = angle_format_script_files + src_files + ['common.h', 'constants.h'] 161 outputs = [metal_source_output_header, metal_shader_output_file] 162 163 if sys.argv[1] == 'inputs': 164 print(','.join(inputs)) 165 elif sys.argv[1] == 'outputs': 166 print(','.join(outputs)) 167 else: 168 print('Invalid script parameters') 169 return 1 170 return 0 171 172 os.chdir(sys.path[0]) 173 174 combined_metal_src = generate_combined_metal_src(src_files) 175 176 generate_combined_metal_src_header(combined_metal_src, metal_source_output_header) 177 178 # Write also the shader text. At the time of writing WebKit compilation would use this. 179 # The build system should take `metal_shader_output_file` and produce 180 # libANGLE/renderer/metal/shaders/mtl_internal_shaders_metallib.h in some part 181 # of the include path before the real libANGLE file. 182 generate_combined_metal_shader_file(combined_metal_src, metal_shader_output_file) 183 184 return 0 185 186 187if __name__ == '__main__': 188 sys.exit(main()) 189