1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
media / gpu / chromeos / shaders / compile_shaders.py [blame]
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Wrapper script for compiling Vulkan shaders.
Vulkan shaders need to be compiled ahead of time rather than on the fly. This
script will do that and wrap the SPIR-V bytecode in a C++ header so it can be
included in Chrome.
Usage:
compile_shaders.py <directory to compile>
This script will automatically compile all .frag and .vert files in the target
directory and output a C header file to stdout containing all of the bytecode.
"""
from collections.abc import Sequence
from absl import app
import glob
import os
import subprocess
def execute_glslang_validator(filepath):
return subprocess.run(
['glslangValidator', '-V100', '-x', '-o', '/dev/stdout', filepath],
capture_output=True, check=True).stdout.decode()
def parse_glslang_output(output):
word_size_bytes = 4
word_size_hex_string = 2*word_size_bytes + 2 # includes preceding '0x'
bytecode_words = []
idx = 0
while True:
idx = output.find('0x', idx)
if idx == -1:
return bytecode_words
bytecode_words.append(output[idx:idx+word_size_hex_string])
idx += word_size_hex_string
def generate_shader_literal(filepath, bytecode_words):
column_width = 80
indent = ' '
num_words_per_line = int((column_width - len(indent))
/ (len(bytecode_words[0]) + 2))
filename = os.path.basename(filepath)
name, ext = os.path.splitext(filename)
literal = 'constexpr uint32_t k' + name + ext[1:].capitalize() + '[] = {'
for i in range(0, len(bytecode_words)):
if i % num_words_per_line == 0:
if i != 0:
literal += ','
literal += '\n' + indent + bytecode_words[i]
else:
literal += ', ' + bytecode_words[i]
literal += ',\n};\n'
# Add shader source to the end
with open(filepath, 'r') as shader_source:
literal += '// clang-format off\n'
literal += '/***\n'
literal += filename + ' source:\n'
literal += shader_source.read()
literal += '***/\n'
literal += '// clang-format on\n\n'
return literal
def get_targets(directory_path):
if directory_path[-1] != '/':
directory_path += '/'
ret = [glob.glob(directory_path + e) for e in ['*.vert', '*.frag']]
ret = ret[0] + ret[1]
ret.sort()
return ret
def main(argv: Sequence[str]) -> None:
if len(argv) > 2:
raise app.UsageError('Too many command-line arguments.')
if len(argv) < 2:
raise app.UsageError('Too few command-line arguments.')
if not os.path.isdir(argv[1]):
raise app.UsageError('No such directory: ' + argv[1])
targets = get_targets(argv[1])
output = '// Copyright 2023 The Chromium Authors\n' \
'// Use of this source code is governed by ' \
'a BSD-style license that can be\n' \
'// found in the LICENSE file.\n\n' \
'// DO NOT EDIT\n' \
'// This file is automatically generated by ' \
'the compile_shaders.py script\n\n' \
'#ifndef MEDIA_GPU_CHROMEOS_SHADERS_SHADERS_H_\n' \
'#define MEDIA_GPU_CHROMEOS_SHADERS_SHADERS_H_\n\n' \
'#include <stdint.h>\n\n' \
'namespace {\n\n'
for target in targets:
compiler_output = execute_glslang_validator(target)
bytecode = parse_glslang_output(compiler_output)
output += generate_shader_literal(target, bytecode)
output += '} // namespace\n\n'
output += '#endif'
print(output)
if __name__ == '__main__':
app.run(main)