diff --git a/a.out b/a.out index f4d0557..c18378b 100755 Binary files a/a.out and b/a.out differ diff --git a/cve_pipeline.cpp b/cve_pipeline.cpp index 25b7110..320fc8b 100644 --- a/cve_pipeline.cpp +++ b/cve_pipeline.cpp @@ -70,13 +70,20 @@ namespace cve { vertexInputInfo.pVertexBindingDescriptions = nullptr; vertexInputInfo.pVertexAttributeDescriptions = nullptr; + VkPipelineViewportStateCreateInfo viewportInfo{}; + viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportInfo.viewportCount = 1; + viewportInfo.pViewports = &configInfo.viewport; + viewportInfo.scissorCount = 1; + viewportInfo.pScissors = &configInfo.scissor; + VkGraphicsPipelineCreateInfo pipelineInfo{}; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineInfo.stageCount = 2; pipelineInfo.pStages = shaderStages; pipelineInfo.pVertexInputState = &vertexInputInfo; pipelineInfo.pInputAssemblyState = &configInfo.inputAssemblyInfo; - pipelineInfo.pViewportState = &configInfo.viewportInfo; + pipelineInfo.pViewportState = &viewportInfo; pipelineInfo.pRasterizationState = &configInfo.rasterizationInfo; pipelineInfo.pMultisampleState = &configInfo.multisampleInfo; pipelineInfo.pColorBlendState = &configInfo.colorBlendInfo; @@ -124,12 +131,6 @@ namespace cve { configInfo.scissor.offset = {0, 0}; configInfo.scissor.extent = {width, height}; - configInfo.viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - configInfo.viewportInfo.viewportCount = 1; - configInfo.viewportInfo.pViewports = &configInfo.viewport; - configInfo.viewportInfo.scissorCount = 1; - configInfo.viewportInfo.pScissors = &configInfo.scissor; - configInfo.rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; configInfo.rasterizationInfo.depthClampEnable = VK_FALSE; configInfo.rasterizationInfo.rasterizerDiscardEnable = VK_FALSE; diff --git a/cve_pipeline.hpp b/cve_pipeline.hpp index f029047..ea1a5c4 100644 --- a/cve_pipeline.hpp +++ b/cve_pipeline.hpp @@ -9,7 +9,6 @@ namespace cve { struct PipelineConfigInfo { VkViewport viewport; VkRect2D scissor; - VkPipelineViewportStateCreateInfo viewportInfo; VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo; VkPipelineRasterizationStateCreateInfo rasterizationInfo; VkPipelineMultisampleStateCreateInfo multisampleInfo; diff --git a/cve_swap_chain.cpp b/cve_swap_chain.cpp new file mode 100644 index 0000000..65cf606 --- /dev/null +++ b/cve_swap_chain.cpp @@ -0,0 +1,417 @@ +#include "cve_swap_chain.hpp" + +// std +#include +#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; }; }