diff --git a/config/Sources.cmake b/config/Sources.cmake index ac49dac1a030be3b3dff1c4f6dc3339b1baca422..1b35d798597d0c781d5c4e90679679a83fcedcb2 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -24,6 +24,12 @@ set(vkcv_sources ${vkcv_include}/vkcv/BufferManager.hpp ${vkcv_source}/vkcv/BufferManager.cpp + ${vkcv_include}/vkcv/Image.hpp + ${vkcv_source}/vkcv/Image.cpp + + ${vkcv_source}/vkcv/ImageManager.hpp + ${vkcv_source}/vkcv/ImageManager.cpp + ${vkcv_include}/vkcv/SwapChain.hpp ${vkcv_source}/vkcv/SwapChain.cpp @@ -50,9 +56,6 @@ set(vkcv_sources ${vkcv_source}/vkcv/ImageLayoutTransitions.hpp ${vkcv_source}/vkcv/ImageLayoutTransitions.cpp - - ${vkcv_source}/vkcv/Framebuffer.hpp - ${vkcv_source}/vkcv/Framebuffer.cpp ${vkcv_include}/vkcv/VertexLayout.hpp ${vkcv_source}/vkcv/VertexLayout.cpp diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp index 52e338483af7ddd01b6345b4ff1d0187ae4fbd8a..4b12f47ba8497bd09260d24523c28f207f64a98c 100644 --- a/include/vkcv/BufferManager.hpp +++ b/include/vkcv/BufferManager.hpp @@ -31,9 +31,9 @@ namespace vkcv { vk::Buffer m_handle; vk::DeviceMemory m_memory; - size_t m_size; + size_t m_size = 0; void* m_mapped = nullptr; - bool m_mappable; + bool m_mappable = false; }; Core* m_core; @@ -55,7 +55,7 @@ namespace vkcv /** * Creates and allocates a new buffer and returns its - * unique buffer handle id. + * unique buffer handle. * * @param type Type of buffer * @param size Size of buffer in bytes @@ -66,7 +66,7 @@ namespace vkcv /** * Returns the Vulkan buffer handle of a buffer - * represented by a given buffer handle id. + * represented by a given buffer handle. * * @param handle Buffer handle * @return Vulkan buffer handle @@ -74,6 +74,16 @@ namespace vkcv [[nodiscard]] vk::Buffer getBuffer(const BufferHandle& handle) const; + /** + * Returns the size of a buffer represented + * by a given buffer handle. + * + * @param handle Buffer handle + * @return Size of the buffer + */ + [[nodiscard]] + size_t getBufferSize(const BufferHandle& handle) const; + /** * Returns the Vulkan device memory handle of a buffer * represented by a given buffer handle id. @@ -86,7 +96,7 @@ namespace vkcv /** * Fills a buffer represented by a given buffer - * handle id with custom data. + * handle with custom data. * * @param handle Buffer handle * @param data Pointer to data @@ -97,7 +107,7 @@ namespace vkcv /** * Maps memory to a buffer represented by a given - * buffer handle id and returns it. + * buffer handle and returns it. * * @param handle Buffer handle * @param offset Offset of mapping in bytes @@ -108,7 +118,7 @@ namespace vkcv /** * Unmaps memory from a buffer represented by a given - * buffer handle id. + * buffer handle. * * @param handle Buffer handle */ @@ -116,7 +126,7 @@ namespace vkcv /** * Destroys and deallocates buffer represented by a given - * buffer handle id. + * buffer handle. * * @param handle Buffer handle */ diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index ab0d2aca0eccbd3c6adfea191cefa2c63623b0b9..388bc0ea40950f0e73a23a280af753b79ad00da7 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -13,6 +13,7 @@ #include "vkcv/PassConfig.hpp" #include "vkcv/Handles.hpp" #include "vkcv/Buffer.hpp" +#include "vkcv/Image.hpp" #include "vkcv/PipelineConfig.hpp" #include "CommandResources.hpp" #include "SyncResources.hpp" @@ -26,6 +27,7 @@ namespace vkcv class PipelineManager; class DescriptorManager; class BufferManager; + class ImageManager; struct SubmitInfo { QueueType queueType; @@ -63,6 +65,7 @@ namespace vkcv std::unique_ptr<PipelineManager> m_PipelineManager; std::unique_ptr<DescriptorManager> m_DescriptorManager; std::unique_ptr<BufferManager> m_BufferManager; + std::unique_ptr<ImageManager> m_ImageManager; CommandResources m_CommandResources; SyncResources m_SyncResources; @@ -170,6 +173,18 @@ namespace vkcv Buffer<T> createBuffer(vkcv::BufferType type, size_t count, BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL) { return Buffer<T>::create(m_BufferManager.get(), type, count, memoryType); } + + /** + * Creates an #Image with a given format, width, height and depth. + * + * @param format Image format + * @param width Image width + * @param height Image height + * @param depth Image depth + * @return Image-Object + */ + [[nodiscard]] + Image createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth = 1); /** TODO: * @param setDescriptions diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp index 7b500ea514f1034e9822f9c12936b5e63ba3225e..1fd6573976b1f5aab572eb982cc0c61eff01a9e9 100644 --- a/include/vkcv/Handles.hpp +++ b/include/vkcv/Handles.hpp @@ -64,4 +64,10 @@ namespace vkcv using Handle::Handle; }; + class ImageHandle : public Handle { + friend class ImageManager; + private: + using Handle::Handle; + }; + } diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5050513b6fd612605b7c83d18d2d3beaaef9a8a0 --- /dev/null +++ b/include/vkcv/Image.hpp @@ -0,0 +1,50 @@ +#pragma once +/** + * @authors Lars Hoerttrich + * @file vkcv/Buffer.hpp + * @brief class for image handles + */ +#include "vulkan/vulkan.hpp" + +#include "Handles.hpp" + +namespace vkcv { + + class ImageManager; + class Image { + friend class Core; + public: + [[nodiscard]] + vk::Format getFormat() const; + + [[nodiscard]] + uint32_t getWidth() const; + + [[nodiscard]] + uint32_t getHeight() const; + + [[nodiscard]] + uint32_t getDepth() const; + + [[nodiscard]] + vk::ImageLayout getLayout() const; + + void switchLayout(vk::ImageLayout newLayout); + + void fill(void* data, size_t size = SIZE_MAX); + private: + ImageManager* const m_manager; + const ImageHandle m_handle; + const vk::Format m_format; + const uint32_t m_width; + const uint32_t m_height; + const uint32_t m_depth; + vk::ImageLayout m_layout; + + Image(ImageManager* manager, const ImageHandle& handle, vk::Format format, uint32_t width, uint32_t height, uint32_t depth); + + static Image create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth); + + }; + +} diff --git a/include/vkcv/PassConfig.hpp b/include/vkcv/PassConfig.hpp index b8e80c67c2b70e3a0e6e2732b950ccaed38da3bf..d9a5bcd83acca5f5ba86b4e6ce6973acbed89de6 100644 --- a/include/vkcv/PassConfig.hpp +++ b/include/vkcv/PassConfig.hpp @@ -53,7 +53,6 @@ namespace vkcv struct PassConfig { - PassConfig() = delete; explicit PassConfig(std::vector<AttachmentDescription> attachments) noexcept; std::vector<AttachmentDescription> attachments{}; }; diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index e1cbc40e747f9f68e44db6ea9ca3dcd556148a4b..6ba3656f6c02077a868c5d99b1331fab5be0abe6 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -60,9 +60,19 @@ int main(int argc, const char** argv) { vkcv::AttachmentLayout::PRESENTATION, vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, - core.getSwapchainImageFormat()); + core.getSwapchainImageFormat() + ); + + const vkcv::AttachmentDescription depth_attachment( + vkcv::AttachmentLayout::UNDEFINED, + vkcv::AttachmentLayout::DEPTH_STENCIL_ATTACHMENT, + vkcv::AttachmentLayout::DEPTH_STENCIL_ATTACHMENT, + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + vk::Format::eD32Sfloat + ); - vkcv::PassConfig trianglePassDefinition({ present_color_attachment }); + vkcv::PassConfig trianglePassDefinition({ present_color_attachment, depth_attachment }); vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition); if (!trianglePass) { @@ -83,6 +93,10 @@ int main(int argc, const char** argv) { std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; return EXIT_FAILURE; } + + vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h); + + texture.fill(mesh.texture_hack.img); auto start = std::chrono::system_clock::now(); while (window.isWindowOpen()) { @@ -94,8 +108,20 @@ int main(int argc, const char** argv) { cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count()); const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView(); - core.renderMesh(trianglePass, trianglePipeline, windowWidth, windowHeight, sizeof(mvp), &mvp, vertexBuffer.getHandle(), indexBuffer.getHandle(), mesh.vertexGroups[0].numIndices); + core.renderMesh( + trianglePass, + trianglePipeline, + windowWidth, + windowHeight, + sizeof(mvp), + &mvp, + vertexBuffer.getHandle(), + indexBuffer.getHandle(), + mesh.vertexGroups[0].numIndices + ); + core.endFrame(); } + return 0; } diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp index e27338d837e89b5a9442c4ba2dff2061bf0e0f53..740e89993860a7f71ac78aedae5cf3ed946c7b49 100644 --- a/src/vkcv/BufferManager.cpp +++ b/src/vkcv/BufferManager.cpp @@ -28,28 +28,29 @@ namespace vkcv { } /** - * @brief searches memory type index for buffer allocation, inspired by vulkan tutorial and "https://github.com/KhronosGroup/Vulkan-Hpp/blob/master/samples/utils/utils.hpp" + * @brief searches memory type index for buffer allocation, combines requirements of buffer and application * @param physicalMemoryProperties Memory Properties of physical device - * @param typeBits + * @param typeBits Bit field for suitable memory types * @param requirements Property flags that are required * @return memory type index for Buffer */ - uint32_t searchMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) { - uint32_t memoryTypeIndex = 0; - - for (uint32_t i = 0; i < physicalMemoryProperties.memoryTypeCount; i++) - { - if ((typeBits & 1) && - ((physicalMemoryProperties.memoryTypes[i].propertyFlags & requirements) == requirements)) - { - memoryTypeIndex = i; - break; - } - - typeBits >>= 1; + uint32_t searchBufferMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) { + const uint32_t memoryCount = physicalMemoryProperties.memoryTypeCount; + for (uint32_t memoryIndex = 0; memoryIndex < memoryCount; ++memoryIndex) { + const uint32_t memoryTypeBits = (1 << memoryIndex); + const bool isRequiredMemoryType = typeBits & memoryTypeBits; + + const vk::MemoryPropertyFlags properties = + physicalMemoryProperties.memoryTypes[memoryIndex].propertyFlags; + const bool hasRequiredProperties = + (properties & requirements) == requirements; + + if (isRequiredMemoryType && hasRequiredProperties) + return static_cast<int32_t>(memoryIndex); } - - return memoryTypeIndex; + + // failed to find memory type + return -1; } BufferHandle BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType) { @@ -106,7 +107,7 @@ namespace vkcv { break; } - const uint32_t memoryTypeIndex = searchMemoryType( + const uint32_t memoryTypeIndex = searchBufferMemoryType( physicalDevice.getMemoryProperties(), requirements.memoryTypeBits, memoryTypeFlags @@ -193,6 +194,18 @@ namespace vkcv { return buffer.m_handle; } + size_t BufferManager::getBufferSize(const BufferHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_buffers.size()) { + return 0; + } + + auto& buffer = m_buffers[id]; + + return buffer.m_size; + } + vk::DeviceMemory BufferManager::getDeviceMemory(const BufferHandle& handle) const { const uint64_t id = handle.getId(); @@ -315,10 +328,12 @@ namespace vkcv { if (buffer.m_memory) { device.freeMemory(buffer.m_memory); + buffer.m_memory = nullptr; } if (buffer.m_handle) { device.destroyBuffer(buffer.m_handle); + buffer.m_handle = nullptr; } } diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 13800254d78cb679c81d58a003089aa961986937..d24282f194e79c36096677b7f13d4b218911c3a1 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -10,10 +10,10 @@ #include "PassManager.hpp" #include "PipelineManager.hpp" #include "vkcv/BufferManager.hpp" +#include "ImageManager.hpp" #include "DescriptorManager.hpp" #include "Surface.hpp" #include "ImageLayoutTransitions.hpp" -#include "Framebuffer.hpp" namespace vkcv { @@ -95,11 +95,14 @@ namespace vkcv m_PipelineManager{std::make_unique<PipelineManager>(m_Context.m_Device)}, m_DescriptorManager(std::make_unique<DescriptorManager>(m_Context.m_Device)), m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager())}, + m_ImageManager{std::unique_ptr<ImageManager>(new ImageManager(*m_BufferManager))}, m_CommandResources(commandResources), m_SyncResources(syncResources) { m_BufferManager->m_core = this; m_BufferManager->init(); + + m_ImageManager->m_core = this; } Core::~Core() noexcept { @@ -118,8 +121,7 @@ namespace vkcv PipelineHandle Core::createGraphicsPipeline(const PipelineConfig &config) { - const vk::RenderPass &pass = m_PassManager->getVkPass(config.m_PassHandle); - return m_PipelineManager->createPipeline(config, pass); + return m_PipelineManager->createPipeline(config, *m_PassManager); } @@ -161,6 +163,13 @@ namespace vkcv m_Context.getDevice().waitIdle(); // FIMXE: this is a sin against graphics programming, but its getting late - Alex destroyTemporaryFramebuffers(); } + + vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass& renderpass, + const int width, const int height, const std::vector<vk::ImageView>& attachments) { + const vk::FramebufferCreateFlags flags = {}; + const vk::FramebufferCreateInfo createInfo(flags, renderpass, attachments.size(), attachments.data(), width, height, 1); + return device.createFramebuffer(createInfo); + } void Core::renderMesh(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, const int width, const int height, const size_t pushConstantSize, const void *pushConstantData, @@ -171,6 +180,17 @@ namespace vkcv } const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle); + const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle); + + ImageHandle depthImage; + + for (const auto& attachment : passConfig.attachments) { + if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + depthImage = m_ImageManager->createImage(width, height, 1, attachment.format); + break; + } + } + const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex]; const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle); const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle); @@ -178,19 +198,47 @@ namespace vkcv const vk::Buffer vulkanVertexBuffer = m_BufferManager->getBuffer(vertexBuffer); const vk::Buffer vulkanIndexBuffer = m_BufferManager->getBuffer(indexBuffer); - const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView); + std::vector<vk::ImageView> attachments; + attachments.push_back(imageView); + + if (depthImage) { + attachments.push_back(m_ImageManager->getVulkanImageView(depthImage)); + } + + const vk::Framebuffer framebuffer = createFramebuffer( + m_Context.getDevice(), + renderpass, + width, + height, + attachments + ); + m_TemporaryFramebuffers.push_back(framebuffer); SubmitInfo submitInfo; submitInfo.queueType = QueueType::Graphics; submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; - submitCommands(submitInfo, [renderpass, renderArea, imageView, framebuffer, pipeline, pipelineLayout, - pushConstantSize, pushConstantData, vulkanVertexBuffer, indexCount, vulkanIndexBuffer](const vk::CommandBuffer& cmdBuffer) { - - const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f }; - const vk::ClearValue clearValues(clearColor); - - const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, 1, &clearValues); + submitCommands(submitInfo, [&](const vk::CommandBuffer& cmdBuffer) { + std::vector<vk::ClearValue> clearValues; + + for (const auto& attachment : passConfig.attachments) { + if (attachment.load_operation == AttachmentOperation::CLEAR) { + float clear = 0.0f; + + if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + clear = 1.0f; + } + + clearValues.emplace_back(std::array<float, 4>{ + clear, + clear, + clear, + 1.f + }); + } + } + + const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); const vk::SubpassContents subpassContents = {}; cmdBuffer.beginRenderPass(beginInfo, subpassContents, {}); @@ -201,7 +249,9 @@ namespace vkcv cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData); cmdBuffer.drawIndexed(indexCount, 1, 0, 0, {}); cmdBuffer.endRenderPass(); - }, nullptr); + }, [&]() { + m_ImageManager->destroyImage(depthImage); + }); } void Core::endFrame() { @@ -262,6 +312,11 @@ namespace vkcv finish(); } } + + Image Core::createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth) + { + return Image::create(m_ImageManager.get(), format, width, height, depth); + } ResourcesHandle Core::createResourceDescription(const std::vector<DescriptorSet> &descriptorSets) { diff --git a/src/vkcv/Framebuffer.cpp b/src/vkcv/Framebuffer.cpp deleted file mode 100644 index 3b3e8000668e460d476b211984e9e12249f066c0..0000000000000000000000000000000000000000 --- a/src/vkcv/Framebuffer.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "Framebuffer.hpp" - -namespace vkcv { - vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass renderpass, - const int width, const int height, const vk::ImageView imageView) { - const vk::FramebufferCreateFlags flags = {}; - const uint32_t attachmentCount = 1; // TODO: proper value - const vk::FramebufferCreateInfo createInfo(flags, renderpass, attachmentCount, &imageView, width, height, 1); - return device.createFramebuffer(createInfo, nullptr, {}); - } -} \ No newline at end of file diff --git a/src/vkcv/Framebuffer.hpp b/src/vkcv/Framebuffer.hpp deleted file mode 100644 index 7d5d718adbde0c3f8eb8d97c539fb73f7771987f..0000000000000000000000000000000000000000 --- a/src/vkcv/Framebuffer.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include <vulkan/vulkan.hpp> - -namespace vkcv{ - vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass renderpass, - const int width, const int height, const vk::ImageView imageView); -} \ No newline at end of file diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c4341b4f173028afa5dce98383b2ae34833ee2fa --- /dev/null +++ b/src/vkcv/Image.cpp @@ -0,0 +1,58 @@ +/** + * @authors Lars Hoerttrich + * @file vkcv/Image.cpp + * @brief class for image handles + */ +#include "vkcv/Image.hpp" +#include "ImageManager.hpp" + +namespace vkcv{ + + Image Image::create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth) + { + return Image(manager, manager->createImage(width, height, depth, format), format, width, height, depth); + } + + vk::Format Image::getFormat() const { + return m_format; + } + + uint32_t Image::getWidth() const { + return m_width; + } + + uint32_t Image::getHeight() const { + return m_height; + } + + uint32_t Image::getDepth() const { + return m_depth; + } + + vk::ImageLayout Image::getLayout() const { + return m_layout; + } + + void Image::switchLayout(vk::ImageLayout newLayout) + { + m_manager->switchImageLayout(m_handle, m_layout, newLayout); + m_layout = newLayout; + } + + void Image::fill(void *data, size_t size) { + m_manager->fillImage(m_handle, data, size); + } + + Image::Image(ImageManager* manager, const ImageHandle& handle, + vk::Format format, uint32_t width, uint32_t height, uint32_t depth) : + m_manager(manager), + m_handle(handle), + m_format(format), + m_width(width), + m_height(height), + m_depth(depth), + m_layout(vk::ImageLayout::eUndefined) + { + } + +} diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3896d6bc4abdd24264ad5d468b49ebf08bd20be7 --- /dev/null +++ b/src/vkcv/ImageManager.cpp @@ -0,0 +1,397 @@ +/** + * @authors Lars Hoerttrich + * @file vkcv/ImageManager.cpp + * @brief class creating and managing images + */ +#include "ImageManager.hpp" +#include "vkcv/Core.hpp" + +#include <algorithm> + +namespace vkcv { + + /** + * @brief searches memory type index for image allocation, combines requirements of image and application + * @param physicalMemoryProperties Memory Properties of physical device + * @param typeBits Bit field for suitable memory types + * @param requirements Property flags that are required + * @return memory type index for image + */ + uint32_t searchImageMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) { + const uint32_t memoryCount = physicalMemoryProperties.memoryTypeCount; + for (uint32_t memoryIndex = 0; memoryIndex < memoryCount; ++memoryIndex) { + const uint32_t memoryTypeBits = (1 << memoryIndex); + const bool isRequiredMemoryType = typeBits & memoryTypeBits; + + const vk::MemoryPropertyFlags properties = + physicalMemoryProperties.memoryTypes[memoryIndex].propertyFlags; + const bool hasRequiredProperties = + (properties & requirements) == requirements; + + if (isRequiredMemoryType && hasRequiredProperties) + return static_cast<int32_t>(memoryIndex); + } + + // failed to find memory type + return -1; + } + + ImageManager::ImageManager(BufferManager& bufferManager) noexcept : + m_core(nullptr), m_bufferManager(bufferManager), m_images() + { + } + + ImageManager::~ImageManager() noexcept { + for (uint64_t id = 0; id < m_images.size(); id++) { + destroyImage(ImageHandle(id)); + } + } + + bool isDepthImageFormat(vk::Format format) { + if ((format == vk::Format::eD16Unorm) || (format == vk::Format::eD16UnormS8Uint) || + (format == vk::Format::eD24UnormS8Uint) || (format == vk::Format::eD32Sfloat) || + (format == vk::Format::eD32SfloatS8Uint)) { + return true; + } else { + return false; + } + } + + ImageHandle ImageManager::createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format) + { + const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice(); + + const vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format); + + vk::ImageCreateFlags createFlags; + vk::ImageUsageFlags imageUsageFlags = ( + vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst + ); + + const bool isDepthFormat = isDepthImageFormat(format); + + if (isDepthFormat) { + imageUsageFlags |= vk::ImageUsageFlagBits::eDepthStencilAttachment; + } + + const vk::Device& device = m_core->getContext().getDevice(); + + vk::ImageType imageType = vk::ImageType::e3D; + vk::ImageViewType imageViewType = vk::ImageViewType::e3D; + + if (depth <= 1) { + if (height <= 1) { + imageType = vk::ImageType::e1D; + imageViewType = vk::ImageViewType::e1D; + } else { + imageType = vk::ImageType::e2D; + imageViewType = vk::ImageViewType::e2D; + } + } + + vk::ImageTiling imageTiling = vk::ImageTiling::eOptimal; + + if (!formatProperties.optimalTilingFeatures) { + if (!formatProperties.linearTilingFeatures) + return ImageHandle(); + + imageTiling = vk::ImageTiling::eLinear; + } + + const vk::ImageFormatProperties imageFormatProperties = physicalDevice.getImageFormatProperties( + format, imageType, imageTiling, imageUsageFlags + ); + + const uint32_t mipLevels = std::min<uint32_t>(1, imageFormatProperties.maxMipLevels); + const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers); + + const vk::ImageCreateInfo imageCreateInfo( + createFlags, + imageType, + format, + vk::Extent3D(width, height, depth), + mipLevels, + arrayLayers, + vk::SampleCountFlagBits::e1, + imageTiling, + imageUsageFlags, + vk::SharingMode::eExclusive, + {}, + vk::ImageLayout::eUndefined + ); + + vk::Image image = device.createImage(imageCreateInfo); + + const vk::MemoryRequirements requirements = device.getImageMemoryRequirements(image); + + vk::MemoryPropertyFlags memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal; + + const uint32_t memoryTypeIndex = searchImageMemoryType( + physicalDevice.getMemoryProperties(), + requirements.memoryTypeBits, + memoryTypeFlags + ); + + vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex)); + device.bindImageMemory(image, memory, 0); + + vk::ImageAspectFlags aspectFlags; + + if (isDepthFormat) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } + + const vk::ImageViewCreateInfo imageViewCreateInfo ( + {}, + image, + imageViewType, + format, + vk::ComponentMapping( + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity + ), + vk::ImageSubresourceRange( + aspectFlags, + 0, + mipLevels, + 0, + arrayLayers + ) + ); + + vk::ImageView view = device.createImageView(imageViewCreateInfo); + + const uint64_t id = m_images.size(); + m_images.push_back({ image, memory, view, width, height, depth, format, arrayLayers, mipLevels }); + return ImageHandle(id); + } + + vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return nullptr; + } + + auto& image = m_images[id]; + + return image.m_handle; + } + + vk::DeviceMemory ImageManager::getVulkanDeviceMemory(const ImageHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return nullptr; + } + + auto& image = m_images[id]; + + return image.m_memory; + } + + vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return nullptr; + } + + auto& image = m_images[id]; + + return image.m_view; + } + + void ImageManager::switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return; + } + + auto& image = m_images[id]; + + //alternativly we could use switch case for every variable to set + vk::AccessFlags sourceAccessMask; + vk::PipelineStageFlags sourceStage; + + vk::AccessFlags destinationAccessMask; + vk::PipelineStageFlags destinationStage; + + if ((oldLayout == vk::ImageLayout::eUndefined) && + (newLayout == vk::ImageLayout::eTransferDstOptimal)) + { + destinationAccessMask = vk::AccessFlagBits::eTransferWrite; + + sourceStage = vk::PipelineStageFlagBits::eTopOfPipe; + destinationStage = vk::PipelineStageFlagBits::eTransfer; + } + else if ((oldLayout == vk::ImageLayout::eTransferDstOptimal) && + (newLayout == vk::ImageLayout::eShaderReadOnlyOptimal)) + { + sourceAccessMask = vk::AccessFlagBits::eTransferWrite; + destinationAccessMask = vk::AccessFlagBits::eShaderRead; + + sourceStage = vk::PipelineStageFlagBits::eTransfer; + destinationStage = vk::PipelineStageFlagBits::eFragmentShader; + } + + vk::ImageAspectFlags aspectFlags; + + if (isDepthImageFormat(image.m_format)) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } + + vk::ImageSubresourceRange imageSubresourceRange( + aspectFlags, + 0, + image.m_levels, + 0, + image.m_layers + ); + + vk::ImageMemoryBarrier imageMemoryBarrier( + sourceAccessMask, + destinationAccessMask, + oldLayout, + newLayout, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + image.m_handle, + imageSubresourceRange + ); + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Graphics; + + m_core->submitCommands( + submitInfo, + [sourceStage, destinationStage, imageMemoryBarrier](const vk::CommandBuffer& commandBuffer) { + commandBuffer.pipelineBarrier( + sourceStage, + destinationStage, + {}, + nullptr, + nullptr, + imageMemoryBarrier + ); + }, + nullptr + ); + } + + void ImageManager::fillImage(const ImageHandle& handle, void* data, size_t size) + { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return; + } + + auto& image = m_images[id]; + + switchImageLayout( + handle, + vk::ImageLayout::eUndefined, + vk::ImageLayout::eTransferDstOptimal + ); + + uint32_t channels = 4; // TODO: check image.m_format + const size_t image_size = ( + image.m_width * image.m_height * image.m_depth * channels + ); + + const size_t max_size = std::min(size, image_size); + + BufferHandle bufferHandle = m_bufferManager.createBuffer( + BufferType::STAGING, max_size, BufferMemoryType::HOST_VISIBLE + ); + + m_bufferManager.fillBuffer(bufferHandle, data, max_size, 0); + + vk::Buffer stagingBuffer = m_bufferManager.getBuffer(bufferHandle); + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Transfer; + + m_core->submitCommands( + submitInfo, + [&image, &stagingBuffer](const vk::CommandBuffer& commandBuffer) { + vk::ImageAspectFlags aspectFlags; + + if (isDepthImageFormat(image.m_format)) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } + + const vk::BufferImageCopy region ( + 0, + 0, + 0, + vk::ImageSubresourceLayers( + aspectFlags, + 0, + 0, + image.m_layers + ), + vk::Offset3D(0, 0, 0), + vk::Extent3D(image.m_width, image.m_height, image.m_depth) + ); + + commandBuffer.copyBufferToImage( + stagingBuffer, + image.m_handle, + vk::ImageLayout::eTransferDstOptimal, + 1, + ®ion + ); + }, + [&]() { + switchImageLayout( + handle, + vk::ImageLayout::eTransferDstOptimal, + vk::ImageLayout::eShaderReadOnlyOptimal + ); + + m_bufferManager.destroyBuffer(bufferHandle); + } + ); + } + + void ImageManager::destroyImage(const ImageHandle& handle) + { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return; + } + + auto& image = m_images[id]; + + const vk::Device& device = m_core->getContext().getDevice(); + + if (image.m_view) { + device.destroyImageView(image.m_view); + image.m_view = nullptr; + } + + if (image.m_memory) { + device.freeMemory(image.m_memory); + image.m_memory = nullptr; + } + + if (image.m_handle) { + device.destroyImage(image.m_handle); + image.m_handle = nullptr; + } + } + + +} \ No newline at end of file diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7dc6746f37b7d03900302afbd0536b909f9e48fc --- /dev/null +++ b/src/vkcv/ImageManager.hpp @@ -0,0 +1,70 @@ +#pragma once +/** + * @authors Lars Hoerttrich + * @file vkcv/ImageManager.hpp + * @brief class creating and managing images + */ +#include <vector> +#include <vulkan/vulkan.hpp> + +#include "vkcv/BufferManager.hpp" +#include "vkcv/Handles.hpp" + +namespace vkcv { + + class ImageManager + { + friend class Core; + private: + struct Image + { + vk::Image m_handle; + vk::DeviceMemory m_memory; + vk::ImageView m_view; + uint32_t m_width = 0; + uint32_t m_height = 0; + uint32_t m_depth = 0; + vk::Format m_format; + uint32_t m_layers = 1; + uint32_t m_levels = 1; + }; + + Core* m_core; + BufferManager& m_bufferManager; + + std::vector<Image> m_images; + + ImageManager(BufferManager& bufferManager) noexcept; + + public: + ~ImageManager() noexcept; + ImageManager(ImageManager&& other) = delete; + ImageManager(const ImageManager& other) = delete; + + ImageManager& operator=(ImageManager&& other) = delete; + ImageManager& operator=(const ImageManager& other) = delete; + + ImageHandle createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format); + + [[nodiscard]] + vk::Image getVulkanImage(const ImageHandle& handle) const; + + [[nodiscard]] + vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle& handle) const; + + [[nodiscard]] + vk::ImageView getVulkanImageView(const ImageHandle& handle) const; + + void switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout); + void fillImage(const ImageHandle& handle, void* data, size_t size); + + /** + * Destroys and deallocates image represented by a given + * buffer handle. + * + * @param handle Image handle + */ + void destroyImage(const ImageHandle& handle); + + }; +} \ No newline at end of file diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp index d69024a805f45cda549365c7cc97fc57f59ef926..26e5f290d04ebaf16940cd99386253b5ab3622cc 100644 --- a/src/vkcv/PassManager.cpp +++ b/src/vkcv/PassManager.cpp @@ -49,16 +49,16 @@ namespace vkcv PassManager::PassManager(vk::Device device) noexcept : m_Device{device}, - m_RenderPasses{}, + m_Passes{}, m_NextPassId(0) {} PassManager::~PassManager() noexcept { - for(const auto &pass : m_RenderPasses) - m_Device.destroy(pass); - - m_RenderPasses.clear(); + for(const auto &pass : m_Passes) + m_Device.destroy(pass.m_Handle); + + m_Passes.clear(); m_NextPassId = 0; } @@ -90,46 +90,74 @@ namespace vkcv colorAttachmentReferences.push_back(attachmentRef); } - vk::AttachmentDescription attachmentDesc({}, - format, - vk::SampleCountFlagBits::e1, - getVKLoadOpFromAttachOp(config.attachments[i].load_operation), - getVkStoreOpFromAttachOp(config.attachments[i].store_operation), - vk::AttachmentLoadOp::eDontCare, - vk::AttachmentStoreOp::eDontCare, - getVkLayoutFromAttachLayout(config.attachments[i].layout_initial), - getVkLayoutFromAttachLayout(config.attachments[i].layout_final)); + vk::AttachmentDescription attachmentDesc( + {}, + format, + vk::SampleCountFlagBits::e1, + getVKLoadOpFromAttachOp(config.attachments[i].load_operation), + getVkStoreOpFromAttachOp(config.attachments[i].store_operation), + vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, + getVkLayoutFromAttachLayout(config.attachments[i].layout_initial), + getVkLayoutFromAttachLayout(config.attachments[i].layout_final) + ); + attachmentDescriptions.push_back(attachmentDesc); } - vk::SubpassDescription subpassDescription({}, - vk::PipelineBindPoint::eGraphics, - 0, - {}, - static_cast<uint32_t>(colorAttachmentReferences.size()), - colorAttachmentReferences.data(), - {}, - pDepthAttachment, - 0, - {}); - - vk::RenderPassCreateInfo passInfo({}, - static_cast<uint32_t>(attachmentDescriptions.size()), - attachmentDescriptions.data(), - 1, - &subpassDescription, - 0, - {}); + + const vk::SubpassDescription subpassDescription( + {}, + vk::PipelineBindPoint::eGraphics, + 0, + {}, + static_cast<uint32_t>(colorAttachmentReferences.size()), + colorAttachmentReferences.data(), + {}, + pDepthAttachment, + 0, + {} + ); - vk::RenderPass vkObject{nullptr}; - if(m_Device.createRenderPass(&passInfo, nullptr, &vkObject) != vk::Result::eSuccess) - return PassHandle(); + const vk::RenderPassCreateInfo passInfo( + {}, + static_cast<uint32_t>(attachmentDescriptions.size()), + attachmentDescriptions.data(), + 1, + &subpassDescription, + 0, + {} + ); - m_RenderPasses.push_back(vkObject); + vk::RenderPass renderPass = m_Device.createRenderPass(passInfo); + + m_Passes.push_back({ renderPass, config }); return PassHandle(m_NextPassId++); } vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const { - return m_RenderPasses[handle.getId()]; + const uint64_t id = handle.getId(); + + if (id >= m_Passes.size()) { + return nullptr; + } + + auto& pass = m_Passes[id]; + + return pass.m_Handle; + } + + const PassConfig& PassManager::getPassConfig(const PassHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_Passes.size()) { + static PassConfig emptyConfig = PassConfig({}); + return emptyConfig; + } + + auto& pass = m_Passes[id]; + + return pass.m_Config; } + } diff --git a/src/vkcv/PassManager.hpp b/src/vkcv/PassManager.hpp index b6be2cb13d8d24bdb9759f8878917f99e31afbec..bfc20fe25ace95bd8d94832b953b6b14ab9cadee 100644 --- a/src/vkcv/PassManager.hpp +++ b/src/vkcv/PassManager.hpp @@ -10,8 +10,13 @@ namespace vkcv class PassManager { private: + struct Pass { + vk::RenderPass m_Handle; + PassConfig m_Config; + }; + vk::Device m_Device; - std::vector<vk::RenderPass> m_RenderPasses; + std::vector<Pass> m_Passes; uint64_t m_NextPassId; public: PassManager() = delete; // no default ctor @@ -28,5 +33,9 @@ namespace vkcv [[nodiscard]] vk::RenderPass getVkPass(const PassHandle &handle) const; + + [[nodiscard]] + const PassConfig& getPassConfig(const PassHandle &handle) const; + }; } diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 8b6202eb901f26c84585597e327c297902e54b03..f9c56b41fb60841f19edf294bd9adb739dd19691 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -23,8 +23,10 @@ namespace vkcv m_NextPipelineId = 0; } - PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, const vk::RenderPass &pass) + PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, PassManager& passManager) { + const vk::RenderPass &pass = passManager.getVkPass(config.m_PassHandle); + const bool existsVertexShader = config.m_ShaderProgram.existsShader(ShaderStage::VERTEX); const bool existsFragmentShader = config.m_ShaderProgram.existsShader(ShaderStage::FRAGMENT); if (!(existsVertexShader && existsFragmentShader)) @@ -170,10 +172,34 @@ namespace vkcv m_Device.destroy(fragmentModule); return PipelineHandle(); } - - // graphics pipeline create + + const vk::PipelineDepthStencilStateCreateInfo depthStencilCreateInfo( + vk::PipelineDepthStencilStateCreateFlags(), + true, + true, + vk::CompareOp::eLessOrEqual, + false, + false, + {}, + {}, + 0.0f, + 1.0f + ); + + const vk::PipelineDepthStencilStateCreateInfo* p_depthStencilCreateInfo = nullptr; + + const PassConfig& passConfig = passManager.getPassConfig(config.m_PassHandle); + + for (const auto& attachment : passConfig.attachments) { + if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + p_depthStencilCreateInfo = &depthStencilCreateInfo; + break; + } + } + + // graphics pipeline create std::vector<vk::PipelineShaderStageCreateInfo> shaderStages = { pipelineVertexShaderStageInfo, pipelineFragmentShaderStageInfo }; - vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( + const vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( {}, static_cast<uint32_t>(shaderStages.size()), shaderStages.data(), @@ -183,7 +209,7 @@ namespace vkcv &pipelineViewportStateCreateInfo, &pipelineRasterizationStateCreateInfo, &pipelineMultisampleStateCreateInfo, - nullptr, + p_depthStencilCreateInfo, &pipelineColorBlendStateCreateInfo, nullptr, vkPipelineLayout, diff --git a/src/vkcv/PipelineManager.hpp b/src/vkcv/PipelineManager.hpp index b5c0948efa13a4021f424cc576f1403a1ec26ebe..896d0df1ce10f56d291ef1accf93f9783cdd9db4 100644 --- a/src/vkcv/PipelineManager.hpp +++ b/src/vkcv/PipelineManager.hpp @@ -4,6 +4,7 @@ #include <vector> #include "vkcv/Handles.hpp" #include "vkcv/PipelineConfig.hpp" +#include "PassManager.hpp" namespace vkcv { @@ -25,7 +26,7 @@ namespace vkcv PipelineManager & operator=(const PipelineManager &other) = delete; // copy-assign op PipelineManager & operator=(PipelineManager &&other) = delete; // move-assign op - PipelineHandle createPipeline(const PipelineConfig &config, const vk::RenderPass &pass); + PipelineHandle createPipeline(const PipelineConfig &config, PassManager& passManager); [[nodiscard]] vk::Pipeline getVkPipeline(const PipelineHandle &handle) const;