moved triangle.cpp to its own folder, updated readme & license

This commit is contained in:
Charles Giessen 2020-02-03 20:34:46 -07:00
parent 39a3c37465
commit 8734ed18e0
13 changed files with 672 additions and 613 deletions

View File

@ -25,13 +25,13 @@ target_link_libraries(vk-bootstrap-test vk-bootstrap)
target_link_libraries(vk-bootstrap-test glfw) target_link_libraries(vk-bootstrap-test glfw)
target_link_libraries(vk-bootstrap-test Catch2) target_link_libraries(vk-bootstrap-test Catch2)
add_executable(vk-bootstrap-triangle tests/triangle.cpp) add_executable(vk-bootstrap-triangle example/triangle.cpp)
target_link_libraries(vk-bootstrap-triangle vk-bootstrap) target_link_libraries(vk-bootstrap-triangle vk-bootstrap)
target_link_libraries(vk-bootstrap-triangle glfw) target_link_libraries(vk-bootstrap-triangle glfw)
add_custom_command( add_custom_command(
TARGET vk-bootstrap-triangle POST_BUILD TARGET vk-bootstrap-triangle POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/tests/shaders ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/example/shaders ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}
) )
endif() endif()

View File

@ -1,4 +1,4 @@
Copyright © 2020 Copyright © 2020 Charles Giessen (charles-lunarg@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -10,6 +10,13 @@ This library simplifies the tedious process of:
* Getting Queues * Getting Queues
* Swapchain Creation * Swapchain Creation
It also adds several convenience for:
* enabling validation layers
* setting up a debug callback
* automate getting a drawable surface
* selecting a gpu based on a small set of common criteria
## Example ## Example
```cpp ```cpp
@ -18,28 +25,32 @@ builder.setup_validation_layers()
.set_app_name ("example") .set_app_name ("example")
.set_default_debug_messenger (); .set_default_debug_messenger ();
auto inst_ret = builder.build(); auto inst_ret = builder.build();
vkb::Instance inst; if (!inst_ret.has_value()) {
if (inst_ret.has_value()) { // error
// successfully created instance
inst = inst_ret.value();
} }
vkb::Instance inst = inst_ret.value();
vkb::PhysicalDeviceSelector(inst); vkb::PhysicalDeviceSelector selector{ inst };
selector.set_surface (/* from user created window*/) selector.set_surface (/* from user created window*/)
.set_minimum_version (1, 0) .set_minimum_version (1, 0)
.require_dedicated_transfer_queue(); .require_dedicated_transfer_queue();
auto phys_ret = selector.select (); auto phys_ret = selector.select ();
vkb::PhysicalDevice phys; if (!phys_ret.has_value()) {
if (phys_ret.has_value()) { // error
// successfully selected a sufficient physical device
phys = phys_ret.value();
} }
vkb::PhysicalDevice physical_device = phys_ret.value();
vkb::DeviceBuilder device_builder(phys_dev); vkb::DeviceBuilder device_builder{ physical_device };
auto dev_ret = device_builder.build (); auto dev_ret = device_builder.build ();
if(dev_ret.has_value()){ if (!dev_ret.has_value()){
// successfully created a vulkan device // error
} }
vkb::Device device = dev_ret.value();
VkQueue graphics_queue = get_queue_graphics(device).value();
VkQueue compute_queue = get_queue_compute(device).value();
VkQueue transfer_queue = get_queue_transfer(device).value();
``` ```
## Building ## Building
@ -70,4 +81,10 @@ Then return to the build directory and enable tests with `VK_BOOTSTRAP_TEST`
cmake ../path/to/vk-bootstrap/ -DVK_BOOTSTRAP_TEST=ON cmake ../path/to/vk-bootstrap/ -DVK_BOOTSTRAP_TEST=ON
``` ```
## Todo's
* Package library to be usable
* More examples
* Testing
* Documenting API
* Fleshing out device configuration

541
example/triangle.cpp Normal file
View File

@ -0,0 +1,541 @@
#include <stdio.h>
#include <memory>
#include <iostream>
#include <fstream>
#include "../tests/common.h"
const int MAX_FRAMES_IN_FLIGHT = 2;
struct Init
{
GLFWwindow* window;
vkb::Instance instance;
VkSurfaceKHR surface;
vkb::Device device;
vkb::Swapchain swapchain;
};
struct RenderData
{
VkQueue graphics_queue;
VkQueue present_queue;
std::vector<VkImage> swapchain_images;
std::vector<VkImageView> swapchain_image_views;
std::vector<VkFramebuffer> framebuffers;
VkRenderPass render_pass;
VkPipelineLayout pipeline_layout;
VkPipeline graphics_pipeline;
VkCommandPool command_pool;
std::vector<VkCommandBuffer> command_buffers;
std::vector<VkSemaphore> available_semaphores;
std::vector<VkSemaphore> finished_semaphore;
std::vector<VkFence> in_flight_fences;
std::vector<VkFence> image_in_flight;
size_t current_frame = 0;
};
int device_initialization (Init& init)
{
init.window = create_window_glfw (false);
vkb::InstanceBuilder instance_builder;
auto instance_ret = instance_builder.set_default_debug_messenger ().setup_validation_layers ().build ();
if (!instance_ret)
{
std::cout << instance_ret.error ().msg << "\n";
}
init.instance = instance_ret.value ();
init.surface = create_surface_glfw (init.instance.instance, init.window);
vkb::PhysicalDeviceSelector phys_device_selector (init.instance);
auto phys_device_ret = phys_device_selector.set_surface (init.surface).select ();
if (!phys_device_ret)
{
std::cout << phys_device_ret.error ().msg << "\n";
}
vkb::PhysicalDevice physical_device = phys_device_ret.value ();
vkb::DeviceBuilder device_builder{ physical_device };
auto device_ret = device_builder.build ();
if (!device_ret)
{
std::cout << device_ret.error ().msg << "\n";
}
init.device = device_ret.value ();
vkb::SwapchainBuilder swapchain_builder{ init.device };
auto swap_ret =
swapchain_builder.use_default_format_selection ().use_default_present_mode_selection ().build ();
if (!swap_ret)
{
std::cout << swap_ret.error ().msg << "\n";
}
init.swapchain = swap_ret.value ();
return 0;
}
int get_queues (Init& init, RenderData& data)
{
data.graphics_queue = vkb::get_queue_graphics (init.device).value ();
data.present_queue = vkb::get_queue_graphics (init.device).value ();
return 0;
}
int create_render_pass (Init& init, RenderData& data)
{
VkAttachmentDescription color_attachment = {};
color_attachment.format = init.swapchain.image_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 color_attachment_ref = {};
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_ref;
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;
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;
render_pass_info.dependencyCount = 1;
render_pass_info.pDependencies = &dependency;
if (vkCreateRenderPass (init.device.device, &render_pass_info, nullptr, &data.render_pass) != VK_SUCCESS)
{
return -1; // failed to create render pass!
}
return 0;
}
std::vector<char> readFile (const std::string& filename)
{
std::ifstream file (filename, std::ios::ate | std::ios::binary);
if (!file.is_open ())
{
throw std::runtime_error ("failed to open file!");
}
size_t file_size = (size_t)file.tellg ();
std::vector<char> buffer (file_size);
file.seekg (0);
file.read (buffer.data (), file_size);
file.close ();
return buffer;
}
VkShaderModule createShaderModule (Init& init, const std::vector<char>& code)
{
VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.codeSize = code.size ();
create_info.pCode = reinterpret_cast<const uint32_t*> (code.data ());
VkShaderModule shaderModule;
if (vkCreateShaderModule (init.device.device, &create_info, nullptr, &shaderModule) != VK_SUCCESS)
{
return VK_NULL_HANDLE; // failed to create shader module
}
return shaderModule;
}
int create_graphics_pipeline (Init& init, RenderData& data)
{
auto vert_code = readFile ("vert.spv");
auto frag_code = readFile ("frag.spv");
VkShaderModule vert_module = createShaderModule (init, vert_code);
VkShaderModule frag_module = createShaderModule (init, frag_code);
if (vert_module == VK_NULL_HANDLE || frag_module == VK_NULL_HANDLE)
{
return -1; // failed to create shader modules
}
VkPipelineShaderStageCreateInfo vert_stage_info = {};
vert_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vert_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vert_stage_info.module = vert_module;
vert_stage_info.pName = "main";
VkPipelineShaderStageCreateInfo frag_stage_info = {};
frag_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
frag_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
frag_stage_info.module = frag_module;
frag_stage_info.pName = "main";
VkPipelineShaderStageCreateInfo shader_stages[] = { vert_stage_info, frag_stage_info };
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_info.vertexBindingDescriptionCount = 0;
vertex_input_info.vertexAttributeDescriptionCount = 0;
VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly.primitiveRestartEnable = VK_FALSE;
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)init.swapchain.extent.width;
viewport.height = (float)init.swapchain.extent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
scissor.offset = { 0, 0 };
scissor.extent = init.swapchain.extent;
VkPipelineViewportStateCreateInfo viewport_state = {};
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state.viewportCount = 1;
viewport_state.pViewports = &viewport;
viewport_state.scissorCount = 1;
viewport_state.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.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo color_blending = {};
color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blending.logicOpEnable = VK_FALSE;
color_blending.logicOp = VK_LOGIC_OP_COPY;
color_blending.attachmentCount = 1;
color_blending.pAttachments = &colorBlendAttachment;
color_blending.blendConstants[0] = 0.0f;
color_blending.blendConstants[1] = 0.0f;
color_blending.blendConstants[2] = 0.0f;
color_blending.blendConstants[3] = 0.0f;
VkPipelineLayoutCreateInfo pipeline_layout_info = {};
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_info.setLayoutCount = 0;
pipeline_layout_info.pushConstantRangeCount = 0;
if (vkCreatePipelineLayout (init.device.device, &pipeline_layout_info, nullptr, &data.pipeline_layout) != VK_SUCCESS)
{
return -1; // failed to create pipeline layout
}
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shader_stages;
pipelineInfo.pVertexInputState = &vertex_input_info;
pipelineInfo.pInputAssemblyState = &input_assembly;
pipelineInfo.pViewportState = &viewport_state;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &color_blending;
pipelineInfo.layout = data.pipeline_layout;
pipelineInfo.renderPass = data.render_pass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
if (vkCreateGraphicsPipelines (
init.device.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &data.graphics_pipeline) != VK_SUCCESS)
{
return -1; // failed to create graphics pipeline
}
vkDestroyShaderModule (init.device.device, frag_module, nullptr);
vkDestroyShaderModule (init.device.device, vert_module, nullptr);
return 0;
}
int create_framebuffers (Init& init, RenderData& data)
{
data.swapchain_images = vkb::get_swapchain_images (init.swapchain).value ();
// init.swapchain.image_count = data.swapchain_images.size ();
data.swapchain_image_views =
vkb::get_swapchain_image_views (init.swapchain, data.swapchain_images).value ();
data.framebuffers.resize (data.swapchain_image_views.size ());
for (size_t i = 0; i < data.swapchain_image_views.size (); i++)
{
VkImageView attachments[] = { data.swapchain_image_views[i] };
VkFramebufferCreateInfo framebuffer_info = {};
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.renderPass = data.render_pass;
framebuffer_info.attachmentCount = 1;
framebuffer_info.pAttachments = attachments;
framebuffer_info.width = init.swapchain.extent.width;
framebuffer_info.height = init.swapchain.extent.height;
framebuffer_info.layers = 1;
if (vkCreateFramebuffer (init.device.device, &framebuffer_info, nullptr, &data.framebuffers[i]) != VK_SUCCESS)
{
return -1; // failed to create framebuffer
}
}
return 0;
}
int create_command_pool (Init& init, RenderData& data)
{
VkCommandPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
pool_info.queueFamilyIndex = vkb::get_queue_index_graphics (init.device);
if (vkCreateCommandPool (init.device.device, &pool_info, nullptr, &data.command_pool) != VK_SUCCESS)
{
return -1; // failed to create command pool
}
return 0;
}
int create_command_buffers (Init& init, RenderData& data)
{
data.command_buffers.resize (data.framebuffers.size ());
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = data.command_pool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t)data.command_buffers.size ();
if (vkAllocateCommandBuffers (init.device.device, &allocInfo, data.command_buffers.data ()) != VK_SUCCESS)
{
return -1; // failed to allocate command buffers;
}
for (size_t i = 0; i < data.command_buffers.size (); i++)
{
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (vkBeginCommandBuffer (data.command_buffers[i], &begin_info) != VK_SUCCESS)
{
return -1; // failed to begin recording command buffer
}
VkRenderPassBeginInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_info.renderPass = data.render_pass;
render_pass_info.framebuffer = data.framebuffers[i];
render_pass_info.renderArea.offset = { 0, 0 };
render_pass_info.renderArea.extent = init.swapchain.extent;
VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
render_pass_info.clearValueCount = 1;
render_pass_info.pClearValues = &clearColor;
vkCmdBeginRenderPass (data.command_buffers[i], &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline (data.command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, data.graphics_pipeline);
vkCmdDraw (data.command_buffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass (data.command_buffers[i]);
if (vkEndCommandBuffer (data.command_buffers[i]) != VK_SUCCESS)
{
return -1; // failed to record command buffer!
}
}
return 0;
}
int create_sync_objects (Init& init, RenderData& data)
{
data.available_semaphores.resize (MAX_FRAMES_IN_FLIGHT);
data.finished_semaphore.resize (MAX_FRAMES_IN_FLIGHT);
data.in_flight_fences.resize (MAX_FRAMES_IN_FLIGHT);
data.image_in_flight.resize (init.swapchain.image_count, VK_NULL_HANDLE);
VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fence_info = {};
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
if (vkCreateSemaphore (init.device.device, &semaphore_info, nullptr, &data.available_semaphores[i]) != VK_SUCCESS ||
vkCreateSemaphore (init.device.device, &semaphore_info, nullptr, &data.finished_semaphore[i]) != VK_SUCCESS ||
vkCreateFence (init.device.device, &fence_info, nullptr, &data.in_flight_fences[i]) != VK_SUCCESS)
{
return -1; // failed to create synchronization objects for a frame
}
}
return 0;
}
int draw_frame (Init& init, RenderData& data)
{
vkWaitForFences (init.device.device, 1, &data.in_flight_fences[data.current_frame], VK_TRUE, UINT64_MAX);
uint32_t image_index = 0;
vkAcquireNextImageKHR (init.device.device,
init.swapchain.swapchain,
UINT64_MAX,
data.available_semaphores[data.current_frame],
VK_NULL_HANDLE,
&image_index);
if (data.image_in_flight[image_index] != VK_NULL_HANDLE)
{
vkWaitForFences (init.device.device, 1, &data.image_in_flight[image_index], VK_TRUE, UINT64_MAX);
}
data.image_in_flight[image_index] = data.in_flight_fences[data.current_frame];
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore wait_semaphores[] = { data.available_semaphores[data.current_frame] };
VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = wait_semaphores;
submitInfo.pWaitDstStageMask = wait_stages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &data.command_buffers[image_index];
VkSemaphore signal_semaphores[] = { data.finished_semaphore[data.current_frame] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signal_semaphores;
vkResetFences (init.device.device, 1, &data.in_flight_fences[data.current_frame]);
if (vkQueueSubmit (data.graphics_queue, 1, &submitInfo, data.in_flight_fences[data.current_frame]) != VK_SUCCESS)
{
return -1; //"failed to submit draw command buffer
}
VkPresentInfoKHR present_info = {};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = signal_semaphores;
VkSwapchainKHR swapChains[] = { init.swapchain.swapchain };
present_info.swapchainCount = 1;
present_info.pSwapchains = swapChains;
present_info.pImageIndices = &image_index;
vkQueuePresentKHR (data.present_queue, &present_info);
data.current_frame = (data.current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
return 0;
}
void cleanup (Init& init, RenderData& data)
{
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
{
vkDestroySemaphore (init.device.device, data.finished_semaphore[i], nullptr);
vkDestroySemaphore (init.device.device, data.available_semaphores[i], nullptr);
vkDestroyFence (init.device.device, data.in_flight_fences[i], nullptr);
}
vkDestroyCommandPool (init.device.device, data.command_pool, nullptr);
for (auto framebuffer : data.framebuffers)
{
vkDestroyFramebuffer (init.device.device, framebuffer, nullptr);
}
vkDestroyPipeline (init.device.device, data.graphics_pipeline, nullptr);
vkDestroyPipelineLayout (init.device.device, data.pipeline_layout, nullptr);
vkDestroyRenderPass (init.device.device, data.render_pass, nullptr);
for (auto imageView : data.swapchain_image_views)
{
vkDestroyImageView (init.device.device, imageView, nullptr);
}
vkb::destroy_swapchain (init.swapchain);
vkb::destroy_device (init.device);
vkDestroySurfaceKHR (init.instance.instance, init.surface, nullptr);
vkb::destroy_instance (init.instance);
destroy_window_glfw (init.window);
}
int main ()
{
Init init;
RenderData render_data;
int res = 0;
res = device_initialization (init);
res = get_queues (init, render_data);
res = create_render_pass (init, render_data);
res = create_graphics_pipeline (init, render_data);
res = create_framebuffers (init, render_data);
res = create_command_pool (init, render_data);
res = create_command_buffers (init, render_data);
res = create_sync_objects (init, render_data);
if (res != 0)
{
std::cout << "failed to initialize vulkan\n";
return -1;
}
while (!glfwWindowShouldClose (init.window))
{
glfwPollEvents ();
int res = draw_frame (init, render_data);
if (res != 0)
{
std::cout << "failed to draw frame \n";
return -1;
}
}
vkDeviceWaitIdle (init.device.device);
cleanup (init, render_data);
return 0;
}

View File

@ -6,7 +6,7 @@
namespace vkb namespace vkb
{ {
const char* DebugMessageSeverity (VkDebugUtilsMessageSeverityFlagBitsEXT s) const char* to_string_message_severity (VkDebugUtilsMessageSeverityFlagBitsEXT s)
{ {
switch (s) switch (s)
{ {
@ -22,7 +22,7 @@ const char* DebugMessageSeverity (VkDebugUtilsMessageSeverityFlagBitsEXT s)
return "UNKNOWN"; return "UNKNOWN";
} }
} }
const char* DebugMessageType (VkDebugUtilsMessageTypeFlagsEXT s) const char* to_string_message_type (VkDebugUtilsMessageTypeFlagsEXT s)
{ {
switch (s) switch (s)
{ {
@ -84,8 +84,8 @@ VkBool32 default_debug_callback (VkDebugUtilsMessageSeverityFlagBitsEXT messageS
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) void* pUserData)
{ {
auto ms = DebugMessageSeverity (messageSeverity); auto ms = to_string_message_severity (messageSeverity);
auto mt = DebugMessageType (messageType); auto mt = to_string_message_type (messageType);
printf ("[%s: %s]\n%s\n", ms, mt, pCallbackData->pMessage); printf ("[%s: %s]\n%s\n", ms, mt, pCallbackData->pMessage);
return VK_FALSE; return VK_FALSE;
} }
@ -130,11 +130,14 @@ void setup_pNext_chain (T& structure, std::vector<VkBaseOutStructure*>& structs)
void destroy_instance (Instance instance) void destroy_instance (Instance instance)
{ {
if (instance.debug_messenger != nullptr)
detail::destroy_debug_utils_messenger (instance.instance, instance.debug_messenger, instance.allocator);
if (instance.instance != VK_NULL_HANDLE) if (instance.instance != VK_NULL_HANDLE)
{
if (instance.debug_messenger != nullptr)
detail::destroy_debug_utils_messenger (
instance.instance, instance.debug_messenger, instance.allocator);
vkDestroyInstance (instance.instance, instance.allocator); vkDestroyInstance (instance.instance, instance.allocator);
} }
}
detail::Expected<Instance, VkResult> InstanceBuilder::build () detail::Expected<Instance, VkResult> InstanceBuilder::build ()
{ {
@ -961,6 +964,9 @@ SwapchainBuilder::SwapchainBuilder (Device const& device)
detail::Expected<Swapchain, VkResult> SwapchainBuilder::build () detail::Expected<Swapchain, VkResult> SwapchainBuilder::build ()
{ {
if (info.desired_formats.size () == 0) use_default_format_selection ();
if (info.desired_present_modes.size () == 0) use_default_present_mode_selection ();
auto surface_support = auto surface_support =
detail::query_surface_support_details (info.physical_device.phys_device, info.surface); detail::query_surface_support_details (info.physical_device.phys_device, info.surface);
if (!surface_support.has_value ()) return surface_support.error (); if (!surface_support.has_value ()) return surface_support.error ();
@ -1016,16 +1022,12 @@ detail::Expected<Swapchain, VkResult> SwapchainBuilder::build ()
{ {
return detail::Error{ res, "Failed to create swapchain" }; return detail::Error{ res, "Failed to create swapchain" };
} }
auto swapchain_images = swapchain.device = info.device;
detail::get_vector<VkImage> (vkGetSwapchainImagesKHR, info.device, swapchain.swapchain);
if (!swapchain_images)
{
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "Failed to get swapchain Images" };
}
swapchain.images = swapchain_images.value ();
swapchain.image_format = surface_format.format; swapchain.image_format = surface_format.format;
swapchain.extent = extent; swapchain.extent = extent;
auto images = get_swapchain_images (swapchain);
swapchain.image_count = images.value ().size ();
return swapchain; return swapchain;
} }
detail::Expected<Swapchain, VkResult> SwapchainBuilder::recreate (Swapchain const& swapchain) detail::Expected<Swapchain, VkResult> SwapchainBuilder::recreate (Swapchain const& swapchain)
@ -1033,11 +1035,52 @@ detail::Expected<Swapchain, VkResult> SwapchainBuilder::recreate (Swapchain cons
info.old_swapchain = swapchain.swapchain; info.old_swapchain = swapchain.swapchain;
return build (); return build ();
} }
detail::Expected<std::vector<VkImage>, VkResult> get_swapchain_images (Swapchain const& swapchain)
{
auto swapchain_images =
detail::get_vector<VkImage> (vkGetSwapchainImagesKHR, swapchain.device, swapchain.swapchain);
if (!swapchain_images)
{
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "Failed to get swapchain Images" };
}
return swapchain_images.value ();
}
detail::Expected<std::vector<VkImageView>, VkResult> get_swapchain_image_views (
Swapchain const& swapchain, std::vector<VkImage> const& images)
{
std::vector<VkImageView> views;
views.resize (swapchain.image_count);
for (size_t i = 0; i < swapchain.image_count; i++)
{
VkImageViewCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = images[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = swapchain.image_format;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView (swapchain.device, &createInfo, nullptr, &views[i]) != VK_SUCCESS)
{
return detail::Error{ VK_ERROR_INITIALIZATION_FAILED, "Failed to create image views" };
}
}
return views;
}
void destroy_swapchain (Swapchain const& swapchain) void destroy_swapchain (Swapchain const& swapchain)
{ {
if (swapchain.device != VK_NULL_HANDLE && swapchain.swapchain != VK_NULL_HANDLE && if (swapchain.device != VK_NULL_HANDLE && swapchain.swapchain != VK_NULL_HANDLE)
swapchain.allocator != VK_NULL_HANDLE)
vkDestroySwapchainKHR (swapchain.device, swapchain.swapchain, swapchain.allocator); vkDestroySwapchainKHR (swapchain.device, swapchain.swapchain, swapchain.allocator);
} }
@ -1072,8 +1115,6 @@ SwapchainBuilder& SwapchainBuilder::use_default_present_mode_selection ()
{ {
info.desired_present_modes.push_back (VK_PRESENT_MODE_MAILBOX_KHR); info.desired_present_modes.push_back (VK_PRESENT_MODE_MAILBOX_KHR);
info.desired_present_modes.push_back (VK_PRESENT_MODE_FIFO_KHR); info.desired_present_modes.push_back (VK_PRESENT_MODE_FIFO_KHR);
info.desired_present_modes.push_back (VK_PRESENT_MODE_FIFO_RELAXED_KHR);
info.desired_present_modes.push_back (VK_PRESENT_MODE_IMMEDIATE_KHR);
return *this; return *this;
} }

View File

@ -178,9 +178,8 @@ bool check_layers_supported (std::vector<const char*> layer_names);
} // namespace detail } // namespace detail
const char* DebugMessageSeverity (VkDebugUtilsMessageSeverityFlagBitsEXT s); const char* to_string_message_severity (VkDebugUtilsMessageSeverityFlagBitsEXT s);
const char* DebugMessageType (VkDebugUtilsMessageTypeFlagsEXT s); const char* to_string_message_type (VkDebugUtilsMessageTypeFlagsEXT s);
struct Instance struct Instance
{ {
@ -455,12 +454,15 @@ struct Swapchain
VkDevice device = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE;
VkSwapchainKHR swapchain = VK_NULL_HANDLE; VkSwapchainKHR swapchain = VK_NULL_HANDLE;
VkAllocationCallbacks* allocator = VK_NULL_HANDLE; VkAllocationCallbacks* allocator = VK_NULL_HANDLE;
std::vector<VkImage> images; uint32_t image_count = 0;
VkFormat image_format = VK_FORMAT_UNDEFINED; VkFormat image_format = VK_FORMAT_UNDEFINED;
VkExtent2D extent = { 0, 0 }; VkExtent2D extent = { 0, 0 };
}; };
void destroy_swapchain (Swapchain const& swapchain); void destroy_swapchain (Swapchain const& swapchain);
detail::Expected<std::vector<VkImage>, VkResult> get_swapchain_images (Swapchain const& swapchain);
detail::Expected<std::vector<VkImageView>, VkResult> get_swapchain_image_views (
Swapchain const& swapchain, std::vector<VkImage> const& images);
class SwapchainBuilder class SwapchainBuilder
{ {
@ -481,8 +483,6 @@ class SwapchainBuilder
SwapchainBuilder& add_fallback_present_mode (VkPresentModeKHR present_mode); SwapchainBuilder& add_fallback_present_mode (VkPresentModeKHR present_mode);
SwapchainBuilder& use_default_present_mode_selection (); SwapchainBuilder& use_default_present_mode_selection ();
private: private:
struct SwapchainInfo struct SwapchainInfo
{ {

View File

@ -10,10 +10,12 @@
#include "../src/VkBootstrap.h" #include "../src/VkBootstrap.h"
GLFWwindow* create_window_glfw () GLFWwindow* create_window_glfw (bool resize = true)
{ {
glfwInit (); glfwInit ();
glfwWindowHint (GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint (GLFW_CLIENT_API, GLFW_NO_API);
if (!resize) glfwWindowHint (GLFW_RESIZABLE, GLFW_FALSE);
return glfwCreateWindow (640, 480, "Vulkan Triangle", NULL, NULL); return glfwCreateWindow (640, 480, "Vulkan Triangle", NULL, NULL);
} }
void destroy_window_glfw (GLFWwindow* window) void destroy_window_glfw (GLFWwindow* window)

View File

@ -49,8 +49,8 @@ int test_instance_basic ()
VkDebugUtilsMessageTypeFlagsEXT messageType, VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) -> VkBool32 { void* pUserData) -> VkBool32 {
auto ms = vkb::DebugMessageSeverity (messageSeverity); auto ms = vkb::to_string_message_severity (messageSeverity);
auto mt = vkb::DebugMessageType (messageType); auto mt = vkb::to_string_message_type (messageType);
printf ("[%s: %s](user defined)\n%s\n", ms, mt, pCallbackData->pMessage); printf ("[%s: %s](user defined)\n%s\n", ms, mt, pCallbackData->pMessage);
return VK_FALSE; return VK_FALSE;
}) })

View File

@ -1,542 +0,0 @@
#include <stdio.h>
#include <memory>
#include <iostream>
#include <fstream>
#include "common.h"
struct Init
{
GLFWwindow* window;
vkb::Instance instance;
VkSurfaceKHR surface;
vkb::Device device;
vkb::Swapchain swapchain;
};
struct RenderData
{
VkQueue graphicsQueue;
VkQueue presentQueue;
std::vector<VkImageView> swapChainImageViews;
std::vector<VkFramebuffer> swapChainFramebuffers;
VkRenderPass renderPass;
VkPipelineLayout pipelineLayout;
VkPipeline graphicsPipeline;
VkCommandPool commandPool;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
std::vector<VkFence> imagesInFlight;
size_t currentFrame = 0;
};
void device_initialization (Init& init)
{
init.window = create_window_glfw ();
vkb::InstanceBuilder instance_builder;
auto instance_ret = instance_builder.set_default_debug_messenger ().setup_validation_layers ().build ();
if (!instance_ret)
{
std::cout << instance_ret.error ().msg << "\n";
}
init.instance = instance_ret.value ();
init.surface = create_surface_glfw (init.instance.instance, init.window);
vkb::PhysicalDeviceSelector phys_device_selector (init.instance);
auto phys_device_ret = phys_device_selector.set_surface (init.surface).select ();
if (!phys_device_ret)
{
std::cout << phys_device_ret.error ().msg << "\n";
}
vkb::PhysicalDevice physical_device = phys_device_ret.value ();
vkb::DeviceBuilder device_builder (physical_device);
auto device_ret = device_builder.build ();
if (!device_ret)
{
std::cout << device_ret.error ().msg << "\n";
}
init.device = device_ret.value ();
vkb::SwapchainBuilder swapchain_builder (init.device);
auto swap_ret =
swapchain_builder.use_default_format_selection ().use_default_present_mode_selection ().build ();
if (!swap_ret)
{
std::cout << swap_ret.error ().msg << "\n";
}
init.swapchain = swap_ret.value ();
}
void get_queues (Init& init, RenderData& data)
{
data.graphicsQueue = vkb::get_queue_graphics (init.device).value ();
data.presentQueue = vkb::get_queue_graphics (init.device).value ();
}
void createImageViews (Init& init, RenderData& data)
{
data.swapChainImageViews.resize (init.swapchain.images.size ());
for (size_t i = 0; i < init.swapchain.images.size (); i++)
{
VkImageViewCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = init.swapchain.images[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = init.swapchain.image_format;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView (init.device.device, &createInfo, nullptr, &data.swapChainImageViews[i]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create image views!");
}
}
}
void createRenderPass (Init& init, RenderData& data)
{
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = init.swapchain.image_format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
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;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 1;
renderPassInfo.pDependencies = &dependency;
if (vkCreateRenderPass (init.device.device, &renderPassInfo, nullptr, &data.renderPass) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create render pass!");
}
}
std::vector<char> readFile (const std::string& filename)
{
std::ifstream file (filename, std::ios::ate | std::ios::binary);
if (!file.is_open ())
{
throw std::runtime_error ("failed to open file!");
}
size_t fileSize = (size_t)file.tellg ();
std::vector<char> buffer (fileSize);
file.seekg (0);
file.read (buffer.data (), fileSize);
file.close ();
return buffer;
}
VkShaderModule createShaderModule (Init& init, const std::vector<char>& code)
{
VkShaderModuleCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = code.size ();
createInfo.pCode = reinterpret_cast<const uint32_t*> (code.data ());
VkShaderModule shaderModule;
if (vkCreateShaderModule (init.device.device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create shader module!");
}
return shaderModule;
}
void createGraphicsPipeline (Init& init, RenderData& data)
{
auto vertShaderCode = readFile ("vert.spv");
auto fragShaderCode = readFile ("frag.spv");
VkShaderModule vertShaderModule = createShaderModule (init, vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule (init, fragShaderCode);
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.vertexAttributeDescriptionCount = 0;
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)init.swapchain.extent.width;
viewport.height = (float)init.swapchain.extent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
scissor.offset = { 0, 0 };
scissor.extent = init.swapchain.extent;
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.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.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0;
pipelineLayoutInfo.pushConstantRangeCount = 0;
if (vkCreatePipelineLayout (init.device.device, &pipelineLayoutInfo, nullptr, &data.pipelineLayout) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create pipeline layout!");
}
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.layout = data.pipelineLayout;
pipelineInfo.renderPass = data.renderPass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
if (vkCreateGraphicsPipelines (
init.device.device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &data.graphicsPipeline) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create graphics pipeline!");
}
vkDestroyShaderModule (init.device.device, fragShaderModule, nullptr);
vkDestroyShaderModule (init.device.device, vertShaderModule, nullptr);
}
void createFramebuffers (Init& init, RenderData& data)
{
data.swapChainFramebuffers.resize (data.swapChainImageViews.size ());
for (size_t i = 0; i < data.swapChainImageViews.size (); i++)
{
VkImageView attachments[] = { data.swapChainImageViews[i] };
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = data.renderPass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = init.swapchain.extent.width;
framebufferInfo.height = init.swapchain.extent.height;
framebufferInfo.layers = 1;
if (vkCreateFramebuffer (
init.device.device, &framebufferInfo, nullptr, &data.swapChainFramebuffers[i]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create framebuffer!");
}
}
}
void createCommandPool (Init& init, RenderData& data)
{
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = vkb::get_queue_index_graphics (init.device);
if (vkCreateCommandPool (init.device.device, &poolInfo, nullptr, &data.commandPool) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create command pool!");
}
}
void createCommandBuffers (Init& init, RenderData& data)
{
data.commandBuffers.resize (data.swapChainFramebuffers.size ());
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = data.commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t)data.commandBuffers.size ();
if (vkAllocateCommandBuffers (init.device.device, &allocInfo, data.commandBuffers.data ()) != VK_SUCCESS)
{
throw std::runtime_error ("failed to allocate command buffers!");
}
for (size_t i = 0; i < data.commandBuffers.size (); i++)
{
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (vkBeginCommandBuffer (data.commandBuffers[i], &beginInfo) != VK_SUCCESS)
{
throw std::runtime_error ("failed to begin recording command buffer!");
}
VkRenderPassBeginInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = data.renderPass;
renderPassInfo.framebuffer = data.swapChainFramebuffers[i];
renderPassInfo.renderArea.offset = { 0, 0 };
renderPassInfo.renderArea.extent = init.swapchain.extent;
VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass (data.commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline (data.commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, data.graphicsPipeline);
vkCmdDraw (data.commandBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass (data.commandBuffers[i]);
if (vkEndCommandBuffer (data.commandBuffers[i]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to record command buffer!");
}
}
}
void createSyncObjects (Init& init, RenderData& data)
{
data.imageAvailableSemaphores.resize (init.swapchain.images.size ());
data.renderFinishedSemaphores.resize (init.swapchain.images.size ());
data.inFlightFences.resize (init.swapchain.images.size ());
data.imagesInFlight.resize (init.swapchain.images.size (), VK_NULL_HANDLE);
VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (size_t i = 0; i < init.swapchain.images.size (); i++)
{
if (vkCreateSemaphore (
init.device.device, &semaphoreInfo, nullptr, &data.imageAvailableSemaphores[i]) != VK_SUCCESS ||
vkCreateSemaphore (
init.device.device, &semaphoreInfo, nullptr, &data.renderFinishedSemaphores[i]) != VK_SUCCESS ||
vkCreateFence (init.device.device, &fenceInfo, nullptr, &data.inFlightFences[i]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to create synchronization objects for a frame!");
}
}
}
void drawFrame (Init& init, RenderData& data)
{
vkWaitForFences (init.device.device, 1, &data.inFlightFences[data.currentFrame], VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
vkAcquireNextImageKHR (init.device.device,
init.swapchain.swapchain,
UINT64_MAX,
data.imageAvailableSemaphores[data.currentFrame],
VK_NULL_HANDLE,
&imageIndex);
if (data.imagesInFlight[imageIndex] != VK_NULL_HANDLE)
{
vkWaitForFences (init.device.device, 1, &data.imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
}
data.imagesInFlight[imageIndex] = data.inFlightFences[data.currentFrame];
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = { data.imageAvailableSemaphores[data.currentFrame] };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &data.commandBuffers[imageIndex];
VkSemaphore signalSemaphores[] = { data.renderFinishedSemaphores[data.currentFrame] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
vkResetFences (init.device.device, 1, &data.inFlightFences[data.currentFrame]);
if (vkQueueSubmit (data.graphicsQueue, 1, &submitInfo, data.inFlightFences[data.currentFrame]) != VK_SUCCESS)
{
throw std::runtime_error ("failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { init.swapchain.swapchain };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
vkQueuePresentKHR (data.presentQueue, &presentInfo);
data.currentFrame = (data.currentFrame + 1) % init.swapchain.images.size ();
}
void cleanup (Init& init, RenderData& data)
{
for (size_t i = 0; i < init.swapchain.images.size (); i++)
{
vkDestroySemaphore (init.device.device, data.renderFinishedSemaphores[i], nullptr);
vkDestroySemaphore (init.device.device, data.imageAvailableSemaphores[i], nullptr);
vkDestroyFence (init.device.device, data.inFlightFences[i], nullptr);
}
vkDestroyCommandPool (init.device.device, data.commandPool, nullptr);
for (auto framebuffer : data.swapChainFramebuffers)
{
vkDestroyFramebuffer (init.device.device, framebuffer, nullptr);
}
vkDestroyPipeline (init.device.device, data.graphicsPipeline, nullptr);
vkDestroyPipelineLayout (init.device.device, data.pipelineLayout, nullptr);
vkDestroyRenderPass (init.device.device, data.renderPass, nullptr);
for (auto imageView : data.swapChainImageViews)
{
vkDestroyImageView (init.device.device, imageView, nullptr);
}
vkb::destroy_swapchain (init.swapchain);
vkb::destroy_device (init.device);
vkDestroySurfaceKHR (init.instance.instance, init.surface, nullptr);
vkb::destroy_instance (init.instance);
destroy_window_glfw (init.window);
}
int main ()
{
Init init;
RenderData render_data;
device_initialization (init);
get_queues (init, render_data);
createImageViews (init, render_data);
createRenderPass (init, render_data);
createGraphicsPipeline (init, render_data);
createFramebuffers (init, render_data);
createCommandPool (init, render_data);
createCommandBuffers (init, render_data);
createSyncObjects (init, render_data);
while (!glfwWindowShouldClose (init.window))
{
glfwPollEvents ();
drawFrame (init, render_data);
}
vkDeviceWaitIdle (init.device.device);
cleanup (init, render_data);
return 0;
}