summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChuyan Zhang <me@zcy.moe>2024-09-06 01:50:30 -0700
committerChuyan Zhang <me@zcy.moe>2024-09-06 01:50:30 -0700
commite12ca33626bdadedc3158cb69f2a4d2f9bbeeeb0 (patch)
tree1e7803cbdd48b8a2de355916f042f7b77325f920
parent8c7926588b616988e7b016eaf704acee0ee77cc9 (diff)
downloadiris-e12ca33626bdadedc3158cb69f2a4d2f9bbeeeb0.tar.gz
iris-e12ca33626bdadedc3158cb69f2a4d2f9bbeeeb0.zip
setup swapchain
-rw-r--r--.gitignore5
-rw-r--r--CMakeLists.txt12
-rw-r--r--ext/imgui.cmake2
-rw-r--r--src/app.cpp44
-rw-r--r--src/vulkan_helper.cpp145
-rw-r--r--src/vulkan_helper.h30
-rw-r--r--src/vulkan_swapchain.cpp279
-rw-r--r--src/vulkan_swapchain.h34
8 files changed, 536 insertions, 15 deletions
diff --git a/.gitignore b/.gitignore
index 6eb228f..01b5580 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,4 +38,7 @@
.vscode/
# CMake build directory
-build/ \ No newline at end of file
+build/
+
+# clangd config
+.clangd \ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 05e821f..0cedde2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,10 +27,16 @@ file(GLOB_RECURSE SOURCES "${SRC_DIR}/*.cpp")
# Add executable from your source files
add_executable(IrisRenderer ${SOURCES})
-find_package(glfw3 REQUIRED)
+# find_package(glfw3 REQUIRED)
+# target_link_libraries(IrisRenderer PRIVATE glfw)
+
+# find_package(Vulkan REQUIRED)
+# target_link_libraries(IrisRenderer PRIVATE Vulkan::Vulkan)
# Link external libraries to your project
-target_link_libraries(IrisRenderer PRIVATE argparse imgui tinygltf tinyobjloader glfw)
+target_link_libraries(IrisRenderer PRIVATE
+ argparse imgui tinygltf tinyobjloader
+ glfw vulkan dl pthread X11 Xxf86vm Xrandr Xi)
# Optional: Include additional compiler options or flags
-target_compile_options(IrisRenderer PRIVATE -Wall -Wextra)
+target_compile_options(IrisRenderer PRIVATE -Wall -Wextra -Wno-missing-field-initializers)
diff --git a/ext/imgui.cmake b/ext/imgui.cmake
index 42d7c5d..8f48df1 100644
--- a/ext/imgui.cmake
+++ b/ext/imgui.cmake
@@ -3,8 +3,10 @@ add_library(imgui STATIC
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_demo.cpp
${IMGUI_DIR}/imgui_draw.cpp
+ ${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
+ ${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp
)
target_include_directories(imgui PUBLIC
diff --git a/src/app.cpp b/src/app.cpp
index 7ef2310..b3df2a5 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -1,21 +1,45 @@
+#include "vulkan_swapchain.h"
+
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_vulkan.h"
#include "argparse/argparse.hpp"
+
+#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
+#include <cstdlib>
#include <iostream>
+#include <memory>
+#include <vector>
+
+std::unique_ptr<iris::Swapchain> start_up(int width, int height) {
+ auto glfw_extensions = get_glfw_instance_extensions();
+ // std::vector<const char*> glfw_extensions = {
+ // "VK_KHR_surface",
+ // };
+ // for (const auto& extension : glfw_extensions) {
+ // std::cerr << "GLFW extension: " << extension << std::endl;
+ // }
+ iris::Device device({}, glfw_extensions);
-GLFWwindow *start_up(int width, int height) {
- return nullptr;
+ auto window = glfwCreateWindow(width, height, "IrisRenderer", nullptr, nullptr);
+ if (window == nullptr) {
+ std::cerr << "Failed to create GLFW window" << std::endl;
+ abort();
+ }
+ return std::make_unique<iris::Swapchain>(window, device);
}
void main_loop(GLFWwindow *window) {
-
+ (void) window;
}
-void shut_down(GLFWwindow *window) {
+void shut_down(std::unique_ptr<iris::Swapchain>& swapchain) {
+ ImGui_ImplVulkan_Shutdown();
+ glfwDestroyWindow(swapchain->window);
+ glfwTerminate();
}
int main(int argc, char** argv) {
@@ -51,13 +75,11 @@ int main(int argc, char** argv) {
return -1;
}
- auto window = start_up(window_width, window_height);
- if (window == nullptr) {
- return -2;
+ auto swapchain = start_up(window_width, window_height);
+ while (!glfwWindowShouldClose(swapchain->window)) {
+ main_loop(swapchain->window);
}
- while (!glfwWindowShouldClose(window)) {
- main_loop(window);
- }
- shut_down(window);
+
+ shut_down(swapchain);
return 0;
} \ No newline at end of file
diff --git a/src/vulkan_helper.cpp b/src/vulkan_helper.cpp
new file mode 100644
index 0000000..84a2b6d
--- /dev/null
+++ b/src/vulkan_helper.cpp
@@ -0,0 +1,145 @@
+#include "vulkan_helper.h"
+#include "vulkan/vulkan_core.h"
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+
+namespace iris {
+
+Device::Device(std::vector<std::string> layers, std::vector<std::string> instance_extensions) {
+ VkApplicationInfo app_info = {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pApplicationName = "IrisRenderer",
+ .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
+ .pEngineName = "No Engine",
+ .engineVersion = VK_MAKE_VERSION(1, 0, 0),
+ .apiVersion = VK_API_VERSION_1_0,
+ };
+
+ // Create the Vulkan instance
+ uint32_t enabled_layer_count = layers.size();
+ std::vector<const char*> layers_cstr;
+ for (const auto& layer : layers) {
+ layers_cstr.push_back(layer.c_str());
+ }
+
+ uint32_t enabled_extension_count = instance_extensions.size();
+ std::vector<const char*> instance_extensions_cstr;
+ for (const auto& extension : instance_extensions) {
+ instance_extensions_cstr.push_back(extension.c_str());
+ }
+
+ VkInstanceCreateInfo instance_info = {
+ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ .pApplicationInfo = &app_info,
+ .enabledLayerCount = enabled_layer_count,
+ .ppEnabledLayerNames = enabled_layer_count == 0 ? VK_NULL_HANDLE : layers_cstr.data(),
+ .enabledExtensionCount = enabled_extension_count,
+ .ppEnabledExtensionNames = enabled_extension_count == 0 ? VK_NULL_HANDLE : instance_extensions_cstr.data(),
+ };
+ CHECK_VULKAN(vkCreateInstance(
+ &instance_info,
+ VK_NULL_HANDLE,
+ &instance));
+
+ // Enumerate and select the physical device
+ uint32_t physical_device_count = 0;
+ CHECK_VULKAN(vkEnumeratePhysicalDevices(
+ instance,
+ &physical_device_count,
+ VK_NULL_HANDLE));
+ std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
+ CHECK_VULKAN(vkEnumeratePhysicalDevices(
+ instance,
+ &physical_device_count,
+ physical_devices.data()));
+ // For now, just select the first physical device, optionally check capabilities of the device
+ physical_device = physical_devices[0];
+ {
+ uint32_t device_extension_count = 0;
+ CHECK_VULKAN(vkEnumerateDeviceExtensionProperties(
+ physical_device,
+ VK_NULL_HANDLE,
+ &device_extension_count,
+ VK_NULL_HANDLE));
+ std::vector<VkExtensionProperties> device_extensions(device_extension_count);
+ CHECK_VULKAN(vkEnumerateDeviceExtensionProperties(
+ physical_device,
+ VK_NULL_HANDLE,
+ &device_extension_count,
+ device_extensions.data()));
+
+ bool has_raytracing = false;
+ bool has_acceleration_structure = false;
+ for (const auto& extension : device_extensions) {
+ if (std::string(extension.extensionName) == VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME) {
+ has_raytracing = true;
+ }
+ if (std::string(extension.extensionName) == VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME) {
+ has_acceleration_structure = true;
+ }
+ }
+
+ if (!has_raytracing || !has_acceleration_structure) {
+ // TODO throw an exception
+ std::cerr << "Physical device does not support ray tracing extensions" << std::endl;
+ abort();
+ }
+ }
+
+ // Create the logical device
+ float queue_priority = 1.0f;
+ main_queue_family_index = 0; // TODO: query capabilities to find a proper queue index
+ VkDeviceQueueCreateInfo queue_info = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ .queueFamilyIndex = main_queue_family_index,
+ .queueCount = 1,
+ .pQueuePriorities = &queue_priority,
+ };
+
+ VkPhysicalDeviceAccelerationStructureFeaturesKHR acceleration_structure_features = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR,
+ .accelerationStructure = VK_TRUE,
+ };
+ VkPhysicalDeviceRayTracingPipelineFeaturesKHR raytracing_features = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR,
+ .pNext = &acceleration_structure_features,
+ .rayTracingPipeline = VK_TRUE,
+ };
+ VkPhysicalDeviceFeatures2 device_features = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
+ .pNext = &raytracing_features,
+ .features {
+ .samplerAnisotropy = VK_TRUE,
+ }
+ };
+
+ constexpr char *device_extensions[] = {
+ VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME,
+ VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME,
+ VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME,
+ VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME
+ };
+ VkDeviceCreateInfo device_info = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ .pNext = &device_features,
+ .queueCreateInfoCount = 1,
+ .pQueueCreateInfos = &queue_info,
+ .enabledExtensionCount = sizeof(device_extensions) / sizeof(device_extensions[0]),
+ .ppEnabledExtensionNames = device_extensions,
+ };
+ CHECK_VULKAN(vkCreateDevice(
+ physical_device,
+ &device_info,
+ VK_NULL_HANDLE,
+ &device));
+
+ // Get the graphics queue
+ vkGetDeviceQueue(
+ device,
+ main_queue_family_index,
+ 0,
+ &graphics_queue);
+}
+
+} // namespace iris \ No newline at end of file
diff --git a/src/vulkan_helper.h b/src/vulkan_helper.h
new file mode 100644
index 0000000..089a09f
--- /dev/null
+++ b/src/vulkan_helper.h
@@ -0,0 +1,30 @@
+#include <cstdint>
+#include <vulkan/vulkan_core.h>
+#include <vector>
+#include <string>
+
+#define CHECK_VULKAN(result) \
+ do { \
+ VkResult res = result; \
+ if (res != VK_SUCCESS) { \
+ /* TODO: throw error instead of returning */ \
+ std::cerr << "Vulkan error: " << res << std::endl; \
+ abort(); \
+ } \
+ } while (0)
+
+namespace iris {
+
+struct Device {
+ VkInstance instance;
+ VkPhysicalDevice physical_device;
+ VkDevice device;
+ uint32_t main_queue_family_index;
+ VkQueue graphics_queue;
+
+ Device(
+ std::vector<std::string> layers,
+ std::vector<std::string> instance_extensions);
+};
+
+} // namespace iris \ No newline at end of file
diff --git a/src/vulkan_swapchain.cpp b/src/vulkan_swapchain.cpp
new file mode 100644
index 0000000..651b558
--- /dev/null
+++ b/src/vulkan_swapchain.cpp
@@ -0,0 +1,279 @@
+#include "vulkan_swapchain.h"
+
+#include "GLFW/glfw3.h"
+#include "imgui_impl_glfw.h"
+#include "imgui_impl_vulkan.h"
+#include "vulkan/vulkan_core.h"
+
+#include <cstdint>
+#include <cstdlib>
+#include <vector>
+#include <iostream>
+#include <string>
+
+std::vector<std::string> get_glfw_instance_extensions() {
+ uint32_t extension_count = 0;
+ const char **extensions = glfwGetRequiredInstanceExtensions(&extension_count);
+
+ std::vector<std::string> result;
+ result.reserve(extension_count);
+ for (uint32_t i = 0; i < extension_count; i++) {
+ result.push_back(extensions[i]);
+ }
+ return result;
+}
+
+namespace iris {
+
+Swapchain::Swapchain(
+ GLFWwindow *window,
+ iris::Device device) : device(device), window(window)
+{
+ if (!glfwVulkanSupported()) {
+ std::cerr << "GLFW failed to find Vulkan support" << std::endl;
+ // TODO throw an exception
+ abort();
+ }
+
+ // Create the surface
+ CHECK_VULKAN(glfwCreateWindowSurface(
+ device.instance,
+ window,
+ VK_NULL_HANDLE,
+ &surface));
+
+ // Optionally check surface capabilities
+ {
+ VkBool32 supported = VK_FALSE;
+ CHECK_VULKAN(vkGetPhysicalDeviceSurfaceSupportKHR(
+ device.physical_device,
+ device.main_queue_family_index,
+ surface,
+ &supported));
+ if (supported != VK_TRUE) {
+ // TODO throw an exception
+ std::cerr << "Surface does not support presentation" << std::endl;
+ abort();
+ }
+
+ VkSurfaceCapabilitiesKHR surface_capabilities;
+ CHECK_VULKAN(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+ device.physical_device,
+ surface,
+ &surface_capabilities));
+
+ if (surface_capabilities.maxImageCount < SWAPCHAIN_IMAGE_COUNT) {
+ // TODO throw an exception
+ std::cerr << "Surface does not support enough images" << std::endl;
+ abort();
+ }
+ }
+
+ // Create render pass
+ VkAttachmentDescription2 attachment = {
+ .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
+ .format = VK_FORMAT_B8G8R8A8_UNORM,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ };
+ VkAttachmentReference2 color_attachment = {
+ .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
+ .attachment = 0,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ };
+ VkSubpassDescription2 subpass = {
+ .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
+ .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = &color_attachment,
+ };
+ VkSubpassDependency2 dependencies[2] = {
+ {
+ .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
+ .srcSubpass = VK_SUBPASS_EXTERNAL,
+ .dstSubpass = 0,
+ .srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
+ },
+ {
+ .sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
+ .srcSubpass = 0,
+ .dstSubpass = VK_SUBPASS_EXTERNAL,
+ .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
+ },
+ };
+
+ VkRenderPassCreateInfo2 render_pass_create_info = {
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
+ .attachmentCount = 1,
+ .pAttachments = &attachment,
+ .subpassCount = 1,
+ .pSubpasses = &subpass,
+ .dependencyCount = 2,
+ .pDependencies = dependencies,
+ };
+ CHECK_VULKAN(vkCreateRenderPass2(
+ device.device,
+ &render_pass_create_info,
+ nullptr,
+ &render_pass));
+
+ // Create swapchain and image/imageview/framebuffers
+ // TODO: move this into `resize` to support resizing
+ {
+ // swapchain
+ int i_width, i_height;
+ glfwGetFramebufferSize(window, &i_width, &i_height);
+ width = (uint32_t) i_width;
+ height = (uint32_t) i_height;
+
+ VkSwapchainCreateInfoKHR swapchain_create_info = {
+ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+ .surface = surface,
+ .minImageCount = SWAPCHAIN_IMAGE_COUNT,
+ .imageFormat = VK_FORMAT_B8G8R8A8_UNORM,
+ .imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
+ .imageExtent = {width, height},
+ .imageArrayLayers = 1,
+ .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
+ .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
+ .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+ .presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR,
+ .clipped = VK_TRUE,
+ .oldSwapchain = VK_NULL_HANDLE,
+ };
+ CHECK_VULKAN(vkCreateSwapchainKHR(
+ device.device,
+ &swapchain_create_info,
+ nullptr,
+ &swapchain));
+
+ // images
+ uint32_t image_count = 0;
+ CHECK_VULKAN(vkGetSwapchainImagesKHR(
+ device.device,
+ swapchain,
+ &image_count,
+ nullptr));
+ if (image_count > SWAPCHAIN_IMAGE_COUNT) {
+ // TODO throw an exception
+ std::cerr << "Swapchain image count is greater than expected" << std::endl;
+ abort();
+ }
+ CHECK_VULKAN(vkGetSwapchainImagesKHR(
+ device.device,
+ swapchain,
+ &image_count,
+ swapchain_images));
+
+ // image views
+ for (uint32_t i = 0; i < image_count; i++) {
+ VkImageViewCreateInfo image_view_create_info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = swapchain_images[i],
+ .viewType = VK_IMAGE_VIEW_TYPE_2D,
+ .format = VK_FORMAT_B8G8R8A8_UNORM,
+ .components = {
+ .r = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .g = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .b = VK_COMPONENT_SWIZZLE_IDENTITY,
+ .a = VK_COMPONENT_SWIZZLE_IDENTITY,
+ },
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+
+ CHECK_VULKAN(vkCreateImageView(
+ device.device,
+ &image_view_create_info,
+ nullptr,
+ &swapchain_image_views[i]));
+ }
+
+ // framebuffers
+ for (uint32_t i = 0; i < image_count; i++) {
+ VkFramebufferCreateInfo frame_buffer_create_info = {
+ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .renderPass = render_pass,
+ .attachmentCount = 1,
+ .pAttachments = &swapchain_image_views[i],
+ .width = width,
+ .height = height,
+ .layers = 1,
+ };
+ CHECK_VULKAN(vkCreateFramebuffer(
+ device.device,
+ &frame_buffer_create_info,
+ nullptr,
+ &framebuffers[i]));
+ }
+ }
+
+
+ // Create descriptor pool
+ VkDescriptorPoolSize pool_size = {
+ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .descriptorCount = 1,
+ };
+ VkDescriptorPoolCreateInfo pool_info = {
+ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .maxSets = 1,
+ .poolSizeCount = 1,
+ .pPoolSizes = &pool_size,
+ };
+ CHECK_VULKAN(vkCreateDescriptorPool(
+ device.device,
+ &pool_info,
+ nullptr,
+ &descriptor_pool));
+
+ // initialize ImGui
+ ImGui_ImplGlfw_InitForVulkan(window, true);
+ ImGui_ImplVulkan_InitInfo init_info = {
+ .Instance = device.instance,
+ .PhysicalDevice = device.physical_device,
+ .Device = device.device,
+ .QueueFamily = device.main_queue_family_index,
+ .Queue = device.graphics_queue,
+ .DescriptorPool = descriptor_pool,
+ .MinImageCount = SWAPCHAIN_IMAGE_COUNT,
+ .ImageCount = SWAPCHAIN_IMAGE_COUNT,
+ .MSAASamples = VK_SAMPLE_COUNT_1_BIT,
+
+ // optional
+ .CheckVkResultFn = [](const VkResult err) { CHECK_VULKAN(err); },
+ };
+ ImGui_ImplVulkan_Init(&init_info);
+}
+
+Swapchain::~Swapchain() {
+ ImGui_ImplVulkan_Shutdown();
+
+ for (uint32_t i = 0; i < SWAPCHAIN_IMAGE_COUNT; i++) {
+ vkDestroyFramebuffer(device.device, framebuffers[i], nullptr);
+ vkDestroyImageView(device.device, swapchain_image_views[i], nullptr);
+ }
+
+ vkDestroyDescriptorPool(device.device, descriptor_pool, nullptr);
+ vkDestroySwapchainKHR(device.device, swapchain, nullptr);
+ vkDestroyRenderPass(device.device, render_pass, nullptr);
+ vkDestroySurfaceKHR(device.instance, surface, nullptr);
+}
+
+} // namespace iris \ No newline at end of file
diff --git a/src/vulkan_swapchain.h b/src/vulkan_swapchain.h
new file mode 100644
index 0000000..99405b6
--- /dev/null
+++ b/src/vulkan_swapchain.h
@@ -0,0 +1,34 @@
+#include "imgui_impl_glfw.h"
+#include "imgui_impl_vulkan.h"
+#include "vulkan_helper.h"
+#include "vulkan/vulkan_core.h"
+#include <cstdint>
+#include <sys/types.h>
+#include <vector>
+#include <string>
+
+std::vector<std::string> get_glfw_instance_extensions();
+
+namespace iris {
+
+struct Swapchain {
+ iris::Device device;
+
+ VkSurfaceKHR surface = VK_NULL_HANDLE;
+ VkSwapchainKHR swapchain = VK_NULL_HANDLE;
+ VkRenderPass render_pass = VK_NULL_HANDLE;
+ VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
+ GLFWwindow *window = nullptr;
+ uint32_t width = -1;
+ uint32_t height = -1;
+
+ static constexpr uint32_t SWAPCHAIN_IMAGE_COUNT = 3;
+ VkImage swapchain_images[SWAPCHAIN_IMAGE_COUNT];
+ VkImageView swapchain_image_views[SWAPCHAIN_IMAGE_COUNT];
+ VkFramebuffer framebuffers[SWAPCHAIN_IMAGE_COUNT];
+
+ Swapchain(GLFWwindow *window, iris::Device device);
+ ~Swapchain();
+};
+
+} // namespace iris \ No newline at end of file