Files
2025-01-29 10:55:49 +01:00

949 lines
31 KiB
C

#include "vulkan.h"
#include <kinc/graphics5/pipeline.h>
#include <kinc/graphics5/shader.h>
#include <vulkan/vulkan_core.h>
#include <assert.h>
VkDescriptorSetLayout desc_layout;
extern kinc_g5_texture_t *vulkanTextures[16];
extern kinc_g5_render_target_t *vulkanRenderTargets[16];
extern uint32_t swapchainImageCount;
extern uint32_t current_buffer;
bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex);
static VkDescriptorPool descriptor_pool;
static bool has_number(kinc_internal_named_number *named_numbers, const char *name) {
for (int i = 0; i < KINC_INTERNAL_NAMED_NUMBER_COUNT; ++i) {
if (strcmp(named_numbers[i].name, name) == 0) {
return true;
}
}
return false;
}
static uint32_t find_number(kinc_internal_named_number *named_numbers, const char *name) {
for (int i = 0; i < KINC_INTERNAL_NAMED_NUMBER_COUNT; ++i) {
if (strcmp(named_numbers[i].name, name) == 0) {
return named_numbers[i].number;
}
}
return -1;
}
static void set_number(kinc_internal_named_number *named_numbers, const char *name, uint32_t number) {
for (int i = 0; i < KINC_INTERNAL_NAMED_NUMBER_COUNT; ++i) {
if (strcmp(named_numbers[i].name, name) == 0) {
named_numbers[i].number = number;
return;
}
}
for (int i = 0; i < KINC_INTERNAL_NAMED_NUMBER_COUNT; ++i) {
if (named_numbers[i].name[0] == 0) {
strcpy(named_numbers[i].name, name);
named_numbers[i].number = number;
return;
}
}
assert(false);
}
struct indexed_name {
uint32_t id;
char *name;
};
struct indexed_index {
uint32_t id;
uint32_t value;
};
#define MAX_THINGS 256
static struct indexed_name names[MAX_THINGS];
static uint32_t names_size = 0;
static struct indexed_name memberNames[MAX_THINGS];
static uint32_t memberNames_size = 0;
static struct indexed_index locs[MAX_THINGS];
static uint32_t locs_size = 0;
static struct indexed_index bindings[MAX_THINGS];
static uint32_t bindings_size = 0;
static struct indexed_index offsets[MAX_THINGS];
static uint32_t offsets_size = 0;
static void add_name(uint32_t id, char *name) {
names[names_size].id = id;
names[names_size].name = name;
++names_size;
}
static char *find_name(uint32_t id) {
for (uint32_t i = 0; i < names_size; ++i) {
if (names[i].id == id) {
return names[i].name;
}
}
return NULL;
}
static void add_member_name(uint32_t id, char *name) {
memberNames[memberNames_size].id = id;
memberNames[memberNames_size].name = name;
++memberNames_size;
}
static char *find_member_name(uint32_t id) {
for (uint32_t i = 0; i < memberNames_size; ++i) {
if (memberNames[i].id == id) {
return memberNames[i].name;
}
}
return NULL;
}
static void add_location(uint32_t id, uint32_t location) {
locs[locs_size].id = id;
locs[locs_size].value = location;
++locs_size;
}
static void add_binding(uint32_t id, uint32_t binding) {
bindings[bindings_size].id = id;
bindings[bindings_size].value = binding;
++bindings_size;
}
static void add_offset(uint32_t id, uint32_t offset) {
offsets[offsets_size].id = id;
offsets[offsets_size].value = offset;
++offsets_size;
}
static void parseShader(kinc_g5_shader_t *shader, kinc_internal_named_number *locations, kinc_internal_named_number *textureBindings,
kinc_internal_named_number *uniformOffsets) {
names_size = 0;
memberNames_size = 0;
locs_size = 0;
bindings_size = 0;
offsets_size = 0;
uint32_t *spirv = (uint32_t *)shader->impl.source;
int spirvsize = shader->impl.length / 4;
int index = 0;
uint32_t magicNumber = spirv[index++];
uint32_t version = spirv[index++];
uint32_t generator = spirv[index++];
uint32_t bound = spirv[index++];
index++;
while (index < spirvsize) {
int wordCount = spirv[index] >> 16;
uint32_t opcode = spirv[index] & 0xffff;
uint32_t *operands = wordCount > 1 ? &spirv[index + 1] : NULL;
uint32_t length = wordCount - 1;
switch (opcode) {
case 5: { // OpName
uint32_t id = operands[0];
char *string = (char *)&operands[1];
add_name(id, string);
break;
}
case 6: { // OpMemberName
uint32_t type = operands[0];
char *name = find_name(type);
if (name != NULL && strcmp(name, "_k_global_uniform_buffer_type") == 0) {
uint32_t member = operands[1];
char *string = (char *)&operands[2];
add_member_name(member, string);
}
break;
}
case 71: { // OpDecorate
uint32_t id = operands[0];
uint32_t decoration = operands[1];
if (decoration == 30) { // location
uint32_t location = operands[2];
add_location(id, location);
}
if (decoration == 33) { // binding
uint32_t binding = operands[2];
add_binding(id, binding);
}
break;
}
case 72: { // OpMemberDecorate
uint32_t type = operands[0];
char *name = find_name(type);
if (name != NULL && strcmp(name, "_k_global_uniform_buffer_type") == 0) {
uint32_t member = operands[1];
uint32_t decoration = operands[2];
if (decoration == 35) { // offset
uint32_t offset = operands[3];
add_offset(member, offset);
}
}
break;
}
}
index += wordCount;
}
for (uint32_t i = 0; i < locs_size; ++i) {
char *name = find_name(locs[i].id);
if (name != NULL) {
set_number(locations, name, locs[i].value);
}
}
for (uint32_t i = 0; i < bindings_size; ++i) {
char *name = find_name(bindings[i].id);
if (name != NULL) {
set_number(textureBindings, name, bindings[i].value);
}
}
for (uint32_t i = 0; i < offsets_size; ++i) {
char *name = find_member_name(offsets[i].id);
if (name != NULL) {
set_number(uniformOffsets, name, offsets[i].value);
}
}
}
static VkShaderModule prepare_shader_module(const void *code, size_t size) {
VkShaderModuleCreateInfo moduleCreateInfo;
VkShaderModule module;
VkResult err;
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
moduleCreateInfo.pNext = NULL;
moduleCreateInfo.codeSize = size;
moduleCreateInfo.pCode = (const uint32_t *)code;
moduleCreateInfo.flags = 0;
err = vkCreateShaderModule(vk_ctx.device, &moduleCreateInfo, NULL, &module);
assert(!err);
return module;
}
static VkShaderModule prepare_vs(VkShaderModule *vert_shader_module, kinc_g5_shader_t *vertexShader) {
*vert_shader_module = prepare_shader_module(vertexShader->impl.source, vertexShader->impl.length);
return *vert_shader_module;
}
static VkShaderModule prepare_fs(VkShaderModule *frag_shader_module, kinc_g5_shader_t *fragmentShader) {
*frag_shader_module = prepare_shader_module(fragmentShader->impl.source, fragmentShader->impl.length);
return *frag_shader_module;
}
static VkFormat convert_format(kinc_g5_render_target_format_t format) {
switch (format) {
case KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT:
return VK_FORMAT_R32G32B32A32_SFLOAT;
case KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT:
return VK_FORMAT_R16G16B16A16_SFLOAT;
case KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT:
return VK_FORMAT_R32_SFLOAT;
case KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT:
return VK_FORMAT_R16_SFLOAT;
case KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED:
return VK_FORMAT_R8_UNORM;
case KINC_G5_RENDER_TARGET_FORMAT_32BIT:
default:
return VK_FORMAT_B8G8R8A8_UNORM;
}
}
void kinc_g5_pipeline_init(kinc_g5_pipeline_t *pipeline) {
kinc_g5_internal_pipeline_init(pipeline);
}
void kinc_g5_pipeline_destroy(kinc_g5_pipeline_t *pipeline) {
vkDestroyPipeline(vk_ctx.device, pipeline->impl.framebuffer_pipeline, NULL);
vkDestroyPipeline(vk_ctx.device, pipeline->impl.rendertarget_pipeline, NULL);
vkDestroyPipelineLayout(vk_ctx.device, pipeline->impl.pipeline_layout, NULL);
}
kinc_g5_constant_location_t kinc_g5_pipeline_get_constant_location(kinc_g5_pipeline_t *pipeline, const char *name) {
kinc_g5_constant_location_t location;
location.impl.vertexOffset = -1;
location.impl.fragmentOffset = -1;
if (has_number(pipeline->impl.vertexOffsets, name)) {
location.impl.vertexOffset = find_number(pipeline->impl.vertexOffsets, name);
}
if (has_number(pipeline->impl.fragmentOffsets, name)) {
location.impl.fragmentOffset = find_number(pipeline->impl.fragmentOffsets, name);
}
return location;
}
kinc_g5_texture_unit_t kinc_g5_pipeline_get_texture_unit(kinc_g5_pipeline_t *pipeline, const char *name) {
kinc_g5_texture_unit_t unit;
int number = find_number(pipeline->impl.textureBindings, name);
assert(number == -1 || number >= 2); // something wrong with the SPIR-V when this triggers
for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) {
unit.stages[i] = -1;
}
if (number >= 0) {
unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] = number - 2;
}
return unit;
}
static VkCullModeFlagBits convert_cull_mode(kinc_g5_cull_mode_t cullMode) {
switch (cullMode) {
case KINC_G5_CULL_MODE_CLOCKWISE:
return VK_CULL_MODE_BACK_BIT;
case KINC_G5_CULL_MODE_COUNTERCLOCKWISE:
return VK_CULL_MODE_FRONT_BIT;
case KINC_G5_CULL_MODE_NEVER:
default:
return VK_CULL_MODE_NONE;
}
}
static VkCompareOp convert_compare_mode(kinc_g5_compare_mode_t compare) {
switch (compare) {
default:
case KINC_G5_COMPARE_MODE_ALWAYS:
return VK_COMPARE_OP_ALWAYS;
case KINC_G5_COMPARE_MODE_NEVER:
return VK_COMPARE_OP_NEVER;
case KINC_G5_COMPARE_MODE_EQUAL:
return VK_COMPARE_OP_EQUAL;
case KINC_G5_COMPARE_MODE_NOT_EQUAL:
return VK_COMPARE_OP_NOT_EQUAL;
case KINC_G5_COMPARE_MODE_LESS:
return VK_COMPARE_OP_LESS;
case KINC_G5_COMPARE_MODE_LESS_EQUAL:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case KINC_G5_COMPARE_MODE_GREATER:
return VK_COMPARE_OP_GREATER;
case KINC_G5_COMPARE_MODE_GREATER_EQUAL:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
}
}
static VkBlendFactor convert_blend_factor(kinc_g5_blending_factor_t factor) {
switch (factor) {
case KINC_G5_BLEND_ONE:
return VK_BLEND_FACTOR_ONE;
case KINC_G5_BLEND_ZERO:
return VK_BLEND_FACTOR_ZERO;
case KINC_G5_BLEND_SOURCE_ALPHA:
return VK_BLEND_FACTOR_SRC_ALPHA;
case KINC_G5_BLEND_DEST_ALPHA:
return VK_BLEND_FACTOR_DST_ALPHA;
case KINC_G5_BLEND_INV_SOURCE_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case KINC_G5_BLEND_INV_DEST_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case KINC_G5_BLEND_SOURCE_COLOR:
return VK_BLEND_FACTOR_SRC_COLOR;
case KINC_G5_BLEND_DEST_COLOR:
return VK_BLEND_FACTOR_DST_COLOR;
case KINC_G5_BLEND_INV_SOURCE_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case KINC_G5_BLEND_INV_DEST_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
case KINC_G5_BLEND_CONSTANT:
return VK_BLEND_FACTOR_CONSTANT_COLOR;
case KINC_G5_BLEND_INV_CONSTANT:
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
default:
assert(false);
return VK_BLEND_FACTOR_ONE;
}
}
static VkBlendOp convert_blend_operation(kinc_g5_blending_operation_t op) {
switch (op) {
case KINC_G5_BLENDOP_ADD:
return VK_BLEND_OP_ADD;
case KINC_G5_BLENDOP_SUBTRACT:
return VK_BLEND_OP_SUBTRACT;
case KINC_G5_BLENDOP_REVERSE_SUBTRACT:
return VK_BLEND_OP_REVERSE_SUBTRACT;
case KINC_G5_BLENDOP_MIN:
return VK_BLEND_OP_MIN;
case KINC_G5_BLENDOP_MAX:
return VK_BLEND_OP_MAX;
default:
assert(false);
return VK_BLEND_OP_ADD;
}
}
void kinc_g5_pipeline_compile(kinc_g5_pipeline_t *pipeline) {
memset(pipeline->impl.vertexLocations, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT);
memset(pipeline->impl.vertexOffsets, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT);
memset(pipeline->impl.fragmentLocations, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT);
memset(pipeline->impl.fragmentOffsets, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT);
memset(pipeline->impl.textureBindings, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT);
parseShader(pipeline->vertexShader, pipeline->impl.vertexLocations, pipeline->impl.textureBindings, pipeline->impl.vertexOffsets);
parseShader(pipeline->fragmentShader, pipeline->impl.fragmentLocations, pipeline->impl.textureBindings, pipeline->impl.fragmentOffsets);
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {0};
pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pPipelineLayoutCreateInfo.pNext = NULL;
pPipelineLayoutCreateInfo.setLayoutCount = 1;
pPipelineLayoutCreateInfo.pSetLayouts = &desc_layout;
VkResult err = vkCreatePipelineLayout(vk_ctx.device, &pPipelineLayoutCreateInfo, NULL, &pipeline->impl.pipeline_layout);
assert(!err);
VkGraphicsPipelineCreateInfo pipeline_info = {0};
VkPipelineInputAssemblyStateCreateInfo ia = {0};
VkPipelineRasterizationStateCreateInfo rs = {0};
VkPipelineColorBlendStateCreateInfo cb = {0};
VkPipelineDepthStencilStateCreateInfo ds = {0};
VkPipelineViewportStateCreateInfo vp = {0};
VkPipelineMultisampleStateCreateInfo ms = {0};
#define dynamicStatesCount 2
VkDynamicState dynamicStateEnables[dynamicStatesCount];
VkPipelineDynamicStateCreateInfo dynamicState = {0};
memset(dynamicStateEnables, 0, sizeof(dynamicStateEnables));
memset(&dynamicState, 0, sizeof(dynamicState));
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.pDynamicStates = dynamicStateEnables;
memset(&pipeline_info, 0, sizeof(pipeline_info));
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.layout = pipeline->impl.pipeline_layout;
int vertexAttributeCount = 0;
int vertexBindingCount = 0;
for (int i = 0; i < 16; ++i) {
if (pipeline->inputLayout[i] == NULL) {
break;
}
vertexAttributeCount += pipeline->inputLayout[i]->size;
vertexBindingCount++;
}
#ifdef KORE_WINDOWS
VkVertexInputBindingDescription *vi_bindings = (VkVertexInputBindingDescription *)alloca(sizeof(VkVertexInputBindingDescription) * vertexBindingCount);
#else
VkVertexInputBindingDescription vi_bindings[vertexBindingCount];
#endif
#ifdef KORE_WINDOWS
VkVertexInputAttributeDescription *vi_attrs = (VkVertexInputAttributeDescription *)alloca(sizeof(VkVertexInputAttributeDescription) * vertexAttributeCount);
#else
VkVertexInputAttributeDescription vi_attrs[vertexAttributeCount];
#endif
VkPipelineVertexInputStateCreateInfo vi = {0};
memset(&vi, 0, sizeof(vi));
vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vi.pNext = NULL;
vi.vertexBindingDescriptionCount = vertexBindingCount;
vi.pVertexBindingDescriptions = vi_bindings;
vi.vertexAttributeDescriptionCount = vertexAttributeCount;
vi.pVertexAttributeDescriptions = vi_attrs;
uint32_t attr = 0;
for (int binding = 0; binding < vertexBindingCount; ++binding) {
uint32_t offset = 0;
uint32_t stride = 0;
for (int i = 0; i < pipeline->inputLayout[binding]->size; ++i) {
kinc_g5_vertex_element_t element = pipeline->inputLayout[binding]->elements[i];
vi_attrs[attr].binding = binding;
vi_attrs[attr].location = find_number(pipeline->impl.vertexLocations, element.name);
vi_attrs[attr].offset = offset;
offset += kinc_g4_vertex_data_size(element.data);
stride += kinc_g4_vertex_data_size(element.data);
switch (element.data) {
case KINC_G4_VERTEX_DATA_F32_1X:
vi_attrs[attr].format = VK_FORMAT_R32_SFLOAT;
break;
case KINC_G4_VERTEX_DATA_F32_2X:
vi_attrs[attr].format = VK_FORMAT_R32G32_SFLOAT;
break;
case KINC_G4_VERTEX_DATA_F32_3X:
vi_attrs[attr].format = VK_FORMAT_R32G32B32_SFLOAT;
break;
case KINC_G4_VERTEX_DATA_F32_4X:
vi_attrs[attr].format = VK_FORMAT_R32G32B32A32_SFLOAT;
break;
case KINC_G4_VERTEX_DATA_I8_1X:
vi_attrs[attr].format = VK_FORMAT_R8_SINT;
break;
case KINC_G4_VERTEX_DATA_U8_1X:
vi_attrs[attr].format = VK_FORMAT_R8_UINT;
break;
case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R8_SNORM;
break;
case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R8_UNORM;
break;
case KINC_G4_VERTEX_DATA_I8_2X:
vi_attrs[attr].format = VK_FORMAT_R8G8_SINT;
break;
case KINC_G4_VERTEX_DATA_U8_2X:
vi_attrs[attr].format = VK_FORMAT_R8G8_UINT;
break;
case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R8G8_SNORM;
break;
case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R8G8_UNORM;
break;
case KINC_G4_VERTEX_DATA_I8_4X:
vi_attrs[attr].format = VK_FORMAT_R8G8B8A8_SINT;
break;
case KINC_G4_VERTEX_DATA_U8_4X:
vi_attrs[attr].format = VK_FORMAT_R8G8B8A8_UINT;
break;
case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R8G8B8A8_SNORM;
break;
case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R8G8B8A8_UNORM;
break;
case KINC_G4_VERTEX_DATA_I16_1X:
vi_attrs[attr].format = VK_FORMAT_R16_SINT;
break;
case KINC_G4_VERTEX_DATA_U16_1X:
vi_attrs[attr].format = VK_FORMAT_R16_UINT;
break;
case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R16_SNORM;
break;
case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R16_UNORM;
break;
case KINC_G4_VERTEX_DATA_I16_2X:
vi_attrs[attr].format = VK_FORMAT_R16G16_SINT;
break;
case KINC_G4_VERTEX_DATA_U16_2X:
vi_attrs[attr].format = VK_FORMAT_R16G16_UINT;
break;
case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R16G16_SNORM;
break;
case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R16G16_UNORM;
break;
case KINC_G4_VERTEX_DATA_I16_4X:
vi_attrs[attr].format = VK_FORMAT_R16G16B16A16_SINT;
break;
case KINC_G4_VERTEX_DATA_U16_4X:
vi_attrs[attr].format = VK_FORMAT_R16G16B16A16_UINT;
break;
case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R16G16B16A16_SNORM;
break;
case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED:
vi_attrs[attr].format = VK_FORMAT_R16G16B16A16_UNORM;
break;
case KINC_G4_VERTEX_DATA_I32_1X:
vi_attrs[attr].format = VK_FORMAT_R32_SINT;
break;
case KINC_G4_VERTEX_DATA_U32_1X:
vi_attrs[attr].format = VK_FORMAT_R32_UINT;
break;
case KINC_G4_VERTEX_DATA_I32_2X:
vi_attrs[attr].format = VK_FORMAT_R32G32_SINT;
break;
case KINC_G4_VERTEX_DATA_U32_2X:
vi_attrs[attr].format = VK_FORMAT_R32G32_UINT;
break;
case KINC_G4_VERTEX_DATA_I32_3X:
vi_attrs[attr].format = VK_FORMAT_R32G32B32_SINT;
break;
case KINC_G4_VERTEX_DATA_U32_3X:
vi_attrs[attr].format = VK_FORMAT_R32G32B32_UINT;
break;
case KINC_G4_VERTEX_DATA_I32_4X:
vi_attrs[attr].format = VK_FORMAT_R32G32B32A32_SINT;
break;
case KINC_G4_VERTEX_DATA_U32_4X:
vi_attrs[attr].format = VK_FORMAT_R32G32B32A32_UINT;
break;
default:
assert(false);
break;
}
attr++;
}
vi_bindings[binding].binding = binding;
vi_bindings[binding].stride = stride;
vi_bindings[binding].inputRate = pipeline->inputLayout[binding]->instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
}
memset(&ia, 0, sizeof(ia));
ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
memset(&rs, 0, sizeof(rs));
rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rs.polygonMode = VK_POLYGON_MODE_FILL;
rs.cullMode = convert_cull_mode(pipeline->cullMode);
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rs.depthClampEnable = VK_FALSE;
rs.rasterizerDiscardEnable = VK_FALSE;
rs.depthBiasEnable = VK_FALSE;
rs.lineWidth = 1.0f;
memset(&cb, 0, sizeof(cb));
cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
VkPipelineColorBlendAttachmentState att_state[8];
memset(att_state, 0, sizeof(att_state));
for (int i = 0; i < pipeline->colorAttachmentCount; ++i) {
att_state[i].colorWriteMask =
(pipeline->colorWriteMaskRed[i] ? VK_COLOR_COMPONENT_R_BIT : 0) | (pipeline->colorWriteMaskGreen[i] ? VK_COLOR_COMPONENT_G_BIT : 0) |
(pipeline->colorWriteMaskBlue[i] ? VK_COLOR_COMPONENT_B_BIT : 0) | (pipeline->colorWriteMaskAlpha[i] ? VK_COLOR_COMPONENT_A_BIT : 0);
att_state[i].blendEnable = pipeline->blend_source != KINC_G5_BLEND_ONE || pipeline->blend_destination != KINC_G5_BLEND_ZERO ||
pipeline->alpha_blend_source != KINC_G5_BLEND_ONE || pipeline->alpha_blend_destination != KINC_G5_BLEND_ZERO;
att_state[i].srcColorBlendFactor = convert_blend_factor(pipeline->blend_source);
att_state[i].dstColorBlendFactor = convert_blend_factor(pipeline->blend_destination);
att_state[i].colorBlendOp = convert_blend_operation(pipeline->blend_operation);
att_state[i].srcAlphaBlendFactor = convert_blend_factor(pipeline->alpha_blend_source);
att_state[i].dstAlphaBlendFactor = convert_blend_factor(pipeline->alpha_blend_destination);
att_state[i].alphaBlendOp = convert_blend_operation(pipeline->alpha_blend_operation);
}
cb.attachmentCount = pipeline->colorAttachmentCount;
cb.pAttachments = att_state;
memset(&vp, 0, sizeof(vp));
vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
vp.viewportCount = 1;
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
vp.scissorCount = 1;
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
memset(&ds, 0, sizeof(ds));
ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
ds.depthTestEnable = pipeline->depthMode != KINC_G5_COMPARE_MODE_ALWAYS;
ds.depthWriteEnable = pipeline->depthWrite;
ds.depthCompareOp = convert_compare_mode(pipeline->depthMode);
ds.depthBoundsTestEnable = VK_FALSE;
ds.back.failOp = VK_STENCIL_OP_KEEP;
ds.back.passOp = VK_STENCIL_OP_KEEP;
ds.back.compareOp = VK_COMPARE_OP_ALWAYS;
ds.stencilTestEnable = VK_FALSE;
ds.front = ds.back;
memset(&ms, 0, sizeof(ms));
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
ms.pSampleMask = NULL;
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
pipeline_info.stageCount = 2;
VkPipelineShaderStageCreateInfo shaderStages[2];
memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo));
shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderStages[0].module = prepare_vs(&pipeline->impl.vert_shader_module, pipeline->vertexShader);
shaderStages[0].pName = "main";
shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStages[1].module = prepare_fs(&pipeline->impl.frag_shader_module, pipeline->fragmentShader);
shaderStages[1].pName = "main";
pipeline_info.pVertexInputState = &vi;
pipeline_info.pInputAssemblyState = &ia;
pipeline_info.pRasterizationState = &rs;
pipeline_info.pColorBlendState = &cb;
pipeline_info.pMultisampleState = &ms;
pipeline_info.pViewportState = &vp;
pipeline_info.pDepthStencilState = &ds;
pipeline_info.pStages = shaderStages;
pipeline_info.renderPass = vk_ctx.windows[vk_ctx.current_window].framebuffer_render_pass;
pipeline_info.pDynamicState = &dynamicState;
err = vkCreateGraphicsPipelines(vk_ctx.device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &pipeline->impl.framebuffer_pipeline);
assert(!err);
if (pipeline->depthAttachmentBits > 0) {
pipeline_info.renderPass = vk_ctx.windows[vk_ctx.current_window].rendertarget_render_pass_with_depth;
}
else {
pipeline_info.renderPass = vk_ctx.windows[vk_ctx.current_window].rendertarget_render_pass;
}
err = vkCreateGraphicsPipelines(vk_ctx.device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &pipeline->impl.rendertarget_pipeline);
assert(!err);
vkDestroyShaderModule(vk_ctx.device, pipeline->impl.frag_shader_module, NULL);
vkDestroyShaderModule(vk_ctx.device, pipeline->impl.vert_shader_module, NULL);
}
void createDescriptorLayout() {
VkDescriptorSetLayoutBinding layoutBindings[18];
memset(layoutBindings, 0, sizeof(layoutBindings));
layoutBindings[0].binding = 0;
layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
layoutBindings[0].descriptorCount = 1;
layoutBindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
layoutBindings[0].pImmutableSamplers = NULL;
layoutBindings[1].binding = 1;
layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
layoutBindings[1].descriptorCount = 1;
layoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
layoutBindings[1].pImmutableSamplers = NULL;
for (int i = 2; i < 18; ++i) {
layoutBindings[i].binding = i;
layoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
layoutBindings[i].descriptorCount = 1;
layoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT;
layoutBindings[i].pImmutableSamplers = NULL;
}
VkDescriptorSetLayoutCreateInfo descriptor_layout = {0};
descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptor_layout.pNext = NULL;
descriptor_layout.bindingCount = 18;
descriptor_layout.pBindings = layoutBindings;
VkResult err = vkCreateDescriptorSetLayout(vk_ctx.device, &descriptor_layout, NULL, &desc_layout);
assert(!err);
VkDescriptorPoolSize typeCounts[2];
memset(typeCounts, 0, sizeof(typeCounts));
typeCounts[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
typeCounts[0].descriptorCount = 2 * 1024;
typeCounts[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
typeCounts[1].descriptorCount = 16 * 1024;
VkDescriptorPoolCreateInfo pool_info = {0};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.pNext = NULL;
pool_info.maxSets = 1024;
pool_info.poolSizeCount = 2;
pool_info.pPoolSizes = typeCounts;
err = vkCreateDescriptorPool(vk_ctx.device, &pool_info, NULL, &descriptor_pool);
assert(!err);
}
int calc_descriptor_id(void) {
int texture_count = 0;
for (int i = 0; i < 16; ++i) {
if (vulkanTextures[i] != NULL) {
texture_count++;
}
else if (vulkanRenderTargets[i] != NULL) {
texture_count++;
}
}
bool uniform_buffer = vk_ctx.vertex_uniform_buffer != NULL && vk_ctx.fragment_uniform_buffer != NULL;
return 1 | (texture_count << 1) | ((uniform_buffer ? 1 : 0) << 8);
}
#define MAX_DESCRIPTOR_SETS 256
struct destriptor_set {
int id;
bool in_use;
VkDescriptorImageInfo tex_desc[16];
VkDescriptorSet set;
};
static struct destriptor_set descriptor_sets[MAX_DESCRIPTOR_SETS] = {0};
static int descriptor_sets_count = 0;
static int write_tex_descs(VkDescriptorImageInfo *tex_descs) {
memset(tex_descs, 0, sizeof(VkDescriptorImageInfo) * 16);
int texture_count = 0;
for (int i = 0; i < 16; ++i) {
if (vulkanTextures[i] != NULL) {
tex_descs[i].sampler = vulkanSamplers[i];
tex_descs[i].imageView = vulkanTextures[i]->impl.texture.view;
texture_count++;
}
else if (vulkanRenderTargets[i] != NULL) {
tex_descs[i].sampler = vulkanSamplers[i];
if (vulkanRenderTargets[i]->impl.stage_depth == i) {
tex_descs[i].imageView = vulkanRenderTargets[i]->impl.depthView;
vulkanRenderTargets[i]->impl.stage_depth = -1;
}
else {
tex_descs[i].imageView = vulkanRenderTargets[i]->impl.sourceView;
}
texture_count++;
}
tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
return texture_count;
}
static bool textures_changed(struct destriptor_set *set) {
VkDescriptorImageInfo tex_desc[16];
write_tex_descs(tex_desc);
return memcmp(&tex_desc, &set->tex_desc, sizeof(tex_desc)) != 0;
}
static void update_textures(struct destriptor_set *set) {
memset(&set->tex_desc, 0, sizeof(set->tex_desc));
int texture_count = write_tex_descs(set->tex_desc);
VkWriteDescriptorSet writes[16];
memset(&writes, 0, sizeof(writes));
for (int i = 0; i < 16; ++i) {
writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[i].dstSet = set->set;
writes[i].dstBinding = i + 2;
writes[i].descriptorCount = 1;
writes[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writes[i].pImageInfo = &set->tex_desc[i];
}
if (vulkanTextures[0] != NULL || vulkanRenderTargets[0] != NULL) {
vkUpdateDescriptorSets(vk_ctx.device, texture_count, writes, 0, NULL);
}
}
void reuse_descriptor_sets(void) {
for (int i = 0; i < descriptor_sets_count; ++i) {
descriptor_sets[i].in_use = false;
}
}
VkDescriptorSet getDescriptorSet() {
int id = calc_descriptor_id();
for (int i = 0; i < descriptor_sets_count; ++i) {
if (descriptor_sets[i].id == id) {
if (!descriptor_sets[i].in_use) {
descriptor_sets[i].in_use = true;
update_textures(&descriptor_sets[i]);
return descriptor_sets[i].set;
}
else {
if (!textures_changed(&descriptor_sets[i])) {
return descriptor_sets[i].set;
}
}
}
}
VkDescriptorSetAllocateInfo alloc_info = {0};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.pNext = NULL;
alloc_info.descriptorPool = descriptor_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &desc_layout;
VkDescriptorSet descriptor_set;
VkResult err = vkAllocateDescriptorSets(vk_ctx.device, &alloc_info, &descriptor_set);
assert(!err);
VkDescriptorBufferInfo buffer_descs[2];
memset(&buffer_descs, 0, sizeof(buffer_descs));
if (vk_ctx.vertex_uniform_buffer != NULL) {
buffer_descs[0].buffer = *vk_ctx.vertex_uniform_buffer;
}
buffer_descs[0].offset = 0;
buffer_descs[0].range = 256 * sizeof(float);
if (vk_ctx.fragment_uniform_buffer != NULL) {
buffer_descs[1].buffer = *vk_ctx.fragment_uniform_buffer;
}
buffer_descs[1].offset = 0;
buffer_descs[1].range = 256 * sizeof(float);
VkDescriptorImageInfo tex_desc[16];
memset(&tex_desc, 0, sizeof(tex_desc));
int texture_count = 0;
for (int i = 0; i < 16; ++i) {
if (vulkanTextures[i] != NULL) {
assert(vulkanSamplers[i] != VK_NULL_HANDLE);
tex_desc[i].sampler = vulkanSamplers[i];
tex_desc[i].imageView = vulkanTextures[i]->impl.texture.view;
texture_count++;
}
else if (vulkanRenderTargets[i] != NULL) {
tex_desc[i].sampler = vulkanSamplers[i];
if (vulkanRenderTargets[i]->impl.stage_depth == i) {
tex_desc[i].imageView = vulkanRenderTargets[i]->impl.depthView;
vulkanRenderTargets[i]->impl.stage_depth = -1;
}
else {
tex_desc[i].imageView = vulkanRenderTargets[i]->impl.sourceView;
}
texture_count++;
}
tex_desc[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
VkWriteDescriptorSet writes[18];
memset(&writes, 0, sizeof(writes));
writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[0].dstSet = descriptor_set;
writes[0].dstBinding = 0;
writes[0].descriptorCount = 1;
writes[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
writes[0].pBufferInfo = &buffer_descs[0];
writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[1].dstSet = descriptor_set;
writes[1].dstBinding = 1;
writes[1].descriptorCount = 1;
writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
writes[1].pBufferInfo = &buffer_descs[1];
for (int i = 2; i < 18; ++i) {
writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[i].dstSet = descriptor_set;
writes[i].dstBinding = i;
writes[i].descriptorCount = 1;
writes[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writes[i].pImageInfo = &tex_desc[i - 2];
}
if (vulkanTextures[0] != NULL || vulkanRenderTargets[0] != NULL) {
if (vk_ctx.vertex_uniform_buffer != NULL && vk_ctx.fragment_uniform_buffer != NULL) {
vkUpdateDescriptorSets(vk_ctx.device, 2 + texture_count, writes, 0, NULL);
}
else {
vkUpdateDescriptorSets(vk_ctx.device, texture_count, writes + 2, 0, NULL);
}
}
else {
if (vk_ctx.vertex_uniform_buffer != NULL && vk_ctx.fragment_uniform_buffer != NULL) {
vkUpdateDescriptorSets(vk_ctx.device, 2, writes, 0, NULL);
}
}
assert(descriptor_sets_count + 1 < MAX_DESCRIPTOR_SETS);
descriptor_sets[descriptor_sets_count].id = id;
descriptor_sets[descriptor_sets_count].in_use = true;
descriptor_sets[descriptor_sets_count].set = descriptor_set;
write_tex_descs(descriptor_sets[descriptor_sets_count].tex_desc);
descriptor_sets_count += 1;
return descriptor_set;
}