From 2bc7fe05f2485382c3aaf3cfb8c1bed38109925a Mon Sep 17 00:00:00 2001 From: "matthias@arch" Date: Sun, 23 Oct 2022 01:13:51 +0200 Subject: [PATCH] Use allocator for buffers/images everything now uses VulkanAllocator for allocations (through VulkanInstance::createXXX functions) --- src/Makefile | 2 +- src/main.cpp | 4 +- src/renderer/renderer2D.cpp | 8 +- src/renderer/renderer2D.hpp | 3 +- src/renderer/renderer3D.cpp | 37 ++++----- src/renderer/renderer3D.hpp | 6 +- src/utility/texture_atlas.cpp | 54 ++++++++----- src/utility/texture_atlas.hpp | 21 ++++- src/utility/texture_manager.cpp | 29 +++++-- src/utility/texture_manager.hpp | 4 + src/utility/vulkan_util.cpp | 22 +++++ src/utility/vulkan_util.hpp | 14 ++++ src/vulkan_allocator.cpp | 116 +++++++++++++++++---------- src/vulkan_allocator.hpp | 100 ++++++++++++++++++++++- src/vulkan_instance.cpp | 138 +++++++++++++++++++------------- src/vulkan_instance.hpp | 48 +++++++---- vulkan.conf | 1 + 17 files changed, 435 insertions(+), 172 deletions(-) diff --git a/src/Makefile b/src/Makefile index 062f6cc..37a3497 100755 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,7 @@ CXXFLAGS += $(IFLAGS) # SETTINGS OBJECT_DIR = ../build EXEC = ../vulkan_test -LOG_LEVEL = LOG_LEVEL_2 +LOG_LEVEL = LOG_LEVEL_0 CXXFLAGS += -D $(LOG_LEVEL) diff --git a/src/main.cpp b/src/main.cpp index 02f00ba..e8d392d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,13 +64,13 @@ namespace gz::vk { try { uint32_t imageIndex; /* std::chrono::time_point now = std::chrono::system_clock::now(); */ - while (! glfwWindowShouldClose(vulkanInstance.window)) { + while (! glfwWindowShouldClose(vulkanInstance.getWindow())) { glfwPollEvents(); imageIndex = vulkanInstance.beginFrameDraw(); r2D.drawFrame(imageIndex); r3D.drawFrame(imageIndex); vulkanInstance.endFrameDraw(imageIndex); - auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.settings.get("framerate")); + auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.getSettings().get("framerate")); std::this_thread::sleep_for(SLEEP_TIME); /* std::chrono::time_point now2 = std::chrono::system_clock::now(); */ /* std::chrono::nanoseconds dur = std::chrono::duration_cast(now2 - now); */ diff --git a/src/renderer/renderer2D.cpp b/src/renderer/renderer2D.cpp index 687e6da..25f9aa6 100644 --- a/src/renderer/renderer2D.cpp +++ b/src/renderer/renderer2D.cpp @@ -43,6 +43,9 @@ namespace gz::vk { void Renderer2D::cleanup() { + rLog.log0("cleanup: cleaning up"); + // UPDATE DOC ON CHANGES! + /* vk.destroyCommandBuffers(commandBuffers); */ cleanupSwapChainDependantResources(); cleanup_(); @@ -68,9 +71,8 @@ namespace gz::vk { vk.destroyFramebuffers(framebuffers); for (size_t i = 0; i < images.size(); i++) { - vkDestroyImageView(vk.getDevice(), imageViews[i], nullptr); - vkDestroyImage(vk.getDevice(), images[i], nullptr); - vkFreeMemory(vk.getDevice(), imageMemory[i], nullptr); + vk.destroyImageView(imageViews[i]); + vk.destroyImage(images[i], imageMemory[i]); } vkDestroyRenderPass(vk.getDevice(), renderPass, nullptr); diff --git a/src/renderer/renderer2D.hpp b/src/renderer/renderer2D.hpp index faa9015..0fc283f 100644 --- a/src/renderer/renderer2D.hpp +++ b/src/renderer/renderer2D.hpp @@ -3,6 +3,7 @@ #include "renderer.hpp" #include "shape.hpp" +#include "vulkan_allocator.hpp" #include "vulkan_util.hpp" namespace gz::vk { @@ -67,7 +68,7 @@ namespace gz::vk { * @{ */ std::vector images; - std::vector imageMemory; + std::vector imageMemory; std::vector imageViews; /** * @brief Creates the images (on imageMemory) and imageViews with the format of the VulkanInstance::swapChain images diff --git a/src/renderer/renderer3D.cpp b/src/renderer/renderer3D.cpp index 0886959..ca3622a 100644 --- a/src/renderer/renderer3D.cpp +++ b/src/renderer/renderer3D.cpp @@ -1,6 +1,7 @@ #include "renderer3D.hpp" #include "vertex.hpp" +#include "vulkan_allocator.hpp" #include "vulkan_instance.hpp" #include "texture_manager.hpp" #include "exceptions.hpp" @@ -49,10 +50,14 @@ namespace gz::vk { } void Renderer3D::cleanup() { + rLog.log0("cleanup: cleaning up"); + // UPDATE DOC ON CHANGES! for (size_t i = 0; i < vk.getMaxFramesInFlight(); i++) { - vkDestroyBuffer(vk.getDevice(), uniformBuffers[i], nullptr); - vkFreeMemory(vk.getDevice(), uniformBuffersMemory[i], nullptr); + vk.destroyBuffer(uniformBuffers[i], uniformBuffersMemory[i]); } + vkDestroyDescriptorSetLayout(vk.getDevice(), descriptorSetLayout, NO_ALLOC); + vkDestroyDescriptorPool(vk.getDevice(), descriptorPool, NO_ALLOC); + cleanupSwapChainDependantResources(); cleanup_(); } @@ -77,9 +82,8 @@ namespace gz::vk { vk.destroyFramebuffers(framebuffers); for (size_t i = 0; i < images.size(); i++) { - vkDestroyImageView(vk.getDevice(), imageViews[i], nullptr); - vkDestroyImage(vk.getDevice(), images[i], nullptr); - vkFreeMemory(vk.getDevice(), imageMemory[i], nullptr); + vk.destroyImageView(imageViews[i]); + vk.destroyImage(images[i], imageMemory[i]); } vkDestroyRenderPass(vk.getDevice(), renderPass, nullptr); @@ -313,32 +317,28 @@ namespace gz::vk { // copy to vertexBuffer // create staging buffer VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; + MemoryInfo stagingBufferMemory; vk.createBuffer(requiredVertexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); // fill staging buffer void* data; - vkMapMemory(vk.getDevice(), stagingBufferMemory, NO_OFFSET, requiredVertexBufferSize, NO_FLAGS, &data); + vkMapMemory(vk.getDevice(), stagingBufferMemory.memory, stagingBufferMemory.offset, requiredVertexBufferSize, NO_FLAGS, &data); memcpy(data, model.vertices.data(), requiredVertexBufferSize); - vkUnmapMemory(vk.getDevice(), stagingBufferMemory); + vkUnmapMemory(vk.getDevice(), stagingBufferMemory.memory); // fill vertex buffer vk.copyBuffer(stagingBuffer, vertexBuffer, requiredVertexBufferSize); - vkDestroyBuffer(vk.getDevice(), stagingBuffer, nullptr); - vkFreeMemory(vk.getDevice(), stagingBufferMemory, nullptr); + vk.destroyBuffer(stagingBuffer, stagingBufferMemory); rLog.log0("Renderer3D: loadModel: filling index buffer"); data = nullptr; - stagingBuffer = VK_NULL_HANDLE; - stagingBufferMemory = VK_NULL_HANDLE; // copy to index buffer vk.createBuffer(requiredIndexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); // fill staging buffer - vkMapMemory(vk.getDevice(), stagingBufferMemory, NO_OFFSET, requiredIndexBufferSize, NO_FLAGS, &data); + vkMapMemory(vk.getDevice(), stagingBufferMemory.memory, stagingBufferMemory.offset, requiredIndexBufferSize, NO_FLAGS, &data); memcpy(data, model.indices.data(), requiredIndexBufferSize); - vkUnmapMemory(vk.getDevice(), stagingBufferMemory); + vkUnmapMemory(vk.getDevice(), stagingBufferMemory.memory); // fill index buffer vk.copyBuffer(stagingBuffer, indexBuffer, requiredIndexBufferSize); - vkDestroyBuffer(vk.getDevice(), stagingBuffer, nullptr); - vkFreeMemory(vk.getDevice(), stagingBufferMemory, nullptr); + vk.destroyBuffer(stagingBuffer, stagingBufferMemory); } // @@ -434,10 +434,11 @@ namespace gz::vk { /* ubo.view = glm::mat4(1); */ /* ubo.projection = glm::mat4(1); */ /* ubo.projection[1][1] *= -1; // y coordinate inverted in opengl */ + MemoryInfo& uniformBufferMI = uniformBuffersMemory[vk.getCurrentFrame()]; void* data; - vkMapMemory(vk.getDevice(), uniformBuffersMemory[vk.getCurrentFrame()], NO_OFFSET, sizeof(ubo), NO_FLAGS, &data); + vkMapMemory(vk.getDevice(), uniformBufferMI.memory, uniformBufferMI.offset, sizeof(ubo), NO_FLAGS, &data); memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(vk.getDevice(), uniformBuffersMemory[vk.getCurrentFrame()]); + vkUnmapMemory(vk.getDevice(), uniformBufferMI.memory); } diff --git a/src/renderer/renderer3D.hpp b/src/renderer/renderer3D.hpp index fb149ee..3f66c68 100644 --- a/src/renderer/renderer3D.hpp +++ b/src/renderer/renderer3D.hpp @@ -2,6 +2,7 @@ #include "renderer.hpp" +#include "vulkan_allocator.hpp" #include "vulkan_util.hpp" namespace gz::vk { @@ -34,7 +35,7 @@ namespace gz::vk { private: void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame); std::vector uniformBuffers; - std::vector uniformBuffersMemory; + std::vector uniformBuffersMemory; void createUniformBuffers(); void updateUniformBuffer(); @@ -45,7 +46,7 @@ namespace gz::vk { */ /// @{ std::vector images; - std::vector imageMemory; + std::vector imageMemory; std::vector imageViews; /** * @brief Creates the images (on imageMemory) and imageViews with the format of the VulkanInstance::swapChain images @@ -137,6 +138,7 @@ namespace gz::vk { * @details: * Does: * -# destroy uniform buffers + * -# destroy descriptor set layout and pool * -# call cleanupSwapChainDependantResources() * -# call Renderer::cleanup_() */ diff --git a/src/utility/texture_atlas.cpp b/src/utility/texture_atlas.cpp index fe16618..d1b7946 100644 --- a/src/utility/texture_atlas.cpp +++ b/src/utility/texture_atlas.cpp @@ -20,17 +20,28 @@ std::string TextureImageArea::toString() const { return "<(" + gz::toString(x) + "," + gz::toString(y) + "), (" + gz::toString(width) + "x" + gz::toString(height) + ")>"; } - // // INIT & CLEANUP // TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY) - : vk(instance), slotWidth(slotWidth), slotHeight(slotHeight), slotCountX(slotCountX), slotCountY(slotCountY) + : vk(instance), + slotWidth(slotWidth), slotHeight(slotHeight), + slotCountX(slotCountX), slotCountY(slotCountY) { +#ifdef LOG_LEVEL_0 + LogCreateInfo logCI{}; + logCI.logfile = "textureAtlas.log"; + logCI.storeLog = false; + logCI.prefix = "TextureAtlas (" + gz::toString(slotCountX) + "x" + gz::toString(slotCountY) + ")X(" + gz::toString(slotWidth) + "x" + gz::toString(slotHeight) + ")"; + logCI.prefixColor = Color::GREEN; + logCI.timeColor = VULKAN_MESSAGE_TIME_COLOR; + tLog = Log(std::move(logCI)); +#endif + // whole image freeAreas.insert({ 0, 0, slotCountX, slotCountY}); createImageResources(); - vk.registerCleanupCallback(std::bind(&TextureAtlas::cleanup, this)); + /* vk.registerCleanupCallback(std::bind(&TextureAtlas::cleanup, this)); */ VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan(std::string("TextureAtlas-") + gz::toString(slotWidth) + "x" + gz::toString(slotHeight), { &textureImage, &textureImageMemory, &textureImageView, &textureSampler }, {})); @@ -38,11 +49,12 @@ TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_ void TextureAtlas::cleanup() { - VulkanInstance::vLog("TextureAtlas::cleanup, textureSampler", reinterpret_cast(textureSampler), "textureImageView", reinterpret_cast(textureImageView)); - vkDestroySampler(vk.getDevice(), textureSampler, nullptr); - vkDestroyImageView(vk.getDevice(), textureImageView, nullptr); - vkDestroyImage(vk.getDevice(), textureImage, nullptr); - vkFreeMemory(vk.getDevice(), textureImageMemory, nullptr); +#ifdef LOG_LEVEL_0 + tLog.log0("cleanup: cleaning up"); +#endif + vk.destroyTextureSampler(textureSampler); + vk.destroyImageView(textureImageView); + vk.destroyImage(textureImage, textureImageMemory); } @@ -51,7 +63,7 @@ void TextureAtlas::createImageResources() { VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); - VkCommandBuffer cmdBuffer = vk.beginSingleTimeCommands(vk.commandPoolGraphics); + VkCommandBuffer cmdBuffer = vk.beginSingleTimeCommands(POOL_GRAPHICS); vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cmdBuffer); VkImageSubresourceRange subresourceRange{}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -61,7 +73,7 @@ void TextureAtlas::createImageResources() { subresourceRange.layerCount = 1; vkCmdClearColorImage(cmdBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &missingTextureColor, 1, &subresourceRange); vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, &cmdBuffer); - vk.endSingleTimeCommands(cmdBuffer, vk.commandPoolGraphics, vk.graphicsQ); + vk.endSingleTimeCommands(cmdBuffer, POOL_GRAPHICS); vk.createImageView(VK_FORMAT_R8G8B8A8_SRGB, textureImage, textureImageView, VK_IMAGE_ASPECT_COLOR_BIT); vk.createTextureSampler(textureSampler); @@ -135,7 +147,9 @@ std::pair TextureAtlas::addTexture(uint8_t* pixels, uint16 freeAreas.insert(std::move(node)); } mergeFreeAreas(); - VulkanInstance::vLog("TextureAtlas::addTexture: Adding texture at position x,y=(", x, y, "), with slotCountX,Y=(", slotsX, slotsY, "), with dimensions w,h=(", width, height, ")"); +#ifdef LOG_LEVEL_0 + tLog.log0("addTexture: Adding texture at position x,y=(", x, y, "), with slotCountX,Y=(", slotsX, slotsY, "), with dimensions w,h=(", width, height, ")"); +#endif blitTextureOnImage(pixels, x, y, width, height); // topleft normalized: slot / slotCount // bottomright normalized: (slot + (textureSize/slotCountize)) / slotCount @@ -149,14 +163,14 @@ void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t constexpr size_t BYTES_PER_PIXEL = 4; VkDeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL; VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; + MemoryInfo stagingBufferMemory; vk.createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory); void* data; - vkMapMemory(vk.getDevice(), stagingBufferMemory, NO_OFFSET, imageSize, NO_FLAGS, &data); + vkMapMemory(vk.getDevice(), stagingBufferMemory.memory, stagingBufferMemory.offset, imageSize, NO_FLAGS, &data); memcpy(data, pixels, static_cast(imageSize)); - vkUnmapMemory(vk.getDevice(), stagingBufferMemory); + vkUnmapMemory(vk.getDevice(), stagingBufferMemory.memory); vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); vk.copyBufferToImage(stagingBuffer, textureImage, @@ -164,14 +178,15 @@ void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t static_cast(textureWidth), static_cast(textureHeight)); // dimensions vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - vkDestroyBuffer(vk.getDevice(), stagingBuffer, nullptr); - vkFreeMemory(vk.getDevice(), stagingBufferMemory, nullptr); + vk.destroyBuffer(stagingBuffer, stagingBufferMemory); } void TextureAtlas::mergeFreeAreas() { begin: - VulkanInstance::vLog("TextureAtlas::mergeFreeAreas before merge:", freeAreas); +#ifdef LOG_LEVEL_0 + tLog.log0("mergeFreeAreas before merge:", freeAreas); +#endif // surely not the most efficient way, but it should only be run at load time and thus not be too problematic // iterate over the areas for each area for (auto it = freeAreas.begin(); it != freeAreas.end(); it++) { @@ -195,8 +210,9 @@ begin: } } } - VulkanInstance::vLog("TextureAtlas::mergeFreeAreas after merge:", freeAreas); - +#ifdef LOG_LEVEL_0 + tLog.log0("mergeFreeAreas after merge:", freeAreas); +#endif } std::string TextureAtlas::toString() const { diff --git a/src/utility/texture_atlas.hpp b/src/utility/texture_atlas.hpp index 2688fc0..e9063c5 100644 --- a/src/utility/texture_atlas.hpp +++ b/src/utility/texture_atlas.hpp @@ -1,11 +1,18 @@ #pragma once +#include "vulkan_allocator.hpp" +#include #include #include + #include #include #include +// only log when trace +#ifdef LOG_LEVEL_0 +#include +#endif namespace gz::vk { /** @@ -34,6 +41,7 @@ namespace gz::vk { /// Defined in vulkan_instance.hpp class VulkanInstance; + class TextureAtlas { public: /** @@ -58,12 +66,14 @@ namespace gz::vk { std::string toString() const; - private: - VulkanInstance& vk; /** * @brief Destroy all vulkan objects owned by this object + * @note This function has to be called manually (by TextureManager). This class does not register a cleanupCallback with the instance! */ void cleanup(); + + private: + VulkanInstance& vk; void blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight); /** * @brief Create textureImage, textureImageView and textureSampler @@ -72,7 +82,7 @@ namespace gz::vk { */ void createImageResources(); VkImage textureImage; - VkDeviceMemory textureImageMemory; + MemoryInfo textureImageMemory; VkImageView textureImageView; VkSampler textureSampler; @@ -87,5 +97,10 @@ namespace gz::vk { uint16_t slotHeight; uint16_t slotCountX; uint16_t slotCountY; + + /// Use log only with trace +#ifdef LOG_LEVEL_0 + Log tLog; +#endif }; } // namespace gz::vk diff --git a/src/utility/texture_manager.cpp b/src/utility/texture_manager.cpp index 728b4e9..b3fe585 100644 --- a/src/utility/texture_manager.cpp +++ b/src/utility/texture_manager.cpp @@ -9,6 +9,14 @@ namespace gz::vk { TextureManager::TextureManager(VulkanInstance& instance) : vk(instance) { + LogCreateInfo logCI{}; + logCI.logfile = "textureManager.log"; + logCI.storeLog = false; + logCI.prefix = "Texture"; + logCI.prefixColor = Color::GREEN; + logCI.timeColor = VULKAN_MESSAGE_TIME_COLOR; + tLog = Log(std::move(logCI)); + vk.registerCleanupCallback(std::bind(&TextureManager::cleanup, this)); atlases.insert({ 0, TextureAtlas(vk, 24, 24, 24, 24) }); createDescriptorSetLayout(); @@ -21,9 +29,14 @@ namespace gz::vk { void TextureManager::cleanup() { + tLog.log0("cleanup: cleaning up"); /* vkFreeDescriptorSets(vk.getDevice(), descriptorPool, 1, &descriptorSet); */ vkDestroyDescriptorSetLayout(vk.getDevice(), descriptorSetLayout, NO_ALLOC); vkDestroyDescriptorPool(vk.getDevice(), descriptorPool, NO_ALLOC); + for (auto& [i, atlas] : atlases) { + atlas.cleanup(); + + } } @@ -32,7 +45,7 @@ namespace gz::vk { loadTextureFromFile(textureName); } TextureInfo& texI = textureInfos[textureName]; - VulkanInstance::vLog("getTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight); + tLog.log0("getTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight); /* texCoords.x = texI.texCoordTopLeft.x + texCoords.x * (texI.texCoordBottomRight.x - texI.texCoordTopLeft.x); */ /* texCoords.y = texI.texCoordTopLeft.y + texCoords.y * (texI.texCoordBottomRight.y - texI.texCoordTopLeft.y); */ texCoords = texI.texCoordTopLeft + texCoords * (texI.texCoordBottomRight - texI.texCoordTopLeft); @@ -49,10 +62,10 @@ namespace gz::vk { texI.atlas = 0; auto texCoords = atlases.at(0).addTexture(pixels, textureWidth, textureHeight); stbi_image_free(pixels); - VulkanInstance::vLog("TextureManager::loadTextureFromFile: TexCoords of new image:", texCoords.first, texCoords.second); + tLog.log0("TextureManager::loadTextureFromFile: TexCoords of new image:", texCoords.first, texCoords.second); texI.texCoordTopLeft = std::move(texCoords.first); texI.texCoordBottomRight = std::move(texCoords.second); - VulkanInstance::vLog("TextureManager::loadTextureFromFile: After loading:", atlases.at(0)); + tLog.log0("TextureManager::loadTextureFromFile: After loading:", atlases.at(0)); } @@ -84,9 +97,9 @@ void TextureManager::createDescriptorSetLayout() { void TextureManager::createDescriptorPool() { std::array poolSizes; /* poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; */ - /* poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); */ + /* poolSizes[0].descriptorCount = static_cast(vk.getMaxFramesInFlight()); */ poolSizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + poolSizes[0].descriptorCount = static_cast(vk.getMaxFramesInFlight()); VkDescriptorPoolCreateInfo poolCI{}; poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; @@ -102,16 +115,16 @@ void TextureManager::createDescriptorPool() { void TextureManager::createDescriptorSet() { - /* std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); */ + /* std::vector layouts(vk.getMaxFramesInFlight(), descriptorSetLayout); */ VkDescriptorSetAllocateInfo setAI{}; setAI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; setAI.descriptorPool = descriptorPool; - /* setAI.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); */ + /* setAI.descriptorSetCount = static_cast(vk.getMaxFramesInFlight()); */ /* setAI.pSetLayouts = layouts.data(); */ setAI.descriptorSetCount = 1; setAI.pSetLayouts = &descriptorSetLayout; - /* descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); */ + /* descriptorSets.resize(vk.getMaxFramesInFlight()); */ /* VkResult result = vkAllocateDescriptorSets(vk.getDevice(), &setAI, descriptorSets.data()); */ VkResult result = vkAllocateDescriptorSets(vk.getDevice(), &setAI, &descriptorSet); if (result != VK_SUCCESS) { diff --git a/src/utility/texture_manager.hpp b/src/utility/texture_manager.hpp index 826d82d..417f371 100644 --- a/src/utility/texture_manager.hpp +++ b/src/utility/texture_manager.hpp @@ -3,8 +3,11 @@ #include "texture_atlas.hpp" #include "stb_image.h" + #include +#include #include + #include namespace gz::vk { @@ -38,6 +41,7 @@ namespace gz::vk { std::unordered_map atlases; util::unordered_string_map textureInfos; VulkanInstance& vk; + Log tLog; /** * @name Create desciptors * @details These functions create a desciptor with bindings for diff --git a/src/utility/vulkan_util.cpp b/src/utility/vulkan_util.cpp index ebbfd97..11922f3 100644 --- a/src/utility/vulkan_util.cpp +++ b/src/utility/vulkan_util.cpp @@ -95,6 +95,28 @@ namespace gz::vk { } + void getBufferMemoryRequirements(const VkDevice& device, const VkBuffer& buffer, VkBufferMemoryRequirementsInfo2& bufMemReq, VkMemoryRequirements2& memReq) { + bufMemReq.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2; + bufMemReq.pNext = nullptr; + bufMemReq.buffer = buffer; + + memReq.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; + memReq.pNext = nullptr; + vkGetBufferMemoryRequirements2(device, &bufMemReq, &memReq); + } + + + void getImageMemoryRequirements(const VkDevice& device, const VkImage& image, VkImageMemoryRequirementsInfo2& imMemReq, VkMemoryRequirements2& memReq) { + imMemReq.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2; + imMemReq.pNext = nullptr; + imMemReq.image = image; + + memReq.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; + memReq.pNext = nullptr; + vkGetImageMemoryRequirements2(device, &imMemReq, &memReq); + } + + // // // diff --git a/src/utility/vulkan_util.hpp b/src/utility/vulkan_util.hpp index 4312c4f..0e53fa7 100644 --- a/src/utility/vulkan_util.hpp +++ b/src/utility/vulkan_util.hpp @@ -127,6 +127,20 @@ namespace gz::vk { * @brief Get a VkDependencyInfo struct for a single image memory barrier */ VkDependencyInfo getDepInfo(const VkImageMemoryBarrier2& barrier); + + /** + * @brief Fill bufMeqReq and memReq + * @details + * Fill the structs and call vkGetBufferMemoryRequirements2() + */ + void getBufferMemoryRequirements(const VkDevice& device, const VkBuffer& buffer, VkBufferMemoryRequirementsInfo2& bufMemReq, VkMemoryRequirements2& memReq); + + /** + * @brief Fill imMemReq and memReq + * @details + * Fill the structs and call vkGetImageMemoryRequirements2() + */ + void getImageMemoryRequirements(const VkDevice& device, const VkImage& image, VkImageMemoryRequirementsInfo2& imMemReq, VkMemoryRequirements2& memReq); /// @} /** diff --git a/src/vulkan_allocator.cpp b/src/vulkan_allocator.cpp index 1dc2d36..68f9fa6 100644 --- a/src/vulkan_allocator.cpp +++ b/src/vulkan_allocator.cpp @@ -4,29 +4,22 @@ #include "vulkan_instance.hpp" #include +#include #include namespace gz::vk { - struct MemoryBlock { - size_t size; - size_t offset; - bool free; + std::string MemoryBlock::toString() const { + return std::string(""); + } + + + DeviceMemory::DeviceMemory(VkDeviceSize size_) { + size = size_; + memory = VK_NULL_HANDLE; + blocks.emplace_back(MemoryBlock{size, 0, true}); }; - struct DeviceMemory { - DeviceMemory() = delete; - DeviceMemory(VkDeviceSize size_) { - size = size_; - memory = VK_NULL_HANDLE; - blocks.emplace_back(MemoryBlock{size, 0, true}); - }; - VkDeviceSize size; - VkDeviceMemory memory; - std::list blocks; - }; - - VulkanAllocator::VulkanAllocator(VulkanInstance& instance) : vk(instance) { @@ -40,30 +33,46 @@ namespace gz::vk { } - void VulkanAllocator::allocate(const VkMemoryAllocateInfo& allocI, MemoryInfo& memoryInfo) { + void VulkanAllocator::allocate(const VkMemoryAllocateInfo& allocI, const VkMemoryRequirements2& memReq, MemoryInfo& memoryInfo) { + aLog.log0("allocate: Requesting memory with ( size", toHexString(allocI.allocationSize), + "), ( memoryTypeIndex", allocI.memoryTypeIndex, + "), ( alignment", toHexString(memReq.memoryRequirements.alignment), ")"); // go through allocated memories with matching memoryType - for (auto& deviceMemory : memory[allocI.memoryTypeIndex]) { + for (auto deviceMemory = memory[allocI.memoryTypeIndex].begin(); deviceMemory != memory[allocI.memoryTypeIndex].end(); deviceMemory++) { /* auto bestBlockIt = deviceMemory.blocks.end(); */ // go through blocks in memory - for (auto block = deviceMemory.blocks.begin(); block != deviceMemory.blocks.end(); block++) { + for (auto block = deviceMemory->blocks.begin(); block != deviceMemory->blocks.end(); block++) { if (!block->free) { continue; } if (block->size >= allocI.allocationSize) { - aLog.log0("allocate: memoryTypeIndex:", allocI.memoryTypeIndex, - "size:", allocI.allocationSize, "block size:", block->size, "block offset:", block->offset); + // check if alignment is ok + if (block->offset % memReq.memoryRequirements.alignment != 0) { + // move non-aligned space to previous block + // new offset === offset + (align - offset % align) + size_t moveToPreviousBlock = memReq.memoryRequirements.alignment - block->offset % memReq.memoryRequirements.alignment; + // check if still large enough + if (block->size - moveToPreviousBlock < allocI.allocationSize) { continue; } + block--; + block->size += moveToPreviousBlock; + block++; + block->offset += moveToPreviousBlock; + block->size -= moveToPreviousBlock; + } // if the block is larger than the needed size, split the block if (block->size > allocI.allocationSize) { // emplace free part of block after used part of block auto newBlock = ++block; block--; - deviceMemory.blocks.emplace(newBlock, + deviceMemory->blocks.emplace(newBlock, MemoryBlock{ block->size - allocI.allocationSize, block->offset + allocI.allocationSize, true }); - block--; + /* block--; */ block->size = allocI.allocationSize; } block->free = false; - memoryInfo.memory = deviceMemory.memory; + memoryInfo.memory = deviceMemory->memory; memoryInfo.offset = block->offset; memoryInfo.memoryTypeIndex = allocI.memoryTypeIndex; + + aLog.log0("allocate: (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(deviceMemory - memory[memoryInfo.memoryTypeIndex].begin()) + ") Blocks:", deviceMemory->blocks); return; } } @@ -75,7 +84,7 @@ namespace gz::vk { allocI_.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocI_.memoryTypeIndex = allocI.memoryTypeIndex; allocI_.allocationSize = memory[allocI.memoryTypeIndex].back().size; - aLog.log0("allocate: Allocating new memory of size:", allocI_.allocationSize, " and memoryTypeIndex:", allocI.memoryTypeIndex); + aLog.log0("allocate: Allocating new memory of size:", gz::toHexString(allocI_.allocationSize, 0), "(memory", memoryInfo.memoryTypeIndex, "-", gz::toString(memory[memoryInfo.memoryTypeIndex].size()) + ")"); VkResult result = vkAllocateMemory(vk.getDevice(), &allocI_, NO_ALLOC, &memory[allocI.memoryTypeIndex].back().memory); if (result != VK_SUCCESS) { throw getVkException(result, "Failed to allocate memory", "VulkanAllocator::allocate"); @@ -87,9 +96,11 @@ namespace gz::vk { deviceMemory.blocks.front().size -= allocI.allocationSize; deviceMemory.blocks.emplace_front(MemoryBlock{ allocI.allocationSize, 0, false }); + // alignment always satisfied with vkAllocateMemory() and offset 0 memoryInfo.memory = deviceMemory.memory; memoryInfo.offset = 0; memoryInfo.memoryTypeIndex = allocI.memoryTypeIndex; + aLog.log0("allocate: (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(memory[memoryInfo.memoryTypeIndex].size()) + ") Blocks:", deviceMemory.blocks); } @@ -100,29 +111,35 @@ namespace gz::vk { // go through blocks in memory for (auto block = deviceMemory->blocks.begin(); block != deviceMemory->blocks.end(); block++) { if (block->offset != memoryInfo.offset) { continue; } - aLog.log0("free: Freeing block at offset", memoryInfo.offset); + aLog.log0("free: (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(deviceMemory - memory[memoryInfo.memoryTypeIndex].begin()) + ") Freeing block at offset:", gz::toHexString(memoryInfo.offset, 0)); block->free = true; - // merge with previous block, if free - auto otherBlock = block; - otherBlock--; - if (otherBlock->free) { - otherBlock->size += block->size; - deviceMemory->blocks.erase(block); - block = otherBlock; + if (block != deviceMemory->blocks.begin()) { + // merge with previous block, if free + auto otherBlock = block; + otherBlock--; + if (otherBlock->free) { + otherBlock->size += block->size; + deviceMemory->blocks.erase(block); + block = otherBlock; + } } - // merge with next block, if free - otherBlock = block; - otherBlock++; - if (otherBlock->free) { - block->size += otherBlock->size; - deviceMemory->blocks.erase(otherBlock); + if (block != --(deviceMemory->blocks.end())) { + // merge with next block, if free + auto otherBlock = block; + otherBlock++; + if (otherBlock->free) { + block->size += otherBlock->size; + deviceMemory->blocks.erase(otherBlock); + } } + memoryInfo.memory = VK_NULL_HANDLE; memoryInfo.offset = 0; + aLog.log0("free: (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(deviceMemory - memory[memoryInfo.memoryTypeIndex].begin()) + ") Blocks:", deviceMemory->blocks); // if now there is only one free block, everything is free -> deallocate memory if (deviceMemory->blocks.size() == 1) { - aLog.log0("free: Deallocting memory of size:", deviceMemory->size, "and memoryTypeIndex:", memoryInfo.memoryTypeIndex); + aLog.log0("free: Deallocating (memory", memoryInfo.memoryTypeIndex, "-", gz::toString(deviceMemory - memory[memoryInfo.memoryTypeIndex].begin()) + ") of size:", gz::toHexString(deviceMemory->size, 0)); vkFreeMemory(vk.getDevice(), deviceMemory->memory, NO_ALLOC); memory[memoryInfo.memoryTypeIndex].erase(deviceMemory); } @@ -133,4 +150,21 @@ namespace gz::vk { } + void VulkanAllocator::cleanup() { + aLog.log0("cleanup: cleaning up"); + for (auto memType = memory.begin(); memType != memory.end(); memType++) { + for (auto deviceMemory = memType->second.begin(); deviceMemory != memType->second.end(); deviceMemory++) { + // check if all blocks are free + for (auto block = deviceMemory->blocks.begin(); block != deviceMemory->blocks.end(); block++) { + if (block->free) { continue; } + aLog.warning("cleanup: (memory", memType->first, "-", gz::toString(deviceMemory - memType->second.begin()) + ") Block not freed: ", *block); + } + aLog.log0("free: Deallocating (memory", memType->first, "-", gz::toString(deviceMemory - memType->second.begin()) + ") of size:", gz::toHexString(deviceMemory->size, 0)); + vkFreeMemory(vk.getDevice(), deviceMemory->memory, NO_ALLOC); + } + } + memory.clear(); + } + + } // namespace gz::vk diff --git a/src/vulkan_allocator.hpp b/src/vulkan_allocator.hpp index 956b81d..72947b9 100644 --- a/src/vulkan_allocator.hpp +++ b/src/vulkan_allocator.hpp @@ -13,20 +13,99 @@ /* #include */ namespace gz::vk { + /** + * @brief Contains information about a subsection (block) of a VkDeviceMemory + */ struct MemoryInfo { + /** + * @brief Handle of the memory + */ VkDeviceMemory memory = VK_NULL_HANDLE; + /** + * @brief Offset into memory + */ VkDeviceSize offset = 0; + /** + * @brief The memoryTypeIndex memory was allocated from. Needed for VulkanAllocator::free() + */ uint32_t memoryTypeIndex = 0; }; + + /** + * @brief Information on a single block of memory + */ + struct MemoryBlock { + size_t size; + size_t offset; + bool free; + std::string toString() const; + }; + + /** + * @brief Manage a single VkDeviceMemory chunk + */ + struct DeviceMemory { + DeviceMemory() = delete; + DeviceMemory(VkDeviceSize size_); + + VkDeviceSize size; + VkDeviceMemory memory; + std::list blocks; + }; + + // if no memory is available, allocate a chunk of multiplier * requested size constexpr VkDeviceSize TODO_ALLOCATION_SIZE_MULTIPLIIER = 10; // defined in vulkan_instance.hpp class VulkanInstance; - // defined in vulkan_allocator.cpp - struct DeviceMemory; + /** + * @brief Allocator for device local memory + * @details + * Allocates larger chunks of VkDeviceMemory from which blocks can be @ref allocate() "allocated". + * + * This class is for device local memory only, not for host memory. + * You can not use it for vulkan memory allocation callbacks (pAllocator). + * + * @subsection usage How to use + * The usage is explained with the example of creating and destroying a single buffer. + * @code + * // Create buffer handle + * VkBuffer buffer; + * // Create empty memory info struct + * MemoryInfo bufferMI; + * + * VkBufferCreateInfo bufferCI{} + * ... + * vkCreateBuffer(...); + * + * // get memory requirements + * VkMemoryRequirements2 memReq; + * VkBufferMemoryRequirementsInfo2 bufMemReq; + * // from vulkan_util.hpp, same exists for imageMemoryRequirements + * getBufferMemoryRequirements(device, buffer, bufMemReq, memReq); + * + * // set allocation info + * VkMemoryAllocateInfo memAI{}; + * ... + * memAI.memoryTypeIndex = VulkanInstance::findMemoryType(memReq.memoryRequirements.memoryTypeBits, wantedProperties); + * + * // allocate memory for the buffer + * VulkanAllocator::allocate(memAI, memReq, bufferMI); + * + * // bind the buffer to the memory + * vkBindBufferMemory(device, buffer, bufferMI.memory, bufferMI.offset); + * + * ... + * + * vkDestroyBuffer(device, buffer); + * VulkanAllocator::free(bufferMemoryInfo); + * @endcode + * + * You will of course have to use a VulkanAllocator and VulkanInstance object, since the used methods are not static. + */ class VulkanAllocator { public: VulkanAllocator(VulkanInstance& instance); @@ -39,8 +118,10 @@ namespace gz::vk { * You can then bind something of size allocI.allocationSize to memoryInfo.memory at offset memoryInfo.offset. * @throws VkException when a call to vkAllocateMemory is needed and fails * @todo Determine the size of new allocations + * @todo alignment, maybe use VkMemoryRequirements instead of VkMemoryAllocateInfo? + * @todo maybe increase the block size of the allocated block so that the following blocks is already 16-aligned? */ - void allocate(const VkMemoryAllocateInfo& allocI, MemoryInfo& memoryInfo); + void allocate(const VkMemoryAllocateInfo& allocI, const VkMemoryRequirements2& memReq, MemoryInfo& memoryInfo); /** * @brief Free a block allocated with allocate() * @@ -48,10 +129,21 @@ namespace gz::vk { * @throws VkUserError if memoryInfo was not allocated from this allocator */ void free(MemoryInfo& memoryInfo); + + /** + * @brief Deallocate all memory + * @details + * Prints warnings if blocks have not been freed. + * + * @note This function has to be called manually (by VulkanInstance). This class does not register a cleanupCallback with the instance! + * + */ + void cleanup(); private: - /// allocated memory for memoryIndexType + /// Allocated memory for memoryIndexType std::map> memory; Log aLog; + /// Needed for access to logical device VulkanInstance& vk; }; // class VulkanAllocator diff --git a/src/vulkan_instance.cpp b/src/vulkan_instance.cpp index d3c55e2..db8b3cd 100644 --- a/src/vulkan_instance.cpp +++ b/src/vulkan_instance.cpp @@ -5,6 +5,7 @@ #include "vk_enum_string.h" #include "vulkan_allocator.hpp" +#include "vulkan_util.hpp" #include #include #include @@ -248,6 +249,8 @@ using std::uint32_t; vkDestroyCommandPool(device, commandPoolGraphics, nullptr); vkDestroyCommandPool(device, commandPoolTransfer, nullptr); + allocator.cleanup(); + vkDestroyDevice(device, nullptr); vkDestroySurfaceKHR(instance, surface, nullptr); cleanupDebugMessenger(); @@ -465,6 +468,43 @@ using std::uint32_t; } + void VulkanInstance::loadModel(VerticesAndIndices& model) { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + std::string warnings, errors; + if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warnings, &errors, MODEL_PATH.c_str())) { + vLog.warning("Warning from tinyobj::LoadObj: ", warnings); + throw gz::Exception("Error loading obj: " + errors, "loadModel"); + } + if (!warnings.empty()) { + vLog.warning("Warning from tinyobj::LoadObj: ", warnings); + } + std::unordered_map uniqueVertices; + for (const auto& shape : shapes) { + for (const auto& index : shape.mesh.indices) { + Vertex3D vertex; + vertex.pos = { + attrib.vertices[3 * index.vertex_index + 0], + attrib.vertices[3 * index.vertex_index + 1], + attrib.vertices[3 * index.vertex_index + 2] + }; + vertex.texCoord = { + attrib.vertices[2 * index.texcoord_index + 0], + // obj: y = 0 means bottom, vulkan: y = 0 means top + 1.0f - attrib.vertices[2 * index.texcoord_index + 1], + }; + vertex.color = { 1.0f, 1.0f, 1.0f }; + if (!uniqueVertices.contains(vertex)) { + uniqueVertices.insert({ vertex, model.vertices.size() }); + model.vertices.push_back(vertex); + } + /* model.vertices.push_back(vertex); */ + model.indices.push_back(uniqueVertices[vertex]); + } + } + } + // TODO // DEPTH /* VkFormat VulkanInstance::findDepthFormat() { */ @@ -702,7 +742,7 @@ using std::uint32_t; // BUFFER - void VulkanInstance::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, MemoryInfo& memI, VkSharingMode sharingMode, std::vector* qFamiliesWithAccess) { + void VulkanInstance::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, MemoryInfo& bufferMI, VkSharingMode sharingMode, std::vector* qFamiliesWithAccess) { VkBufferCreateInfo bufferCI{}; bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCI.size = size; @@ -722,18 +762,23 @@ using std::uint32_t; throw getVkException(result, "Failed to create buffer", "createBuffer"); } - VkMemoryRequirements memoryRequirements{}; - vkGetBufferMemoryRequirements(device, buffer, &memoryRequirements); + VkMemoryRequirements2 memReq; + VkBufferMemoryRequirementsInfo2 bufMemReq; + getBufferMemoryRequirements(device, buffer, bufMemReq, memReq); VkMemoryAllocateInfo memoryAI{}; memoryAI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memoryAI.allocationSize = memoryRequirements.size; + memoryAI.allocationSize = memReq.memoryRequirements.size; VkMemoryPropertyFlags wantedProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - memoryAI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, wantedProperties); + memoryAI.memoryTypeIndex = findMemoryType(memReq.memoryRequirements.memoryTypeBits, wantedProperties); - allocator.allocate(memoryAI, memI); + allocator.allocate(memoryAI, memReq, bufferMI); - vkBindBufferMemory(device, buffer, memI.memory, memI.offset); + result = vkBindBufferMemory(device, buffer, bufferMI.memory, bufferMI.offset); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to bind buffer to its memory", "createBuffer"); + } + vLog.log0("createBuffer: created and bound buffer of size", memoryAI.allocationSize); } @@ -843,8 +888,15 @@ using std::uint32_t; } } + + void VulkanInstance::destroyTextureSampler(VkSampler& sampler) { + vkDestroySampler(device, sampler, NO_ALLOC); + sampler = VK_NULL_HANDLE; + } + + // PUBLIC INTERFACE: IMAGE UTILITY - void VulkanInstance::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory) { + void VulkanInstance::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, MemoryInfo& imageMI) { VkImageCreateInfo imageCI{}; imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.imageType = VK_IMAGE_TYPE_2D; @@ -866,25 +918,32 @@ using std::uint32_t; throw getVkException(result, "Failed to create image", "createImage"); } - VkMemoryRequirements memoryRequirements; - vkGetImageMemoryRequirements(device, image, &memoryRequirements); + VkMemoryRequirements2 memReq; + VkImageMemoryRequirementsInfo2 imMemReq; + getImageMemoryRequirements(device, image, imMemReq, memReq); VkMemoryAllocateInfo memoryAI{}; memoryAI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memoryAI.allocationSize = memoryRequirements.size; - memoryAI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, memoryProperties); + memoryAI.allocationSize = memReq.memoryRequirements.size; + memoryAI.memoryTypeIndex = findMemoryType(memReq.memoryRequirements.memoryTypeBits, memoryProperties); - MemoryInfo memI; - allocator.allocate(memoryAI, memI); - /* result = vkAllocateMemory(device, &memoryAI, NO_ALLOC, &imageMemory); */ + allocator.allocate(memoryAI, memReq, imageMI); - result = vkBindImageMemory(device, image, memI.memory, memI.offset); + result = vkBindImageMemory(device, image, imageMI.memory, imageMI.offset); if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create image", "createImage"); + throw getVkException(result, "Failed to bind image to its memory", "createImage"); } + vLog.log0("createImage: created and bound image of size", memoryAI.allocationSize); } + void VulkanInstance::destroyImage(VkImage& image, MemoryInfo& imageMemory) { + vkDestroyImage(device, image, NO_ALLOC); + allocator.free(imageMemory); + image = VK_NULL_HANDLE; + } + + // IMAGE VIEW void VulkanInstance::createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags) { VkImageViewCreateInfo imageViewCI{}; @@ -908,6 +967,12 @@ using std::uint32_t; } + void VulkanInstance::destroyImageView(VkImageView& imageView) { + vkDestroyImageView(device, imageView, NO_ALLOC); + imageView = VK_NULL_HANDLE; + } + + void VulkanInstance::copyBufferToImage(VkBuffer buffer, VkImage image, int32_t offsetX, int32_t offsetY, uint32_t width, uint32_t height) { VkCommandBuffer cmdBuffer = beginSingleTimeCommands(POOL_TRANSFER); VkBufferImageCopy region{}; @@ -1724,45 +1789,6 @@ using std::uint32_t; } -// MODEL - void VulkanInstance::loadModel(VerticesAndIndices& model) { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - std::string warnings, errors; - if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warnings, &errors, MODEL_PATH.c_str())) { - vLog.warning("Warning from tinyobj::LoadObj: ", warnings); - throw gz::Exception("Error loading obj: " + errors, "loadModel"); - } - if (!warnings.empty()) { - vLog.warning("Warning from tinyobj::LoadObj: ", warnings); - } - std::unordered_map uniqueVertices; - for (const auto& shape : shapes) { - for (const auto& index : shape.mesh.indices) { - Vertex3D vertex; - vertex.pos = { - attrib.vertices[3 * index.vertex_index + 0], - attrib.vertices[3 * index.vertex_index + 1], - attrib.vertices[3 * index.vertex_index + 2] - }; - vertex.texCoord = { - attrib.vertices[2 * index.texcoord_index + 0], - // obj: y = 0 means bottom, vulkan: y = 0 means top - 1.0f - attrib.vertices[2 * index.texcoord_index + 1], - }; - vertex.color = { 1.0f, 1.0f, 1.0f }; - if (!uniqueVertices.contains(vertex)) { - uniqueVertices.insert({ vertex, model.vertices.size() }); - model.vertices.push_back(vertex); - } - /* model.vertices.push_back(vertex); */ - model.indices.push_back(uniqueVertices[vertex]); - } - } - } - - // DEBUG void VulkanInstance::setupDebugMessenger() { // get the address of the vkCreateDebugUtilsMessengerEXT function diff --git a/src/vulkan_instance.hpp b/src/vulkan_instance.hpp index 679121b..4533fcf 100644 --- a/src/vulkan_instance.hpp +++ b/src/vulkan_instance.hpp @@ -97,8 +97,25 @@ namespace gz::vk { */ uint32_t beginFrameDraw(); void endFrameDraw(uint32_t imageIndex); + /** + * @brief Cleanup the instance + * @details + * -# wait for the device to idle + * -# call @ref registerCleanupCallback() "cleanup callbacks" in reverse order to the registration + * -# destroy command buffers + * -# call cleanupSwapChain() + * -# destroy @ref createSyncObjects() "synchronization objects" + * -# destroy @ref createCommandPools() "command pools" + * -# call VulkanAllocator::cleanup() + * -# destroy device + * -# destroy surface + * -# call cleanupDebugMessenger() + * -# destroy instance + * -# call glfwDestroyWindow() + * -# call glfwTerminate() + */ void cleanup(); - const GLFWwindow* getWindow() const { return window; } + GLFWwindow* getWindow() { return window; } /// @} /** @@ -142,21 +159,22 @@ namespace gz::vk { public: /// @{ const VkDevice& getDevice() const { return device; } - const SettingsManager& getSettings() const { return settings; } const VkExtent2D& getScExtent() const { return scExtent; } const std::vector& getScImages() const { return scImages; } const VkFormat& getScImageFormat() const { return scImageFormat; } + SettingsManager& getSettings() { return settings; } + /** * @brief Get the maximum number of frames in flight * @see MAX_FRAMES_IN_FLIGHT */ - const uint32_t getMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; } + uint32_t getMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; } /** * @brief Get the frame that is currently in flight * @see currentFrame */ - const uint32_t getCurrentFrame() const { return currentFrame; } + uint32_t getCurrentFrame() const { return currentFrame; } void submitThisFrame(VkCommandBuffer& cmdBuffer); @@ -178,6 +196,8 @@ namespace gz::vk { * @param commandPool: The same pool as passed to beginSingleTimeCommands() */ void endSingleTimeCommands(VkCommandBuffer cmdBuffer, InstanceCommandPool commandPool); + + void loadModel(VerticesAndIndices& model); /// @} /** @@ -234,6 +254,9 @@ namespace gz::vk { * @param buffer The (null) handle to the buffer * @param bufferMemory Reference to an (uninitialized) MemoryInfo struct. It will be valid when the function returns * @param properties Properties that the buffers memory needs to satisfy + * @details + * The memory the buffer will be bound to is HOST_VISIBLE and HOST_COHERENT + * */ void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, MemoryInfo& bufferMemory, VkSharingMode sharingMode=VK_SHARING_MODE_EXCLUSIVE, std::vector* qFamiliesWithAccess=nullptr); /** @@ -284,6 +307,8 @@ namespace gz::vk { */ void createTextureSampler(VkSampler& sampler); + void destroyTextureSampler(VkSampler& sampler); + /** * @name Public Interface: Image utility * @details @@ -292,13 +317,16 @@ namespace gz::vk { */ /// @{ /** - * @brief Create the image, allocate the imageMemory and bind the image to it + * @brief Create a 2D image, allocate the imageMemory and bind the image to it */ - void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory); + void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, MemoryInfo& imageMI); + void destroyImage(VkImage& image, MemoryInfo& imageMemory); /** * @brief Create a 2D imageView with format for image. */ void createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags); + void destroyImageView(VkImageView& imageView); + void copyBufferToImage(VkBuffer buffer, VkImage image, int32_t offsetX, int32_t offsetY, uint32_t width, uint32_t height); /** * @todo make a version using vkCmdResolveImage for multisampled images @@ -601,14 +629,6 @@ namespace gz::vk { static std::vector readFile(const std::string& filename); /// @} - /** - * @name Models - */ - /// @{ - void loadModel(VerticesAndIndices& model); - /// @} - - /** * @name Utility */ diff --git a/vulkan.conf b/vulkan.conf index ab5c247..5261500 100644 --- a/vulkan.conf +++ b/vulkan.conf @@ -1,4 +1,5 @@ # Written by writeKeyValueFile +max_frames_in_flight = 3 max_anisotropy = 1 anisotropy_enable = false framerate = 60