forked from pupbrained/vulkan-test
lotsa things
This commit is contained in:
parent
f99ff172db
commit
31f772eb2d
238
src/main.cpp
238
src/main.cpp
|
@ -23,9 +23,10 @@
|
|||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||
|
||||
// Include custom utility headers
|
||||
#include "util/types.h" // Custom type definitions
|
||||
#include "util/unique_image.h" // Custom image handling utilities
|
||||
#include "util/vertex.h" // Custom vertex structure definition
|
||||
#include "util/shaders.hpp" // Compiled shader code
|
||||
#include "util/types.hpp" // Custom type definitions
|
||||
#include "util/unique_image.hpp" // Custom image handling utilities
|
||||
#include "util/vertex.hpp" // Custom vertex structure definition
|
||||
|
||||
// ImGui headers for GUI
|
||||
#include <imgui.h>
|
||||
|
@ -96,14 +97,20 @@ constexpr std::array<const char*, 2> deviceExtensions = { vk::KHRSwapchainExtens
|
|||
constexpr std::array<const char*, 1> deviceExtensions = { vk::KHRSwapchainExtensionName };
|
||||
#endif
|
||||
|
||||
// Main application class
|
||||
/**
|
||||
* @brief The Vulkan application class.
|
||||
*
|
||||
* This class encapsulates the entire Vulkan application, managing the Vulkan
|
||||
* instance, window, rendering loop, and resource cleanup. It handles the
|
||||
* initialization of Vulkan, manages resources, and orchestrates rendering.
|
||||
*/
|
||||
class VulkanApp {
|
||||
public:
|
||||
/**
|
||||
* @brief Runs the Vulkan application.
|
||||
*
|
||||
* This function initializes the application window, sets up Vulkan, and enters the main rendering loop.
|
||||
* It also cleans up resources when the application is closed.
|
||||
* This function initializes the application window, sets up Vulkan, and enters
|
||||
* the main rendering loop. It also cleans up resources when the application is closed.
|
||||
*/
|
||||
fn run() -> void {
|
||||
initWindow(); // Initialize the application window
|
||||
|
@ -125,154 +132,113 @@ class VulkanApp {
|
|||
}
|
||||
|
||||
private:
|
||||
// GLFW and Vulkan instance handles
|
||||
vkfw::UniqueInstance mVKFWInstance; // GLFW instance
|
||||
vkfw::UniqueWindow mWindow; // Application window
|
||||
vk::UniqueInstance mInstance; // Vulkan instance
|
||||
vkfw::UniqueInstance mVKFWInstance; ///< GLFW instance
|
||||
vkfw::UniqueWindow mWindow; ///< Application window
|
||||
vk::UniqueInstance mInstance; ///< Vulkan instance
|
||||
|
||||
// Debug messenger for validation layers
|
||||
vk::UniqueDebugUtilsMessengerEXT mDebugMessenger;
|
||||
vk::UniqueDebugUtilsMessengerEXT mDebugMessenger; ///< Debug messenger
|
||||
|
||||
// Vulkan surface for rendering
|
||||
vk::UniqueSurfaceKHR mSurface;
|
||||
vk::UniqueSurfaceKHR mSurface; ///< Vulkan surface for rendering
|
||||
|
||||
// Vulkan devices and queues
|
||||
vk::PhysicalDevice mPhysicalDevice; // Physical GPU
|
||||
vk::SampleCountFlagBits mMsaaSamples; // Multisampling count
|
||||
vk::UniqueDevice mDevice; // Logical Vulkan device
|
||||
vk::PhysicalDevice mPhysicalDevice; ///< Physical GPU
|
||||
vk::SampleCountFlagBits mMsaaSamples; ///< Multisampling count
|
||||
vk::UniqueDevice mDevice; ///< Logical Vulkan device
|
||||
|
||||
vk::Queue mGraphicsQueue; // Queue for graphics commands
|
||||
vk::Queue mPresentQueue; // Queue for presentation commands
|
||||
vk::Queue mGraphicsQueue; ///< Queue for graphics commands
|
||||
vk::Queue mPresentQueue; ///< Queue for presentation commands
|
||||
|
||||
// Swap chain and related resources
|
||||
vk::UniqueSwapchainKHR mSwapChain; // Swap chain for frame buffering
|
||||
std::vector<vk::Image> mSwapChainImages; // Images in the swap chain
|
||||
vk::Format mSwapChainImageFormat; // Format of swap chain images
|
||||
vk::Extent2D mSwapChainExtent; // Dimensions of swap chain images
|
||||
std::vector<vk::UniqueImageView> mSwapChainImageViews; // Image views for swap chain images
|
||||
std::vector<vk::UniqueFramebuffer> mSwapChainFramebuffers; // Framebuffers for rendering
|
||||
vk::UniqueSwapchainKHR mSwapChain; ///< Swap chain for frame buffering
|
||||
std::vector<vk::Image> mSwapChainImages; ///< Images in the swap chain
|
||||
vk::Format mSwapChainImageFormat; ///< Format of swap chain images
|
||||
vk::Extent2D mSwapChainExtent; ///< Dimensions of swap chain images
|
||||
std::vector<vk::UniqueImageView> mSwapChainImageViews; ///< Image views for swap chain images
|
||||
std::vector<vk::UniqueFramebuffer> mSwapChainFramebuffers; ///< Framebuffers for rendering
|
||||
|
||||
// Render pass and pipeline objects
|
||||
vk::UniqueRenderPass mRenderPass; // Render pass definition
|
||||
vk::UniqueDescriptorSetLayout mDescriptorSetLayout; // Descriptor set layout
|
||||
vk::UniquePipelineLayout mPipelineLayout; // Pipeline layout
|
||||
vk::UniquePipeline mGraphicsPipeline; // Graphics pipeline
|
||||
vk::UniqueRenderPass mRenderPass; ///< Render pass definition
|
||||
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::UniqueCommandPool mCommandPool; ///< Command pool for allocating command buffers
|
||||
|
||||
// Multisampling color resources
|
||||
vk::UniqueImage mColorImage;
|
||||
vk::UniqueDeviceMemory mColorImageMemory;
|
||||
vk::UniqueImageView mColorImageView;
|
||||
vk::UniqueImage mColorImage; ///< Color image
|
||||
vk::UniqueDeviceMemory mColorImageMemory; ///< Memory for color image
|
||||
vk::UniqueImageView mColorImageView; ///< Image view for color image
|
||||
|
||||
// Depth buffer resources
|
||||
vk::UniqueImage mDepthImage;
|
||||
vk::UniqueDeviceMemory mDepthImageMemory;
|
||||
vk::UniqueImageView mDepthImageView;
|
||||
vk::UniqueImage mDepthImage; ///< Depth image
|
||||
vk::UniqueDeviceMemory mDepthImageMemory; ///< Memory for depth image
|
||||
vk::UniqueImageView mDepthImageView; ///< Image view for depth image
|
||||
|
||||
// Texture resources
|
||||
u32 mMipLevels; // Number of mipmap levels
|
||||
vk::UniqueImage mTextureImage; // Texture image
|
||||
vk::UniqueDeviceMemory mTextureImageMemory; // Memory for texture image
|
||||
vk::UniqueImageView mTextureImageView; // Image view for texture
|
||||
vk::UniqueSampler mTextureSampler; // Sampler for texture
|
||||
u32 mMipLevels; ///< Number of mipmap levels
|
||||
vk::UniqueImage mTextureImage; ///< Texture image
|
||||
vk::UniqueDeviceMemory mTextureImageMemory; ///< Memory for texture image
|
||||
vk::UniqueImageView mTextureImageView; ///< Image view for texture
|
||||
vk::UniqueSampler mTextureSampler; ///< Sampler for texture
|
||||
|
||||
// Vertex and index data
|
||||
std::vector<Vertex> mVertices; // Vertex data for the model
|
||||
std::vector<u32> mIndices; // Index data for the model
|
||||
vk::UniqueBuffer mVertexBuffer; // Buffer for vertex data
|
||||
vk::UniqueDeviceMemory mVertexBufferMemory; // Memory for vertex buffer
|
||||
vk::UniqueBuffer mIndexBuffer; // Buffer for index data
|
||||
vk::UniqueDeviceMemory mIndexBufferMemory; // Memory for index buffer
|
||||
std::vector<Vertex> mVertices; ///< Vertex data for the model
|
||||
std::vector<u32> mIndices; ///< Index data for the model
|
||||
vk::UniqueBuffer mVertexBuffer; ///< Buffer for vertex data
|
||||
vk::UniqueDeviceMemory mVertexBufferMemory; ///< Memory for vertex buffer
|
||||
vk::UniqueBuffer mIndexBuffer; ///< Buffer for index data
|
||||
vk::UniqueDeviceMemory mIndexBufferMemory; ///< Memory for index buffer
|
||||
|
||||
// Uniform buffers for shader parameters
|
||||
std::vector<vk::UniqueBuffer> mUniformBuffers;
|
||||
std::vector<vk::UniqueDeviceMemory> mUniformBuffersMemory;
|
||||
std::vector<void*> mUniformBuffersMapped;
|
||||
std::vector<vk::UniqueBuffer> mUniformBuffers; ///< Uniform buffers for shader parameters
|
||||
std::vector<vk::UniqueDeviceMemory> mUniformBuffersMemory; ///< Memory for uniform buffers
|
||||
std::vector<void*> mUniformBuffersMapped; ///< Mapped uniform buffers
|
||||
|
||||
// Descriptor pools and sets
|
||||
vk::UniqueDescriptorPool mDescriptorPool; // Descriptor pool for the application
|
||||
vk::UniqueDescriptorPool mImGuiDescriptorPool; // Separate descriptor pool for ImGui
|
||||
std::vector<vk::DescriptorSet> mDescriptorSets; // Descriptor sets for binding resources
|
||||
vk::UniqueDescriptorPool mDescriptorPool; ///< Descriptor pool for the application
|
||||
vk::UniqueDescriptorPool mImGuiDescriptorPool; ///< Separate descriptor pool for ImGui
|
||||
std::vector<vk::DescriptorSet> mDescriptorSets; ///< Descriptor sets for binding resources
|
||||
|
||||
// Command buffers for recording drawing commands
|
||||
std::vector<vk::UniqueCommandBuffer> mCommandBuffers;
|
||||
std::vector<vk::UniqueCommandBuffer> mCommandBuffers; ///< Command buffers for drawing
|
||||
|
||||
// Synchronization primitives
|
||||
std::vector<vk::UniqueSemaphore>
|
||||
mImageAvailableSemaphores; // Signals that an image is available for rendering
|
||||
std::vector<vk::UniqueSemaphore> mRenderFinishedSemaphores; // Signals that rendering has finished
|
||||
std::vector<vk::UniqueFence> mInFlightFences; // Ensures CPU-GPU synchronization
|
||||
mImageAvailableSemaphores; ///< Signals that an image is available for rendering
|
||||
std::vector<vk::UniqueSemaphore> mRenderFinishedSemaphores; ///< Signals that rendering has finished
|
||||
std::vector<vk::UniqueFence> mInFlightFences; ///< Ensures CPU-GPU synchronization
|
||||
|
||||
// State variables
|
||||
bool mFramebufferResized = false; // Flag indicating if the framebuffer was resized
|
||||
u32 mCurrentFrame = 0; // Index of the current frame being rendered
|
||||
bool mFramebufferResized = false; ///< Flag indicating if the framebuffer was resized
|
||||
u32 mCurrentFrame = 0; ///< Index of the current frame being rendered
|
||||
|
||||
// Helper struct to store queue family indices
|
||||
/**
|
||||
* @brief Struct to store queue family indices.
|
||||
*
|
||||
* This struct contains the indices of the graphics and presentation queue families.
|
||||
*/
|
||||
struct QueueFamilyIndices {
|
||||
std::optional<u32> graphics_family; // Index of graphics queue family
|
||||
std::optional<u32> present_family; // Index of presentation queue family
|
||||
std::optional<u32> graphics_family; ///< Index of graphics queue family
|
||||
std::optional<u32> present_family; ///< Index of presentation queue family
|
||||
|
||||
// Check if all required queue families are found
|
||||
/**
|
||||
* @brief Check if all required queue families are found.
|
||||
*
|
||||
* @return True if both graphics and presentation families are found, false otherwise.
|
||||
*/
|
||||
fn isComplete() -> bool { return graphics_family.has_value() && present_family.has_value(); }
|
||||
};
|
||||
|
||||
// Helper struct to store swap chain support details
|
||||
/**
|
||||
* @brief Struct to hold swap chain support details.
|
||||
*
|
||||
* This struct contains information about the surface capabilities,
|
||||
* supported formats, and presentation modes.
|
||||
*/
|
||||
struct SwapChainSupportDetails {
|
||||
vk::SurfaceCapabilitiesKHR capabilities; // Surface capabilities
|
||||
std::vector<vk::SurfaceFormatKHR> formats; // Supported surface formats
|
||||
std::vector<vk::PresentModeKHR> present_modes; // Supported presentation modes
|
||||
vk::SurfaceCapabilitiesKHR capabilities; ///< Surface capabilities
|
||||
std::vector<vk::SurfaceFormatKHR> formats; ///< Supported surface formats
|
||||
std::vector<vk::PresentModeKHR> present_modes; ///< Supported presentation modes
|
||||
};
|
||||
|
||||
// Struct to hold uniform buffer object data
|
||||
struct UniformBufferObject {
|
||||
alignas(16) glm::mat4 model; // Model transformation matrix
|
||||
alignas(16) glm::mat4 view; // View transformation matrix
|
||||
alignas(16) glm::mat4 proj; // Projection matrix
|
||||
};
|
||||
|
||||
static fn compileShader(const std::string& source, const shaderc_shader_kind& kind)
|
||||
-> std::vector<uint32_t> {
|
||||
shaderc::Compiler compiler;
|
||||
shaderc::CompileOptions options;
|
||||
|
||||
shaderc::CompilationResult<u32> result =
|
||||
compiler.CompileGlslToSpv(source, kind, "shader.glsl", "main", options);
|
||||
|
||||
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
|
||||
fmt::println(stderr, "Shader compilation failed: {}", result.GetErrorMessage());
|
||||
return {};
|
||||
}
|
||||
|
||||
return { result.cbegin(), result.cend() };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the contents of a file into a vector of chars.
|
||||
* @brief Struct representing a uniform buffer object.
|
||||
*
|
||||
* @param filename The path to the file to be read.
|
||||
* @return std::vector<char> A vector containing the file's contents.
|
||||
* @throws std::runtime_error if the file cannot be opened.
|
||||
*
|
||||
* This function opens the specified file in binary mode, reads its entire contents
|
||||
* into a vector of chars, and returns that vector. It's commonly used for reading
|
||||
* shader files or other binary data needed by the application.
|
||||
* This struct holds the model, view, and projection matrices for use in shaders.
|
||||
*/
|
||||
static fn readFile(const std::string& filename) -> std::vector<char> {
|
||||
// Open the file in binary mode
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
|
||||
// Check if the file was successfully opened
|
||||
if (!file)
|
||||
throw std::runtime_error("Failed to open file: " + filename);
|
||||
|
||||
// Read the entire file content into a vector
|
||||
// This constructor reads from the start (first iterator) to the end (default constructor) of the stream
|
||||
std::vector<char> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
struct UniformBufferObject {
|
||||
alignas(16) glm::mat4 model; ///< Model transformation matrix
|
||||
alignas(16) glm::mat4 view; ///< View transformation matrix
|
||||
alignas(16) glm::mat4 proj; ///< Projection matrix
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update the FPS counter in the window title.
|
||||
|
@ -282,13 +248,13 @@ class VulkanApp {
|
|||
*
|
||||
* This function updates the window title with the current frames per second.
|
||||
*/
|
||||
static fn updateFPS(const vkfw::Window& window, const std::string& baseTitle) -> void {
|
||||
static int FrameCount = 0;
|
||||
static double LastTime = glfwGetTime();
|
||||
static double Fps = 0.0;
|
||||
static fn updateFPS(const vkfw::Window& window, const string& baseTitle) -> void {
|
||||
static u32 FrameCount = 0;
|
||||
static f64 LastTime = glfwGetTime();
|
||||
static f64 Fps = 0.0;
|
||||
|
||||
// Get the current time
|
||||
double currentTime = glfwGetTime();
|
||||
f64 currentTime = glfwGetTime();
|
||||
FrameCount++;
|
||||
|
||||
// If one second has passed, calculate the FPS
|
||||
|
@ -298,7 +264,7 @@ class VulkanApp {
|
|||
FrameCount = 0;
|
||||
|
||||
// Update window title
|
||||
std::string newTitle = format("{} - {:.0F} FPS", baseTitle, Fps);
|
||||
string newTitle = format("{} - {:.0F} FPS", baseTitle, Fps);
|
||||
glfwSetWindowTitle(window, newTitle.c_str());
|
||||
}
|
||||
}
|
||||
|
@ -428,7 +394,7 @@ class VulkanApp {
|
|||
.DescriptorPool = mImGuiDescriptorPool.get(),
|
||||
.RenderPass = mRenderPass.get(),
|
||||
.MinImageCount = MAX_FRAMES_IN_FLIGHT,
|
||||
.ImageCount = static_cast<uint32_t>(mSwapChainImages.size()),
|
||||
.ImageCount = static_cast<u32>(mSwapChainImages.size()),
|
||||
.MSAASamples = static_cast<VkSampleCountFlagBits>(mMsaaSamples),
|
||||
.PipelineCache = VK_NULL_HANDLE,
|
||||
.Subpass = 0,
|
||||
|
@ -525,7 +491,7 @@ class VulkanApp {
|
|||
.applicationVersion = 1,
|
||||
.pEngineName = "No Engine",
|
||||
.engineVersion = 1,
|
||||
.apiVersion = vk::ApiVersion12,
|
||||
.apiVersion = vk::ApiVersion13,
|
||||
};
|
||||
|
||||
// Get the required extensions
|
||||
|
@ -898,9 +864,9 @@ class VulkanApp {
|
|||
*/
|
||||
fn createGraphicsPipeline() -> void {
|
||||
std::vector<u32> vertShaderCode =
|
||||
compileShader(vertShaderSrc, shaderc_shader_kind::shaderc_vertex_shader);
|
||||
ShaderCompiler::getCompiledShader(vertShaderSrc, shaderc_shader_kind::shaderc_vertex_shader, "vert");
|
||||
std::vector<u32> fragShaderCode =
|
||||
compileShader(fragShaderSrc, shaderc_shader_kind::shaderc_fragment_shader);
|
||||
ShaderCompiler::getCompiledShader(fragShaderSrc, shaderc_shader_kind::shaderc_fragment_shader, "frag");
|
||||
|
||||
vk::UniqueShaderModule vertShaderModule = createShaderModule(vertShaderCode);
|
||||
vk::UniqueShaderModule fragShaderModule = createShaderModule(fragShaderCode);
|
||||
|
@ -1582,7 +1548,7 @@ class VulkanApp {
|
|||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
std::string warn, err;
|
||||
string warn, err;
|
||||
|
||||
std::filesystem::path modelPath = std::filesystem::current_path() / "models" / "viking_room.obj";
|
||||
|
||||
|
|
166
src/util/shaders.hpp
Normal file
166
src/util/shaders.hpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <fmt/base.h>
|
||||
#include <fstream>
|
||||
#include <shaderc/shaderc.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "types.hpp"
|
||||
|
||||
class ShaderCompiler {
|
||||
public:
|
||||
ShaderCompiler() = default;
|
||||
|
||||
/**
|
||||
* @brief Compiles or loads a cached SPIR-V shader from a file.
|
||||
*
|
||||
* @param shaderSource The source code of the shader.
|
||||
* @param kind The type of shader being compiled (vertex, fragment, etc.).
|
||||
* @param shaderName The name used for caching the compiled shader.
|
||||
* @return std::vector<u32> A vector containing the compiled SPIR-V code.
|
||||
* @throws std::runtime_error if shader compilation fails.
|
||||
*
|
||||
* This function attempts to load a shader from the cache. If the shader
|
||||
* is not found in the cache, it compiles the shader from the source code
|
||||
* and saves the result to the cache for future use.
|
||||
*/
|
||||
static fn getCompiledShader(
|
||||
const char* shaderSource,
|
||||
const shaderc_shader_kind& kind,
|
||||
const string& shaderName
|
||||
) -> std::vector<u32> {
|
||||
using namespace std;
|
||||
|
||||
const string cacheFile = getCacheFilePath(shaderName);
|
||||
|
||||
// Try loading from cache first
|
||||
vector<u32> spirvCode = loadCachedShader(cacheFile);
|
||||
|
||||
if (!spirvCode.empty()) {
|
||||
fmt::println("Loaded shader from cache: {}", cacheFile);
|
||||
return spirvCode;
|
||||
}
|
||||
|
||||
// Cache miss, compile the shader
|
||||
spirvCode = compileShader(shaderSource, kind);
|
||||
|
||||
if (spirvCode.empty())
|
||||
throw runtime_error("Shader compilation failed for: " + shaderName);
|
||||
|
||||
// Cache the compiled SPIR-V binary
|
||||
saveCompiledShader(spirvCode, cacheFile);
|
||||
return spirvCode;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Generates the cache file path based on the operating system.
|
||||
*
|
||||
* @param shaderName The name used for the shader file.
|
||||
* @return string The full path to the cache file.
|
||||
*
|
||||
* This function determines the appropriate directory for caching shaders
|
||||
* based on the operating system and returns the full path for the specified shader name.
|
||||
*/
|
||||
static fn getCacheFilePath(const string& shaderName) -> string {
|
||||
using namespace std::filesystem;
|
||||
|
||||
#ifdef _WIN32
|
||||
path cacheDir = path(getenv("LOCALAPPDATA")) / "VulkanApp" / "Shaders";
|
||||
#elif defined(__APPLE__)
|
||||
path cacheDir = path(getenv("HOME")) / "Library" / "Application Support" / "VulkanApp" / "Shaders";
|
||||
#else // Assume Linux or other UNIX-like systems
|
||||
path cacheDir = path(getenv("HOME")) / ".local" / "share" / "VulkanApp" / "Shaders";
|
||||
#endif
|
||||
|
||||
if (!exists(cacheDir))
|
||||
create_directories(cacheDir); // Create the directory if it doesn't exist
|
||||
|
||||
return (cacheDir / (shaderName + ".spv")).string();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compiles GLSL code to SPIR-V.
|
||||
*
|
||||
* @param source The GLSL source code to compile.
|
||||
* @param kind The type of shader being compiled.
|
||||
* @return std::vector<u32> A vector containing the compiled SPIR-V code.
|
||||
*
|
||||
* This function uses the shaderc library to compile GLSL source code into
|
||||
* SPIR-V binary format. If the compilation fails, an empty vector is returned.
|
||||
*/
|
||||
static fn compileShader(const char* source, const shaderc_shader_kind& kind) -> std::vector<u32> {
|
||||
using namespace shaderc;
|
||||
|
||||
Compiler compiler;
|
||||
CompileOptions options;
|
||||
|
||||
SpvCompilationResult result = compiler.CompileGlslToSpv(source, kind, "shader.glsl", "main", options);
|
||||
|
||||
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
|
||||
fmt::println(stderr, "Shader compilation failed: {}", result.GetErrorMessage());
|
||||
return {};
|
||||
}
|
||||
|
||||
return { result.cbegin(), result.cend() };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Loads compiled SPIR-V shader code from a cache file.
|
||||
*
|
||||
* @param cacheFile The path to the cached SPIR-V file.
|
||||
* @return std::vector<u32> A vector containing the loaded SPIR-V code.
|
||||
* @throws std::runtime_error if the file cannot be read.
|
||||
*
|
||||
* This function checks if the specified cache file exists and reads its
|
||||
* contents into a vector. If the file cannot be opened, an exception is thrown.
|
||||
*/
|
||||
static fn loadCachedShader(const string& cacheFile) -> std::vector<u32> {
|
||||
using namespace std;
|
||||
|
||||
if (!filesystem::exists(cacheFile))
|
||||
return {}; // No cached file
|
||||
|
||||
ifstream file(cacheFile, ios::binary);
|
||||
|
||||
// Check if the file was successfully opened
|
||||
if (!file)
|
||||
throw runtime_error("Failed to open cached shader file: " + cacheFile);
|
||||
|
||||
usize fileSize = filesystem::file_size(cacheFile);
|
||||
vector<u32> spirvCode(fileSize / sizeof(u32));
|
||||
|
||||
// Read entire file content into the vector
|
||||
if (!file.read(bit_cast<char*>(spirvCode.data()), static_cast<streamsize>(fileSize)))
|
||||
throw runtime_error("Failed to read cached shader file: " + cacheFile);
|
||||
|
||||
return spirvCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Saves compiled SPIR-V binary to a cache file.
|
||||
*
|
||||
* @param spirvCode The SPIR-V code to save.
|
||||
* @param cacheFile The path to the file where the SPIR-V code will be saved.
|
||||
* @throws std::runtime_error if the file cannot be written.
|
||||
*
|
||||
* This function writes the compiled SPIR-V binary to the specified file.
|
||||
* If the file cannot be opened or written, an exception is thrown.
|
||||
*/
|
||||
static fn saveCompiledShader(const std::vector<u32>& spirvCode, const string& cacheFile) -> void {
|
||||
using namespace std;
|
||||
|
||||
ofstream file(cacheFile, ios::binary);
|
||||
|
||||
// Check if the file was successfully opened
|
||||
if (!file)
|
||||
throw runtime_error("Failed to open file for saving shader: " + cacheFile);
|
||||
|
||||
if (!file.write(
|
||||
bit_cast<const char*>(spirvCode.data()), static_cast<streamsize>(spirvCode.size() * sizeof(u32))
|
||||
))
|
||||
throw runtime_error("Failed to save shader to cache: " + cacheFile);
|
||||
}
|
||||
};
|
|
@ -1,69 +0,0 @@
|
|||
#include <filesystem>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace stb {
|
||||
class UniqueImage {
|
||||
public:
|
||||
// Constructor
|
||||
UniqueImage(const std::filesystem::path& path) { load(path.string().c_str()); }
|
||||
|
||||
// Deleted copy constructor and assignment operator
|
||||
UniqueImage(const UniqueImage&) = delete;
|
||||
fn operator=(const UniqueImage&)->UniqueImage& = delete;
|
||||
|
||||
// Move constructor and assignment operator
|
||||
UniqueImage(UniqueImage&& other) noexcept
|
||||
: mData(other.mData), mWidth(other.mWidth), mHeight(other.mHeight), mChannels(other.mChannels) {
|
||||
other.mData = nullptr; // Set the other's data to nullptr
|
||||
}
|
||||
|
||||
fn operator=(UniqueImage&& other) noexcept -> UniqueImage& {
|
||||
if (this != &other) {
|
||||
// Free existing resource
|
||||
if (mData)
|
||||
stbi_image_free(mData);
|
||||
|
||||
// Move the resource
|
||||
mData = other.mData;
|
||||
mWidth = other.mWidth;
|
||||
mHeight = other.mHeight;
|
||||
mChannels = other.mChannels;
|
||||
other.mData = nullptr; // Set the other's data to nullptr
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~UniqueImage() {
|
||||
if (mData)
|
||||
stbi_image_free(mData);
|
||||
}
|
||||
|
||||
// Accessors for image data and dimensions
|
||||
[[nodiscard]] fn getData() const -> unsigned char* { return mData; }
|
||||
|
||||
[[nodiscard]] fn getWidth() const -> int { return mWidth; }
|
||||
|
||||
[[nodiscard]] fn getHeight() const -> int { return mHeight; }
|
||||
|
||||
[[nodiscard]] fn getChannels() const -> int { return mChannels; }
|
||||
|
||||
private:
|
||||
unsigned char* mData = nullptr;
|
||||
int mWidth = 0;
|
||||
int mHeight = 0;
|
||||
int mChannels = 0;
|
||||
|
||||
// Load function
|
||||
void load(const char* filename) {
|
||||
mData = stbi_load(filename, &mWidth, &mHeight, &mChannels, STBI_rgb_alpha);
|
||||
|
||||
if (!mData)
|
||||
throw std::runtime_error("Failed to load image: " + std::string(stbi_failure_reason()));
|
||||
}
|
||||
};
|
||||
}
|
121
src/util/unique_image.hpp
Normal file
121
src/util/unique_image.hpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
#include <filesystem>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
||||
#include "types.hpp"
|
||||
|
||||
namespace stb {
|
||||
/**
|
||||
* @brief A class that handles loading and managing image data.
|
||||
*
|
||||
* This class uses the stb_image library to load images from the filesystem
|
||||
* and provides access to the image data, dimensions, and channel count.
|
||||
*/
|
||||
class UniqueImage {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a UniqueImage object and loads an image from the specified path.
|
||||
*
|
||||
* @param path The filesystem path to the image file to load.
|
||||
*/
|
||||
UniqueImage(const std::filesystem::path& path) { load(path.c_str()); }
|
||||
|
||||
// Deleted copy constructor to prevent copying.
|
||||
UniqueImage(const UniqueImage&) = delete;
|
||||
|
||||
// Deleted copy assignment operator to prevent copying.
|
||||
fn operator=(const UniqueImage&)->UniqueImage& = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor for UniqueImage.
|
||||
*
|
||||
* Transfers ownership of resources from another UniqueImage object.
|
||||
*
|
||||
* @param other The UniqueImage object from which to move resources.
|
||||
*/
|
||||
UniqueImage(UniqueImage&& other) noexcept
|
||||
: mData(other.mData), mWidth(other.mWidth), mHeight(other.mHeight), mChannels(other.mChannels) {
|
||||
other.mData = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator for UniqueImage.
|
||||
*
|
||||
* Transfers ownership of resources from another UniqueImage object.
|
||||
*
|
||||
* @param other The UniqueImage object from which to move resources.
|
||||
* @return Reference to this object.
|
||||
*/
|
||||
fn operator=(UniqueImage&& other) noexcept -> UniqueImage& {
|
||||
if (this != &other) {
|
||||
if (mData)
|
||||
stbi_image_free(mData);
|
||||
|
||||
mData = other.mData;
|
||||
mWidth = other.mWidth;
|
||||
mHeight = other.mHeight;
|
||||
mChannels = other.mChannels;
|
||||
other.mData = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor for UniqueImage.
|
||||
*
|
||||
* Frees the image data if it is allocated.
|
||||
*/
|
||||
~UniqueImage() {
|
||||
if (mData)
|
||||
stbi_image_free(mData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the image data.
|
||||
*
|
||||
* @return Pointer to the image data in memory.
|
||||
*/
|
||||
[[nodiscard]] fn getData() const -> u8* { return mData; }
|
||||
|
||||
/**
|
||||
* @brief Retrieves the width of the image.
|
||||
*
|
||||
* @return The width of the image in pixels.
|
||||
*/
|
||||
[[nodiscard]] fn getWidth() const -> i32 { return mWidth; }
|
||||
|
||||
/**
|
||||
* @brief Retrieves the height of the image.
|
||||
*
|
||||
* @return The height of the image in pixels.
|
||||
*/
|
||||
[[nodiscard]] fn getHeight() const -> i32 { return mHeight; }
|
||||
|
||||
/**
|
||||
* @brief Retrieves the number of channels in the image.
|
||||
*
|
||||
* @return The number of channels in the image (e.g., 3 for RGB, 4 for RGBA).
|
||||
*/
|
||||
[[nodiscard]] fn getChannels() const -> i32 { return mChannels; }
|
||||
|
||||
private:
|
||||
u8* mData = nullptr; ///< Pointer to the image data.
|
||||
i32 mWidth = 0; ///< Width of the image in pixels.
|
||||
i32 mHeight = 0; ///< Height of the image in pixels.
|
||||
i32 mChannels = 0; ///< Number of channels in the image.
|
||||
|
||||
/**
|
||||
* @brief Loads an image from a file.
|
||||
*
|
||||
* @param filename The name of the file from which to load the image.
|
||||
* @throws std::runtime_error If the image fails to load.
|
||||
*/
|
||||
fn load(const char* filename) -> void {
|
||||
mData = stbi_load(filename, &mWidth, &mHeight, &mChannels, STBI_rgb_alpha);
|
||||
|
||||
if (!mData)
|
||||
throw std::runtime_error("Failed to load image: " + string(stbi_failure_reason()));
|
||||
}
|
||||
};
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "types.h"
|
||||
#include "types.hpp"
|
||||
|
||||
// Vertex data for the model
|
||||
struct Vertex {
|
Loading…
Reference in a new issue