This commit is contained in:
Mars 2024-10-15 20:38:05 -04:00
parent 154a81721d
commit b2779c119a
Signed by: pupbrained
GPG key ID: 874E22DF2F9DFCB5

View file

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