diff --git a/src/main.cpp b/src/main.cpp index 2e0d9bf..736f2a4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -91,97 +91,110 @@ class VulkanApp { } private: - // GLFW instance - vkfw::UniqueInstance mVKFWInstance; - // GLFW window - vkfw::UniqueWindow mWindow; + // Instances + vkfw::UniqueInstance mVKFWInstance; // Unique handle to the GLFW instance + vkfw::UniqueWindow mWindow; // Unique handle to the GLFW window + vk::UniqueInstance mInstance; // Unique handle to the Vulkan instance - // Vulkan instance - vk::UniqueInstance mInstance; + // Debug messenger for Vulkan validation layers + vk::UniqueDebugUtilsMessengerEXT mDebugMessenger; // Unique handle to the debug messenger - // Debug messenger - vk::UniqueDebugUtilsMessengerEXT mDebugMessenger; - // Window surface - vk::UniqueSurfaceKHR mSurface; + // Surface for rendering + vk::UniqueSurfaceKHR mSurface; // Unique handle to the Vulkan surface - // Represents the physical device - vk::PhysicalDevice mPhysicalDevice; - // Number of MSAA samples - vk::SampleCountFlagBits mMsaaSamples; - // Represents the logical device - vk::UniqueDevice mDevice; + // Physical device and logical device + vk::PhysicalDevice mPhysicalDevice; // Handle to the selected physical device (GPU) + vk::SampleCountFlagBits mMsaaSamples; // Multisample anti-aliasing (MSAA) sample count + vk::UniqueDevice mDevice; // Unique handle to the logical device - vk::Queue mGraphicsQueue; - vk::Queue mPresentQueue; + // Vulkan queues for graphics and presentation + vk::Queue mGraphicsQueue; // Handle to the graphics queue + vk::Queue mPresentQueue; // Handle to the presentation queue - vk::UniqueSwapchainKHR mSwapChain; - std::vector mSwapChainImages; - vk::Format mSwapChainImageFormat; - vk::Extent2D mSwapChainExtent; - std::vector mSwapChainImageViews; - std::vector mSwapChainFramebuffers; + // Swapchain and related resources + vk::UniqueSwapchainKHR mSwapChain; // The swapchain handle + std::vector mSwapChainImages; // Images in the swapchain + vk::Format mSwapChainImageFormat; // Format of the swapchain images + vk::Extent2D mSwapChainExtent; // Dimensions of the swapchain images + std::vector mSwapChainImageViews; // Image views for the swapchain images + std::vector mSwapChainFramebuffers; // Framebuffers for the swapchain images - vk::UniqueRenderPass mRenderPass; - vk::UniqueDescriptorSetLayout mDescriptorSetLayout; - vk::UniquePipelineLayout mPipelineLayout; - vk::UniquePipeline mGraphicsPipeline; + // Render pass and pipeline configurations + vk::UniqueRenderPass mRenderPass; // Render pass configuration + vk::UniqueDescriptorSetLayout mDescriptorSetLayout; // Descriptor set layout + vk::UniquePipelineLayout mPipelineLayout; // Pipeline layout + vk::UniquePipeline mGraphicsPipeline; // Graphics pipeline + // Command pool for allocating command buffers vk::UniqueCommandPool mCommandPool; - vk::UniqueImage mColorImage; - vk::UniqueDeviceMemory mColorImageMemory; - vk::UniqueImageView mColorImageView; + // Color image resources + vk::UniqueImage mColorImage; // Color image + vk::UniqueDeviceMemory mColorImageMemory; // Memory for the color image + vk::UniqueImageView mColorImageView; // Image view for the color image - vk::UniqueImage mDepthImage; - vk::UniqueDeviceMemory mDepthImageMemory; - vk::UniqueImageView mDepthImageView; + // Depth image resources + vk::UniqueImage mDepthImage; // Depth image + vk::UniqueDeviceMemory mDepthImageMemory; // Memory for the depth image + vk::UniqueImageView mDepthImageView; // Image view for the depth image - u32 mMipLevels; - vk::UniqueImage mTextureImage; - vk::UniqueDeviceMemory mTextureImageMemory; - vk::UniqueImageView mTextureImageView; - vk::UniqueSampler mTextureSampler; + // Texture resources + u32 mMipLevels; // Number of mipmap levels + vk::UniqueImage mTextureImage; // Texture image + vk::UniqueDeviceMemory mTextureImageMemory; // Memory for the texture image + vk::UniqueImageView mTextureImageView; // Image view for the texture image + vk::UniqueSampler mTextureSampler; // Sampler for the texture - std::vector mVertices; - std::vector mIndices; - vk::UniqueBuffer mVertexBuffer; - vk::UniqueDeviceMemory mVertexBufferMemory; - vk::UniqueBuffer mIndexBuffer; - vk::UniqueDeviceMemory mIndexBufferMemory; + // Vertex and index buffers + std::vector mVertices; // Vertex data + std::vector mIndices; // Index data + vk::UniqueBuffer mVertexBuffer; // Vertex buffer + vk::UniqueDeviceMemory mVertexBufferMemory; // Memory for the vertex buffer + vk::UniqueBuffer mIndexBuffer; // Index buffer + vk::UniqueDeviceMemory mIndexBufferMemory; // Memory for the index buffer - std::vector mUniformBuffers; - std::vector mUniformBuffersMemory; - std::vector mUniformBuffersMapped; + // Uniform buffers + std::vector mUniformBuffers; // Uniform buffers + std::vector mUniformBuffersMemory; // Memory for the uniform buffers + std::vector mUniformBuffersMapped; // Mapped pointers for the uniform buffers - vk::UniqueDescriptorPool mDescriptorPool; - std::vector mDescriptorSets; + // Descriptor pool and sets + vk::UniqueDescriptorPool mDescriptorPool; // Descriptor pool + std::vector mDescriptorSets; // Descriptor sets - std::vector mCommandBuffers; + // Command buffers + std::vector mCommandBuffers; // Command buffers - std::vector mImageAvailableSemaphores; - std::vector mRenderFinishedSemaphores; - std::vector mInFlightFences; + // Synchronization primitives + std::vector mImageAvailableSemaphores; // Semaphores for image availability + std::vector mRenderFinishedSemaphores; // Semaphores for render completion + std::vector mInFlightFences; // Fences for in-flight frames - bool mFramebufferResized = false; - u32 mCurrentFrame = 0; + // Framebuffer resize flag and current frame index + bool mFramebufferResized = false; // Flag indicating if the framebuffer was resized + u32 mCurrentFrame = 0; // Index of the current frame + // Struct to hold queue family indices struct QueueFamilyIndices { - std::optional graphics_family; - std::optional present_family; + std::optional graphics_family; // Index of the graphics queue family + std::optional present_family; // Index of the presentation queue family + // Check if both queue families are available fn isComplete() -> bool { return graphics_family.has_value() && present_family.has_value(); } }; + // Struct to hold swapchain support details struct SwapChainSupportDetails { - vk::SurfaceCapabilitiesKHR capabilities; - std::vector formats; - std::vector present_modes; + vk::SurfaceCapabilitiesKHR capabilities; // Surface capabilities + std::vector formats; // Supported surface formats + std::vector present_modes; // Supported presentation modes }; + // Struct to hold uniform buffer object data struct UniformBufferObject { - alignas(16) glm::mat4 model; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 proj; + alignas(16) glm::mat4 model; // Model matrix + alignas(16) glm::mat4 view; // View matrix + alignas(16) glm::mat4 proj; // Projection matrix }; // Read a file into a vector of chars @@ -1157,14 +1170,17 @@ class VulkanApp { return { std::move(image), std::move(imageMemory) }; } + // Transition image between layouts fn transitionImageLayout( const vk::Image& image, const vk::ImageLayout& oldLayout, const vk::ImageLayout& newLayout, const u32& mipLevels ) -> void { + // Create a command buffer vk::CommandBuffer commandBuffer = beginSingleTimeCommands(); + // Define the image memory barrier vk::ImageMemoryBarrier barrier { .oldLayout = oldLayout, .newLayout = newLayout, @@ -1180,9 +1196,11 @@ class VulkanApp { }, }; + // Define the source and destination stages vk::PipelineStageFlags sourceStage; vk::PipelineStageFlags destinationStage; + // Define the access masks if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eTransferDstOptimal) { barrier.srcAccessMask = {}; barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite; @@ -1197,11 +1215,14 @@ class VulkanApp { sourceStage = vk::PipelineStageFlagBits::eTransfer; destinationStage = vk::PipelineStageFlagBits::eFragmentShader; } else { + // Ensure that the layout transition is supported throw std::invalid_argument("Unsupported layout transition!"); } + // Record the pipeline barrier commandBuffer.pipelineBarrier(sourceStage, destinationStage, {}, {}, {}, barrier); + // End the command buffer endSingleTimeCommands(commandBuffer); } @@ -1646,17 +1667,27 @@ class VulkanApp { } fn updateUniformBuffer(const u32& currentImage) -> void { + // For convenience using namespace std::chrono; - using time_point = high_resolution_clock::time_point; + using time_point = high_resolution_clock::time_point; + + // Time of the program start static time_point StartTime = high_resolution_clock::now(); + // Current time time_point currentTime = high_resolution_clock::now(); - f32 time = duration(currentTime - StartTime).count(); + // Time since the program started + f32 time = duration(currentTime - StartTime).count(); + + // Uniform buffer object UniformBufferObject ubo { + // Model matrix - glm::rotate(matrix, angle, axis) .model = glm::rotate(glm::mat4(1.0F), time * glm::radians(90.0F), glm::vec3(0.0F, 0.0F, 1.0F)), + // View matrix - glm::lookAt(eye, center, up) .view = glm::lookAt(glm::vec3(2.0F, 2.0F, 2.0F), glm::vec3(0.0F, 0.0F, 0.0F), glm::vec3(0.0F, 0.0F, 1.0F)), + // Projection matrix - glm::perspective(fov, aspect, near, far) .proj = glm::perspective( glm::radians(45.0F), static_cast(mSwapChainExtent.width) / static_cast(mSwapChainExtent.height), @@ -1668,37 +1699,46 @@ class VulkanApp { // Flip the Y axis, because glm was designed for OpenGL ubo.proj[1][1] *= -1; + // Copy the uniform buffer object to the mapped memory memcpy(mUniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); } + // Draw a frame to the window fn drawFrame() -> void { try { + // Wait for the fence to signal that the frame is finished vk::Result result = mDevice->waitForFences(mInFlightFences[mCurrentFrame].get(), vk::Bool32(vk::True), UINT64_MAX); + // Make sure the result is successful if (result != vk::Result::eSuccess) throw std::runtime_error("Failed to wait for fences!"); - vk::Result imageIndexResult = vk::Result::eSuccess; - u32 imageIndexValue = 0; - - std::tie(imageIndexResult, imageIndexValue) = mDevice->acquireNextImageKHR( + // Acquire the next image from the swap chain + auto [imageIndexResult, imageIndexValue] = mDevice->acquireNextImageKHR( mSwapChain.get(), UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame].get(), nullptr ); + // Check if the swap chain needs to be recreated if (imageIndexResult == vk::Result::eErrorOutOfDateKHR) { recreateSwapChain(); return; } + // Check if the image index is valid if (imageIndexResult != vk::Result::eSuccess && imageIndexResult != vk::Result::eSuboptimalKHR) throw std::runtime_error("Failed to acquire swap chain image!"); + // Update the uniform buffer with the current image updateUniformBuffer(mCurrentFrame); + // Reset the current fence mDevice->resetFences(mInFlightFences[mCurrentFrame].get()); + // Reset the current command buffer mCommandBuffers[mCurrentFrame]->reset(vk::CommandBufferResetFlagBits::eReleaseResources); + + // Define the command buffer submit info recordCommandBuffer(mCommandBuffers[mCurrentFrame].get(), imageIndexValue); std::array waitStages = { @@ -1715,6 +1755,7 @@ class VulkanApp { .pSignalSemaphores = &mRenderFinishedSemaphores[mCurrentFrame].get(), }; + // Submit the graphics queue mGraphicsQueue.submit(submitInfo, mInFlightFences[mCurrentFrame].get()); vk::PresentInfoKHR presentInfo { @@ -1725,23 +1766,30 @@ class VulkanApp { .pImageIndices = &imageIndexValue, }; + // Present the swap chain image vk::Result presentResult = mPresentQueue.presentKHR(presentInfo); + // Check if the swap chain needs to be recreated if (presentResult == vk::Result::eErrorOutOfDateKHR || presentResult == vk::Result::eSuboptimalKHR || mFramebufferResized) { mFramebufferResized = false; recreateSwapChain(); - } else if (presentResult != vk::Result::eSuccess) + } else if (presentResult != vk::Result::eSuccess) { + // Throw if present failed throw std::runtime_error("Failed to present swap chain image!"); + } + // Increment the current frame (or loop back to 0) mCurrentFrame = (mCurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } catch (vk::OutOfDateKHRError& /*err*/) { + // Recreate the swap chain if it's out of date mFramebufferResized = false; recreateSwapChain(); return; } } + // Create the shader module fn createShaderModule(const std::vector& code) -> vk::UniqueShaderModule { vk::ShaderModuleCreateInfo createInfo { .codeSize = code.size(),