#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; } VkShaderModule create_shader (cruft::vk::device &dev, const std::experimental::filesystem::path &src) { auto bytes = util::slurp (src); assert (bytes.size () % 4 == 0); VkShaderModuleCreateInfo create_info {}; create_info.sType = cruft::vk::structure_type_v; create_info.codeSize = bytes.size (); create_info.pCode = reinterpret_cast (bytes.data ()); VkShaderModule value; cruft::vk::error::try_code ( vkCreateShaderModule (dev.id (), &create_info, nullptr, &value) ); return value; } /////////////////////////////////////////////////////////////////////////////// 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 ()) ); { VkDebugReportCallbackEXT callback; VkDebugReportCallbackCreateInfoEXT debug_info {}; debug_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; debug_info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; debug_info.pfnCallback = vk_debug_callback; auto func = (decltype(&vkCreateDebugReportCallbackEXT))vkGetInstanceProcAddr (instance.id (),"vkCreateDebugReportCallbackEXT"); cruft::vk::error::try_code ( func (instance.id (), &debug_info, nullptr, &callback) ); } auto pdevices = cruft::vk::physical_device::find (instance); auto &pdevice = pdevices[0]; VkSurfaceKHR surface; cruft::vk::error::try_code ( glfwCreateWindowSurface (instance.id (), window, nullptr, &surface) ); VkSurfaceCapabilitiesKHR surface_capabilities; std::vector surface_formats; std::vector present_modes; vkGetPhysicalDeviceSurfaceCapabilitiesKHR (pdevice.id (), surface, &surface_capabilities); { uint32_t format_count = 0; cruft::vk::error::try_code ( vkGetPhysicalDeviceSurfaceFormatsKHR (pdevice.id (), surface, &format_count, nullptr) ); surface_formats.resize (format_count); cruft::vk::error::try_code ( vkGetPhysicalDeviceSurfaceFormatsKHR ( pdevice.id (), surface, &format_count, surface_formats.data () ) ); uint32_t present_count = 0; cruft::vk::error::try_code ( vkGetPhysicalDeviceSurfacePresentModesKHR (pdevice.id (), surface, &present_count, nullptr) ); present_modes.resize (present_count); cruft::vk::error::try_code ( vkGetPhysicalDeviceSurfacePresentModesKHR (pdevice.id (), surface, &present_count, nullptr) ); } auto queues = pdevice.queue_families (); int graphics_queue_id = -1, present_queue_id = -1; for (int i = 0; unsigned (i) != queues.size (); ++i) { if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) graphics_queue_id = i; VkBool32 present_support = false; vkGetPhysicalDeviceSurfaceSupportKHR (pdevice.id (), i, surface, &present_support); if (present_support) present_queue_id = i; } 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 = std::size (device_queues); 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_COLORSPACE_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, .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, }; VkSwapchainKHR swapchain; cruft::vk::error::try_code ( vkCreateSwapchainKHR (ldevice.id (), &swap_create_info, nullptr, &swapchain) ); uint32_t swap_image_count = 0; cruft::vk::error::try_code ( vkGetSwapchainImagesKHR (ldevice.id (), swapchain, &swap_image_count, nullptr) ); std::vector swap_images (swap_image_count); cruft::vk::error::try_code ( vkGetSwapchainImagesKHR (ldevice.id (), swapchain, &swap_image_count, swap_images.data ()) ); std::vector swap_image_views (swap_image_count); std::transform ( std::cbegin (swap_images), std::cend (swap_images), std::begin (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 }; VkImageView v; cruft::vk::error::try_code ( vkCreateImageView (ldevice.id (), &create_info, nullptr, &v) ); return v; }); auto graphics_queue = ldevice.queue (graphics_queue_id); auto present_queue = ldevice.queue (present_queue_id); auto vert_module = create_shader (ldevice, "./hello/shader.vert"); auto frag_module = create_shader (ldevice, "./hello/shader.frag"); 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; 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; VkPipelineShaderStageCreateInfo stages[] = { vert_stage_info, frag_stage_info }; VkPipelineVertexInputStateCreateInfo vertex_input {}; vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input.vertexBindingDescriptionCount = 0; vertex_input.pVertexBindingDescriptions = nullptr; vertex_input.vertexAttributeDescriptionCount = 0; vertex_input.pVertexBindingDescriptions = nullptr; 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 VkPipelineLayout pipeline_layout; cruft::vk::error::try_code ( vkCreatePipelineLayout (ldevice.id (), &pipeline_layout_info, nullptr, &pipeline_layout) ); 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; VkRenderPass render_pass; cruft::vk::error::try_code ( vkCreateRenderPass (ldevice.id (), &render_pass_info, nullptr, &render_pass) ); 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; pipeline_info.renderPass = render_pass; pipeline_info.subpass = 0; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_info.basePipelineIndex = -1; VkPipeline graphics_pipeline; cruft::vk::error::try_code ( vkCreateGraphicsPipelines ( ldevice.id (), VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &graphics_pipeline ) ); std::vector swapchain_framebuffers (swap_image_views.size ()); for (size_t i = 0; i < swap_image_views.size (); ++i) { VkImageView attachments[] = { swap_image_views[i] }; VkFramebufferCreateInfo framebuffer_info {}; framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebuffer_info.renderPass = render_pass; framebuffer_info.attachmentCount = 1; framebuffer_info.pAttachments = attachments; framebuffer_info.width = present_extent.width; framebuffer_info.height = present_extent.height; framebuffer_info.layers = 1; cruft::vk::error::try_code ( vkCreateFramebuffer (ldevice.id (), &framebuffer_info, nullptr, &swapchain_framebuffers[i]) ); }; VkCommandPoolCreateInfo pool_info {}; pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; pool_info.queueFamilyIndex = device_queues[0].queueFamilyIndex; VkCommandPool command_pool; cruft::vk::error::try_code ( vkCreateCommandPool (ldevice.id (), &pool_info, nullptr, &command_pool) ); 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; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.commandBufferCount = (uint32_t) command_buffers.size(); cruft::vk::error::try_code ( vkAllocateCommandBuffers (ldevice.id (), &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; render_begin.framebuffer = swapchain_framebuffers[i]; 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); vkCmdDraw (command_buffers[i], 3, 1, 0, 0); vkCmdEndRenderPass (command_buffers[i]); vkEndCommandBuffer (command_buffers[i]); }; VkSemaphoreCreateInfo semaphore_info {}; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; VkSemaphore image_semaphore, render_semaphore; cruft::vk::error::try_code ( vkCreateSemaphore (ldevice.id (), &semaphore_info, nullptr, &image_semaphore) ); cruft::vk::error::try_code ( vkCreateSemaphore (ldevice.id (), &semaphore_info, nullptr, &render_semaphore) ); uint32_t image_index; cruft::vk::error::try_code ( vkAcquireNextImageKHR ( ldevice.id (), swapchain, std::numeric_limits::max (), image_semaphore, VK_NULL_HANDLE, &image_index ) ); VkSubmitInfo submit_info {}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; VkSemaphore wait_semaphores[] = { image_semaphore }; 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 }; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = signal_semaphores; cruft::vk::error::try_code ( vkQueueSubmit (graphics_queue.id (), 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.srcAccessMask = 0; 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 }; present_info.swapchainCount = 1; present_info.pSwapchains = swapchains; present_info.pImageIndices = &image_index; present_info.pResults = nullptr; vkQueuePresentKHR (present_queue.id (), &present_info); LOG_INFO ("entering runloop"); while (!glfwWindowShouldClose (window)) { glfwPollEvents (); } vkDeviceWaitIdle (ldevice.id ()); LOG_INFO ("terminating glfw"); glfwDestroyWindow (window); glfwTerminate (); }