12 from textwrap
import dedent
15 def __init__(self
, stage
):
16 self
.stream
= cStringIO
.StringIO()
19 if self
.stage
== 'VERTEX':
21 elif self
.stage
== 'TESS_CONTROL':
23 elif self
.stage
== 'TESS_EVALUATION':
25 elif self
.stage
== 'GEOMETRY':
27 elif self
.stage
== 'FRAGMENT':
29 elif self
.stage
== 'COMPUTE':
34 def add_text(self
, s
):
37 def finish_text(self
, line
):
40 def glsl_source(self
):
41 return self
.stream
.getvalue()
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'
49 glsl_file
= open(glsl_fname
, 'w')
50 glsl_file
.write('#version 420 core\n')
51 glsl_file
.write(self
.glsl_source())
54 out
= open('glslang.out', 'wb')
55 err
= subprocess
.call([glslang
, '-V', glsl_fname
], stdout
=out
)
57 out
= open('glslang.out', 'r')
58 sys
.stderr
.write(out
.read())
67 assert len(dword_str
) == 4
68 yield struct
.unpack('I', dword_str
)[0]
70 spirv_file
= open(spirv_fname
, 'rb')
71 self
.dwords
= list(dwords(spirv_file
))
75 os
.remove(spirv_fname
)
77 def dump_c_code(self
, f
, glsl_only
= False):
79 var_prefix
= '_glsl_helpers_shader{0}'.format(self
.line
)
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():
88 f
.write('\n"{0}\\n"'.format(line
))
94 # Now dump the SPIR-V source
95 f
.write('static const uint32_t {0}_spir_v_src[] = {{'.format(var_prefix
))
97 while line_start
< len(self
.dwords
):
99 for i
in range(line_start
, min(line_start
+ 6, len(self
.dwords
))):
100 f
.write(' 0x{:08x},'.format(self
.dwords
[i
]))
104 token_exp
= re
.compile(r
'(GLSL_VK_SHADER|\(|\)|,)')
107 def __init__(self
, f
):
119 m
= token_exp
.search(line
, pos
)
122 leftover
+= line
[pos
:m
.start()]
132 leftover
+= line
[pos
:]
135 self
.line_number
+= 1
140 self
.token_iter
= tokenize(self
.infile
)
142 def handle_shader_src(self
):
144 for t
in self
.token_iter
:
152 self
.current_shader
.add_text(t
)
154 def handle_macro(self
):
155 t
= self
.token_iter
.next()
157 t
= self
.token_iter
.next()
158 t
= self
.token_iter
.next()
161 stage
= self
.token_iter
.next().strip()
163 t
= self
.token_iter
.next()
166 self
.current_shader
= Shader(stage
)
167 self
.handle_shader_src()
168 self
.current_shader
.finish_text(self
.line_number
)
170 self
.shaders
.append(self
.current_shader
)
171 self
.current_shader
= None
174 for t
in self
.token_iter
:
175 if t
== 'GLSL_VK_SHADER':
178 def open_file(name
, mode
):
187 return open(name
, mode
)
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.
195 If '-' is passed as the input file or output file, stdin or stdout will be
196 used instead of a file on disc.""")
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',
206 help='Full path to the glslangValidator program.')
207 p
.add_argument('--glsl-only', action
='store_true')
208 p
.add_argument('infile', metavar
='INFILE')
210 return p
.parse_args()
214 infname
= args
.infile
215 outfname
= args
.outfile
216 glslang
= args
.glslang
217 glsl_only
= args
.glsl_only
219 with
open_file(infname
, 'r') as infile
:
220 parser
= Parser(infile
)
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')
232 for shader
in parser
.shaders
:
235 os
.chdir(current_dir
)
237 shutil
.rmtree(tmpdir
)
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.
247 #define _ANV_SPIRV_MAGIC "\\x03\\x02\\x23\\x07\\0\\0\\0\\0"
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"
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)
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__), \\
266 vkCreateShader((VkDevice) device, &__shader_create_info, &__shader); \\
271 for shader
in parser
.shaders
:
272 shader
.dump_c_code(outfile
, glsl_only
)