aco: add framework for testing isel and integration tests

And add some simple tests to demonstrate/test the pipeline builder and
glsl_scraper.py.

Signed-off-by: Rhys Perry <pendingchaos02@gmail.com>
Acked-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
Acked-by: Daniel Schürmann <daniel@schuermann.dev>
Acked-by: Timur Kristóf <timur.kristof@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/3521>
This commit is contained in:
Rhys Perry 2020-02-14 11:32:18 +00:00 committed by Marge Bot
parent bb7d7755f5
commit d488d0fd7b
8 changed files with 1148 additions and 6 deletions

View File

@ -543,6 +543,10 @@ if with_gallium_zink
dep_vulkan = dependency('vulkan')
endif
if with_vulkan_overlay_layer or with_aco_tests
prog_glslang = find_program('glslangValidator')
endif
_xvmc = get_option('gallium-xvmc')
if _xvmc == 'true'
_xvmc = 'enabled'

View File

@ -292,7 +292,7 @@ option(
'build-aco-tests',
type : 'boolean',
value : false,
description : 'Build ACO tests. These do not require an AMD GPU.'
description : 'Build ACO tests. These require RADV and glslang but not an AMD GPU.'
)
option(
'install-intel-gpu-tests',

View File

@ -0,0 +1,339 @@
#! /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)

View File

@ -22,12 +22,21 @@
*
*/
#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;
@ -36,6 +45,35 @@ Temp inputs[16];
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));
@ -168,3 +206,570 @@ void writeout(unsigned i, Temp tmp)
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);
}

View File

@ -25,6 +25,40 @@
#define ACO_TEST_HELPERS_H
#include "framework.h"
#include "vulkan/vulkan.h"
enum QoShaderDeclType {
QoShaderDeclType_ubo,
QoShaderDeclType_ssbo,
QoShaderDeclType_img_buf,
QoShaderDeclType_img,
QoShaderDeclType_tex_buf,
QoShaderDeclType_combined,
QoShaderDeclType_tex,
QoShaderDeclType_samp,
QoShaderDeclType_in,
QoShaderDeclType_out,
};
struct QoShaderDecl {
const char *name;
const char *type;
QoShaderDeclType decl_type;
//TODO: array size?
unsigned location;
unsigned component;
unsigned binding;
unsigned set;
};
struct QoShaderModuleCreateInfo {
void *pNext;
size_t spirvSize;
const void *pSpirv;
uint32_t declarationCount;
const QoShaderDecl *pDeclarations;
VkShaderStageFlagBits stage;
};
extern ac_shader_config config;
extern radv_shader_info info;
@ -47,4 +81,73 @@ void finish_assembler_test();
void writeout(unsigned i, aco::Temp tmp=aco::Temp(0, aco::s1));
/* vulkan helpers */
VkDevice get_vk_device(enum chip_class chip_class);
VkDevice get_vk_device(enum radeon_family family);
void print_pipeline_ir(VkDevice device, VkPipeline pipeline, VkShaderStageFlagBits stages,
const char *name, bool remove_encoding=false);
VkShaderModule __qoCreateShaderModule(VkDevice dev, const QoShaderModuleCreateInfo *info);
class PipelineBuilder {
public:
/* inputs */
VkDevice device;
VkFormat color_outputs[16];
VkFormat ds_output;
VkPrimitiveTopology topology;
VkSampleCountFlagBits samples;
bool sample_shading_enable;
float min_sample_shading;
uint32_t patch_size;
VkPipelineVertexInputStateCreateInfo vs_input;
VkVertexInputBindingDescription vs_bindings[16];
VkVertexInputAttributeDescription vs_attributes[16];
VkPushConstantRange push_constant_range;
uint64_t desc_layouts_used;
unsigned num_desc_bindings[64];
VkDescriptorSetLayoutBinding desc_bindings[64][64];
VkPipelineShaderStageCreateInfo stages[5];
VkShaderStageFlags owned_stages;
/* outputs */
VkGraphicsPipelineCreateInfo gfx_pipeline_info;
VkComputePipelineCreateInfo cs_pipeline_info;
VkDescriptorSetLayout desc_layouts[64];
VkPipelineLayout pipeline_layout;
VkRenderPass render_pass;
VkPipeline pipeline;
PipelineBuilder(VkDevice dev);
~PipelineBuilder();
PipelineBuilder(const PipelineBuilder&) = delete;
PipelineBuilder& operator = (const PipelineBuilder&) = delete;
void add_desc_binding(VkShaderStageFlags stage_flags, uint32_t layout,
uint32_t binding, VkDescriptorType type, uint32_t count=1);
void add_vertex_binding(uint32_t binding, uint32_t stride, VkVertexInputRate rate=VK_VERTEX_INPUT_RATE_VERTEX);
void add_vertex_attribute(uint32_t location, uint32_t binding, VkFormat format, uint32_t offset);
void add_resource_decls(QoShaderModuleCreateInfo *module);
void add_io_decls(QoShaderModuleCreateInfo *module);
void add_stage(VkShaderStageFlagBits stage, VkShaderModule module, const char *name="main");
void add_vsfs(VkShaderModule vs, VkShaderModule fs);
void add_vsfs(QoShaderModuleCreateInfo vs, QoShaderModuleCreateInfo fs);
void add_cs(VkShaderModule cs);
void add_cs(QoShaderModuleCreateInfo cs);
bool is_compute();
void create_pipeline();
void print_ir(VkShaderStageFlagBits stages, const char *name, bool remove_encoding=false);
private:
void create_compute_pipeline();
void create_graphics_pipeline();
};
#endif /* ACO_TEST_HELPERS_H */

View File

@ -23,15 +23,26 @@ aco_tests_files = files(
'helpers.h',
'main.cpp',
'test_assembler.cpp',
'test_isel.cpp',
'test_optimizer.cpp',
'test_tests.cpp',
)
spirv_files = files(
'test_isel.cpp',
)
gen_spirv = generator(prog_python,
output : '@BASENAME@-spirv.h',
arguments : [join_paths(meson.current_source_dir(), 'glsl_scraper.py'),
'@INPUT@', '--with-glslang', prog_glslang.path(), '-o', '@OUTPUT@'])
gen_spirv_files = gen_spirv.process(spirv_files)
test(
'aco_tests',
executable(
'aco_tests',
aco_tests_files,
[aco_tests_files, gen_spirv_files],
cpp_args : ['-DACO_TEST_SOURCE_DIR="@0@"'.format(meson.current_source_dir()),
'-DACO_TEST_BUILD_ROOT="@0@"'.format(meson.build_root()),
'-DACO_TEST_PYTHON_BIN="@0@"'.format(prog_python.path())],
@ -39,7 +50,7 @@ test(
inc_include, inc_src, inc_gallium, inc_compiler, inc_mesa, inc_mapi, inc_amd, inc_amd_common, inc_amd_common_llvm,
],
link_with : [
libamd_common, libamd_common_llvm
libamd_common, libamd_common_llvm, libvulkan_radeon,
],
dependencies : [
dep_llvm, dep_thread, idep_aco, idep_nir, idep_mesautil

View File

@ -0,0 +1,82 @@
/*
* Copyright © 2020 Valve Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "helpers.h"
#include "test_isel-spirv.h"
using namespace aco;
BEGIN_TEST(isel.interp.simple)
QoShaderModuleCreateInfo vs = qoShaderModuleCreateInfoGLSL(VERTEX,
layout(location = 0) in vec4 in_color;
layout(location = 0) out vec4 out_color;
void main() {
out_color = in_color;
}
);
QoShaderModuleCreateInfo fs = qoShaderModuleCreateInfoGLSL(FRAGMENT,
layout(location = 0) in vec4 in_color;
layout(location = 0) out vec4 out_color;
void main() {
//>> v1: %a_tmp = v_interp_p1_f32 %bx, %pm:m0 attr0.w
//! v1: %a = v_interp_p2_f32 %by, %pm:m0, %a_tmp attr0.w
//! v1: %b_tmp = v_interp_p1_f32 %bx, %pm:m0 attr0.z
//! v1: %b = v_interp_p2_f32 %by, %pm:m0, %b_tmp attr0.z
//! v1: %g_tmp = v_interp_p1_f32 %bx, %pm:m0 attr0.y
//! v1: %g = v_interp_p2_f32 %by, %pm:m0, %g_tmp attr0.y
//! v1: %r_tmp = v_interp_p1_f32 %bx, %pm:m0 attr0.x
//! v1: %r = v_interp_p2_f32 %by, %pm:m0, %r_tmp attr0.x
//! exp %r, %g, %b, %a mrt0
out_color = in_color;
}
);
PipelineBuilder bld(get_vk_device(GFX9));
bld.add_vsfs(vs, fs);
bld.print_ir(VK_SHADER_STAGE_FRAGMENT_BIT, "ACO IR");
END_TEST
BEGIN_TEST(isel.compute.simple)
for (unsigned i = GFX7; i <= GFX8; i++) {
if (!set_variant((chip_class)i))
continue;
QoShaderModuleCreateInfo cs = qoShaderModuleCreateInfoGLSL(COMPUTE,
layout(local_size_x=1) in;
layout(binding=0) buffer Buf {
uint res;
};
void main() {
//~gfx7>> v1: %data = v_mov_b32 42
//~gfx7>> buffer_store_dword %_, v1: undef, 0, %data disable_wqm storage:buffer semantics: scope:invocation
//~gfx8>> s1: %data = s_mov_b32 42
//~gfx8>> s_buffer_store_dword %_, 0, %data storage:buffer semantics: scope:invocation
res = 42;
}
);
PipelineBuilder bld(get_vk_device((chip_class)i));
bld.add_cs(cs);
bld.print_ir(VK_SHADER_STAGE_COMPUTE_BIT, "ACO IR", true);
}
END_TEST

View File

@ -18,8 +18,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
glslang = find_program('glslangValidator')
overlay_shaders = [
'overlay.frag',
'overlay.vert',
@ -28,7 +26,7 @@ overlay_spv = []
foreach s : ['overlay.frag', 'overlay.vert']
overlay_spv += custom_target(
s + '.spv.h', input : s, output : s + '.spv.h',
command : [glslang, '-V', '-x', '-o', '@OUTPUT@', '@INPUT@'])
command : [prog_glslang, '-V', '-x', '-o', '@OUTPUT@', '@INPUT@'])
endforeach
vklayer_files = files(