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 args += ['-E', temp_fname] 104 105 combined_source = subprocess.check_output(args) 106 os.remove(temp_fname) 107 os.remove(autogen_header_file) 108 109 # Remove '@@' tokens 110 final_combined_src_string = combined_source.replace('@@'.encode('utf-8'), ''.encode('utf-8')) 111 112 return final_combined_src_string 113 114 115def generate_combined_metal_src_header(combined_metal_src, dest_header): 116 boilerplate_code = template_header_boilerplate.format( 117 script_name=os.path.basename(sys.argv[0])) 118 119 with open(dest_header, 'wt') as out_file: 120 out_file.write(boilerplate_code) 121 out_file.write('\n') 122 out_file.write('// C++ string version of combined Metal default shaders.\n\n') 123 out_file.write('\n\nstatic char gDefaultMetallibSrc[] = R"(\n') 124 out_file.write(combined_metal_src.decode("utf-8")) 125 out_file.write('\n') 126 out_file.write(')";\n') 127 out_file.close() 128 129 130def generate_combined_metal_shader_file(combined_metal_src, dest_file): 131 boilerplate_code = template_header_boilerplate.format( 132 script_name=os.path.basename(sys.argv[0])) 133 134 with open(dest_file, 'wt') as out_file: 135 out_file.write(boilerplate_code) 136 out_file.write('\n') 137 out_file.write('// Combined Metal default shaders.\n\n') 138 out_file.write(combined_metal_src.decode("utf-8")) 139 out_file.write('\n') 140 out_file.close() 141 142 143def main(): 144 angle_format_script_files = [ 145 '../../angle_format_map.json', '../../angle_format.py', '../../gen_angle_format_table.py' 146 ] 147 src_files = [ 148 'blit.metal', 'clear.metal', 'gen_indices.metal', 'gen_mipmap.metal', 'copy_buffer.metal', 149 'visibility.metal', 'rewrite_indices.metal' 150 ] 151 152 # auto_script parameters. 153 if len(sys.argv) > 1: 154 inputs = angle_format_script_files + src_files + ['common.h', 'constants.h'] 155 outputs = [metal_source_output_header, metal_shader_output_file] 156 157 if sys.argv[1] == 'inputs': 158 print(','.join(inputs)) 159 elif sys.argv[1] == 'outputs': 160 print(','.join(outputs)) 161 else: 162 print('Invalid script parameters') 163 return 1 164 return 0 165 166 os.chdir(sys.path[0]) 167 168 combined_metal_src = generate_combined_metal_src(src_files) 169 170 generate_combined_metal_src_header(combined_metal_src, metal_source_output_header) 171 172 # Write also the shader text. At the time of writing WebKit compilation would use this. 173 # The build system should take `metal_shader_output_file` and produce 174 # libANGLE/renderer/metal/shaders/mtl_internal_shaders_metallib.h in some part 175 # of the include path before the real libANGLE file. 176 generate_combined_metal_shader_file(combined_metal_src, metal_shader_output_file) 177 178 return 0 179 180 181if __name__ == '__main__': 182 sys.exit(main()) 183