776 lines
27 KiB
C++
776 lines
27 KiB
C++
/*
|
|
* 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 "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;
|
|
Builder bld(NULL);
|
|
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));
|
|
info.wave_size = wave_size;
|
|
|
|
program.reset(new Program);
|
|
aco::init_program(program.get(), stage, &info, chip_class, family, &config);
|
|
|
|
Block *block = program->create_and_insert_block();
|
|
block->kind = block_kind_top_level;
|
|
|
|
bld = Builder(program.get(), &program->blocks[0]);
|
|
|
|
config.float_mode = program->blocks[0].fp_mode.val;
|
|
}
|
|
|
|
bool setup_cs(const char *input_spec, enum chip_class chip_class,
|
|
enum radeon_family family, unsigned wave_size)
|
|
{
|
|
const char *old_subvariant = subvariant;
|
|
subvariant = "";
|
|
if (!set_variant(chip_class, old_subvariant))
|
|
return false;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.cs.block_size[0] = 1;
|
|
info.cs.block_size[1] = 1;
|
|
info.cs.block_size[2] = 1;
|
|
|
|
create_program(chip_class, compute_cs, wave_size, family);
|
|
|
|
if (input_spec) {
|
|
unsigned num_inputs = DIV_ROUND_UP(strlen(input_spec), 3u);
|
|
aco_ptr<Instruction> startpgm{create_instruction<Pseudo_instruction>(aco_opcode::p_startpgm, Format::PSEUDO, 0, num_inputs + 1)};
|
|
for (unsigned i = 0; i < num_inputs; i++) {
|
|
RegClass cls(input_spec[i * 3] == 'v' ? RegType::vgpr : RegType::sgpr, input_spec[i * 3 + 1] - '0');
|
|
inputs[i] = bld.tmp(cls);
|
|
startpgm->definitions[i] = Definition(inputs[i]);
|
|
}
|
|
exec_input = bld.tmp(program->lane_mask);
|
|
startpgm->definitions[num_inputs] = bld.exec(Definition(exec_input));
|
|
bld.insert(std::move(startpgm));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void finish_program(Program *program)
|
|
{
|
|
for (Block& BB : program->blocks) {
|
|
for (unsigned idx : BB.linear_preds)
|
|
program->blocks[idx].linear_succs.emplace_back(BB.index);
|
|
for (unsigned idx : BB.logical_preds)
|
|
program->blocks[idx].logical_succs.emplace_back(BB.index);
|
|
}
|
|
|
|
for (Block& block : program->blocks) {
|
|
if (block.linear_succs.size() == 0) {
|
|
block.kind |= block_kind_uniform;
|
|
Builder bld(program, &block);
|
|
if (program->wb_smem_l1_on_end)
|
|
bld.smem(aco_opcode::s_dcache_wb, false);
|
|
bld.sopp(aco_opcode::s_endpgm);
|
|
}
|
|
}
|
|
}
|
|
|
|
void finish_validator_test()
|
|
{
|
|
finish_program(program.get());
|
|
aco_print_program(program.get(), output);
|
|
fprintf(output, "Validation results:\n");
|
|
if (aco::validate(program.get(), output))
|
|
fprintf(output, "Validation passed\n");
|
|
else
|
|
fprintf(output, "Validation failed\n");
|
|
}
|
|
|
|
void finish_opt_test()
|
|
{
|
|
finish_program(program.get());
|
|
if (!aco::validate(program.get(), output)) {
|
|
fail_test("Validation before optimization failed");
|
|
return;
|
|
}
|
|
aco::optimize(program.get());
|
|
if (!aco::validate(program.get(), output)) {
|
|
fail_test("Validation after optimization failed");
|
|
return;
|
|
}
|
|
aco_print_program(program.get(), output);
|
|
}
|
|
|
|
void finish_to_hw_instr_test()
|
|
{
|
|
finish_program(program.get());
|
|
aco::lower_to_hw_instr(program.get());
|
|
aco_print_program(program.get(), output);
|
|
}
|
|
|
|
void finish_assembler_test()
|
|
{
|
|
finish_program(program.get());
|
|
std::vector<uint32_t> binary;
|
|
unsigned exec_size = emit_program(program.get(), binary);
|
|
|
|
/* we could use CLRX for disassembly but that would require it to be
|
|
* installed */
|
|
if (program->chip_class == GFX10_3 && LLVM_VERSION_MAJOR < 9) {
|
|
skip_test("LLVM 11 needed for GFX10_3 disassembly");
|
|
} else if (program->chip_class == GFX10 && LLVM_VERSION_MAJOR < 9) {
|
|
skip_test("LLVM 9 needed for GFX10 disassembly");
|
|
} else if (program->chip_class >= GFX8) {
|
|
std::ostringstream ss;
|
|
print_asm(program.get(), binary, exec_size / 4u, ss);
|
|
|
|
fputs(ss.str().c_str(), output);
|
|
} else {
|
|
//TODO: maybe we should use CLRX and skip this test if it's not available?
|
|
for (uint32_t dword : binary)
|
|
fprintf(output, "%.8x\n", dword);
|
|
}
|
|
}
|
|
|
|
void writeout(unsigned i, Temp tmp)
|
|
{
|
|
if (tmp.id())
|
|
bld.pseudo(aco_opcode::p_unit_test, Operand(i), tmp);
|
|
else
|
|
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);
|
|
}
|