summaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
authorJason Ekstrand <jason.ekstrand@intel.com>2015-08-28 21:42:17 -0700
committerJason Ekstrand <jason.ekstrand@intel.com>2015-08-28 22:16:12 -0700
commitb5a52577781ff191766f49866ce00af749cb86b7 (patch)
tree411fd71c68b41f5119e848df37509b3faddad11b /misc
parentf24e8c61e1cbfa58b00fcf4a17eae01b3fb8578f (diff)
cru: Use glslc instead of glslangValidator for GLSL -> SPIR-V compilation
The shaderc project from Google (https://github.com/google/shaderc) provides a glslang wrapper program called 'glslc' that has a much improved command-line interfacce. In particular, we no longer have to read and write files with specific names to a temporary directory in order to compile shaders. This should make the build process substantially smoother at the expense of having to install a tool.
Diffstat (limited to 'misc')
-rw-r--r--misc/glsl_scraper.py122
1 files changed, 47 insertions, 75 deletions
diff --git a/misc/glsl_scraper.py b/misc/glsl_scraper.py
index a0d5617..cc0861e 100644
--- a/misc/glsl_scraper.py
+++ b/misc/glsl_scraper.py
@@ -11,60 +11,56 @@ import sys
import tempfile
from textwrap import dedent
+class ShaderCompileError(RuntimeError):
+ def __init__(self):
+ super(ShaderCompileError, self).__init__('Compile error')
+
class Shader:
def __init__(self, stage):
self.stream = io.StringIO()
self.stage = stage
+ self.dwords = None
+
+ def add_text(self, s):
+ self.stream.write(s)
+
+ def finish_text(self, line):
+ self.line = line
+ def glsl_source(self):
+ return dedent(self.stream.getvalue())
+
+ def __run_glslc(self, extra_args=[]):
+ stage_flag = '-fshader-stage='
if self.stage == 'VERTEX':
- self.ext = 'vert'
+ stage_flag += 'vertex'
elif self.stage == 'TESS_CONTROL':
- self.ext = 'tesc'
+ stage_flag += 'tesscontrol'
elif self.stage == 'TESS_EVALUATION':
- self.ext = 'tese'
+ stage_flag += 'tesseval'
elif self.stage == 'GEOMETRY':
- self.ext = 'geom'
+ stage_flag += 'geometry'
elif self.stage == 'FRAGMENT':
- self.ext = 'frag'
+ stage_flag += 'fragment'
elif self.stage == 'COMPUTE':
- self.ext = 'comp'
+ stage_flag += 'compute'
else:
assert False
- self.dwords = None
+ with subprocess.Popen([glslc] + extra_args +
+ [stage_flag, '-std=430core', '-o', '-', '-'],
+ stdout = subprocess.PIPE,
+ stdin = subprocess.PIPE) as proc:
- def add_text(self, s):
- self.stream.write(s)
+ proc.stdin.write(self.glsl_source().encode('utf-8'))
+ out, err = proc.communicate(timeout=30)
- def finish_text(self, line):
- self.line = line
+ if proc.returncode != 0:
+ raise ShaderCompileError()
- def glsl_source(self):
- return dedent(self.stream.getvalue())
+ return out
def compile(self):
- # We can assume if we got here that we have a temp directory and that
- # we're currently living in it.
- glsl_fname = 'shader{0}.{1}'.format(self.line, self.ext)
- spirv_fname = self.ext + '.spv'
-
- glsl_file = open(glsl_fname, 'w')
- glsl_file.write('#version 430 core\n')
- glsl_file.write(self.glsl_source())
- glsl_file.close()
-
- out = open('glslang.out', 'wb')
- err = subprocess.call([glslang, '-H', '-V', glsl_fname], stdout=out)
- out.close()
- out = open('glslang.out', 'r')
- if err != 0:
- sys.stderr.write(out.read())
- return False
- else:
- self.assembly = out.readlines()
-
- out.close()
-
def dwords(f):
while True:
dword_str = f.read(4)
@@ -73,14 +69,9 @@ class Shader:
assert len(dword_str) == 4
yield struct.unpack('I', dword_str)[0]
- spirv_file = open(spirv_fname, 'rb')
- self.dwords = list(dwords(spirv_file))
- spirv_file.close()
-
- os.remove(glsl_fname)
- os.remove(spirv_fname)
-
- return True
+ spirv = self.__run_glslc()
+ self.dwords = list(dwords(io.BytesIO(spirv)))
+ self.assembly = str(self.__run_glslc(['-S']), 'utf-8')
def _dump_glsl_code(self, f, var_name):
# First dump the GLSL source as strings
@@ -96,15 +87,8 @@ class Shader:
def _dump_spirv_code(self, f, var_name):
f.write('/* SPIR-V Assembly:\n')
f.write(' *\n')
- preamble = True
- for line in self.assembly:
- if preamble and line.startswith('// Module'):
- preamble = False
-
- if preamble:
- continue
-
- f.write(' * ' + line)
+ for line in self.assembly.splitlines():
+ f.write(' * ' + line + '\n')
f.write(' */\n')
f.write('static const uint32_t {0}[] = {{'.format(var_name))
@@ -242,10 +226,10 @@ def parse_args():
formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument('-o', '--outfile', default='-',
help='Output to the given file (default: stdout).')
- p.add_argument('--with-glslang', metavar='PATH',
- default='glslangValidator',
- dest='glslang',
- help='Full path to the glslangValidator program.')
+ p.add_argument('--with-glslc', metavar='PATH',
+ default='glslc',
+ dest='glslc',
+ help='Full path to the glslc shader compiler.')
p.add_argument('--glsl-only', action='store_true')
p.add_argument('infile', metavar='INFILE')
@@ -255,7 +239,7 @@ def parse_args():
args = parse_args()
infname = args.infile
outfname = args.outfile
-glslang = args.glslang
+glslc = args.glslc
glsl_only = args.glsl_only
with open_file(infname, 'r') as infile:
@@ -263,28 +247,16 @@ with open_file(infname, 'r') as infile:
parser.run()
if not glsl_only:
- # glslangValidator has an absolutely *insane* interface. We pretty much
- # have to run in a temporary directory. Sad day.
- current_dir = os.getcwd()
- tmpdir = tempfile.mkdtemp('glsl_scraper')
-
- try:
- os.chdir(tmpdir)
-
- for shader in parser.shaders:
- success = shader.compile()
-
+ for shader in parser.shaders:
+ try:
+ shader.compile()
+ except ShaderCompileError:
# If any compile fails, we set glsl_only and bail. This way
# the entire fill will either support SPIR-V or not. If we did
# it per-shader, then we could have problems with linking a
# pipeline that's half GLSL and half SPIR-V.
- if not success:
- glsl_only = True
- break
-
- os.chdir(current_dir)
- finally:
- shutil.rmtree(tmpdir)
+ glsl_only = True
+ break
with open_file(outfname, 'w') as outfile:
outfile.write(dedent("""\