#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// static VkBool32 vk_debug_callback (VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { (void)flags; (void)objType; (void)obj; (void)location; (void)code; (void)layerPrefix; (void)msg; (void)userData; std::cerr << "validation: " << msg << '\n'; return VK_FALSE; } /////////////////////////////////////////////////////////////////////////////// struct vertex_t { util::point2f position; util::colour3f colour; }; //----------------------------------------------------------------------------- static constexpr vertex_t VERTICES[] = { { { 0.0f, -0.5f }, { 1.0f, 0.0f, 0.0f } }, { { 0.5f, 0.5f }, { 0.0f, 1.0f, 0.0f } }, { {-0.5f, 0.5f }, { 0.0f, 0.0f, 1.0f } }, }; /////////////////////////////////////////////////////////////////////////////// int main (void) { LOG_INFO ("intialising glfw"); glfwInit (); LOG_INFO ("intialising window"); glfwWindowHint (GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint (GLFW_RESIZABLE, GLFW_FALSE); int resolution_x = 800, resolution_y = 600; auto window = glfwCreateWindow (resolution_x, resolution_y, "vkcruft-hello", nullptr, nullptr); if (!window) { LOG_ERROR("unable to initialised GLFW"); return -1; } unsigned int glfwExtensionCount; auto glfwExtensions = glfwGetRequiredInstanceExtensions (&glfwExtensionCount); std::vector extensions (glfwExtensions + 0, glfwExtensions + glfwExtensionCount); extensions.push_back ("VK_EXT_debug_report"); const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; //------------------------------------------------------------------------- cruft::vk::instance instance ( util::make_view (layers), util::make_view (extensions.data (), extensions.data () + extensions.size ()) ); load_traits (instance); //------------------------------------------------------------------------- VkDebugReportCallbackCreateInfoEXT debug_info {}; debug_info.sType = cruft::vk::structure_type_v; debug_info.flags = //VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT //VK_DEBUG_REPORT_DEBUG_BIT_EXT | ; debug_info.pfnCallback = vk_debug_callback; auto debug_callback = cruft::vk::make_owned ( instance, &debug_info, nullptr ); //------------------------------------------------------------------------- auto pdevices = cruft::vk::physical_device::find (instance); auto &pdevice = pdevices[0]; //------------------------------------------------------------------------- auto surface = cruft::vk::make_owned ( cruft::vk::error::try_query ( glfwCreateWindowSurface, instance.native (), window, nullptr ), instance ); auto surface_capabilities = pdevice.surface_capabilities (surface.get ()); auto surface_formats = pdevice.surface_formats (surface.get ()); auto present_modes = pdevice.present_modes (surface.get ()); auto queues = pdevice.queue_families (); int graphics_queue_id = -1, present_queue_id = -1; for (int i = 0, last = queues.size (); i != last; ++i) if (queues[i].queueCount > 0 && queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { graphics_queue_id = i; break; } for (int i = 0, last = queues.size (); i != last; ++i) { if (cruft::vk::error::try_query (vkGetPhysicalDeviceSurfaceSupportKHR, pdevice.native (), i, surface->native ())) { present_queue_id = i; break; } } if (graphics_queue_id == -1 || present_queue_id == -1) { std::cerr << "unable to find useful queues\n"; return EXIT_FAILURE; } float priority = 1.f; VkDeviceQueueCreateInfo device_queues[] = { { .sType = cruft::vk::structure_type_v, .pNext = nullptr, .flags = 0, .queueFamilyIndex = unsigned (graphics_queue_id), .queueCount = 1, .pQueuePriorities = &priority, }, { .sType = cruft::vk::structure_type_v, .pNext = nullptr, .flags = 0, .queueFamilyIndex = unsigned (present_queue_id), .queueCount = 1, .pQueuePriorities = &priority } }; const char *device_extensions[] = { "VK_KHR_swapchain" }; VkPhysicalDeviceFeatures device_features {}; VkDeviceCreateInfo device_info {}; device_info.sType = cruft::vk::structure_type_v; device_info.pQueueCreateInfos = std::data (device_queues); device_info.queueCreateInfoCount = graphics_queue_id == present_queue_id ? 1 : 2; device_info.pEnabledFeatures = &device_features; device_info.enabledLayerCount = std::size (layers); device_info.ppEnabledLayerNames = layers; device_info.enabledExtensionCount = std::size (device_extensions); device_info.ppEnabledExtensionNames = std::data (device_extensions); cruft::vk::device ldevice (pdevice, device_info); //------------------------------------------------------------------------- #if 0 VkSurfaceFormatKHR surface_format = (surface_formats.size () == 1 && surface_formats[0].format == VK_FORMAT_UNDEFINED) ? { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR } : surface_formats[0]; #endif VkSurfaceFormatKHR surface_format { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; VkExtent2D present_extent { .width = util::limit ( unsigned (resolution_x), surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width ), .height = util::limit ( unsigned (resolution_y), surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height ) }; uint32_t image_count = surface_capabilities.minImageCount; VkSwapchainCreateInfoKHR swap_create_info { .sType = cruft::vk::structure_type_v, .pNext = nullptr, .flags = {}, .surface = surface->native (), .minImageCount = image_count, .imageFormat = surface_format.format, .imageColorSpace = surface_format.colorSpace, .imageExtent = present_extent, .imageArrayLayers = 1, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .imageSharingMode = VkSharingMode::VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, .preTransform = surface_capabilities.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, .presentMode = present_mode, .clipped = VK_TRUE, .oldSwapchain = VK_NULL_HANDLE, }; auto swapchain = cruft::vk::make_owned (ldevice, &swap_create_info, nullptr); std::vector swap_images = cruft::vk::error::try_values ( vkGetSwapchainImagesKHR, ldevice, swapchain ); using image_view_ptr = cruft::vk::owned_ptr; std::vector swap_image_views; swap_image_views.reserve (std::size (swap_images)); std::transform ( std::cbegin (swap_images), std::cend (swap_images), std::back_inserter (swap_image_views), [&] (auto i) { VkImageViewCreateInfo create_info {}; create_info.sType = cruft::vk::structure_type_v, create_info.pNext = nullptr, create_info.image = i, create_info.viewType = VK_IMAGE_VIEW_TYPE_2D, create_info.format = surface_format.format, create_info.components = VkComponentMapping { .r = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY, }; create_info.subresourceRange = VkImageSubresourceRange { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1 }; return cruft::vk::make_owned (ldevice, &create_info, nullptr); }); auto graphics_queue = cruft::vk::make_owned (ldevice, graphics_queue_id, 0); auto present_queue = cruft::vk::make_owned (ldevice, present_queue_id, 0); auto vert_module = cruft::vk::make_owned (ldevice, "./hello.vert.spv"); auto frag_module = cruft::vk::make_owned (ldevice, "./hello.frag.spv"); VkBufferCreateInfo buffer_info {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = sizeof (VERTICES); buffer_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; auto vertex_buffer = cruft::vk::make_owned (ldevice, &buffer_info, nullptr); auto memory_requirements = cruft::vk::error::try_query ( vkGetBufferMemoryRequirements, ldevice.native (), vertex_buffer->native () ); auto memory_properties = pdevice.memory_properties (); VkMemoryAllocateInfo allocate_info {}; allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocate_info.allocationSize = memory_requirements.size; allocate_info.memoryTypeIndex = [&] () { for (uint32_t i = 0; i < memory_properties.memoryTypeCount; ++i) { if ((memory_requirements.memoryTypeBits & (1u << i)) && memory_properties.memoryTypes[i].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) return i; } throw std::runtime_error ("no suitable memory type"); } (); auto vertex_memory = cruft::vk::make_owned ( ldevice, &allocate_info, nullptr ); cruft::vk::error::try_code ( vkBindBufferMemory (ldevice.native (), vertex_buffer->native (), vertex_memory->native (), 0) ); { auto data = map (vertex_memory, sizeof (VERTICES)); memcpy (std::data (data), std::data (VERTICES), sizeof (VERTICES)); } VkPipelineShaderStageCreateInfo vert_stage_info {}; vert_stage_info.sType = cruft::vk::structure_type_v; vert_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; vert_stage_info.module = vert_module->native (); vert_stage_info.pName = "main"; VkPipelineShaderStageCreateInfo frag_stage_info = vert_stage_info; frag_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; frag_stage_info.module = frag_module->native (); VkPipelineShaderStageCreateInfo stages[] = { vert_stage_info, frag_stage_info }; VkVertexInputBindingDescription vertex_binding {}; vertex_binding.binding = 0; vertex_binding.stride = sizeof (vertex_t); vertex_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; std::array vertex_attribute {}; vertex_attribute[0].binding = 0; vertex_attribute[0].location = 0; vertex_attribute[0].format = VK_FORMAT_R32G32_SFLOAT; vertex_attribute[0].offset = offsetof(vertex_t, position); vertex_attribute[1].binding = 0; vertex_attribute[1].location = 1; vertex_attribute[1].format = VK_FORMAT_R32G32B32_SFLOAT; vertex_attribute[1].offset = offsetof (vertex_t, colour); VkPipelineVertexInputStateCreateInfo vertex_input {}; vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input.vertexBindingDescriptionCount = 1; vertex_input.pVertexBindingDescriptions = &vertex_binding; vertex_input.vertexAttributeDescriptionCount = 2; vertex_input.pVertexAttributeDescriptions = std::data (vertex_attribute); VkPipelineInputAssemblyStateCreateInfo assembly_info {}; assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; assembly_info.primitiveRestartEnable = VK_FALSE; VkViewport viewport { .x = 0.f, .y = 0.f, .width = float (present_extent.width), .height = float (present_extent.height), .minDepth = 0.f, .maxDepth = 1.f, }; VkRect2D scissor { .offset = {}, .extent = present_extent }; VkPipelineViewportStateCreateInfo viewport_create_info {}; viewport_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_create_info.viewportCount = 1; viewport_create_info.pViewports = &viewport; viewport_create_info.scissorCount = 1; viewport_create_info.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterizer {}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.lineWidth = 1.f; rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; rasterizer.depthBiasEnable = VK_FALSE; rasterizer.depthBiasConstantFactor = 0.f; rasterizer.depthBiasClamp = 0.f; rasterizer.depthBiasSlopeFactor = 0.f; VkPipelineMultisampleStateCreateInfo multisampling {}; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.sampleShadingEnable = VK_FALSE; multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; multisampling.minSampleShading = 1.0f; multisampling.pSampleMask = nullptr; multisampling.alphaToCoverageEnable = VK_FALSE; multisampling.alphaToOneEnable = VK_FALSE; VkPipelineColorBlendAttachmentState blend_attachment = {}; blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; blend_attachment.blendEnable = VK_FALSE; blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional VkPipelineColorBlendStateCreateInfo colour_blend = {}; colour_blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colour_blend.logicOpEnable = VK_FALSE; colour_blend.logicOp = VK_LOGIC_OP_COPY; // Optional colour_blend.attachmentCount = 1; colour_blend.pAttachments = &blend_attachment; colour_blend.blendConstants[0] = 0.0f; // Optional colour_blend.blendConstants[1] = 0.0f; // Optional colour_blend.blendConstants[2] = 0.0f; // Optional colour_blend.blendConstants[3] = 0.0f; // Optional VkPipelineLayoutCreateInfo pipeline_layout_info {}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_info.setLayoutCount = 0; // Optional pipeline_layout_info.pSetLayouts = nullptr; // Optional pipeline_layout_info.pushConstantRangeCount = 0; // Optional pipeline_layout_info.pPushConstantRanges = 0; // Optional auto pipeline_layout = cruft::vk::make_owned ( ldevice, &pipeline_layout_info, nullptr ); VkAttachmentDescription color_attachment {}; color_attachment.format = surface_format.format; color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; VkAttachmentReference colour_attachment_ref {}; colour_attachment_ref.attachment = 0; colour_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colour_attachment_ref; VkRenderPassCreateInfo render_pass_info {}; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; render_pass_info.attachmentCount = 1; render_pass_info.pAttachments = &color_attachment; render_pass_info.subpassCount = 1; render_pass_info.pSubpasses = &subpass; auto render_pass = cruft::vk::make_owned ( ldevice, &render_pass_info, nullptr ); VkGraphicsPipelineCreateInfo pipeline_info {}; pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_info.stageCount = 2; pipeline_info.pStages = stages; pipeline_info.pVertexInputState = &vertex_input; pipeline_info.pInputAssemblyState = &assembly_info; pipeline_info.pViewportState = &viewport_create_info; pipeline_info.pRasterizationState = &rasterizer; pipeline_info.pMultisampleState = &multisampling; pipeline_info.pDepthStencilState = nullptr; pipeline_info.pColorBlendState = &colour_blend; pipeline_info.layout = pipeline_layout->native (); pipeline_info.renderPass = render_pass->native (); pipeline_info.subpass = 0; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_info.basePipelineIndex = -1; auto graphics_pipeline = cruft::vk::make_owned> ( ldevice, VK_NULL_HANDLE, 1, &pipeline_info, nullptr ); using framebuffer_ptr = cruft::vk::owned_ptr; std::vector swapchain_framebuffers; swapchain_framebuffers.reserve (swap_image_views.size ()); for (size_t i = 0; i < swap_image_views.size (); ++i) { VkImageView attachments[] = { swap_image_views[i]->native () }; VkFramebufferCreateInfo framebuffer_info {}; framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebuffer_info.renderPass = render_pass->native (); framebuffer_info.attachmentCount = 1; framebuffer_info.pAttachments = attachments; framebuffer_info.width = present_extent.width; framebuffer_info.height = present_extent.height; framebuffer_info.layers = 1; swapchain_framebuffers.push_back ( cruft::vk::make_owned (ldevice, &framebuffer_info, nullptr) ); }; VkCommandPoolCreateInfo pool_info {}; pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; pool_info.queueFamilyIndex = device_queues[0].queueFamilyIndex; auto command_pool = cruft::vk::make_owned ( ldevice, &pool_info, nullptr ); std::vector command_buffers (swapchain_framebuffers.size ()); VkCommandBufferAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.commandPool = command_pool->native (); alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.commandBufferCount = (uint32_t) command_buffers.size(); cruft::vk::error::try_code ( vkAllocateCommandBuffers (ldevice.native (), &alloc_info, command_buffers.data ()) ); for (size_t i = 0; i < command_buffers.size (); ++i) { VkCommandBufferBeginInfo command_begin {}; command_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; command_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; cruft::vk::error::try_code ( vkBeginCommandBuffer (command_buffers[i], &command_begin) ); VkRenderPassBeginInfo render_begin {}; render_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_begin.renderPass = render_pass->native (); render_begin.framebuffer = swapchain_framebuffers[i]->native (); render_begin.renderArea.extent = present_extent; VkClearValue clear_color { 0.f, 0.f, 0.f, 1.f }; render_begin.clearValueCount = 1; render_begin.pClearValues = &clear_color; vkCmdBeginRenderPass (command_buffers[i], &render_begin, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline ( command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline->native () ); VkBuffer buffers[] = {vertex_buffer->native ()}; VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers (command_buffers[i], 0, 1, buffers, offsets); vkCmdDraw (command_buffers[i], 3, 1, 0, 0); vkCmdEndRenderPass (command_buffers[i]); cruft::vk::error::try_func (&vkEndCommandBuffer, command_buffers[i]); }; VkSemaphoreCreateInfo semaphore_info {}; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; auto image_semaphore = cruft::vk::make_owned (ldevice, &semaphore_info, nullptr); auto render_semaphore = cruft::vk::make_owned (ldevice, &semaphore_info, nullptr); uint32_t image_index; cruft::vk::error::try_code ( vkAcquireNextImageKHR ( ldevice.native (), swapchain->native (), std::numeric_limits::max (), image_semaphore->native (), VK_NULL_HANDLE, &image_index ) ); VkSubmitInfo submit_info {}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; VkSemaphore wait_semaphores[] = { image_semaphore->native () }; VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = wait_semaphores; submit_info.pWaitDstStageMask = wait_stages; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &command_buffers[image_index]; VkSemaphore signal_semaphores[] = { render_semaphore->native () }; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = signal_semaphores; cruft::vk::error::try_code ( vkQueueSubmit (graphics_queue->native (), 1, &submit_info, VK_NULL_HANDLE) ); VkSubpassDependency dependency {}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; render_pass_info.dependencyCount = 1; render_pass_info.pDependencies = &dependency; VkPresentInfoKHR present_info {}; present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present_info.waitSemaphoreCount = 1; present_info.pWaitSemaphores = signal_semaphores; VkSwapchainKHR swapchains[] = { swapchain->native () }; present_info.swapchainCount = 1; present_info.pSwapchains = swapchains; present_info.pImageIndices = &image_index; present_info.pResults = nullptr; cruft::vk::error::try_func (vkQueuePresentKHR, present_queue->native (), &present_info); LOG_INFO ("entering runloop"); while (!glfwWindowShouldClose (window)) { glfwPollEvents (); } cruft::vk::error::try_func (vkDeviceWaitIdle, ldevice.native ()); LOG_INFO ("terminating glfw"); glfwDestroyWindow (window); glfwTerminate (); }