diff --git a/.vimspector.json b/.vimspector.json index 85ea0e3..a5cb228 100644 --- a/.vimspector.json +++ b/.vimspector.json @@ -1,6 +1,6 @@ { "configurations": { - "gze": { + "vulkan_test": { "adapter": "vscode-cpptools", "configuration": { "name": "vulkan (cpp)", diff --git a/Makefile b/Makefile index ffbda2c..ff30056 100755 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ CXXFLAGS += $(IFLAGS) default: $(EXEC) echo $(OBJECTS) +# TODO: REMOVE -g! release: CXXFLAGS += -O3 release : default diff --git a/main.cpp b/main.cpp index 64a292a..55d2874 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,15 @@ #include "main.hpp" +#include "shape.hpp" #include "vulkan_instance.hpp" #include "renderer2D.hpp" +#include +#include #include namespace gz::vk { int mainLoop() { + Log log("", true, false, "Main", Color::BG_RED); gz::SettingsManagerCreateInfo smCI{}; smCI.filepath = gz::vk::CONFIG_FILE; smCI.readFileOnCreation = true; @@ -15,18 +19,32 @@ namespace gz::vk { VulkanInstance vulkanInstance(smCI); vulkanInstance.init(); - Renderer2D r2D(vulkanInstance); + TextureManager textureManager(vulkanInstance); + Renderer2D r2D(vulkanInstance, textureManager); - Rectangle rect1( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}); - Rectangle rect2(190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}); - Rectangle rect3(420, 64, 512, 64, { 0.0f, 1.0f, 0.0f}); - Rectangle rect4( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}); - Rectangle rect5( -600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}); - r2D.drawShape(rect1); - r2D.drawShape(rect2); - r2D.drawShape(rect3); - r2D.drawShape(rect4); - r2D.drawShape(rect5); + log("Startup complete. Drawing shapes"); + + Rectangle rect1( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "blocks/leaves.png"); + Rectangle rect2( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "blocks/crate.png"); + Rectangle rect3( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "blocks/dirt.png"); + Rectangle rect4(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "blocks/ice.png"); + Rectangle rect5( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "items/sword.png"); + Rectangle rect6( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "items/crossbow.png"); + Rectangle rect7( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "special/hotbar.png"); + Rectangle rect8(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "special/func_nocol.png"); + Rectangle rect9(-200, -400, 200, 200, { 0.0f, 1.0f, 0.0f}, "entities/sheep.png"); + Rectangle rect10(-400, -400, 800, 800, { 0.0f, 1.0f, 0.0f}, "atlas"); + r2D.drawShape(&rect1); + r2D.drawShape(&rect2); + r2D.drawShape(&rect3); + r2D.drawShape(&rect4); + r2D.drawShape(&rect5); + r2D.drawShape(&rect6); + r2D.drawShape(&rect7); + r2D.drawShape(&rect8); + r2D.drawShape(&rect9); + r2D.drawShape(&rect10); + log("Drawing complete. Filling r2D with shapes."); r2D.fillVertexBufferWithShapes(); r2D.fillIndexBufferWithShapes(); /* gz::FontManager fm("fonts"); */ @@ -34,6 +52,7 @@ namespace gz::vk { /* /1* fm.getFaces().at("menu.ttf"). *1/ */ try { uint32_t imageIndex; + /* std::chrono::time_point now = std::chrono::system_clock::now(); */ while (! glfwWindowShouldClose(vulkanInstance.window)) { glfwPollEvents(); imageIndex = vulkanInstance.beginFrameDraw(); @@ -41,8 +60,11 @@ namespace gz::vk { vulkanInstance.endFrameDraw(imageIndex); auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.settings.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); */ + /* std::cout << "Frametime: (ns)" << dur.count() << " in fps: " << 1e9 / (dur.count()) << "\n"; */ + /* now = now2; */ } - r2D.cleanup(); vulkanInstance.cleanup(); } catch (const std::exception& e) { diff --git a/renderer.cpp b/renderer.cpp index ec38d17..27f19c1 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1,17 +1,15 @@ #include "renderer.hpp" -#include + +#include "vulkan_instance.hpp" namespace gz::vk { void Renderer::cleanup_(){ - vkFreeCommandBuffers(vk.device, vk.commandPoolGraphics, static_cast(commandBuffers.size()), commandBuffers.data()); + vk.destroyCommandBuffers(commandBuffers); vkDestroyBuffer(vk.device, indexBuffer, nullptr); vkFreeMemory(vk.device, indexBufferMemory, nullptr); vkDestroyBuffer(vk.device, vertexBuffer, nullptr); vkFreeMemory(vk.device, vertexBufferMemory, nullptr); - - } - } diff --git a/renderer.hpp b/renderer.hpp index d61ad3d..cb1a2e7 100644 --- a/renderer.hpp +++ b/renderer.hpp @@ -1,12 +1,28 @@ -#include "vulkan_instance.hpp" +#pragma once + +// includes for child classes +#include "texture_manager.hpp" +#include "vertex.hpp" +#include + + +#include +#include namespace gz::vk { + /// Defined in texture_manager.hpp + class TextureManager; + /// Defined in vulkan_instance.hpp + class VulkanInstance; + class Renderer { public: - Renderer(VulkanInstance& instance) : vk(instance) {}; + Renderer(VulkanInstance& instance, TextureManager& textureManager) : vk(instance), textureManager(textureManager) {}; protected: void cleanup_(); VulkanInstance& vk; + TextureManager& textureManager; + std::vector commandBuffers; /// On device local memory VkBuffer vertexBuffer; diff --git a/renderer2D.cpp b/renderer2D.cpp index a5fd63d..1e2250f 100644 --- a/renderer2D.cpp +++ b/renderer2D.cpp @@ -2,246 +2,321 @@ #include "exceptions.hpp" #include "vk_enum_string.h" +#include "vulkan_instance.hpp" +#include "texture_manager.hpp" #include +#include #include namespace gz::vk { -Renderer2D::Renderer2D(VulkanInstance& instance) : - Renderer(instance), - rLog("renderer2D.log", true, false, "2D-Renderer", Color::BMAGENTA, true, 100) { - vk.createCommandBuffers(commandBuffers); - const size_t vertexCount = 500; - const size_t indexCount = 1000; - vk.createVertexBuffer(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize); - vk.createIndexBuffer(indexCount, indexBuffer, indexBufferMemory, indexBufferSize); - createRenderPass(); - createImages(); - renderPassID = vk.createFramebuffers(imageViews, renderPass); - rLog("Created Renderer2D"); -} - - -void Renderer2D::cleanup() { - cleanup_(); -} - -void Renderer2D::createImages() { - images.resize(vk.scImages.size()); - imageMemory.resize(vk.scImages.size()); - imageViews.resize(vk.scImages.size()); - VkImageUsageFlags usage= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - for (size_t i = 0; i < images.size(); i++) { - vk.createImage(vk.scExtent.width, vk.scExtent.height, vk.scImageFormat, VK_IMAGE_TILING_OPTIMAL, usage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, images[i], imageMemory[i]); - vk.createImageView(vk.scImageFormat, images[i], imageViews[i], VK_IMAGE_ASPECT_COLOR_BIT); - } - -} - -void Renderer2D::createRenderPass() { - VkAttachmentDescription colorBlendAttachment{}; - colorBlendAttachment.format = vk.scImageFormat; - colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - VkAttachmentReference colorAttachmentRef{}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - /* VkAttachmentDescription depthAttachment{}; */ - /* depthAttachment.format = findDepthFormat(); */ - /* depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; */ - /* depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */ - /* depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */ - /* depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; */ - /* depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */ - /* depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; */ - /* depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */ - - /* VkAttachmentReference depthAttachmentRef{}; */ - /* depthAttachmentRef.attachment = 1; */ - /* depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */ - - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - /* subpass.pDepthStencilAttachment = &depthAttachmentRef; */ - - VkSubpassDependency dependency{}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - /* VkSubpassDependency dependency{}; */ - /* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */ - /* dependency.dstSubpass = 0; */ - /* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ - /* dependency.srcAccessMask = 0; */ - /* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ - /* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */ - - /* std::array attachments = { colorBlendAttachment, depthAttachment }; */ - std::vector attachments = { colorBlendAttachment }; - VkRenderPassCreateInfo renderPassCI{}; - renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCI.attachmentCount = static_cast(attachments.size()); - renderPassCI.pAttachments = attachments.data(); - renderPassCI.subpassCount = 1; - renderPassCI.pSubpasses = &subpass; - renderPassCI.dependencyCount = 1; - renderPassCI.pDependencies = &dependency; - /* renderPassCI.dependencyCount = 0; */ - /* renderPassCI.pDependencies = nullptr; */ - /* renderPassCI.correlatedViewMaskCount = 0; */ - /* renderPassCI.pCorrelatedViewMasks = nullptr; */ - VkResult result = vkCreateRenderPass(vk.device, &renderPassCI, nullptr, &renderPass); - if (result != VK_SUCCESS) { - throw getVkException(result, "Could not create render pass", "Renderer2D::createRenderPass"); - } - rLog("createRenderPass: Created render pass."); - -} - - -void Renderer2D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) { - VkCommandBufferBeginInfo commandBufferBI{}; - commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - /* commandBufferBI.flags = 0; */ - /* commandBufferBI.pInheritanceInfo = nullptr; */ - VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to begin 2D command buffer", "Renderer2D::recordCommandBuffer"); +// +// INIT & CLEANUP +// + Renderer2D::Renderer2D(VulkanInstance& instance, TextureManager& textureManager) : + Renderer(instance, textureManager), + rLog("renderer2D.log", true, false, "2D-Renderer", Color::LI_MAGENTA, VULKAN_MESSAGE_TIME_COLOR, true, 100) + { + vk.registerCleanupCallback(std::bind(&Renderer2D::cleanup, this)); + vk.registerSwapChainRecreateCallback(std::bind(&Renderer2D::swapChainRecreateCallback, this)); + vk.createCommandBuffers(commandBuffers); + const size_t vertexCount = 500; + const size_t indexCount = 1000; + vk.createVertexBuffer(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize); + vk.createIndexBuffer(indexCount, indexBuffer, indexBufferMemory, indexBufferSize); + initSwapChainDependantResources(); + VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan("Renderer2D", + { &pipelines[PL_2D].pipeline, &renderPass, &vertexBuffer, &vertexBufferMemory, &indexBuffer, &indexBufferMemory }, + { &framebuffers, &images, &imageMemory, &imageViews, &commandBuffers })); + rLog("Created Renderer2D"); } - VkRenderPassBeginInfo renderPassBI{}; - renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassBI.renderPass = renderPass; - renderPassBI.framebuffer = vk.getFramebuffers(renderPassID)[imageIndex]; - renderPassBI.renderArea.offset = { 0, 0 }; - renderPassBI.renderArea.extent = vk.scExtent; - // clear - std::array clearValues{}; - clearValues[0].color = {{1.0f, 0.0f, 0.0f, 1.0f}}; - clearValues[1].depthStencil = {1.0f, 0}; - renderPassBI.clearValueCount = static_cast(clearValues.size()); - renderPassBI.pClearValues = clearValues.data(); - vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); - - vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipelines[PL_2D].pipeline); - VkBuffer vertexBuffers[] = { vertexBuffer }; - VkDeviceSize offsets[] = {0}; - uint32_t bindingCount = 1; - vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets); - // TODO use correct index type! - vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32); - - /* uint32_t descriptorCount = 1; */ - /* uint32_t firstSet = 0; */ - /* uint32_t dynamicOffsetCount = 0; */ - /* uint32_t* dynamicOffsets = nullptr; */ - /* vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].layout, firstSet, descriptorCount, &descriptorSets[currentFrame], dynamicOffsetCount, dynamicOffsets); */ - - int instanceCount = 1; - int firstIndex = 0; - int firstInstance = 0; - vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance); - - vkCmdEndRenderPass(commandBuffers[currentFrame]); - vk.copyImageToImage(commandBuffers[currentFrame], images[imageIndex], vk.scImages[imageIndex], vk.scExtent.width, vk.scExtent.height); - result = vkEndCommandBuffer(commandBuffers[currentFrame]); - if (result != VK_SUCCESS) { - rLog.error("Failed to record 2D - command buffer", "VkResult:", STR_VK_RESULT(result)); - throw getVkException(result, "Failed to record 2D - command buffer", "Renderer2D::recordCommandBuffer"); - } - vk.commandBuffersToSubmitThisFrame.push_back(commandBuffers[currentFrame]); -} - - -void Renderer2D::fillVertexBufferWithShapes() { - rLog("fillVertexBufferWithShapes"); - if (vertexBufferSize < shapesVerticesCount * sizeof(Vertex2D)) { - throw VkException("vertex buffer too small! vertexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); + void Renderer2D::cleanup() { + /* vk.destroyCommandBuffers(commandBuffers); */ + cleanupSwapChainDependantResources(); + cleanup_(); } - // create staging buffer - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - vk.createBuffer(vertexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, vertexBufferSize, NO_FLAGS, &data); - Vertex2D* vdata = reinterpret_cast(data); - size_t offset = 0; - for (auto it = shapes.begin(); it != shapes.end(); it++) { - rLog("fillVertexBufferWithShapes: copying vertex buffer nr", it - shapes.begin(), "-", it->getVertices(), "to address:", long(vdata + offset), " offset:", offset); - memcpy(vdata+offset, it->getVertices().data(), it->getVertices().size() * sizeof(Vertex2D)); - offset += it->getVertices().size(); - } - vkUnmapMemory(vk.device, stagingBufferMemory); - // fill vertex buffer - vk.copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize); - vkDestroyBuffer(vk.device, stagingBuffer, nullptr); - vkFreeMemory(vk.device, stagingBufferMemory, nullptr); -} -void Renderer2D::fillIndexBufferWithShapes() { - rLog("fillIndexBufferWithShapes"); - if (indexBufferSize < shapesIndicesCount * sizeof(uint32_t)) { - throw VkException("index buffer too small! indexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); +// +// SWAPCHAIN DEPENDANT +// + void Renderer2D::initSwapChainDependantResources() { + createRenderPass(); + createImages(); + vk.createFramebuffers(framebuffers, imageViews, renderPass); + std::vector descriptorSetLayouts = { textureManager.getDescriptorSetLayout() }; + vk.createGraphicsPipeline("shaders/vert2D.spv", "shaders/frag2D.spv", descriptorSetLayouts, false, renderPass, pipelines[PL_2D]); } - // create staging buffer - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - vk.createBuffer(indexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, indexBufferSize, NO_FLAGS, &data); - uint32_t* idata = reinterpret_cast(data); - size_t offset = 0; - for (auto it = shapes.begin(); it != shapes.end(); it++) { - rLog("fillIndexBufferWithShapes: copying index buffer nr", it - shapes.begin(), "-", it->getIndices(), "to address:", long(idata + offset), " offset:", offset); - memcpy(idata+offset, it->getIndices().data(), it->getIndices().size() * sizeof(uint32_t)); - offset += it->getIndices().size(); + void Renderer2D::cleanupSwapChainDependantResources() { + // destroy pipelines + pipelines.destroy(vk.device); + + vk.destroyFramebuffers(framebuffers); + + for (size_t i = 0; i < images.size(); i++) { + vkDestroyImageView(vk.device, imageViews[i], nullptr); + vkDestroyImage(vk.device, images[i], nullptr); + vkFreeMemory(vk.device, imageMemory[i], nullptr); + } + vkDestroyRenderPass(vk.device, renderPass, nullptr); + + } - rLog("fillIndexBufferWithShapes: indices count:", shapesIndicesCount); - vkUnmapMemory(vk.device, stagingBufferMemory); - - // fill index buffer - vk.copyBuffer(stagingBuffer, indexBuffer, indexBufferSize); - vkDestroyBuffer(vk.device, stagingBuffer, nullptr); - vkFreeMemory(vk.device, stagingBufferMemory, nullptr); - -} -void Renderer2D::drawShape(const Shape& shape) { - shapes.emplace_back(shape); - // make indices valid - shapes.rbegin()->setIndexOffset(shapesVerticesCount); - shapes.rbegin()->setNormalize(vk.scExtent.width, vk.scExtent.height); - shapesVerticesCount += shape.getVertices().size(); - shapesIndicesCount += shape.getIndices().size(); -} + void Renderer2D::swapChainRecreateCallback() { + cleanupSwapChainDependantResources(); + initSwapChainDependantResources(); + } -void Renderer2D::drawFrame(uint32_t imageIndex) { - vkResetCommandBuffer(commandBuffers[vk.currentFrame], NO_FLAGS); - recordCommandBuffer(imageIndex, vk.currentFrame); +// +// IMAGES +// + void Renderer2D::createImages() { + images.resize(vk.scImages.size()); + imageMemory.resize(vk.scImages.size()); + imageViews.resize(vk.scImages.size()); + VkImageUsageFlags usage= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + for (size_t i = 0; i < images.size(); i++) { + vk.createImage(vk.scExtent.width, vk.scExtent.height, vk.scImageFormat, VK_IMAGE_TILING_OPTIMAL, usage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, images[i], imageMemory[i]); + vk.createImageView(vk.scImageFormat, images[i], imageViews[i], VK_IMAGE_ASPECT_COLOR_BIT); + } + } -} +// +// RENDER PASS +// + void Renderer2D::createRenderPass() { + VkAttachmentDescription2 colorBlendAttachment{}; + colorBlendAttachment.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; + colorBlendAttachment.format = vk.scImageFormat; + colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + + VkAttachmentReference2 colorAttachmentRef{}; + colorAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + /* VkAttachmentDescription depthAttachment{}; */ + /* depthAttachment.format = findDepthFormat(); */ + /* depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; */ + /* depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */ + /* depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */ + /* depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; */ + /* depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */ + /* depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; */ + /* depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */ + + /* VkAttachmentReference depthAttachmentRef{}; */ + /* depthAttachmentRef.attachment = 1; */ + /* depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */ + + VkSubpassDescription2 subpass{}; + subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + /* subpass.pDepthStencilAttachment = &depthAttachmentRef; */ + + VkSubpassDependency2 colorAttachmentSD{}; + colorAttachmentSD.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2; + colorAttachmentSD.srcSubpass = VK_SUBPASS_EXTERNAL; + colorAttachmentSD.dstSubpass = 0; + colorAttachmentSD.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + colorAttachmentSD.srcAccessMask = 0; + colorAttachmentSD.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + colorAttachmentSD.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + + // dependecy for the image layout transition to transfer dst + VkSubpassDependency2 layoutTransitionSD{}; + colorAttachmentSD.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2; + colorAttachmentSD.srcSubpass = 0; + colorAttachmentSD.dstSubpass = VK_SUBPASS_EXTERNAL; + colorAttachmentSD.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + colorAttachmentSD.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + colorAttachmentSD.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + colorAttachmentSD.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT; + colorAttachmentSD.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + /* VkSubpassDependency dependency{}; */ + /* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */ + /* dependency.dstSubpass = 0; */ + /* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ + /* dependency.srcAccessMask = 0; */ + /* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ + /* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */ + + /* std::array attachments = { colorBlendAttachment, depthAttachment }; */ + std::vector attachments = { colorBlendAttachment }; + std::vector dependencies = { colorAttachmentSD, layoutTransitionSD }; + VkRenderPassCreateInfo2 renderPassCI{}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2; + renderPassCI.attachmentCount = static_cast(attachments.size()); + renderPassCI.pAttachments = attachments.data(); + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpass; + renderPassCI.dependencyCount = dependencies.size(); + renderPassCI.pDependencies = dependencies.data(); + /* renderPassCI.dependencyCount = 0; */ + /* renderPassCI.pDependencies = nullptr; */ + /* renderPassCI.correlatedViewMaskCount = 0; */ + /* renderPassCI.pCorrelatedViewMasks = nullptr; */ + VkResult result = vkCreateRenderPass2(vk.device, &renderPassCI, nullptr, &renderPass); + if (result != VK_SUCCESS) { + throw getVkException(result, "Could not create render pass", "Renderer2D::createRenderPass"); + } + rLog("createRenderPass: Created render pass."); + + } + + +// +// RENDERING +// + void Renderer2D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) { + VkCommandBufferBeginInfo commandBufferBI{}; + commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + /* commandBufferBI.flags = 0; */ + /* commandBufferBI.pInheritanceInfo = nullptr; */ + VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to begin 2D command buffer", "Renderer2D::recordCommandBuffer"); + } + + VkRenderPassBeginInfo renderPassBI{}; + renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassBI.renderPass = renderPass; + renderPassBI.framebuffer = framebuffers[imageIndex]; + renderPassBI.renderArea.offset = { 0, 0 }; + renderPassBI.renderArea.extent = vk.scExtent; + // clear + std::array clearValues{}; + clearValues[0].color = {{1.0f, 0.0f, 0.0f, 1.0f}}; + /* clearValues[1].depthStencil = {1.0f, 0}; */ + renderPassBI.clearValueCount = static_cast(clearValues.size()); + renderPassBI.pClearValues = clearValues.data(); + + vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].pipeline); + VkBuffer vertexBuffers[] = { vertexBuffer }; + VkDeviceSize offsets[] = {0}; + uint32_t bindingCount = 1; + vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets); + // TODO use correct index type! + vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32); + + uint32_t descriptorCount = 1; + uint32_t firstSet = 0; + uint32_t dynamicOffsetCount = 0; + uint32_t* dynamicOffsets = nullptr; + vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets); + + int instanceCount = 1; + int firstIndex = 0; + int firstInstance = 0; + vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance); + vkCmdEndRenderPass(commandBuffers[currentFrame]); + + vk.copyImageToImage(commandBuffers[currentFrame], images[imageIndex], vk.scImages[imageIndex], vk.scExtent); + result = vkEndCommandBuffer(commandBuffers[currentFrame]); + if (result != VK_SUCCESS) { + rLog.error("Failed to record 2D - command buffer", "VkResult:", STR_VK_RESULT(result)); + throw getVkException(result, "Failed to record 2D - command buffer", "Renderer2D::recordCommandBufferWithTexture"); + } + vk.submitThisFrame(commandBuffers[currentFrame]); + } + + + void Renderer2D::fillVertexBufferWithShapes() { + rLog("fillVertexBufferWithShapes"); + if (vertexBufferSize < shapesVerticesCount * sizeof(Vertex2D)) { + throw VkException("vertex buffer too small! vertexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); + } + + // create staging buffer + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + vk.createBuffer(vertexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, vertexBufferSize, NO_FLAGS, &data); + Vertex2D* vdata = reinterpret_cast(data); + size_t offset = 0; + for (auto it = shapes.begin(); it != shapes.end(); it++) { + rLog("fillVertexBufferWithShapes: copying vertex buffer nr", it - shapes.begin(), "-", it->getVertices(), "to address:", long(vdata + offset), " offset:", offset); + memcpy(vdata+offset, it->getVertices().data(), it->getVertices().size() * sizeof(Vertex2D)); + offset += it->getVertices().size(); + } + vkUnmapMemory(vk.device, stagingBufferMemory); + // fill vertex buffer + vk.copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize); + vkDestroyBuffer(vk.device, stagingBuffer, nullptr); + vkFreeMemory(vk.device, stagingBufferMemory, nullptr); + } + void Renderer2D::fillIndexBufferWithShapes() { + rLog("fillIndexBufferWithShapes"); + if (indexBufferSize < shapesIndicesCount * sizeof(uint32_t)) { + throw VkException("index buffer too small! indexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); + } + + // create staging buffer + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + vk.createBuffer(indexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, indexBufferSize, NO_FLAGS, &data); + uint32_t* idata = reinterpret_cast(data); + size_t offset = 0; + for (auto it = shapes.begin(); it != shapes.end(); it++) { + rLog("fillIndexBufferWithShapes: copying index buffer nr", it - shapes.begin(), "-", it->getIndices(), "to address:", long(idata + offset), " offset:", offset); + memcpy(idata+offset, it->getIndices().data(), it->getIndices().size() * sizeof(uint32_t)); + offset += it->getIndices().size(); + } + rLog("fillIndexBufferWithShapes: indices count:", shapesIndicesCount); + vkUnmapMemory(vk.device, stagingBufferMemory); + + // fill index buffer + vk.copyBuffer(stagingBuffer, indexBuffer, indexBufferSize); + vkDestroyBuffer(vk.device, stagingBuffer, nullptr); + vkFreeMemory(vk.device, stagingBufferMemory, nullptr); + + } + + + void Renderer2D::drawShape(Shape* shape) { + // make indices valid + shape->setIndexOffset(shapesVerticesCount); + shape->normalizeVertices(vk.scExtent.width, vk.scExtent.height); + shape->setTextureCoordinates(textureManager); + // object slicing here, need to call virtual setTextureCoordinates before this! + shapes.push_back(*shape); + shapesVerticesCount += shape->getVertices().size(); + shapesIndicesCount += shape->getIndices().size(); + } + + + void Renderer2D::drawFrame(uint32_t imageIndex) { + vkResetCommandBuffer(commandBuffers[vk.currentFrame], NO_FLAGS); + /* recordCommandBuffer(imageIndex, vk.currentFrame); */ + recordCommandBuffer(imageIndex, vk.currentFrame); + + } } // namespace gz::vk diff --git a/renderer2D.hpp b/renderer2D.hpp index 1fdf3c5..2a6eb25 100644 --- a/renderer2D.hpp +++ b/renderer2D.hpp @@ -1,31 +1,45 @@ +#pragma once + #include "renderer.hpp" +#include "shape.hpp" +#include "vulkan_util.hpp" + namespace gz::vk { class Renderer2D : public Renderer { public: - Renderer2D(VulkanInstance& instance); - void drawShape(const Shape& shape); + /** + * @brief Create a 2D renderer + * @details + * -# @ref VulkanInstance::registerCleanupCallback "register" @ref cleanup() "cleanup callback" + * -# @ref VulkanInstance::registerSwapChainRecreateCallback "register" @ref swapChainRecreateCallback "swapChain recreation callback" + * -# create command buffers + * -# create vertex & index buffers + * -# call initSwapChainDependantResources + */ + Renderer2D(VulkanInstance& instance, TextureManager& textureManager); + /** + * @name Rendering + */ + /// @{ + void drawShape(Shape* shape); /** * @brief Copies the vertices from shapes into the vertex buffer, using a staging buffer */ void fillVertexBufferWithShapes(); void fillIndexBufferWithShapes(); void drawFrame(uint32_t imageIndex); - void cleanup(); - /** - * @brief Create a render pass - * @details - * Attachments: - * - color blend: - * - loadOp = VK_ATTACHMENT_LOAD_OP_LOAD (not clear!) - * - storeOp = VK_ATTACHMENT_STORE_OP_STORE - * - initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL - * - finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL - * - stencil load/store = dont care - */ - void createRenderPass(); + /// @} private: + /** + * @brief Destroy all vulkan objects owned by this object + * @details: + * Does: + * -# call cleanupSwapChainDependantResources() + * -# call Renderer::cleanup_() + */ + void cleanup(); std::vector shapes; size_t shapesVerticesCount = 0; size_t shapesIndicesCount = 0; @@ -37,26 +51,82 @@ namespace gz::vk { * - image layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL * - clear image * -# bind 2d pipeline, vertex and index buffer + * -# bind texture sampler * -# draw indexed: draw the shapes from shapes vector * -# end render pass * - image layout: VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL * -# copy image to swapChain image with same imageIndex - * */ void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame); + // IMAGE + /** + * @name Image (render targets) + * @details + * The images are used as render targets. After rendering, the current image gets blitted onto the current swap chain image. + * @{ + */ std::vector images; std::vector imageMemory; std::vector imageViews; /** - * @brief Creates the images and imageViews with the format of the VulkanInstance::swapChain images + * @brief Creates the images (on imageMemory) and imageViews with the format of the VulkanInstance::swapChain images */ void createImages(); + /// @} + + /** + * @name Render pass + * @details + * Attachments: + * - color blend: + * - loadOp = VK_ATTACHMENT_LOAD_OP_LOAD (not clear!) + * - storeOp = VK_ATTACHMENT_STORE_OP_STORE + * - initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + * - finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + * - stencil load/store = dont care + */ + /// @{ + /** + * @brief Create a render pass + */ + void createRenderPass(); VkRenderPass renderPass; - int renderPassID; + /// @} + + std::vector framebuffers; + // PIPELINE + PipelineContainer pipelines; + + // SWAPCHAIN RECREATION + /** + * @brief Swapchain recreation + */ + /// @{ + /** + * @brief Cleans up resources that were initialized by initSwapChainDependantResources + */ + void cleanupSwapChainDependantResources(); + /** + * @brief Sets up resources that depend on the swap chain or its attributes + * @details + * Initializes up: + * - render pass + * - images, imageMemory, imageViews + * - framebuffers + * - pipeline + */ + void initSwapChainDependantResources(); + /** + * @brief Recreates swap chain dependant resources + * @details + * Calls: + * -# cleanupSwapChainDependantResources + * -# initSwapChainDependantResources + */ + void swapChainRecreateCallback(); + /// @} Log rLog; - }; - } // namespace gz::vk diff --git a/renderer2D.log b/renderer2D.log new file mode 100644 index 0000000..e69de29 diff --git a/renderer3D.cpp b/renderer3D.cpp index 9e9d4a6..03d9de7 100644 --- a/renderer3D.cpp +++ b/renderer3D.cpp @@ -1,104 +1,436 @@ #include "renderer3D.hpp" +#include "vulkan_instance.hpp" +#include "texture_manager.hpp" #include "exceptions.hpp" #include "vk_enum_string.h" #include namespace gz::vk { +// +// INIT & CLEANUP +// + Renderer3D::Renderer3D(VulkanInstance& instance, TextureManager& textureManager) : + Renderer(instance, textureManager), + rLog("renderer3D.log", true, false, "3D-Renderer", Color::CYAN, VULKAN_MESSAGE_TIME_COLOR, true, 100) { -Renderer3D::Renderer3D(VulkanInstance& instance) : - Renderer(instance), - rLog("renderer3D.log", true, false, "3D-Renderer", Color::BCYAN, true, 100) { - vk.createCommandBuffers(commandBuffers); - const size_t vertexCount = 500; - const size_t indexCount = 1000; - vk.createVertexBuffer(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize); - vk.createIndexBuffer(indexCount, indexBuffer, indexBufferMemory, indexBufferSize); - rLog("Created Renderer3D"); -} + vk.registerCleanupCallback(std::bind(&Renderer3D::cleanup, this)); + vk.registerSwapChainRecreateCallback(std::bind(&Renderer3D::swapChainRecreateCallback, this)); -void Renderer3D::cleanup() { - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroyBuffer(vk.device, uniformBuffers[i], nullptr); - vkFreeMemory(vk.device, uniformBuffersMemory[i], nullptr); - } - cleanup_(); -} + vk.createCommandBuffers(commandBuffers); + const size_t vertexCount = 500; + const size_t indexCount = 1000; + vk.createVertexBuffer(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize); + vk.createIndexBuffer(indexCount, indexBuffer, indexBufferMemory, indexBufferSize); + rLog("Created Renderer3D"); - -void Renderer3D::updateUniformBuffer() { - static auto startTime = std::chrono::high_resolution_clock::now(); - auto currentTime = std::chrono::high_resolution_clock::now(); - float time = std::chrono::duration(currentTime - startTime).count(); - - // TODO use push constant instead of ubo - UniformBufferObject ubo{}; - /* ubo.model = glm::rotate(glm::mat4(1.0f), time * std::numbers::pi_v / 2, glm::vec3(0.0f, 0.0f, 1.0f)); */ - /* ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); */ - /* ubo.projection = glm::perspective(glm::radians(45.0f), static_cast(scExtent.width) / scExtent.height, 1.0f, 10.0f); */ - ubo.model = glm::mat4(1); - ubo.view = glm::mat4(1); - ubo.projection = glm::mat4(1); - /* ubo.projection[1][1] *= -1; // y coordinate inverted in opengl */ - void* data; - vkMapMemory(vk.device, uniformBuffersMemory[vk.currentFrame], NO_OFFSET, sizeof(ubo), NO_FLAGS, &data); - memcpy(data, &ubo, sizeof(ubo)); - vkUnmapMemory(vk.device, uniformBuffersMemory[vk.currentFrame]); -} - - -void Renderer3D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) { - VkCommandBufferBeginInfo commandBufferBI{}; - commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - /* commandBufferBI.flags = 0; */ - /* commandBufferBI.pInheritanceInfo = nullptr; */ - VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to begin 3D - command buffer", "recordCommandBuffer"); + initSwapChainDependantResources(); + VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan("Renderer3D", + { &pipelines[PL_3D].pipeline, &renderPass, &vertexBuffer, &vertexBufferMemory, &indexBuffer, &indexBufferMemory }, + { &framebuffers, &images, &imageMemory, &imageViews, &commandBuffers })); + rLog("Created Renderer3D"); } - VkRenderPassBeginInfo renderPassBI{}; - renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassBI.renderPass = vk.renderPassBegin; - // TODO - /* renderPassBI.framebuffer = vk.scFramebuffers[0][imageIndex]; */ - renderPassBI.renderArea.offset = { 0, 0 }; - renderPassBI.renderArea.extent = vk.scExtent; - std::array clearValues{}; - clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}}; - clearValues[1].depthStencil = {1.0f, 0}; - renderPassBI.clearValueCount = static_cast(clearValues.size()); - renderPassBI.pClearValues = clearValues.data(); - - vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); - - vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipelines[PL_3D].pipeline); - VkBuffer vertexBuffers[] = { vertexBuffer }; - VkDeviceSize offsets[] = {0}; - uint32_t bindingCount = 1; - vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets); - // TODO use correct index type! - vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32); - - uint32_t descriptorCount = 1; - uint32_t firstSet = 0; - uint32_t dynamicOffsetCount = 0; - uint32_t* dynamicOffsets = nullptr; - vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipelines[PL_3D].layout, firstSet, descriptorCount, &vk.descriptorSets[currentFrame], dynamicOffsetCount, dynamicOffsets); - - int instanceCount = 1; - int firstIndex = 0; - int firstInstance = 0; - /* vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance); */ - - vkCmdEndRenderPass(commandBuffers[currentFrame]); - result = vkEndCommandBuffer(commandBuffers[currentFrame]); - if (result != VK_SUCCESS) { - rLog.error("Failed to record command buffer", "VkResult:", STR_VK_RESULT(result)); - throw getVkException(result, "Failed to record command buffer"); + void Renderer3D::cleanup() { + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroyBuffer(vk.device, uniformBuffers[i], nullptr); + vkFreeMemory(vk.device, uniformBuffersMemory[i], nullptr); + } + cleanupSwapChainDependantResources(); + cleanup_(); } -} + + +// +// SWAPCHAIN DEPENDANT +// + void Renderer3D::initSwapChainDependantResources() { + createRenderPass(); + createImages(); + vk.createFramebuffers(framebuffers, imageViews, renderPass); + std::vector descriptorSetLayouts = { textureManager.getDescriptorSetLayout() }; + vk.createGraphicsPipeline("shaders/vert2D.spv", "shaders/frag2D.spv", descriptorSetLayouts, false, renderPass, pipelines[PL_2D]); + } + + + void Renderer3D::cleanupSwapChainDependantResources() { + // destroy pipelines + pipelines.destroy(vk.device); + + vk.destroyFramebuffers(framebuffers); + + for (size_t i = 0; i < images.size(); i++) { + vkDestroyImageView(vk.device, imageViews[i], nullptr); + vkDestroyImage(vk.device, images[i], nullptr); + vkFreeMemory(vk.device, imageMemory[i], nullptr); + } + vkDestroyRenderPass(vk.device, renderPass, nullptr); + + + } + + + void Renderer3D::swapChainRecreateCallback() { + cleanupSwapChainDependantResources(); + initSwapChainDependantResources(); + } + + +// +// IMAGES +// + void Renderer3D::createImages() { + images.resize(vk.scImages.size()); + imageMemory.resize(vk.scImages.size()); + imageViews.resize(vk.scImages.size()); + VkImageUsageFlags usage= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + for (size_t i = 0; i < images.size(); i++) { + vk.createImage(vk.scExtent.width, vk.scExtent.height, vk.scImageFormat, VK_IMAGE_TILING_OPTIMAL, usage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, images[i], imageMemory[i]); + vk.createImageView(vk.scImageFormat, images[i], imageViews[i], VK_IMAGE_ASPECT_COLOR_BIT); + } + } + + +// +// RENDER PASS +// + void Renderer3D::createRenderPass() { + VkAttachmentDescription2 colorBlendAttachment{}; + colorBlendAttachment.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; + colorBlendAttachment.format = vk.scImageFormat; + colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + + VkAttachmentReference2 colorAttachmentRef{}; + colorAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + /* VkAttachmentDescription depthAttachment{}; */ + /* depthAttachment.format = findDepthFormat(); */ + /* depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; */ + /* depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */ + /* depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */ + /* depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; */ + /* depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */ + /* depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; */ + /* depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */ + + /* VkAttachmentReference depthAttachmentRef{}; */ + /* depthAttachmentRef.attachment = 1; */ + /* depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */ + + VkSubpassDescription2 subpass{}; + subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + /* subpass.pDepthStencilAttachment = &depthAttachmentRef; */ + + VkSubpassDependency2 colorAttachmentSD{}; + colorAttachmentSD.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2; + colorAttachmentSD.srcSubpass = VK_SUBPASS_EXTERNAL; + colorAttachmentSD.dstSubpass = 0; + colorAttachmentSD.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + colorAttachmentSD.srcAccessMask = 0; + colorAttachmentSD.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + colorAttachmentSD.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + + // dependecy for the image layout transition to transfer dst + VkSubpassDependency2 layoutTransitionSD{}; + colorAttachmentSD.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2; + colorAttachmentSD.srcSubpass = 0; + colorAttachmentSD.dstSubpass = VK_SUBPASS_EXTERNAL; + colorAttachmentSD.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + colorAttachmentSD.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + colorAttachmentSD.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + colorAttachmentSD.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT; + colorAttachmentSD.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + /* VkSubpassDependency dependency{}; */ + /* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */ + /* dependency.dstSubpass = 0; */ + /* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ + /* dependency.srcAccessMask = 0; */ + /* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ + /* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */ + + /* std::array attachments = { colorBlendAttachment, depthAttachment }; */ + std::vector attachments = { colorBlendAttachment }; + std::vector dependencies = { colorAttachmentSD, layoutTransitionSD }; + VkRenderPassCreateInfo2 renderPassCI{}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2; + renderPassCI.attachmentCount = static_cast(attachments.size()); + renderPassCI.pAttachments = attachments.data(); + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpass; + renderPassCI.dependencyCount = dependencies.size(); + renderPassCI.pDependencies = dependencies.data(); + /* renderPassCI.dependencyCount = 0; */ + /* renderPassCI.pDependencies = nullptr; */ + /* renderPassCI.correlatedViewMaskCount = 0; */ + /* renderPassCI.pCorrelatedViewMasks = nullptr; */ + VkResult result = vkCreateRenderPass2(vk.device, &renderPassCI, nullptr, &renderPass); + if (result != VK_SUCCESS) { + throw getVkException(result, "Could not create render pass", "Renderer3D::createRenderPass"); + } + rLog("createRenderPass: Created render pass."); + + } + + + + + + void Renderer3D::updateUniformBuffer() { + static auto startTime = std::chrono::high_resolution_clock::now(); + auto currentTime = std::chrono::high_resolution_clock::now(); + float time = std::chrono::duration(currentTime - startTime).count(); + + // TODO use push constant instead of ubo + UniformBufferObject ubo{}; + ubo.model = glm::rotate(glm::mat4(1.0f), time * std::numbers::pi_v / 2, glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.projection = glm::perspective(glm::radians(45.0f), static_cast(vk.scExtent.width) / vk.scExtent.height, 1.0f, 10.0f); + /* ubo.model = glm::mat4(1); */ + /* ubo.view = glm::mat4(1); */ + /* ubo.projection = glm::mat4(1); */ + /* ubo.projection[1][1] *= -1; // y coordinate inverted in opengl */ + void* data; + vkMapMemory(vk.device, uniformBuffersMemory[vk.currentFrame], NO_OFFSET, sizeof(ubo), NO_FLAGS, &data); + memcpy(data, &ubo, sizeof(ubo)); + vkUnmapMemory(vk.device, uniformBuffersMemory[vk.currentFrame]); + } + +// +// DESCRIPTORS +// + void Renderer3D::createDescriptorResources() { + // LAYOUT + // 1) uniform buffer object + VkDescriptorSetLayoutBinding uboLayoutBinding{}; + uboLayoutBinding.binding = 0; + uboLayoutBinding.descriptorCount = 1; + uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + /* uboLayoutBinding.pImmutableSamplers = nullptr; */ + + // 2) combined image sampler + VkDescriptorSetLayoutBinding samplerLayoutBinding{}; + samplerLayoutBinding.binding = 1; + samplerLayoutBinding.descriptorCount = 1; + samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + /* samplerLayoutBinding.pImmutableSamplers = nullptr; */ + + std::vector bindings = { uboLayoutBinding, samplerLayoutBinding }; + vk.createDescriptorSetLayout(bindings, descriptorSetLayout); + + // POOL + std::array poolSizes; + poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + poolSizes[1].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + + VkDescriptorPoolCreateInfo poolCI{}; + poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolCI.poolSizeCount = static_cast(poolSizes.size()); + poolCI.pPoolSizes = poolSizes.data(); + poolCI.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); + VkResult result = vkCreateDescriptorPool(vk.device, &poolCI, nullptr, &descriptorPool); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create descriptor pool", "Renderer3D::createDescriptorResources"); + } + + // SETS + std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); + VkDescriptorSetAllocateInfo setAI{}; + setAI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + setAI.descriptorPool = descriptorPool; + setAI.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); + setAI.pSetLayouts = layouts.data(); + + descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); + result = vkAllocateDescriptorSets(vk.device, &setAI, descriptorSets.data()); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create descriptor sets", "Renderer3D::createDescriptorResources"); + } + + // configure sets + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferI{}; + bufferI.buffer = uniformBuffers[i]; + bufferI.offset = 0; + bufferI.range = VK_WHOLE_SIZE; // sizeof(UniformBufferObject); + + VkDescriptorImageInfo imageI{}; + imageI.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + // TODO + /* imageI.imageView = textureImageView; */ + /* imageI.sampler = textureSampler; */ + + std::array descriptorW{}; + descriptorW[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorW[0].dstSet = descriptorSets[i]; + descriptorW[0].dstBinding = bindingUniformBuffer; + descriptorW[0].dstArrayElement = 0; + descriptorW[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorW[0].descriptorCount = 1; + descriptorW[0].pBufferInfo = &bufferI; + /* descriptorW[0].pImageInfo = nullptr; */ + /* descriptorW[0].pTexelBufferView = nullptr; */ + + descriptorW[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorW[1].dstSet = descriptorSets[i]; + descriptorW[1].dstBinding = bindingCombinedImageSampler; + descriptorW[1].dstArrayElement = 0; + descriptorW[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorW[1].descriptorCount = 1; + /* descriptorW[1].pBufferInfo = &bufferI; */ + descriptorW[1].pImageInfo = &imageI; + /* descriptorW[1].pTexelBufferView = nullptr; */ + + uint32_t descriptorWriteCount = static_cast(descriptorW.size()); + uint32_t descriptorCopyCount = 0; + vkUpdateDescriptorSets(vk.device, descriptorWriteCount, descriptorW.data(), descriptorCopyCount, nullptr); + } + rLog("createDescriptorResources: Created descriptor layouts, pool and sets."); + } + +// +// RENDERING +// + void Renderer3D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) {} + /* VkCommandBufferBeginInfo commandBufferBI{}; */ + /* commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; */ + /* /1* commandBufferBI.flags = 0; *1/ */ + /* /1* commandBufferBI.pInheritanceInfo = nullptr; *1/ */ + /* VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI); */ + /* if (result != VK_SUCCESS) { */ + /* throw getVkException(result, "Failed to begin 2D command buffer", "Renderer3D::recordCommandBuffer"); */ + /* } */ + + /* VkRenderPassBeginInfo renderPassBI{}; */ + /* renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; */ + /* renderPassBI.renderPass = renderPass; */ + /* renderPassBI.framebuffer = framebuffers[imageIndex]; */ + /* renderPassBI.renderArea.offset = { 0, 0 }; */ + /* renderPassBI.renderArea.extent = vk.scExtent; */ + /* // clear */ + /* std::array clearValues{}; */ + /* clearValues[0].color = {{1.0f, 0.0f, 0.0f, 1.0f}}; */ + /* /1* clearValues[1].depthStencil = {1.0f, 0}; *1/ */ + /* renderPassBI.clearValueCount = static_cast(clearValues.size()); */ + /* renderPassBI.pClearValues = clearValues.data(); */ + + /* vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); */ + + /* vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].pipeline); */ + /* VkBuffer vertexBuffers[] = { vertexBuffer }; */ + /* VkDeviceSize offsets[] = {0}; */ + /* uint32_t bindingCount = 1; */ + /* vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets); */ + /* // TODO use correct index type! */ + /* vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32); */ + + /* uint32_t descriptorCount = 1; */ + /* uint32_t firstSet = 0; */ + /* uint32_t dynamicOffsetCount = 0; */ + /* uint32_t* dynamicOffsets = nullptr; */ + /* vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets); */ + + /* int instanceCount = 1; */ + /* int firstIndex = 0; */ + /* int firstInstance = 0; */ + /* vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance); */ + /* vkCmdEndRenderPass(commandBuffers[currentFrame]); */ + + /* vk.copyImageToImage(commandBuffers[currentFrame], images[imageIndex], vk.scImages[imageIndex], vk.scExtent); */ + /* result = vkEndCommandBuffer(commandBuffers[currentFrame]); */ + /* if (result != VK_SUCCESS) { */ + /* rLog.error("Failed to record 2D - command buffer", "VkResult:", STR_VK_RESULT(result)); */ + /* throw getVkException(result, "Failed to record 2D - command buffer", "Renderer3D::recordCommandBufferWithTexture"); */ + /* } */ + /* vk.submitThisFrame(commandBuffers[currentFrame]); */ + /* } */ + + + /* void Renderer3D::fillVertexBufferWithShapes() { */ + /* rLog("fillVertexBufferWithShapes"); */ + /* if (vertexBufferSize < shapesVerticesCount * sizeof(Vertex2D)) { */ + /* throw VkException("vertex buffer too small! vertexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); */ + /* } */ + + /* // create staging buffer */ + /* VkBuffer stagingBuffer; */ + /* VkDeviceMemory stagingBufferMemory; */ + /* vk.createBuffer(vertexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, vertexBufferSize, NO_FLAGS, &data); */ + /* Vertex2D* vdata = reinterpret_cast(data); */ + /* size_t offset = 0; */ + /* for (auto it = shapes.begin(); it != shapes.end(); it++) { */ + /* rLog("fillVertexBufferWithShapes: copying vertex buffer nr", it - shapes.begin(), "-", it->getVertices(), "to address:", long(vdata + offset), " offset:", offset); */ + /* memcpy(vdata+offset, it->getVertices().data(), it->getVertices().size() * sizeof(Vertex2D)); */ + /* offset += it->getVertices().size(); */ + /* } */ + /* vkUnmapMemory(vk.device, stagingBufferMemory); */ + /* // fill vertex buffer */ + /* vk.copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize); */ + /* vkDestroyBuffer(vk.device, stagingBuffer, nullptr); */ + /* vkFreeMemory(vk.device, stagingBufferMemory, nullptr); */ + /* } */ + /* void Renderer3D::fillIndexBufferWithShapes() { */ + /* rLog("fillIndexBufferWithShapes"); */ + /* if (indexBufferSize < shapesIndicesCount * sizeof(uint32_t)) { */ + /* throw VkException("index buffer too small! indexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes"); */ + /* } */ + + /* // create staging buffer */ + /* VkBuffer stagingBuffer; */ + /* VkDeviceMemory stagingBufferMemory; */ + /* vk.createBuffer(indexBufferSize, 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.device, stagingBufferMemory, NO_OFFSET, indexBufferSize, NO_FLAGS, &data); */ + /* uint32_t* idata = reinterpret_cast(data); */ + /* size_t offset = 0; */ + /* for (auto it = shapes.begin(); it != shapes.end(); it++) { */ + /* rLog("fillIndexBufferWithShapes: copying index buffer nr", it - shapes.begin(), "-", it->getIndices(), "to address:", long(idata + offset), " offset:", offset); */ + /* memcpy(idata+offset, it->getIndices().data(), it->getIndices().size() * sizeof(uint32_t)); */ + /* offset += it->getIndices().size(); */ + /* } */ + /* rLog("fillIndexBufferWithShapes: indices count:", shapesIndicesCount); */ + /* vkUnmapMemory(vk.device, stagingBufferMemory); */ + + /* // fill index buffer */ + /* vk.copyBuffer(stagingBuffer, indexBuffer, indexBufferSize); */ + /* vkDestroyBuffer(vk.device, stagingBuffer, nullptr); */ + /* vkFreeMemory(vk.device, stagingBufferMemory, nullptr); */ + + /* } */ + + +// +// RENDERING +// + void Renderer3D::drawFrame(uint32_t imageIndex) { + vkResetCommandBuffer(commandBuffers[vk.currentFrame], NO_FLAGS); + /* recordCommandBuffer(imageIndex, vk.currentFrame); */ + recordCommandBuffer(imageIndex, vk.currentFrame); + + } + + diff --git a/renderer3D.hpp b/renderer3D.hpp index 46c2f75..d5e9983 100644 --- a/renderer3D.hpp +++ b/renderer3D.hpp @@ -1,5 +1,9 @@ +#pragma once + #include "renderer.hpp" +#include "vulkan_util.hpp" + namespace gz::vk { struct UniformBufferObject { alignas(16) glm::mat4 model; @@ -7,43 +11,134 @@ namespace gz::vk { alignas(16) glm::mat4 projection; }; - - const std::vector vertices2 = { - {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}}, - {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, - {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, - {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, - - {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - {{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, - {{ 0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, - {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, - - }; - /* const std::vector vertices = { */ - /* {{-1.0f, -1.0f}, {0.0f, 0.0f, 1.0f}}, */ - /* {{0.5f, 0.5f}, {0.0, 1.0f, 1.0f}}, */ - /* {{-1.0, 0.0f}, {1.0f, 0.0f, 0.0f}}, */ - /* {{1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}}, */ - /* {{-0.5f, -0.5f}, {0.0, 1.0f, 1.0f}}, */ - /* {{1.0, 0.0f}, {1.0f, 0.0f, 1.0f}}, */ - /* }; */ - - - const std::vector indices2 = { - 0, 1, 2, 2, 3, 0, - 4, 5, 6, 6, 7, 4 - }; - class Renderer3D : public Renderer { public: - Renderer3D(VulkanInstance& instance); - void cleanup(); - protected: + /** + * @brief Create a 3D renderer + * @details + * -# @ref VulkanInstance::registerCleanupCallback "register" @ref cleanup() "cleanup callback" + * -# @ref VulkanInstance::registerSwapChainRecreateCallback "register" @ref swapChainRecreateCallback "swapChain recreation callback" + * -# create command buffers + * -# create vertex & index buffers + * -# call initSwapChainDependantResources + */ + Renderer3D(VulkanInstance& instance, TextureManager& textureManager); + /** + * @name Rendering + */ + /// @{ + void drawFrame(uint32_t imageIndex); + /// @} + private: void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame); std::vector uniformBuffers; std::vector uniformBuffersMemory; void updateUniformBuffer(); + + /** + * @name Image (render targets) + * @details + * The images are used as render targets. After rendering, the current image gets blitted onto the current swap chain image. + */ + /// @{ + std::vector images; + std::vector imageMemory; + std::vector imageViews; + /** + * @brief Creates the images (on imageMemory) and imageViews with the format of the VulkanInstance::swapChain images + */ + void createImages(); + /// @} + + /** + * @name Render pass + * @details + * Attachments: + * - color blend: + * - loadOp = VK_ATTACHMENT_LOAD_OP_LOAD (not clear!) + * - storeOp = VK_ATTACHMENT_STORE_OP_STORE + * - initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + * - finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + * - stencil load/store = dont care + */ + /// @{ + /** + * @brief Create a render pass + */ + void createRenderPass(); + VkRenderPass renderPass; + /// @} + + std::vector framebuffers; + // PIPELINE + PipelineContainer pipelines; + + /** + * @name Desciptors + * @details These functions create a desciptor with bindings for + * -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER) + * -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) + */ + /// @{ + VkDescriptorPool descriptorPool; + VkDescriptorSetLayout descriptorSetLayout; + std::vector descriptorSets; + /** + * @brief Create a descriptor layout binding for the MVP uniform buffer object + * @details Create a desciptor set layout with bindings for + * -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER) + * -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) + */ + void createDescriptorPool(); + /* + * @bried Create one descriptor set with layout descriptorSetLayout for each frame + */ + void createDescriptorSets(); + const uint32_t bindingUniformBuffer = 0; + const uint32_t bindingCombinedImageSampler = 1; + /* const uint32_t bindingCombinedImageSampler = 1; */ + + + void createDescriptorResources(); + + /** + * @brief Swapchain recreation + */ + /// @{ + /** + * @brief Cleans up resources that were initialized by initSwapChainDependantResources + */ + void cleanupSwapChainDependantResources(); + /** + * @brief Sets up resources that depend on the swap chain or its attributes + * @details + * Initializes up: + * - images, imageMemory, imageViews + * - framebuffers + * - render pass + * - pipeline + */ + void initSwapChainDependantResources(); + /** + * @brief Recreates swap chain dependant resources + * @details + * Calls: + * -# cleanupSwapChainDependantResources + * -# initSwapChainDependantResources + */ + void swapChainRecreateCallback(); + /// @} + + /* + * @brief Destroy all vulkan objects owned by this object + * @details: + * Does: + * -# destroy uniform buffers + * -# call cleanupSwapChainDependantResources() + * -# call Renderer::cleanup_() + */ + void cleanup(); + Log rLog; }; diff --git a/shader.vert b/shader.vert index dd5c8bc..351875e 100644 --- a/shader.vert +++ b/shader.vert @@ -19,7 +19,7 @@ void main() { gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); /* gl_Position = vec4(inPosition, 1.0); */ - debugPrintfEXT("inPosition %v3f, inColor %v3f", inPosition, inColor); + /* debugPrintfEXT("inPosition %v3f, inColor %v3f", inPosition, inColor); */ fragColor = inColor; fragTextureCoordinate = inTextureCoordinate; } diff --git a/shader2D.frag b/shader2D.frag index ab8245a..5ac53ff 100644 --- a/shader2D.frag +++ b/shader2D.frag @@ -1,7 +1,7 @@ #version 450 #extension GL_EXT_debug_printf : enable -/* layout(binding = 1) uniform sampler2D textureSampler; */ +layout(binding = 0) uniform sampler2D textureSampler; layout(location = 0) in vec3 fragColor; layout(location = 1) in vec2 fragTextureCoordinate; @@ -9,8 +9,9 @@ layout(location = 1) in vec2 fragTextureCoordinate; layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(fragColor, 1.0); - /* debugPrintfEXT("outColor %v3f", fragColor); */ + /* outColor = vec4(fragColor, 1.0); */ /* outColor = vec4(fragTextureCoordinate, 0.0, 1.0); */ /* outColor = vec4(fragColor * texture(textureSampler, fragTextureCoordinate).rgb, 1.0); */ + outColor = texture(textureSampler, fragTextureCoordinate); + /* debugPrintfEXT("outColor %v3f", outColor); */ } diff --git a/shader2D.vert b/shader2D.vert index eb7ed66..d48fa9a 100644 --- a/shader2D.vert +++ b/shader2D.vert @@ -19,7 +19,7 @@ void main() { /* gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); */ gl_Position = vec4(inPosition, 0.0, 1.0); - debugPrintfEXT("inPosition %v2f, inColor %v3f", inPosition, inColor); + /* debugPrintfEXT("inPosition %v2f, inColor %v3f", inPosition, inColor); */ fragColor = inColor; fragTextureCoordinate = inTextureCoordinate; } diff --git a/shape.cpp b/shape.cpp index 319a392..88ddce4 100644 --- a/shape.cpp +++ b/shape.cpp @@ -1,8 +1,12 @@ #include "shape.hpp" +#include "texture_manager.hpp" + namespace gz::vk { - Rectangle::Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color) - : top(top), left(left), width(width), height(height), color(color) { + Rectangle::Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color, std::string&& texture) + : top(top), left(left), width(width), height(height), color(color) + { + this->texture = std::move(texture); generateVertices(); } @@ -11,9 +15,9 @@ namespace gz::vk { void Rectangle::generateVertices() { vertices.clear(); vertices.emplace_back(Vertex2D{glm::vec2(top, left), color, glm::vec2(0, 0)}); - vertices.emplace_back(Vertex2D{glm::vec2(top, left + width), color, glm::vec2(1, 0)}); + vertices.emplace_back(Vertex2D{glm::vec2(top, left + width), color, glm::vec2(0, 1)}); vertices.emplace_back(Vertex2D{glm::vec2(top + height, left + width), color, glm::vec2(1, 1)}); - vertices.emplace_back(Vertex2D{glm::vec2(top + height, left), color, glm::vec2(0, 1)}); + vertices.emplace_back(Vertex2D{glm::vec2(top + height, left), color, glm::vec2(1, 0)}); indices = { 0, 1, 2, 2, 3, 0 }; /* indices = { 2, 1, 0, 2, 3, 0 }; */ } @@ -25,10 +29,30 @@ namespace gz::vk { } - void Shape::setNormalize(float width, float height) { + void Shape::normalizeVertices(float width, float height) { for (size_t i = 0; i < vertices.size(); i++) { vertices[i].pos.x /= width; vertices[i].pos.y /= height; } } -} // namespaec gz::vk + + void Rectangle::setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) { + vertices[0].texCoord = topLeft; + vertices[1].texCoord.x = bottomRight.x; + vertices[1].texCoord.y = topLeft.y; + vertices[2].texCoord = bottomRight; + vertices[3].texCoord.x = topLeft.x; + vertices[3].texCoord.y = bottomRight.y; + } + + void Rectangle::setTextureCoordinates(TextureManager& textureManager) { + if (texture != "atlas") { + textureManager.getTexCoords(texture, vertices[0].texCoord); + textureManager.getTexCoords(texture, vertices[1].texCoord); + textureManager.getTexCoords(texture, vertices[2].texCoord); + textureManager.getTexCoords(texture, vertices[3].texCoord); + } + } + + +} // namespace gz::vk diff --git a/shape.hpp b/shape.hpp index 9eb139c..4a0da76 100644 --- a/shape.hpp +++ b/shape.hpp @@ -4,14 +4,26 @@ #include namespace gz::vk { + // defined in texture_manager.hpp + class TextureManager; class Shape { public: const std::vector& getVertices() const { return vertices; } const std::vector& getIndices() const { return indices; } + const std::string& getTexture() const { return texture; } + /** + * @brief Add an offset to all indices + */ void setIndexOffset(uint32_t offset); - void setNormalize(float width, float height); - + /** + * @brief Normalize the vertices, so that (1, 1) is (width, height) + */ + void normalizeVertices(float width, float height); + virtual void setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {}; + virtual void setTextureCoordinates(TextureManager& textureManager) {}; + virtual ~Shape() {}; protected: + std::string texture = "texture.png"; std::vector vertices; std::vector indices; @@ -19,7 +31,15 @@ namespace gz::vk { class Rectangle : public Shape { public: - Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color); + Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color, std::string&& texture); + void setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) override; + /** + * @brief Get the correct texture coordinates from a TextureManager + * @details + * If the texture is "atlas", the texture coordinates will remain at (0,0), (0,1), (1, 1), (1, 0) and + * thus show the entire atlas. + */ + void setTextureCoordinates(TextureManager& textureManager) override; private: float top, left; uint32_t width, height; diff --git a/texture_atlas.cpp b/texture_atlas.cpp new file mode 100644 index 0000000..ac07717 --- /dev/null +++ b/texture_atlas.cpp @@ -0,0 +1,208 @@ +#include "texture_atlas.hpp" + +#include "vulkan_instance.hpp" +#include "exceptions.hpp" + +#include "stb_image.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gz::vk { +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) +{ + // whole image + freeAreas.insert({ 0, 0, slotCountX, slotCountY}); + createImageResources(); + vk.registerCleanupCallback(std::bind(&TextureAtlas::cleanup, this)); + VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan(std::string("TextureAtlas-") + gz::toString(slotWidth) + "x" + gz::toString(slotHeight), + { &textureImage, &textureImageMemory, &textureImageView, &textureSampler }, + {})); +} + + +void TextureAtlas::cleanup() { + VulkanInstance::vLog("TextureAtlas::cleanup, textureSampler", reinterpret_cast(textureSampler), "textureImageView", reinterpret_cast(textureImageView)); + vkDestroySampler(vk.device, textureSampler, nullptr); + vkDestroyImageView(vk.device, textureImageView, nullptr); + vkDestroyImage(vk.device, textureImage, nullptr); + vkFreeMemory(vk.device, textureImageMemory, nullptr); +} + + +void TextureAtlas::createImageResources() { + vk.createImage(slotWidth * slotCountX, slotHeight * slotCountY, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_LINEAR, + 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); + 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; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = 1; + subresourceRange.baseArrayLayer = 0; + 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.createImageView(VK_FORMAT_R8G8B8A8_SRGB, textureImage, textureImageView, VK_IMAGE_ASPECT_COLOR_BIT); + vk.createTextureSampler(textureSampler); +} + + +// +// ADD TEXTURE +// +std::pair TextureAtlas::addTexture(uint8_t* pixels, uint16_t width, uint16_t height) { + if (freeAreas.empty()) { + throw Exception("No texture slots available", "TextureAtlas::addTexture"); + } + // needed slot count in x/y direction + uint16_t slotsX, slotsY; + // width / slotHeight (+ 1 if another slot is not fully filled) + slotsX = width / slotWidth + ((width % slotWidth != 0) ? 1 : 0); + slotsY = height / slotHeight + ((height % slotHeight != 0) ? 1 : 0); + + // find smallest area that fits the texture + auto freeArea = freeAreas.end(); + for (auto it = freeAreas.begin(); it != freeAreas.end(); it++) { + // if fits the texture + if (it->width >= slotsX and it->height >= slotsY) { + freeArea = it; + break; + } + } + // check if match + if (freeArea == freeAreas.end()) { + throw Exception("Not enough texture slots available to fit the texture", "TextureAtlas::addTexture"); + } + + // consume free area + auto node = freeAreas.extract(freeArea); + TextureImageArea& selectedArea = node.value(); + + // topleft slot for texture + uint16_t x = selectedArea.x; + uint16_t y = selectedArea.y; + // update selectedArea + uint16_t selectedAreaNewY = selectedArea.y + slotsY; + uint16_t selectedAreaNewWidth = slotsX; + uint16_t selectedAreaNewHeight = selectedArea.height - slotsY; + // if split needed + if (slotsX < selectedArea.width and slotsY < selectedArea.height) { + // split so that the size difference between the new free area and the remaining free area is maximal + TextureImageArea newFreeArea; + newFreeArea.x = selectedArea.x + slotsX; // right of new texture + newFreeArea.y = selectedArea.y; + newFreeArea.width = selectedArea.width - slotsX; + // try full height first first + newFreeArea.height = selectedArea.height; + + // size difference with (newArea.height == full height) < size difference with (newArea.height == textureHeight) + int sizeDiffNewAreaFullHeight = static_cast(newFreeArea.width * newFreeArea.height) - selectedAreaNewWidth * selectedAreaNewHeight; + int sizeDiffNewAreaTextureHeight = static_cast(newFreeArea.width * (selectedArea.height - slotsY)) - selectedArea.width * selectedAreaNewHeight; + if (sizeDiffNewAreaFullHeight * sizeDiffNewAreaFullHeight < sizeDiffNewAreaTextureHeight * sizeDiffNewAreaTextureHeight) { + newFreeArea.height = slotsY; + selectedAreaNewWidth = selectedArea.width; + } + // insert new area + freeAreas.insert(std::move(newFreeArea)); + } + + if (selectedAreaNewHeight > 0 and selectedAreaNewWidth > 0) { + // insert selected area with updated width, height and y + selectedArea.width = selectedAreaNewWidth; + selectedArea.height = selectedAreaNewHeight; + selectedArea.y = selectedAreaNewY; + 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, ")"); + blitTextureOnImage(pixels, x, y, width, height); + // topleft normalized: slot / slotCount + // bottomright normalized: (slot + (textureSize/slotCountize)) / slotCount + return { glm::vec2(static_cast(x) / slotCountX, static_cast(y) / slotCountY), + glm::vec2((static_cast(x) + static_cast(width) / slotWidth) / slotCountX, + (static_cast(y) + static_cast(height) / slotHeight) / slotCountY) }; +} + + +void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight) { + constexpr size_t BYTES_PER_PIXEL = 4; + VkDeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL; + VkBuffer stagingBuffer; + VkDeviceMemory 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.device, stagingBufferMemory, NO_OFFSET, imageSize, NO_FLAGS, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(vk.device, stagingBufferMemory); + + vk.transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vk.copyBufferToImage(stagingBuffer, textureImage, + static_cast(slotWidth * slotX), static_cast(slotHeight * slotY), // offset + 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.device, stagingBuffer, nullptr); + vkFreeMemory(vk.device, stagingBufferMemory, nullptr); +} + + +void TextureAtlas::mergeFreeAreas() { +begin: + VulkanInstance::vLog("TextureAtlas::mergeFreeAreas before merge:", freeAreas); + // 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++) { + for (auto it2 = freeAreas.begin(); it2 != freeAreas.end(); it2++) { + if (it == it2) { continue; } + // if same y and same height and next to each other + if (it->y == it2->y and it->height == it2->height and it2->x == it->x + it->width) { + auto area1 = freeAreas.extract(it); // node + area1.value().width += it2->width; + freeAreas.insert(std::move(area1)); + freeAreas.erase(it2); + goto begin; // start over since the iterators are invalid and new merges might be possible + } + // if same x and same with and below/above each other + if (it->x == it2->x and it->width == it2->width and it2->y == it->y + it->height) { + auto area1 = freeAreas.extract(it); // node + area1.value().height += it2->height; + freeAreas.insert(std::move(area1)); + freeAreas.erase(it2); + goto begin; // start over since the iterators are invalid and new merges might be possible + } + } + } + VulkanInstance::vLog("TextureAtlas::mergeFreeAreas after merge:", freeAreas); + +} + +std::string TextureAtlas::toString() const { + return "TextureAtlas(SlotSize: " + gz::toString(slotWidth) + "x" + gz::toString(slotHeight) + + ", SlotCount: " + gz::toString(slotCountX) + "x" + gz::toString(slotCountY) + ", FreeAreas: " + gz::toString(freeAreas) + ")"; +} + + +} // namespace gz diff --git a/texture_atlas.hpp b/texture_atlas.hpp new file mode 100644 index 0000000..2688fc0 --- /dev/null +++ b/texture_atlas.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include +#include +#include + + +namespace gz::vk { + /** + * @brief Describes an area in the textureImage that is not in use + */ + struct TextureImageArea { + // topleft slot + uint16_t x; + uint16_t y; + // in slots + uint16_t width; + uint16_t height; + std::string toString() const; + }; + /// strict weak ordering comparison, true if a < b + inline auto freeAreaCmp = [](const TextureImageArea& a, const TextureImageArea& b) { + // if a size < b size + if (a.width * a.height < b.width * b.height) { return true; } + if (a.width * a.height != b.width * b.height) { return false; } + // if a size == b size, return true if a is toplefter + if (a.x + a.y < b.x + b.y) { return true; } + if (a.x + a.y != b.x + b.y) { return false; } + // if both are same topleft return if a is lefter + return a.x < b.x; + }; + + /// Defined in vulkan_instance.hpp + class VulkanInstance; + class TextureAtlas { + public: + /** + * @brief Create a texture atlas + * @details + * -# @ref VulkanInstance::registerCleanupCallback "register" @ref cleanup() "cleanup callback" + * -# @ref createImageResources "create texture image, view and sampler" + * + * The textureImage will have the dimensions slotWidth * slotCountX, slotHeight * slotCountY + */ + TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY); + /** + * @brief Add a texture to the atlas + * @param textureWidth Width in pixels + * @param textureHeight Height in pixels + * @returns The topleft and bottomright texture coordinates in the image of the atlas + * @throws Exception when there is no space in the atlas to add the texture + */ + std::pair addTexture(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight); + const VkImageView& getTextureImageView() const { return textureImageView; } + const VkSampler& getTextureSampler() const { return textureSampler; } + + std::string toString() const; + + private: + VulkanInstance& vk; + /** + * @brief Destroy all vulkan objects owned by this object + */ + void cleanup(); + void blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight); + /** + * @brief Create textureImage, textureImageView and textureSampler + * @details + * the textureImage is created with missingTextureColor and then transitioned to SHADER_READ_ONLY_OPTIMAL layout. + */ + void createImageResources(); + VkImage textureImage; + VkDeviceMemory textureImageMemory; + VkImageView textureImageView; + VkSampler textureSampler; + + std::set freeAreas; + /** + * @todo implement merge + */ + void mergeFreeAreas(); + + + uint16_t slotWidth; + uint16_t slotHeight; + uint16_t slotCountX; + uint16_t slotCountY; + }; +} // namespace gz::vk diff --git a/texture_manager.cpp b/texture_manager.cpp new file mode 100644 index 0000000..8f9fb5c --- /dev/null +++ b/texture_manager.cpp @@ -0,0 +1,145 @@ +#include "texture_manager.hpp" +#include "exceptions.hpp" +#include "vulkan_instance.hpp" + +#include +#include + +namespace gz::vk { + TextureManager::TextureManager(VulkanInstance& instance) + : vk(instance) + { + vk.registerCleanupCallback(std::bind(&TextureManager::cleanup, this)); + atlases.insert({ 0, TextureAtlas(vk, 24, 24, 24, 24) }); + createDescriptorSetLayout(); + createDescriptorPool(); + createDescriptorSet(); + VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan("TextureManager", + { &descriptorSetLayout, &descriptorPool, &descriptorSet }, + {})); + } + + + void TextureManager::cleanup() { + /* vkFreeDescriptorSets(vk.device, descriptorPool, 1, &descriptorSet); */ + vkDestroyDescriptorSetLayout(vk.device, descriptorSetLayout, NO_ALLOC); + vkDestroyDescriptorPool(vk.device, descriptorPool, NO_ALLOC); + } + + + void TextureManager::getTexCoords(const std::string& textureName, glm::vec2& texCoords) { + if (!textureInfos.contains(textureName)) { + loadTextureFromFile(textureName); + } + TextureInfo& texI = textureInfos[textureName]; + VulkanInstance::vLog("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); + } + + + void TextureManager::loadTextureFromFile(const std::string& textureName) { + int textureWidth, textureHeight, textureChannels; + stbi_uc* pixels = stbi_load(("textures/" + textureName).c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha); + if (!pixels) { + throw Exception("Failed to load texture: textures/" + textureName, "TextureManager::loadTexture"); + } + TextureInfo& texI = textureInfos[textureName]; + 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); + texI.texCoordTopLeft = std::move(texCoords.first); + texI.texCoordBottomRight = std::move(texCoords.second); + VulkanInstance::vLog("TextureManager::loadTextureFromFile: After loading:", atlases.at(0)); + } + + +// +// DESCRIPTOR SET +// +void TextureManager::createDescriptorSetLayout() { + // combined image sampler + VkDescriptorSetLayoutBinding samplerLayoutBinding{}; + samplerLayoutBinding.binding = 0; + samplerLayoutBinding.descriptorCount = 1; + samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + /* samplerLayoutBinding.pImmutableSamplers = nullptr; */ + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; + descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCI.bindingCount = 1; + descriptorSetLayoutCI.pBindings = &samplerLayoutBinding; + + VkResult result = vkCreateDescriptorSetLayout(vk.device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout); + if (result != VK_SUCCESS) { + throw getVkException(result, "Could not create descriptorSetLayout", "TextureManager::createDescriptorSetLayout"); + } + /* vLog("createDescriptorSetLayout: Created descriptor set layout."); */ +} + + +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].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); + + VkDescriptorPoolCreateInfo poolCI{}; + poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolCI.poolSizeCount = static_cast(poolSizes.size()); + poolCI.pPoolSizes = poolSizes.data(); + poolCI.maxSets = 1; + VkResult result = vkCreateDescriptorPool(vk.device, &poolCI, nullptr, &descriptorPool); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create descriptor pool", "TextureManager::createDescriptorPool"); + } + /* vLog("createDescriptorPool: Created descriptor pool."); */ +} + + +void TextureManager::createDescriptorSet() { + /* std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); */ + VkDescriptorSetAllocateInfo setAI{}; + setAI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + setAI.descriptorPool = descriptorPool; + /* setAI.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); */ + /* setAI.pSetLayouts = layouts.data(); */ + setAI.descriptorSetCount = 1; + setAI.pSetLayouts = &descriptorSetLayout; + + /* descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); */ + /* VkResult result = vkAllocateDescriptorSets(vk.device, &setAI, descriptorSets.data()); */ + VkResult result = vkAllocateDescriptorSets(vk.device, &setAI, &descriptorSet); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create descriptor set", "TextureManager::createDescriptorPool"); + } + + // configure sets + std::vector imageInfos; + for (auto it = atlases.cbegin(); it != atlases.cend(); it++) { + imageInfos.emplace_back(); + imageInfos.back().imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfos.back().imageView = it->second.getTextureImageView(); + imageInfos.back().sampler = it->second.getTextureSampler(); + } + + std::array descriptorW{}; + descriptorW[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorW[0].dstSet = descriptorSet; + descriptorW[0].dstBinding = 0; + descriptorW[0].dstArrayElement = 0; + descriptorW[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorW[0].descriptorCount = static_cast(imageInfos.size()); + descriptorW[0].pImageInfo = imageInfos.data(); + + uint32_t descriptorWriteCount = static_cast(descriptorW.size()); + uint32_t descriptorCopyCount = 0; + vkUpdateDescriptorSets(vk.device, descriptorWriteCount, descriptorW.data(), descriptorCopyCount, nullptr); + /* } */ + /* vLog("createDescriptorSets: Created descriptor sets."); */ +} +} // namespace gz::vk diff --git a/texture_manager.hpp b/texture_manager.hpp new file mode 100644 index 0000000..4c5de52 --- /dev/null +++ b/texture_manager.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "texture_atlas.hpp" + +#include "stb_image.h" +#include +#include +#include + +namespace gz::vk { + struct TextureInfo { + uint32_t atlas; + glm::vec2 texCoordTopLeft; + glm::vec2 texCoordBottomRight; + }; + /// Defined in vulkan_instance.hpp + class VulkanInstance; + class TextureManager { + public: + /** + * @brief Create a texture manager + * @details + * -# @ref VulkanInstance::registerCleanupCallback "register" @ref cleanup() "cleanup callback" + * -# @ref createDescriptor "create descriptor set layout, pool and set + */ + TextureManager(VulkanInstance& instance); + void getTexCoords(const std::string& textureName, glm::vec2& texCoords); + const VkDescriptorSet& getDescriptorSet() const { return descriptorSet; } + const VkDescriptorSetLayout& getDescriptorSetLayout() const { return descriptorSetLayout; } + + + private: + void cleanup(); + void loadTextureFromFile(const std::string& textureName); + std::unordered_map atlases; + util::unordered_string_map textureInfos; + VulkanInstance& vk; + /** + * @name Create desciptors + * @details These functions create a desciptor with bindings for + * -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) at binding 0 + * @{ + */ + VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorPool descriptorPool; + VkDescriptorSet descriptorSet; + /* std::vector descriptorSets; */ + /** + * @brief Create a descriptor set layout + * @details + * Bindings: + * -0 COMBINED_IMAGE_SAMPLER + */ + void createDescriptorSetLayout(); + void createDescriptorPool(); + void createDescriptorSet(); + /* const uint32_t bindingCombinedImageSampler = 1; */ + + /** + * @} + */ + + + }; +} // namespace gz::vk diff --git a/vertex.cpp b/vertex.cpp index bdc489b..a6b7f31 100644 --- a/vertex.cpp +++ b/vertex.cpp @@ -24,7 +24,7 @@ VkFormat getVkFormat() { // template std::string Vertex::toString() const { - return "pos: " + gz::toString(pos) + ", color: " + gz::toString(color) + ", texCoords: "; // + gz::toString(texCoord); + return "pos: <" + gz::toString(pos) + ", color: " + gz::toString(color) + ", texCoords: " + gz::toString(texCoord) + ">"; }; diff --git a/vk_layer_settings.txt b/vk_layer_settings.txt index b395f32..1cc9298 100644 --- a/vk_layer_settings.txt +++ b/vk_layer_settings.txt @@ -1,2 +1,2 @@ # https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/master/layers/vk_layer_settings.txt -khronos_validation.enables = VK_LAYER_ENABLES=VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT;VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD +# khronos_validation.enables = VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT;VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD diff --git a/vulkan_instance.cpp b/vulkan_instance.cpp index 9a9578c..57bd473 100644 --- a/vulkan_instance.cpp +++ b/vulkan_instance.cpp @@ -4,7 +4,13 @@ #include "font.hpp" #include "vk_enum_string.h" +#include +#include +#include +#include +#include #include +#include #include #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" @@ -29,15 +35,11 @@ const unsigned int HEIGHT = 600; const std::vector extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, - VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME + /* VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME */ }; const std::vector instanceExtensions = { }; -using gz::Exception; -using gz::VkException; -using gz::VkUserError; -using gz::getVkException; // @@ -63,7 +65,9 @@ constexpr VkDebugUtilsMessageTypeFlagsEXT debugMessageTypes = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; const std::vector validationFeaturesEnable { - VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT + VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT, + VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT, + VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT }; const VkValidationFeaturesEXT validationFeatures { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, // sType @@ -78,8 +82,6 @@ const VkValidationFeaturesEXT validationFeatures { /* VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT | */ /* VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; */ -constexpr gz::Color VULKAN_MESSAGE_PREFIX_COLOR = gz::Color::BBLUE; - constexpr VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCI { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, // sType &validationFeatures, // pNext @@ -173,14 +175,14 @@ bool validationLayersSupported() { // void VulkanInstance::createInstance() { - VkApplicationInfo appInfo; + VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pEngineName = "Glowzwiebel Engine"; appInfo.pApplicationName = "Hallo Dreieck"; appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.apiVersion = VK_API_VERSION_1_0; + appInfo.apiVersion = VK_API_VERSION_1_3; - VkInstanceCreateInfo instanceCI; + VkInstanceCreateInfo instanceCI{}; instanceCI.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCI.pApplicationInfo = &appInfo; instanceCI.pNext = &debugUtilsMessengerCI; @@ -241,61 +243,138 @@ void VulkanInstance::createInstance() { } -gz::Log VulkanInstance::vLog("vulkan.log", false, true, "Vulkan", VULKAN_MESSAGE_PREFIX_COLOR); + +bool VulkanInstance::lastColorWhite; +std::vector VulkanInstance::objectsUsingVulkan; +void VulkanInstance::registerObjectUsingVulkan(const ObjectUsingVulkan& obj) { + objectsUsingVulkan.push_back(obj); +} +const std::regex reAddress(R"(0x[0-9a-f]+)"); +std::string VulkanInstance::handleOwnerString(100, ' '); // reserve size 100 +void VulkanInstance::getHandleOwnerString(std::string_view message) { + handleOwnerString.clear(); + re::svmatch match; + if (re::regex_search(message, match, reAddress)) { + handleOwnerString = "Handle ownerships: [ "; + bool foundHandle = false; + for (auto it = match.begin(); it != match.end(); it++) { + uint64_t address; + std::stringstream ss; + ss << std::hex << it->str(); + ss >> address; + for (auto& obj : objectsUsingVulkan) { + /* vLog(address, obj.handles); */ + if (obj.handles.contains(address)) { + handleOwnerString += it->str() + " - " + obj.objectName; + address = 0; + foundHandle = true; + } + } + if (address != 0) { + handleOwnerString += "Unknown"; + } + handleOwnerString += ", "; + } + if (foundHandle) { + handleOwnerString.erase(handleOwnerString.size()-2); + handleOwnerString += " ]"; + } + else { + handleOwnerString.clear(); + } + } +} + +gz::Log VulkanInstance::vLog("vulkan.log", true, true, "Vulkan", VULKAN_MESSAGE_PREFIX_COLOR, VULKAN_MESSAGE_TIME_COLOR); VKAPI_ATTR VkBool32 VKAPI_CALL VulkanInstance::debugLog( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverety, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { - - gz::Color messageColor; - switch(messageSeverety) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - messageColor = gz::Color::RESET; - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - messageColor = gz::Color::WHITE; - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - messageColor = gz::Color::YELLOW; - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - messageColor = gz::Color::RED; - break; - default: - messageColor = gz::Color::RESET; - } + // Set message color std::string type; - gz::Color typeColor; + std::vector colors; switch(messageType) { case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: - type = "General"; - typeColor = gz::Color::GREEN; + type = "General:"; + colors.push_back(gz::Color::GREEN); break; case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: - type = "Performance"; - typeColor = gz::Color::CYAN; + type = "Performance:"; + colors.push_back(gz::Color::CYAN); break; case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: - type = "Validation"; - typeColor = gz::Color::MAGENTA; + type = "Validation:"; + colors.push_back(gz::Color::MAGENTA); break; default: - type = "None"; - typeColor = gz::Color::WHITE; + type = "None:"; + colors.push_back(gz::Color::WHITE); } - size_t offset = 0; - if (strcmp(pCallbackData->pMessageIdName, "UNASSIGNED-DEBUG-PRINTF") == 0) { - type = "Shader"; - typeColor = gz::Color::YELLOW; - messageColor = gz::Color::RESET; - std::string_view message(pCallbackData->pMessage); - offset = message.find_first_of('|'); - if (offset == std::string::npos) { offset = 0; } - else { offset += 2; } // skip "| " + + // color for second keyword / whole message if not validation message + switch(messageSeverety) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + colors.push_back(gz::Color::RESET); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + colors.push_back(gz::Color::WHITE); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + colors.push_back(gz::Color::BG_YELLOW); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + colors.push_back(gz::Color::BG_RED); + break; + default: + colors.push_back(gz::Color::RESET); } - vLog.clog(typeColor, std::move(type), messageColor, pCallbackData->pMessage+offset); + + + std::string_view message(pCallbackData->pMessage); + std::string_view messageIdName(pCallbackData->pMessageIdName); + // extra differenciation between different validation messages + // is validation or performance message: get type from brackets, print owner of any pointers + if (messageType & (VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)) { + // if cant be found npos + 1 = 0 + size_t startAt = message.find(']') + 1; + if (messageIdName.starts_with("UNASSIGNED-DEBUG-PRINTF")) { + type = "Shader:"; + colors[0] = gz::Color::LI_CYAN; + startAt = message.find_first_of('|'); + if (startAt == std::string::npos) { startAt = 0; } + else { startAt += 2; } // skip "| " + } + else if (messageIdName.starts_with("UNASSIGNED-BestPractices")) { + type = "Best Practice:"; + colors[0] = gz::Color::LI_GREEN; + } + else if (messageIdName.starts_with("SYNC-HAZARD")) { + type = "Synchronization"; + colors[0] = gz::Color::LI_RED; + } + message = std::string_view(message.begin() + startAt, message.end()); + + colors.push_back(colors[1]); // color for messageIdName, use also for brackets + colors.push_back(colors[1]); + // color for actual message + if (lastColorWhite) { + colors.push_back(gz::Color::RESET); + } + else { + colors.push_back(gz::Color::WHITE); + + } + // color for handleOwnerString + colors.push_back(gz::Color::LI_CYAN); + getHandleOwnerString(message); + vLog.clog(colors, std::move(type), "[", messageIdName, "]", message, handleOwnerString); + } + else { + vLog.clog(colors, std::move(type), message); + } + lastColorWhite = !lastColorWhite; return VK_FALSE; } @@ -306,6 +385,13 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanInstance::debugLog( // // DEVICE // +PhysicalDeviceFeatures VulkanInstance::getRequiredFeatures() const { + PhysicalDeviceFeatures requiredFeatures; + requiredFeatures.f13.synchronization2 = VK_TRUE; + return requiredFeatures; +} + + void VulkanInstance::selectPhysicalDevice() { uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); @@ -330,7 +416,7 @@ void VulkanInstance::selectPhysicalDevice() { physicalDevice = devices[bestGPUIndex]; vkGetPhysicalDeviceProperties(physicalDevice, &phDevProperties); - vkGetPhysicalDeviceFeatures(physicalDevice, &phDevFeatures); + vkGetPhysicalDeviceFeatures2(physicalDevice, &phDevFeatures.f); qFamilyIndices = findQueueFamilies(physicalDevice); vLog("Selected GPU:", phDevProperties.deviceName, "with the following queue family indices:", qFamilyIndices); } @@ -376,6 +462,17 @@ unsigned int VulkanInstance::rateDevice(VkPhysicalDevice device) { return 0; } + // supports all features + PhysicalDeviceFeatures requiredFeatures = getRequiredFeatures(); + PhysicalDeviceFeatures features; + vLog("features sType", STR_VK_STRUCTURE_TYPE(features.f.sType)); + vkGetPhysicalDeviceFeatures2(device, &features.f); + if (!features.requiredFeaturesAvailable(requiredFeatures)) { + return 0; + } + // optional features + if (features.f.features.samplerAnisotropy == VK_TRUE) { score += SCORE_HAS_FEATURE; } + // swap chain support SwapChainSupport scDetails = querySwapChainSupport(device); if (scDetails.formats.empty() or scDetails.presentModes.empty()) { @@ -389,9 +486,6 @@ unsigned int VulkanInstance::rateDevice(VkPhysicalDevice device) { score += memProperties.memoryHeaps[i].size / 1e6; } - VkPhysicalDeviceFeatures features{}; - vkGetPhysicalDeviceFeatures(device, &features); - if (features.samplerAnisotropy == VK_TRUE) { score += SCORE_HAS_FEATURE; } vLog("rateDevice: GPU: ", properties.deviceName, " - Score: ", score); return score; @@ -400,7 +494,7 @@ unsigned int VulkanInstance::rateDevice(VkPhysicalDevice device) { void VulkanInstance::setValidSettings() { // anisotropic filtering - settings.setAllowedValues("anisotropy_enable", { false, vkBool2Bool(phDevFeatures.samplerAnisotropy) }, gz::SM_LIST); + settings.setAllowedValues("anisotropy_enable", { false, vkBool2Bool(phDevFeatures.f.features.samplerAnisotropy) }, gz::SM_LIST); settings.setAllowedValues("max_anisotropy", { 1.0f, phDevProperties.limits.maxSamplerAnisotropy }, gz::SM_RANGE); } @@ -481,14 +575,16 @@ void VulkanInstance::createLogicalDevice() { deviceQueueCI.back().pQueuePriorities = &qPriority; } - VkPhysicalDeviceFeatures deviceFeatures{}; - deviceFeatures.samplerAnisotropy = bool2VkBool(settings.getOr("anisotropy_enable", false)); + PhysicalDeviceFeatures enabledFeatures = getRequiredFeatures(); + // optional features + enabledFeatures.f.features.samplerAnisotropy = bool2VkBool(settings.getOr("anisotropy_enable", false)); VkDeviceCreateInfo deviceCI{}; deviceCI.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCI.pQueueCreateInfos = deviceQueueCI.data(); deviceCI.queueCreateInfoCount = static_cast(deviceQueueCI.size()); - deviceCI.pEnabledFeatures = &deviceFeatures; + deviceCI.pNext = &enabledFeatures.f; + /* deviceCI.pEnabledFeatures = &deviceFeatures.f; */ deviceCI.enabledExtensionCount = static_cast(extensions.size()); deviceCI.ppEnabledExtensionNames = extensions.data(); @@ -528,7 +624,7 @@ void VulkanInstance::createSurface() { // SWAP CHAIN // SwapChainSupport VulkanInstance::querySwapChainSupport(VkPhysicalDevice physicalDevice) { - SwapChainSupport scDetails; + SwapChainSupport scDetails{}; uint32_t formatCount; vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr); @@ -608,11 +704,13 @@ void VulkanInstance::createSwapChain() { swapChainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; /* QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); */ + // must live until vkCreateSwapchainKHR + std::vector qIndices; if (qFamilyIndices.graphicsFamily.value() != qFamilyIndices.presentFamily.value()) { swapChainCI.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - swapChainCI.queueFamilyIndexCount = 2; - uint32_t qIndices[] = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.presentFamily.value() }; - swapChainCI.pQueueFamilyIndices = qIndices; + qIndices = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.presentFamily.value() }; + swapChainCI.queueFamilyIndexCount = static_cast(qIndices.size()); + swapChainCI.pQueueFamilyIndices = qIndices.data(); } else { swapChainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -644,56 +742,54 @@ void VulkanInstance::recreateSwapChain() { while (width == 0 || height == 0) { // minimized glfwGetFramebufferSize(window, &width, &height); glfwWaitEvents(); - vLog("recreateSwapChain: new framebuffer size:", width, height); } + vLog("recreateSwapChain: new framebuffer size:", width, height); vkDeviceWaitIdle(device); cleanupSwapChain(); createSwapChain(); - createImageViews(); - createRenderPassBegin(); - createRenderPassEnd(); + createSwapChainImageViews(); /* createDescriptorSetLayout(); */ /* createGraphicsPipeline("shaders/vert.spv", "shaders/frag.spv", true, renderPass1, pipelines[PL_3D]); */ - createGraphicsPipeline("shaders/vert2D.spv", "shaders/frag2D.spv", false, renderPassBegin, pipelines[PL_2D]); - createDepthImageAndView(); - recreateFramebuffers(); + /* createGraphicsPipeline("shaders/vert2D.spv", "shaders/frag2D.spv", false, renderPassBegin, pipelines[PL_2D]); */ + /* createDepthImageAndView(); */ + /* recreateFramebuffers(); */ /* createUniformBuffers(); */ createCommandBuffers(commandBuffersBegin); createCommandBuffers(commandBuffersEnd); /* createDescriptorPool(); */ /* createDescriptorSets(); */ + for (auto& f : scRecreateCallbacks) { + f(); + } + vLog("recreateSwapChain: Updating all ObjectsUsingVulkan"); + for (auto& obj : VulkanInstance::objectsUsingVulkan) { + obj.updateHandles(); + } } void VulkanInstance::cleanupSwapChain() { - vkDestroyImageView(device, depthImageView, nullptr); - vkDestroyImage(device, depthImage, nullptr); - vkFreeMemory(device, depthImageMemory, nullptr); + /* vkDestroyImageView(device, depthImageView, nullptr); */ + /* vkDestroyImage(device, depthImage, nullptr); */ + /* vkFreeMemory(device, depthImageMemory, nullptr); */ - vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); - vkDestroyDescriptorPool(device, descriptorPool, nullptr); - - for (auto& framebuffer : scFramebuffers) { - destroyFramebuffers(framebuffer.first); - } - - // destroy pipelines - auto it = pipelines.begin(); - while (it != pipelines.end()) { - it = pipelines.erase(it, device); - } - - vkDestroyRenderPass(device, renderPassBegin, nullptr); - vkDestroyRenderPass(device, renderPassEnd, nullptr); for (auto& imageView : scImageViews) { vkDestroyImageView(device, imageView, nullptr); } vkDestroySwapchainKHR(device, swapChain, nullptr); - } +void VulkanInstance::registerSwapChainRecreateCallback(std::function callbackF) { + scRecreateCallbacks.push_back(callbackF); +} +void VulkanInstance::registerCleanupCallback(std::function callbackF) { + cleanupCallbacks.push_back(callbackF); +} + + + // // IMAGE VIEW // @@ -719,7 +815,7 @@ void VulkanInstance::createImageView(VkFormat format, VkImage& image, VkImageVie } -void VulkanInstance::createImageViews() { +void VulkanInstance::createSwapChainImageViews() { scImageViews.resize(scImages.size()); for (unsigned int i = 0; i < scImageViews.size(); i++) { createImageView(scImageFormat, scImages[i], scImageViews[i], VK_IMAGE_ASPECT_COLOR_BIT); @@ -728,246 +824,113 @@ void VulkanInstance::createImageViews() { } -// -// RENDER PASS -// -void VulkanInstance::createRenderPassBegin() { - VkAttachmentDescription colorBlendAttachment{}; - colorBlendAttachment.format = scImageFormat; - colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference colorAttachmentRef{}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - /* VkAttachmentDescription depthAttachment{}; */ - /* depthAttachment.format = findDepthFormat(); */ - /* depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; */ - /* depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */ - /* depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */ - /* depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; */ - /* depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */ - /* depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; */ - /* depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */ - - /* VkAttachmentReference depthAttachmentRef{}; */ - /* depthAttachmentRef.attachment = 1; */ - /* depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */ - - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - /* subpass.pDepthStencilAttachment = &depthAttachmentRef; */ - - VkSubpassDependency dependency{}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - /* VkSubpassDependency dependency{}; */ - /* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */ - /* dependency.dstSubpass = 0; */ - /* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ - /* dependency.srcAccessMask = 0; */ - /* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ - /* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */ - - /* std::array attachments = { colorBlendAttachment, depthAttachment }; */ - std::array attachments = { colorBlendAttachment }; - VkRenderPassCreateInfo renderPassCI{}; - renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCI.attachmentCount = static_cast(attachments.size()); - renderPassCI.pAttachments = attachments.data(); - renderPassCI.subpassCount = 1; - renderPassCI.pSubpasses = &subpass; - renderPassCI.dependencyCount = 1; - renderPassCI.pDependencies = &dependency; - /* renderPassCI.dependencyCount = 0; */ - /* renderPassCI.pDependencies = nullptr; */ - /* renderPassCI.correlatedViewMaskCount = 0; */ - /* renderPassCI.pCorrelatedViewMasks = nullptr; */ - VkResult result = vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPassBegin); - if (result != VK_SUCCESS) { - throw getVkException(result, "Could not create renderPassBegin", "createRenderPassBegin"); - } - vLog("createRenderPass: Created renderpassBegin."); -} - -void VulkanInstance::createRenderPassEnd() { - VkAttachmentDescription colorBlendAttachment{}; - colorBlendAttachment.format = scImageFormat; - colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 0; - /* subpass.pColorAttachments = &colorAttachmentRef; */ - /* subpass.pDepthStencilAttachment = &depthAttachmentRef; */ - - VkSubpassDependency dependency{}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - /* VkSubpassDependency dependency{}; */ - /* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */ - /* dependency.dstSubpass = 0; */ - /* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ - /* dependency.srcAccessMask = 0; */ - /* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */ - /* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */ - - /* std::array attachments = { colorBlendAttachment, depthAttachment }; */ - std::array attachments = { colorBlendAttachment }; - VkRenderPassCreateInfo renderPassCI{}; - renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCI.attachmentCount = static_cast(attachments.size()); - renderPassCI.pAttachments = attachments.data(); - renderPassCI.subpassCount = 1; - renderPassCI.pSubpasses = &subpass; - renderPassCI.dependencyCount = 0; - /* renderPassCI.pDependencies = &dependency; */ - /* renderPassCI.dependencyCount = 0; */ - /* renderPassCI.pDependencies = nullptr; */ - /* renderPassCI.correlatedViewMaskCount = 0; */ - /* renderPassCI.pCorrelatedViewMasks = nullptr; */ - VkResult result = vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPassEnd); - if (result != VK_SUCCESS) { - throw getVkException(result, "Could not create renderPassEnd", "createRenderPassEnd"); - } - vLog("createRenderPass: Created renderPassEnd."); - -} - - // // DESCRIPTORS // -void VulkanInstance::createDescriptorSetLayout() { - // 1) uniform buffer object - VkDescriptorSetLayoutBinding uboLayoutBinding{}; - uboLayoutBinding.binding = bindingUniformBuffer; - uboLayoutBinding.descriptorCount = 1; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - /* uboLayoutBinding.pImmutableSamplers = nullptr; */ - - - // 2) combined image sampler - VkDescriptorSetLayoutBinding samplerLayoutBinding{}; - samplerLayoutBinding.binding = bindingCombinedImageSampler; - samplerLayoutBinding.descriptorCount = 1; - samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - /* samplerLayoutBinding.pImmutableSamplers = nullptr; */ - - std::array bindings = { uboLayoutBinding, samplerLayoutBinding }; - +void VulkanInstance::createDescriptorSetLayout(std::vector bindings, VkDescriptorSetLayout& layout) { VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptorSetLayoutCI.bindingCount = static_cast(bindings.size()); descriptorSetLayoutCI.pBindings = bindings.data(); - VkResult result = vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout); + VkResult result = vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &layout); if (result != VK_SUCCESS) { throw getVkException(result, "Could not create descriptorSetLayout", "createDescriptorSetLayout"); } - vLog("createDescriptorSetLayout: Created descriptor set layout."); + /* vLog("createDescriptorSetLayout: Created descriptor set layout."); */ } -void VulkanInstance::createDescriptorPool() { - std::array poolSizes; - poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); - poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - poolSizes[1].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); - VkDescriptorPoolCreateInfo poolCI{}; - poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolCI.poolSizeCount = static_cast(poolSizes.size()); - poolCI.pPoolSizes = poolSizes.data(); - poolCI.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); - VkResult result = vkCreateDescriptorPool(device, &poolCI, nullptr, &descriptorPool); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create descriptor pool", "createDescriptorPool"); - } - vLog("createDescriptorPool: Created descriptor pool."); -} +/* VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; */ +/* descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; */ +/* descriptorSetLayoutCI.bindingCount = static_cast(bindings.size()); */ +/* descriptorSetLayoutCI.pBindings = bindings.data(); */ -void VulkanInstance::createDescriptorSets() { - std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); - VkDescriptorSetAllocateInfo setAI{}; - setAI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - setAI.descriptorPool = descriptorPool; - setAI.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); - setAI.pSetLayouts = layouts.data(); +/* VkResult result = vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout); */ +/* if (result != VK_SUCCESS) { */ +/* throw getVkException(result, "Could not create descriptorSetLayout", "createDescriptorSetLayout"); */ +/* } */ +/* vLog("createDescriptorSetLayout: Created descriptor set layout."); */ +/* } */ - descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); - VkResult result = vkAllocateDescriptorSets(device, &setAI, descriptorSets.data()); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create descriptor sets", "createDescriptorPool"); - } - // configure sets - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - VkDescriptorBufferInfo bufferI{}; - // TODO - /* bufferI.buffer = uniformBuffers[i]; */ - bufferI.offset = 0; - bufferI.range = VK_WHOLE_SIZE; // sizeof(UniformBufferObject); +/* void VulkanInstance::createDescriptorPool() { */ +/* std::array poolSizes; */ +/* poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; */ +/* poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); */ +/* poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; */ +/* poolSizes[1].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); */ - VkDescriptorImageInfo imageI{}; - imageI.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageI.imageView = textureImageView; - imageI.sampler = textureSampler; +/* VkDescriptorPoolCreateInfo poolCI{}; */ +/* poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; */ +/* poolCI.poolSizeCount = static_cast(poolSizes.size()); */ +/* poolCI.pPoolSizes = poolSizes.data(); */ +/* poolCI.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); */ +/* VkResult result = vkCreateDescriptorPool(device, &poolCI, nullptr, &descriptorPool); */ +/* if (result != VK_SUCCESS) { */ +/* throw getVkException(result, "Failed to create descriptor pool", "createDescriptorPool"); */ +/* } */ +/* vLog("createDescriptorPool: Created descriptor pool."); */ +/* } */ - std::array descriptorW{}; - descriptorW[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorW[0].dstSet = descriptorSets[i]; - descriptorW[0].dstBinding = bindingUniformBuffer; - descriptorW[0].dstArrayElement = 0; - descriptorW[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorW[0].descriptorCount = 1; - descriptorW[0].pBufferInfo = &bufferI; - /* descriptorW[0].pImageInfo = nullptr; */ - /* descriptorW[0].pTexelBufferView = nullptr; */ - descriptorW[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorW[1].dstSet = descriptorSets[i]; - descriptorW[1].dstBinding = bindingCombinedImageSampler; - descriptorW[1].dstArrayElement = 0; - descriptorW[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorW[1].descriptorCount = 1; - /* descriptorW[1].pBufferInfo = &bufferI; */ - descriptorW[1].pImageInfo = &imageI; - /* descriptorW[1].pTexelBufferView = nullptr; */ +/* void VulkanInstance::createDescriptorSets() { */ +/* std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); */ +/* VkDescriptorSetAllocateInfo setAI{}; */ +/* setAI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; */ +/* setAI.descriptorPool = descriptorPool; */ +/* setAI.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); */ +/* setAI.pSetLayouts = layouts.data(); */ + +/* descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); */ +/* VkResult result = vkAllocateDescriptorSets(device, &setAI, descriptorSets.data()); */ +/* if (result != VK_SUCCESS) { */ +/* throw getVkException(result, "Failed to create descriptor sets", "createDescriptorPool"); */ +/* } */ + +/* // configure sets */ +/* for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { */ +/* VkDescriptorBufferInfo bufferI{}; */ +/* // TODO */ +/* /1* bufferI.buffer = uniformBuffers[i]; *1/ */ +/* bufferI.offset = 0; */ +/* bufferI.range = VK_WHOLE_SIZE; // sizeof(UniformBufferObject); */ + +/* VkDescriptorImageInfo imageI{}; */ +/* imageI.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; */ +/* imageI.imageView = textureImageView; */ +/* imageI.sampler = textureSampler; */ + +/* std::array descriptorW{}; */ +/* descriptorW[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; */ +/* descriptorW[0].dstSet = descriptorSets[i]; */ +/* descriptorW[0].dstBinding = bindingUniformBuffer; */ +/* descriptorW[0].dstArrayElement = 0; */ +/* descriptorW[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; */ +/* descriptorW[0].descriptorCount = 1; */ +/* descriptorW[0].pBufferInfo = &bufferI; */ +/* /1* descriptorW[0].pImageInfo = nullptr; *1/ */ +/* /1* descriptorW[0].pTexelBufferView = nullptr; *1/ */ + +/* descriptorW[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; */ +/* descriptorW[1].dstSet = descriptorSets[i]; */ +/* descriptorW[1].dstBinding = bindingCombinedImageSampler; */ +/* descriptorW[1].dstArrayElement = 0; */ +/* descriptorW[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; */ +/* descriptorW[1].descriptorCount = 1; */ +/* /1* descriptorW[1].pBufferInfo = &bufferI; *1/ */ +/* descriptorW[1].pImageInfo = &imageI; */ +/* /1* descriptorW[1].pTexelBufferView = nullptr; *1/ */ - uint32_t descriptorWriteCount = static_cast(descriptorW.size()); - uint32_t descriptorCopyCount = 0; - vkUpdateDescriptorSets(device, descriptorWriteCount, descriptorW.data(), descriptorCopyCount, nullptr); - } - vLog("createDescriptorSets: Created descriptor sets."); -} +/* uint32_t descriptorWriteCount = static_cast(descriptorW.size()); */ +/* uint32_t descriptorCopyCount = 0; */ +/* vkUpdateDescriptorSets(device, descriptorWriteCount, descriptorW.data(), descriptorCopyCount, nullptr); */ +/* } */ +/* vLog("createDescriptorSets: Created descriptor sets."); */ +/* } */ // @@ -1138,7 +1101,7 @@ void VulkanInstance::createDescriptorSets() { template -void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline) { +void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline) { auto vertShaderCode = readFile(vertexShader); auto fragShaderCode = readFile(fragmentShader); @@ -1265,8 +1228,8 @@ void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, con pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; /* pipelineLayoutCI.pushConstantRangeCount = 0; */ /* pipelineLayoutCI.pPushConstantRanges = nullptr; */ - /* pipelineLayoutCI.setLayoutCount = 1; */ - /* pipelineLayoutCI.pSetLayouts = &descriptorSetLayout; */ + pipelineLayoutCI.setLayoutCount = static_cast(descriptorSetLayouts.size()); + pipelineLayoutCI.pSetLayouts = descriptorSetLayouts.data(); VkResult result = vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipeline.layout); if (result != VK_SUCCESS) { throw getVkException(result, "Failed to create pipeline layout", "createGraphicsPipeline"); @@ -1305,8 +1268,8 @@ void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, con vLog("createGraphicsPipeline: Created graphics pipeline."); } -template void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); -template void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); +template void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); +template void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); // // BUFFER @@ -1461,23 +1424,7 @@ template void VulkanInstance::createIndexBuffer(size_t indexCount, VkB // // FRAMEBUFFERS // -int VulkanInstance::createFramebuffers(std::vector& imageViews, VkRenderPass& renderPass) { - if (framebufferIDs.empty()) { - int MAX_FRAMEBUFFER_COUNT = 32; - for (int i = 0; i < MAX_FRAMEBUFFER_COUNT; i++) { - framebufferIDs.insert(i); - } - } - int id = *framebufferIDs.begin(); - framebufferIDs.erase(id); - scFramebuffers[id].second = renderPass; - createFramebuffers_(scFramebuffers[id].first, imageViews, renderPass); - vLog("createFramebuffers: Created frame buffers, id:", id); - return id; -} - - -void VulkanInstance::createFramebuffers_(std::vector& framebuffers, std::vector& imageViews, VkRenderPass& renderPass) { +void VulkanInstance::createFramebuffers(std::vector& framebuffers, std::vector& imageViews, VkRenderPass& renderPass) { framebuffers.resize(scImageViews.size()); VkFramebufferCreateInfo framebufferCI{}; @@ -1487,7 +1434,7 @@ void VulkanInstance::createFramebuffers_(std::vector& framebuffer framebufferCI.height = scExtent.height; framebufferCI.layers = 1; - for (unsigned int i = 0; i < scImageViews.size(); i++) { + for (size_t i = 0; i < framebuffers.size(); i++) { // TODO include depth image view for 3D std::vector attachments = { imageViews[i] }; //, depthImageView }; framebufferCI.pAttachments = attachments.data(); @@ -1498,29 +1445,13 @@ void VulkanInstance::createFramebuffers_(std::vector& framebuffer throw getVkException(result, "Could not create framebuffer", "createFramebuffers_"); } } + vLog("createFramebuffers: Created", framebuffers.size(), "framebuffers."); } -void VulkanInstance::recreateFramebuffers() { - for (auto& framebufferPair: scFramebuffers) { - // TODO!!! - /* createFramebuffers_(framebufferPair.second.first, framebufferPair.second.second); */ - } -} - - -std::vector& VulkanInstance::getFramebuffers(int id) { - return scFramebuffers[id].first; -} - - -void VulkanInstance::destroyFramebuffers(int id) { - if (scFramebuffers.contains(id)) { - for (auto& framebuffer : scFramebuffers[id].first) { - vkDestroyFramebuffer(device, framebuffer, nullptr); - } - scFramebuffers.erase(id); - framebufferIDs.insert(id); +void VulkanInstance::destroyFramebuffers(std::vector& framebuffers) { + for (auto& framebuffer : framebuffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); } } @@ -1579,6 +1510,12 @@ void VulkanInstance::createCommandBuffers(std::vector& commandB } +void VulkanInstance::destroyCommandBuffers(std::vector& commandBuffers) { + vkFreeCommandBuffers(device, commandPoolGraphics, static_cast(commandBuffers.size()), commandBuffers.data()); + commandBuffers.resize(0); +} + + void VulkanInstance::createSyncObjects() { imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); @@ -1628,7 +1565,7 @@ void VulkanInstance::createImage(uint32_t width, uint32_t height, VkFormat forma imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCI.samples = VK_SAMPLE_COUNT_1_BIT; /* imageCI.flags = 0; */ - VkResult result = vkCreateImage(device, &imageCI, nullptr, &image); + VkResult result = vkCreateImage(device, &imageCI, NO_ALLOC, &image); if (result != VK_SUCCESS) { throw getVkException(result, "Failed to create image", "createTextureImage"); } @@ -1641,16 +1578,19 @@ void VulkanInstance::createImage(uint32_t width, uint32_t height, VkFormat forma memoryAI.allocationSize = memoryRequirements.size; memoryAI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, memoryProperties); - result = vkAllocateMemory(device, &memoryAI, nullptr, &imageMemory); + result = vkAllocateMemory(device, &memoryAI, NO_ALLOC, &imageMemory); if (result != VK_SUCCESS) { throw getVkException(result, "Failed to allocate memory for image", "createTextureImage"); } - vkBindImageMemory(device, image, imageMemory, NO_OFFSET); + result = vkBindImageMemory(device, image, imageMemory, NO_OFFSET); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create image", "createTextureImage"); + } } -void VulkanInstance::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) { +void VulkanInstance::copyBufferToImage(VkBuffer buffer, VkImage image, int32_t offsetX, int32_t offsetY, uint32_t width, uint32_t height) { VkCommandBuffer cmdBuffer = beginSingleTimeCommands(commandPoolTransfer); VkBufferImageCopy region{}; region.bufferOffset = NO_OFFSET; @@ -1662,7 +1602,7 @@ void VulkanInstance::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; - region.imageOffset = { 0, 0, 0 }; + region.imageOffset = { offsetX, offsetY, 0 }; region.imageExtent = { width, height, 1 }; const uint32_t regionCount = 1; vkCmdCopyBufferToImage(cmdBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regionCount, ®ion); @@ -1671,7 +1611,7 @@ void VulkanInstance::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t } -void VulkanInstance::copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height) { +void VulkanInstance::copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, VkExtent2D extent) { VkImageCopy region{}; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.mipLevel = 0; @@ -1688,7 +1628,7 @@ void VulkanInstance::copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcIma /* offsets[1] = { 0, 0, 0 }; */ region.srcOffset = { 0, 0, 0 }; region.dstOffset = { 0, 0, 0 }; - region.extent = { width, height, 1 }; + region.extent = { extent.width, extent.height, 1 }; const uint32_t regionCount = 1; vkCmdCopyImage(cmdBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regionCount, ®ion); } @@ -1700,9 +1640,8 @@ bool hasStencilComponent(VkFormat format) { void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkCommandBuffer* cmdBuffer) { - - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + VkImageMemoryBarrier2 barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; barrier.oldLayout = oldLayout; barrier.newLayout = newLayout; // not using barrier for queue famlily ownership transfer @@ -1717,8 +1656,6 @@ void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkIma barrier.srcAccessMask = 0; barrier.dstAccessMask = 0; - VkPipelineStageFlags srcStage; - VkPipelineStageFlags dstStage; VkCommandPool commandPool; VkQueue q; if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and @@ -1730,8 +1667,8 @@ void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkIma barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; } - srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + barrier.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; commandPool = commandPoolGraphics; q = graphicsQ; } @@ -1740,8 +1677,8 @@ void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkIma barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + barrier.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; commandPool = commandPoolTransfer; q = transferQ; } @@ -1750,8 +1687,18 @@ void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkIma barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + commandPool = commandPoolGraphics; + q = graphicsQ; + } + else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL and + newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + barrier.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; commandPool = commandPoolGraphics; q = graphicsQ; } @@ -1760,8 +1707,8 @@ void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkIma barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; commandPool = commandPoolGraphics; q = graphicsQ; } @@ -1776,12 +1723,8 @@ void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkIma else { cmdBuffer_ = *cmdBuffer; } - uint32_t memBarrierCount = 0, bufBarrierCount = 0; - uint32_t imBarrierCount = 1; - vkCmdPipelineBarrier(cmdBuffer_, srcStage, dstStage, NO_FLAGS, - memBarrierCount, nullptr, - bufBarrierCount, nullptr, - imBarrierCount, &barrier); + VkDependencyInfo depI = getDepInfo(barrier); + vkCmdPipelineBarrier2(cmdBuffer_, &depI); if (cmdBuffer == nullptr) { endSingleTimeCommands(cmdBuffer_, commandPool, q); @@ -1795,60 +1738,62 @@ void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkIma VkFormat VulkanInstance::findDepthFormat() { return findSupportedFormat({ 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); } -void VulkanInstance::createDepthImageAndView() { - VkMemoryPropertyFlags memoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; - VkFormat depthFormat = findDepthFormat(); - createImage(scExtent.width, scExtent.height, depthFormat, tiling, imageUsage, memoryProperties, depthImage, depthImageMemory); - createImageView(depthFormat, depthImage, depthImageView, VK_IMAGE_ASPECT_DEPTH_BIT); - transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); +/* void VulkanInstance::createDepthImageAndView() { */ +/* VkMemoryPropertyFlags memoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; */ +/* VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; */ +/* VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; */ +/* VkFormat depthFormat = findDepthFormat(); */ + +/* createImage(scExtent.width, scExtent.height, depthFormat, tiling, imageUsage, memoryProperties, depthImage, depthImageMemory); */ +/* createImageView(depthFormat, depthImage, depthImageView, VK_IMAGE_ASPECT_DEPTH_BIT); */ + +/* transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); */ -} +/* } */ // // TEXTURE // -void VulkanInstance::createTextureImageAndView() { - int textureWidth, textureHeight, textureChannels; - stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha); - if (!pixels) { - throw Exception("Failed to load texture image", "createTextureImage"); - } +/* void VulkanInstance::createTextureImageAndView() { */ +/* int textureWidth, textureHeight, textureChannels; */ +/* stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha); */ +/* if (!pixels) { */ +/* throw Exception("Failed to load texture image", "createTextureImage"); */ +/* } */ - const size_t BYTES_PER_PIXEL = 4; - VkDeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL; - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - 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(device, stagingBufferMemory, NO_OFFSET, imageSize, NO_FLAGS, &data); - memcpy(data, pixels, static_cast(imageSize)); - vkUnmapMemory(device, stagingBufferMemory); +/* const size_t BYTES_PER_PIXEL = 4; */ +/* VkDeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL; */ +/* VkBuffer stagingBuffer; */ +/* VkDeviceMemory stagingBufferMemory; */ +/* 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(device, stagingBufferMemory, NO_OFFSET, imageSize, NO_FLAGS, &data); */ +/* memcpy(data, pixels, static_cast(imageSize)); */ +/* vkUnmapMemory(device, stagingBufferMemory); */ - stbi_image_free(pixels); - createImage(textureWidth, textureHeight, 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); +/* stbi_image_free(pixels); */ +/* createImage(textureWidth, textureHeight, 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); */ - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - copyBufferToImage(stagingBuffer, textureImage, static_cast(textureWidth), static_cast(textureHeight)); - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +/* transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); */ +/* copyBufferToImage(stagingBuffer, textureImage, static_cast(textureWidth), static_cast(textureHeight)); */ +/* transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); */ - vkDestroyBuffer(device, stagingBuffer, nullptr); - vkFreeMemory(device, stagingBufferMemory, nullptr); +/* vkDestroyBuffer(device, stagingBuffer, nullptr); */ +/* vkFreeMemory(device, stagingBufferMemory, nullptr); */ - createImageView(VK_FORMAT_R8G8B8A8_SRGB, textureImage, textureImageView, VK_IMAGE_ASPECT_COLOR_BIT); -} +/* createImageView(VK_FORMAT_R8G8B8A8_SRGB, textureImage, textureImageView, VK_IMAGE_ASPECT_COLOR_BIT); */ +/* } */ -void VulkanInstance::createTextureSampler() { +void VulkanInstance::createTextureSampler(VkSampler& sampler) { VkSamplerCreateInfo samplerCI{}; samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; // TODO: LINEAR or NEAREST @@ -1869,12 +1814,10 @@ void VulkanInstance::createTextureSampler() { samplerCI.minLod = 0.0f; samplerCI.maxLod = 0.0f; - VkResult result = vkCreateSampler(device, &samplerCI, nullptr, &textureSampler); + VkResult result = vkCreateSampler(device, &samplerCI, nullptr, &sampler); if (result != VK_SUCCESS) { throw getVkException(result, "Failed to create texture sampler.", "createTextureSampler"); } - - } @@ -2003,6 +1946,9 @@ void VulkanInstance::init() { if (glfwVulkanSupported() == GLFW_FALSE) { throw std::runtime_error("Vulkan not supported."); } + VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan("VulkanInstance", + { &instance, &physicalDevice, &device, &graphicsQ, &presentQ, &transferQ, &commandPoolGraphics, &commandPoolTransfer, &surface, &swapChain }, + { &scImages, &scImageViews, &commandBuffersBegin, &commandBuffersEnd })); createInstance(); setupDebugMessenger(); createSurface(); @@ -2011,18 +1957,15 @@ void VulkanInstance::init() { createLogicalDevice(); createSwapChain(); - createImageViews(); - createRenderPassBegin(); - createRenderPassEnd(); + createSwapChainImageViews(); /* createDescriptorSetLayout(); */ // after descriptorSetLayout - createGraphicsPipeline("shaders/vert2D.spv", "shaders/frag2D.spv", false, renderPassBegin, pipelines[PL_2D]); /* createGraphicsPipeline("shaders/vert.spv", "shaders/frag.spv", true, renderPass1, pipelines[PL_3D]); */ createCommandPools(); - createDepthImageAndView(); // after CommandPools - createTextureImageAndView(); // after CommandPools + /* createDepthImageAndView(); // after CommandPools */ + /* createTextureImageAndView(); // after CommandPools */ /* createFramebuffers(); // after depth image */ - createTextureSampler(); + /* createTextureSampler(); */ loadModel(); /* createVertexBuffer(VERTEX_BUFFER_SIZE); */ /* createIndexBuffer(INDEX_BUFFER_SIZE); */ @@ -2032,16 +1975,28 @@ void VulkanInstance::init() { createSyncObjects(); /* createDescriptorPool(); */ /* createDescriptorSets(); // after UniformBuffers */ + + for (auto& obj : VulkanInstance::objectsUsingVulkan) { + obj.updateHandles(); + } } void VulkanInstance::cleanup() { vkDeviceWaitIdle(device); + // call callbacks in reverse order + for (auto it = cleanupCallbacks.rbegin(); it != cleanupCallbacks.rend(); it++) { + (*it)(); + } + /* vLog("cleanup:", commandBuffersToSubmitThisFrame.size()); */ - vkDestroyImageView(device, textureImageView, nullptr); - vkDestroySampler(device, textureSampler, nullptr); - vkDestroyImage(device, textureImage, nullptr); - vkFreeMemory(device, textureImageMemory, nullptr); + destroyCommandBuffers(commandBuffersBegin); + destroyCommandBuffers(commandBuffersEnd); + + /* vkDestroyImageView(device, textureImageView, nullptr); */ + /* vkDestroySampler(device, textureSampler, nullptr); */ + /* vkDestroyImage(device, textureImage, nullptr); */ + /* vkFreeMemory(device, textureImageMemory, nullptr); */ cleanupSwapChain(); @@ -2069,9 +2024,9 @@ void VulkanInstance::cleanup() { // // APP // -void VulkanInstance::mainLoop() { - vkDeviceWaitIdle(device); +void VulkanInstance::submitThisFrame(VkCommandBuffer& cmdBuffer) { + commandBuffersToSubmitThisFrame.push_back(cmdBuffer); } @@ -2089,8 +2044,8 @@ uint32_t VulkanInstance::beginFrameDraw() { return imageIndex; } else if (result != VK_SUBOPTIMAL_KHR and result != VK_SUCCESS) { - vLog.error("Failed to acquire swap chain image. Result:", STR_VK_RESULT(result)); - throw std::runtime_error("Failed to acquire swap chain image"); + /* vLog.error("Failed to acquire swap chain image. Result:", STR_VK_RESULT(result)); */ + throw getVkException(result, "Failed to acquire swap chain image.", "beginFrameDraw"); } vkResetFences(device, 1, &inFlightFences[currentFrame]); @@ -2105,6 +2060,23 @@ uint32_t VulkanInstance::beginFrameDraw() { } // transition to transfer dst layout transitionImageLayout(scImages[imageIndex], scImageFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &commandBuffersBegin[currentFrame]); + VkImageMemoryBarrier2 imageBarrier{}; + imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + imageBarrier.image = scImages[imageIndex]; + imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageBarrier.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT; + /* imageBarrier.srcAccessMask = VK_ACCESS_2_SHADING_RATE_IMAGE_READ_BIT_NV; */ + imageBarrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBarrier.subresourceRange.baseMipLevel = 0; + imageBarrier.subresourceRange.levelCount = 1; + imageBarrier.subresourceRange.baseArrayLayer = 0; + imageBarrier.subresourceRange.layerCount = 1; + VkDependencyInfo depI = getDepInfo(imageBarrier); + vkCmdPipelineBarrier2(commandBuffersBegin[currentFrame], &depI); + + /* VkRenderPassBeginInfo renderPassBI{}; */ /* renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; */ /* renderPassBI.renderPass = renderPassBegin; */ @@ -2184,7 +2156,6 @@ void VulkanInstance::endFrameDraw(uint32_t imageIndex) { presentI.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentI.waitSemaphoreCount = 1; presentI.pWaitSemaphores = signalSemaphores; - vLog("endFrameDraw:: imageIndex:", imageIndex); VkSwapchainKHR swapChains[] = {swapChain}; presentI.swapchainCount = 1; diff --git a/vulkan_instance.hpp b/vulkan_instance.hpp index d469501..5fc8bbf 100644 --- a/vulkan_instance.hpp +++ b/vulkan_instance.hpp @@ -23,54 +23,100 @@ #include namespace gz::vk { -const std::string CONFIG_FILE = "vulkan.conf"; -const int MAX_FRAMES_IN_FLIGHT = 3; + + constexpr gz::Color VULKAN_MESSAGE_PREFIX_COLOR = gz::Color::BO_BLUE; + constexpr gz::Color VULKAN_MESSAGE_TIME_COLOR = gz::Color::BLUE; + const std::string CONFIG_FILE = "vulkan.conf"; + const int MAX_FRAMES_IN_FLIGHT = 3; #define SettingsTypes uint32_t, bool, float -/* const std::string MODEL_PATH = "models/gazebo-3d-model/gazebo.obj"; */ -/* const std::string TEXTURE_PATH = "models/gazebo-3d-model/gazebo_diffuse.png"; */ + /* const std::string MODEL_PATH = "models/gazebo-3d-model/gazebo.obj"; */ + /* const std::string TEXTURE_PATH = "models/gazebo-3d-model/gazebo_diffuse.png"; */ -const std::string MODEL_PATH = "models/armoire-3d-model/Armoire.obj"; -const std::string TEXTURE_PATH = "models/armoire-3d-model/Armoire_diffuse.png"; + const std::string MODEL_PATH = "models/armoire-3d-model/Armoire.obj"; + const std::string TEXTURE_PATH = "models/armoire-3d-model/Armoire_diffuse.png"; -const gz::util::unordered_string_map INITIAL_SETTINGS = { - { "framerate", "60" }, - { "anisotropy_enable", "false" }, - { "max_anisotropy", "1" }, - /* { "", "" } */ -}; + const gz::util::unordered_string_map INITIAL_SETTINGS = { + { "framerate", "60" }, + { "anisotropy_enable", "false" }, + { "max_anisotropy", "1" }, + /* { "", "" } */ + }; -const int BINDING = 0; -const uint32_t NO_FLAGS = 0; -const uint32_t NO_OFFSET = 0; -const size_t VERTEX_BUFFER_SIZE = 512; -const size_t INDEX_BUFFER_SIZE = 512; + constexpr VkClearColorValue missingTextureColor = { { 0.4f, 0.0f, 0.4f, 1.0f } }; + const int BINDING = 0; + const uint32_t NO_FLAGS = 0; + const uint32_t NO_OFFSET = 0; + constexpr VkAllocationCallbacks* NO_ALLOC = nullptr; + const size_t VERTEX_BUFFER_SIZE = 512; + const size_t INDEX_BUFFER_SIZE = 512; class VulkanInstance { friend class Renderer; friend class Renderer2D; friend class Renderer3D; + friend class TextureManager; + friend class TextureAtlas; public: VulkanInstance(gz::SettingsManagerCreateInfosmCI) : settings(smCI) {}; + /** + * @brief Initializes the vulkan instance + * @details + * -# @ref createWindow "create a window through glfw" + * -# @ref createInstance "create the vulkan instance" + * -# @ref setupDebugMessenger "sets up the debug messenger" + * -# @ref createSurface "create a the window surface" + * -# @ref selectPhysicalDevice "select a GPU" + * -# @ref setValidSettings "set the possible settings for the SettingsManager" + * -# @ref createLogicalDevice "create a logical device" + * -# @ref createSwapChain "create the swap chain" + * -# @ref createSwapChainImageViews "create the swap chain image views" + * -# @ref createCommandPools "create the command pools" + * @todo move depth image, texture, texture samples, model stuff to renderers + * -# @ref createCommandBuffers "create command buffers for swap chain image layout transitions" + * -# @ref createSyncObjects "create synchronization objects" + */ void init(); + /** + * @brief Acquires a new frame from the swap chain and sets the image layout + * @details + * How to draw stuff right now: + * -# Call this function + * -# Call the draw functions from the renderers. + * The renderers need to put the recorded command buffers into the commandBuffersToSubmitThisFrame vector using submitThisFrame() + * -# Call endFrameDraw + */ uint32_t beginFrameDraw(); - void submitThisFrame(VkCommandBuffer); + void submitThisFrame(VkCommandBuffer& cmdBuffer); void endFrameDraw(uint32_t imageIndex); - void deInit(); GLFWwindow* window; + /** + * @brief Destroys everything that was initalized in init + * @details + * -# Calls every callback registered by registerCleanupCallback() (FILO order) + * -# calls cleanupSwapChain() + * -# destroys everything initalized in init() + * -# destroys the window + * -# calls glfwTerminate() + */ void cleanup(); + void registerCleanupCallback(std::function callbackF); + void registerSwapChainRecreateCallback(std::function callbackF); /// The frame in the swap chain that is currently drawn to uint32_t currentFrame = 0; + + // // SETTINGS // gz::SettingsManager settings; private: - void mainLoop(); std::vector commandBuffersToSubmitThisFrame; void createWindow(); + std::vector> cleanupCallbacks; + std::vector> scRecreateCallbacks; // // INSTANCE @@ -79,9 +125,9 @@ const size_t INDEX_BUFFER_SIZE = 512; /** * @brief Create the vulkan instance * @details - * - check if validationLayers are available (if enabled) - * - create instance with info - * - check if all extensions required by glfw are available + * -# check if validationLayers are available (if enabled) + * -# create instance with info + * -# check if all extensions required by glfw are available */ void createInstance(); @@ -89,8 +135,17 @@ const size_t INDEX_BUFFER_SIZE = 512; // PHYSICAL DEVICE // VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + /// all the properties of the selected physcial device VkPhysicalDeviceProperties phDevProperties; - VkPhysicalDeviceFeatures phDevFeatures; + /// all the features that the selected physical device supports + PhysicalDeviceFeatures phDevFeatures; + /** + * @brief Get rhe required physical device features + * @details + * The required features are: + * - synchronization2 + */ + PhysicalDeviceFeatures getRequiredFeatures() const; /** * @brief Assign the physicalDevice handle to the @ref rateDevice "best rated" GPU * @details @@ -110,10 +165,11 @@ const size_t INDEX_BUFFER_SIZE = 512; /** * @brief Set valid values for the SettingsManager according to phDevFeatures and phDevProperties * @details - * Must be called after selectPhysicalDevice + * Must be called after selectPhysicalDevice and before createLogicalDevice * Sets valid values for: * - anisotropy_enable * - max_anisotropy + * */ void setValidSettings(); /** @@ -152,6 +208,7 @@ const size_t INDEX_BUFFER_SIZE = 512; // VkSwapchainKHR swapChain; std::vector scImages; + std::vector scImageViews; VkFormat scImageFormat; VkExtent2D scExtent; @@ -166,13 +223,23 @@ const size_t INDEX_BUFFER_SIZE = 512; VkPresentModeKHR selectSwapChainPresentMode(const std::vector& availableModes); VkExtent2D selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); void createSwapChain(); - void recreateSwapChain(); + void createSwapChainImageViews(); void cleanupSwapChain(); + /** + * @brief Recreate the swap chain for the current window size + * @details + * Does: + * -# get new window dimensions + * - blocks while dimensions == 0 (minimized) + * -# calls cleanupSwapChain + * -# calls createSwapChain + * -# other stuff + * -# calls all callbacks registered with registerSwapChainRecreateCallback + */ + void recreateSwapChain(); // // IMAGE VIEW // - std::vector scImageViews; - void createImageViews(); /** * @brief Create a 2D imageView with format for image. */ @@ -180,8 +247,8 @@ const size_t INDEX_BUFFER_SIZE = 512; // // RENDER PASS // - VkRenderPass renderPassBegin; - VkRenderPass renderPassEnd; + /* VkRenderPass renderPassBegin; */ + /* VkRenderPass renderPassEnd; */ /** * @brief Create the render pass * @details @@ -189,40 +256,16 @@ const size_t INDEX_BUFFER_SIZE = 512; * -# color attachment * -# depth stencil attachment */ - void createRenderPassBegin(); - void createRenderPassEnd(); + /* void createRenderPassBegin(); */ + /* void createRenderPassEnd(); */ // // DESCRIPTORS // + /* VkDescriptorSetLayout descriptorSetLayout; */ /** - * @name Create desciptors - * @details These functions create a desciptor with bindings for - * -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER) - * -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) - * @{ + * @brief Create a descriptor layout with bindings */ - VkDescriptorSetLayout descriptorSetLayout; - /** - * @brief Create a descriptor layout binding for the MVP uniform buffer object - */ - void createDescriptorSetLayout(); - VkDescriptorPool descriptorPool; - /** - * @brief Create a descriptor layout binding for the MVP uniform buffer object - * @details Create a desciptor set layout with bindings for - * -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER) - * -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) - */ - void createDescriptorPool(); - std::vector descriptorSets; - /* - * @bried Create one descriptor set with layout descriptorSetLayout for each frame - */ - void createDescriptorSets(); - const uint32_t bindingUniformBuffer = 0; - const uint32_t bindingCombinedImageSampler = 1; - /* const uint32_t bindingCombinedImageSampler = 1; */ - + void createDescriptorSetLayout(std::vector bindings, VkDescriptorSetLayout& layout); /** * @} */ @@ -230,7 +273,6 @@ const size_t INDEX_BUFFER_SIZE = 512; // PIPELINE // - PipelineContainer pipelines; /* void createGraphicsPipeline(); */ VkShaderModule createShaderModule(const std::vector& code); /** @@ -254,7 +296,7 @@ const size_t INDEX_BUFFER_SIZE = 512; * */ template - void createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); + void createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); // // FONT // @@ -306,28 +348,11 @@ const size_t INDEX_BUFFER_SIZE = 512; // // FRAMEBUFFERS // - /* std::vector scFramebuffers; */ - std::set framebufferIDs; - // render pass is need for recreation of framebuffer - std::unordered_map, VkRenderPass>> scFramebuffers; /** - * @brief Create a vector of framebuffers and return the id as a handle - * @returns id that can be used with getFramebuffers and destroyFramebuffers + * @brief Destroy the framebuffers */ - int createFramebuffers(std::vector& imageViews, VkRenderPass& renderPass); - /** - * @brief Destroy the framebuffers from id - */ - void destroyFramebuffers(int id); - void createFramebuffers_(std::vector& framebuffers, std::vector& imageViews, VkRenderPass& renderPass); - /** - * @brief Recreate all framebuffers in scFramebuffers - */ - void recreateFramebuffers(); - /** - * @brief Get the framebuffer vector to id - */ - std::vector& getFramebuffers(int id); + void destroyFramebuffers(std::vector& framebuffers); + void createFramebuffers(std::vector& framebuffers, std::vector& imageViews, VkRenderPass& renderPass); bool frameBufferResized = false; static void frameBufferResizedCallback(GLFWwindow* window, int width, int height); // @@ -339,15 +364,24 @@ const size_t INDEX_BUFFER_SIZE = 512; // // COMMAND BUFFER // + /** + * @brief Create MAX_FRAMES_IN_FLIGHT command buffers from the commandPoolGraphics + */ void createCommandBuffers(std::vector& commandBuffers); + /** + * @brief Destroy all command buffers (must be from commandPoolGraphics) + */ void destroyCommandBuffers(std::vector& commandBuffers); std::vector commandBuffersBegin; std::vector commandBuffersEnd; // // IMAGE UTILITY // + /** + * @brief Create the 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 copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height); + 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 * @brief Copy srcImage to dstImage @@ -362,18 +396,22 @@ const size_t INDEX_BUFFER_SIZE = 512; * @param srcImage Image with layout TRANSFER_SRC_OPTIMAL * @param dstImage Image with layout TRANSFER_DST_OPTIMAL */ - void copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height); //, VkCommandPool& commandPool=commandPoolTransfer); + void copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, VkExtent2D extent); //, VkCommandPool& commandPool=commandPoolTransfer); /** * @brief Transition the layout of image from oldLayout to newLayout * @details * Supported transitions: - * - undefined -> depth stencil attachment (graphics q) - * - undefined -> transfer dst optimal (transfer q) - * - transfer dst optimal -> shader read only optimal (graphics q) - * - transfer dst optimal -> present src (graphics q) + * - UNDEFINED -> DEPTH_STENCIL_ATTACHMENT_OPTIMAL (graphics q) + * - UNDEFINED -> TRANSFER_DST_OPTIMAL (transfer q) + * - SHADER_READ_ONLY_OPTIMAL -> TRANSFER_DST_OPTIMAL (graphics q) + * - TRANSFER_DST_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL (graphics q) + * - TRANSFER_DST_OPTIMAL -> PRESENT_SRC_KHR (graphics q) * * If you do not provide a command buffer, a command buffer from the indicated queue will be created and submitted. * If you do provide a command buffer, the command is recorded but NOT submitted. + * + * The transition is done by submitting a VkImageMemoryBarrier2 + * * @param cmdBuffer [Optional] The command buffer where the command will be recorded to * @throws VkUserError if the transition is not supported. */ @@ -382,21 +420,21 @@ const size_t INDEX_BUFFER_SIZE = 512; // // DEPTH // - VkImage depthImage; - VkDeviceMemory depthImageMemory; - VkImageView depthImageView; + /* VkImage depthImage; */ + /* VkDeviceMemory depthImageMemory; */ + /* VkImageView depthImageView; */ VkFormat findDepthFormat(); - void createDepthImageAndView(); + /* void createDepthImageAndView(); */ // // TEXTURES // - VkImage textureImage; - VkDeviceMemory textureImageMemory; - VkImageView textureImageView; - VkSampler textureSampler; + /* VkImage textureImage; */ + /* VkDeviceMemory textureImageMemory; */ + /* VkImageView textureImageView; */ + /* VkSampler textureSampler; */ /// Load textures/image.png - void createTextureImageAndView(); - void createTextureSampler(); + /* void createTextureImageAndView(); */ + void createTextureSampler(VkSampler& sampler); // // SYNCHRONIZATION @@ -425,10 +463,27 @@ const size_t INDEX_BUFFER_SIZE = 512; // // UTILITY // + /** + * @name Handle ownership + * @brief Retrieve the owner of a vulkan handle + * @see ObjectUsingVulkan + * @{ + */ + static std::vector objectsUsingVulkan; + static void registerObjectUsingVulkan(const ObjectUsingVulkan& obj); + static void getHandleOwnerString(std::string_view message); + static std::string handleOwnerString; + static bool lastColorWhite; + /// @} /** * @brief Log messages from validation layers with the Apps logger * @details * Using the static vLog to log vulkan messages with a prefix dependant on the messageType. + * - Validation and performace messages usually have a bracket containing a VUID, + * these brackets are printed in yellow/red when its a warning/error. + * - Handles printed by the validation layer are checked with objectsUsingVulkan, if found + * the owner of the handle is added to the message @see registerObjectUsingVulkan, ObjectUsingVulkan + * - Different message sources (layer, shader, general...) get a prefix with a different color */ static VKAPI_ATTR VkBool32 VKAPI_CALL debugLog( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverety, diff --git a/vulkan_util.cpp b/vulkan_util.cpp index 3fce456..7188a95 100644 --- a/vulkan_util.cpp +++ b/vulkan_util.cpp @@ -1,8 +1,73 @@ #include "vulkan_util.hpp" +#include #include namespace gz::vk { +// +// CONVENIENCE +// + PhysicalDeviceFeatures::PhysicalDeviceFeatures() { + f.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + f11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + f12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + f13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + f.pNext = &f11; + f11.pNext = &f12; + f12.pNext = &f13; + } + bool PhysicalDeviceFeatures::requiredFeaturesAvailable(const PhysicalDeviceFeatures& requiredFeatures) const { + // iterate over all the features in the structs + // VkPhysicalDeviceFeatures2 + size_t featureCount = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32); + // pointer to first feature + const VkBool32* pFeature = &f.features.robustBufferAccess; + const VkBool32* pRequiredFeature = &requiredFeatures.f.features.robustBufferAccess; + for (size_t i = 0; i < featureCount; i++) { + if (*(pRequiredFeature + i) == VK_TRUE and *(pFeature + i) == VK_FALSE) { + /* std::cout << "core feature not supported: i = " << i << "\n"; */ + return false; + } + } + + // VkPhysicalDeviceVulkan11Features + // pointer to first feature: after sType and pNext + featureCount = (sizeof(VkPhysicalDeviceVulkan11Features) - sizeof(VkStructureType) - sizeof(void*)) / sizeof(VkBool32); + pFeature = &f11.storageBuffer16BitAccess; + pRequiredFeature = &requiredFeatures.f11.storageBuffer16BitAccess; + for (size_t i = 0; i < featureCount; i++) { + if (*(pRequiredFeature + i) == VK_TRUE and *(pFeature + i) == VK_FALSE) { + /* std::cout << "vulkan11 feature not supported: i = " << i << "\n"; */ + return false; + } + } + + // VkPhysicalDeviceVulkan12Features + // pointer to first feature: after sType and pNext + featureCount = (sizeof(VkPhysicalDeviceVulkan12Features) - sizeof(VkStructureType) - sizeof(void*)) / sizeof(VkBool32); + pFeature = &f12.samplerMirrorClampToEdge; + pRequiredFeature = &requiredFeatures.f12.samplerMirrorClampToEdge; + for (size_t i = 0; i < featureCount; i++) { + if (*(pRequiredFeature + i) == VK_TRUE and *(pFeature + i) == VK_FALSE) { + /* std::cout << "vulkan12 feature not supported: i = " << i << "\n"; */ + return false; + } + } + + // VkPhysicalDeviceVulkan13Features + // pointer to first feature: after sType and pNext + featureCount = (sizeof(VkPhysicalDeviceVulkan13Features) - sizeof(VkStructureType) - sizeof(void*)) / sizeof(VkBool32); + pFeature = &f13.robustImageAccess; + pRequiredFeature = &requiredFeatures.f13.robustImageAccess; + for (size_t i = 0; i < featureCount; i++) { + if (*(pRequiredFeature + i) == VK_TRUE and *(pFeature + i) == VK_FALSE) { + /* std::cout << "vulkan13 feature not supported: i = " << i << "\n"; */ + return false; + } + } + return true; + } + void PipelineContainer::erase(const PipelineT& key, VkDevice& device, const VkAllocationCallbacks* pAllocator) { vkDestroyPipeline(device, pipelines[key].pipeline, pAllocator); vkDestroyPipelineLayout(device, pipelines[key].layout, pAllocator); @@ -14,6 +79,46 @@ namespace gz::vk { return pipelines.erase(it); } + void PipelineContainer::destroy(VkDevice& device, const VkAllocationCallbacks* pAllocator) { + auto it = pipelines.begin(); while (it != pipelines.end()) { + it = erase(it, device); + } + } + + + VkDependencyInfo getDepInfo(const VkImageMemoryBarrier2& barrier) { + VkDependencyInfo depI{}; + depI.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + depI.imageMemoryBarrierCount = 1; + depI.pImageMemoryBarriers = &barrier; + return depI; + } + + +// +// +// + ObjectUsingVulkan::ObjectUsingVulkan(std::string&& name, std::vector&& ptrsToHandles, std::vector&& vectorWithPtrsToHandles) { + objectName = std::move(name); + for (auto it = vectorWithPtrsToHandles.begin(); it != vectorWithPtrsToHandles.end(); it++) { + vectorWithPtrToHandle.emplace_back(reinterpret_cast*>(*it)); + } + ptrToHandle = std::move(ptrsToHandles); + updateHandles(); + } + + void ObjectUsingVulkan::updateHandles() { + handles.clear(); + for (auto it = ptrToHandle.begin(); it != ptrToHandle.end(); it++) { + handles.insert(reinterpret_cast(reinterpret_cast(*it))); + } + for (auto it = vectorWithPtrToHandle.begin(); it != vectorWithPtrToHandle.end(); it++) { + for (auto it2 = (*it)->begin(); it2 != (*it)->end(); it2++) { + handles.insert(reinterpret_cast(reinterpret_cast(*it2))); + } + } + } + } // namespace gz::vk diff --git a/vulkan_util.hpp b/vulkan_util.hpp index b7301b0..6735c37 100644 --- a/vulkan_util.hpp +++ b/vulkan_util.hpp @@ -1,10 +1,39 @@ +#pragma once + #include "vertex.hpp" #include +#include #include #include namespace gz::vk { + /** + * @name Convenience structs + * @details + * Structures and functionas making usiing the vulkan api more convenient + * @{ + */ + /** + * @brief Struct containing all physical device features structs + * @details + * Make sure to call vkGetPhysicalDeviceFeatures2 with the f member, not this struct itself + */ + struct PhysicalDeviceFeatures { + /** + * @brief Initialize the structs with sType and pNext chain f -> f11 -> f12 -> f13 + */ + PhysicalDeviceFeatures(); + VkPhysicalDeviceFeatures2 f{}; + VkPhysicalDeviceVulkan11Features f11{}; + VkPhysicalDeviceVulkan12Features f12{}; + VkPhysicalDeviceVulkan13Features f13{}; + /** + * @brief Check if all features enabled in requiredFeatures are enabled in this + */ + bool requiredFeaturesAvailable(const PhysicalDeviceFeatures& requiredFeatures) const; + }; + enum PipelineT { PL_3D, PL_2D }; @@ -22,10 +51,17 @@ namespace gz::vk { using iterator = std::unordered_map::iterator; Pipeline& operator[](const PipelineT& key) { return pipelines[key]; } /** - * @brief Destroy the pipeline+layout and then remove the handles + * @brief Destroy the pipeline+layout and then remove the handles from the underlying map */ void erase(const PipelineT& key, VkDevice& device, const VkAllocationCallbacks* pAllocator=nullptr); + /** + * @brief Destroy the pipeline+layout and then remove the handles from the underlying map + */ iterator erase(const iterator& it, VkDevice& device, const VkAllocationCallbacks* pAllocator=nullptr); + /** + * @brief Destroy all pipelines#layouts and then remove the handles from the underlying map + */ + void destroy(VkDevice& device, const VkAllocationCallbacks* pAllocator=nullptr); iterator begin() { return pipelines.begin(); } iterator end() { return pipelines.end(); } size_t size() const { return pipelines.size(); } @@ -34,6 +70,12 @@ namespace gz::vk { }; + struct SwapChainSupport { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; + }; + template concept SupportedIndexType = std::same_as or std::same_as; @@ -81,9 +123,36 @@ namespace gz::vk { }; - struct SwapChainSupport { - VkSurfaceCapabilitiesKHR capabilities; - std::vector formats; - std::vector presentModes; + /** + * @brief Get a VkDependencyInfo struct for a single image memory barrier + */ + VkDependencyInfo getDepInfo(const VkImageMemoryBarrier2& barrier); + /// @} + + /** + * @brief Used for debugging + * @see VulkanInstance::debugLog + * @details + * Handles like VkDevice are pointers to the actual handle. + * Register the handle to a vulkan object with this struct and call updateHandles(). + * The handles set will then contain the actual address of the handle, as printed by the validation layers. + * That means you can check a handle returned by a validation layer + * with this struct and see if this handle belongs to object that created this struct. + * @warning + * Every pointer you submit to this must stay valid! + * Use this struct only when debugging! + */ + struct ObjectUsingVulkan { + ObjectUsingVulkan(std::string&& name, std::vector&& ptrsToHandles, std::vector&& vectorWithPtrsToHandles); + std::string objectName; + std::vector ptrToHandle; + std::vector*> vectorWithPtrToHandle; + std::set handles; + /** + * @brief Update the handles map with the current handle (aka segfault generator) + */ + void updateHandles(); + }; + } // namespace gz::vk