--- /dev/null
+#! /usr/bin/env python3
+# Taken from Crucible and modified to parse declarations
+
+import argparse
+import io
+import os
+import re
+import shutil
+import struct
+import subprocess
+import sys
+import tempfile
+from textwrap import dedent
+
+class ShaderCompileError(RuntimeError):
+ def __init__(self, *args):
+ super(ShaderCompileError, self).__init__(*args)
+
+target_env_re = re.compile(r'QO_TARGET_ENV\s+(\S+)')
+
+stage_to_glslang_stage = {
+ 'VERTEX': 'vert',
+ 'TESS_CONTROL': 'tesc',
+ 'TESS_EVALUATION': 'tese',
+ 'GEOMETRY': 'geom',
+ 'FRAGMENT': 'frag',
+ 'COMPUTE': 'comp',
+}
+
+base_layout_qualifier_id_re = r'({0}\s*=\s*(?P<{0}>\d+))'
+id_re = '(?P<name_%d>[^(gl_)]\S+)'
+type_re = '(?P<dtype_%d>\S+)'
+location_re = base_layout_qualifier_id_re.format('location')
+component_re = base_layout_qualifier_id_re.format('component')
+binding_re = base_layout_qualifier_id_re.format('binding')
+set_re = base_layout_qualifier_id_re.format('set')
+unk_re = r'\S+(=\d+)?'
+layout_qualifier_re = r'layout\W*\((%s)+\)' % '|'.join([location_re, binding_re, set_re, unk_re, '[, ]+'])
+ubo_decl_re = 'uniform\W+%s(\W*{)?(?P<type_ubo>)' % (id_re%0)
+ssbo_decl_re = 'buffer\W+%s(\W*{)?(?P<type_ssbo>)' % (id_re%1)
+image_buffer_decl_re = r'uniform\W+imageBuffer\w+%s;(?P<type_img_buf>)' % (id_re%2)
+image_decl_re = r'uniform\W+image\S+\W+%s;(?P<type_img>)' % (id_re%3)
+texture_buffer_decl_re = r'uniform\W+textureBuffer\w+%s;(?P<type_tex_buf>)' % (id_re%4)
+combined_texture_sampler_decl_re = r'uniform\W+sampler\S+\W+%s;(?P<type_combined>)' % (id_re%5)
+texture_decl_re = r'uniform\W+texture\S+\W+%s;(?P<type_tex>)' % (id_re%6)
+sampler_decl_re = r'uniform\W+sampler\w+%s;(?P<type_samp>)' % (id_re%7)
+input_re = r'in\W+%s\W+%s;(?P<type_in>)' % (type_re%0, id_re%8)
+output_re = r'out\W+%s\W+%s;(?P<type_out>)' % (type_re%1, id_re%9)
+match_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'))$')
+
+class Shader:
+ def __init__(self, stage):
+ self.glsl = None
+ self.stream = io.StringIO()
+ self.stage = stage
+ self.dwords = None
+ self.target_env = ""
+ self.declarations = []
+
+ def add_text(self, s):
+ self.stream.write(s)
+
+ def finish_text(self, start_line, end_line):
+ self.glsl = self.stream.getvalue()
+ self.stream = None
+
+ # Handle the QO_EXTENSION macro
+ self.glsl = self.glsl.replace('QO_EXTENSION', '#extension')
+
+ # Handle the QO_DEFINE macro
+ self.glsl = self.glsl.replace('QO_DEFINE', '#define')
+
+ m = target_env_re.search(self.glsl)
+ if m:
+ self.target_env = m.group(1)
+ self.glsl = self.glsl.replace('QO_TARGET_ENV', '// --target-env')
+
+ self.start_line = start_line
+ self.end_line = end_line
+
+ def __run_glslang(self, extra_args=[]):
+ stage = stage_to_glslang_stage[self.stage]
+ stage_flags = ['-S', stage]
+
+ in_file = tempfile.NamedTemporaryFile(suffix='.'+stage)
+ src = ('#version 450\n' + self.glsl).encode('utf-8')
+ in_file.write(src)
+ in_file.flush()
+ out_file = tempfile.NamedTemporaryFile(suffix='.spirv')
+ args = [glslang, '-H'] + extra_args + stage_flags
+ if self.target_env:
+ args += ['--target-env', self.target_env]
+ args += ['-o', out_file.name, in_file.name]
+ with subprocess.Popen(args,
+ stdout = subprocess.PIPE,
+ stderr = subprocess.PIPE,
+ stdin = subprocess.PIPE) as proc:
+
+ out, err = proc.communicate(timeout=30)
+ in_file.close()
+
+ if proc.returncode != 0:
+ # Unfortunately, glslang dumps errors to standard out.
+ # However, since we don't really want to count on that,
+ # we'll grab the output of both
+ message = out.decode('utf-8') + '\n' + err.decode('utf-8')
+ raise ShaderCompileError(message.strip())
+
+ out_file.seek(0)
+ spirv = out_file.read()
+ out_file.close()
+ return (spirv, out)
+
+ def _parse_declarations(self):
+ for line in self.glsl.splitlines():
+ res = re.match(match_decl_re, line.lstrip().rstrip())
+ if res == None:
+ continue
+ res = {k:v for k, v in res.groupdict().items() if v != None}
+ name = [v for k, v in res.items() if k.startswith('name_')][0]
+ data_type = ([v for k, v in res.items() if k.startswith('dtype_')] + [''])[0]
+ decl_type = [k for k, v in res.items() if k.startswith('type_')][0][5:]
+ location = int(res.get('location', 0))
+ component = int(res.get('component', 0))
+ binding = int(res.get('binding', 0))
+ desc_set = int(res.get('set', 0))
+ self.declarations.append('{"%s", "%s", QoShaderDeclType_%s, %d, %d, %d, %d}' %
+ (name, data_type, decl_type, location, component, binding, desc_set))
+
+ def compile(self):
+ 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, assembly) = self.__run_glslang()
+ self.dwords = list(dwords(io.BytesIO(spirv)))
+ self.assembly = str(assembly, 'utf-8')
+
+ self._parse_declarations()
+
+ def _dump_glsl_code(self, f):
+ # Dump GLSL code for reference. Use // instead of /* */
+ # comments so we don't need to escape the GLSL code.
+ f.write('// GLSL code:\n')
+ f.write('//')
+ for line in self.glsl.splitlines():
+ f.write('\n// {0}'.format(line))
+ f.write('\n\n')
+
+ def _dump_spirv_code(self, f, var_name):
+ f.write('/* SPIR-V Assembly:\n')
+ f.write(' *\n')
+ for line in self.assembly.splitlines():
+ f.write(' * ' + line + '\n')
+ f.write(' */\n')
+
+ f.write('static const uint32_t {0}[] = {{'.format(var_name))
+ 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')
+
+ def dump_c_code(self, f):
+ f.write('\n\n')
+ var_prefix = '__qonos_shader{0}'.format(self.end_line)
+
+ self._dump_glsl_code(f)
+ self._dump_spirv_code(f, var_prefix + '_spir_v_src')
+ f.write('static const QoShaderDecl {0}_decls[] = {{{1}}};\n'.format(var_prefix, ', '.join(self.declarations)))
+
+ f.write(dedent("""\
+ static const QoShaderModuleCreateInfo {0}_info = {{
+ .spirvSize = sizeof({0}_spir_v_src),
+ .pSpirv = {0}_spir_v_src,
+ .declarationCount = sizeof({0}_decls) / sizeof({0}_decls[0]),
+ .pDeclarations = {0}_decls,
+ """.format(var_prefix)))
+
+ f.write(" .stage = VK_SHADER_STAGE_{0}_BIT,\n".format(self.stage))
+
+ f.write('};\n')
+
+ f.write('#define __qonos_shader{0}_info __qonos_shader{1}_info\n'\
+ .format(self.start_line, self.end_line))
+
+token_exp = re.compile(r'(qoShaderModuleCreateInfoGLSL|qoCreateShaderModuleGLSL|\(|\)|,)')
+
+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, macro):
+ t = next(self.token_iter)
+ assert t == '('
+
+ start_line = self.line_number
+
+ if macro == 'qoCreateShaderModuleGLSL':
+ # Throw away the device parameter
+ t = next(self.token_iter)
+ t = next(self.token_iter)
+ assert t == ','
+
+ stage = next(self.token_iter).strip()
+
+ t = next(self.token_iter)
+ assert t == ','
+
+ self.current_shader = Shader(stage)
+ self.handle_shader_src()
+ self.current_shader.finish_text(start_line, self.line_number)
+
+ self.shaders.append(self.current_shader)
+ self.current_shader = None
+
+ def run(self):
+ for t in self.token_iter:
+ if t in ('qoShaderModuleCreateInfoGLSL', 'qoCreateShaderModuleGLSL'):
+ self.handle_macro(t)
+
+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
+ qoShaderModuleCreateInfoGLSL and qoCreateShaderModuleGLSL macaros,
+ 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 shader compiler.')
+ p.add_argument('infile', metavar='INFILE')
+
+ return p.parse_args()
+
+
+args = parse_args()
+infname = args.infile
+outfname = args.outfile
+glslang = args.glslang
+
+with open_file(infname, 'r') as infile:
+ parser = Parser(infile)
+ parser.run()
+
+for shader in parser.shaders:
+ shader.compile()
+
+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 __QO_SHADER_INFO_VAR2(_line) __qonos_shader ## _line ## _info
+ #define __QO_SHADER_INFO_VAR(_line) __QO_SHADER_INFO_VAR2(_line)
+
+ #define qoShaderModuleCreateInfoGLSL(stage, ...) \\
+ __QO_SHADER_INFO_VAR(__LINE__)
+
+ #define qoCreateShaderModuleGLSL(dev, stage, ...) \\
+ __qoCreateShaderModule((dev), &__QO_SHADER_INFO_VAR(__LINE__))
+ """))
+
+ for shader in parser.shaders:
+ shader.dump_c_code(outfile)
*
*/
#include "helpers.h"
+#include "vulkan/vk_format.h"
+#include "llvm/ac_llvm_util.h"
#include <stdio.h>
#include <sstream>
#include <llvm-c/Target.h>
+#include <mutex>
using namespace aco;
+extern "C" {
+PFN_vkVoidFunction VKAPI_CALL vk_icdGetInstanceProcAddr(
+ VkInstance instance,
+ const char* pName);
+}
+
ac_shader_config config;
radv_shader_info info;
std::unique_ptr<Program> program;
Temp exec_input;
const char *subvariant = "";
+static VkInstance instance_cache[CHIP_LAST] = {VK_NULL_HANDLE};
+static VkDevice device_cache[CHIP_LAST] = {VK_NULL_HANDLE};
+static std::mutex create_device_mutex;
+
+#define FUNCTION_LIST\
+ ITEM(CreateInstance)\
+ ITEM(DestroyInstance)\
+ ITEM(EnumeratePhysicalDevices)\
+ ITEM(GetPhysicalDeviceProperties2)\
+ ITEM(CreateDevice)\
+ ITEM(DestroyDevice)\
+ ITEM(CreateShaderModule)\
+ ITEM(DestroyShaderModule)\
+ ITEM(CreateGraphicsPipelines)\
+ ITEM(CreateComputePipelines)\
+ ITEM(DestroyPipeline)\
+ ITEM(CreateDescriptorSetLayout)\
+ ITEM(DestroyDescriptorSetLayout)\
+ ITEM(CreatePipelineLayout)\
+ ITEM(DestroyPipelineLayout)\
+ ITEM(CreateRenderPass)\
+ ITEM(DestroyRenderPass)\
+ ITEM(GetPipelineExecutablePropertiesKHR)\
+ ITEM(GetPipelineExecutableInternalRepresentationsKHR)
+
+#define ITEM(n) PFN_vk##n n;
+FUNCTION_LIST
+#undef ITEM
+
void create_program(enum chip_class chip_class, Stage stage, unsigned wave_size, enum radeon_family family)
{
memset(&config, 0, sizeof(config));
bld.pseudo(aco_opcode::p_unit_test, Operand(i));
}
+VkDevice get_vk_device(enum chip_class chip_class)
+{
+ enum radeon_family family;
+ switch (chip_class) {
+ case GFX6:
+ family = CHIP_TAHITI;
+ break;
+ case GFX7:
+ family = CHIP_BONAIRE;
+ break;
+ case GFX8:
+ family = CHIP_POLARIS10;
+ break;
+ case GFX9:
+ family = CHIP_VEGA10;
+ break;
+ case GFX10:
+ family = CHIP_NAVI10;
+ break;
+ default:
+ family = CHIP_UNKNOWN;
+ break;
+ }
+ return get_vk_device(family);
+}
+
+VkDevice get_vk_device(enum radeon_family family)
+{
+ assert(family != CHIP_UNKNOWN);
+
+ std::lock_guard<std::mutex> guard(create_device_mutex);
+
+ if (device_cache[family])
+ return device_cache[family];
+
+ setenv("RADV_FORCE_FAMILY", ac_get_llvm_processor_name(family), 1);
+
+ VkApplicationInfo app_info = {};
+ app_info.pApplicationName = "aco_tests";
+ app_info.apiVersion = VK_API_VERSION_1_2;
+ VkInstanceCreateInfo instance_create_info = {};
+ instance_create_info.pApplicationInfo = &app_info;
+ instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ VkResult result = ((PFN_vkCreateInstance)vk_icdGetInstanceProcAddr(NULL, "vkCreateInstance"))(&instance_create_info, NULL, &instance_cache[family]);
+ assert(result == VK_SUCCESS);
+
+ #define ITEM(n) n = (PFN_vk##n)vk_icdGetInstanceProcAddr(instance_cache[family], "vk" #n);
+ FUNCTION_LIST
+ #undef ITEM
+
+ uint32_t device_count = 1;
+ VkPhysicalDevice device = VK_NULL_HANDLE;
+ result = EnumeratePhysicalDevices(instance_cache[family], &device_count, &device);
+ assert(result == VK_SUCCESS);
+ assert(device != VK_NULL_HANDLE);
+
+ VkDeviceCreateInfo device_create_info = {};
+ device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+ static const char *extensions[] = {"VK_KHR_pipeline_executable_properties"};
+ device_create_info.enabledExtensionCount = sizeof(extensions) / sizeof(extensions[0]);
+ device_create_info.ppEnabledExtensionNames = extensions;
+ result = CreateDevice(device, &device_create_info, NULL, &device_cache[family]);
+
+ return device_cache[family];
+}
+
+static struct DestroyDevices {
+ ~DestroyDevices() {
+ for (unsigned i = 0; i < CHIP_LAST; i++) {
+ if (!device_cache[i])
+ continue;
+ DestroyDevice(device_cache[i], NULL);
+ DestroyInstance(instance_cache[i], NULL);
+ }
+ }
+} destroy_devices;
+
+void print_pipeline_ir(VkDevice device, VkPipeline pipeline, VkShaderStageFlagBits stages,
+ const char *name, bool remove_encoding)
+{
+ uint32_t executable_count = 16;
+ VkPipelineExecutablePropertiesKHR executables[16];
+ VkPipelineInfoKHR pipeline_info;
+ pipeline_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR;
+ pipeline_info.pNext = NULL;
+ pipeline_info.pipeline = pipeline;
+ VkResult result = GetPipelineExecutablePropertiesKHR(device, &pipeline_info, &executable_count, executables);
+ assert(result == VK_SUCCESS);
+
+ uint32_t executable = 0;
+ for (; executable < executable_count; executable++) {
+ if (executables[executable].stages == stages)
+ break;
+ }
+ assert(executable != executable_count);
+
+ VkPipelineExecutableInfoKHR exec_info;
+ exec_info.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR;
+ exec_info.pNext = NULL;
+ exec_info.pipeline = pipeline;
+ exec_info.executableIndex = executable;
+
+ uint32_t ir_count = 16;
+ VkPipelineExecutableInternalRepresentationKHR ir[16];
+ memset(ir, 0, sizeof(ir));
+ result = GetPipelineExecutableInternalRepresentationsKHR(device, &exec_info, &ir_count, ir);
+ assert(result == VK_SUCCESS);
+
+ for (unsigned i = 0; i < ir_count; i++) {
+ if (strcmp(ir[i].name, name))
+ continue;
+
+ char *data = (char*)malloc(ir[i].dataSize);
+ ir[i].pData = data;
+ result = GetPipelineExecutableInternalRepresentationsKHR(device, &exec_info, &ir_count, ir);
+ assert(result == VK_SUCCESS);
+
+ if (remove_encoding) {
+ for (char *c = data; *c; c++) {
+ if (*c == ';') {
+ for (; *c && *c != '\n'; c++)
+ *c = ' ';
+ }
+ }
+ }
+
+ fprintf(output, "%s", data);
+ free(data);
+ return;
+ }
+}
+
+VkShaderModule __qoCreateShaderModule(VkDevice dev, const QoShaderModuleCreateInfo *info)
+{
+ VkShaderModuleCreateInfo module_info;
+ module_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ module_info.pNext = NULL;
+ module_info.flags = 0;
+ module_info.codeSize = info->spirvSize;
+ module_info.pCode = (const uint32_t*)info->pSpirv;
+
+ VkShaderModule module;
+ VkResult result = CreateShaderModule(dev, &module_info, NULL, &module);
+ assert(result == VK_SUCCESS);
+
+ return module;
+}
+
+PipelineBuilder::PipelineBuilder(VkDevice dev) {
+ memset(this, 0, sizeof(*this));
+ topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ device = dev;
+}
+
+PipelineBuilder::~PipelineBuilder()
+{
+ DestroyPipeline(device, pipeline, NULL);
+
+ for (unsigned i = 0; i < (is_compute() ? 1 : gfx_pipeline_info.stageCount); i++) {
+ VkPipelineShaderStageCreateInfo *stage_info = &stages[i];
+ if (owned_stages & stage_info->stage)
+ DestroyShaderModule(device, stage_info->module, NULL);
+ }
+
+ DestroyPipelineLayout(device, pipeline_layout, NULL);
+
+ for (unsigned i = 0; i < util_bitcount64(desc_layouts_used); i++)
+ DestroyDescriptorSetLayout(device, desc_layouts[i], NULL);
+
+ DestroyRenderPass(device, render_pass, NULL);
+}
+
+void PipelineBuilder::add_desc_binding(VkShaderStageFlags stage_flags, uint32_t layout,
+ uint32_t binding, VkDescriptorType type, uint32_t count)
+{
+ desc_layouts_used |= 1ull << layout;
+ desc_bindings[layout][num_desc_bindings[layout]++] = {binding, type, count, stage_flags, NULL};
+}
+
+void PipelineBuilder::add_vertex_binding(uint32_t binding, uint32_t stride, VkVertexInputRate rate)
+{
+ vs_bindings[vs_input.vertexBindingDescriptionCount++] = {binding, stride, rate};
+}
+
+void PipelineBuilder::add_vertex_attribute(uint32_t location, uint32_t binding, VkFormat format, uint32_t offset)
+{
+ vs_attributes[vs_input.vertexAttributeDescriptionCount++] = {location, binding, format, offset};
+}
+
+void PipelineBuilder::add_resource_decls(QoShaderModuleCreateInfo *module)
+{
+ for (unsigned i = 0; i < module->declarationCount; i++) {
+ const QoShaderDecl *decl = &module->pDeclarations[i];
+ switch (decl->decl_type) {
+ case QoShaderDeclType_ubo:
+ add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
+ break;
+ case QoShaderDeclType_ssbo:
+ add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
+ break;
+ case QoShaderDeclType_img_buf:
+ add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
+ break;
+ case QoShaderDeclType_img:
+ add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
+ break;
+ case QoShaderDeclType_tex_buf:
+ add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
+ break;
+ case QoShaderDeclType_combined:
+ add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
+ break;
+ case QoShaderDeclType_tex:
+ add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
+ break;
+ case QoShaderDeclType_samp:
+ add_desc_binding(module->stage, decl->set, decl->binding, VK_DESCRIPTOR_TYPE_SAMPLER);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void PipelineBuilder::add_io_decls(QoShaderModuleCreateInfo *module)
+{
+ unsigned next_vtx_offset = 0;
+ for (unsigned i = 0; i < module->declarationCount; i++) {
+ const QoShaderDecl *decl = &module->pDeclarations[i];
+ switch (decl->decl_type) {
+ case QoShaderDeclType_in:
+ if (module->stage == VK_SHADER_STAGE_VERTEX_BIT) {
+ if (!strcmp(decl->type, "float") || decl->type[0] == 'v')
+ add_vertex_attribute(decl->location, 0, VK_FORMAT_R32G32B32A32_SFLOAT, next_vtx_offset);
+ else if (decl->type[0] == 'u')
+ add_vertex_attribute(decl->location, 0, VK_FORMAT_R32G32B32A32_UINT, next_vtx_offset);
+ else if (decl->type[0] == 'i')
+ add_vertex_attribute(decl->location, 0, VK_FORMAT_R32G32B32A32_SINT, next_vtx_offset);
+ next_vtx_offset += 16;
+ }
+ break;
+ case QoShaderDeclType_out:
+ if (module->stage == VK_SHADER_STAGE_FRAGMENT_BIT) {
+ if (!strcmp(decl->type, "float") || decl->type[0] == 'v')
+ color_outputs[decl->location] = VK_FORMAT_R32G32B32A32_SFLOAT;
+ else if (decl->type[0] == 'u')
+ color_outputs[decl->location] = VK_FORMAT_R32G32B32A32_UINT;
+ else if (decl->type[0] == 'i')
+ color_outputs[decl->location] = VK_FORMAT_R32G32B32A32_SINT;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (next_vtx_offset)
+ add_vertex_binding(0, next_vtx_offset);
+}
+
+void PipelineBuilder::add_stage(VkShaderStageFlagBits stage, VkShaderModule module, const char *name)
+{
+ VkPipelineShaderStageCreateInfo *stage_info;
+ if (stage == VK_SHADER_STAGE_COMPUTE_BIT)
+ stage_info = &stages[0];
+ else
+ stage_info = &stages[gfx_pipeline_info.stageCount++];
+ stage_info->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stage_info->pNext = NULL;
+ stage_info->flags = 0;
+ stage_info->stage = stage;
+ stage_info->module = module;
+ stage_info->pName = name;
+ stage_info->pSpecializationInfo = NULL;
+ owned_stages |= stage;
+}
+
+void PipelineBuilder::add_vsfs(VkShaderModule vs, VkShaderModule fs)
+{
+ add_stage(VK_SHADER_STAGE_VERTEX_BIT, vs);
+ add_stage(VK_SHADER_STAGE_FRAGMENT_BIT, fs);
+}
+
+void PipelineBuilder::add_vsfs(QoShaderModuleCreateInfo vs, QoShaderModuleCreateInfo fs)
+{
+ add_vsfs(__qoCreateShaderModule(device, &vs), __qoCreateShaderModule(device, &fs));
+ add_resource_decls(&vs);
+ add_io_decls(&vs);
+ add_resource_decls(&fs);
+ add_io_decls(&fs);
+}
+
+void PipelineBuilder::add_cs(VkShaderModule cs)
+{
+ add_stage(VK_SHADER_STAGE_COMPUTE_BIT, cs);
+}
+
+void PipelineBuilder::add_cs(QoShaderModuleCreateInfo cs)
+{
+ add_cs(__qoCreateShaderModule(device, &cs));
+ add_resource_decls(&cs);
+}
+
+bool PipelineBuilder::is_compute() {
+ return gfx_pipeline_info.stageCount == 0;
+}
+
+void PipelineBuilder::create_compute_pipeline() {
+ VkComputePipelineCreateInfo create_info;
+ create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
+ create_info.pNext = NULL;
+ create_info.flags = VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR;
+ create_info.stage = stages[0];
+ create_info.layout = pipeline_layout;
+ create_info.basePipelineHandle = VK_NULL_HANDLE;
+ create_info.basePipelineIndex = 0;
+
+ VkResult result = CreateComputePipelines(device, VK_NULL_HANDLE, 1, &create_info, NULL, &pipeline);
+ assert(result == VK_SUCCESS);
+}
+
+void PipelineBuilder::create_graphics_pipeline() {
+ /* create the create infos */
+ if (!samples)
+ samples = VK_SAMPLE_COUNT_1_BIT;
+
+ unsigned num_color_attachments = 0;
+ VkPipelineColorBlendAttachmentState blend_attachment_states[16];
+ VkAttachmentReference color_attachments[16];
+ VkAttachmentDescription attachment_descs[17];
+ for (unsigned i = 0; i < 16; i++) {
+ if (color_outputs[i] == VK_FORMAT_UNDEFINED)
+ continue;
+
+ VkAttachmentDescription *desc = &attachment_descs[num_color_attachments];
+ desc->flags = 0;
+ desc->format = color_outputs[i];
+ desc->samples = samples;
+ desc->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+ desc->storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ desc->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+ desc->stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
+ desc->initialLayout = VK_IMAGE_LAYOUT_GENERAL;
+ desc->finalLayout = VK_IMAGE_LAYOUT_GENERAL;
+
+ VkAttachmentReference *ref = &color_attachments[num_color_attachments];
+ ref->attachment = num_color_attachments;
+ ref->layout = VK_IMAGE_LAYOUT_GENERAL;
+
+ VkPipelineColorBlendAttachmentState *blend = &blend_attachment_states[num_color_attachments];
+ blend->blendEnable = false;
+ blend->colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
+ VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT |
+ VK_COLOR_COMPONENT_A_BIT;
+
+ num_color_attachments++;
+ }
+
+ unsigned num_attachments = num_color_attachments;
+ VkAttachmentReference ds_attachment;
+ if (ds_output != VK_FORMAT_UNDEFINED) {
+ VkAttachmentDescription *desc = &attachment_descs[num_attachments];
+ desc->flags = 0;
+ desc->format = ds_output;
+ desc->samples = samples;
+ desc->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+ desc->storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ desc->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+ desc->stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
+ desc->initialLayout = VK_IMAGE_LAYOUT_GENERAL;
+ desc->finalLayout = VK_IMAGE_LAYOUT_GENERAL;
+
+ ds_attachment.attachment = num_color_attachments;
+ ds_attachment.layout = VK_IMAGE_LAYOUT_GENERAL;
+
+ num_attachments++;
+ }
+
+ vs_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vs_input.pNext = NULL;
+ vs_input.flags = 0;
+ vs_input.pVertexBindingDescriptions = vs_bindings;
+ vs_input.pVertexAttributeDescriptions = vs_attributes;
+
+ VkPipelineInputAssemblyStateCreateInfo assembly_state;
+ assembly_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ assembly_state.pNext = NULL;
+ assembly_state.flags = 0;
+ assembly_state.topology = topology;
+ assembly_state.primitiveRestartEnable = false;
+
+ VkPipelineTessellationStateCreateInfo tess_state;
+ tess_state.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
+ tess_state.pNext = NULL;
+ tess_state.flags = 0;
+ tess_state.patchControlPoints = patch_size;
+
+ VkPipelineViewportStateCreateInfo viewport_state;
+ viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewport_state.pNext = NULL;
+ viewport_state.flags = 0;
+ viewport_state.viewportCount = 1;
+ viewport_state.pViewports = NULL;
+ viewport_state.scissorCount = 1;
+ viewport_state.pScissors = NULL;
+
+ VkPipelineRasterizationStateCreateInfo rasterization_state;
+ rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rasterization_state.pNext = NULL;
+ rasterization_state.flags = 0;
+ rasterization_state.depthClampEnable = false;
+ rasterization_state.rasterizerDiscardEnable = false;
+ rasterization_state.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterization_state.cullMode = VK_CULL_MODE_NONE;
+ rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ rasterization_state.depthBiasEnable = false;
+ rasterization_state.lineWidth = 1.0;
+
+ VkPipelineMultisampleStateCreateInfo ms_state;
+ ms_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ ms_state.pNext = NULL;
+ ms_state.flags = 0;
+ ms_state.rasterizationSamples = samples;
+ ms_state.sampleShadingEnable = sample_shading_enable;
+ ms_state.minSampleShading = min_sample_shading;
+ VkSampleMask sample_mask = 0xffffffff;
+ ms_state.pSampleMask = &sample_mask;
+ ms_state.alphaToCoverageEnable = false;
+ ms_state.alphaToOneEnable = false;
+
+ VkPipelineDepthStencilStateCreateInfo ds_state;
+ ds_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ ds_state.pNext = NULL;
+ ds_state.flags = 0;
+ ds_state.depthTestEnable = ds_output != VK_FORMAT_UNDEFINED;
+ ds_state.depthWriteEnable = true;
+ ds_state.depthCompareOp = VK_COMPARE_OP_ALWAYS;
+ ds_state.depthBoundsTestEnable = false;
+ ds_state.stencilTestEnable = true;
+ ds_state.front.failOp = VK_STENCIL_OP_KEEP;
+ ds_state.front.passOp = VK_STENCIL_OP_REPLACE;
+ ds_state.front.depthFailOp = VK_STENCIL_OP_REPLACE;
+ ds_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
+ ds_state.front.compareMask = 0xffffffff,
+ ds_state.front.reference = 0;
+ ds_state.back = ds_state.front;
+
+ VkPipelineColorBlendStateCreateInfo color_blend_state;
+ color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ color_blend_state.pNext = NULL;
+ color_blend_state.flags = 0;
+ color_blend_state.logicOpEnable = false;
+ color_blend_state.attachmentCount = num_color_attachments;
+ color_blend_state.pAttachments = blend_attachment_states;
+
+ VkDynamicState dynamic_states[9] = {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR,
+ VK_DYNAMIC_STATE_LINE_WIDTH,
+ VK_DYNAMIC_STATE_DEPTH_BIAS,
+ VK_DYNAMIC_STATE_BLEND_CONSTANTS,
+ VK_DYNAMIC_STATE_DEPTH_BOUNDS,
+ VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
+ VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
+ VK_DYNAMIC_STATE_STENCIL_REFERENCE
+ };
+
+ VkPipelineDynamicStateCreateInfo dynamic_state;
+ dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamic_state.pNext = NULL;
+ dynamic_state.flags = 0;
+ dynamic_state.dynamicStateCount = sizeof(dynamic_states) / sizeof(VkDynamicState);
+ dynamic_state.pDynamicStates = dynamic_states;
+
+ gfx_pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ gfx_pipeline_info.pNext = NULL;
+ gfx_pipeline_info.flags = VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR;
+ gfx_pipeline_info.pVertexInputState = &vs_input;
+ gfx_pipeline_info.pInputAssemblyState = &assembly_state;
+ gfx_pipeline_info.pTessellationState = &tess_state;
+ gfx_pipeline_info.pViewportState = &viewport_state;
+ gfx_pipeline_info.pRasterizationState = &rasterization_state;
+ gfx_pipeline_info.pMultisampleState = &ms_state;
+ gfx_pipeline_info.pDepthStencilState = &ds_state;
+ gfx_pipeline_info.pColorBlendState = &color_blend_state;
+ gfx_pipeline_info.pDynamicState = &dynamic_state;
+ gfx_pipeline_info.subpass = 0;
+
+ /* create the objects used to create the pipeline */
+ VkSubpassDescription subpass;
+ subpass.flags = 0;
+ subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpass.inputAttachmentCount = 0;
+ subpass.pInputAttachments = NULL;
+ subpass.colorAttachmentCount = num_color_attachments;
+ subpass.pColorAttachments = color_attachments;
+ subpass.pResolveAttachments = NULL;
+ subpass.pDepthStencilAttachment = ds_output == VK_FORMAT_UNDEFINED ? NULL : &ds_attachment;
+ subpass.preserveAttachmentCount = 0;
+ subpass.pPreserveAttachments = NULL;
+
+ VkRenderPassCreateInfo renderpass_info;
+ renderpass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ renderpass_info.pNext = NULL;
+ renderpass_info.flags = 0;
+ renderpass_info.attachmentCount = num_attachments;
+ renderpass_info.pAttachments = attachment_descs;
+ renderpass_info.subpassCount = 1;
+ renderpass_info.pSubpasses = &subpass;
+ renderpass_info.dependencyCount = 0;
+ renderpass_info.pDependencies = NULL;
+
+ VkResult result = CreateRenderPass(device, &renderpass_info, NULL, &render_pass);
+ assert(result == VK_SUCCESS);
+
+ gfx_pipeline_info.layout = pipeline_layout;
+ gfx_pipeline_info.renderPass = render_pass;
+
+ /* create the pipeline */
+ gfx_pipeline_info.pStages = stages;
+
+ result = CreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gfx_pipeline_info, NULL, &pipeline);
+ assert(result == VK_SUCCESS);
+}
+
+void PipelineBuilder::create_pipeline() {
+ unsigned num_desc_layouts = 0;
+ for (unsigned i = 0; i < 64; i++) {
+ if (!(desc_layouts_used & (1ull << i)))
+ continue;
+
+ VkDescriptorSetLayoutCreateInfo desc_layout_info;
+ desc_layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ desc_layout_info.pNext = NULL;
+ desc_layout_info.flags = 0;
+ desc_layout_info.bindingCount = num_desc_bindings[i];
+ desc_layout_info.pBindings = desc_bindings[i];
+
+ VkResult result = CreateDescriptorSetLayout(device, &desc_layout_info, NULL, &desc_layouts[num_desc_layouts]);
+ assert(result == VK_SUCCESS);
+ num_desc_layouts++;
+ }
+
+ VkPipelineLayoutCreateInfo pipeline_layout_info;
+ pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipeline_layout_info.pNext = NULL;
+ pipeline_layout_info.flags = 0;
+ pipeline_layout_info.pushConstantRangeCount = 1;
+ pipeline_layout_info.pPushConstantRanges = &push_constant_range;
+ pipeline_layout_info.setLayoutCount = num_desc_layouts;
+ pipeline_layout_info.pSetLayouts = desc_layouts;
+
+ VkResult result = CreatePipelineLayout(device, &pipeline_layout_info, NULL, &pipeline_layout);
+ assert(result == VK_SUCCESS);
+
+ if (is_compute())
+ create_compute_pipeline();
+ else
+ create_graphics_pipeline();
+}
+
+void PipelineBuilder::print_ir(VkShaderStageFlagBits stages, const char *name, bool remove_encoding)
+{
+ if (!pipeline)
+ create_pipeline();
+ print_pipeline_ir(device, pipeline, stages, name, remove_encoding);
+}