diff --git a/example/triangle.cpp b/example/triangle.cpp index 2029212..76602de 100644 --- a/example/triangle.cpp +++ b/example/triangle.cpp @@ -6,6 +6,7 @@ #include #include "../tests/common.h" +#include "VkPipelineBuilder.h" #include "example_config.h" @@ -13,13 +14,13 @@ const int MAX_FRAMES_IN_FLIGHT = 2; struct Init { GLFWwindow* window; - VulkanLibrary vk_lib; + VulkanLibrary vk_lib; vkb::Instance instance; VkSurfaceKHR surface; vkb::Device device; vkb::Swapchain swapchain; - //convenience - VulkanLibrary* operator->(){ return &vk_lib; } + // convenience + VulkanLibrary* operator->() { return &vk_lib; } }; struct RenderData { @@ -44,72 +45,72 @@ struct RenderData { size_t current_frame = 0; }; -int device_initialization (Init& init) { - init.window = create_window_glfw ("Vulkan Triangle", true); +int device_initialization(Init& init) { + init.window = create_window_glfw("Vulkan Triangle", true); vkb::InstanceBuilder instance_builder; - auto instance_ret = instance_builder.use_default_debug_messenger ().request_validation_layers ().build (); + auto instance_ret = instance_builder.use_default_debug_messenger().request_validation_layers().build(); if (!instance_ret) { - std::cout << instance_ret.error ().message () << "\n"; + std::cout << instance_ret.error().message() << "\n"; return -1; } - init.instance = instance_ret.value (); + init.instance = instance_ret.value(); - init.vk_lib.init(init.instance); + init.vk_lib.init(init.instance); - init.surface = create_surface_glfw (init.instance, init.window); + init.surface = create_surface_glfw(init.instance, init.window); - vkb::PhysicalDeviceSelector phys_device_selector (init.instance); - auto phys_device_ret = phys_device_selector.set_surface (init.surface).select (); + 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 ().message () << "\n"; + std::cout << phys_device_ret.error().message() << "\n"; return -1; } - vkb::PhysicalDevice physical_device = phys_device_ret.value (); + vkb::PhysicalDevice physical_device = phys_device_ret.value(); vkb::DeviceBuilder device_builder{ physical_device }; - auto device_ret = device_builder.build (); + auto device_ret = device_builder.build(); if (!device_ret) { - std::cout << device_ret.error ().message () << "\n"; + std::cout << device_ret.error().message() << "\n"; return -1; } - init.device = device_ret.value (); - init.vk_lib.init(init.device); + init.device = device_ret.value(); + init.vk_lib.init(init.device); return 0; } -int create_swapchain (Init& init) { +int create_swapchain(Init& init) { vkb::SwapchainBuilder swapchain_builder{ init.device }; - auto swap_ret = swapchain_builder.set_old_swapchain (init.swapchain).build (); + auto swap_ret = swapchain_builder.set_old_swapchain(init.swapchain).build(); if (!swap_ret) { - std::cout << swap_ret.error ().message () << " " << swap_ret.vk_result () << "\n"; - return -1; + std::cout << swap_ret.error().message() << " " << swap_ret.vk_result() << "\n"; + return -1; } - vkb::destroy_swapchain(init.swapchain); - init.swapchain = swap_ret.value (); + vkb::destroy_swapchain(init.swapchain); + init.swapchain = swap_ret.value(); return 0; } -int get_queues (Init& init, RenderData& data) { - auto gq = init.device.get_queue (vkb::QueueType::graphics); - if (!gq.has_value ()) { - std::cout << "failed to get graphics queue: " << gq.error ().message () << "\n"; +int get_queues(Init& init, RenderData& data) { + auto gq = init.device.get_queue(vkb::QueueType::graphics); + if (!gq.has_value()) { + std::cout << "failed to get graphics queue: " << gq.error().message() << "\n"; return -1; } - data.graphics_queue = gq.value (); + data.graphics_queue = gq.value(); - auto pq = init.device.get_queue (vkb::QueueType::present); - if (!pq.has_value ()) { - std::cout << "failed to get present queue: " << pq.error ().message () << "\n"; + auto pq = init.device.get_queue(vkb::QueueType::present); + if (!pq.has_value()) { + std::cout << "failed to get present queue: " << pq.error().message() << "\n"; return -1; } - data.present_queue = pq.value (); + data.present_queue = pq.value(); return 0; } -int create_render_pass (Init& init, RenderData& data) { +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; @@ -146,51 +147,69 @@ int create_render_pass (Init& init, RenderData& data) { render_pass_info.dependencyCount = 1; render_pass_info.pDependencies = &dependency; - if (init->vkCreateRenderPass (init.device, &render_pass_info, nullptr, &data.render_pass) != VK_SUCCESS) { + if (init->vkCreateRenderPass(init.device, &render_pass_info, nullptr, &data.render_pass) != VK_SUCCESS) { std::cout << "failed to create render pass\n"; return -1; // failed to create render pass! } return 0; } -std::vector readFile (const std::string& filename) { - std::ifstream file (filename, std::ios::ate | std::ios::binary); +std::vector 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!"); + if (!file.is_open()) { + throw std::runtime_error("failed to open file!"); } - size_t file_size = (size_t)file.tellg (); - std::vector buffer (file_size); + size_t file_size = (size_t)file.tellg(); + std::vector buffer(file_size); - file.seekg (0); - file.read (buffer.data (), static_cast (file_size)); + file.seekg(0); + file.read(buffer.data(), static_cast(file_size)); - file.close (); + file.close(); return buffer; } -VkShaderModule createShaderModule (Init& init, const std::vector& code) { +VkShaderModule createShaderModule(Init& init, const std::vector& code) { VkShaderModuleCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - create_info.codeSize = code.size (); - create_info.pCode = reinterpret_cast (code.data ()); + create_info.codeSize = code.size(); + create_info.pCode = reinterpret_cast(code.data()); VkShaderModule shaderModule; - if (init->vkCreateShaderModule (init.device, &create_info, nullptr, &shaderModule) != VK_SUCCESS) { + if (init->vkCreateShaderModule(init.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) { +int create_graphics_pipeline_new(Init& init, RenderData& data) { + vkb::GraphicsPipelineBuilder gpb{ init.device }; + auto vert_code = readFile(std::string(EXAMPLE_BUILD_DIRECTORY) + "/vert.spv"); auto frag_code = readFile(std::string(EXAMPLE_BUILD_DIRECTORY) + "/frag.spv"); - VkShaderModule vert_module = createShaderModule (init, vert_code); - VkShaderModule frag_module = createShaderModule (init, frag_code); + 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) { + std::cout << "failed to create shader module\n"; + return -1; // failed to create shader modules + } + + gpb.set_vertex_shader_entrypoint_name() + + return 0; +} + +int create_graphics_pipeline(Init& init, RenderData& data) { + auto vert_code = readFile(std::string(EXAMPLE_BUILD_DIRECTORY) + "/vert.spv"); + auto frag_code = readFile(std::string(EXAMPLE_BUILD_DIRECTORY) + "/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) { std::cout << "failed to create shader module\n"; return -1; // failed to create shader modules @@ -255,8 +274,8 @@ int create_graphics_pipeline (Init& init, RenderData& data) { 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.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 = {}; @@ -275,8 +294,7 @@ int create_graphics_pipeline (Init& init, RenderData& data) { pipeline_layout_info.setLayoutCount = 0; pipeline_layout_info.pushConstantRangeCount = 0; - if (init->vkCreatePipelineLayout ( - init.device, &pipeline_layout_info, nullptr, &data.pipeline_layout) != VK_SUCCESS) { + if (init->vkCreatePipelineLayout(init.device, &pipeline_layout_info, nullptr, &data.pipeline_layout) != VK_SUCCESS) { std::cout << "failed to create pipeline layout\n"; return -1; // failed to create pipeline layout } @@ -285,8 +303,8 @@ int create_graphics_pipeline (Init& init, RenderData& data) { VkPipelineDynamicStateCreateInfo dynamic_info = {}; dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamic_info.dynamicStateCount = static_cast (dynamic_states.size ()); - dynamic_info.pDynamicStates = dynamic_states.data (); + dynamic_info.dynamicStateCount = static_cast(dynamic_states.size()); + dynamic_info.pDynamicStates = dynamic_states.data(); VkGraphicsPipelineCreateInfo pipeline_info = {}; pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; @@ -304,24 +322,23 @@ int create_graphics_pipeline (Init& init, RenderData& data) { pipeline_info.subpass = 0; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; - if (init->vkCreateGraphicsPipelines ( - init.device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &data.graphics_pipeline) != VK_SUCCESS) { + if (init->vkCreateGraphicsPipelines(init.device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &data.graphics_pipeline) != VK_SUCCESS) { std::cout << "failed to create pipline\n"; return -1; // failed to create graphics pipeline } - init->vkDestroyShaderModule (init.device, frag_module, nullptr); - init->vkDestroyShaderModule (init.device, vert_module, nullptr); + init->vkDestroyShaderModule(init.device, frag_module, nullptr); + init->vkDestroyShaderModule(init.device, vert_module, nullptr); return 0; } -int create_framebuffers (Init& init, RenderData& data) { - data.swapchain_images = init.swapchain.get_images ().value (); - data.swapchain_image_views = init.swapchain.get_image_views ().value (); +int create_framebuffers(Init& init, RenderData& data) { + data.swapchain_images = init.swapchain.get_images().value(); + data.swapchain_image_views = init.swapchain.get_image_views().value(); - data.framebuffers.resize (data.swapchain_image_views.size ()); + data.framebuffers.resize(data.swapchain_image_views.size()); - for (size_t i = 0; i < data.swapchain_image_views.size (); i++) { + for (size_t i = 0; i < data.swapchain_image_views.size(); i++) { VkImageView attachments[] = { data.swapchain_image_views[i] }; VkFramebufferCreateInfo framebuffer_info = {}; @@ -333,43 +350,43 @@ int create_framebuffers (Init& init, RenderData& data) { framebuffer_info.height = init.swapchain.extent.height; framebuffer_info.layers = 1; - if (init->vkCreateFramebuffer (init.device, &framebuffer_info, nullptr, &data.framebuffers[i]) != VK_SUCCESS) { + if (init->vkCreateFramebuffer(init.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) { +int create_command_pool(Init& init, RenderData& data) { VkCommandPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - pool_info.queueFamilyIndex = init.device.get_queue_index (vkb::QueueType::graphics).value (); + pool_info.queueFamilyIndex = init.device.get_queue_index(vkb::QueueType::graphics).value(); - if (init->vkCreateCommandPool (init.device, &pool_info, nullptr, &data.command_pool) != VK_SUCCESS) { + if (init->vkCreateCommandPool(init.device, &pool_info, nullptr, &data.command_pool) != VK_SUCCESS) { std::cout << "failed to create command pool\n"; return -1; // failed to create command pool } return 0; } -int create_command_buffers (Init& init, RenderData& data) { - data.command_buffers.resize (data.framebuffers.size ()); +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 (); + allocInfo.commandBufferCount = (uint32_t)data.command_buffers.size(); - if (init->vkAllocateCommandBuffers (init.device, &allocInfo, data.command_buffers.data ()) != VK_SUCCESS) { + if (init->vkAllocateCommandBuffers(init.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++) { + 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 (init->vkBeginCommandBuffer (data.command_buffers[i], &begin_info) != VK_SUCCESS) { + if (init->vkBeginCommandBuffer(data.command_buffers[i], &begin_info) != VK_SUCCESS) { return -1; // failed to begin recording command buffer } @@ -395,18 +412,18 @@ int create_command_buffers (Init& init, RenderData& data) { scissor.offset = { 0, 0 }; scissor.extent = init.swapchain.extent; - init->vkCmdSetViewport (data.command_buffers[i], 0, 1, &viewport); - init->vkCmdSetScissor (data.command_buffers[i], 0, 1, &scissor); + init->vkCmdSetViewport(data.command_buffers[i], 0, 1, &viewport); + init->vkCmdSetScissor(data.command_buffers[i], 0, 1, &scissor); - init->vkCmdBeginRenderPass (data.command_buffers[i], &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); + init->vkCmdBeginRenderPass(data.command_buffers[i], &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); - init->vkCmdBindPipeline (data.command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, data.graphics_pipeline); + init->vkCmdBindPipeline(data.command_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, data.graphics_pipeline); - init->vkCmdDraw (data.command_buffers[i], 3, 1, 0, 0); + init->vkCmdDraw(data.command_buffers[i], 3, 1, 0, 0); - init->vkCmdEndRenderPass (data.command_buffers[i]); + init->vkCmdEndRenderPass(data.command_buffers[i]); - if (init->vkEndCommandBuffer (data.command_buffers[i]) != VK_SUCCESS) { + if (init->vkEndCommandBuffer(data.command_buffers[i]) != VK_SUCCESS) { std::cout << "failed to record command buffer\n"; return -1; // failed to record command buffer! } @@ -414,11 +431,11 @@ int create_command_buffers (Init& init, RenderData& data) { 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); +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; @@ -428,9 +445,9 @@ int create_sync_objects (Init& init, RenderData& data) { fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - if (init->vkCreateSemaphore (init.device, &semaphore_info, nullptr, &data.available_semaphores[i]) != VK_SUCCESS || - init->vkCreateSemaphore (init.device, &semaphore_info, nullptr, &data.finished_semaphore[i]) != VK_SUCCESS || - init->vkCreateFence (init.device, &fence_info, nullptr, &data.in_flight_fences[i]) != VK_SUCCESS) { + if (init->vkCreateSemaphore(init.device, &semaphore_info, nullptr, &data.available_semaphores[i]) != VK_SUCCESS || + init->vkCreateSemaphore(init.device, &semaphore_info, nullptr, &data.finished_semaphore[i]) != VK_SUCCESS || + init->vkCreateFence(init.device, &fence_info, nullptr, &data.in_flight_fences[i]) != VK_SUCCESS) { std::cout << "failed to create sync objects\n"; return -1; // failed to create synchronization objects for a frame } @@ -438,44 +455,40 @@ int create_sync_objects (Init& init, RenderData& data) { return 0; } -int recreate_swapchain (Init& init, RenderData& data) { - init->vkDeviceWaitIdle (init.device); +int recreate_swapchain(Init& init, RenderData& data) { + init->vkDeviceWaitIdle(init.device); - init->vkDestroyCommandPool (init.device, data.command_pool, nullptr); + init->vkDestroyCommandPool(init.device, data.command_pool, nullptr); for (auto framebuffer : data.framebuffers) { - init->vkDestroyFramebuffer (init.device, framebuffer, nullptr); + init->vkDestroyFramebuffer(init.device, framebuffer, nullptr); } - init.swapchain.destroy_image_views (data.swapchain_image_views); + init.swapchain.destroy_image_views(data.swapchain_image_views); - if (0 != create_swapchain (init)) return -1; - if (0 != create_framebuffers (init, data)) return -1; - if (0 != create_command_pool (init, data)) return -1; - if (0 != create_command_buffers (init, data)) return -1; + if (0 != create_swapchain(init)) return -1; + if (0 != create_framebuffers(init, data)) return -1; + if (0 != create_command_pool(init, data)) return -1; + if (0 != create_command_buffers(init, data)) return -1; return 0; } -int draw_frame (Init& init, RenderData& data) { - init->vkWaitForFences (init.device, 1, &data.in_flight_fences[data.current_frame], VK_TRUE, UINT64_MAX); +int draw_frame(Init& init, RenderData& data) { + init->vkWaitForFences(init.device, 1, &data.in_flight_fences[data.current_frame], VK_TRUE, UINT64_MAX); uint32_t image_index = 0; - VkResult result = init->vkAcquireNextImageKHR (init.device, - init.swapchain, - UINT64_MAX, - data.available_semaphores[data.current_frame], - VK_NULL_HANDLE, - &image_index); + VkResult result = init->vkAcquireNextImageKHR( + init.device, init.swapchain, UINT64_MAX, data.available_semaphores[data.current_frame], VK_NULL_HANDLE, &image_index); if (result == VK_ERROR_OUT_OF_DATE_KHR) { - return recreate_swapchain (init, data); + return recreate_swapchain(init, data); } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { std::cout << "failed to acquire swapchain image. Error " << result << "\n"; return -1; } if (data.image_in_flight[image_index] != VK_NULL_HANDLE) { - init->vkWaitForFences (init.device, 1, &data.image_in_flight[image_index], VK_TRUE, UINT64_MAX); + init->vkWaitForFences(init.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]; @@ -495,9 +508,9 @@ int draw_frame (Init& init, RenderData& data) { submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signal_semaphores; - init->vkResetFences (init.device, 1, &data.in_flight_fences[data.current_frame]); + init->vkResetFences(init.device, 1, &data.in_flight_fences[data.current_frame]); - if (init->vkQueueSubmit (data.graphics_queue, 1, &submitInfo, data.in_flight_fences[data.current_frame]) != VK_SUCCESS) { + if (init->vkQueueSubmit(data.graphics_queue, 1, &submitInfo, data.in_flight_fences[data.current_frame]) != VK_SUCCESS) { std::cout << "failed to submit draw command buffer\n"; return -1; //"failed to submit draw command buffer } @@ -514,9 +527,9 @@ int draw_frame (Init& init, RenderData& data) { present_info.pImageIndices = &image_index; - result = init->vkQueuePresentKHR (data.present_queue, &present_info); + result = init->vkQueuePresentKHR(data.present_queue, &present_info); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { - return recreate_swapchain (init, data); + return recreate_swapchain(init, data); } else if (result != VK_SUCCESS) { std::cout << "failed to present swapchain image\n"; return -1; @@ -526,56 +539,56 @@ int draw_frame (Init& init, RenderData& data) { return 0; } -void cleanup (Init& init, RenderData& data) { +void cleanup(Init& init, RenderData& data) { for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - init->vkDestroySemaphore (init.device, data.finished_semaphore[i], nullptr); - init->vkDestroySemaphore (init.device, data.available_semaphores[i], nullptr); - init->vkDestroyFence (init.device, data.in_flight_fences[i], nullptr); + init->vkDestroySemaphore(init.device, data.finished_semaphore[i], nullptr); + init->vkDestroySemaphore(init.device, data.available_semaphores[i], nullptr); + init->vkDestroyFence(init.device, data.in_flight_fences[i], nullptr); } - init->vkDestroyCommandPool (init.device, data.command_pool, nullptr); + init->vkDestroyCommandPool(init.device, data.command_pool, nullptr); for (auto framebuffer : data.framebuffers) { - init->vkDestroyFramebuffer (init.device, framebuffer, nullptr); + init->vkDestroyFramebuffer(init.device, framebuffer, nullptr); } - init->vkDestroyPipeline (init.device, data.graphics_pipeline, nullptr); - init->vkDestroyPipelineLayout (init.device, data.pipeline_layout, nullptr); - init->vkDestroyRenderPass (init.device, data.render_pass, nullptr); + init->vkDestroyPipeline(init.device, data.graphics_pipeline, nullptr); + init->vkDestroyPipelineLayout(init.device, data.pipeline_layout, nullptr); + init->vkDestroyRenderPass(init.device, data.render_pass, nullptr); - init.swapchain.destroy_image_views (data.swapchain_image_views); + init.swapchain.destroy_image_views(data.swapchain_image_views); - vkb::destroy_swapchain (init.swapchain); - vkb::destroy_device (init.device); - vkb::destroy_surface(init.instance, init.surface); - vkb::destroy_instance (init.instance); - destroy_window_glfw (init.window); + vkb::destroy_swapchain(init.swapchain); + vkb::destroy_device(init.device); + vkb::destroy_surface(init.instance, init.surface); + vkb::destroy_instance(init.instance); + destroy_window_glfw(init.window); } -int main () { +int main() { Init init; RenderData render_data; - if (0 != device_initialization (init)) return -1; - if (0 != create_swapchain (init)) return -1; - if (0 != get_queues (init, render_data)) return -1; - if (0 != create_render_pass (init, render_data)) return -1; - if (0 != create_graphics_pipeline (init, render_data)) return -1; - if (0 != create_framebuffers (init, render_data)) return -1; - if (0 != create_command_pool (init, render_data)) return -1; - if (0 != create_command_buffers (init, render_data)) return -1; - if (0 != create_sync_objects (init, render_data)) return -1; + if (0 != device_initialization(init)) return -1; + if (0 != create_swapchain(init)) return -1; + if (0 != get_queues(init, render_data)) return -1; + if (0 != create_render_pass(init, render_data)) return -1; + if (0 != create_graphics_pipeline(init, render_data)) return -1; + if (0 != create_framebuffers(init, render_data)) return -1; + if (0 != create_command_pool(init, render_data)) return -1; + if (0 != create_command_buffers(init, render_data)) return -1; + if (0 != create_sync_objects(init, render_data)) return -1; - while (!glfwWindowShouldClose (init.window)) { - glfwPollEvents (); - int res = draw_frame (init, render_data); + while (!glfwWindowShouldClose(init.window)) { + glfwPollEvents(); + int res = draw_frame(init, render_data); if (res != 0) { std::cout << "failed to draw frame \n"; return -1; } } - init->vkDeviceWaitIdle (init.device); + init->vkDeviceWaitIdle(init.device); - cleanup (init, render_data); + cleanup(init, render_data); return 0; -} \ No newline at end of file +} diff --git a/src/VkBootstrap.cpp b/src/VkBootstrap.cpp index 6ee9851..d71f50d 100644 --- a/src/VkBootstrap.cpp +++ b/src/VkBootstrap.cpp @@ -15,6 +15,7 @@ */ #include "VkBootstrap.h" +#include "VkPipelineBuilder.h" #include @@ -365,11 +366,23 @@ struct DeviceErrorCategory : std::error_category { const DeviceErrorCategory device_error_category; struct SwapchainErrorCategory : std::error_category { - const char* name() const noexcept override { return "vbk_swapchain"; } + const char* name() const noexcept override { return "vkb_swapchain"; } std::string message(int err) const override { return to_string(static_cast(err)); } }; const SwapchainErrorCategory swapchain_error_category; +struct PipelineLayoutErrorCategory : std::error_category { + const char* name() const noexcept override { return "vkb_pipeline_layout"; } + std::string message(int err) const override { return to_string(static_cast(err)); } +}; +const PipelineLayoutErrorCategory pipeline_layout_error_category; + +struct GraphicsPipelineErrorCategory : std::error_category { + const char* name() const noexcept override { return "vkb_graphics_pipeline"; } + std::string message(int err) const override { return to_string(static_cast(err)); } +}; +const GraphicsPipelineErrorCategory graphics_pipeline_error_category; + } // namespace detail std::error_code make_error_code(InstanceError instance_error) { @@ -387,6 +400,13 @@ std::error_code make_error_code(DeviceError device_error) { std::error_code make_error_code(SwapchainError swapchain_error) { return { static_cast(swapchain_error), detail::swapchain_error_category }; } +std::error_code make_error_code(PipelineLayoutError pipeline_layout_error) { + return { static_cast(pipeline_layout_error), detail::pipeline_layout_error_category }; +} +std::error_code make_error_code(GraphicsPipelineError graphics_pipeline_error) { + return { static_cast(graphics_pipeline_error), detail::graphics_pipeline_error_category }; +} + #define CASE_TO_STRING(CATEGORY, TYPE) \ case CATEGORY::TYPE: \ return #TYPE; @@ -446,6 +466,26 @@ const char* to_string(SwapchainError err) { return ""; } } +const char* to_string(PipelineLayoutError err) { + switch (err) { + case PipelineLayoutError::device_handle_not_provided: + return "device_handle_not_provided"; + case PipelineLayoutError::failed_to_create_pipeline_layout: + return "failed_to_create_pipeline_layout"; + default: + return ""; + } +} +const char* to_string(GraphicsPipelineError err) { + switch (err) { + case GraphicsPipelineError::device_handle_not_provided: + return "device_handle_not_provided"; + case GraphicsPipelineError::failed_to_create_graphics_pipeline: + return "failed_to_create_graphics_pipeline"; + default: + return ""; + } +} Result SystemInfo::get_system_info() { if (!detail::vulkan_functions().init_vulkan_funcs(nullptr)) { @@ -2017,4 +2057,741 @@ void SwapchainBuilder::add_desired_present_modes(std::vector& modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); modes.push_back(VK_PRESENT_MODE_FIFO_KHR); } + +// ---- PipelineLayout ---- // +PipelineLayoutBuilder& PipelineLayoutBuilder::set_pipeline_layout_flags(VkPipelineLayoutCreateFlags layout_flags) { + info.flags = layout_flags; + return *this; +} +PipelineLayoutBuilder::PipelineLayoutBuilder(Device const& device) { + info.device = device.device; + detail::vulkan_functions().get_device_proc_addr(info.device, info.pipeline_layout_create_proc, "vkCreatePipelineLayout"); +} +PipelineLayoutBuilder::PipelineLayoutBuilder(VkDevice const device) { + info.device = device; + detail::vulkan_functions().get_device_proc_addr(info.device, info.pipeline_layout_create_proc, "vkCreatePipelineLayout"); +} +PipelineLayoutBuilder& PipelineLayoutBuilder::add_descriptor_layout(VkDescriptorSetLayout descriptor_set_layout) { + info.descriptor_layouts.push_back(descriptor_set_layout); + return *this; +} +PipelineLayoutBuilder& PipelineLayoutBuilder::add_descriptor_layouts(std::vector descriptor_set_layouts) { + for (const auto& layout : descriptor_set_layouts) + info.descriptor_layouts.push_back(layout); + return *this; +} +PipelineLayoutBuilder& PipelineLayoutBuilder::clear_descriptor_layouts() { + info.descriptor_layouts.clear(); + return *this; +} +PipelineLayoutBuilder& PipelineLayoutBuilder::add_push_constant_range(VkPushConstantRange push_constant_range) { + info.push_constant_ranges.push_back(push_constant_range); + return *this; +} +PipelineLayoutBuilder& PipelineLayoutBuilder::add_push_constant_ranges(std::vector push_constant_ranges) { + for (const auto& range : push_constant_ranges) + info.push_constant_ranges.push_back(range); + return *this; +} +PipelineLayoutBuilder& PipelineLayoutBuilder::clear_push_constant_ranges() { + info.push_constant_ranges.clear(); + return *this; +} +PipelineLayoutBuilder& PipelineLayoutBuilder::clear_pNext_chain() { + info.pNext_chain.clear(); + return *this; +} +PipelineLayoutBuilder& PipelineLayoutBuilder::set_allocation_callbacks(VkAllocationCallbacks* callbacks) { + info.allocation_callbacks = callbacks; + return *this; +} +Result PipelineLayoutBuilder::build() const { + if (info.device == VK_NULL_HANDLE) return Error{ PipelineLayoutError::device_handle_not_provided }; + + VkPipelineLayoutCreateInfo pipeline_layout_create_info = {}; + pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + detail::setup_pNext_chain(pipeline_layout_create_info, info.pNext_chain); +#if !defined(NDEBUG) + for (auto& node : info.pNext_chain) + assert(node->sType != VK_STRUCTURE_TYPE_APPLICATION_INFO); +#endif + pipeline_layout_create_info.flags = info.flags; + pipeline_layout_create_info.setLayoutCount = static_cast(info.descriptor_layouts.size()); + pipeline_layout_create_info.pSetLayouts = info.descriptor_layouts.data(); + pipeline_layout_create_info.pushConstantRangeCount = static_cast(info.push_constant_ranges.size()); + pipeline_layout_create_info.pPushConstantRanges = info.push_constant_ranges.data(); + + VkPipelineLayout pipeline_layout{}; + VkResult res = info.pipeline_layout_create_proc( + info.device, &pipeline_layout_create_info, info.allocation_callbacks, &pipeline_layout); + + if (res != VK_SUCCESS) return Error{ PipelineLayoutError::failed_to_create_pipeline_layout, res }; + + return pipeline_layout; +} + +// ---- Graphics Pipeline ---- // + +GraphicsPipelineBuilder::GraphicsPipelineBuilder(Device const& device, VkPipelineCache pipeline_cache) { + info.device = device.device; + info.pipeline_cache = pipeline_cache; + detail::vulkan_functions().get_device_proc_addr(info.device, info.graphics_pipeline_create_proc, "vkCreateGraphicsPipelines"); +} +GraphicsPipelineBuilder::GraphicsPipelineBuilder(VkDevice const device, VkPipelineCache pipeline_cache) { + info.device = device; + info.pipeline_cache = pipeline_cache; + detail::vulkan_functions().get_device_proc_addr(info.device, info.graphics_pipeline_create_proc, "vkCreateGraphicsPipelines"); +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_allocation_callbacks(VkAllocationCallbacks* callbacks) { + info.allocation_callbacks = callbacks; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_pNext() { + info.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_pipeline_create_flags(VkPipelineCreateFlags pipeline_create_flags) { + info.flags = pipeline_create_flags; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_pipeline_layout(VkPipelineLayout pipeline_layout) { + info.pipeline_layout = pipeline_layout; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_renderpass(VkRenderPass renderpass, uint32_t subpass_index) { + info.renderpass = renderpass; + info.subpass = subpass_index; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_base_pipeline(VkPipeline base_pipeline, uint32_t base_pipeline_index) { + info.base_pipeline = base_pipeline; + info.base_pipeline_index = base_pipeline_index; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_additional_shader_stage(VkPipelineShaderStageCreateInfo& shader_stage_info) { + info.additional_shader_stages.push_back(shader_stage_info); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_additional_shader_stages( + std::vector shader_stage_infos) { + for (auto shader_stage_info : shader_stage_infos) + info.additional_shader_stages.push_back(shader_stage_info); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_additional_shader_stages() { + info.additional_shader_stages.clear(); + return *this; +} + +// Vertex input state +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_vertex_input_pNext() { + info.vertex_input.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_vertex_input_binding_desc(VkVertexInputBindingDescription vertex_input_binding_desc) { + info.vertex_input.binding_descs.push_back(vertex_input_binding_desc); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_vertex_input_binding_descs( + std::vector vertex_input_binding_descs) { + for (auto binding_desc : vertex_input_binding_descs) + info.vertex_input.binding_descs.push_back(binding_desc); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_vertex_input_binding_descs() { + info.vertex_input.binding_descs.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_vertex_input_attrib_desc(VkVertexInputAttributeDescription vertex_input_attrib_desc) { + info.vertex_input.attrib_descs.push_back(vertex_input_attrib_desc); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_vertex_input_attrib_descs( + std::vector vertex_input_attrib_descs) { + for (auto attrib_desc : vertex_input_attrib_descs) + info.vertex_input.attrib_descs.push_back(attrib_desc); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_vertex_input_attrib_descs() { + info.vertex_input.attrib_descs.clear(); + return *this; +} + +// Input assembly state +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_input_assembly_primitive_topology(VkPrimitiveTopology topology) { + info.input_assembly.topology = topology; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_input_assembly_primitive_restart(bool enable_primitive_restart) { + info.input_assembly.primitiveRestartEnable = enable_primitive_restart ? VK_TRUE : VK_FALSE; + return *this; +} + +// Vertex shader +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_vertex_shader_pNext() { + info.vertex_shader.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_vertex_shader_flags(VkPipelineShaderStageCreateFlags flags) { + info.vertex_shader.flags = flags; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_vertex_shader_module(VkShaderModule shader_module) { + info.vertex_shader.shader_module = shader_module; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_vertex_shader_name(const char* name) { + info.vertex_shader.name = name; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_vertex_shader_specialization_info(VkSpecializationInfo& specialization_info) { + info.vertex_shader.specialization_info = specialization_info; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_vertex_shader() { + info.vertex_shader.pNext_chain.clear(); + info.vertex_shader.flags = 0; + info.vertex_shader.shader_module = VK_NULL_HANDLE; + info.vertex_shader.name = ""; + info.vertex_shader.specialization_info = {}; + return *this; +} + +// Tessellation control shader +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_tessellation_control_shader_pNext() { + info.tessellation_control_shader.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_tessellation_control_shader_flags(VkPipelineShaderStageCreateFlags flags) { + info.tessellation_control_shader.flags = flags; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_tessellation_control_shader_module(VkShaderModule shader_module) { + info.tessellation_control_shader.shader_module = shader_module; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_tessellation_control_shader_name(const char* name) { + info.tessellation_control_shader.name = name; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_tessellation_control_shader_specialization_info( + VkSpecializationInfo& specialization_info) { + info.tessellation_control_shader.specialization_info = specialization_info; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_tessellation_control_shader() { + info.tessellation_control_shader.pNext_chain.clear(); + info.tessellation_control_shader.flags = 0; + info.tessellation_control_shader.shader_module = VK_NULL_HANDLE; + info.tessellation_control_shader.name = ""; + info.tessellation_control_shader.specialization_info = {}; + return *this; +} + +// Tessellation evaluation shader +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_tessellation_eval_shader_pNext() { + info.tessellation_eval_shader.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_tessellation_eval_shader_flags(VkPipelineShaderStageCreateFlags flags) { + info.tessellation_eval_shader.flags = flags; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_tessellation_eval_shader_module(VkShaderModule shader_module) { + info.tessellation_eval_shader.shader_module = shader_module; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_tessellation_eval_shader_name(const char* name) { + info.tessellation_eval_shader.name = name; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_tessellation_eval_shader_specialization_info( + VkSpecializationInfo& specialization_info) { + info.tessellation_eval_shader.specialization_info = specialization_info; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_tessellation_eval_shader() { + info.tessellation_eval_shader.pNext_chain.clear(); + info.tessellation_eval_shader.flags = 0; + info.tessellation_eval_shader.shader_module = VK_NULL_HANDLE; + info.tessellation_eval_shader.name = ""; + info.tessellation_eval_shader.specialization_info = {}; + return *this; +} + +// Tessellation state +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_tessellation_state_pNext() { + info.tessellation_state.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_tessellation_state_patch_control_points(uint32_t patch_control_points) { + info.tessellation_state.patch_control_points = patch_control_points; + return *this; +} + +// Geometry shader +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_geometry_shader_pNext() { + info.geometry_shader.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_geometry_shader_flags(VkPipelineShaderStageCreateFlags flags) { + info.geometry_shader.flags = flags; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_geometry_shader_module(VkShaderModule shader_module) { + info.geometry_shader.shader_module = shader_module; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_geometry_shader_name(const char* name) { + info.geometry_shader.name = name; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_geometry_shader_specialization_info(VkSpecializationInfo& specialization_info) { + info.geometry_shader.specialization_info = specialization_info; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_geometry_shader() { + info.geometry_shader.pNext_chain.clear(); + info.geometry_shader.flags = 0; + info.geometry_shader.shader_module = VK_NULL_HANDLE; + info.geometry_shader.name = ""; + info.geometry_shader.specialization_info = {}; + return *this; +} + +// Viewport state +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_viewport_state_pNext() { + info.viewport_state.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_viewport_state_viewport(VkViewport viewport) { + info.viewport_state.viewports.push_back(viewport); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_viewport_state_viewports(std::vector viewports) { + for (auto viewport : viewports) + info.viewport_state.viewports.push_back(viewport); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_viewport_state_viewports() { + info.viewport_state.viewports.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_viewport_state_scissor(VkRect2D scissor) { + info.viewport_state.scissors.push_back(scissor); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_viewport_state_scissors(std::vector scissors) { + for (auto scissor : scissors) + info.viewport_state.scissors.push_back(scissor); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_viewport_state_scissors() { + info.viewport_state.scissors.clear(); + return *this; +} + +// Fragment shader +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_fragment_shader_pNext() { + info.fragment_shader.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_fragment_shader_flags(VkPipelineShaderStageCreateFlags flags) { + info.fragment_shader.flags = flags; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_fragment_shader_module(VkShaderModule shader_module) { + info.fragment_shader.shader_module = shader_module; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_fragment_shader_name(const char* name) { + info.fragment_shader.name = name; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_fragment_shader_specialization_info(VkSpecializationInfo& specialization_info) { + info.fragment_shader.specialization_info = specialization_info; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_fragment_shader() { + info.fragment_shader.pNext_chain.clear(); + info.fragment_shader.flags = 0; + info.fragment_shader.shader_module = VK_NULL_HANDLE; + info.fragment_shader.name = ""; + info.fragment_shader.specialization_info = {}; + return *this; +} + +// Rasterization state +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_rasterization_state_pNext() { + info.rasterization_state.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_rasterization_state_depth_clamp(bool enable_depth_clamp) { + info.rasterization_state.depth_clamp_enable = enable_depth_clamp ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_rasterization_state_discard(bool enable_rasterizer_discard) { + info.rasterization_state.rasterizer_discard_enable = enable_rasterizer_discard ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_rasterization_state_polygon_mode(VkPolygonMode polygon_mode) { + info.rasterization_state.polygon_mode = polygon_mode; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_rasterization_state_cull_mode_flags(VkCullModeFlags cull_mode_flags) { + info.rasterization_state.cull_mode_flags = cull_mode_flags; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_rasterization_state_front_face(VkFrontFace front_face) { + info.rasterization_state.front_face = front_face; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_rasterization_state_depth_bias(bool enable_depth_bias) { + info.rasterization_state.depth_bias_enable = enable_depth_bias ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_rasterization_state_depth_bias_constant_factor(float depth_bias_constant_factor) { + info.rasterization_state.depth_bias_constant_factor = depth_bias_constant_factor; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_rasterization_state_depth_bias_clamp(float depth_bias_clamp) { + info.rasterization_state.depth_bias_clamp = depth_bias_clamp; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_rasterization_state_depth_bias_slope_factor(float depth_bias_slope_factor) { + info.rasterization_state.depth_bias_slope_factor = depth_bias_slope_factor; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_rasterization_state_line_width(float line_width) { + info.rasterization_state.line_width = line_width; + return *this; +} + +// Multisample state +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_multisample_state_pNext() { + info.multisample_state.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_multisample_state_sample_count(VkSampleCountFlagBits sample_count) { + info.multisample_state.sample_count = sample_count; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_multisample_state_sample_shading(bool enable_sample_shading) { + info.multisample_state.sample_shading = enable_sample_shading ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_multisample_state_min_sample_shading(float min_sample_shading) { + info.multisample_state.min_sample_shading = min_sample_shading; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_multisample_state_sample_mask(VkSampleMask sample_mask) { + info.multisample_state.sample_mask = sample_mask; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_multisample_state_alpha_to_coverage(bool enable_alpha_to_coverage) { + info.multisample_state.alpha_to_coverage = enable_alpha_to_coverage ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_multisample_state_alpha_to_one(bool enable_alpha_to_one) { + info.multisample_state.alpha_to_one = enable_alpha_to_one ? VK_TRUE : VK_FALSE; + return *this; +} + +// Depth stencil state +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_depth_stencil_state_pNext() { + info.depth_stencil_state.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_depth_stencil_state_flags(VkPipelineDepthStencilStateCreateFlags flags) { + info.depth_stencil_state.flags = flags; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_depth_stencil_depth_testing(bool enable_depth_testing) { + info.depth_stencil_state.depth_test = enable_depth_testing ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_depth_stencil_depth_write(bool enable_depth_write) { + info.depth_stencil_state.depth_write = enable_depth_write ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_depth_stencil_compare_op(VkCompareOp compare_op) { + info.depth_stencil_state.depth_compare_operation = compare_op; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_depth_stencil_depth_bounds_test(bool enable_depth_bounds_test) { + info.depth_stencil_state.depth_bounds_test = enable_depth_bounds_test ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_depth_stencil_stencil_test(bool enable_stencil_test) { + info.depth_stencil_state.stencil_test = enable_stencil_test ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_depth_stencil_front_stencil_op_state(VkStencilOpState front) { + info.depth_stencil_state.front_stencil_op_state = front; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_depth_stencil_back_stencil_op_state(VkStencilOpState back) { + info.depth_stencil_state.back_stencil_op_state = back; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_depth_stencil_min_depth_bounds(float min_depth_bounds) { + info.depth_stencil_state.min_depth_bounds = min_depth_bounds; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_depth_stencil_max_depth_bounds(float max_depth_bounds) { + info.depth_stencil_state.max_depth_bounds = max_depth_bounds; + return *this; +} + +// Color blend state +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_color_blend_state_pNext() { + info.color_blend_state.pNext_chain.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_color_blend_state_flags(VkPipelineColorBlendStateCreateFlags flags) { + info.color_blend_state.flags = flags; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::enable_color_blend_state_logic_op(bool enable_logic_op) { + info.color_blend_state.logic_op_enable = enable_logic_op ? VK_TRUE : VK_FALSE; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_color_blend_state_logic_op(VkLogicOp logic_op) { + info.color_blend_state.logic_op = logic_op; + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_color_blend_state_color_blend_attachment( + VkPipelineColorBlendAttachmentState attachment) { + info.color_blend_state.attachments.push_back(attachment); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_color_blend_state_color_blend_attachments( + std::vector attachments) { + for (auto attachment : attachments) + info.color_blend_state.attachments.push_back(attachment); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_color_blend_state_color_blend_attachments() { + info.color_blend_state.attachments.clear(); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::set_color_blend_state_blend_constants( + float red, float green, float blue, float alpha) { + info.color_blend_state.blend_constants[0] = red; + info.color_blend_state.blend_constants[1] = green; + info.color_blend_state.blend_constants[2] = blue; + info.color_blend_state.blend_constants[3] = alpha; + return *this; +} + +// Dynamic state +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_dynamic_state(VkDynamicState& dynamic_state) { + info.dynamic_state.dynamic_states.push_back(dynamic_state); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::add_dynamic_states(std::vector dynamic_states) { + for (auto& dynamic_state : dynamic_states) + info.dynamic_state.dynamic_states.push_back(dynamic_state); + return *this; +} +GraphicsPipelineBuilder& GraphicsPipelineBuilder::clear_dynamic_states() { + info.dynamic_state.dynamic_states.clear(); + return *this; +} + +// Build +Result GraphicsPipelineBuilder::build() const { + if (info.device == VK_NULL_HANDLE) return Error{ GraphicsPipelineError::device_handle_not_provided }; + + // Define everything that is not defined within another struct. + VkGraphicsPipelineCreateInfo graphics_pipeline_create_info = {}; + graphics_pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + detail::setup_pNext_chain(graphics_pipeline_create_info, info.pNext_chain); + graphics_pipeline_create_info.flags = info.flags; + graphics_pipeline_create_info.layout = info.pipeline_layout; + graphics_pipeline_create_info.renderPass = info.renderpass; + graphics_pipeline_create_info.subpass = info.subpass; + graphics_pipeline_create_info.basePipelineHandle = info.base_pipeline; + graphics_pipeline_create_info.basePipelineIndex = static_cast(info.base_pipeline_index); + + // Begin preparing the shader stages to be added to creation struct. + std::vector shader_stages; + + // The inclusion of a stage is determined by the shader module being set. + if (info.vertex_shader.shader_module != VK_NULL_HANDLE) { + VkPipelineShaderStageCreateInfo vertex_shader = {}; + vertex_shader.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + detail::setup_pNext_chain(vertex_shader, info.vertex_shader.pNext_chain); + vertex_shader.flags = info.vertex_shader.flags; + vertex_shader.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertex_shader.module = info.vertex_shader.shader_module; + vertex_shader.pName = info.vertex_shader.name; + vertex_shader.pSpecializationInfo = &info.vertex_shader.specialization_info; + + shader_stages.push_back(vertex_shader); + } + if (info.tessellation_control_shader.shader_module != VK_NULL_HANDLE) { + VkPipelineShaderStageCreateInfo tessellation_control_shader = {}; + tessellation_control_shader.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + detail::setup_pNext_chain(tessellation_control_shader, info.tessellation_control_shader.pNext_chain); + tessellation_control_shader.flags = info.tessellation_control_shader.flags; + tessellation_control_shader.stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; + tessellation_control_shader.module = info.tessellation_control_shader.shader_module; + tessellation_control_shader.pName = info.tessellation_control_shader.name; + tessellation_control_shader.pSpecializationInfo = &info.tessellation_control_shader.specialization_info; + + shader_stages.push_back(tessellation_control_shader); + } + if (info.tessellation_eval_shader.shader_module != VK_NULL_HANDLE) { + VkPipelineShaderStageCreateInfo tessellation_eval_shader = {}; + tessellation_eval_shader.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + detail::setup_pNext_chain(tessellation_eval_shader, info.tessellation_eval_shader.pNext_chain); + tessellation_eval_shader.flags = info.tessellation_eval_shader.flags; + tessellation_eval_shader.stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; + tessellation_eval_shader.module = info.tessellation_eval_shader.shader_module; + tessellation_eval_shader.pName = info.tessellation_eval_shader.name; + tessellation_eval_shader.pSpecializationInfo = &info.tessellation_eval_shader.specialization_info; + + shader_stages.push_back(tessellation_eval_shader); + } + if (info.geometry_shader.shader_module != VK_NULL_HANDLE) { + VkPipelineShaderStageCreateInfo geometry_shader = {}; + geometry_shader.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + detail::setup_pNext_chain(geometry_shader, info.geometry_shader.pNext_chain); + geometry_shader.flags = info.geometry_shader.flags; + geometry_shader.stage = VK_SHADER_STAGE_GEOMETRY_BIT; + geometry_shader.module = info.geometry_shader.shader_module; + geometry_shader.pName = info.geometry_shader.name; + geometry_shader.pSpecializationInfo = &info.geometry_shader.specialization_info; + + shader_stages.push_back(geometry_shader); + } + if (info.fragment_shader.shader_module != VK_NULL_HANDLE) { + VkPipelineShaderStageCreateInfo fragment_shader = {}; + fragment_shader.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + detail::setup_pNext_chain(fragment_shader, info.fragment_shader.pNext_chain); + fragment_shader.flags = info.fragment_shader.flags; + fragment_shader.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragment_shader.module = info.fragment_shader.shader_module; + fragment_shader.pName = info.fragment_shader.name; + fragment_shader.pSpecializationInfo = &info.fragment_shader.specialization_info; + + shader_stages.push_back(fragment_shader); + } + + // Append any additional shader stages that were defined externally + for (auto shader_stage : info.additional_shader_stages) + shader_stages.push_back(shader_stage); + + // Append shader stages to the creation struct. + graphics_pipeline_create_info.stageCount = static_cast(shader_stages.size()); + graphics_pipeline_create_info.pStages = shader_stages.data(); + + // Prepare the state structs. + // Vertex input + VkPipelineVertexInputStateCreateInfo vertex_input = {}; + vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + detail::setup_pNext_chain(vertex_input, info.vertex_input.pNext_chain); + vertex_input.vertexBindingDescriptionCount = static_cast(info.vertex_input.binding_descs.size()); + vertex_input.pVertexBindingDescriptions = info.vertex_input.binding_descs.data(); + vertex_input.vertexAttributeDescriptionCount = static_cast(info.vertex_input.attrib_descs.size()); + vertex_input.pVertexAttributeDescriptions = info.vertex_input.attrib_descs.data(); + + // Input Assembly + VkPipelineInputAssemblyStateCreateInfo input_assembly = {}; + input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.topology = info.input_assembly.topology; + input_assembly.primitiveRestartEnable = info.input_assembly.primitiveRestartEnable; + + // Tessellation state + VkPipelineTessellationStateCreateInfo tessellation_state = {}; + tessellation_state.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; + detail::setup_pNext_chain(tessellation_state, info.tessellation_state.pNext_chain); + tessellation_state.patchControlPoints = info.tessellation_state.patch_control_points; + + // Viewport state + VkPipelineViewportStateCreateInfo viewport_state = {}; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + detail::setup_pNext_chain(viewport_state, info.viewport_state.pNext_chain); + viewport_state.viewportCount = static_cast(info.viewport_state.viewports.size()); + viewport_state.pViewports = info.viewport_state.viewports.data(); + viewport_state.scissorCount = static_cast(info.viewport_state.scissors.size()); + viewport_state.pScissors = info.viewport_state.scissors.data(); + + // Rasterization state + VkPipelineRasterizationStateCreateInfo rasterization_state = {}; + rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + detail::setup_pNext_chain(rasterization_state, info.rasterization_state.pNext_chain); + rasterization_state.depthClampEnable = info.rasterization_state.depth_clamp_enable; + rasterization_state.rasterizerDiscardEnable = info.rasterization_state.rasterizer_discard_enable; + rasterization_state.polygonMode = info.rasterization_state.polygon_mode; + rasterization_state.cullMode = info.rasterization_state.cull_mode_flags; + rasterization_state.frontFace = info.rasterization_state.front_face; + rasterization_state.depthBiasEnable = info.rasterization_state.depth_bias_enable; + rasterization_state.depthBiasConstantFactor = info.rasterization_state.depth_bias_constant_factor; + rasterization_state.depthBiasClamp = info.rasterization_state.depth_bias_clamp; + rasterization_state.depthBiasSlopeFactor = info.rasterization_state.depth_bias_slope_factor; + rasterization_state.lineWidth = info.rasterization_state.line_width; + + // Multisample state + VkPipelineMultisampleStateCreateInfo multisample_state = {}; + multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + detail::setup_pNext_chain(multisample_state, info.multisample_state.pNext_chain); + multisample_state.rasterizationSamples = info.multisample_state.sample_count; + multisample_state.sampleShadingEnable = info.multisample_state.sample_shading; + multisample_state.minSampleShading = info.multisample_state.min_sample_shading; + multisample_state.pSampleMask = &info.multisample_state.sample_mask; + multisample_state.alphaToCoverageEnable = info.multisample_state.alpha_to_coverage; + multisample_state.alphaToOneEnable = info.multisample_state.alpha_to_one; + + // Depth stencil state + VkPipelineDepthStencilStateCreateInfo depth_stencil_state = {}; + depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + detail::setup_pNext_chain(depth_stencil_state, info.depth_stencil_state.pNext_chain); + depth_stencil_state.flags = info.depth_stencil_state.flags; + depth_stencil_state.depthTestEnable = info.depth_stencil_state.depth_test; + depth_stencil_state.depthWriteEnable = info.depth_stencil_state.depth_write; + depth_stencil_state.depthCompareOp = info.depth_stencil_state.depth_compare_operation; + depth_stencil_state.stencilTestEnable = info.depth_stencil_state.stencil_test; + depth_stencil_state.front = info.depth_stencil_state.front_stencil_op_state; + depth_stencil_state.back = info.depth_stencil_state.back_stencil_op_state; + depth_stencil_state.minDepthBounds = info.depth_stencil_state.min_depth_bounds; + depth_stencil_state.maxDepthBounds = info.depth_stencil_state.max_depth_bounds; + + // Color blend state + VkPipelineColorBlendStateCreateInfo color_blend_state = {}; + color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + detail::setup_pNext_chain(color_blend_state, info.color_blend_state.pNext_chain); + color_blend_state.flags = info.color_blend_state.flags; + color_blend_state.logicOpEnable = info.color_blend_state.logic_op_enable; + color_blend_state.logicOp = info.color_blend_state.logic_op; + color_blend_state.attachmentCount = static_cast(info.color_blend_state.attachments.size()); + color_blend_state.pAttachments = info.color_blend_state.attachments.data(); + memcpy(color_blend_state.blendConstants, info.color_blend_state.blend_constants, (sizeof(float) * 4)); + + // Dynamic state + VkPipelineDynamicStateCreateInfo dynamic_state = {}; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = static_cast(info.dynamic_state.dynamic_states.size()); + dynamic_state.pDynamicStates = info.dynamic_state.dynamic_states.data(); + + // Append all the state structs to the creation struct. + graphics_pipeline_create_info.pVertexInputState = &vertex_input; + graphics_pipeline_create_info.pInputAssemblyState = &input_assembly; + graphics_pipeline_create_info.pTessellationState = &tessellation_state; + graphics_pipeline_create_info.pViewportState = &viewport_state; + graphics_pipeline_create_info.pRasterizationState = &rasterization_state; + graphics_pipeline_create_info.pMultisampleState = &multisample_state; + graphics_pipeline_create_info.pColorBlendState = &color_blend_state; + graphics_pipeline_create_info.pDynamicState = &dynamic_state; + + VkPipeline pipeline = VK_NULL_HANDLE; + VkResult res = info.graphics_pipeline_create_proc( + info.device, info.pipeline_cache, 1, &graphics_pipeline_create_info, info.allocation_callbacks, &pipeline); + + if (res != VK_SUCCESS) return Error{ GraphicsPipelineError::failed_to_create_graphics_pipeline }; + + return pipeline; +} + } // namespace vkb diff --git a/src/VkBootstrap.h b/src/VkBootstrap.h index 313ec64..c366e45 100644 --- a/src/VkBootstrap.h +++ b/src/VkBootstrap.h @@ -214,12 +214,22 @@ enum class SwapchainError { failed_create_swapchain_image_views, required_min_image_count_too_low, }; +enum class PipelineLayoutError { + device_handle_not_provided, + failed_to_create_pipeline_layout, +}; +enum class GraphicsPipelineError { + device_handle_not_provided, + failed_to_create_graphics_pipeline, +}; std::error_code make_error_code(InstanceError instance_error); std::error_code make_error_code(PhysicalDeviceError physical_device_error); std::error_code make_error_code(QueueError queue_error); std::error_code make_error_code(DeviceError device_error); std::error_code make_error_code(SwapchainError swapchain_error); +std::error_code make_error_code(PipelineLayoutError pipeline_layout_error); +std::error_code make_error_code(GraphicsPipelineError graphics_pipeline_error); const char* to_string_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT s); const char* to_string_message_type(VkDebugUtilsMessageTypeFlagsEXT s); @@ -229,6 +239,8 @@ const char* to_string(PhysicalDeviceError err); const char* to_string(QueueError err); const char* to_string(DeviceError err); const char* to_string(SwapchainError err); +const char* to_string(PipelineLayoutError err); +const char* to_string(GraphicsPipelineError err); // Gathers useful information about the available vulkan capabilities, like layers and instance // extensions. Use this for enabling features conditionally, ie if you would like an extension but @@ -938,8 +950,7 @@ class SwapchainBuilder { } info; }; -} // namespace vkb - +}; // namespace vkb namespace std { template <> struct is_error_code_enum : true_type {}; @@ -947,4 +958,6 @@ template <> struct is_error_code_enum : true_type {}; template <> struct is_error_code_enum : true_type {}; template <> struct is_error_code_enum : true_type {}; template <> struct is_error_code_enum : true_type {}; +template <> struct is_error_code_enum : true_type {}; +template <> struct is_error_code_enum : true_type {}; } // namespace std diff --git a/src/VkPipelineBuilder.h b/src/VkPipelineBuilder.h new file mode 100644 index 0000000..3f5ab1d --- /dev/null +++ b/src/VkPipelineBuilder.h @@ -0,0 +1,386 @@ +/* + * 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: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Copyright © 2020 Charles Giessen (charles@lunarg.com) + */ + +#pragma once + +#include "VkBootstrap.h" + +namespace vkb { +class PipelineLayoutBuilder { + public: + // Construct a PipelineLayoutBuilder with a 'vkb::Device' + explicit PipelineLayoutBuilder(Device const& device); + // Construct a PipelineLayoutBuilder with a specific VkDevice handle + explicit PipelineLayoutBuilder(VkDevice const device); + + Result build() const; + + // Set the bitmask of the pipeline layout creation flags. + PipelineLayoutBuilder& set_pipeline_layout_flags(VkPipelineLayoutCreateFlags layout_flags); + + // Add descriptor set layout(s) to the list of descriptor set layouts to be applied to pipeline layout. + PipelineLayoutBuilder& add_descriptor_layout(VkDescriptorSetLayout descriptor_set_layout); + PipelineLayoutBuilder& add_descriptor_layouts(std::vector descriptor_set_layouts); + PipelineLayoutBuilder& clear_descriptor_layouts(); + + // Add push constant range(s) to the list of push constant ranges to be applied to pipeline layout. + PipelineLayoutBuilder& add_push_constant_range(VkPushConstantRange push_constant_range); + PipelineLayoutBuilder& add_push_constant_ranges(std::vector push_constant_ranges); + PipelineLayoutBuilder& clear_push_constant_ranges(); + + // Add a structure to the pNext chain of vkPipelineLayoutCreateInfo. + // The structure must be valid when PipelineLayoutBuilder::build() is called. + template PipelineLayoutBuilder& add_pNext(T* structure) { + info.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + PipelineLayoutBuilder& clear_pNext_chain(); + + // Provide custom allocation callbacks. + PipelineLayoutBuilder& set_allocation_callbacks(VkAllocationCallbacks* callbacks); + + private: + struct PipelineLayoutInfo { + VkDevice device = VK_NULL_HANDLE; + VkAllocationCallbacks* allocation_callbacks = nullptr; + VkPipelineLayoutCreateFlags flags = 0; + std::vector pNext_chain; + std::vector descriptor_layouts; + std::vector push_constant_ranges; + PFN_vkCreatePipelineLayout pipeline_layout_create_proc = nullptr; + } info; +}; + +class GraphicsPipelineBuilder { + public: + GraphicsPipelineBuilder(Device const& device, VkPipelineCache pipeline_cache = VK_NULL_HANDLE); + GraphicsPipelineBuilder(VkDevice const device, VkPipelineCache pipeline_cache = VK_NULL_HANDLE); + + Result build() const; + + template GraphicsPipelineBuilder& add_pNext(T* structure) { + info.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_pNext(); + GraphicsPipelineBuilder& set_pipeline_create_flags(VkPipelineCreateFlags pipeline_create_flags); + GraphicsPipelineBuilder& set_pipeline_layout(VkPipelineLayout pipeline_layout); + GraphicsPipelineBuilder& set_renderpass(VkRenderPass renderpass, uint32_t subpass_index); + GraphicsPipelineBuilder& set_base_pipeline(VkPipeline base_pipeline, uint32_t base_pipeline_index); + GraphicsPipelineBuilder& add_additional_shader_stage(VkPipelineShaderStageCreateInfo& shader_stage_info); + GraphicsPipelineBuilder& add_additional_shader_stages(std::vector shader_stage_infos); + GraphicsPipelineBuilder& clear_additional_shader_stages(); + + // Vertex input state + template GraphicsPipelineBuilder& add_vertex_input_pNext(T* structure) { + info.vertex_input.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_vertex_input_pNext(); + GraphicsPipelineBuilder& add_vertex_input_binding_desc(VkVertexInputBindingDescription vertex_input_binding_desc); + GraphicsPipelineBuilder& add_vertex_input_binding_descs(std::vector vertex_input_binding_descs); + GraphicsPipelineBuilder& clear_vertex_input_binding_descs(); + GraphicsPipelineBuilder& add_vertex_input_attrib_desc(VkVertexInputAttributeDescription vertex_input_attrib_desc); + GraphicsPipelineBuilder& add_vertex_input_attrib_descs(std::vector vertex_input_attrib_descs); + GraphicsPipelineBuilder& clear_vertex_input_attrib_descs(); + + // Input assembly state + GraphicsPipelineBuilder& set_input_assembly_primitive_topology(VkPrimitiveTopology topology); + GraphicsPipelineBuilder& set_input_assembly_primitive_restart(bool enable_primitive_restart); + + // Vertex shader + template GraphicsPipelineBuilder& add_vertex_shader_pNext(T* structure) { + info.vertex_shader.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_vertex_shader_pNext(); + GraphicsPipelineBuilder& set_vertex_shader_flags(VkPipelineShaderStageCreateFlags flags); + GraphicsPipelineBuilder& set_vertex_shader_module(VkShaderModule shader_module); + GraphicsPipelineBuilder& set_vertex_shader_name(const char* name); + GraphicsPipelineBuilder& set_vertex_shader_specialization_info(VkSpecializationInfo& specialization_info); + GraphicsPipelineBuilder& clear_vertex_shader(); + + // Tessellation control shader + template GraphicsPipelineBuilder& add_tessellation_control_shader_pNext(T* structure) { + info.tessellation_control_shader.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_tessellation_control_shader_pNext(); + GraphicsPipelineBuilder& set_tessellation_control_shader_flags(VkPipelineShaderStageCreateFlags flags); + GraphicsPipelineBuilder& set_tessellation_control_shader_module(VkShaderModule shader_module); + GraphicsPipelineBuilder& set_tessellation_control_shader_name(const char* name); + GraphicsPipelineBuilder& set_tessellation_control_shader_specialization_info(VkSpecializationInfo& specialization_info); + GraphicsPipelineBuilder& clear_tessellation_control_shader(); + + // Tessellation evaluation shader + template GraphicsPipelineBuilder& add_tessellation_eval_shader_pNext(T* structure) { + info.tessellation_eval_shader.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_tessellation_eval_shader_pNext(); + GraphicsPipelineBuilder& set_tessellation_eval_shader_flags(VkPipelineShaderStageCreateFlags flags); + GraphicsPipelineBuilder& set_tessellation_eval_shader_module(VkShaderModule shader_module); + GraphicsPipelineBuilder& set_tessellation_eval_shader_name(const char* name); + GraphicsPipelineBuilder& set_tessellation_eval_shader_specialization_info(VkSpecializationInfo& specialization_info); + GraphicsPipelineBuilder& clear_tessellation_eval_shader(); + + // Tessellation state + template GraphicsPipelineBuilder& add_tessellation_state_pNext(T* structure) { + info.tessellation_state.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_tessellation_state_pNext(); + GraphicsPipelineBuilder& set_tessellation_state_patch_control_points(uint32_t patch_control_points); + + // Geometry shader + template GraphicsPipelineBuilder& add_geometry_shader_pNext(T* structure) { + info.geometry_shader.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_geometry_shader_pNext(); + GraphicsPipelineBuilder& set_geometry_shader_flags(VkPipelineShaderStageCreateFlags flags); + GraphicsPipelineBuilder& set_geometry_shader_module(VkShaderModule shader_module); + GraphicsPipelineBuilder& set_geometry_shader_name(const char* name); + GraphicsPipelineBuilder& set_geometry_shader_specialization_info(VkSpecializationInfo& specialization_info); + GraphicsPipelineBuilder& clear_geometry_shader(); + + // Viewport state + template GraphicsPipelineBuilder& add_viewport_state_pNext(T* structure) { + info.viewport_state.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_viewport_state_pNext(); + GraphicsPipelineBuilder& add_viewport_state_viewport(VkViewport viewport); + GraphicsPipelineBuilder& add_viewport_state_viewports(std::vector viewports); + GraphicsPipelineBuilder& clear_viewport_state_viewports(); + GraphicsPipelineBuilder& add_viewport_state_scissor(VkRect2D scissor); + GraphicsPipelineBuilder& add_viewport_state_scissors(std::vector scissors); + GraphicsPipelineBuilder& clear_viewport_state_scissors(); + + // Fragment shader + template GraphicsPipelineBuilder& add_fragment_shader_pNext(T* structure) { + info.fragment_shader.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_fragment_shader_pNext(); + GraphicsPipelineBuilder& set_fragment_shader_flags(VkPipelineShaderStageCreateFlags flags); + GraphicsPipelineBuilder& set_fragment_shader_module(VkShaderModule shader_module); + GraphicsPipelineBuilder& set_fragment_shader_name(const char* name); + GraphicsPipelineBuilder& set_fragment_shader_specialization_info(VkSpecializationInfo& specialization_info); + GraphicsPipelineBuilder& clear_fragment_shader(); + + // Rasterization state + template GraphicsPipelineBuilder& add_rasterization_state_pNext(T* structure) { + info.rasterization_state.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_rasterization_state_pNext(); + GraphicsPipelineBuilder& enable_rasterization_state_depth_clamp(bool enable_depth_clamp); + GraphicsPipelineBuilder& enable_rasterization_state_discard(bool enable_rasterizer_discard); + GraphicsPipelineBuilder& set_rasterization_state_polygon_mode(VkPolygonMode polygon_mode); + GraphicsPipelineBuilder& set_rasterization_state_cull_mode_flags(VkCullModeFlags cull_mode_flags); + GraphicsPipelineBuilder& set_rasterization_state_front_face(VkFrontFace front_face); + GraphicsPipelineBuilder& enable_rasterization_state_depth_bias(bool enable_depth_bias); + GraphicsPipelineBuilder& set_rasterization_state_depth_bias_constant_factor(float depth_bias_constant_factor); + GraphicsPipelineBuilder& set_rasterization_state_depth_bias_clamp(float depth_bias_clamp); + GraphicsPipelineBuilder& set_rasterization_state_depth_bias_slope_factor(float depth_bias_slope_factor); + GraphicsPipelineBuilder& set_rasterization_state_line_width(float line_width); + + // Multisample state + template GraphicsPipelineBuilder& add_multisample_state_pNext(T* structure) { + info.multisample_state.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_multisample_state_pNext(); + GraphicsPipelineBuilder& set_multisample_state_sample_count(VkSampleCountFlagBits sample_count); + GraphicsPipelineBuilder& enable_multisample_state_sample_shading(bool enable_sample_shading); + GraphicsPipelineBuilder& set_multisample_state_min_sample_shading(float min_sample_shading); + GraphicsPipelineBuilder& set_multisample_state_sample_mask(VkSampleMask sample_mask); + GraphicsPipelineBuilder& enable_multisample_state_alpha_to_coverage(bool enable_alpha_to_coverage); + GraphicsPipelineBuilder& enable_multisample_state_alpha_to_one(bool enable_alpha_to_one); + + // Depth stencil state + template GraphicsPipelineBuilder& add_depth_stencil_state_pNext(T* structure) { + info.depth_stencil_state.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_depth_stencil_state_pNext(); + GraphicsPipelineBuilder& set_depth_stencil_state_flags(VkPipelineDepthStencilStateCreateFlags flags); + GraphicsPipelineBuilder& enable_depth_stencil_depth_testing(bool enable_depth_testing); + GraphicsPipelineBuilder& enable_depth_stencil_depth_write(bool enable_depth_write); + GraphicsPipelineBuilder& set_depth_stencil_compare_op(VkCompareOp compare_op); + GraphicsPipelineBuilder& enable_depth_stencil_depth_bounds_test(bool enable_depth_bounds_test); + GraphicsPipelineBuilder& enable_depth_stencil_stencil_test(bool enable_stencil_test); + GraphicsPipelineBuilder& set_depth_stencil_front_stencil_op_state(VkStencilOpState front); + GraphicsPipelineBuilder& set_depth_stencil_back_stencil_op_state(VkStencilOpState back); + GraphicsPipelineBuilder& set_depth_stencil_min_depth_bounds(float min_depth_bounds); + GraphicsPipelineBuilder& set_depth_stencil_max_depth_bounds(float max_depth_bounds); + + // Color blend state + template GraphicsPipelineBuilder& add_color_blend_state_pNext(T* structure) { + info.color_blend_state.pNext_chain.push_back(reinterpret_cast(structure)); + return *this; + } + GraphicsPipelineBuilder& clear_color_blend_state_pNext(); + GraphicsPipelineBuilder& set_color_blend_state_flags(VkPipelineColorBlendStateCreateFlags flags); + GraphicsPipelineBuilder& enable_color_blend_state_logic_op(bool enable_logic_op); + GraphicsPipelineBuilder& set_color_blend_state_logic_op(VkLogicOp logic_op); + GraphicsPipelineBuilder& add_color_blend_state_color_blend_attachment(VkPipelineColorBlendAttachmentState attachment); + GraphicsPipelineBuilder& add_color_blend_state_color_blend_attachments(std::vector attachments); + GraphicsPipelineBuilder& clear_color_blend_state_color_blend_attachments(); + GraphicsPipelineBuilder& set_color_blend_state_blend_constants(float red, float green, float blue, float alpha); + + // Dynamic state + GraphicsPipelineBuilder& clear_dynamic_state_pNext(); + GraphicsPipelineBuilder& add_dynamic_state(VkDynamicState& dynamic_state); + GraphicsPipelineBuilder& add_dynamic_states(std::vector dynamic_states); + GraphicsPipelineBuilder& clear_dynamic_states(); + + // Provide custom allocation callbacks. + GraphicsPipelineBuilder& set_allocation_callbacks(VkAllocationCallbacks* callbacks); + + private: + struct GraphicsPipelineInfo { + VkDevice device = VK_NULL_HANDLE; + VkPipelineCache pipeline_cache = VK_NULL_HANDLE; + VkAllocationCallbacks* allocation_callbacks = nullptr; + VkPipelineCreateFlags flags = 0; + VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; + VkRenderPass renderpass = VK_NULL_HANDLE; + uint32_t subpass = 0; + VkPipeline base_pipeline = VK_NULL_HANDLE; + uint32_t base_pipeline_index = 0; + std::vector pNext_chain; + std::vector additional_shader_stages; + PFN_vkCreateGraphicsPipelines graphics_pipeline_create_proc = nullptr; + + struct VertexInput { // Vertex input state + std::vector pNext_chain; + std::vector binding_descs; + std::vector attrib_descs; + } vertex_input; + + struct InputAssembly { // Input assembly state + VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + VkBool32 primitiveRestartEnable = VK_FALSE; + } input_assembly; + + // Vertex shader state + struct VertexShader { + VkPipelineShaderStageCreateFlags flags = 0; + std::vector pNext_chain; + VkShaderModule shader_module = VK_NULL_HANDLE; + const char* name = ""; + VkSpecializationInfo specialization_info = {}; + } vertex_shader; + + struct TessellationControlShader { // Tessellation shader stage + VkPipelineShaderStageCreateFlags flags = 0; + std::vector pNext_chain; + VkShaderModule shader_module = VK_NULL_HANDLE; + const char* name = ""; + VkSpecializationInfo specialization_info = {}; + } tessellation_control_shader; + + struct TessellationEvaluationShader { // Tessellation shader stage + VkPipelineShaderStageCreateFlags flags = 0; + std::vector pNext_chain; + VkShaderModule shader_module = VK_NULL_HANDLE; + const char* name = ""; + VkSpecializationInfo specialization_info = {}; + } tessellation_eval_shader; + + struct TessellationState { + std::vector pNext_chain; + uint32_t patch_control_points; + } tessellation_state; + + struct GeometryShader { + VkPipelineShaderStageCreateFlags flags = 0; + std::vector pNext_chain; + VkShaderModule shader_module = VK_NULL_HANDLE; + const char* name = ""; + VkSpecializationInfo specialization_info = {}; + } geometry_shader; + + struct ViewportState { // Viewport state + std::vector pNext_chain; + std::vector viewports; + std::vector scissors; + } viewport_state; + + struct FragmentShader { + VkPipelineShaderStageCreateFlags flags = 0; + std::vector pNext_chain; + VkShaderModule shader_module = VK_NULL_HANDLE; + const char* name = ""; + VkSpecializationInfo specialization_info = {}; + } fragment_shader; + + struct RasterizationState { // Rasterization state + std::vector pNext_chain; + VkBool32 depth_clamp_enable = VK_FALSE; + VkBool32 rasterizer_discard_enable = VK_FALSE; + VkPolygonMode polygon_mode = VK_POLYGON_MODE_FILL; + VkCullModeFlags cull_mode_flags = VK_CULL_MODE_BACK_BIT; + VkFrontFace front_face = VK_FRONT_FACE_COUNTER_CLOCKWISE; + VkBool32 depth_bias_enable = VK_FALSE; + float depth_bias_constant_factor = 0.0f; + float depth_bias_clamp = 0.0f; + float depth_bias_slope_factor = 0.0f; + float line_width = 0.0f; + } rasterization_state; + + struct MultisampleState { + std::vector pNext_chain; + VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_1_BIT; + VkBool32 sample_shading = VK_FALSE; + float min_sample_shading = 0.0f; + VkSampleMask sample_mask = 0; + VkBool32 alpha_to_coverage = VK_FALSE; + VkBool32 alpha_to_one = VK_FALSE; + } multisample_state; + + struct DepthStencilState { + std::vector pNext_chain; + VkPipelineDepthStencilStateCreateFlags flags = 0; + VkBool32 depth_test = VK_FALSE; + VkBool32 depth_write = VK_FALSE; + VkCompareOp depth_compare_operation = VK_COMPARE_OP_GREATER; + VkBool32 depth_bounds_test = VK_FALSE; + VkBool32 stencil_test = VK_FALSE; + VkStencilOpState front_stencil_op_state = {}; + VkStencilOpState back_stencil_op_state = {}; + float min_depth_bounds = 0.0f; + float max_depth_bounds = 0.0f; + } depth_stencil_state; + + struct ColorBlendState { + std::vector pNext_chain; + VkPipelineColorBlendStateCreateFlags flags = 0; + VkBool32 logic_op_enable = VK_FALSE; + VkLogicOp logic_op = {}; + std::vector attachments; + float blend_constants[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + } color_blend_state; + + struct DynamicState { + std::vector dynamic_states; + } dynamic_state; + + } info; +}; +}; // namespace vkb