summaryrefslogtreecommitdiff
path: root/src/vulkan.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vulkan.cpp')
-rw-r--r--src/vulkan.cpp618
1 files changed, 618 insertions, 0 deletions
diff --git a/src/vulkan.cpp b/src/vulkan.cpp
new file mode 100644
index 0000000..20fa449
--- /dev/null
+++ b/src/vulkan.cpp
@@ -0,0 +1,618 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "vulkan.h"
+#include "gauges.h"
+#include "spv.h"
+
+pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
+
+INSTANCE_DATA g_instances[MAX_INSTANCES];
+DEVICE_DATA * g_devices[MAX_DEVICES];
+
+INSTANCE_DATA* vk_find_instance( void* key ) {
+ for( I32 i = 0; i < MAX_INSTANCES; i++ )
+ if( g_instances[i].key == key )
+ return &g_instances[i];
+ return 0;
+}
+
+DEVICE_DATA* vk_find_device( void* key ) {
+ for( I32 i = 0; i < MAX_DEVICES; i++ )
+ if( g_devices[i] && g_devices[i]->key == key )
+ return g_devices[i];
+ return 0;
+}
+
+SWAP_DATA* vk_find_swap( DEVICE_DATA* d, VkSwapchainKHR sc ) {
+ for( I32 i = 0; i < MAX_SWAPS; i++ )
+ if( d->swaps[i] && d->swaps[i]->sc == sc )
+ return d->swaps[i];
+ return 0;
+}
+
+INSTANCE_DATA* vk_instance_register( VkInstance inst, PFN_vkGetInstanceProcAddr gipa ) {
+ pthread_mutex_lock( &g_lock );
+ defer( pthread_mutex_unlock( &g_lock ) );
+
+ for( I32 i = 0; i < MAX_INSTANCES; i++ ) {
+ if( g_instances[i].key )
+ continue;
+
+ INSTANCE_DATA* id = &g_instances[i];
+ id->key = GET_KEY( inst );
+ id->instance = inst;
+ id->gipa = gipa;
+ id->destroy_instance = ( decltype( id->destroy_instance ) )gipa( inst, "vkDestroyInstance" );
+ id->get_memory_properties = ( decltype( id->get_memory_properties ) )gipa( inst, "vkGetPhysicalDeviceMemoryProperties" );
+ id->get_queue_family_properties = ( decltype( id->get_queue_family_properties ) )gipa( inst, "vkGetPhysicalDeviceQueueFamilyProperties" );
+ return id;
+ }
+ return 0;
+}
+
+void vk_instance_forget( VkInstance inst ) {
+ pthread_mutex_lock( &g_lock );
+ defer( pthread_mutex_unlock( &g_lock ) );
+
+ INSTANCE_DATA* id = vk_find_instance( GET_KEY( inst ) );
+ if( id )
+ memset( id, 0, sizeof(* id ) );
+}
+
+void vk_device_register( DEVICE_DATA* d ) {
+ pthread_mutex_lock( &g_lock );
+ defer( pthread_mutex_unlock( &g_lock ) );
+
+ for( I32 i = 0; i < MAX_DEVICES; i++ )
+ if( !g_devices[i] ) {
+ g_devices[i] = d;
+ return;
+ }
+}
+
+void vk_device_unregister( DEVICE_DATA* d ) {
+ pthread_mutex_lock( &g_lock );
+ defer( pthread_mutex_unlock( &g_lock ) );
+
+ for( I32 i = 0; i < MAX_DEVICES; i++ )
+ if( g_devices[i] == d )
+ g_devices[i] = 0;
+}
+
+void vk_device_add_queue( DEVICE_DATA* d, VkQueue q, U32 family ) {
+ pthread_mutex_lock( &g_lock );
+ defer( pthread_mutex_unlock( &g_lock ) );
+
+ if( d->nqueues < MAX_QUEUES ) {
+ d->queues[d->nqueues].q = q;
+ d->queues[d->nqueues].family = family;
+ d->nqueues++;
+ }
+}
+
+#define RESOLVE( field, name ) \
+ d->field = (PFN_##name)gdpa( dev, #name )
+
+void vk_resolve_device_procs( DEVICE_DATA* d, VkDevice dev, PFN_vkGetDeviceProcAddr gdpa ) {
+ RESOLVE( destroy_device, vkDestroyDevice );
+ RESOLVE( get_device_queue, vkGetDeviceQueue );
+ RESOLVE( get_device_queue2, vkGetDeviceQueue2 );
+ RESOLVE( create_swapchain, vkCreateSwapchainKHR );
+ RESOLVE( destroy_swapchain, vkDestroySwapchainKHR );
+ RESOLVE( get_swapchain_images, vkGetSwapchainImagesKHR );
+ RESOLVE( queue_present, vkQueuePresentKHR );
+ RESOLVE( queue_submit, vkQueueSubmit );
+ RESOLVE( device_wait_idle, vkDeviceWaitIdle );
+ RESOLVE( create_image_view, vkCreateImageView );
+ RESOLVE( destroy_image_view, vkDestroyImageView );
+ RESOLVE( create_render_pass, vkCreateRenderPass );
+ RESOLVE( destroy_render_pass, vkDestroyRenderPass );
+ RESOLVE( create_framebuffer, vkCreateFramebuffer );
+ RESOLVE( destroy_framebuffer, vkDestroyFramebuffer );
+ RESOLVE( create_shader_module, vkCreateShaderModule );
+ RESOLVE( destroy_shader_module, vkDestroyShaderModule );
+ RESOLVE( create_pipeline_layout, vkCreatePipelineLayout );
+ RESOLVE( destroy_pipeline_layout, vkDestroyPipelineLayout );
+ RESOLVE( create_graphics_pipelines, vkCreateGraphicsPipelines );
+ RESOLVE( destroy_pipeline, vkDestroyPipeline );
+ RESOLVE( create_command_pool, vkCreateCommandPool );
+ RESOLVE( destroy_command_pool, vkDestroyCommandPool );
+ RESOLVE( allocate_command_buffers, vkAllocateCommandBuffers );
+ RESOLVE( begin_command_buffer, vkBeginCommandBuffer );
+ RESOLVE( end_command_buffer, vkEndCommandBuffer );
+ RESOLVE( cmd_begin_render_pass, vkCmdBeginRenderPass );
+ RESOLVE( cmd_end_render_pass, vkCmdEndRenderPass );
+ RESOLVE( cmd_bind_pipeline, vkCmdBindPipeline );
+ RESOLVE( cmd_bind_vertex_buffers, vkCmdBindVertexBuffers );
+ RESOLVE( cmd_push_constants, vkCmdPushConstants );
+ RESOLVE( cmd_set_viewport, vkCmdSetViewport );
+ RESOLVE( cmd_set_scissor, vkCmdSetScissor );
+ RESOLVE( cmd_draw, vkCmdDraw );
+ RESOLVE( create_semaphore, vkCreateSemaphore );
+ RESOLVE( destroy_semaphore, vkDestroySemaphore );
+ RESOLVE( create_fence, vkCreateFence );
+ RESOLVE( destroy_fence, vkDestroyFence );
+ RESOLVE( wait_for_fences, vkWaitForFences );
+ RESOLVE( reset_fences, vkResetFences );
+ RESOLVE( create_buffer, vkCreateBuffer );
+ RESOLVE( destroy_buffer, vkDestroyBuffer );
+ RESOLVE( get_buffer_memory_requirements, vkGetBufferMemoryRequirements );
+ RESOLVE( allocate_memory, vkAllocateMemory );
+ RESOLVE( free_memory, vkFreeMemory );
+ RESOLVE( bind_buffer_memory, vkBindBufferMemory );
+ RESOLVE( map_memory, vkMapMemory );
+ RESOLVE( unmap_memory, vkUnmapMemory );
+}
+
+
+STAT vk_swap_create_render_pass( DEVICE_DATA* d, SWAP_DATA* s ) {
+ VkAttachmentDescription att = {
+ .format = s->fmt,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ };
+ VkAttachmentReference ref = {
+ .attachment = 0,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ };
+ VkSubpassDescription sub = {
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = &ref,
+ };
+ VkRenderPassCreateInfo rpci = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ .attachmentCount = 1,
+ .pAttachments = &att,
+ .subpassCount = 1,
+ .pSubpasses = &sub,
+ };
+ if( d->create_render_pass( d->dev, &rpci, 0, &s->rp ) != VK_SUCCESS )
+ return STAT_ERR;
+ return STAT_OK;
+}
+
+STAT vk_swap_create_framebuffers( DEVICE_DATA* d, SWAP_DATA* s ) {
+ for( U32 i = 0; i < s->nimages; i++ ) {
+ VkImageViewCreateInfo vci = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = s->imgs[i],
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = s->fmt,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .levelCount = 1,
+ .layerCount = 1,
+ },
+ };
+ if( d->create_image_view( d->dev, &vci, 0, &s->views[i] ) != VK_SUCCESS )
+ return STAT_ERR;
+
+ VkFramebufferCreateInfo fci = {
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .renderPass = s->rp,
+ .attachmentCount = 1,
+ .pAttachments = &s->views[i],
+ .width = s->extent.width,
+ .height = s->extent.height,
+ .layers = 1,
+ };
+ if( d->create_framebuffer( d->dev, &fci, 0, &s->fbs[i] ) != VK_SUCCESS )
+ return STAT_ERR;
+ }
+ return STAT_OK;
+}
+
+STAT vk_swap_create_shaders( DEVICE_DATA* d, SWAP_DATA* s ) {
+ VkShaderModuleCreateInfo smci = {
+ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
+ .codeSize = gauge_vert_spv_len,
+ .pCode = (const U32*)gauge_vert_spv,
+ };
+ if( d->create_shader_module( d->dev, &smci, 0, &s->vs ) != VK_SUCCESS )
+ return STAT_ERR;
+
+ smci.codeSize = gauge_frag_spv_len;
+ smci.pCode = (const U32*)gauge_frag_spv;
+ if( d->create_shader_module( d->dev, &smci, 0, &s->fs ) != VK_SUCCESS )
+ return STAT_ERR;
+
+ VkPushConstantRange pcr = {
+ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
+ .offset = 0,
+ .size = 2 * sizeof(F32),
+ };
+ VkPipelineLayoutCreateInfo plci = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ .pushConstantRangeCount = 1,
+ .pPushConstantRanges = &pcr,
+ };
+ if( d->create_pipeline_layout( d->dev, &plci, 0, &s->pl ) != VK_SUCCESS )
+ return STAT_ERR;
+ return STAT_OK;
+}
+
+// jesus fucking christ
+STAT vk_swap_create_pipeline( DEVICE_DATA* d, SWAP_DATA* s ) {
+ VkPipelineShaderStageCreateInfo stages[2] = {
+ {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .stage = VK_SHADER_STAGE_VERTEX_BIT,
+ .module = s->vs,
+ .pName = "main"
+ },
+ {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
+ .module = s->fs,
+ .pName = "main"
+ },
+ };
+
+ VkVertexInputBindingDescription bind = {
+ .binding = 0,
+ .stride = sizeof( VERTEX ),
+ .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
+ };
+ VkVertexInputAttributeDescription attrs[2] = {
+ {
+ .location = 0, .binding = 0,
+ .format = VK_FORMAT_R32G32_SFLOAT, .offset = 0
+ },
+ {
+ .location = 1, .binding = 0,
+ .format = VK_FORMAT_R8G8B8A8_UNORM, .offset = 8
+ },
+ };
+ VkPipelineVertexInputStateCreateInfo vin = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = 1,
+ .pVertexBindingDescriptions = &bind,
+ .vertexAttributeDescriptionCount = 2,
+ .pVertexAttributeDescriptions = attrs,
+ };
+ VkPipelineInputAssemblyStateCreateInfo ia = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+ .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+ };
+ VkPipelineViewportStateCreateInfo vp = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .viewportCount = 1,
+ .scissorCount = 1,
+ };
+ VkPipelineRasterizationStateCreateInfo rs = {
+ .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 ms = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
+ };
+ VkPipelineColorBlendAttachmentState ba = {
+ .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_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 cb = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+ .attachmentCount = 1,
+ .pAttachments = &ba,
+ };
+ VkDynamicState dyns[2] = {
+ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
+ };
+ VkPipelineDynamicStateCreateInfo dyn = {
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ .dynamicStateCount = 2,
+ .pDynamicStates = dyns,
+ };
+ VkGraphicsPipelineCreateInfo gpci = {
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .stageCount = 2,
+ .pStages = stages,
+ .pVertexInputState = &vin,
+ .pInputAssemblyState = &ia,
+ .pViewportState = &vp,
+ .pRasterizationState = &rs,
+ .pMultisampleState = &ms,
+ .pColorBlendState = &cb,
+ .pDynamicState = &dyn,
+ .layout = s->pl,
+ .renderPass = s->rp,
+ };
+ if( d->create_graphics_pipelines( d->dev, VK_NULL_HANDLE, 1, &gpci, 0, &s->pipe ) != VK_SUCCESS )
+ return STAT_ERR;
+ return STAT_OK;
+}
+
+U32 find_memory_type( DEVICE_DATA* d, U32 bits, VkMemoryPropertyFlags want ) {
+ VkPhysicalDeviceMemoryProperties mp;
+ d->inst->get_memory_properties( d->phys, &mp );
+ for( U32 m = 0; m < mp.memoryTypeCount; m++ )
+ if( ( bits & ( 1u << m ) ) &&
+ ( mp.memoryTypes[m].propertyFlags & want ) == want )
+ return m;
+ return UINT32_MAX;
+}
+
+STAT vk_swap_create_vbuffers( DEVICE_DATA* d, SWAP_DATA* s ) {
+ for( U32 i = 0; i < s->nimages; i++ ) {
+ VkBufferCreateInfo bci = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .size = MAX_VERTS * sizeof(VERTEX),
+ .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ };
+ if( d->create_buffer( d->dev, &bci, 0, &s->vbufs[i] ) != VK_SUCCESS )
+ return STAT_ERR;
+
+ VkMemoryRequirements mr;
+ d->get_buffer_memory_requirements( d->dev, s->vbufs[i], &mr );
+ U32 type = find_memory_type(
+ d, mr.memoryTypeBits,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
+ );
+
+ if( type == UINT32_MAX )
+ return STAT_ERR;
+
+ VkMemoryAllocateInfo mai = {
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = mr.size,
+ .memoryTypeIndex = type,
+ };
+ if( d->allocate_memory( d->dev, &mai, 0, &s->vmems[i] ) != VK_SUCCESS )
+ return STAT_ERR;
+ if( d->bind_buffer_memory( d->dev, s->vbufs[i], s->vmems[i], 0 )
+ != VK_SUCCESS )
+ return STAT_ERR;
+ if( d->map_memory( d->dev, s->vmems[i], 0, VK_WHOLE_SIZE, 0,
+ &s->vmaps[i] ) != VK_SUCCESS )
+ return STAT_ERR;
+ }
+ return STAT_OK;
+}
+
+STAT vk_swap_create_sync( DEVICE_DATA* d, SWAP_DATA* s ) {
+ for( U32 i = 0; i < s->nimages; i++ ) {
+ VkSemaphoreCreateInfo sci = {
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ };
+ if( d->create_semaphore( d->dev, &sci, 0, &s->sems[i] ) != VK_SUCCESS )
+ return STAT_ERR;
+
+ VkFenceCreateInfo fci = {
+ .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+ .flags = VK_FENCE_CREATE_SIGNALED_BIT,
+ };
+ if( d->create_fence( d->dev, &fci, 0, &s->fences[i] ) != VK_SUCCESS )
+ return STAT_ERR;
+ }
+ return STAT_OK;
+}
+
+STAT swap_build_static( DEVICE_DATA* d, SWAP_DATA* s ) {
+ if( !OK( vk_swap_create_render_pass( d, s ) ) ) return STAT_ERR;
+ if( !OK( vk_swap_create_framebuffers( d, s ) ) ) return STAT_ERR;
+ if( !OK( vk_swap_create_shaders( d, s ) ) ) return STAT_ERR;
+ if( !OK( vk_swap_create_pipeline( d, s ) ) ) return STAT_ERR;
+ if( !OK( vk_swap_create_vbuffers( d, s ) ) ) return STAT_ERR;
+ if( !OK( vk_swap_create_sync( d, s ) ) ) return STAT_ERR;
+ return STAT_OK;
+}
+
+SWAP_DATA* vk_swap_create(
+ DEVICE_DATA* d,
+ VkSwapchainKHR sc,
+ const VkSwapchainCreateInfoKHR* ci
+) {
+ pthread_mutex_lock( &g_lock );
+ defer( pthread_mutex_unlock( &g_lock ) );
+
+ SWAP_DATA* s = (SWAP_DATA*)calloc( 1, sizeof( SWAP_DATA ) );
+ s->sc = sc;
+ s->fmt = ci->imageFormat;
+ s->extent = ci->imageExtent;
+
+ U32 n = 0;
+ d->get_swapchain_images( d->dev, sc, &n, 0 );
+ if( n > MAX_IMAGES )
+ n = MAX_IMAGES;
+ s->nimages = n;
+ d->get_swapchain_images( d->dev, sc, &n, s->imgs );
+
+ if( !OK( swap_build_static( d, s ) ) ) {
+ dlog( "fhvkov: swapchain setup failed, overlay disabled for this swapchain\n" );
+ s->broken = 1;
+ }
+
+ for( I32 i = 0; i < MAX_SWAPS; i++ )
+ if( !d->swaps[i] ) {
+ d->swaps[i] = s;
+ return s;
+ }
+
+ vk_swap_destroy( d, s );
+ return 0;
+}
+
+STAT vk_swap_setup_frames( DEVICE_DATA* d, SWAP_DATA* s, VkQueue q ) {
+ U32 family = UINT32_MAX;
+ for( I32 i = 0; i < d->nqueues; i++ )
+ if( d->queues[i].q == q ) {
+ family = d->queues[i].family;
+ break;
+ }
+ if( family == UINT32_MAX ) {
+ if( !d->warned_family ) {
+ dlog( "fhvkov: present queue family unknown, overlay disabled\n" );
+ d->warned_family = 1;
+ }
+ return STAT_ERR;
+ }
+
+ U32 nfam = 0;
+ d->inst->get_queue_family_properties( d->phys, &nfam, 0 );
+ VkQueueFamilyProperties fam[16];
+ if( nfam > 16 )
+ nfam = 16;
+ d->inst->get_queue_family_properties( d->phys, &nfam, fam );
+ if( family >= nfam || !(fam[family].queueFlags & VK_QUEUE_GRAPHICS_BIT) ) {
+ dlog( "fhvkov: present queue not graphics-capable, overlay disabled\n" );
+ return STAT_ERR;
+ }
+
+ VkCommandPoolCreateInfo cpci = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+ .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+ .queueFamilyIndex = family,
+ };
+ if( d->create_command_pool( d->dev, &cpci, 0, &s->pool ) != VK_SUCCESS )
+ return STAT_ERR;
+
+ VkCommandBufferAllocateInfo cbai = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .commandPool = s->pool,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandBufferCount = s->nimages,
+ };
+ if( d->allocate_command_buffers( d->dev, &cbai, s->cbs ) != VK_SUCCESS )
+ return STAT_ERR;
+
+ s->frames_ready = true;
+ return STAT_OK;
+}
+
+void vk_swap_destroy( DEVICE_DATA* d, SWAP_DATA* s ) {
+ d->device_wait_idle( d->dev );
+ for( U32 i = 0; i < s->nimages; i++ ) {
+ if( s->vmaps[i] ) d->unmap_memory( d->dev, s->vmems[i] );
+ if( s->vbufs[i] ) d->destroy_buffer( d->dev, s->vbufs[i], 0 );
+ if( s->vmems[i] ) d->free_memory( d->dev, s->vmems[i], 0 );
+ if( s->sems[i] ) d->destroy_semaphore( d->dev, s->sems[i], 0 );
+ if( s->fences[i] ) d->destroy_fence( d->dev, s->fences[i], 0 );
+ if( s->fbs[i] ) d->destroy_framebuffer( d->dev, s->fbs[i], 0 );
+ if( s->views[i] ) d->destroy_image_view( d->dev, s->views[i], 0 );
+ }
+ if( s->pool ) d->destroy_command_pool( d->dev, s->pool, 0 );
+ if( s->pipe ) d->destroy_pipeline( d->dev, s->pipe, 0 );
+ if( s->pl ) d->destroy_pipeline_layout( d->dev, s->pl, 0 );
+ if( s->vs ) d->destroy_shader_module( d->dev, s->vs, 0 );
+ if( s->fs ) d->destroy_shader_module( d->dev, s->fs, 0 );
+ if( s->rp ) d->destroy_render_pass( d->dev, s->rp, 0 );
+ free( s );
+}
+
+void vk_swap_forget( DEVICE_DATA* d, VkSwapchainKHR sc ) {
+ pthread_mutex_lock( &g_lock );
+ defer( pthread_mutex_unlock( &g_lock ) );
+
+ for( I32 i = 0; i < MAX_SWAPS; i++ )
+ if( d->swaps[i] && d->swaps[i]->sc == sc ) {
+ vk_swap_destroy( d, d->swaps[i] );
+ d->swaps[i] = 0;
+ return;
+ }
+}
+
+void vk_draw( DEVICE_DATA* d, SWAP_DATA* s, U32 idx ) {
+ VkCommandBuffer cb = s->cbs[idx];
+ F32 w = (F32)s->extent.width, h = (F32)s->extent.height;
+
+ VkCommandBufferBeginInfo bi = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ };
+ d->begin_command_buffer( cb, &bi );
+
+ VkRenderPassBeginInfo rbi = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ .renderPass = s->rp,
+ .framebuffer = s->fbs[idx],
+ .renderArea = { .extent = s->extent },
+ };
+ d->cmd_begin_render_pass( cb, &rbi, VK_SUBPASS_CONTENTS_INLINE );
+ d->cmd_bind_pipeline( cb, VK_PIPELINE_BIND_POINT_GRAPHICS, s->pipe );
+
+ VkViewport vp = { 0.0f, 0.0f, w, h, 0.0f, 1.0f };
+ d->cmd_set_viewport( cb, 0, 1, &vp );
+ VkRect2D sc = { { 0, 0 }, s->extent };
+ d->cmd_set_scissor( cb, 0, 1, &sc );
+
+ VkDeviceSize zero = 0;
+ d->cmd_bind_vertex_buffers( cb, 0, 1, &s->vbufs[idx], &zero );
+
+ F32 pc[2] = { 2.0f / w, 2.0f / h };
+ d->cmd_push_constants( cb, s->pl, VK_SHADER_STAGE_VERTEX_BIT, 0,
+ sizeof( pc ), pc );
+
+ d->cmd_draw( cb, (U32)d->gauge.nverts, 1, 0, 0 );
+ d->cmd_end_render_pass( cb );
+ d->end_command_buffer( cb );
+}
+
+VkResult vk_present(
+ DEVICE_DATA* d,
+ SWAP_DATA* s,
+ VkQueue q,
+ const VkPresentInfoKHR* pi
+) {
+ U32 idx = pi->pImageIndices[0];
+ if( idx >= s->nimages )
+ return d->queue_present( q, pi );
+
+ d->wait_for_fences( d->dev, 1, &s->fences[idx], VK_TRUE, UINT64_MAX );
+ d->reset_fences( d->dev, 1, &s->fences[idx] );
+
+ overlay_build_cluster( d->overlay, &d->gauge, (F32)s->extent.width, (F32)s->extent.height, t_nows() );
+ memcpy( s->vmaps[idx], d->gauge.verts, (PTR)d->gauge.nverts * sizeof( VERTEX ) );
+ vk_draw( d, s, idx );
+
+ U32 nwait = pi->waitSemaphoreCount;
+ if( nwait > MAX_WAIT_SEMS )
+ nwait = MAX_WAIT_SEMS;
+ VkPipelineStageFlags stages[MAX_WAIT_SEMS];
+ for( U32 i = 0; i < nwait; i++ )
+ stages[i] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+
+ VkSubmitInfo si = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .waitSemaphoreCount = nwait,
+ .pWaitSemaphores = pi->pWaitSemaphores,
+ .pWaitDstStageMask = stages,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &s->cbs[idx],
+ .signalSemaphoreCount = 1,
+ .pSignalSemaphores = &s->sems[idx],
+ };
+
+ if( d->queue_submit( q, 1, &si, s->fences[idx] ) != VK_SUCCESS ) {
+ s->broken = true;
+ return d->queue_present( q, pi );
+ }
+
+ VkPresentInfoKHR pi2 = *pi;
+ pi2.waitSemaphoreCount = 1;
+ pi2.pWaitSemaphores = &s->sems[idx];
+
+ return d->queue_present( q, &pi2 );
+}