roleplay/client/src/pipeline.c

1003 lines
33 KiB
C

#include "pipeline.h"
#include "cglm/affine.h"
#include "cglm/mat4.h"
#include "command.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "vk_mem_alloc.h"
#include "vulkan/vulkan_core.h"
VkShaderModule load_shader_file(const char* path, VkDevice device) {
FILE* file;
file = fopen(path, "rb");
if(file == 0) {
return VK_NULL_HANDLE;
}
int result = fseek(file, 0, SEEK_END);
if(result != 0) {
return VK_NULL_HANDLE;
}
long buffer_size = ftell(file);
result = fseek(file, 0, SEEK_SET);
if(result != 0) {
return VK_NULL_HANDLE;
}
char * buffer = malloc(buffer_size);
if(buffer == 0) {
return VK_NULL_HANDLE;
}
size_t read = fread(buffer, 1, buffer_size, file);
VkShaderModuleCreateInfo shader_info = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = read,
.pCode = (uint32_t*)buffer,
};
VkShaderModule shader;
result = vkCreateShaderModule(device, &shader_info, 0, &shader);
free(buffer);
if(result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
return shader;
}
VkResult create_ui_pipeline(
VkDevice device,
VkRenderPass render_pass,
VkPipelineShaderStageCreateInfo* shader_stages,
uint32_t shader_stage_count,
VkPipelineVertexInputStateCreateInfo input_info,
VkPipelineLayoutCreateInfo layout_info,
VkPipelineInputAssemblyStateCreateInfo input_assembly_info,
GraphicsPipeline* pipeline) {
VkResult result;
result = vkCreatePipelineLayout(device, &layout_info, 0, &pipeline->layout);
if(result != VK_SUCCESS) {
return result;
}
VkViewport viewport = {
.x = 0.0f,
.y = 0.0f,
.width = (float)(100),
.height = (float)(100),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
VkRect2D scissor = {
.offset = {
.x = 0,
.y = 0,
},
.extent = {
.width = 100,
.height = 100,
},
};
VkPipelineViewportStateCreateInfo viewport_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.pViewports = &viewport,
.scissorCount = 1,
.pScissors = &scissor,
};
VkPipelineRasterizationStateCreateInfo raster_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0f,
.cullMode = VK_CULL_MODE_BACK_BIT,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f,
};
VkPipelineMultisampleStateCreateInfo multisample_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.sampleShadingEnable = VK_FALSE,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.minSampleShading = 1.0f,
.pSampleMask = 0,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
VkPipelineDepthStencilStateCreateInfo depth_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_LESS,
.depthBoundsTestEnable = VK_FALSE,
.maxDepthBounds = 1.0f,
.minDepthBounds = 0.0f,
.stencilTestEnable = VK_FALSE,
.front = {},
.back = {},
};
VkPipelineColorBlendAttachmentState color_blend_attachment = {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
};
VkPipelineColorBlendStateCreateInfo color_blend_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &color_blend_attachment,
.blendConstants[0] = 0.0f,
.blendConstants[1] = 0.0f,
.blendConstants[2] = 0.0f,
.blendConstants[3] = 0.0f,
};
VkDynamicState dynamic_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
uint32_t dynamic_state_count = sizeof(dynamic_states)/sizeof(VkDynamicState);
VkPipelineDynamicStateCreateInfo dynamic_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = dynamic_state_count,
.pDynamicStates = dynamic_states,
};
VkGraphicsPipelineCreateInfo draw_pipeline_info = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = shader_stage_count,
.pStages = shader_stages,
.pVertexInputState = &input_info,
.pInputAssemblyState = &input_assembly_info,
.pViewportState = &viewport_state,
.pRasterizationState = &raster_info,
.pColorBlendState = &color_blend_info,
.pDynamicState = &dynamic_info,
.pDepthStencilState = &depth_info,
.pMultisampleState = &multisample_info,
.layout = pipeline->layout,
.renderPass = render_pass,
.subpass = 1,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1,
};
result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &draw_pipeline_info, 0, &pipeline->pipeline);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult create_ui_colored_rect_pipeline(VkDevice device, VkRenderPass render_pass, VkDescriptorSetLayout ui_descriptor_layout, GraphicsPipeline* pipeline) {
VkShaderModule vert_shader = load_shader_file("shader_src/ui_colored_rect.vert.spv", device);
if(vert_shader == VK_NULL_HANDLE) {
return VK_ERROR_UNKNOWN;
}
VkShaderModule frag_shader = load_shader_file("shader_src/ui_colored_rect.frag.spv", device);
if(frag_shader == VK_NULL_HANDLE) {
return VK_ERROR_UNKNOWN;
}
VkPipelineShaderStageCreateInfo shader_stages[] = {
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vert_shader,
.pName = "main",
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = frag_shader,
.pName = "main",
},
};
VkVertexInputBindingDescription bindings[] = {
{
.binding = 0,
.stride = sizeof(vec2),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
},
{
.binding = 1,
.stride = sizeof(ColoredRect),
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE,
},
};
VkVertexInputAttributeDescription attributes[] = {
{
.binding = 0,
.location = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = 0,
},
{
.binding = 1,
.location = 1,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof(ColoredRect, pos),
},
{
.binding = 1,
.location = 2,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof(ColoredRect, size),
},
{
.binding = 1,
.location = 3,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = offsetof(ColoredRect, color),
},
};
VkPipelineVertexInputStateCreateInfo input_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pVertexBindingDescriptions = bindings,
.vertexBindingDescriptionCount = sizeof(bindings)/sizeof(VkVertexInputBindingDescription),
.pVertexAttributeDescriptions = attributes,
.vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription),
};
VkPipelineLayoutCreateInfo layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &ui_descriptor_layout,
};
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
VkResult result = create_ui_pipeline(device, render_pass, shader_stages, sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), input_info, layout_info, input_assembly_info, pipeline);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult create_ui_text_pipeline(VkDevice device, VkRenderPass render_pass, VkDescriptorSetLayout ui_descriptor_layout, VkDescriptorSetLayout font_layout, GraphicsPipeline* pipeline, ComputePipeline* compute) {
VkResult result;
VkShaderModule compute_shader = load_shader_file("shader_src/ui_text.comp.spv", device);
if(compute_shader == VK_NULL_HANDLE) {
return VK_ERROR_UNKNOWN;
}
VkPipelineShaderStageCreateInfo compute_shader_stage = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
.pName = "main",
.module = compute_shader,
};
VkPushConstantRange push_constant = {
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT | VK_SHADER_STAGE_VERTEX_BIT,
.size = 8,
};
VkDescriptorSetLayout compute_descriptors[] = {font_layout};
VkPipelineLayoutCreateInfo compute_layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &push_constant,
.setLayoutCount = sizeof(compute_descriptors)/sizeof(VkDescriptorSetLayout),
.pSetLayouts = compute_descriptors,
};
result = vkCreatePipelineLayout(device, &compute_layout_info, NULL, &compute->layout);
if(result != VK_SUCCESS) {
return result;
}
VkComputePipelineCreateInfo compute_pipeline_info = {
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.layout = compute->layout,
.stage = compute_shader_stage,
};
result = vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &compute_pipeline_info, NULL, &compute->pipeline);
if(result != VK_SUCCESS) {
return result;
}
VkShaderModule vert_shader = load_shader_file("shader_src/ui_text.vert.spv", device);
if(vert_shader == VK_NULL_HANDLE) {
return VK_ERROR_UNKNOWN;
}
VkShaderModule frag_shader = load_shader_file("shader_src/ui_text.frag.spv", device);
if(frag_shader == VK_NULL_HANDLE) {
return VK_ERROR_UNKNOWN;
}
// TODO: add a compute stage before the shader stages that sets up the draw buffer and commands for the characters based off of strings + positions
// 1. Reserve a buffer for array of draw command params + Char on the GPU
// 2. Reserve a buffer for Text on the GPU
VkPipelineShaderStageCreateInfo shader_stages[] = {
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vert_shader,
.pName = "main",
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = frag_shader,
.pName = "main",
},
};
VkVertexInputBindingDescription bindings[] = {
{
.binding = 0,
.stride = sizeof(vec2),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
},
};
VkVertexInputAttributeDescription attributes[] = {
{
.binding = 0,
.location = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = 0,
},
};
VkPipelineVertexInputStateCreateInfo input_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pVertexBindingDescriptions = bindings,
.vertexBindingDescriptionCount = sizeof(bindings)/sizeof(VkVertexInputBindingDescription),
.pVertexAttributeDescriptions = attributes,
.vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription),
};
VkDescriptorSetLayout all_layouts[] = {ui_descriptor_layout, font_layout};
VkPipelineLayoutCreateInfo layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = sizeof(all_layouts)/sizeof(VkDescriptorSetLayout),
.pSetLayouts = all_layouts,
.pPushConstantRanges = &push_constant,
.pushConstantRangeCount = 1,
};
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
.primitiveRestartEnable = VK_FALSE,
};
result = create_ui_pipeline(device, render_pass, shader_stages, sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), input_info, layout_info, input_assembly_info, pipeline);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult create_font_descriptor_pools(VkDevice device, uint32_t max_sets, VkDescriptorPool* font_pool, VkDescriptorSetLayout* font_layout) {
VkResult result;
VkDescriptorPoolSize font_pool_sizes[] = {
{
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
},
{
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
},
};
VkDescriptorPoolCreateInfo font_pool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pPoolSizes = font_pool_sizes,
.poolSizeCount = sizeof(font_pool_sizes)/sizeof(VkDescriptorPoolSize),
.maxSets = max_sets,
};
result = vkCreateDescriptorPool(device, &font_pool_info, NULL, font_pool);
if(result != VK_SUCCESS) {
return result;
}
VkDescriptorSetLayoutBinding font_descriptor_bindings[] = {
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT,
},
{
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
},
};
VkDescriptorSetLayoutCreateInfo font_descriptor_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pBindings = font_descriptor_bindings,
.bindingCount = sizeof(font_descriptor_bindings)/sizeof(VkDescriptorSetLayoutBinding),
};
result = vkCreateDescriptorSetLayout(device, &font_descriptor_info, NULL, font_layout);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}
VkResult load_font(VkDevice device, VmaAllocator allocator, VkDescriptorSetLayout layout, VkDescriptorPool pool,VkCommandPool transfer_pool, Queue transfer_queue, FT_Library library, const char* ttf_file, uint32_t size, VkBool32 antialias, uint32_t** charmap, FontDescriptor* descriptor) {
FT_Face face;
int error;
error = FT_New_Face(library, ttf_file, 0, &face);
if(error != FT_Err_Ok) {
return VK_ERROR_UNKNOWN;
}
error = FT_Set_Pixel_Sizes(face, 0, size);
if(error != FT_Err_Ok) {
return VK_ERROR_UNKNOWN;
}
uint32_t* tmp_charmap = malloc(sizeof(uint32_t)*face->num_glyphs);
SymbolInfo* symbols = malloc(sizeof(SymbolInfo)*face->num_glyphs);
FontUniform uniform;
uint32_t glyph_index;
uint32_t max_height = 0;
uint32_t max_width = 0;
uint32_t symbol_count = 0;
uint32_t c;
FT_Int32 load_flags = FT_LOAD_RENDER;
if(antialias == VK_TRUE) {
load_flags += FT_RENDER_MODE_NORMAL;
} else {
load_flags += FT_RENDER_MODE_MONO;
}
// TODO: worry about variants(that's why num_glyphs doesn't match symbol_count)
c = FT_Get_First_Char(face, &glyph_index);
for(uint32_t i = 0; i < face->num_glyphs; i++) {
if(glyph_index == 0) {
break;
}
FT_Load_Glyph(face, glyph_index, load_flags);
uint32_t width = face->glyph->bitmap.width;
uint32_t height = face->glyph->bitmap.rows;
tmp_charmap[i] = c;
symbols[i].width = width;
symbols[i].height = height;
symbols[i].left = face->glyph->bitmap_left;
symbols[i].top = face->glyph->bitmap_top;
symbols[i].advance = face->glyph->advance.x*16/face->units_per_EM;
max_width = width > max_width ? width : max_width;
max_height = height > max_height ? height : max_height;
symbol_count += 1;
c = FT_Get_Next_Char(face, c, &glyph_index);
}
uniform.width = max_width;
uniform.height = max_height;
uniform.num_symbols = symbol_count;
uint32_t image_size = max_width*max_height*sizeof(uint32_t);
uint32_t* images = malloc(image_size*symbol_count);
memset(images, 0x00, image_size*symbol_count);
*charmap = malloc(sizeof(uint32_t)*symbol_count);
memcpy(*charmap, tmp_charmap, sizeof(uint32_t)*symbol_count);
free(tmp_charmap);
for(uint32_t i = 0; i < symbol_count; i++) {
glyph_index = FT_Get_Char_Index(face, (*charmap)[i]);
FT_Load_Glyph(face, glyph_index, load_flags);
for(uint32_t y = 0; y < face->glyph->bitmap.rows; y++) {
for(uint32_t x = 0; x < face->glyph->bitmap.width; x++) {
uint32_t level = face->glyph->bitmap.buffer[y*face->glyph->bitmap.width+x];
level = ((level * 0xFFFFFF) / 256) << 8;
images[max_width*max_height*i + max_width*y + x] = 0x000000FF + level;
}
}
}
error = FT_Done_Face(face);
if(error != FT_Err_Ok) {
return VK_ERROR_UNKNOWN;
}
VkResult result;
VkDescriptorSetAllocateInfo set_allocate_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pSetLayouts = &layout,
.descriptorSetCount = 1,
.descriptorPool = pool,
};
result = vkAllocateDescriptorSets(device, &set_allocate_info, &descriptor->set);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo symbol_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.size = sizeof(SymbolInfo)*uniform.num_symbols,
};
VmaAllocationCreateInfo symbol_memory_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
result = vmaCreateBuffer(allocator, &symbol_buffer_info, &symbol_memory_info, &descriptor->symbols, &descriptor->symbol_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo uniform_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
.size = sizeof(FontUniform),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VmaAllocationCreateInfo uniform_memory_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
result = vmaCreateBuffer(allocator, &uniform_buffer_info, &uniform_memory_info, &descriptor->uniform, &descriptor->uniform_memory, NULL);
VkImageCreateInfo image_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
.extent.depth = 1,
.extent.width = uniform.width,
.extent.height = uniform.height,
.mipLevels = 1,
.arrayLayers = uniform.num_symbols,
.format = VK_FORMAT_R8G8B8A8_SRGB,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.samples = VK_SAMPLE_COUNT_1_BIT,
.imageType = VK_IMAGE_TYPE_2D,
};
VmaAllocationCreateInfo image_memory_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
result = vmaCreateImage(allocator, &image_info, &image_memory_info, &descriptor->image, &descriptor->image_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo staging_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.size = sizeof(FontUniform) + image_size*uniform.num_symbols + sizeof(SymbolInfo)*uniform.num_symbols,
};
VmaAllocationCreateInfo staging_memory_info = {
.usage = VMA_MEMORY_USAGE_CPU_TO_GPU,
};
VkBuffer staging_buffer;
VmaAllocation staging_memory;
result = vmaCreateBuffer(allocator, &staging_info, &staging_memory_info, &staging_buffer, &staging_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
void* mapped_staging;
result = vmaMapMemory(allocator, staging_memory, &mapped_staging);
if(result != VK_SUCCESS) {
return result;
}
memcpy(mapped_staging + image_size*uniform.num_symbols + sizeof(FontUniform), symbols, sizeof(SymbolInfo)*uniform.num_symbols);
VkBufferDeviceAddressInfo address_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = descriptor->symbols,
};
uniform.symbol_list = vkGetBufferDeviceAddress(device, &address_info);
memcpy(mapped_staging + image_size*uniform.num_symbols, &uniform, sizeof(FontUniform));
memcpy(mapped_staging, images, image_size*uniform.num_symbols);
vmaUnmapMemory(allocator, staging_memory);
free(images);
free(symbols);
VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool);
VkBufferCopy uniform_copy_info = {
.size = sizeof(FontUniform),
.srcOffset = image_size*uniform.num_symbols,
.dstOffset = 0,
};
vkCmdCopyBuffer(command_buffer, staging_buffer, descriptor->uniform, 1, &uniform_copy_info);
VkBufferCopy symbol_copy_info = {
.size = sizeof(SymbolInfo)*uniform.num_symbols,
.srcOffset = image_size*uniform.num_symbols + sizeof(FontUniform),
.dstOffset = 0,
};
vkCmdCopyBuffer(command_buffer, staging_buffer, descriptor->symbols, 1, &symbol_copy_info);
VkImageMemoryBarrier first_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = descriptor->image,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.levelCount = 1,
.subresourceRange.layerCount = uniform.num_symbols,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &first_barrier);
VkBufferImageCopy image_copy = {
.imageSubresource.layerCount = uniform.num_symbols,
.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.imageExtent = image_info.extent,
};
vkCmdCopyBufferToImage(command_buffer, staging_buffer, descriptor->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
VkImageMemoryBarrier second_barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.image = descriptor->image,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.levelCount = 1,
.subresourceRange.layerCount = uniform.num_symbols,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &second_barrier);
result = command_end_single(device, command_buffer, transfer_pool, transfer_queue);
if(result != VK_SUCCESS) {
return result;
}
vmaDestroyBuffer(allocator, staging_buffer, staging_memory);
VkImageViewCreateInfo view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = descriptor->image,
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
.format = VK_FORMAT_R8G8B8A8_SRGB,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = uniform.num_symbols,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
},
};
result = vkCreateImageView(device, &view_info, NULL, &descriptor->view);
if(result != VK_SUCCESS) {
return result;
}
VkSamplerCreateInfo sampler_info = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_NEAREST,
.minFilter = VK_FILTER_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
};
result = vkCreateSampler(device, &sampler_info, NULL, &descriptor->sampler);
if(result != VK_SUCCESS) {
return result;
}
VkDescriptorBufferInfo desc_uniform_info = {
.offset = 0,
.range = sizeof(FontUniform),
.buffer = descriptor->uniform,
};
VkDescriptorImageInfo desc_image_info = {
.sampler = descriptor->sampler,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = descriptor->view,
};
VkWriteDescriptorSet descriptor_writes[] = {
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor->set,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &desc_uniform_info,
},
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptor->set,
.dstBinding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &desc_image_info,
}
};
vkUpdateDescriptorSets(device, 2, descriptor_writes, 0, NULL);
return VK_SUCCESS;
}
VkResult create_ui_descriptor_set(VkDevice device, VmaAllocator allocator, VkExtent2D swapchain_extent, vec2 window_scale, VkDescriptorSetLayout* ui_descriptor_layout, VkDescriptorPool* ui_descriptor_pool, VkDescriptorSet* ui_descriptor_set, VmaAllocation* ui_descriptor_memory, VkBuffer* ui_descriptor_buffer) {
VkDescriptorSetLayoutBinding ui_descriptor_bindings[] = {
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
}
};
VkDescriptorSetLayoutCreateInfo ui_descriptor_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pBindings = ui_descriptor_bindings,
.bindingCount = sizeof(ui_descriptor_bindings)/sizeof(VkDescriptorSetLayoutBinding),
};
VkResult result;
result = vkCreateDescriptorSetLayout(device, &ui_descriptor_info, NULL, ui_descriptor_layout);
if(result != VK_SUCCESS) {
return result;
}
VkDescriptorPoolSize pool_size = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
};
VkDescriptorPoolCreateInfo ui_pool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pPoolSizes = &pool_size,
.poolSizeCount = 1,
.maxSets = 1,
};
result = vkCreateDescriptorPool(device, &ui_pool_info, NULL, ui_descriptor_pool);
if(result != VK_SUCCESS) {
return result;
}
VkDescriptorSetAllocateInfo ui_descriptor_allocate_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pSetLayouts = ui_descriptor_layout,
.descriptorSetCount = 1,
.descriptorPool = *ui_descriptor_pool,
};
result = vkAllocateDescriptorSets(device, &ui_descriptor_allocate_info, ui_descriptor_set);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo ui_uniform_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
.size = sizeof(UIUniform),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VmaAllocationCreateInfo ui_uniform_memory_info = {
.usage = VMA_MEMORY_USAGE_CPU_TO_GPU,
};
result = vmaCreateBuffer(allocator, &ui_uniform_buffer_info, &ui_uniform_memory_info, ui_descriptor_buffer, ui_descriptor_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
void* mapped;
result = vmaMapMemory(allocator, *ui_descriptor_memory, &mapped);
if(result != VK_SUCCESS) {
return result;
}
UIUniform ui_uniform;
vec3 screen_offset = {-1.0, -1.0, 0.0};
vec3 screen_scale = {1.0/(float)swapchain_extent.width*window_scale[0], 1.0/(float)swapchain_extent.height*window_scale[1], 1.0};
glm_mat4_identity(ui_uniform.screen);
glm_translate(ui_uniform.screen, screen_offset);
glm_scale(ui_uniform.screen, screen_scale);
memcpy(mapped, &ui_uniform, sizeof(ui_uniform));
vmaUnmapMemory(allocator, *ui_descriptor_memory);
VkDescriptorBufferInfo ui_uniform_info = {
.offset = 0,
.range = sizeof(ui_uniform),
.buffer = *ui_descriptor_buffer,
};
VkWriteDescriptorSet ui_uniform_write = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = *ui_descriptor_set,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.pBufferInfo = &ui_uniform_info,
};
vkUpdateDescriptorSets(device, 1, &ui_uniform_write, 0, NULL);
return VK_SUCCESS;
}
VkResult create_ui_rect_buffer(VkDevice device, Queue transfer_queue, VkCommandPool transfer_pool, VmaAllocator allocator, struct RectBuffer* rect) {
uint32_t vertex_buffer_size = 4 * sizeof(vec2);
uint32_t index_buffer_size = 6 * sizeof(uint32_t);
// Create temp buffer
VkBufferCreateInfo temp_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.size = vertex_buffer_size + index_buffer_size,
};
VmaAllocationCreateInfo temp_allocation_info = {
.usage = VMA_MEMORY_USAGE_CPU_TO_GPU,
};
VkBuffer temp_buffer;
VmaAllocation temp_buffer_memory;
VkResult result;
result = vmaCreateBuffer(allocator, &temp_buffer_info, &temp_allocation_info, &temp_buffer, &temp_buffer_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
// Create buffers
VmaAllocationCreateInfo allocation_info = {
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
VkBufferCreateInfo vertex_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.size = vertex_buffer_size,
};
result = vmaCreateBuffer(allocator, &vertex_buffer_info, &allocation_info, &rect->vertex, &rect->vertex_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
VkBufferCreateInfo index_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.size = index_buffer_size,
};
result = vmaCreateBuffer(allocator, &index_buffer_info, &allocation_info, &rect->index, &rect->index_memory, NULL);
if(result != VK_SUCCESS) {
return result;
}
void* mapped;
result = vmaMapMemory(allocator, temp_buffer_memory, &mapped);
if(result != VK_SUCCESS) {
return result;
}
vec2* mapped_vertex = (vec2*)mapped;
mapped_vertex[0][0] = 0.0f;
mapped_vertex[0][1] = 0.0f;
mapped_vertex[1][0] = 1.0f;
mapped_vertex[1][1] = 0.0f;
mapped_vertex[2][0] = 0.0f;
mapped_vertex[2][1] = 1.0f;
mapped_vertex[3][0] = 1.0f;
mapped_vertex[3][1] = 1.0f;
uint32_t* mapped_index = (uint32_t*)(mapped + vertex_buffer_size);
mapped_index[0] = 0;
mapped_index[1] = 1;
mapped_index[2] = 2;
mapped_index[3] = 1;
mapped_index[4] = 3;
mapped_index[5] = 2;
vmaUnmapMemory(allocator, temp_buffer_memory);
VkCommandBuffer copy_buffer = command_begin_single(device, transfer_pool);
VkBufferCopy vertex_copy_region = {
.size = vertex_buffer_size,
.dstOffset = 0,
.srcOffset = 0,
};
vkCmdCopyBuffer(copy_buffer, temp_buffer, rect->vertex, 1, &vertex_copy_region);
VkBufferCopy index_copy_region = {
.size = index_buffer_size,
.dstOffset = 0,
.srcOffset = vertex_buffer_size,
};
vkCmdCopyBuffer(copy_buffer, temp_buffer, rect->index, 1, &index_copy_region);
result = command_end_single(device, copy_buffer, transfer_pool, transfer_queue);
if(result != VK_SUCCESS) {
return result;
}
vmaDestroyBuffer(allocator, temp_buffer, temp_buffer_memory);
return VK_SUCCESS;
}
VkResult init_pipelines(VkDevice device, VmaAllocator allocator, VkExtent2D swapchain_extent, vec2 window_scale, VkRenderPass render_pass, Queue transfer_queue, VkCommandPool transfer_pool, UIContext* context) {
VkResult result;
result = create_ui_descriptor_set(device, allocator, swapchain_extent, window_scale, &context->ui_descriptor_layout, &context->ui_descriptor_pool, &context->ui_descriptor_set, &context->ui_descriptor_memory, &context->ui_descriptor_buffer);
if(result != VK_SUCCESS) {
return result;
}
result = create_ui_rect_buffer(device, transfer_queue, transfer_pool, allocator, &context->ui_rect);
if(result != VK_SUCCESS) {
return result;
}
result = create_ui_colored_rect_pipeline(device, render_pass, context->ui_descriptor_layout, &context->ui_pipeline_rect);
if(result != VK_SUCCESS) {
return result;
}
result = create_font_descriptor_pools(device, 10, &context->font_pool, &context->font_layout);
if(result != VK_SUCCESS) {
return result;
}
result = create_ui_text_pipeline(device, render_pass, context->ui_descriptor_layout, context->font_layout, &context->ui_pipeline_text, &context->ui_compute_text);
if(result != VK_SUCCESS) {
return result;
}
return VK_SUCCESS;
}