vk: Remove stub for CloneImageData
[mesa.git] / src / vulkan / glsl_scraper.py
1 #! /usr/bin/env python
2
3 import argparse
4 import cStringIO
5 import os
6 import re
7 import shutil
8 import struct
9 import subprocess
10 import sys
11 import tempfile
12 from textwrap import dedent
13
14 class Shader:
15 def __init__(self, stage):
16 self.stream = cStringIO.StringIO()
17 self.stage = stage
18
19 if self.stage == 'VERTEX':
20 self.ext = 'vert'
21 elif self.stage == 'TESS_CONTROL':
22 self.ext = 'tesc'
23 elif self.stage == 'TESS_EVALUATION':
24 self.ext = 'tese'
25 elif self.stage == 'GEOMETRY':
26 self.ext = 'geom'
27 elif self.stage == 'FRAGMENT':
28 self.ext = 'frag'
29 elif self.stage == 'COMPUTE':
30 self.ext = 'comp'
31 else:
32 assert False
33
34 def add_text(self, s):
35 self.stream.write(s)
36
37 def finish_text(self, line):
38 self.line = line
39
40 def glsl_source(self):
41 return self.stream.getvalue()
42
43 def compile(self):
44 # We can assume if we got here that we have a temp directory and that
45 # we're currently living in it.
46 glsl_fname = 'shader{0}.{1}'.format(self.line, self.ext)
47 spirv_fname = self.ext + '.spv'
48
49 glsl_file = open(glsl_fname, 'w')
50 glsl_file.write('#version 420 core\n')
51 glsl_file.write(self.glsl_source())
52 glsl_file.close()
53
54 out = open('glslang.out', 'wb')
55 err = subprocess.call([glslang, '-V', glsl_fname], stdout=out)
56 if err != 0:
57 out = open('glslang.out', 'r')
58 sys.stderr.write(out.read())
59 out.close()
60 exit(1)
61
62 def dwords(f):
63 while True:
64 dword_str = f.read(4)
65 if not dword_str:
66 return
67 assert len(dword_str) == 4
68 yield struct.unpack('I', dword_str)[0]
69
70 spirv_file = open(spirv_fname, 'rb')
71 self.dwords = list(dwords(spirv_file))
72 spirv_file.close()
73
74 os.remove(glsl_fname)
75 os.remove(spirv_fname)
76
77 def dump_c_code(self, f, glsl_only = False):
78 f.write('\n\n')
79 var_prefix = '_glsl_helpers_shader{0}'.format(self.line)
80
81 # First dump the GLSL source as strings
82 f.write('static const char {0}_glsl_src[] ='.format(var_prefix))
83 f.write('\n_ANV_SPIRV_' + self.stage)
84 f.write('\n"#version 330\\n"')
85 for line in self.glsl_source().splitlines():
86 if not line.strip():
87 continue
88 f.write('\n"{0}\\n"'.format(line))
89 f.write(';\n\n')
90
91 if glsl_only:
92 return
93
94 # Now dump the SPIR-V source
95 f.write('static const uint32_t {0}_spir_v_src[] = {{'.format(var_prefix))
96 line_start = 0
97 while line_start < len(self.dwords):
98 f.write('\n ')
99 for i in range(line_start, min(line_start + 6, len(self.dwords))):
100 f.write(' 0x{:08x},'.format(self.dwords[i]))
101 line_start += 6
102 f.write('\n};\n')
103
104 token_exp = re.compile(r'(GLSL_VK_SHADER|\(|\)|,)')
105
106 class Parser:
107 def __init__(self, f):
108 self.infile = f
109 self.paren_depth = 0
110 self.shader = None
111 self.line_number = 1
112 self.shaders = []
113
114 def tokenize(f):
115 leftover = ''
116 for line in f:
117 pos = 0
118 while True:
119 m = token_exp.search(line, pos)
120 if m:
121 if m.start() > pos:
122 leftover += line[pos:m.start()]
123 pos = m.end()
124
125 if leftover:
126 yield leftover
127 leftover = ''
128
129 yield m.group(0)
130
131 else:
132 leftover += line[pos:]
133 break
134
135 self.line_number += 1
136
137 if leftover:
138 yield leftover
139
140 self.token_iter = tokenize(self.infile)
141
142 def handle_shader_src(self):
143 paren_depth = 1
144 for t in self.token_iter:
145 if t == '(':
146 paren_depth += 1
147 elif t == ')':
148 paren_depth -= 1
149 if paren_depth == 0:
150 return
151
152 self.current_shader.add_text(t)
153
154 def handle_macro(self):
155 t = self.token_iter.next()
156 assert t == '('
157 t = self.token_iter.next()
158 t = self.token_iter.next()
159 assert t == ','
160
161 stage = self.token_iter.next().strip()
162
163 t = self.token_iter.next()
164 assert t == ','
165
166 self.current_shader = Shader(stage)
167 self.handle_shader_src()
168 self.current_shader.finish_text(self.line_number)
169
170 self.shaders.append(self.current_shader)
171 self.current_shader = None
172
173 def run(self):
174 for t in self.token_iter:
175 if t == 'GLSL_VK_SHADER':
176 self.handle_macro()
177
178 def open_file(name, mode):
179 if name == '-':
180 if mode == 'w':
181 return sys.stdout
182 elif mode == 'r':
183 return sys.stdin
184 else:
185 assert False
186 else:
187 return open(name, mode)
188
189 def parse_args():
190 description = dedent("""\
191 This program scrapes a C file for any instance of the GLSL_VK_SHADER
192 macro, grabs the GLSL source code, compiles it to SPIR-V. The resulting
193 SPIR-V code is written to another C file as an array of 32-bit words.
194
195 If '-' is passed as the input file or output file, stdin or stdout will be
196 used instead of a file on disc.""")
197
198 p = argparse.ArgumentParser(
199 description=description,
200 formatter_class=argparse.RawDescriptionHelpFormatter)
201 p.add_argument('-o', '--outfile', default='-',
202 help='Output to the given file (default: stdout).')
203 p.add_argument('--with-glslang', metavar='PATH',
204 default='glslangValidator',
205 dest='glslang',
206 help='Full path to the glslangValidator program.')
207 p.add_argument('--glsl-only', action='store_true')
208 p.add_argument('infile', metavar='INFILE')
209
210 return p.parse_args()
211
212
213 args = parse_args()
214 infname = args.infile
215 outfname = args.outfile
216 glslang = args.glslang
217 glsl_only = args.glsl_only
218
219 with open_file(infname, 'r') as infile:
220 parser = Parser(infile)
221 parser.run()
222
223 if not glsl_only:
224 # glslangValidator has an absolutely *insane* interface. We pretty much
225 # have to run in a temporary directory. Sad day.
226 current_dir = os.getcwd()
227 tmpdir = tempfile.mkdtemp('glsl_scraper')
228
229 try:
230 os.chdir(tmpdir)
231
232 for shader in parser.shaders:
233 shader.compile()
234
235 os.chdir(current_dir)
236 finally:
237 shutil.rmtree(tmpdir)
238
239 with open_file(outfname, 'w') as outfile:
240 outfile.write(dedent("""\
241 /* =========================== DO NOT EDIT! ===========================
242 * This file is autogenerated by glsl_scraper.py.
243 */
244
245 #include <stdint.h>
246
247 #define _ANV_SPIRV_MAGIC "\\x03\\x02\\x23\\x07\\0\\0\\0\\0"
248
249 #define _ANV_SPIRV_VERTEX _ANV_SPIRV_MAGIC "\\0\\0\\0\\0"
250 #define _ANV_SPIRV_TESS_CONTROL _ANV_SPIRV_MAGIC "\\1\\0\\0\\0"
251 #define _ANV_SPIRV_TESS_EVALUATION _ANV_SPIRV_MAGIC "\\2\\0\\0\\0"
252 #define _ANV_SPIRV_GEOMETRY _ANV_SPIRV_MAGIC "\\3\\0\\0\\0"
253 #define _ANV_SPIRV_FRAGMENT _ANV_SPIRV_MAGIC "\\4\\0\\0\\0"
254 #define _ANV_SPIRV_COMPUTE _ANV_SPIRV_MAGIC "\\5\\0\\0\\0"
255
256 #define _ANV_GLSL_SRC_VAR2(_line) _glsl_helpers_shader ## _line ## _glsl_src
257 #define _ANV_GLSL_SRC_VAR(_line) _ANV_GLSL_SRC_VAR2(_line)
258
259 #define GLSL_VK_SHADER(device, stage, ...) ({ \\
260 VkShader __shader; \\
261 VkShaderCreateInfo __shader_create_info = { \\
262 .sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO, \\
263 .codeSize = sizeof(_ANV_GLSL_SRC_VAR(__LINE__)), \\
264 .pCode = _ANV_GLSL_SRC_VAR(__LINE__), \\
265 }; \\
266 vkCreateShader((VkDevice) device, &__shader_create_info, &__shader); \\
267 __shader; \\
268 })
269 """))
270
271 for shader in parser.shaders:
272 shader.dump_c_code(outfile, glsl_only)