xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/shaders/gen_mtl_internal_shaders.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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