• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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