#include #include #include #include #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 ); }