Path: blob/21.2-virgl/src/amd/compiler/tests/glsl_scraper.py
7165 views
#! /usr/bin/env python31# Taken from Crucible and modified to parse declarations23import argparse4import io5import os6import re7import shutil8import struct9import subprocess10import sys11import tempfile12from textwrap import dedent1314class ShaderCompileError(RuntimeError):15def __init__(self, *args):16super(ShaderCompileError, self).__init__(*args)1718target_env_re = re.compile(r'QO_TARGET_ENV\s+(\S+)')1920stage_to_glslang_stage = {21'VERTEX': 'vert',22'TESS_CONTROL': 'tesc',23'TESS_EVALUATION': 'tese',24'GEOMETRY': 'geom',25'FRAGMENT': 'frag',26'COMPUTE': 'comp',27}2829base_layout_qualifier_id_re = r'({0}\s*=\s*(?P<{0}>\d+))'30id_re = '(?P<name_%d>[^(gl_)]\S+)'31type_re = '(?P<dtype_%d>\S+)'32location_re = base_layout_qualifier_id_re.format('location')33component_re = base_layout_qualifier_id_re.format('component')34binding_re = base_layout_qualifier_id_re.format('binding')35set_re = base_layout_qualifier_id_re.format('set')36unk_re = r'\S+(=\d+)?'37layout_qualifier_re = r'layout\W*\((%s)+\)' % '|'.join([location_re, binding_re, set_re, unk_re, '[, ]+'])38ubo_decl_re = 'uniform\W+%s(\W*{)?(?P<type_ubo>)' % (id_re%0)39ssbo_decl_re = 'buffer\W+%s(\W*{)?(?P<type_ssbo>)' % (id_re%1)40image_buffer_decl_re = r'uniform\W+imageBuffer\w+%s;(?P<type_img_buf>)' % (id_re%2)41image_decl_re = r'uniform\W+image\S+\W+%s;(?P<type_img>)' % (id_re%3)42texture_buffer_decl_re = r'uniform\W+textureBuffer\w+%s;(?P<type_tex_buf>)' % (id_re%4)43combined_texture_sampler_decl_re = r'uniform\W+sampler\S+\W+%s;(?P<type_combined>)' % (id_re%5)44texture_decl_re = r'uniform\W+texture\S+\W+%s;(?P<type_tex>)' % (id_re%6)45sampler_decl_re = r'uniform\W+sampler\w+%s;(?P<type_samp>)' % (id_re%7)46input_re = r'in\W+%s\W+%s;(?P<type_in>)' % (type_re%0, id_re%8)47output_re = r'out\W+%s\W+%s;(?P<type_out>)' % (type_re%1, id_re%9)48match_decl_re = re.compile(layout_qualifier_re + r'\W*((' + r')|('.join([ubo_decl_re, ssbo_decl_re, image_buffer_decl_re, image_decl_re, texture_buffer_decl_re, combined_texture_sampler_decl_re, texture_decl_re, sampler_decl_re, input_re, output_re]) + r'))$')4950class Shader:51def __init__(self, stage):52self.glsl = None53self.stream = io.StringIO()54self.stage = stage55self.dwords = None56self.target_env = ""57self.declarations = []5859def add_text(self, s):60self.stream.write(s)6162def finish_text(self, start_line, end_line):63self.glsl = self.stream.getvalue()64self.stream = None6566# Handle the QO_EXTENSION macro67self.glsl = self.glsl.replace('QO_EXTENSION', '#extension')6869# Handle the QO_DEFINE macro70self.glsl = self.glsl.replace('QO_DEFINE', '#define')7172m = target_env_re.search(self.glsl)73if m:74self.target_env = m.group(1)75self.glsl = self.glsl.replace('QO_TARGET_ENV', '// --target-env')7677self.start_line = start_line78self.end_line = end_line7980def __run_glslang(self, extra_args=[]):81stage = stage_to_glslang_stage[self.stage]82stage_flags = ['-S', stage]8384in_file = tempfile.NamedTemporaryFile(suffix='.'+stage)85src = ('#version 450\n' + self.glsl).encode('utf-8')86in_file.write(src)87in_file.flush()88out_file = tempfile.NamedTemporaryFile(suffix='.spirv')89args = [glslang, '-H'] + extra_args + stage_flags90if self.target_env:91args += ['--target-env', self.target_env]92args += ['-o', out_file.name, in_file.name]93with subprocess.Popen(args,94stdout = subprocess.PIPE,95stderr = subprocess.PIPE,96stdin = subprocess.PIPE) as proc:9798out, err = proc.communicate(timeout=30)99in_file.close()100101if proc.returncode != 0:102# Unfortunately, glslang dumps errors to standard out.103# However, since we don't really want to count on that,104# we'll grab the output of both105message = out.decode('utf-8') + '\n' + err.decode('utf-8')106raise ShaderCompileError(message.strip())107108out_file.seek(0)109spirv = out_file.read()110out_file.close()111return (spirv, out)112113def _parse_declarations(self):114for line in self.glsl.splitlines():115res = re.match(match_decl_re, line.lstrip().rstrip())116if res == None:117continue118res = {k:v for k, v in res.groupdict().items() if v != None}119name = [v for k, v in res.items() if k.startswith('name_')][0]120data_type = ([v for k, v in res.items() if k.startswith('dtype_')] + [''])[0]121decl_type = [k for k, v in res.items() if k.startswith('type_')][0][5:]122location = int(res.get('location', 0))123component = int(res.get('component', 0))124binding = int(res.get('binding', 0))125desc_set = int(res.get('set', 0))126self.declarations.append('{"%s", "%s", QoShaderDeclType_%s, %d, %d, %d, %d}' %127(name, data_type, decl_type, location, component, binding, desc_set))128129def compile(self):130def dwords(f):131while True:132dword_str = f.read(4)133if not dword_str:134return135assert len(dword_str) == 4136yield struct.unpack('I', dword_str)[0]137138(spirv, assembly) = self.__run_glslang()139self.dwords = list(dwords(io.BytesIO(spirv)))140self.assembly = str(assembly, 'utf-8')141142self._parse_declarations()143144def _dump_glsl_code(self, f):145# Dump GLSL code for reference. Use // instead of /* */146# comments so we don't need to escape the GLSL code.147f.write('// GLSL code:\n')148f.write('//')149for line in self.glsl.splitlines():150f.write('\n// {0}'.format(line))151f.write('\n\n')152153def _dump_spirv_code(self, f, var_name):154f.write('/* SPIR-V Assembly:\n')155f.write(' *\n')156for line in self.assembly.splitlines():157f.write(' * ' + line + '\n')158f.write(' */\n')159160f.write('static const uint32_t {0}[] = {{'.format(var_name))161line_start = 0162while line_start < len(self.dwords):163f.write('\n ')164for i in range(line_start, min(line_start + 6, len(self.dwords))):165f.write(' 0x{:08x},'.format(self.dwords[i]))166line_start += 6167f.write('\n};\n')168169def dump_c_code(self, f):170f.write('\n\n')171var_prefix = '__qonos_shader{0}'.format(self.end_line)172173self._dump_glsl_code(f)174self._dump_spirv_code(f, var_prefix + '_spir_v_src')175f.write('static const QoShaderDecl {0}_decls[] = {{{1}}};\n'.format(var_prefix, ', '.join(self.declarations)))176177f.write(dedent("""\178static const QoShaderModuleCreateInfo {0}_info = {{179.spirvSize = sizeof({0}_spir_v_src),180.pSpirv = {0}_spir_v_src,181.declarationCount = sizeof({0}_decls) / sizeof({0}_decls[0]),182.pDeclarations = {0}_decls,183""".format(var_prefix)))184185f.write(" .stage = VK_SHADER_STAGE_{0}_BIT,\n".format(self.stage))186187f.write('};\n')188189f.write('#define __qonos_shader{0}_info __qonos_shader{1}_info\n'\190.format(self.start_line, self.end_line))191192token_exp = re.compile(r'(qoShaderModuleCreateInfoGLSL|qoCreateShaderModuleGLSL|\(|\)|,)')193194class Parser:195def __init__(self, f):196self.infile = f197self.paren_depth = 0198self.shader = None199self.line_number = 1200self.shaders = []201202def tokenize(f):203leftover = ''204for line in f:205pos = 0206while True:207m = token_exp.search(line, pos)208if m:209if m.start() > pos:210leftover += line[pos:m.start()]211pos = m.end()212213if leftover:214yield leftover215leftover = ''216217yield m.group(0)218219else:220leftover += line[pos:]221break222223self.line_number += 1224225if leftover:226yield leftover227228self.token_iter = tokenize(self.infile)229230def handle_shader_src(self):231paren_depth = 1232for t in self.token_iter:233if t == '(':234paren_depth += 1235elif t == ')':236paren_depth -= 1237if paren_depth == 0:238return239240self.current_shader.add_text(t)241242def handle_macro(self, macro):243t = next(self.token_iter)244assert t == '('245246start_line = self.line_number247248if macro == 'qoCreateShaderModuleGLSL':249# Throw away the device parameter250t = next(self.token_iter)251t = next(self.token_iter)252assert t == ','253254stage = next(self.token_iter).strip()255256t = next(self.token_iter)257assert t == ','258259self.current_shader = Shader(stage)260self.handle_shader_src()261self.current_shader.finish_text(start_line, self.line_number)262263self.shaders.append(self.current_shader)264self.current_shader = None265266def run(self):267for t in self.token_iter:268if t in ('qoShaderModuleCreateInfoGLSL', 'qoCreateShaderModuleGLSL'):269self.handle_macro(t)270271def open_file(name, mode):272if name == '-':273if mode == 'w':274return sys.stdout275elif mode == 'r':276return sys.stdin277else:278assert False279else:280return open(name, mode)281282def parse_args():283description = dedent("""\284This program scrapes a C file for any instance of the285qoShaderModuleCreateInfoGLSL and qoCreateShaderModuleGLSL macaros,286grabs the GLSL source code, compiles it to SPIR-V. The resulting287SPIR-V code is written to another C file as an array of 32-bit288words.289290If '-' is passed as the input file or output file, stdin or stdout291will be used instead of a file on disc.""")292293p = argparse.ArgumentParser(294description=description,295formatter_class=argparse.RawDescriptionHelpFormatter)296p.add_argument('-o', '--outfile', default='-',297help='Output to the given file (default: stdout).')298p.add_argument('--with-glslang', metavar='PATH',299default='glslangValidator',300dest='glslang',301help='Full path to the glslangValidator shader compiler.')302p.add_argument('infile', metavar='INFILE')303304return p.parse_args()305306307args = parse_args()308infname = args.infile309outfname = args.outfile310glslang = args.glslang311312with open_file(infname, 'r') as infile:313parser = Parser(infile)314parser.run()315316for shader in parser.shaders:317shader.compile()318319with open_file(outfname, 'w') as outfile:320outfile.write(dedent("""\321/* ========================== DO NOT EDIT! ==========================322* This file is autogenerated by glsl_scraper.py.323*/324325#include <stdint.h>326327#define __QO_SHADER_INFO_VAR2(_line) __qonos_shader ## _line ## _info328#define __QO_SHADER_INFO_VAR(_line) __QO_SHADER_INFO_VAR2(_line)329330#define qoShaderModuleCreateInfoGLSL(stage, ...) \\331__QO_SHADER_INFO_VAR(__LINE__)332333#define qoCreateShaderModuleGLSL(dev, stage, ...) \\334__qoCreateShaderModule((dev), &__QO_SHADER_INFO_VAR(__LINE__))335"""))336337for shader in parser.shaders:338shader.dump_c_code(outfile)339340341