diff --git a/src/contrib/vcpkg b/src/contrib/vcpkg index e7d7451..f75c836 160000 --- a/src/contrib/vcpkg +++ b/src/contrib/vcpkg @@ -1 +1 @@ -Subproject commit e7d7451462697d77ef319ddf2da8ff7320a82662 +Subproject commit f75c836a67777a86a2c1116a28b179827f028b66 diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index b23c1ee..bc9beb6 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -19,6 +19,11 @@ SET(segfault_renderer_src renderer/RHIVulkan.cpp renderer/vulkanbuffer.cpp renderer/vulkanbuffer.h + renderer/vulkandevice.cpp + renderer/vulkandevice.h + renderer/vulkanutils.cpp + renderer/vulkanutils.h + renderer/vulkantypes.h ) SET(segfault_application_src diff --git a/src/runtime/core/genericfilemanager.h b/src/runtime/core/genericfilemanager.h index cf32024..e6ae72b 100644 --- a/src/runtime/core/genericfilemanager.h +++ b/src/runtime/core/genericfilemanager.h @@ -4,14 +4,39 @@ namespace segfault::core { + /// @class GenericFileManager + /// @brief A generic file manager implementation. class SEGFAULT_EXPORT GenericFileManager final : public IFileManager { public: + /// @brief Constructs a new instance of GenericFileManager. GenericFileManager() = default; + + /// @brief Destroys the GenericFileManager instance. ~GenericFileManager() final = default; + + /// @brief Creates a file reader for the specified file. + /// @param name The name of the file to read. + /// @return A pointer to the created file reader, or nullptr if creation failed. FileArchive *createFileReader(const char *name) final; + + /// @brief Creates a file writer for the specified file. + /// @param name The name of the file to write. + /// @return A pointer to the created file writer, or nullptr if creation failed. FileArchive *createFileWriter(const char *name) final; + + /// @brief Closes the specified file archive. + /// @param archive The file archive to close. void close(FileArchive *archive) final; + + /// @brief Checks if a file exists. + /// @param name The name of the file to check. + /// @return True if the file exists, false otherwise. bool exist(const char* name) final; + /// @brief Gets statistics for the specified archive. + + /// @param name The name of the archive. + /// @param stat The structure to store the statistics in. + /// @return True if statistics were successfully retrieved, false otherwise. bool getArchiveStat(const char *name, FileStat &stat) final; }; diff --git a/src/runtime/renderer/RHIVulkan.cpp b/src/runtime/renderer/RHIVulkan.cpp index 9ea0c54..f33b9e2 100644 --- a/src/runtime/renderer/RHIVulkan.cpp +++ b/src/runtime/renderer/RHIVulkan.cpp @@ -1,5 +1,6 @@ #include "RHI.h" #include "rendercore.h" +#include "vulkanutils.h" #include "core/segfaultexception.h" #include "volk.h" #include "SDL_vulkan.h" @@ -21,44 +22,8 @@ #include namespace segfault::renderer { - using namespace segfault::core; - - struct Vertex { - glm::vec3 pos{}; - glm::vec3 color{}; - glm::vec2 texCoord{}; - - static std::array getAttributeDescriptions() { - std::array attributeDescriptions{}; - - attributeDescriptions[0].binding = 0; - attributeDescriptions[0].location = 0; - attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[0].offset = offsetof(Vertex, pos); - - attributeDescriptions[1].binding = 0; - attributeDescriptions[1].location = 1; - attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[1].offset = offsetof(Vertex, color); - - attributeDescriptions[2].binding = 0; - attributeDescriptions[2].location = 2; - attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; - attributeDescriptions[2].offset = offsetof(Vertex, texCoord); - return attributeDescriptions; - } - - static VkVertexInputBindingDescription getBindingDescription() { - VkVertexInputBindingDescription bindingDescription{}; - - bindingDescription.binding = 0; - bindingDescription.stride = sizeof(Vertex); - bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - return bindingDescription; - } - }; + using namespace segfault::core; const std::vector vertices = { {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}, @@ -135,7 +100,7 @@ namespace segfault::renderer { VkPipeline graphicsPipeline{}; bool framebufferResized{false}; VkBuffer vertexBuffer{}; - + std::vector uniformBuffers{}; std::vector uniformBuffersMemory{}; std::vector uniformBuffersMapped{}; @@ -163,7 +128,7 @@ namespace segfault::renderer { VkResult createDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger); QueueFamilyIndices findQueueFamilies(QueueFamilyIndices& indices); - bool createLogicalDevice(bool enableValidationLayers, VkPhysicalDevice physicalDevice, VkDevice& device, QueueFamilyIndices& indices); + bool createLogicalDevice(bool enableValidationLayers, VkPhysicalDevice physicalDevice, QueueFamilyIndices& indices); VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) const; VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); @@ -203,29 +168,6 @@ namespace segfault::renderer { void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout); void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height); }; - - VkFormat findSupportedFormat(RHIImpl *rhi, const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { - for (VkFormat format : candidates) { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(rhi->physicalDevice, format, &props); - if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) { - return format; - } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) { - return format; - } - } - - throw SegfaultException("failed to find supported format!"); - } - - VkFormat findDepthFormat(RHIImpl* rhi) { - return findSupportedFormat( - rhi, - { 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 - ); - } static std::vector readFile(const std::string& filename) { std::ifstream file(filename, std::ios::ate | std::ios::binary); @@ -252,7 +194,7 @@ namespace segfault::renderer { SwapChainSupportDetails RHIImpl::querySwapChainSupport() { SwapChainSupportDetails details; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &details.capabilities); - + uint32_t formatCount; vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr); @@ -271,7 +213,7 @@ namespace segfault::renderer { return details; } - + bool RHIImpl::checkDeviceExtensionSupport() { uint32_t extensionCount{}; vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr); @@ -311,7 +253,7 @@ namespace segfault::renderer { VkPhysicalDeviceFeatures supportedFeatures; vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures); - return queueFamilyIndices.isComplete() && extensionsSupported && swapChainAdequate + return queueFamilyIndices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy; } @@ -388,7 +330,7 @@ namespace segfault::renderer { auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); if (func != nullptr) { return func(instance, pCreateInfo, pAllocator, pDebugMessenger); - } + } return VK_ERROR_EXTENSION_NOT_PRESENT; } @@ -423,7 +365,7 @@ namespace segfault::renderer { return qfIndices; } - bool RHIImpl::createLogicalDevice(bool enableValidationLayers, VkPhysicalDevice physicalDevice, VkDevice &device, QueueFamilyIndices& qfIndices) { + bool RHIImpl::createLogicalDevice(bool enableValidationLayers, VkPhysicalDevice physicalDevice, QueueFamilyIndices& qfIndices) { qfIndices = findQueueFamilies(qfIndices); std::vector queueCreateInfos{}; @@ -446,15 +388,15 @@ namespace segfault::renderer { // priorities float queuePrioritys[2] = { 1.f, 1.f }; - - queueCreateInfo.pNext = nullptr; + + queueCreateInfo.pNext = nullptr; queueCreateInfo.pQueuePriorities = &queuePrioritys[0]; VkPhysicalDeviceFeatures deviceFeatures{}; VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceFeatures.samplerAnisotropy = VK_TRUE; - + createInfo.pNext = nullptr; createInfo.pQueueCreateInfos = &queueCreateInfo; @@ -475,7 +417,7 @@ namespace segfault::renderer { if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { return false; } - + vkGetDeviceQueue(device, qfIndices.presentFamily.value(), 0, &presentQueue); vkGetDeviceQueue(device, qfIndices.graphicsFamily.value(), 0, &graphicsQueue); @@ -553,8 +495,8 @@ namespace segfault::renderer { createInfo.pQueueFamilyIndices = queueFamilyIndices; } else { createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.queueFamilyIndexCount = 0; // Optional - createInfo.pQueueFamilyIndices = nullptr; // Optional + createInfo.queueFamilyIndexCount = 0; // Optional + createInfo.pQueueFamilyIndices = nullptr; // Optional } createInfo.preTransform = swapChainSupport.capabilities.currentTransform; @@ -604,7 +546,7 @@ namespace segfault::renderer { VkAttachmentDescription colorAttachment{}; colorAttachment.format = swapChainImageFormat; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -615,7 +557,7 @@ namespace segfault::renderer { colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; VkAttachmentDescription depthAttachment{}; - depthAttachment.format = findDepthFormat(this); + depthAttachment.format = VulkanUtils::findDepthFormat(this->physicalDevice); depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; @@ -895,7 +837,7 @@ namespace segfault::renderer { } void RHIImpl::createDepthResources() { - VkFormat depthFormat = findDepthFormat(this); + VkFormat depthFormat = VulkanUtils::findDepthFormat(this->physicalDevice); createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory); depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -964,7 +906,7 @@ namespace segfault::renderer { vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT16); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[currentFrame], 0, nullptr); - + vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffer); @@ -980,7 +922,7 @@ namespace segfault::renderer { renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); - + VkSemaphoreCreateInfo semaphoreInfo{}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; @@ -1014,7 +956,7 @@ namespace segfault::renderer { vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); uint32_t imageIndex{}; - VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], + VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) { recreateSwapChain(); @@ -1024,13 +966,13 @@ namespace segfault::renderer { core::logMessage(core::LogType::Error, "failed to acquire swap chain image!"); throw SegfaultException("failed to acquire swap chain image!"); } - + updateUniformBuffer(currentFrame); vkResetFences(device, 1, &inFlightFences[currentFrame]); vkResetCommandBuffer(commandBuffers[currentFrame], 0); recordCommandBuffer(commandBuffers[currentFrame], imageIndex); - + VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -1039,7 +981,7 @@ namespace segfault::renderer { submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; - + submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; @@ -1065,7 +1007,7 @@ namespace segfault::renderer { presentInfo.pResults = nullptr; // Optional - result = vkQueuePresentKHR(presentQueue, &presentInfo); + result = vkQueuePresentKHR(presentQueue, &presentInfo); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) { framebufferResized = false; recreateSwapChain(); @@ -1073,7 +1015,7 @@ namespace segfault::renderer { core::logMessage(core::LogType::Error, "failed to present swap chain image!"); throw SegfaultException("failed to present swap chain image!"); } - + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } @@ -1115,8 +1057,8 @@ namespace segfault::renderer { throw SegfaultException("failed to find suitable memory type!"); } - void RHIImpl::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, - VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, + void RHIImpl::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, + VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) { VkImageCreateInfo imageInfo{}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -1173,9 +1115,9 @@ namespace segfault::renderer { stbi_image_free(pixels); createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - textureImage, textureImageMemory); - + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + textureImage, textureImageMemory); + transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); @@ -1205,11 +1147,11 @@ namespace segfault::renderer { return imageView; } - + void RHIImpl::createTextureImageView() { textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT); } - + void RHIImpl::createTextureSampler() { VkSamplerCreateInfo samplerInfo{}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; @@ -1273,7 +1215,7 @@ namespace segfault::renderer { memcpy(data, indices.data(), (size_t)bufferSize); vkUnmapMemory(device, stagingBufferMemory); - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); copyBuffer(stagingBuffer, indexBuffer, bufferSize); @@ -1290,7 +1232,7 @@ namespace segfault::renderer { uniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]); vkMapMemory(device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]); @@ -1333,7 +1275,7 @@ namespace segfault::renderer { bufferInfo.buffer = uniformBuffers[i]; bufferInfo.offset = 0; bufferInfo.range = sizeof(UniformBufferObject); - + VkDescriptorImageInfo imageInfo{}; imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; imageInfo.imageView = textureImageView; @@ -1579,10 +1521,10 @@ namespace segfault::renderer { std::vector physicalDevices(physicalDeviceCount); vkEnumeratePhysicalDevices(mImpl->instance, &physicalDeviceCount, physicalDevices.data()); mImpl->physicalDevice = physicalDevices[0]; - + SDL_Vulkan_CreateSurface(mImpl->window, mImpl->instance, &mImpl->surface); - mImpl->createLogicalDevice(mImpl->enableValidationLayers, mImpl->physicalDevice, mImpl->device, mImpl->queueFamilyIndices); + mImpl->createLogicalDevice(mImpl->enableValidationLayers, mImpl->physicalDevice, mImpl->queueFamilyIndices); mImpl->createSwapChain(); mImpl->createImageViews(); @@ -1604,13 +1546,13 @@ namespace segfault::renderer { mImpl->createSyncObjects(); return true; } - + bool RHI::shutdown() { mImpl->cleanupSwapChain(); vkDestroyImage(mImpl->device, mImpl->textureImage, nullptr); vkDestroySampler(mImpl->device, mImpl->textureSampler, nullptr); vkDestroyImageView(mImpl->device, mImpl->textureImageView, nullptr); - + vkFreeMemory(mImpl->device, mImpl->textureImageMemory, nullptr); for (size_t i = 0; i < RHIImpl::MAX_FRAMES_IN_FLIGHT; i++) { vkDestroyBuffer(mImpl->device, mImpl->uniformBuffers[i], nullptr); @@ -1636,7 +1578,7 @@ namespace segfault::renderer { vkDestroyShaderModule(mImpl->device, mImpl->vertShaderModule, nullptr); vkDestroyPipelineLayout(mImpl->device, mImpl->pipelineLayout, nullptr); vkDestroyRenderPass(mImpl->device, mImpl->renderPass, nullptr); - + vkDestroySwapchainKHR(mImpl->device, mImpl->swapChain, nullptr); vkDestroyDevice(mImpl->device, nullptr); delete mImpl; @@ -1645,7 +1587,7 @@ namespace segfault::renderer { return true; } - + void RHI::drawFrame() { mImpl->drawFrame(); } diff --git a/src/runtime/renderer/vulkanbuffer.cpp b/src/runtime/renderer/vulkanbuffer.cpp index 6ba1287..46ad5a9 100644 --- a/src/runtime/renderer/vulkanbuffer.cpp +++ b/src/runtime/renderer/vulkanbuffer.cpp @@ -1,6 +1,8 @@ #include "vulkanbuffer.h" #include #include +#include + namespace segfault::renderer { uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties) { @@ -58,7 +60,7 @@ namespace segfault::renderer { if (vkAllocateMemory(mDevice, &allocInfo, nullptr, &mMemory) != VK_SUCCESS) { return false; } - + return true; } @@ -95,7 +97,7 @@ namespace segfault::renderer { assert(mMapped != nullptr && "Buffer must be mapped before copying data to it."); assert(size <= VK_WHOLE_SIZE && "Size must be less than or equal to VK_WHOLE_SIZE."); assert(size <= 0 || data != nullptr && "Data pointer must not be null when size is greater than 0."); - + if (mMapped != nullptr) { memcpy(mMapped, data, static_cast(size)); } diff --git a/src/runtime/renderer/vulkanbuffer.h b/src/runtime/renderer/vulkanbuffer.h index ba9d6c9..0ab6d94 100644 --- a/src/runtime/renderer/vulkanbuffer.h +++ b/src/runtime/renderer/vulkanbuffer.h @@ -2,15 +2,10 @@ #include "volk.h" +#include "vulkantypes.h" namespace segfault::renderer { - enum class BufferUsage : uint32_t { - VertexBuffer = 0x1, - IndexBuffer = 0x2, - UniformBuffer = 0x4 - }; - class VulkanBuffer { public: VulkanBuffer(VkPhysicalDevice physicalDevice, VkDevice device); @@ -26,6 +21,7 @@ namespace segfault::renderer { void copyTo(void *data, VkDeviceSize size); VkBuffer getBuffer() const { return mBuffer; } VkDeviceMemory getMemory() const { return mMemory; } + size_t getSize() const { return mSize; } private: VkPhysicalDevice mPhysicalDevice{}; @@ -33,6 +29,7 @@ namespace segfault::renderer { VkBuffer mBuffer{}; VkDeviceMemory mMemory{}; void *mMapped{nullptr}; + size_t mSize{0}; }; } // namespace segfault::renderer diff --git a/src/runtime/renderer/vulkandevice.cpp b/src/runtime/renderer/vulkandevice.cpp new file mode 100644 index 0000000..8d63c1d --- /dev/null +++ b/src/runtime/renderer/vulkandevice.cpp @@ -0,0 +1,92 @@ +#include "vulkandevice.h" + +namespace segfault::renderer { + + bool QueueFamilyIndices::isGraphicsComplete() const { + return graphicsFamily.has_value() && presentFamily.has_value(); + } + + VulkanDevice::VulkanDevice(VkPhysicalDevice physicalDevice) : mPhysicalDevice(physicalDevice) { + // empty + } + + VulkanDevice::~VulkanDevice() { + shutdown(); + } + + bool VulkanDevice::init(VkSurfaceKHR surface, const DeviceRequirements& requirements) { + if (mDevice != VK_NULL_HANDLE) { + return true; // Already initialized + } + + mSurface = surface; + + return true; + } + + void VulkanDevice::shutdown() { + if (mDevice == VK_NULL_HANDLE) { + return; + } + + vkDestroyDevice(mDevice, nullptr); + mDevice = VK_NULL_HANDLE; + mSurface = VK_NULL_HANDLE; + } + + VulkanBuffer* VulkanDevice::createBuffer(size_t size, BufferUsage usageFlags, uint32_t memoryPropertyFlags) { + VulkanBuffer* buffer = new VulkanBuffer(mPhysicalDevice, mDevice); + if (!buffer->init(size, usageFlags, memoryPropertyFlags)) { + delete buffer; + return nullptr; + } + return buffer; + } + + bool VulkanDevice::copyBuffer(VulkanBuffer* source, VulkanBuffer* destination, VkDeviceSize size, VkQueue queue, VkCommandPool pool) { + if (!source || !destination) { + return false; + } + + VkCommandBuffer copyCmd = createCommandBuffer(pool); + VkBufferCopy bufferCopy{}; + if (size == 0) + { + bufferCopy.size = source->getSize(); + } + else + { + bufferCopy.size = size; + } + + vkCmdCopyBuffer(copyCmd, source->getBuffer(), destination->getBuffer(), 1, &bufferCopy); + + flushCommandBuffer(copyCmd, queue, pool, true); + } + + VkCommandBuffer VulkanDevice::createCommandBuffer(VkCommandPool commandPool, VkCommandBufferLevel level) { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = level; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + + vkAllocateCommandBuffers(mDevice, &allocInfo, &commandBuffer); + return commandBuffer; + } + + + void VulkanDevice::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, VkCommandPool pool, bool free) { + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); + if (free) { + vkFreeCommandBuffers(mDevice, pool, 1, &commandBuffer); + } + } + +} // namespace segfault::renderer diff --git a/src/runtime/renderer/vulkandevice.h b/src/runtime/renderer/vulkandevice.h new file mode 100644 index 0000000..f85df84 --- /dev/null +++ b/src/runtime/renderer/vulkandevice.h @@ -0,0 +1,95 @@ +#pragma once + +#include "volk.h" +#include "core/segfault.h" +#include +#include +#include +#include +#include "vulkantypes.h" +#include "vulkanbuffer.h" + +namespace segfault::renderer { + + struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional presentFamily; + std::optional computeFamily; + std::optional transferFamily; + + bool isComplete() const; + bool isGraphicsComplete() const; + }; + + struct DeviceRequirements { + std::vector requiredExtensions; + std::vector optionalExtensions; + VkPhysicalDeviceFeatures requiredFeatures{}; + VkPhysicalDeviceFeatures optionalFeatures{}; + bool requirePresentQueue{ false }; + bool requireComputeQueue{ false }; + bool requireTransferQueue{ false }; + }; + + struct DeviceProperties { + VkPhysicalDeviceProperties properties{}; + VkPhysicalDeviceFeatures features{}; + VkPhysicalDeviceMemoryProperties memoryProperties{}; + std::vector availableExtensions; + std::vector queueFamilies; + }; + + //--------------------------------------------------------------------------------------------- + /// @class VulkanDevice + /// @brief Encapsulates a Vulkan physical and logical device with queue management. + /// + /// This class manages the selection, creation, and lifecycle of Vulkan devices. + /// It provides access to device properties, features, queues, and memory management. + //--------------------------------------------------------------------------------------------- + class SEGFAULT_EXPORT VulkanDevice final { + public: + // Disallow copying + VulkanDevice(const VulkanDevice&) = delete; + VulkanDevice& operator=(const VulkanDevice&) = delete; + + /// @brief Constructs a VulkanDevice. + /// @param instance The Vulkan instance. + VulkanDevice(VkPhysicalDevice physicalDevice); + + /// @brief The class destructor. Ensures that all Vulkan resources are properly released. + ~VulkanDevice(); + + /// @brief Initializes the device with the given surface and requirements. + /// @param surface The presentation surface (can be VK_NULL_HANDLE for headless). + /// @param requirements Device feature and extension requirements. + /// @return True if device initialization succeeded. + bool init(VkSurfaceKHR surface, const DeviceRequirements& requirements = {}); + + /// @brief Shuts down the device and releases all resources. + void shutdown(); + + // Accessors + VkPhysicalDevice getPhysicalDevice() const { return mPhysicalDevice; } + VkDevice getDevice() const { return mDevice; } + VkQueue getGraphicsQueue() const { return mGraphicsQueue; } + VkQueue getPresentQueue() const { return mPresentQueue; } + const QueueFamilyIndices& getQueueFamilyIndices() const { return mQueueFamilyIndices; } + uint32_t getGraphicsQueueFamilyIndex() const; + uint32_t getPresentQueueFamilyIndex() const; + const DeviceProperties& getProperties() const { return mProperties; } + VulkanBuffer* createBuffer(size_t size, BufferUsage usageFlags, uint32_t memoryPropertyFlags); + bool copyBuffer(VulkanBuffer *source, VulkanBuffer *destination, VkDeviceSize size, VkQueue queue, VkCommandPool pool); + VkCommandBuffer createCommandBuffer(VkCommandPool commandPool, VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY); + void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, VkCommandPool pool, bool free); + + private: + VkPhysicalDevice mPhysicalDevice{}; + VkDevice mDevice{}; + VkQueue mGraphicsQueue{}; + VkQueue mPresentQueue{}; + QueueFamilyIndices mQueueFamilyIndices{}; + DeviceProperties mProperties{}; + VkSurfaceKHR mSurface{}; + }; + +} // namespace segfault::renderer diff --git a/src/runtime/renderer/vulkantypes.h b/src/runtime/renderer/vulkantypes.h new file mode 100644 index 0000000..0692df1 --- /dev/null +++ b/src/runtime/renderer/vulkantypes.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace segfault::renderer { + + enum class BufferUsage : uint32_t { + VertexBuffer = 0x1, + IndexBuffer = 0x2, + UniformBuffer = 0x4 + }; + +} // namespace segfault::renderer diff --git a/src/runtime/renderer/vulkanutils.cpp b/src/runtime/renderer/vulkanutils.cpp new file mode 100644 index 0000000..9d48d6d --- /dev/null +++ b/src/runtime/renderer/vulkanutils.cpp @@ -0,0 +1,65 @@ +#include "vulkanutils.h" +#include "core/segfaultexception.h" +#include "volk.h" + +#include + +namespace segfault::renderer { + + using namespace segfault::core; + + std::array Vertex::getAttributeDescriptions() { + std::array attributeDescriptions{}; + + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, pos); + + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + + attributeDescriptions[2].binding = 0; + attributeDescriptions[2].location = 2; + attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[2].offset = offsetof(Vertex, texCoord); + + return attributeDescriptions; + } + + VkVertexInputBindingDescription Vertex::getBindingDescription() { + VkVertexInputBindingDescription bindingDescription{}; + + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + return bindingDescription; + } + + VkFormat VulkanUtils::findSupportedFormat(VkPhysicalDevice &physicalDevice, const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { + for (VkFormat format : candidates) { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); + if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) { + return format; + } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) { + return format; + } + } + + throw SegfaultException("failed to find supported format!"); + } + + VkFormat VulkanUtils::findDepthFormat(VkPhysicalDevice &physicalDevice) { + return findSupportedFormat( + physicalDevice, + { 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 + ); + } + +} diff --git a/src/runtime/renderer/vulkanutils.h b/src/runtime/renderer/vulkanutils.h new file mode 100644 index 0000000..d0f682f --- /dev/null +++ b/src/runtime/renderer/vulkanutils.h @@ -0,0 +1,39 @@ +#pragma once + +#include "volk.h" + +#include + +#define GLM_FORCE_RADIANS +#include +#include + +namespace segfault::renderer { + + struct Vertex { + glm::vec3 pos{}; + glm::vec3 color{}; + glm::vec2 texCoord{}; + + static std::array getAttributeDescriptions(); + static VkVertexInputBindingDescription getBindingDescription(); + }; + + /// @brief Utility functions for Vulkan operations. + class VulkanUtils { + public: + /// @brief Finds a supported format from a list of candidates. + /// @param physicalDevice The physical device to query. + /// @param candidates A list of candidate formats. + /// @param tiling The desired image tiling. + /// @param features The required format features. + /// @return The first supported format from the candidates. + static VkFormat findSupportedFormat(VkPhysicalDevice &physicalDevice, const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); + + /// @brief Finds a supported depth format. + /// @param physicalDevice The physical device to query. + /// @return A supported depth format. + static VkFormat findDepthFormat(VkPhysicalDevice &physicalDevice); + }; + +} // namespace segfault::renderer