mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-01-04 15:40:11 +01:00
272 lines
8 KiB
Python
272 lines
8 KiB
Python
#! /usr/bin/env python
|
|
|
|
import argparse
|
|
import cStringIO
|
|
import os
|
|
import re
|
|
import shutil
|
|
import struct
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from textwrap import dedent
|
|
|
|
class Shader:
|
|
def __init__(self, stage):
|
|
self.stream = cStringIO.StringIO()
|
|
self.stage = stage
|
|
|
|
if self.stage == 'VERTEX':
|
|
self.ext = 'vert'
|
|
elif self.stage == 'TESS_CONTROL':
|
|
self.ext = 'tesc'
|
|
elif self.stage == 'TESS_EVALUATION':
|
|
self.ext = 'tese'
|
|
elif self.stage == 'GEOMETRY':
|
|
self.ext = 'geom'
|
|
elif self.stage == 'FRAGMENT':
|
|
self.ext = 'frag'
|
|
elif self.stage == 'COMPUTE':
|
|
self.ext = 'comp'
|
|
else:
|
|
assert False
|
|
|
|
def add_text(self, s):
|
|
self.stream.write(s)
|
|
|
|
def finish_text(self, line):
|
|
self.line = line
|
|
|
|
def glsl_source(self):
|
|
return self.stream.getvalue()
|
|
|
|
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 420 core\n')
|
|
glsl_file.write(self.glsl_source())
|
|
glsl_file.close()
|
|
|
|
out = open('glslang.out', 'wb')
|
|
err = subprocess.call([glslang, '-V', glsl_fname], stdout=out)
|
|
if err != 0:
|
|
out = open('glslang.out', 'r')
|
|
sys.stderr.write(out.read())
|
|
out.close()
|
|
exit(1)
|
|
|
|
def dwords(f):
|
|
while True:
|
|
dword_str = f.read(4)
|
|
if not dword_str:
|
|
return
|
|
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)
|
|
|
|
def dump_c_code(self, f, glsl_only = False):
|
|
f.write('\n\n')
|
|
var_prefix = '_glsl_helpers_shader{0}'.format(self.line)
|
|
|
|
# First dump the GLSL source as strings
|
|
f.write('static const char {0}_glsl_src[] ='.format(var_prefix))
|
|
f.write('\n_ANV_SPIRV_' + self.stage)
|
|
f.write('\n"#version 330\\n"')
|
|
for line in self.glsl_source().splitlines():
|
|
if not line.strip():
|
|
continue
|
|
f.write('\n"{0}\\n"'.format(line))
|
|
f.write(';\n\n')
|
|
|
|
if glsl_only:
|
|
return
|
|
|
|
# Now dump the SPIR-V source
|
|
f.write('static const uint32_t {0}_spir_v_src[] = {{'.format(var_prefix))
|
|
line_start = 0
|
|
while line_start < len(self.dwords):
|
|
f.write('\n ')
|
|
for i in range(line_start, min(line_start + 6, len(self.dwords))):
|
|
f.write(' 0x{:08x},'.format(self.dwords[i]))
|
|
line_start += 6
|
|
f.write('\n};\n')
|
|
|
|
token_exp = re.compile(r'(GLSL_VK_SHADER|\(|\)|,)')
|
|
|
|
class Parser:
|
|
def __init__(self, f):
|
|
self.infile = f
|
|
self.paren_depth = 0
|
|
self.shader = None
|
|
self.line_number = 1
|
|
self.shaders = []
|
|
|
|
def tokenize(f):
|
|
leftover = ''
|
|
for line in f:
|
|
pos = 0
|
|
while True:
|
|
m = token_exp.search(line, pos)
|
|
if m:
|
|
if m.start() > pos:
|
|
leftover += line[pos:m.start()]
|
|
pos = m.end()
|
|
|
|
if leftover:
|
|
yield leftover
|
|
leftover = ''
|
|
|
|
yield m.group(0)
|
|
|
|
else:
|
|
leftover += line[pos:]
|
|
break
|
|
|
|
self.line_number += 1
|
|
|
|
if leftover:
|
|
yield leftover
|
|
|
|
self.token_iter = tokenize(self.infile)
|
|
|
|
def handle_shader_src(self):
|
|
paren_depth = 1
|
|
for t in self.token_iter:
|
|
if t == '(':
|
|
paren_depth += 1
|
|
elif t == ')':
|
|
paren_depth -= 1
|
|
if paren_depth == 0:
|
|
return
|
|
|
|
self.current_shader.add_text(t)
|
|
|
|
def handle_macro(self):
|
|
t = self.token_iter.next()
|
|
assert t == '('
|
|
t = self.token_iter.next()
|
|
t = self.token_iter.next()
|
|
assert t == ','
|
|
|
|
stage = self.token_iter.next().strip()
|
|
|
|
t = self.token_iter.next()
|
|
assert t == ','
|
|
|
|
self.current_shader = Shader(stage)
|
|
self.handle_shader_src()
|
|
self.current_shader.finish_text(self.line_number)
|
|
|
|
self.shaders.append(self.current_shader)
|
|
self.current_shader = None
|
|
|
|
def run(self):
|
|
for t in self.token_iter:
|
|
if t == 'GLSL_VK_SHADER':
|
|
self.handle_macro()
|
|
|
|
def open_file(name, mode):
|
|
if name == '-':
|
|
if mode == 'w':
|
|
return sys.stdout
|
|
elif mode == 'r':
|
|
return sys.stdin
|
|
else:
|
|
assert False
|
|
else:
|
|
return open(name, mode)
|
|
|
|
def parse_args():
|
|
description = dedent("""\
|
|
This program scrapes a C file for any instance of the GLSL_VK_SHADER
|
|
macro, grabs the GLSL source code, compiles it to SPIR-V. The resulting
|
|
SPIR-V code is written to another C file as an array of 32-bit words.
|
|
|
|
If '-' is passed as the input file or output file, stdin or stdout will be
|
|
used instead of a file on disc.""")
|
|
|
|
p = argparse.ArgumentParser(
|
|
description=description,
|
|
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('--glsl-only', action='store_true')
|
|
p.add_argument('infile', metavar='INFILE')
|
|
|
|
return p.parse_args()
|
|
|
|
|
|
args = parse_args()
|
|
infname = args.infile
|
|
outfname = args.outfile
|
|
glslang = args.glslang
|
|
glsl_only = args.glsl_only
|
|
|
|
with open_file(infname, 'r') as infile:
|
|
parser = Parser(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:
|
|
shader.compile()
|
|
|
|
os.chdir(current_dir)
|
|
finally:
|
|
shutil.rmtree(tmpdir)
|
|
|
|
with open_file(outfname, 'w') as outfile:
|
|
outfile.write(dedent("""\
|
|
/* =========================== DO NOT EDIT! ===========================
|
|
* This file is autogenerated by glsl_scraper.py.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#define _ANV_SPIRV_MAGIC "\\x03\\x02\\x23\\x07\\0\\0\\0\\0"
|
|
|
|
#define _ANV_SPIRV_VERTEX _ANV_SPIRV_MAGIC "\\0\\0\\0\\0"
|
|
#define _ANV_SPIRV_TESS_CONTROL _ANV_SPIRV_MAGIC "\\1\\0\\0\\0"
|
|
#define _ANV_SPIRV_TESS_EVALUATION _ANV_SPIRV_MAGIC "\\2\\0\\0\\0"
|
|
#define _ANV_SPIRV_GEOMETRY _ANV_SPIRV_MAGIC "\\3\\0\\0\\0"
|
|
#define _ANV_SPIRV_FRAGMENT _ANV_SPIRV_MAGIC "\\4\\0\\0\\0"
|
|
#define _ANV_SPIRV_COMPUTE _ANV_SPIRV_MAGIC "\\5\\0\\0\\0"
|
|
|
|
#define _ANV_GLSL_SRC_VAR2(_line) _glsl_helpers_shader ## _line ## _glsl_src
|
|
#define _ANV_GLSL_SRC_VAR(_line) _ANV_GLSL_SRC_VAR2(_line)
|
|
|
|
#define GLSL_VK_SHADER(device, stage, ...) ({ \\
|
|
VkShader __shader; \\
|
|
VkShaderCreateInfo __shader_create_info = { \\
|
|
.sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO, \\
|
|
.codeSize = sizeof(_ANV_GLSL_SRC_VAR(__LINE__)), \\
|
|
.pCode = _ANV_GLSL_SRC_VAR(__LINE__), \\
|
|
}; \\
|
|
vkCreateShader((VkDevice) device, &__shader_create_info, &__shader); \\
|
|
__shader; \\
|
|
})
|
|
"""))
|
|
|
|
for shader in parser.shaders:
|
|
shader.dump_c_code(outfile, glsl_only)
|