forked from pupbrained/vulkan-test
urggh
This commit is contained in:
parent
154a81721d
commit
b2779c119a
186
src/main.cpp
186
src/main.cpp
|
@ -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(),
|
||||||
|
|
Loading…
Reference in a new issue