2022-10-09 22:29:26 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
import json
|
|
|
|
import argparse
|
|
|
|
import struct
|
|
|
|
import traceback
|
2022-10-15 20:07:53 +02:00
|
|
|
from spirv import spv
|
2022-10-09 22:29:26 +02:00
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='Build pipeline descriptor')
|
|
|
|
parser.add_argument('--path', nargs='*', help='Directory to look for shaders')
|
2022-10-15 20:07:53 +02:00
|
|
|
parser.add_argument('pipelines', type=argparse.FileType('r'))
|
|
|
|
# TODO strip debug OpName OpLine etc
|
2022-10-09 22:29:26 +02:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
2022-10-15 20:07:53 +02:00
|
|
|
spvOp = spv['Op']
|
|
|
|
spvOpNames = dict()
|
|
|
|
for name, n in spvOp.items():
|
|
|
|
spvOpNames[n] = name
|
|
|
|
|
|
|
|
class SpirvNode:
|
|
|
|
def __init__(self):
|
|
|
|
self.descriptor_set = None
|
|
|
|
self.binding = None
|
|
|
|
self.name = None
|
|
|
|
pass
|
|
|
|
|
|
|
|
class SpirvContext:
|
|
|
|
def __init__(self, nodes_count):
|
|
|
|
self.nodes = [SpirvNode() for i in range(0, nodes_count)]
|
|
|
|
#self.bindings = dict()
|
|
|
|
pass
|
|
|
|
|
|
|
|
def getNode(self, index):
|
|
|
|
return self.nodes[index]
|
|
|
|
|
|
|
|
#def bindNode(self, index):
|
|
|
|
#if not index in bindings
|
|
|
|
#self.bindings[index]
|
|
|
|
|
|
|
|
|
|
|
|
def spvOpHandleName(ctx, args):
|
|
|
|
index = args[0]
|
|
|
|
name = struct.pack(str(len(args)-1)+'I', *args[1:]).split(b'\x00')[0].decode('utf8')
|
|
|
|
ctx.getNode(index).name = name
|
|
|
|
#print('Name for', args[0], name, len(name))
|
|
|
|
|
|
|
|
def spvOpHandleDecorate(ctx, args):
|
|
|
|
node = ctx.getNode(args[0])
|
|
|
|
decor = args[1]
|
|
|
|
if decor == spv['Decoration']['DescriptorSet']:
|
|
|
|
node.descriptor_set = args[2]
|
|
|
|
elif decor == spv['Decoration']['Binding']:
|
|
|
|
node.binding = args[2]
|
|
|
|
#else:
|
|
|
|
#print('Decor ', id, decor)
|
|
|
|
|
|
|
|
spvOpHandlers = {
|
|
|
|
spvOp['OpName']: spvOpHandleName,
|
|
|
|
spvOp['OpDecorate']: spvOpHandleDecorate
|
|
|
|
}
|
|
|
|
|
2022-10-09 22:29:26 +02:00
|
|
|
def parseSpirv(raw_data):
|
|
|
|
if len(raw_data) % 4 != 0:
|
|
|
|
raise Exception('SPIR-V size should be divisible by 4')
|
|
|
|
|
|
|
|
size = len(raw_data) // 4
|
2022-10-15 20:07:53 +02:00
|
|
|
if size < 5:
|
|
|
|
raise Exception('SPIR-V data is too short')
|
|
|
|
|
2022-10-09 22:29:26 +02:00
|
|
|
data = struct.unpack(str(size) + 'I', raw_data)
|
|
|
|
|
2022-10-15 20:07:53 +02:00
|
|
|
if data[0] != spv['MagicNumber']:
|
|
|
|
raise Exception('Unexpected magic ' + str(data[0]))
|
|
|
|
|
|
|
|
nodes_count = data[3]
|
|
|
|
ctx = SpirvContext(nodes_count)
|
|
|
|
|
|
|
|
off = 5
|
|
|
|
while off < size:
|
|
|
|
op = data[off] & 0xffff
|
|
|
|
words = data[off] >> 16
|
|
|
|
args = data[off+1:off+words]
|
|
|
|
if op in spvOpHandlers:
|
|
|
|
spvOpHandlers[op](ctx, args)
|
|
|
|
#print(spvOpNames[op], args)
|
|
|
|
off += words
|
|
|
|
|
|
|
|
for index, node in enumerate(ctx.nodes):
|
|
|
|
if node.descriptor_set is not None:
|
|
|
|
print('[%d:%d] %s (id=%d)' % (node.descriptor_set, node.binding, node.name, index))
|
2022-10-09 22:29:26 +02:00
|
|
|
|
|
|
|
class Shader:
|
|
|
|
def __init__(self, name, filename):
|
|
|
|
self.name = name
|
|
|
|
self.__raw_data = open(filename, 'rb').read()
|
|
|
|
print(name, '=>', len(self.__raw_data))
|
|
|
|
|
|
|
|
try:
|
|
|
|
parseSpirv(self.__raw_data)
|
|
|
|
except:
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
|
|
def doLoadShader(name):
|
|
|
|
try:
|
|
|
|
return Shader(name, name)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if args.path:
|
|
|
|
for path in args.path:
|
|
|
|
try:
|
|
|
|
return Shader(name, path + '/' + name)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
raise Exception('Cannot load shader ' + name)
|
|
|
|
|
|
|
|
shaders = dict()
|
|
|
|
def loadShader(name):
|
|
|
|
if name in shaders:
|
|
|
|
return shaders[name]
|
|
|
|
|
|
|
|
shader = doLoadShader(name)
|
|
|
|
shaders[name] = shader
|
|
|
|
return shader
|
|
|
|
|
|
|
|
class PipelineRayTracing:
|
|
|
|
def __init__(self, name, desc):
|
|
|
|
self.name = name
|
|
|
|
self.rgen = loadShader(desc['rgen'] + '.rgen.spv')
|
|
|
|
self.miss = [] if not 'miss' in desc else [loadShader(s + '.rmiss.spv') for s in desc['miss']]
|
|
|
|
|
|
|
|
def loadHit(hit):
|
|
|
|
ret = dict()
|
|
|
|
suffixes = {'closest': '.rchit.spv', 'any': '.rahit.spv'}
|
|
|
|
for k, v in hit.items():
|
|
|
|
ret[k] = loadShader(v + suffixes[k])
|
|
|
|
return ret
|
|
|
|
|
|
|
|
self.hit = [] if not 'hit' in desc else [loadHit(hit) for hit in desc['hit']]
|
|
|
|
|
|
|
|
class PipelineCompute:
|
|
|
|
def __init__(self, name, desc):
|
|
|
|
self.name = name
|
|
|
|
self.comp = loadShader(desc['comp'] + '.comp.spv')
|
|
|
|
|
|
|
|
def parsePipeline(pipelines, name, desc):
|
|
|
|
if 'inherit' in desc:
|
|
|
|
inherit = pipelines[desc['inherit']]
|
|
|
|
for k, v in inherit.items():
|
|
|
|
if not k in desc:
|
|
|
|
desc[k] = v
|
|
|
|
if 'rgen' in desc:
|
|
|
|
return PipelineRayTracing(name, desc)
|
|
|
|
elif 'comp' in desc:
|
|
|
|
return PipelineCompute(name, desc)
|
|
|
|
|
|
|
|
def loadPipelines():
|
|
|
|
pipelines_desc = json.load(args.pipelines)
|
|
|
|
pipelines = dict()
|
|
|
|
for k, v in pipelines_desc.items():
|
|
|
|
if 'template' in v and v['template']:
|
|
|
|
continue
|
|
|
|
pipelines[k] = parsePipeline(pipelines_desc, k, v)
|
|
|
|
return pipelines
|
|
|
|
|
|
|
|
pipelines = loadPipelines()
|