do not edit — generated by btf.
git.druid.rocksindexmilitantprogrammerastral_canvassrc/render/render.c

src/render/render.c


#include "render/render.h"
#include "render/font.h"
#include "lib/error.h"
 
#include "hud.vert.spv.h"
#include "hud.frag.spv.h"
#include "text.vert.spv.h"
#include "text.frag.spv.h"
 
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
 
static uint32_t packRgba(uint32_t argb)
{
    uint32_t alpha = (argb >> 24) & 0xffu;
    uint32_t red = (argb >> 16) & 0xffu;
    uint32_t green = (argb >> 8) & 0xffu;
    uint32_t blue = argb & 0xffu;
    if (alpha == 0)
        alpha = 0xffu;
    return red | (green << 8) | (blue << 16) | (alpha << 24);
}
 
static VkShaderModule createShader(VkDevice device, const uint32_t *code, size_t bytes)
{
    VkShaderModule shaderModule = VK_NULL_HANDLE;
    VkShaderModuleCreateInfo shaderCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
        .codeSize = bytes,
        .pCode = code,
    };
    if (vkCreateShaderModule(device, &shaderCreateInfo, NULL, &shaderModule) != VK_SUCCESS)
        return VK_NULL_HANDLE;
    return shaderModule;
}
 
static int makePipeline(Renderer *renderer, VkPipeline *outPipeline,
                        const uint32_t *vertexCode, size_t vertexSize,
                        const uint32_t *fragmentCode, size_t fragmentSize,
                        const VkPipelineVertexInputStateCreateInfo *vertexInput,
                        bool blend)
{
    VkDevice device = renderer->vulkan.device;
    VkShaderModule vertexModule = createShader(device, vertexCode, vertexSize);
    VkShaderModule fragmentModule = createShader(device, fragmentCode, fragmentSize);
    if (!vertexModule || !fragmentModule) {
        fprintf(stderr, "vulkan: shader module failed\n");
        if (vertexModule)
            vkDestroyShaderModule(device, vertexModule, NULL);
        if (fragmentModule)
            vkDestroyShaderModule(device, fragmentModule, NULL);
        return -1;
    }
    VkPipelineShaderStageCreateInfo stages[2] = {
        {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
            .stage = VK_SHADER_STAGE_VERTEX_BIT,
            .module = vertexModule,
            .pName = "main",
        },
        {
            .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
            .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
            .module = fragmentModule,
            .pName = "main",
        },
    };
    VkPipelineVertexInputStateCreateInfo emptyVertexInput = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
    };
    VkPipelineInputAssemblyStateCreateInfo inputAssembly = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
        .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
    };
    VkPipelineViewportStateCreateInfo viewportState = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
        .viewportCount = 1,
        .scissorCount = 1,
    };
    VkPipelineRasterizationStateCreateInfo rasterization = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
        .polygonMode = VK_POLYGON_MODE_FILL,
        .cullMode = VK_CULL_MODE_NONE,
        .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
        .lineWidth = 1.0f,
    };
    VkPipelineMultisampleStateCreateInfo multisample = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
        .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
    };
    VkPipelineColorBlendAttachmentState blendAttachment = {
        .blendEnable = blend ? VK_TRUE : VK_FALSE,
        .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_ONE_MINUS_SRC_ALPHA,
        .alphaBlendOp = VK_BLEND_OP_ADD,
        .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
                          VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
    };
    VkPipelineColorBlendStateCreateInfo colorBlend = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
        .attachmentCount = 1,
        .pAttachments = &blendAttachment,
    };
    VkDynamicState dynamicStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
    VkPipelineDynamicStateCreateInfo dynamicState = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
        .dynamicStateCount = 2,
        .pDynamicStates = dynamicStates,
    };
    VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
        .stageCount = 2,
        .pStages = stages,
        .pVertexInputState = vertexInput ? vertexInput : &emptyVertexInput,
        .pInputAssemblyState = &inputAssembly,
        .pViewportState = &viewportState,
        .pRasterizationState = &rasterization,
        .pMultisampleState = &multisample,
        .pColorBlendState = &colorBlend,
        .pDynamicState = &dynamicState,
        .layout = renderer->pipelineLayout,
        .renderPass = renderer->vulkan.renderPass,
        .subpass = 0,
    };
    VkResult result = vkCreateGraphicsPipelines(
        device, VK_NULL_HANDLE, 1, &graphicsPipelineCreateInfo, NULL, outPipeline);
    vkDestroyShaderModule(device, vertexModule, NULL);
    vkDestroyShaderModule(device, fragmentModule, NULL);
    if (result != VK_SUCCESS) {
        fprintf(stderr, "vulkan: vkCreateGraphicsPipelines failed (%d)\n", (int)result);
        return -1;
    }
    return 0;
}
 
static void destroyPipelines(Renderer *renderer)
{
    VkDevice device = renderer->vulkan.device;
    if (!device)
        return;
    if (renderer->hudPipeline) {
        vkDestroyPipeline(device, renderer->hudPipeline, NULL);
        renderer->hudPipeline = VK_NULL_HANDLE;
    }
    if (renderer->textPipeline) {
        vkDestroyPipeline(device, renderer->textPipeline, NULL);
        renderer->textPipeline = VK_NULL_HANDLE;
    }
}
 
static int createPipelines(Renderer *renderer)
{
    destroyPipelines(renderer);
    if (makePipeline(renderer, &renderer->hudPipeline, hud_vert_spv, sizeof(hud_vert_spv),
                     hud_frag_spv, sizeof(hud_frag_spv), NULL, false) < 0)
        return -1;
 
    VkVertexInputBindingDescription bindingDescription = {
        .binding = 0,
        .stride = sizeof(GlyphVertex),
        .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE,
    };
    VkVertexInputAttributeDescription attributes[3] = {
        { .location = 0, .binding = 0, .format = VK_FORMAT_R32G32_SFLOAT,
          .offset = offsetof(GlyphVertex, x) },
        { .location = 1, .binding = 0, .format = VK_FORMAT_R8G8B8A8_UNORM,
          .offset = offsetof(GlyphVertex, rgba) },
        { .location = 2, .binding = 0, .format = VK_FORMAT_R32_UINT,
          .offset = offsetof(GlyphVertex, character) },
    };
    VkPipelineVertexInputStateCreateInfo vertexInput = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
        .vertexBindingDescriptionCount = 1,
        .pVertexBindingDescriptions = &bindingDescription,
        .vertexAttributeDescriptionCount = 3,
        .pVertexAttributeDescriptions = attributes,
    };
    if (makePipeline(renderer, &renderer->textPipeline, text_vert_spv, sizeof(text_vert_spv),
                     text_frag_spv, sizeof(text_frag_spv), &vertexInput, true) < 0)
        return -1;
    return 0;
}
 
static int recreateForResize(Renderer *renderer, Platform *platform)
{
    if (vk_recreate_swapchain(&renderer->vulkan, platform) < 0)
        return -1;
    return createPipelines(renderer);
}
 
static int createFontAtlas(Renderer *renderer)
{
    unsigned char pixels[FONT_ATLAS_W * FONT_ATLAS_H];
    memset(pixels, 0, sizeof(pixels));
    for (int glyphIndex = 0; glyphIndex < FONT_COUNT; glyphIndex++) {
        const unsigned char *glyph = font_glyph((unsigned char)(FONT_FIRST + glyphIndex));
        int column = glyphIndex % FONT_ATLAS_COLS;
        int row = glyphIndex / FONT_ATLAS_COLS;
        for (int y = 0; y < FONT_GLYPH_H; y++) {
            unsigned char bits = glyph[y];
            for (int x = 0; x < FONT_GLYPH_W; x++) {
                if (bits & (1u << x))
                    pixels[(row * FONT_GLYPH_H + y) * FONT_ATLAS_W + column * FONT_GLYPH_W + x] = 255;
            }
        }
    }
 
    VkContext *vulkan = &renderer->vulkan;
    VkBuffer stagingBuffer = VK_NULL_HANDLE;
    VkDeviceMemory stagingMemory = VK_NULL_HANDLE;
    void *mapped = NULL;
    if (vk_create_host_buffer(vulkan, sizeof(pixels), VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
                              &stagingBuffer, &stagingMemory, &mapped) < 0)
        return -1;
    memcpy(mapped, pixels, sizeof(pixels));
 
    VkImageCreateInfo imageCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
        .imageType = VK_IMAGE_TYPE_2D,
        .format = VK_FORMAT_R8_UNORM,
        .extent = { FONT_ATLAS_W, FONT_ATLAS_H, 1 },
        .mipLevels = 1,
        .arrayLayers = 1,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        .tiling = VK_IMAGE_TILING_OPTIMAL,
        .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
        .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
    };
    VK_TRY(vkCreateImage(vulkan->device, &imageCreateInfo, NULL, &renderer->fontImage));
    VkMemoryRequirements memoryRequirements;
    vkGetImageMemoryRequirements(vulkan->device, renderer->fontImage, &memoryRequirements);
    int memoryTypeIndex = vk_find_memory_type(
        vulkan->physicalDevice, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
    if (memoryTypeIndex < 0)
        memoryTypeIndex = vk_find_memory_type(vulkan->physicalDevice, memoryRequirements.memoryTypeBits, 0);
    if (memoryTypeIndex < 0)
        return -1;
    VkMemoryAllocateInfo allocateInfo = {
        .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
        .allocationSize = memoryRequirements.size,
        .memoryTypeIndex = (uint32_t)memoryTypeIndex,
    };
    VK_TRY(vkAllocateMemory(vulkan->device, &allocateInfo, NULL, &renderer->fontMemory));
    VK_TRY(vkBindImageMemory(vulkan->device, renderer->fontImage, renderer->fontMemory, 0));
 
    VkImageViewCreateInfo imageViewCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
        .image = renderer->fontImage,
        .viewType = VK_IMAGE_VIEW_TYPE_2D,
        .format = VK_FORMAT_R8_UNORM,
        .subresourceRange = {
            .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
            .levelCount = 1,
            .layerCount = 1,
        },
    };
    VK_TRY(vkCreateImageView(vulkan->device, &imageViewCreateInfo, NULL, &renderer->fontView));
 
    VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
        .commandPool = vulkan->commandPool,
        .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
        .commandBufferCount = 1,
    };
    VkCommandBuffer commandBuffer;
    VK_TRY(vkAllocateCommandBuffers(vulkan->device, &commandBufferAllocateInfo, &commandBuffer));
    VkCommandBufferBeginInfo beginInfo = {
        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
        .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
    };
    VK_TRY(vkBeginCommandBuffer(commandBuffer, &beginInfo));
    vk_image_barrier(commandBuffer, renderer->fontImage, VK_IMAGE_LAYOUT_UNDEFINED,
                     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                     0, VK_ACCESS_TRANSFER_WRITE_BIT,
                     VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
    VkBufferImageCopy copy = {
        .imageSubresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .layerCount = 1 },
        .imageExtent = { FONT_ATLAS_W, FONT_ATLAS_H, 1 },
    };
    vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, renderer->fontImage,
                           VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
    vk_image_barrier(commandBuffer, renderer->fontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                     VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
                     VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
                     VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
    VK_TRY(vkEndCommandBuffer(commandBuffer));
    VkSubmitInfo submitInfo = {
        .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
        .commandBufferCount = 1,
        .pCommandBuffers = &commandBuffer,
    };
    VK_TRY(vkQueueSubmit(vulkan->graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE));
    VK_TRY(vkQueueWaitIdle(vulkan->graphicsQueue));
    vkFreeCommandBuffers(vulkan->device, vulkan->commandPool, 1, &commandBuffer);
    vkUnmapMemory(vulkan->device, stagingMemory);
    vkDestroyBuffer(vulkan->device, stagingBuffer, NULL);
    vkFreeMemory(vulkan->device, stagingMemory, NULL);
    return 0;
}
 
static int createRenderResources(Renderer *renderer)
{
    VkContext *vulkan = &renderer->vulkan;
    VkSamplerCreateInfo samplerCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
        .magFilter = VK_FILTER_NEAREST,
        .minFilter = VK_FILTER_NEAREST,
        .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
        .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
        .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
        .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
        .maxLod = 0.0f,
    };
    VK_TRY(vkCreateSampler(vulkan->device, &samplerCreateInfo, NULL, &renderer->sampler));
 
    VkDescriptorSetLayoutBinding bindings[2] = {
        {
            .binding = 0,
            .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
            .descriptorCount = 1,
            .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
        },
        {
            .binding = 1,
            .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
            .descriptorCount = 1,
            .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
        },
    };
    VkDescriptorSetLayoutCreateInfo setLayoutCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
        .bindingCount = 2,
        .pBindings = bindings,
    };
    VK_TRY(vkCreateDescriptorSetLayout(
        vulkan->device, &setLayoutCreateInfo, NULL, &renderer->descriptorSetLayout));
 
    VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
        .setLayoutCount = 1,
        .pSetLayouts = &renderer->descriptorSetLayout,
    };
    VK_TRY(vkCreatePipelineLayout(
        vulkan->device, &pipelineLayoutCreateInfo, NULL, &renderer->pipelineLayout));
 
    VkDescriptorPoolSize poolSizes[2] = {
        { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = VK_MAX_FRAMES },
        { .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = VK_MAX_FRAMES },
    };
    VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
        .maxSets = VK_MAX_FRAMES,
        .poolSizeCount = 2,
        .pPoolSizes = poolSizes,
    };
    VK_TRY(vkCreateDescriptorPool(
        vulkan->device, &descriptorPoolCreateInfo, NULL, &renderer->descriptorPool));
 
    if (createFontAtlas(renderer) < 0)
        return -1;
    if (createPipelines(renderer) < 0)
        return -1;
 
    VkDescriptorSetLayout layouts[VK_MAX_FRAMES];
    for (int i = 0; i < VK_MAX_FRAMES; i++)
        layouts[i] = renderer->descriptorSetLayout;
    VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
        .descriptorPool = renderer->descriptorPool,
        .descriptorSetCount = VK_MAX_FRAMES,
        .pSetLayouts = layouts,
    };
    VkDescriptorSet descriptorSets[VK_MAX_FRAMES];
    VK_TRY(vkAllocateDescriptorSets(vulkan->device, &descriptorSetAllocateInfo, descriptorSets));
 
    for (int i = 0; i < VK_MAX_FRAMES; i++) {
        renderer->frames[i].descriptorSet = descriptorSets[i];
        if (vk_create_host_buffer(vulkan, sizeof(SceneUniform), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
                                  &renderer->frames[i].uniformBuffer,
                                  &renderer->frames[i].uniformBufferMemory,
                                  &renderer->frames[i].uniformBufferMapped) < 0)
            return -1;
        if (vk_create_host_buffer(vulkan, sizeof(GlyphVertex) * HUD_MAX_GLYPHS,
                                  VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
                                  &renderer->frames[i].glyphBuffer,
                                  &renderer->frames[i].glyphBufferMemory,
                                  &renderer->frames[i].glyphBufferMapped) < 0)
            return -1;
 
        VkDescriptorBufferInfo uniformBufferInfo = {
            .buffer = renderer->frames[i].uniformBuffer,
            .range = sizeof(SceneUniform),
        };
        VkDescriptorImageInfo imageInfo = {
            .sampler = renderer->sampler,
            .imageView = renderer->fontView,
            .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
        };
        VkWriteDescriptorSet writes[2] = {
            {
                .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                .dstSet = renderer->frames[i].descriptorSet,
                .dstBinding = 0,
                .descriptorCount = 1,
                .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
                .pBufferInfo = &uniformBufferInfo,
            },
            {
                .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                .dstSet = renderer->frames[i].descriptorSet,
                .dstBinding = 1,
                .descriptorCount = 1,
                .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
                .pImageInfo = &imageInfo,
            },
        };
        vkUpdateDescriptorSets(vulkan->device, 2, writes, 0, NULL);
    }
    return 0;
}
 
 
 
 
static int presentVulkan(Renderer *renderer, Platform *platform, const Hud *hud)
{
    VkContext *vulkan = &renderer->vulkan;
    if (!vulkan->ready || !hud)
        return 0;
 
    uint32_t wantedWidth = (uint32_t)(platform->bufferWidth > 0 ? platform->bufferWidth : 1);
    uint32_t wantedHeight = (uint32_t)(platform->bufferHeight > 0 ? platform->bufferHeight : 1);
    VkSurfaceCapabilitiesKHR surfaceCapabilities;
    if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
            vulkan->physicalDevice, vulkan->surface, &surfaceCapabilities) == VK_SUCCESS &&
        surfaceCapabilities.currentExtent.width != UINT32_MAX) {
        wantedWidth = surfaceCapabilities.currentExtent.width;
        wantedHeight = surfaceCapabilities.currentExtent.height;
    }
    if (wantedWidth != vulkan->extent.width || wantedHeight != vulkan->extent.height) {
        if (recreateForResize(renderer, platform) < 0)
            return -1;
    }
 
    VkFrame *frame = &vulkan->frames[vulkan->frameIndex];
    RenderFrame *renderFrame = &renderer->frames[vulkan->frameIndex];
    VK_TRY(vkWaitForFences(vulkan->device, 1, &frame->inFlight, VK_TRUE, UINT64_MAX));
 
    uint32_t imageIndex = 0;
    VkResult acquireResult = vkAcquireNextImageKHR(
        vulkan->device, vulkan->swapchain, UINT64_MAX,
        frame->imageAvailable, VK_NULL_HANDLE, &imageIndex);
    if (acquireResult == VK_ERROR_OUT_OF_DATE_KHR)
        return recreateForResize(renderer, platform);
    if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) {
        fprintf(stderr, "vulkan: vkAcquireNextImageKHR failed (%d)\n", (int)acquireResult);
        return -1;
    }
    if (imageIndex >= vulkan->swapchainImageCount)
        return -1;
 
    if (vulkan->swapchainBusyFences[imageIndex] != VK_NULL_HANDLE)
        VK_TRY(vkWaitForFences(vulkan->device, 1,
                               &vulkan->swapchainBusyFences[imageIndex], VK_TRUE, UINT64_MAX));
    vulkan->swapchainBusyFences[imageIndex] = frame->inFlight;
    VK_TRY(vkResetFences(vulkan->device, 1, &frame->inFlight));
 
 
    SceneUniform uniform = {
        .resolution = { (float)vulkan->extent.width, (float)vulkan->extent.height },
        .scale = platform->scale,
        .hudHeight = hud->hudHeight,
        .cellWidth = hud->cellWidth,
        .cellHeight = hud->cellHeight,
        .columns = hud->columns,
        .rows = hud->rows,
        .hoverColumn = hud->hoverColumn,
        .hoverRow = hud->hoverRow,
        .hover = hud->hover,
    };
    memcpy(uniform.background, hud->background, sizeof(uniform.background));
    memcpy(uniform.grid, hud->grid, sizeof(uniform.grid));
    memcpy(uniform.hoverColor, hud->hoverColor, sizeof(uniform.hoverColor));
    memcpy(uniform.bar, hud->bar, sizeof(uniform.bar));
    memcpy(renderFrame->uniformBufferMapped, &uniform, sizeof(uniform));
 
    int glyphCount = hud->glyphCount;
    if (glyphCount < 0)
        glyphCount = 0;
    if (glyphCount > HUD_MAX_GLYPHS)
        glyphCount = HUD_MAX_GLYPHS;
    GlyphVertex *glyphVertices = (GlyphVertex *)renderFrame->glyphBufferMapped;
    for (int i = 0; i < glyphCount; i++) {
        glyphVertices[i].x = hud->glyphs[i].x;
        glyphVertices[i].y = hud->glyphs[i].y;
        glyphVertices[i].rgba = packRgba(hud->glyphs[i].color);
        glyphVertices[i].character = hud->glyphs[i].character;
    }
 
    VK_TRY(vkResetCommandBuffer(frame->commandBuffer, 0));
    VkCommandBufferBeginInfo beginInfo = {
        .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
        .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
    };
    VK_TRY(vkBeginCommandBuffer(frame->commandBuffer, &beginInfo));
 
    VkClearValue clearValue = { .color = { { 0.0f, 0.0f, 0.0f, 1.0f } } };
    VkRenderPassBeginInfo renderPassBeginInfo = {
        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
        .renderPass = vulkan->renderPass,
        .framebuffer = vulkan->swapchainFramebuffers[imageIndex],
        .renderArea = { .extent = vulkan->extent },
        .clearValueCount = 1,
        .pClearValues = &clearValue,
    };
    vkCmdBeginRenderPass(frame->commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
 
    VkViewport viewport = {
        .width = (float)vulkan->extent.width,
        .height = (float)vulkan->extent.height,
        .minDepth = 0.0f,
        .maxDepth = 1.0f,
    };
    VkRect2D scissor = { .extent = vulkan->extent };
    vkCmdSetViewport(frame->commandBuffer, 0, 1, &viewport);
    vkCmdSetScissor(frame->commandBuffer, 0, 1, &scissor);
 
    vkCmdBindPipeline(frame->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->hudPipeline);
    vkCmdBindDescriptorSets(frame->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
                            renderer->pipelineLayout, 0, 1, &renderFrame->descriptorSet, 0, NULL);
    vkCmdDraw(frame->commandBuffer, 3, 1, 0, 0);
 
    if (glyphCount > 0) {
        VkDeviceSize offset = 0;
        vkCmdBindPipeline(frame->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->textPipeline);
        vkCmdBindDescriptorSets(frame->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                renderer->pipelineLayout, 0, 1, &renderFrame->descriptorSet, 0, NULL);
        vkCmdBindVertexBuffers(frame->commandBuffer, 0, 1, &renderFrame->glyphBuffer, &offset);
        vkCmdDraw(frame->commandBuffer, 6, (uint32_t)glyphCount, 0, 0);
    }
 
    vkCmdEndRenderPass(frame->commandBuffer);
    VK_TRY(vkEndCommandBuffer(frame->commandBuffer));
 
    VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    VkSubmitInfo submitInfo = {
        .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
        .waitSemaphoreCount = 1,
        .pWaitSemaphores = &frame->imageAvailable,
        .pWaitDstStageMask = &waitStage,
        .commandBufferCount = 1,
        .pCommandBuffers = &frame->commandBuffer,
        .signalSemaphoreCount = 1,
        .pSignalSemaphores = &frame->renderFinished,
    };
    VK_TRY(vkQueueSubmit(vulkan->graphicsQueue, 1, &submitInfo, frame->inFlight));
 
    VkPresentInfoKHR presentInfo = {
        .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
        .waitSemaphoreCount = 1,
        .pWaitSemaphores = &frame->renderFinished,
        .swapchainCount = 1,
        .pSwapchains = &vulkan->swapchain,
        .pImageIndices = &imageIndex,
    };
    VkResult presentResult = vkQueuePresentKHR(vulkan->presentQueue, &presentInfo);
    if (presentResult == VK_ERROR_OUT_OF_DATE_KHR || presentResult == VK_SUBOPTIMAL_KHR) {
        if (recreateForResize(renderer, platform) < 0)
            return -1;
    } else if (presentResult != VK_SUCCESS) {
        fprintf(stderr, "vulkan: vkQueuePresentKHR failed (%d)\n", (int)presentResult);
        return -1;
    }
 
    vulkan->frameIndex = (vulkan->frameIndex + 1) % VK_MAX_FRAMES;
    return 0;
}
 
int rendererInit(Renderer *renderer, Platform *platform)
{
    memset(renderer, 0, sizeof(*renderer));
    if (vk_init(&renderer->vulkan, platform) != 0) {
        canvasErrorSet("Vulkan instance failed within lib");
        return -1;
    }
    if (createRenderResources(renderer) != 0) {
        canvasErrorSet("Vulkan pipeline failed within lib");
        vk_destroy(&renderer->vulkan);
        memset(renderer, 0, sizeof(*renderer));
        platform->gpuPresent = false;
        return -1;
    }
    renderer->useVulkan = true;
    return 0;
}
 
int rendererFrame(Renderer *renderer, Platform *platform, const Hud *hud)
{
    return presentVulkan(renderer, platform, hud);
}
 
void rendererDestroy(Renderer *renderer)
{
    if (!renderer)
        return;
    if (renderer->useVulkan && renderer->vulkan.device) {
        vkDeviceWaitIdle(renderer->vulkan.device);
        destroyPipelines(renderer);
        for (int i = 0; i < VK_MAX_FRAMES; i++) {
            if (renderer->frames[i].glyphBufferMapped && renderer->frames[i].glyphBufferMemory)
                vkUnmapMemory(renderer->vulkan.device, renderer->frames[i].glyphBufferMemory);
            if (renderer->frames[i].uniformBufferMapped && renderer->frames[i].uniformBufferMemory)
                vkUnmapMemory(renderer->vulkan.device, renderer->frames[i].uniformBufferMemory);
            if (renderer->frames[i].glyphBuffer)
                vkDestroyBuffer(renderer->vulkan.device, renderer->frames[i].glyphBuffer, NULL);
            if (renderer->frames[i].glyphBufferMemory)
                vkFreeMemory(renderer->vulkan.device, renderer->frames[i].glyphBufferMemory, NULL);
            if (renderer->frames[i].uniformBuffer)
                vkDestroyBuffer(renderer->vulkan.device, renderer->frames[i].uniformBuffer, NULL);
            if (renderer->frames[i].uniformBufferMemory)
                vkFreeMemory(renderer->vulkan.device, renderer->frames[i].uniformBufferMemory, NULL);
        }
        if (renderer->fontView)
            vkDestroyImageView(renderer->vulkan.device, renderer->fontView, NULL);
        if (renderer->fontImage)
            vkDestroyImage(renderer->vulkan.device, renderer->fontImage, NULL);
        if (renderer->fontMemory)
            vkFreeMemory(renderer->vulkan.device, renderer->fontMemory, NULL);
        if (renderer->descriptorPool)
            vkDestroyDescriptorPool(renderer->vulkan.device, renderer->descriptorPool, NULL);
        if (renderer->pipelineLayout)
            vkDestroyPipelineLayout(renderer->vulkan.device, renderer->pipelineLayout, NULL);
        if (renderer->descriptorSetLayout)
            vkDestroyDescriptorSetLayout(renderer->vulkan.device, renderer->descriptorSetLayout, NULL);
        if (renderer->sampler)
            vkDestroySampler(renderer->vulkan.device, renderer->sampler, NULL);
        vk_destroy(&renderer->vulkan);
    }
    memset(renderer, 0, sizeof(*renderer));
}
 
void rendererFillClear(Platform *platform, const Style *theme, Mode mode, Hud *hud)
{
    float alpha;
 
    memset(hud, 0, sizeof(*hud));
    hud->scale = platform && platform->scale > 0 ? (float)platform->scale : 1.0f;
    if (!theme)
        return;
    alpha = theme->backgroundColor.a / 255.0f;
    if (mode != MODE_COMPLEX)
        alpha = 1.0f;
    hud->background[0] = theme->backgroundColor.r / 255.0f;
    hud->background[1] = theme->backgroundColor.g / 255.0f;
    hud->background[2] = theme->backgroundColor.b / 255.0f;
    hud->background[3] = alpha;
}
 
powered by btf.