1*61046927SAndroid Build Coastguard Worker# Copyright © 2022 Intel Corporation 2*61046927SAndroid Build Coastguard Worker# 3*61046927SAndroid Build Coastguard Worker# Permission is hereby granted, free of charge, to any person obtaining a 4*61046927SAndroid Build Coastguard Worker# copy of this software and associated documentation files (the "Software"), 5*61046927SAndroid Build Coastguard Worker# to deal in the Software without restriction, including without limitation 6*61046927SAndroid Build Coastguard Worker# the rights to use, copy, modify, merge, publish, distribute, sublicense, 7*61046927SAndroid Build Coastguard Worker# and/or sell copies of the Software, and to permit persons to whom the 8*61046927SAndroid Build Coastguard Worker# Software is furnished to do so, subject to the following conditions: 9*61046927SAndroid Build Coastguard Worker# 10*61046927SAndroid Build Coastguard Worker# The above copyright notice and this permission notice (including the next 11*61046927SAndroid Build Coastguard Worker# paragraph) shall be included in all copies or substantial portions of the 12*61046927SAndroid Build Coastguard Worker# Software. 13*61046927SAndroid Build Coastguard Worker# 14*61046927SAndroid Build Coastguard Worker# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15*61046927SAndroid Build Coastguard Worker# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16*61046927SAndroid Build Coastguard Worker# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17*61046927SAndroid Build Coastguard Worker# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18*61046927SAndroid Build Coastguard Worker# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19*61046927SAndroid Build Coastguard Worker# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20*61046927SAndroid Build Coastguard Worker# IN THE SOFTWARE. 21*61046927SAndroid Build Coastguard Worker 22*61046927SAndroid Build Coastguard Worker# Converts GLSL shader to SPIR-V library 23*61046927SAndroid Build Coastguard Worker 24*61046927SAndroid Build Coastguard Workerimport argparse 25*61046927SAndroid Build Coastguard Workerimport subprocess 26*61046927SAndroid Build Coastguard Workerimport sys 27*61046927SAndroid Build Coastguard Workerimport os 28*61046927SAndroid Build Coastguard Workerimport typing as T 29*61046927SAndroid Build Coastguard Worker 30*61046927SAndroid Build Coastguard Workerif T.TYPE_CHECKING: 31*61046927SAndroid Build Coastguard Worker class Arguments(T.Protocol): 32*61046927SAndroid Build Coastguard Worker input: str 33*61046927SAndroid Build Coastguard Worker output: str 34*61046927SAndroid Build Coastguard Worker glslang: str 35*61046927SAndroid Build Coastguard Worker create_entry: T.Optional[str] 36*61046927SAndroid Build Coastguard Worker glsl_ver: T.Optional[str] 37*61046927SAndroid Build Coastguard Worker Olib: bool 38*61046927SAndroid Build Coastguard Worker extra: T.Optional[str] 39*61046927SAndroid Build Coastguard Worker vn: str 40*61046927SAndroid Build Coastguard Worker stage: str 41*61046927SAndroid Build Coastguard Worker includes: T.List[str] 42*61046927SAndroid Build Coastguard Worker defines: T.List[str] 43*61046927SAndroid Build Coastguard Worker depfile: T.Optional[str] 44*61046927SAndroid Build Coastguard Worker 45*61046927SAndroid Build Coastguard Worker 46*61046927SAndroid Build Coastguard Workerdef get_args() -> 'Arguments': 47*61046927SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 48*61046927SAndroid Build Coastguard Worker parser.add_argument('input', help="Name of input file.") 49*61046927SAndroid Build Coastguard Worker parser.add_argument('output', help="Name of output file.") 50*61046927SAndroid Build Coastguard Worker parser.add_argument('glslang', help="path to glslangValidator") 51*61046927SAndroid Build Coastguard Worker 52*61046927SAndroid Build Coastguard Worker parser.add_argument("--create-entry", 53*61046927SAndroid Build Coastguard Worker dest="create_entry", 54*61046927SAndroid Build Coastguard Worker help="Create a new entry point and put to the end of a file.") 55*61046927SAndroid Build Coastguard Worker 56*61046927SAndroid Build Coastguard Worker parser.add_argument('--glsl-version', 57*61046927SAndroid Build Coastguard Worker dest="glsl_ver", 58*61046927SAndroid Build Coastguard Worker choices=['100', '110', '120', '130', '140', '150', '300es', '310es', '330', '400', '410', '420', '430', '440', '450', '460'], 59*61046927SAndroid Build Coastguard Worker help="Override GLSL #version declaration in source.") 60*61046927SAndroid Build Coastguard Worker 61*61046927SAndroid Build Coastguard Worker parser.add_argument("-Olib", 62*61046927SAndroid Build Coastguard Worker action='store_true', 63*61046927SAndroid Build Coastguard Worker help="Any optimizations are disabled and unused functions are saved.") 64*61046927SAndroid Build Coastguard Worker 65*61046927SAndroid Build Coastguard Worker parser.add_argument("--extra-flags", 66*61046927SAndroid Build Coastguard Worker dest="extra", 67*61046927SAndroid Build Coastguard Worker help="Pass additional flags to glslangValidator.") 68*61046927SAndroid Build Coastguard Worker 69*61046927SAndroid Build Coastguard Worker parser.add_argument("--vn", 70*61046927SAndroid Build Coastguard Worker dest="vn", 71*61046927SAndroid Build Coastguard Worker required=True, 72*61046927SAndroid Build Coastguard Worker help="Variable name. Creates a C header file that contains a uint32_t array.") 73*61046927SAndroid Build Coastguard Worker 74*61046927SAndroid Build Coastguard Worker parser.add_argument("--stage", 75*61046927SAndroid Build Coastguard Worker default="vert", 76*61046927SAndroid Build Coastguard Worker choices=['vert', 'tesc', 'tese', 'geom', 'frag', 'comp'], 77*61046927SAndroid Build Coastguard Worker help="Uses specified stage rather than parsing the file extension") 78*61046927SAndroid Build Coastguard Worker 79*61046927SAndroid Build Coastguard Worker parser.add_argument("-I", 80*61046927SAndroid Build Coastguard Worker dest="includes", 81*61046927SAndroid Build Coastguard Worker default=[], 82*61046927SAndroid Build Coastguard Worker action='append', 83*61046927SAndroid Build Coastguard Worker help="Include directory") 84*61046927SAndroid Build Coastguard Worker 85*61046927SAndroid Build Coastguard Worker parser.add_argument("-D", 86*61046927SAndroid Build Coastguard Worker dest="defines", 87*61046927SAndroid Build Coastguard Worker default=[], 88*61046927SAndroid Build Coastguard Worker action='append', 89*61046927SAndroid Build Coastguard Worker help="Defines") 90*61046927SAndroid Build Coastguard Worker 91*61046927SAndroid Build Coastguard Worker parser.add_argument('--depfile', 92*61046927SAndroid Build Coastguard Worker dest="depfile", 93*61046927SAndroid Build Coastguard Worker default=None, 94*61046927SAndroid Build Coastguard Worker action='store', 95*61046927SAndroid Build Coastguard Worker help='Where glslangValidator should write its depfile, if unset no depfile will be written.') 96*61046927SAndroid Build Coastguard Worker 97*61046927SAndroid Build Coastguard Worker args = parser.parse_args() 98*61046927SAndroid Build Coastguard Worker return args 99*61046927SAndroid Build Coastguard Worker 100*61046927SAndroid Build Coastguard Worker 101*61046927SAndroid Build Coastguard Workerdef create_include_guard(lines: T.List[str], filename: str) -> T.List[str]: 102*61046927SAndroid Build Coastguard Worker filename = filename.replace('.', '_') 103*61046927SAndroid Build Coastguard Worker upper_name = filename.upper() 104*61046927SAndroid Build Coastguard Worker 105*61046927SAndroid Build Coastguard Worker guard_head = [f"#ifndef {upper_name}\n", 106*61046927SAndroid Build Coastguard Worker f"#define {upper_name}\n"] 107*61046927SAndroid Build Coastguard Worker guard_tail = [f"\n#endif // {upper_name}\n"] 108*61046927SAndroid Build Coastguard Worker 109*61046927SAndroid Build Coastguard Worker # remove '#pragma once' 110*61046927SAndroid Build Coastguard Worker for idx, l in enumerate(lines): 111*61046927SAndroid Build Coastguard Worker if '#pragma once' in l: 112*61046927SAndroid Build Coastguard Worker lines.pop(idx) 113*61046927SAndroid Build Coastguard Worker break 114*61046927SAndroid Build Coastguard Worker 115*61046927SAndroid Build Coastguard Worker return guard_head + lines + guard_tail 116*61046927SAndroid Build Coastguard Worker 117*61046927SAndroid Build Coastguard Worker 118*61046927SAndroid Build Coastguard Workerdef convert_to_static_variable(lines: T.List[str], varname: str) -> T.List[str]: 119*61046927SAndroid Build Coastguard Worker for idx, l in enumerate(lines): 120*61046927SAndroid Build Coastguard Worker if varname in l: 121*61046927SAndroid Build Coastguard Worker lines[idx] = f"static {l}" 122*61046927SAndroid Build Coastguard Worker return lines 123*61046927SAndroid Build Coastguard Worker raise RuntimeError(f'Did not find {varname}, this is unexpected') 124*61046927SAndroid Build Coastguard Worker 125*61046927SAndroid Build Coastguard Worker 126*61046927SAndroid Build Coastguard Workerdef override_version(lines: T.List[str], glsl_version: str) -> T.List[str]: 127*61046927SAndroid Build Coastguard Worker for idx, l in enumerate(lines): 128*61046927SAndroid Build Coastguard Worker if '#version ' in l: 129*61046927SAndroid Build Coastguard Worker lines[idx] = f"#version {glsl_version}\n" 130*61046927SAndroid Build Coastguard Worker return lines 131*61046927SAndroid Build Coastguard Worker raise RuntimeError('Did not find #version directive, this is unexpected') 132*61046927SAndroid Build Coastguard Worker 133*61046927SAndroid Build Coastguard Worker 134*61046927SAndroid Build Coastguard Workerdef postprocess_file(args: 'Arguments') -> None: 135*61046927SAndroid Build Coastguard Worker with open(args.output, "r") as r: 136*61046927SAndroid Build Coastguard Worker lines = r.readlines() 137*61046927SAndroid Build Coastguard Worker 138*61046927SAndroid Build Coastguard Worker # glslangValidator creates a variable without the static modifier. 139*61046927SAndroid Build Coastguard Worker lines = convert_to_static_variable(lines, args.vn) 140*61046927SAndroid Build Coastguard Worker 141*61046927SAndroid Build Coastguard Worker # '#pragma once' is unstandardised. 142*61046927SAndroid Build Coastguard Worker lines = create_include_guard(lines, os.path.basename(r.name)) 143*61046927SAndroid Build Coastguard Worker 144*61046927SAndroid Build Coastguard Worker with open(args.output, "w") as w: 145*61046927SAndroid Build Coastguard Worker w.writelines(lines) 146*61046927SAndroid Build Coastguard Worker 147*61046927SAndroid Build Coastguard Worker 148*61046927SAndroid Build Coastguard Workerdef preprocess_file(args: 'Arguments', origin_file: T.TextIO, directory: os.PathLike, filemap: T.Dict[str, str]) -> str: 149*61046927SAndroid Build Coastguard Worker if args.create_entry is None and args.glsl_ver is None: 150*61046927SAndroid Build Coastguard Worker return origin_file.name 151*61046927SAndroid Build Coastguard Worker 152*61046927SAndroid Build Coastguard Worker with open(os.path.join(directory, os.path.basename(origin_file.name)), "w") as copy_file: 153*61046927SAndroid Build Coastguard Worker lines = origin_file.readlines() 154*61046927SAndroid Build Coastguard Worker 155*61046927SAndroid Build Coastguard Worker if args.create_entry is not None: 156*61046927SAndroid Build Coastguard Worker lines.append(f"\nvoid {args.create_entry}() {{}}\n") 157*61046927SAndroid Build Coastguard Worker 158*61046927SAndroid Build Coastguard Worker if args.glsl_ver is not None: 159*61046927SAndroid Build Coastguard Worker override_version(lines, args.glsl_ver) 160*61046927SAndroid Build Coastguard Worker 161*61046927SAndroid Build Coastguard Worker copy_file.writelines(lines) 162*61046927SAndroid Build Coastguard Worker 163*61046927SAndroid Build Coastguard Worker filemap[copy_file.name] = origin_file.name 164*61046927SAndroid Build Coastguard Worker return copy_file.name 165*61046927SAndroid Build Coastguard Worker 166*61046927SAndroid Build Coastguard Worker 167*61046927SAndroid Build Coastguard Workerdef fixup_depfile(depfile: str, filemap: T.Mapping[str, str]) -> None: 168*61046927SAndroid Build Coastguard Worker """Replace generated file references in the depfile with their source. 169*61046927SAndroid Build Coastguard Worker """ 170*61046927SAndroid Build Coastguard Worker depends: T.List[str] = [] 171*61046927SAndroid Build Coastguard Worker out: T.Optional[str] = None 172*61046927SAndroid Build Coastguard Worker with open(depfile, 'r') as f: 173*61046927SAndroid Build Coastguard Worker for line in f.readlines(): 174*61046927SAndroid Build Coastguard Worker if ':' in line: 175*61046927SAndroid Build Coastguard Worker out, line = line.split(':', 1) 176*61046927SAndroid Build Coastguard Worker depends.extend(l.strip() for l in line.split()) 177*61046927SAndroid Build Coastguard Worker 178*61046927SAndroid Build Coastguard Worker with open(depfile, 'w') as f: 179*61046927SAndroid Build Coastguard Worker f.write(out) 180*61046927SAndroid Build Coastguard Worker f.write(': ') 181*61046927SAndroid Build Coastguard Worker for dep in depends: 182*61046927SAndroid Build Coastguard Worker f.write(filemap.get(dep, dep)) 183*61046927SAndroid Build Coastguard Worker f.write(' ') 184*61046927SAndroid Build Coastguard Worker f.write('\n') 185*61046927SAndroid Build Coastguard Worker 186*61046927SAndroid Build Coastguard Worker 187*61046927SAndroid Build Coastguard Workerdef process_file(args: 'Arguments') -> None: 188*61046927SAndroid Build Coastguard Worker filemap: T.Dict[str, str] = {} 189*61046927SAndroid Build Coastguard Worker 190*61046927SAndroid Build Coastguard Worker with open(args.input, "r") as infile: 191*61046927SAndroid Build Coastguard Worker copy_file = preprocess_file( 192*61046927SAndroid Build Coastguard Worker args, infile, os.path.dirname(args.output), filemap) 193*61046927SAndroid Build Coastguard Worker 194*61046927SAndroid Build Coastguard Worker cmd_list = [args.glslang] 195*61046927SAndroid Build Coastguard Worker 196*61046927SAndroid Build Coastguard Worker if args.Olib: 197*61046927SAndroid Build Coastguard Worker cmd_list.append("--keep-uncalled") 198*61046927SAndroid Build Coastguard Worker 199*61046927SAndroid Build Coastguard Worker if args.vn is not None: 200*61046927SAndroid Build Coastguard Worker cmd_list.extend(["--variable-name", args.vn]) 201*61046927SAndroid Build Coastguard Worker 202*61046927SAndroid Build Coastguard Worker if args.extra is not None: 203*61046927SAndroid Build Coastguard Worker cmd_list.append(args.extra) 204*61046927SAndroid Build Coastguard Worker 205*61046927SAndroid Build Coastguard Worker if args.create_entry is not None: 206*61046927SAndroid Build Coastguard Worker cmd_list.extend(["--entry-point", args.create_entry]) 207*61046927SAndroid Build Coastguard Worker 208*61046927SAndroid Build Coastguard Worker if args.depfile is not None: 209*61046927SAndroid Build Coastguard Worker cmd_list.extend(['--depfile', args.depfile]) 210*61046927SAndroid Build Coastguard Worker 211*61046927SAndroid Build Coastguard Worker for f in args.includes: 212*61046927SAndroid Build Coastguard Worker cmd_list.append('-I' + f) 213*61046927SAndroid Build Coastguard Worker for d in args.defines: 214*61046927SAndroid Build Coastguard Worker cmd_list.append('-D' + d) 215*61046927SAndroid Build Coastguard Worker 216*61046927SAndroid Build Coastguard Worker cmd_list.extend([ 217*61046927SAndroid Build Coastguard Worker '-V', 218*61046927SAndroid Build Coastguard Worker '-o', args.output, 219*61046927SAndroid Build Coastguard Worker '-S', args.stage, 220*61046927SAndroid Build Coastguard Worker copy_file, 221*61046927SAndroid Build Coastguard Worker ]) 222*61046927SAndroid Build Coastguard Worker 223*61046927SAndroid Build Coastguard Worker ret = subprocess.run(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30) 224*61046927SAndroid Build Coastguard Worker if ret.returncode != 0: 225*61046927SAndroid Build Coastguard Worker print(ret.stdout) 226*61046927SAndroid Build Coastguard Worker print(ret.stderr, file=sys.stderr) 227*61046927SAndroid Build Coastguard Worker sys.exit(1) 228*61046927SAndroid Build Coastguard Worker 229*61046927SAndroid Build Coastguard Worker if args.vn is not None: 230*61046927SAndroid Build Coastguard Worker postprocess_file(args) 231*61046927SAndroid Build Coastguard Worker 232*61046927SAndroid Build Coastguard Worker if args.create_entry is not None: 233*61046927SAndroid Build Coastguard Worker os.remove(copy_file) 234*61046927SAndroid Build Coastguard Worker 235*61046927SAndroid Build Coastguard Worker if args.depfile is not None: 236*61046927SAndroid Build Coastguard Worker fixup_depfile(args.depfile, filemap) 237*61046927SAndroid Build Coastguard Worker 238*61046927SAndroid Build Coastguard Worker 239*61046927SAndroid Build Coastguard Workerdef main() -> None: 240*61046927SAndroid Build Coastguard Worker args = get_args() 241*61046927SAndroid Build Coastguard Worker process_file(args) 242*61046927SAndroid Build Coastguard Worker 243*61046927SAndroid Build Coastguard Worker 244*61046927SAndroid Build Coastguard Workerif __name__ == "__main__": 245*61046927SAndroid Build Coastguard Worker main() 246