From 6d22df0c46c6a8b4d240b204e41d7f6a6bd02bfe Mon Sep 17 00:00:00 2001 From: JimmyBinoculars Date: Fri, 20 Feb 2026 11:30:18 +0000 Subject: [PATCH] Redesigning pipeline --- a.out | Bin 72648 -> 92872 bytes cve_pipeline.cpp | 15 +- cve_pipeline.hpp | 1 - cve_swap_chain.cpp | 417 +++++++++++++++++++++++++++++++++++++++++++++ cve_swap_chain.hpp | 80 +++++++++ cve_window.hpp | 1 + first_app.cpp | 8 + first_app.hpp | 21 ++- 8 files changed, 533 insertions(+), 10 deletions(-) create mode 100644 cve_swap_chain.cpp create mode 100644 cve_swap_chain.hpp diff --git a/a.out b/a.out index f4d05573aa9db7c00e9afddfdbade5eda7900415..c18378ba81e0798bb694756c3430e6671088b059 100755 GIT binary patch literal 92872 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 delta 23662 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 +#include +#include +#include +#include +#include +#include + +namespace cve { + +CveSwapChain::CveSwapChain(CveDevice &deviceRef, VkExtent2D extent) + : device{deviceRef}, windowExtent{extent} { + createSwapChain(); + createImageViews(); + createRenderPass(); + createDepthResources(); + createFramebuffers(); + createSyncObjects(); +} + +CveSwapChain::~CveSwapChain() { + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(device.device(), imageView, nullptr); + } + swapChainImageViews.clear(); + + if (swapChain != nullptr) { + vkDestroySwapchainKHR(device.device(), swapChain, nullptr); + swapChain = nullptr; + } + + for (int i = 0; i < depthImages.size(); i++) { + vkDestroyImageView(device.device(), depthImageViews[i], nullptr); + vkDestroyImage(device.device(), depthImages[i], nullptr); + vkFreeMemory(device.device(), depthImageMemorys[i], nullptr); + } + + for (auto framebuffer : swapChainFramebuffers) { + vkDestroyFramebuffer(device.device(), framebuffer, nullptr); + } + + vkDestroyRenderPass(device.device(), renderPass, nullptr); + + // cleanup synchronization objects + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device.device(), renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device.device(), imageAvailableSemaphores[i], nullptr); + vkDestroyFence(device.device(), inFlightFences[i], nullptr); + } +} + +VkResult CveSwapChain::acquireNextImage(uint32_t *imageIndex) { + vkWaitForFences( + device.device(), + 1, + &inFlightFences[currentFrame], + VK_TRUE, + std::numeric_limits::max()); + + VkResult result = vkAcquireNextImageKHR( + device.device(), + swapChain, + std::numeric_limits::max(), + imageAvailableSemaphores[currentFrame], // must be a not signaled semaphore + VK_NULL_HANDLE, + imageIndex); + + return result; +} + +VkResult CveSwapChain::submitCommandBuffers( + const VkCommandBuffer *buffers, uint32_t *imageIndex) { + if (imagesInFlight[*imageIndex] != VK_NULL_HANDLE) { + vkWaitForFences(device.device(), 1, &imagesInFlight[*imageIndex], VK_TRUE, UINT64_MAX); + } + imagesInFlight[*imageIndex] = inFlightFences[currentFrame]; + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = buffers; + + VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + vkResetFences(device.device(), 1, &inFlightFences[currentFrame]); + if (vkQueueSubmit(device.graphicsQueue(), 1, &submitInfo, inFlightFences[currentFrame]) != + VK_SUCCESS) { + throw std::runtime_error("failed to submit draw command buffer!"); + } + + VkPresentInfoKHR presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = {swapChain}; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + + presentInfo.pImageIndices = imageIndex; + + auto result = vkQueuePresentKHR(device.presentQueue(), &presentInfo); + + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + + return result; +} + +void CveSwapChain::createSwapChain() { + SwapChainSupportDetails swapChainSupport = device.getSwapChainSupport(); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && + imageCount > swapChainSupport.capabilities.maxImageCount) { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = device.surface(); + + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = device.findPhysicalQueueFamilies(); + uint32_t queueFamilyIndices[] = {indices.graphicsFamily, indices.presentFamily}; + + if (indices.graphicsFamily != indices.presentFamily) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; // Optional + createInfo.pQueueFamilyIndices = nullptr; // Optional + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(device.device(), &createInfo, nullptr, &swapChain) != VK_SUCCESS) { + throw std::runtime_error("failed to create swap chain!"); + } + + // we only specified a minimum number of images in the swap chain, so the implementation is + // allowed to create a swap chain with more. That's why we'll first query the final number of + // images with vkGetSwapchainImagesKHR, then resize the container and finally call it again to + // retrieve the handles. + vkGetSwapchainImagesKHR(device.device(), swapChain, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device.device(), swapChain, &imageCount, swapChainImages.data()); + + swapChainImageFormat = surfaceFormat.format; + swapChainExtent = extent; +} + +void CveSwapChain::createImageViews() { + swapChainImageViews.resize(swapChainImages.size()); + for (size_t i = 0; i < swapChainImages.size(); i++) { + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = swapChainImages[i]; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = swapChainImageFormat; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device.device(), &viewInfo, nullptr, &swapChainImageViews[i]) != + VK_SUCCESS) { + throw std::runtime_error("failed to create texture image view!"); + } + } +} + +void CveSwapChain::createRenderPass() { + VkAttachmentDescription depthAttachment{}; + depthAttachment.format = findDepthFormat(); + depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depthAttachmentRef{}; + depthAttachmentRef.attachment = 1; + depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentDescription colorAttachment = {}; + colorAttachment.format = getSwapChainImageFormat(); + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + subpass.pDepthStencilAttachment = &depthAttachmentRef; + + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.srcAccessMask = 0; + dependency.srcStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + dependency.dstSubpass = 0; + dependency.dstStageMask = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + dependency.dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + + std::array attachments = {colorAttachment, depthAttachment}; + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = static_cast(attachments.size()); + renderPassInfo.pAttachments = attachments.data(); + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(device.device(), &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + throw std::runtime_error("failed to create render pass!"); + } +} + +void CveSwapChain::createFramebuffers() { + swapChainFramebuffers.resize(imageCount()); + for (size_t i = 0; i < imageCount(); i++) { + std::array attachments = {swapChainImageViews[i], depthImageViews[i]}; + + VkExtent2D swapChainExtent = getSwapChainExtent(); + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = static_cast(attachments.size()); + framebufferInfo.pAttachments = attachments.data(); + framebufferInfo.width = swapChainExtent.width; + framebufferInfo.height = swapChainExtent.height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer( + device.device(), + &framebufferInfo, + nullptr, + &swapChainFramebuffers[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create framebuffer!"); + } + } +} + +void CveSwapChain::createDepthResources() { + VkFormat depthFormat = findDepthFormat(); + VkExtent2D swapChainExtent = getSwapChainExtent(); + + depthImages.resize(imageCount()); + depthImageMemorys.resize(imageCount()); + depthImageViews.resize(imageCount()); + + for (int i = 0; i < depthImages.size(); i++) { + VkImageCreateInfo imageInfo{}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = swapChainExtent.width; + imageInfo.extent.height = swapChainExtent.height; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = depthFormat; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.flags = 0; + + device.createImageWithInfo( + imageInfo, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + depthImages[i], + depthImageMemorys[i]); + + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = depthImages[i]; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = depthFormat; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device.device(), &viewInfo, nullptr, &depthImageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create texture image view!"); + } + } +} + +void CveSwapChain::createSyncObjects() { + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + imagesInFlight.resize(imageCount(), VK_NULL_HANDLE); + + VkSemaphoreCreateInfo semaphoreInfo = {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo = {}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + if (vkCreateSemaphore(device.device(), &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != + VK_SUCCESS || + vkCreateSemaphore(device.device(), &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != + VK_SUCCESS || + vkCreateFence(device.device(), &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to create synchronization objects for a frame!"); + } + } +} + +VkSurfaceFormatKHR CveSwapChain::chooseSwapSurfaceFormat( + const std::vector &availableFormats) { + for (const auto &availableFormat : availableFormats) { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && + availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return availableFormat; + } + } + + return availableFormats[0]; +} + +VkPresentModeKHR CveSwapChain::chooseSwapPresentMode( + const std::vector &availablePresentModes) { + for (const auto &availablePresentMode : availablePresentModes) { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + std::cout << "Present mode: Mailbox" << std::endl; + return availablePresentMode; + } + } + + // for (const auto &availablePresentMode : availablePresentModes) { + // if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) { + // std::cout << "Present mode: Immediate" << std::endl; + // return availablePresentMode; + // } + // } + + std::cout << "Present mode: V-Sync" << std::endl; + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D CveSwapChain::chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities) { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } else { + VkExtent2D actualExtent = windowExtent; + actualExtent.width = std::max( + capabilities.minImageExtent.width, + std::min(capabilities.maxImageExtent.width, actualExtent.width)); + actualExtent.height = std::max( + capabilities.minImageExtent.height, + std::min(capabilities.maxImageExtent.height, actualExtent.height)); + + return actualExtent; + } +} + +VkFormat CveSwapChain::findDepthFormat() { + return device.findSupportedFormat( + {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, + VK_IMAGE_TILING_OPTIMAL, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); +} + +} // namespace cve diff --git a/cve_swap_chain.hpp b/cve_swap_chain.hpp new file mode 100644 index 0000000..c6a0b58 --- /dev/null +++ b/cve_swap_chain.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include "cve_device.hpp" + +// vulkan headers +#include + +// std lib headers +#include +#include + +namespace cve { + +class CveSwapChain { + public: + static constexpr int MAX_FRAMES_IN_FLIGHT = 2; + + CveSwapChain(CveDevice &deviceRef, VkExtent2D windowExtent); + ~CveSwapChain(); + + CveSwapChain(const CveSwapChain &) = delete; + void operator=(const CveSwapChain &) = delete; + + VkFramebuffer getFrameBuffer(int index) { return swapChainFramebuffers[index]; } + VkRenderPass getRenderPass() { return renderPass; } + VkImageView getImageView(int index) { return swapChainImageViews[index]; } + size_t imageCount() { return swapChainImages.size(); } + VkFormat getSwapChainImageFormat() { return swapChainImageFormat; } + VkExtent2D getSwapChainExtent() { return swapChainExtent; } + uint32_t width() { return swapChainExtent.width; } + uint32_t height() { return swapChainExtent.height; } + + float extentAspectRatio() { + return static_cast(swapChainExtent.width) / static_cast(swapChainExtent.height); + } + VkFormat findDepthFormat(); + + VkResult acquireNextImage(uint32_t *imageIndex); + VkResult submitCommandBuffers(const VkCommandBuffer *buffers, uint32_t *imageIndex); + + private: + void createSwapChain(); + void createImageViews(); + void createDepthResources(); + void createRenderPass(); + void createFramebuffers(); + void createSyncObjects(); + + // Helper functions + VkSurfaceFormatKHR chooseSwapSurfaceFormat( + const std::vector &availableFormats); + VkPresentModeKHR chooseSwapPresentMode( + const std::vector &availablePresentModes); + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities); + + VkFormat swapChainImageFormat; + VkExtent2D swapChainExtent; + + std::vector swapChainFramebuffers; + VkRenderPass renderPass; + + std::vector depthImages; + std::vector depthImageMemorys; + std::vector depthImageViews; + std::vector swapChainImages; + std::vector swapChainImageViews; + + CveDevice &device; + VkExtent2D windowExtent; + + VkSwapchainKHR swapChain; + + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; + std::vector imagesInFlight; + size_t currentFrame = 0; +}; + +} // namespace cve diff --git a/cve_window.hpp b/cve_window.hpp index 75afcff..0e08433 100644 --- a/cve_window.hpp +++ b/cve_window.hpp @@ -15,6 +15,7 @@ class CveWindow { CveWindow &operator=(const CveWindow &) = delete; bool shouldClose() { return glfwWindowShouldClose(window); }; + VkExtent2D getExtent() { return { static_cast(width), static_cast(height) }; }; void createWindowSurface(VkInstance instance, VkSurfaceKHR *surface); private: diff --git a/first_app.cpp b/first_app.cpp index 4a87526..65e0cf1 100644 --- a/first_app.cpp +++ b/first_app.cpp @@ -6,4 +6,12 @@ namespace cve { glfwPollEvents(); } } + void FirstApp::createPipelineLayout() { + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 0; + pipelineLayoutInfo.pSetLayouts = nullptr; + pipelineLayoutInfo.pushConstantRangeCount = 0; + pipelineLayoutInfo.pPushConstantRanges = nullptr; + } } diff --git a/first_app.hpp b/first_app.hpp index b7b7cc7..07b683d 100644 --- a/first_app.hpp +++ b/first_app.hpp @@ -3,6 +3,10 @@ #include "cve_window.hpp" #include "cve_pipeline.hpp" #include "cve_device.hpp" +#include "cve_swap_chain.hpp" + +#include +#include namespace cve { class FirstApp { @@ -10,11 +14,24 @@ namespace cve { static constexpr int WIDTH = 800; static constexpr int HEIGHT = 600; + FirstApp(); + ~FirstApp(); + + FirstApp(const FirstApp &) = delete; + FirstApp &operator=(const FirstApp &) = delete; + void run(); private: + void createPipelineLayout(); + void createPipeline(); + void createCommandBuffers(); + void drawFrame(); + CveWindow cveWindow{WIDTH, HEIGHT, "Hello Vulkan!"}; CveDevice cveDevice{cveWindow}; - CvePipeline cvePipeline{cveDevice, "shaders/simple_shader.vert.spv", - "shaders/simple_shader.frag.spv", CvePipeline::defaultPipelineConfigInfo(WIDTH, HEIGHT)}; + CveSwapChain cveSwapChain{cveDevice, cveWindow.getExtent()}; + std::unique_ptr cvePipeline; + VkPipelineLayout pipelineLayout; + std::vector commandBuffers; }; }