1329 lines
43 KiB
C
1329 lines
43 KiB
C
#include "ui.h"
|
|
#include "gpu.h"
|
|
|
|
#include "cglm/affine.h"
|
|
#include "cglm/mat4.h"
|
|
#include "stdio.h"
|
|
#include "stdlib.h"
|
|
#include "string.h"
|
|
#include "vk_mem_alloc.h"
|
|
#include "vulkan/vulkan_core.h"
|
|
#include "spng.h"
|
|
#include <sys/param.h>
|
|
|
|
VkResult create_ui_pipeline(
|
|
VkDevice device,
|
|
VkRenderPass render_pass,
|
|
VkDescriptorSetLayout samplers_layout,
|
|
VkDescriptorSetLayout textures_layout,
|
|
GraphicsPipeline* pipeline,
|
|
ComputePipeline* compute) {
|
|
VkResult result;
|
|
VkShaderModule compute_shader = load_shader_file("shader/string.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,
|
|
.size = 16,
|
|
};
|
|
|
|
VkPipelineLayoutCreateInfo compute_layout_info = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
.pushConstantRangeCount = 1,
|
|
.pPushConstantRanges = &push_constant,
|
|
};
|
|
|
|
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/ui.vert.spv", device);
|
|
if(vert_shader == VK_NULL_HANDLE) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
VkShaderModule frag_shader = load_shader_file("shader/ui.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",
|
|
},
|
|
};
|
|
|
|
VkPipelineVertexInputStateCreateInfo input_info = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
};
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
|
.primitiveRestartEnable = VK_FALSE,
|
|
};
|
|
|
|
VkPushConstantRange push_constants[] = {
|
|
{
|
|
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
.size = 16,
|
|
.offset = 0,
|
|
},
|
|
};
|
|
|
|
VkDescriptorSetLayout set_layouts[] = {samplers_layout, textures_layout, samplers_layout, textures_layout};
|
|
|
|
VkPipelineLayoutCreateInfo layout_info = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
.pushConstantRangeCount = sizeof(push_constants)/sizeof(VkPushConstantRange),
|
|
.pPushConstantRanges = push_constants,
|
|
.setLayoutCount = sizeof(set_layouts)/sizeof(VkDescriptorSetLayout),
|
|
.pSetLayouts = set_layouts,
|
|
};
|
|
|
|
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,
|
|
};
|
|
|
|
// For blending, calculate the new color as new_color = (1-a)(old_color)+(a)(fragment_color)
|
|
// Don't store an alpha value in the color attachment, since it's not used
|
|
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_ZERO,
|
|
.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,
|
|
};
|
|
|
|
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 = sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo),
|
|
.pStages = shader_stages,
|
|
.pVertexInputState = &input_info,
|
|
.pInputAssemblyState = &input_assembly_info,
|
|
.pViewportState = &viewport_state,
|
|
.pRasterizationState = &raster_info,
|
|
.pColorBlendState = &color_blend_info,
|
|
.pDynamicState = &dynamic_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_container(
|
|
ContainerInput* container,
|
|
RenderContext* gpu,
|
|
UIContext* context) {
|
|
uint32_t index = 0xFFFFFFFF;
|
|
for(uint32_t i = 0; i < context->max_containers; i++) {
|
|
if(context->containers[i].id == 0x00000000) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
if(index == 0xFFFFFFFF) {
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
}
|
|
|
|
VkResult result;
|
|
for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUContainer), &context->containers[index].container[i], &context->containers[index].container_memory[i]));
|
|
context->containers[index].address[i] = buffer_address(gpu->device, context->containers[index].container[i]);
|
|
}
|
|
|
|
context->containers[index].data.offset[0] = container->offset[0];
|
|
context->containers[index].data.offset[1] = container->offset[1];
|
|
context->containers[index].data.size[0] = container->size[0];
|
|
context->containers[index].data.size[1] = container->size[1];
|
|
context->containers[index].data.anchor = container->anchor;
|
|
context->containers[index].data.context = context->address;
|
|
add_transfers(&context->containers[index].data, context->containers[index].container, 0, sizeof(GPUContainer), gpu);
|
|
|
|
context->containers[index].id = container->id;
|
|
context->containers[index].layers = malloc(sizeof(Layer)*container->layer_count);
|
|
for(uint32_t i = 0; i < container->layer_count; i++) {
|
|
VK_RESULT(create_layer(i, &container->layers[i], gpu, &context->containers[index]));
|
|
}
|
|
|
|
context->containers[index].layer_count = container->layer_count;
|
|
context->containers[index].callbacks = malloc(sizeof(UICallbacks)*container->callback_count);
|
|
context->containers[index].callback_count = container->callback_count;
|
|
memcpy(
|
|
context->containers[index].callbacks,
|
|
container->callbacks,
|
|
sizeof(UICallbacks)*container->callback_count);
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult create_layer(
|
|
uint32_t index,
|
|
LayerInput* input,
|
|
RenderContext* gpu,
|
|
Container* container) {
|
|
VkResult result;
|
|
|
|
uint32_t max_strings = (input->num_strings > input->max_strings) ? input->num_strings : input->max_strings;
|
|
uint32_t max_codes = (input->num_codes > input->max_codes) ? input->num_codes : input->max_codes;
|
|
uint32_t max_drawables = (input->num_drawables > input->max_drawables) ? input->num_drawables : input->max_drawables;
|
|
|
|
if(max_strings > 0) {
|
|
container->layers[index].strings_buffer = malloc(sizeof(GPUString)*max_strings);
|
|
}
|
|
|
|
if(max_drawables > 0) {
|
|
container->layers[index].drawables_buffer = malloc(sizeof(GPUDrawable)*max_drawables);
|
|
}
|
|
|
|
if(max_codes > 0) {
|
|
container->layers[index].codes_buffer = malloc(sizeof(uint32_t)*max_codes);
|
|
}
|
|
|
|
for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
|
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, sizeof(GPULayer), &container->layers[index].layer[i], &container->layers[index].layer_memory[i]));
|
|
if(max_strings > 0) {
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUString)*max_strings, &container->layers[index].strings[i], &container->layers[index].strings_memory[i]));
|
|
VkDeviceAddress address = buffer_address(gpu->device, container->layers[index].strings[i]);
|
|
add_transfer(
|
|
&address,
|
|
container->layers[index].layer[i],
|
|
offsetof(GPULayer, strings),
|
|
sizeof(VkDeviceAddress),
|
|
i,
|
|
gpu);
|
|
}
|
|
if(max_codes + max_drawables > 0) {
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUDrawable)*(max_drawables + max_codes), &container->layers[index].drawables[i], &container->layers[index].drawables_memory[i]));
|
|
VkDeviceAddress address = buffer_address(gpu->device, container->layers[index].drawables[i]);
|
|
add_transfer(
|
|
&address,
|
|
container->layers[index].layer[i],
|
|
offsetof(GPULayer, drawables),
|
|
sizeof(VkDeviceAddress),
|
|
i,
|
|
gpu);
|
|
}
|
|
if(max_codes > 0) {
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(uint32_t)*max_codes, &container->layers[index].codes[i], &container->layers[index].codes_memory[i]));
|
|
VkDeviceAddress address = buffer_address(gpu->device, container->layers[index].codes[i]);
|
|
add_transfer(
|
|
&address,
|
|
container->layers[index].layer[i],
|
|
offsetof(GPULayer, codes),
|
|
sizeof(VkDeviceAddress),
|
|
i,
|
|
gpu);
|
|
}
|
|
|
|
container->layers[index].address[i] = buffer_address(gpu->device, container->layers[index].layer[i]);
|
|
}
|
|
|
|
for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
|
add_transfer(
|
|
&container->address[i],
|
|
container->layers[index].layer[i],
|
|
offsetof(GPULayer, container),
|
|
sizeof(VkDeviceAddress),
|
|
i,
|
|
gpu);
|
|
}
|
|
|
|
container->layers[index].data.draw.first_vertex = 0;
|
|
container->layers[index].data.draw.vertex_count = 6;
|
|
container->layers[index].data.draw.first_instance = 0;
|
|
container->layers[index].data.draw.instance_count = 0;
|
|
|
|
container->layers[index].data.dispatch_strings.x = max_strings;
|
|
container->layers[index].data.dispatch_strings.y = 1;
|
|
container->layers[index].data.dispatch_strings.z = 1;
|
|
|
|
container->layers[index].data.max_drawables = max_drawables + max_codes;
|
|
container->layers[index].data.max_strings = max_strings;
|
|
container->layers[index].data.max_codes = max_codes;
|
|
container->layers[index].data.num_drawables = max_drawables;
|
|
add_transfers(&container->layers[index].data, container->layers[index].layer, 0, sizeof(GPULayer)-4*sizeof(VkDeviceAddress), gpu);
|
|
|
|
if(input->num_strings > 0) {
|
|
memcpy(container->layers[index].strings_buffer, input->strings, sizeof(GPUString)*input->num_strings);
|
|
add_transfers(container->layers[index].strings_buffer, container->layers[index].strings, 0, sizeof(GPUString)*input->num_strings, gpu);
|
|
}
|
|
|
|
if(input->num_drawables > 0) {
|
|
memcpy(container->layers[index].drawables_buffer, input->drawables, sizeof(GPUDrawable)*input->num_drawables);
|
|
add_transfers(container->layers[index].drawables_buffer, container->layers[index].drawables, 0, sizeof(GPUDrawable)*input->num_drawables, gpu);
|
|
}
|
|
|
|
if(input->num_codes > 0) {
|
|
memcpy(container->layers[index].codes_buffer, input->codes, sizeof(uint32_t)*input->num_codes);
|
|
add_transfers(container->layers[index].codes_buffer, container->layers[index].codes, 0, sizeof(uint32_t)*input->num_codes, gpu);
|
|
}
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult load_texture(
|
|
const char* png_path,
|
|
RenderContext* gpu,
|
|
UIContext* context,
|
|
uint32_t* index) {
|
|
*index = 0xFFFFFFFF;
|
|
for(uint32_t i = 0; i < context->max_textures; i++) {
|
|
if(context->texture_slots[i].path == NULL) {
|
|
context->texture_slots[i].path = malloc(strlen(png_path) + 1);
|
|
memcpy(context->texture_slots[i].path, png_path, strlen(png_path) + 1);
|
|
*index = i;
|
|
break;
|
|
}
|
|
}
|
|
if(*index == 0xFFFFFFFF) {
|
|
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
|
|
}
|
|
|
|
spng_ctx* spng = spng_ctx_new(0);
|
|
if(spng == NULL) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
FILE* png_file = fopen(png_path, "rb");
|
|
if(png_file == NULL) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
fseek(png_file, 0, SEEK_END);
|
|
int error = fseek(png_file, 0, SEEK_END);
|
|
if(error != 0) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
size_t png_size = ftell(png_file);
|
|
|
|
error = fseek(png_file, 0, SEEK_SET);
|
|
if(error != 0) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
|
|
void* png = malloc(png_size);
|
|
if(png == NULL) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
size_t read = fread(png, 1, png_size, png_file);
|
|
if(read != png_size) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
fclose(png_file);
|
|
|
|
spng_set_png_buffer(spng, png, png_size);
|
|
size_t out_size;
|
|
spng_decoded_image_size(spng, SPNG_FMT_RGBA8, &out_size);
|
|
struct spng_ihdr ihdr;
|
|
spng_get_ihdr(spng, &ihdr);
|
|
void* image_buffer = malloc(out_size);
|
|
if(image_buffer == NULL) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
spng_decode_image(spng, image_buffer, out_size, SPNG_FMT_RGBA8, 0);
|
|
|
|
VkResult result;
|
|
|
|
VkImageCreateInfo image_info = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
.extent = {
|
|
.width = ihdr.width,
|
|
.height = ihdr.height,
|
|
.depth = 1,
|
|
},
|
|
.mipLevels = 1,
|
|
.arrayLayers = 1,
|
|
.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 memory_info = {
|
|
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
|
|
};
|
|
|
|
VkBuffer transfer;
|
|
VmaAllocation transfer_memory;
|
|
void* mapped;
|
|
|
|
VK_RESULT(vmaCreateImage(gpu->allocator, &image_info, &memory_info, &context->texture_slots[*index].image, &context->texture_slots[*index].image_memory, NULL));
|
|
VK_RESULT(create_transfer_buffer(gpu->allocator, sizeof(uint32_t)*ihdr.width*ihdr.height, &transfer, &transfer_memory, &mapped));
|
|
memcpy(mapped, image_buffer, sizeof(uint32_t)*ihdr.height*ihdr.width);
|
|
VkCommandBuffer command_buffer = command_begin_single(gpu->device, gpu->transfer_pool);
|
|
|
|
VkImageMemoryBarrier first_barrier = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.image = context->texture_slots[*index].image,
|
|
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
.subresourceRange = {
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.levelCount = 1,
|
|
.layerCount = 1,
|
|
},
|
|
.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 = {
|
|
.imageExtent = {
|
|
.width = ihdr.width,
|
|
.height = ihdr.height,
|
|
.depth = 1,
|
|
},
|
|
.imageSubresource = {
|
|
.layerCount = 1,
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
},
|
|
};
|
|
vkCmdCopyBufferToImage(command_buffer, transfer, context->texture_slots[*index].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
|
|
|
|
VkImageMemoryBarrier second_barrier = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.image = context->texture_slots[*index].image,
|
|
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
.subresourceRange = {
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.layerCount = 1,
|
|
.levelCount = 1,
|
|
},
|
|
.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);
|
|
|
|
|
|
VK_RESULT(command_end_single(gpu->device, command_buffer, gpu->transfer_pool, gpu->transfer_queue));
|
|
destroy_transfer_buffer(gpu->allocator, transfer, transfer_memory);
|
|
free(image_buffer);
|
|
free(png);
|
|
spng_ctx_free(spng);
|
|
|
|
VkImageViewCreateInfo view_info = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
.image = context->texture_slots[*index].image,
|
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
|
.format = VK_FORMAT_R8G8B8A8_SRGB,
|
|
.subresourceRange = {
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.layerCount = 1,
|
|
.levelCount = 1,
|
|
},
|
|
};
|
|
|
|
VK_RESULT(vkCreateImageView(gpu->device, &view_info, NULL, &context->texture_slots[*index].view));
|
|
|
|
VkSamplerCreateInfo sampler_info = {
|
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
|
.magFilter = VK_FILTER_LINEAR,
|
|
.minFilter = VK_FILTER_LINEAR,
|
|
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
};
|
|
|
|
VK_RESULT(vkCreateSampler(gpu->device, &sampler_info, NULL, &context->texture_slots[*index].sampler));
|
|
|
|
VkDescriptorImageInfo desc_sampler_info = {
|
|
.sampler = context->texture_slots[*index].sampler,
|
|
};
|
|
|
|
VkDescriptorImageInfo desc_image_info = {
|
|
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
.imageView = context->texture_slots[*index].view,
|
|
};
|
|
|
|
VkWriteDescriptorSet desc_writes[] = {
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
.dstSet = context->textures,
|
|
.dstBinding = 0,
|
|
.dstArrayElement = *index,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
|
.descriptorCount = 1,
|
|
.pImageInfo = &desc_image_info,
|
|
},
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
.dstSet = context->samplers,
|
|
.dstBinding = 0,
|
|
.dstArrayElement = *index,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
|
|
.descriptorCount = 1,
|
|
.pImageInfo = &desc_sampler_info,
|
|
},
|
|
};
|
|
|
|
vkUpdateDescriptorSets(gpu->device, sizeof(desc_writes)/sizeof(VkWriteDescriptorSet), desc_writes, 0, NULL);
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult load_font(
|
|
uint32_t index,
|
|
const char* ttf_file,
|
|
uint32_t size,
|
|
VkBool32 antialias,
|
|
RenderContext* gpu,
|
|
UIContext* context){
|
|
FT_Face face;
|
|
|
|
int error;
|
|
error = FT_New_Face(context->freetype, ttf_file, 0, &face);
|
|
if(error != FT_Err_Ok) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
context->fonts[index].family = malloc(strlen(face->family_name)+1);
|
|
memcpy(&context->fonts[index].family, face->family_name, strlen(face->family_name)+1);
|
|
|
|
context->fonts[index].style = malloc(strlen(face->style_name)+1);
|
|
memcpy(&context->fonts[index].style, face->style_name, strlen(face->style_name)+1);
|
|
|
|
|
|
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);
|
|
GPUSymbol* symbols = malloc(sizeof(GPUSymbol)*face->num_glyphs);
|
|
GPUFont info;
|
|
|
|
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;
|
|
} else if(c > 255) {
|
|
c = FT_Get_Next_Char(face, c, &glyph_index);
|
|
continue;
|
|
}
|
|
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;
|
|
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);
|
|
}
|
|
|
|
info.width = max_width;
|
|
info.height = max_height;
|
|
info.num_symbols = symbol_count;
|
|
context->fonts[index].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);
|
|
context->fonts[index].charmap = malloc(sizeof(uint32_t)*symbol_count);
|
|
memcpy(context->fonts[index].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, context->fonts[index].charmap[i]);
|
|
FT_Load_Glyph(face, glyph_index, load_flags);
|
|
symbols[i].width = (float)face->glyph->bitmap.width/(float)max_width;
|
|
symbols[i].height = (float)face->glyph->bitmap.rows/(float)max_height;
|
|
if(face->units_per_EM == 2048) {
|
|
symbols[i].advance[0] = (float)face->glyph->advance.x*30/(float)face->units_per_EM /(float)max_width;
|
|
symbols[i].advance[1] = (float)face->glyph->advance.y*30/(float)face->units_per_EM /(float)max_height;
|
|
} else {
|
|
symbols[i].advance[0] = (float)face->glyph->advance.x*16/(float)face->units_per_EM /(float)max_width;
|
|
symbols[i].advance[1] = (float)face->glyph->advance.y*16/(float)face->units_per_EM /(float)max_height;
|
|
}
|
|
symbols[i].left = (float)face->glyph->bitmap_left/(float)max_width;
|
|
symbols[i].top = (float)face->glyph->bitmap_top/(float)max_height;
|
|
for(uint32_t y = 0; y < face->glyph->bitmap.rows; y++) {
|
|
for(uint32_t x = 0; x < face->glyph->bitmap.width; x++) {
|
|
uint64_t level = face->glyph->bitmap.buffer[y*face->glyph->bitmap.width+x];
|
|
level = ((level * 0xFFFFFFFF) / 256);
|
|
images[max_width*max_height*i + max_width*y + x] = level;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
error = FT_Done_Face(face);
|
|
if(error != FT_Err_Ok) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
VkResult result;
|
|
VK_RESULT(create_storage_buffer(gpu->allocator, 0, sizeof(GPUSymbol)*info.num_symbols, &context->fonts[index].symbols, &context->fonts[index].symbol_memory));
|
|
|
|
VkImageCreateInfo image_info = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
.extent.depth = 1,
|
|
.extent.width = info.width,
|
|
.extent.height = info.height,
|
|
.mipLevels = 1,
|
|
.arrayLayers = info.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,
|
|
};
|
|
|
|
VK_RESULT(vmaCreateImage(
|
|
gpu->allocator,
|
|
&image_info,
|
|
&image_memory_info,
|
|
&context->fonts[index].image,
|
|
&context->fonts[index].image_memory,
|
|
NULL));
|
|
|
|
VkBuffer transfer;
|
|
VmaAllocation transfer_memory;
|
|
void* mapped;
|
|
VK_RESULT(create_transfer_buffer(
|
|
gpu->allocator,
|
|
sizeof(GPUFont) + image_size*info.num_symbols + sizeof(GPUSymbol)*info.num_symbols,
|
|
&transfer,
|
|
&transfer_memory,
|
|
&mapped));
|
|
|
|
info.symbol_list = buffer_address(gpu->device, context->fonts[index].symbols);
|
|
|
|
memcpy(mapped, images, image_size*info.num_symbols);
|
|
memcpy(mapped + image_size*info.num_symbols, &info, sizeof(GPUFont));
|
|
memcpy(mapped + image_size*info.num_symbols + sizeof(GPUFont), symbols, sizeof(GPUSymbol)*info.num_symbols);
|
|
|
|
free(images);
|
|
free(symbols);
|
|
|
|
VkCommandBuffer command_buffer = command_begin_single(gpu->device, gpu->transfer_pool);
|
|
command_copy_buffer(
|
|
command_buffer,
|
|
transfer,
|
|
context->font_infos,
|
|
image_size*info.num_symbols,
|
|
index*sizeof(GPUFont),
|
|
sizeof(GPUFont));
|
|
command_copy_buffer(command_buffer,
|
|
transfer,
|
|
context->fonts[index].symbols,
|
|
image_size*info.num_symbols + sizeof(GPUFont),
|
|
0,
|
|
sizeof(GPUSymbol)*info.num_symbols);
|
|
|
|
VkImageMemoryBarrier first_barrier = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.image = context->fonts[index].image,
|
|
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.subresourceRange.levelCount = 1,
|
|
.subresourceRange.layerCount = info.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 = info.num_symbols,
|
|
.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.imageExtent = image_info.extent,
|
|
};
|
|
vkCmdCopyBufferToImage(command_buffer, transfer, context->fonts[index].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
|
|
|
|
VkImageMemoryBarrier second_barrier = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
.image = context->fonts[index].image,
|
|
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.subresourceRange.levelCount = 1,
|
|
.subresourceRange.layerCount = info.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);
|
|
|
|
VK_RESULT(command_end_single(gpu->device, command_buffer, gpu->transfer_pool, gpu->transfer_queue));
|
|
destroy_transfer_buffer(gpu->allocator, transfer, transfer_memory);
|
|
|
|
VkImageViewCreateInfo view_info = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
.image = context->fonts[index].image,
|
|
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
|
|
.format = VK_FORMAT_R8G8B8A8_SRGB,
|
|
.subresourceRange = {
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.layerCount = info.num_symbols,
|
|
.baseMipLevel = 0,
|
|
.levelCount = 1,
|
|
.baseArrayLayer = 0,
|
|
},
|
|
};
|
|
VK_RESULT(vkCreateImageView(gpu->device, &view_info, NULL, &context->fonts[index].view))
|
|
|
|
VkSamplerCreateInfo sampler_info = {
|
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
|
.magFilter = VK_FILTER_NEAREST,
|
|
.minFilter = VK_FILTER_NEAREST,
|
|
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
};
|
|
VK_RESULT(vkCreateSampler(gpu->device, &sampler_info, NULL, &context->fonts[index].sampler));
|
|
|
|
VkDescriptorImageInfo desc_sampler_info = {
|
|
.sampler = context->fonts[index].sampler,
|
|
};
|
|
|
|
VkDescriptorImageInfo desc_texture_info = {
|
|
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
.imageView = context->fonts[index].view,
|
|
};
|
|
|
|
VkWriteDescriptorSet descriptor_writes[] = {
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
.dstSet = context->font_textures,
|
|
.dstBinding = 0,
|
|
.dstArrayElement = index,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
|
.descriptorCount = 1,
|
|
.pImageInfo = &desc_texture_info,
|
|
},
|
|
{
|
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
.dstSet = context->font_samplers,
|
|
.dstBinding = 0,
|
|
.dstArrayElement = index,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
|
|
.descriptorCount = 1,
|
|
.pImageInfo = &desc_sampler_info,
|
|
},
|
|
};
|
|
|
|
vkUpdateDescriptorSets(gpu->device, sizeof(descriptor_writes)/sizeof(VkWriteDescriptorSet), descriptor_writes, 0, NULL);
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult create_ui_descriptor(
|
|
VkDevice device,
|
|
uint32_t max_fonts,
|
|
uint32_t max_textures,
|
|
VkDescriptorSetLayout* sampler_layout,
|
|
VkDescriptorSetLayout* texture_layout,
|
|
VkDescriptorSet* samplers,
|
|
VkDescriptorSet* textures,
|
|
VkDescriptorSet* font_samplers,
|
|
VkDescriptorSet* font_textures,
|
|
VkDescriptorPool* font_pool,
|
|
VkDescriptorPool* texture_pool) {
|
|
VkResult result;
|
|
VkDescriptorSetLayoutBinding sampler_bindings[] = {
|
|
{
|
|
.binding = 0,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
|
|
.descriptorCount = max_fonts,
|
|
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
},
|
|
};
|
|
|
|
VkDescriptorBindingFlags bindless_flags[] = {
|
|
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
|
|
};
|
|
|
|
VkDescriptorSetLayoutBindingFlagsCreateInfo sampler_binding_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
|
.bindingCount = sizeof(sampler_bindings)/sizeof(VkDescriptorSetLayoutBinding),
|
|
.pBindingFlags = bindless_flags,
|
|
};
|
|
|
|
VkDescriptorSetLayoutCreateInfo sampler_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
|
.pBindings = sampler_bindings,
|
|
.bindingCount = sizeof(sampler_bindings)/sizeof(VkDescriptorSetLayoutBinding),
|
|
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
|
|
.pNext = &sampler_binding_info,
|
|
};
|
|
|
|
VK_RESULT(vkCreateDescriptorSetLayout(device, &sampler_info, NULL, sampler_layout));
|
|
|
|
VkDescriptorSetLayoutBinding texture_bindings[] = {
|
|
{
|
|
.binding = 0,
|
|
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
|
.descriptorCount = max_fonts,
|
|
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
},
|
|
};
|
|
|
|
VkDescriptorSetLayoutBindingFlagsCreateInfo texture_bindings_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
|
.bindingCount = sizeof(texture_bindings)/sizeof(VkDescriptorSetLayoutBinding),
|
|
.pBindingFlags = bindless_flags,
|
|
};
|
|
|
|
VkDescriptorSetLayoutCreateInfo texture_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
|
.pBindings = texture_bindings,
|
|
.bindingCount = sizeof(texture_bindings)/sizeof(VkDescriptorSetLayoutBinding),
|
|
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
|
|
.pNext = &texture_bindings_info,
|
|
};
|
|
|
|
VK_RESULT(vkCreateDescriptorSetLayout(device, &texture_info, NULL, texture_layout));
|
|
|
|
VkDescriptorPoolSize font_pool_sizes[] = {
|
|
{
|
|
.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
|
.descriptorCount = max_fonts,
|
|
},
|
|
{
|
|
.type = VK_DESCRIPTOR_TYPE_SAMPLER,
|
|
.descriptorCount = max_fonts,
|
|
},
|
|
};
|
|
VkDescriptorPoolCreateInfo font_pool_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
|
.pPoolSizes = font_pool_sizes,
|
|
.poolSizeCount = 2,
|
|
.maxSets = 2,
|
|
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
|
|
};
|
|
|
|
result = vkCreateDescriptorPool(device, &font_pool_info, NULL, font_pool);
|
|
if(result != VK_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
uint32_t max_font_binding = max_fonts - 1;
|
|
VkDescriptorSetVariableDescriptorCountAllocateInfo count_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
|
|
.descriptorSetCount = 1,
|
|
.pDescriptorCounts = &max_font_binding,
|
|
};
|
|
|
|
VkDescriptorSetAllocateInfo allocate_font_samplers = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
.pSetLayouts = sampler_layout,
|
|
.descriptorSetCount = 1,
|
|
.descriptorPool = *font_pool,
|
|
.pNext = &count_info,
|
|
};
|
|
|
|
VkDescriptorSetAllocateInfo allocate_font_textures = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
.pSetLayouts = texture_layout,
|
|
.descriptorSetCount = 1,
|
|
.descriptorPool = *font_pool,
|
|
.pNext = &count_info,
|
|
};
|
|
|
|
result = vkAllocateDescriptorSets(device, &allocate_font_samplers, font_samplers);
|
|
if(result != VK_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = vkAllocateDescriptorSets(device, &allocate_font_textures, font_textures);
|
|
if(result != VK_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
VkDescriptorPoolSize texture_pool_sizes[] = {
|
|
{
|
|
.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
|
|
.descriptorCount = max_textures,
|
|
},
|
|
{
|
|
.type = VK_DESCRIPTOR_TYPE_SAMPLER,
|
|
.descriptorCount = max_textures,
|
|
},
|
|
};
|
|
VkDescriptorPoolCreateInfo texture_pool_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
|
.pPoolSizes = texture_pool_sizes,
|
|
.poolSizeCount = 2,
|
|
.maxSets = 2,
|
|
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
|
|
};
|
|
|
|
result = vkCreateDescriptorPool(device, &texture_pool_info, NULL, texture_pool);
|
|
if(result != VK_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
uint32_t max_texture_binding = max_textures - 1;
|
|
VkDescriptorSetVariableDescriptorCountAllocateInfo texture_count_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO,
|
|
.descriptorSetCount = 1,
|
|
.pDescriptorCounts = &max_texture_binding,
|
|
};
|
|
|
|
VkDescriptorSetAllocateInfo allocate_samplers = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
.pSetLayouts = sampler_layout,
|
|
.descriptorSetCount = 1,
|
|
.descriptorPool = *texture_pool,
|
|
.pNext = &texture_count_info,
|
|
};
|
|
|
|
VkDescriptorSetAllocateInfo allocate_textures = {
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
.pSetLayouts = texture_layout,
|
|
.descriptorSetCount = 1,
|
|
.descriptorPool = *texture_pool,
|
|
.pNext = &texture_count_info,
|
|
};
|
|
|
|
result = vkAllocateDescriptorSets(device, &allocate_samplers, samplers);
|
|
if(result != VK_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = vkAllocateDescriptorSets(device, &allocate_textures, textures);
|
|
if(result != VK_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult update_ui_context_resolution(
|
|
UIContext* ui,
|
|
RenderContext* gpu) {
|
|
ui->data.screen[0] = gpu->window_scale[0] / gpu->swapchain_extent.width;
|
|
ui->data.screen[1] = gpu->window_scale[1] / gpu->swapchain_extent.height;
|
|
ui->data.extent[0] = gpu->swapchain_extent.width / gpu->window_scale[0];
|
|
ui->data.extent[1] = gpu->swapchain_extent.height / gpu->window_scale[1];
|
|
ui->data.scale[0] = gpu->window_scale[0];
|
|
ui->data.scale[1] = gpu->window_scale[1];
|
|
|
|
return add_transfer(
|
|
&ui->data.screen[0],
|
|
ui->context,
|
|
offsetof(GPUUIContext, screen),
|
|
sizeof(GPUUIContext) - offsetof(GPUUIContext, screen),
|
|
gpu->current_frame, gpu);
|
|
}
|
|
|
|
VkResult create_ui_context(
|
|
uint32_t max_fonts,
|
|
uint32_t max_textures,
|
|
uint32_t max_containers,
|
|
RenderContext* gpu,
|
|
UIContext* context) {
|
|
|
|
VkResult result;
|
|
|
|
if(FT_Init_FreeType(&context->freetype) != FT_Err_Ok) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
VK_RESULT(create_storage_buffer(
|
|
gpu->allocator,
|
|
0,
|
|
sizeof(GPUUIContext),
|
|
&context->context,
|
|
&context->context_memory));
|
|
VK_RESULT(create_storage_buffer(
|
|
gpu->allocator,
|
|
0,
|
|
sizeof(GPUFont)*max_fonts,
|
|
&context->font_infos,
|
|
&context->font_infos_memory));
|
|
|
|
context->data.font_infos = buffer_address(gpu->device, context->font_infos);
|
|
context->data.screen[0] = gpu->window_scale[0] / gpu->swapchain_extent.width;
|
|
context->data.screen[1] = gpu->window_scale[1] / gpu->swapchain_extent.height;
|
|
context->data.extent[0] = gpu->swapchain_extent.width / gpu->window_scale[0];
|
|
context->data.extent[1] = gpu->swapchain_extent.height / gpu->window_scale[1];
|
|
context->data.scale[0] = gpu->window_scale[0];
|
|
context->data.scale[1] = gpu->window_scale[1];
|
|
|
|
VK_RESULT(add_transfer(
|
|
&context->data,
|
|
context->context,
|
|
0,
|
|
sizeof(GPUUIContext),
|
|
gpu->current_frame,
|
|
gpu));
|
|
context->address = buffer_address(gpu->device, context->context);
|
|
|
|
VK_RESULT(create_ui_descriptor(
|
|
gpu->device,
|
|
max_fonts,
|
|
max_textures,
|
|
&context->samplers_layout,
|
|
&context->textures_layout,
|
|
&context->samplers,
|
|
&context->textures,
|
|
&context->font_samplers,
|
|
&context->font_textures,
|
|
&context->fonts_pool,
|
|
&context->textures_pool));
|
|
|
|
VK_RESULT(create_ui_pipeline(
|
|
gpu->device,
|
|
gpu->render_pass,
|
|
context->samplers_layout,
|
|
context->textures_layout,
|
|
&context->pipeline,
|
|
&context->string_pipeline));
|
|
|
|
context->max_textures = max_textures;
|
|
context->max_fonts = max_fonts;
|
|
context->max_containers = max_containers;
|
|
|
|
context->texture_slots = malloc(max_textures*sizeof(Texture));
|
|
memset(context->texture_slots, 0, max_textures*sizeof(Texture));
|
|
context->fonts = malloc(max_fonts*sizeof(Font));
|
|
memset(context->fonts, 0, max_fonts*sizeof(Font));
|
|
context->containers = malloc(max_containers*sizeof(Container));
|
|
memset(context->containers, 0, max_containers*sizeof(Container));
|
|
|
|
VK_RESULT(load_font(0, "fonts/runescape.ttf", 16, VK_FALSE, gpu, context));
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
VkResult map_string(
|
|
const char * text,
|
|
uint32_t* buffer,
|
|
uint32_t offset,
|
|
uint32_t font,
|
|
UIContext* context) {
|
|
size_t i = 0;
|
|
memset((uint8_t*)buffer + offset, 0, strlen(text));
|
|
while(text[i] != '\0') {
|
|
uint32_t mapped = 0xFFFFFFFF;
|
|
for(uint32_t j = 0; j < context->fonts[font].num_symbols; j++) {
|
|
if(context->fonts[font].charmap[j] == (uint32_t)text[i]) {
|
|
mapped = j;
|
|
break;
|
|
}
|
|
}
|
|
if(mapped == 0xFFFFFFFF) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
buffer[(i + offset)/4] += mapped << ((i + offset) % 4)*8;
|
|
i += 1;
|
|
}
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
Container* context_container(uint32_t container_id, UIContext* ui) {
|
|
uint32_t c;
|
|
Container* ret = NULL;
|
|
for(c = 0; c < ui->max_containers; c++) {
|
|
if(ui->containers[c].id == container_id) {
|
|
ret = &ui->containers[c];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
GPUDrawable* container_drawable(uint32_t element_id, Container* container) {
|
|
if(container == NULL || element_id == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
for(uint32_t l = 0; l < container->layer_count; l++) {
|
|
for(uint32_t d = 0; d < container->layers[l].data.num_drawables; d++) {
|
|
if(container->layers[l].drawables_buffer[d].id == element_id) {
|
|
return &container->layers[l].drawables_buffer[d];
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VkResult update_ui_string(
|
|
char* string,
|
|
uint32_t container_id,
|
|
uint32_t layer_index,
|
|
uint32_t string_index,
|
|
UIContext* ui,
|
|
RenderContext* gpu) {
|
|
VkResult result;
|
|
|
|
|
|
Container* container = context_container(container_id, ui);
|
|
if(container == NULL) {
|
|
return VK_ERROR_UNKNOWN;
|
|
}
|
|
|
|
Layer* layer = &container->layers[layer_index];
|
|
layer->strings_buffer[string_index].length = strlen(string);
|
|
if(strlen(string) > 0) {
|
|
VK_RESULT(map_string(
|
|
string,
|
|
layer->codes_buffer,
|
|
layer->strings_buffer[string_index].offset,
|
|
layer->strings_buffer[string_index].font,
|
|
ui));
|
|
VK_RESULT(add_transfers(
|
|
(uint8_t*)layer->codes_buffer + layer->strings_buffer[string_index].offset,
|
|
layer->codes,
|
|
layer->strings_buffer[string_index].offset,
|
|
strlen(string),
|
|
gpu));
|
|
}
|
|
VK_RESULT(add_transfers(
|
|
&layer->strings_buffer[string_index].length,
|
|
layer->strings,
|
|
sizeof(GPUString)*string_index + offsetof(GPUString, length),
|
|
sizeof(uint32_t),
|
|
gpu));
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
bool ui_contains(
|
|
double point[2],
|
|
vec2 container_offset,
|
|
vec2 container_size,
|
|
GPUDrawable* rect,
|
|
vec2 position) {
|
|
vec2 pos = {
|
|
container_offset[0] + rect->pos[0],
|
|
container_offset[1] + rect->pos[1],
|
|
};
|
|
if((point[0] >= pos[0] && point[0] <= pos[0] + rect->size[0]) &&
|
|
(point[1] >= pos[1] && point[1] <= pos[1] + rect->size[1]) &&
|
|
(point[0] >= container_offset[0] && point[0] <= container_offset[0] + container_size[0]) &&
|
|
(point[1] >= container_offset[1] && point[1] <= container_offset[1] + container_size[1])){
|
|
position[0] = (point[0] - pos[0])/rect->size[0];
|
|
position[1] = (point[1] - pos[1])/rect->size[1];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ui_intersect(
|
|
double cursor[2],
|
|
RenderContext* gpu,
|
|
UIContext* ui,
|
|
uint32_t* container,
|
|
uint32_t* element,
|
|
vec2 position) {
|
|
for(uint32_t c = 0; c < ui->max_containers; c++) {
|
|
if(ui->containers[c].id == 0x00000000) {
|
|
continue;
|
|
}
|
|
|
|
vec2 container_offset = {ui->containers[c].data.offset[0], ui->containers[c].data.offset[1]};
|
|
anchor_offset(gpu, &ui->containers[c], container_offset);
|
|
|
|
for(uint32_t l = 0; l < ui->containers[c].layer_count; l++) {
|
|
for(uint32_t d = 0; d < ui->containers[c].layers[l].data.num_drawables; d++) {
|
|
if(ui->containers[c].layers[l].drawables_buffer[d].id == 0x00000000) {
|
|
continue;
|
|
}
|
|
if(ui_contains(
|
|
cursor,
|
|
container_offset,
|
|
ui->containers[c].data.size,
|
|
&ui->containers[c].layers[l].drawables_buffer[d],
|
|
position)) {
|
|
*container = ui->containers[c].id;
|
|
*element = ui->containers[c].layers[l].drawables_buffer[d].id;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void clear_active_element(UIContext* ui) {
|
|
ui->active_callbacks = NULL;
|
|
ui->active_element = 0;
|
|
ui->active_container = 0;
|
|
}
|
|
|
|
void set_active_element(uint32_t container, uint32_t element, UIContext* ui) {
|
|
ui->active_container = container;
|
|
ui->active_element = element;
|
|
ui->active_callbacks = NULL;
|
|
Container* container_ptr = context_container(container, ui);
|
|
if(container_ptr == NULL) {
|
|
fprintf(stderr, "set_active_element passed invalid container id");
|
|
return;
|
|
}
|
|
|
|
for(uint32_t c = 0; c < container_ptr->callback_count; container_ptr++) {
|
|
if(container_ptr->callbacks[c].element == element) {
|
|
ui->active_callbacks = &container_ptr->callbacks[c];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void anchor_offset(RenderContext* gpu, Container* container, vec2 offset) {
|
|
vec2 screen = {
|
|
gpu->swapchain_extent.width/gpu->window_scale[0],
|
|
gpu->swapchain_extent.height/gpu->window_scale[1],
|
|
};
|
|
|
|
switch(container->data.anchor) {
|
|
case ANCHOR_TOP_LEFT:
|
|
break;
|
|
case ANCHOR_TOP_RIGHT:
|
|
offset[0] += screen[0] - container->data.size[0];
|
|
break;
|
|
case ANCHOR_BOTTOM_LEFT:
|
|
offset[1] += screen[1] - container->data.size[1];
|
|
break;
|
|
case ANCHOR_BOTTOM_RIGHT:
|
|
offset[0] += screen[0] - container->data.size[0];
|
|
offset[1] += screen[1] - container->data.size[1];
|
|
break;
|
|
}
|
|
}
|