diff --git a/src/vulkan_instance.cpp b/src/vulkan_instance.cpp index f9e2d1e..ad4d5b9 100644 --- a/src/vulkan_instance.cpp +++ b/src/vulkan_instance.cpp @@ -6,12 +6,12 @@ #include "vulkan_util.hpp" #include -#include -#include +#include +#include +#include #include +#include #include -#include -#include #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" @@ -27,6 +27,14 @@ #define VULKAN_HPP_NO_CONSTRUCTORS #include +#define VULKAN_INSTANCE_SWAP_CHAIN_AS_TRANSFER_DST + +#ifdef VULKAN_INSTANCE_SWAP_CHAIN_AS_TRANSFER_DST +#elif defined VULKAN_INSTANCE_SWAP_CHAIN_AS_RENDER_TARGET +#else + static_assert(false, "either VULKAN_INSTANCE_SWAP_CHAIN_AS_TRANSFER_DST or VULKAN_INSTANCE_SWAP_CHAIN_AS_RENDER_TARGET has to be defined"); +#endif + namespace gz::vlk { using std::uint32_t; @@ -52,6 +60,7 @@ using std::uint32_t; .pUserData = nullptr, }; +// // // IMPLEMENTATION OF VULKANINSTANCE // @@ -63,9 +72,6 @@ using std::uint32_t; throw std::runtime_error("Vulkan not supported."); } - VulkanInstance::registerObjectUsingVulkan("VulkanInstance", - &instance, &physicalDevice, &device, &graphicsQ, &presentQ, &transferQ, &commandPoolGraphics, &commandPoolTransfer, &surface, &swapChain, - &scImages, &scImageViews, &commandBuffersBegin, &commandBuffersEnd); createInstance(); setupDebugMessenger(); createSurface(); @@ -83,9 +89,9 @@ using std::uint32_t; /* createDescriptorPool(); */ /* createDescriptorSets(); // after UniformBuffers */ - for (auto& obj : VulkanInstance::objectsUsingVulkan) { - obj->updateHandles(); - } + VulkanInstance::registerObjectUsingVulkan("VulkanInstance", + &instance, &physicalDevice, &device, &graphicsQ, &presentQ, &transferQ, &commandPoolGraphics, &commandPoolTransfer, &surface, &swapChain, + &scImages, &scImageViews, &commandBuffersBegin, &commandBuffersEnd); } @@ -166,30 +172,17 @@ using std::uint32_t; throw getVkException(result, "Failed to begin clear command buffer", "VulkanInstance::beginFrameDraw"); } // transition to transfer dst layout - transitionImageLayout(scImages[imageIndex], scImageFormat, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, &commandBuffersBegin[currentFrame]); - vk::ImageMemoryBarrier2 imageBarrier { - .srcStageMask = vk::PipelineStageFlagBits2::eNone, - .srcAccessMask = vk::AccessFlagBits2::eNone, - .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, - .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, - .oldLayout = vk::ImageLayout::eUndefined, - .newLayout = vk::ImageLayout::eTransferDstOptimal, - .image = scImages[imageIndex], - .subresourceRange { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - } - }; - vk::DependencyInfo depI = getDepInfo(imageBarrier); - commandBuffersBegin[currentFrame].pipelineBarrier2(depI); +#ifdef VULKAN_INSTANCE_SWAP_CHAIN_AS_TRANSFER_DST + vk::ImageLayout targetLayout = vk::ImageLayout::eTransferDstOptimal; +#elif defined VULKAN_INSTANCE_SWAP_CHAIN_AS_RENDER_TARGET + vk::ImageLayout targetLayout = vk::ImageLayout::eColorAttachmentOptimal; +#endif + transitionImageLayout(scImages[imageIndex], scImageFormat, vk::ImageLayout::eUndefined, targetLayout, &commandBuffersBegin[currentFrame]); result = commandBuffersBegin[currentFrame].end(); if (result != vk::Result::eSuccess) { vLog.error("Failed to record clear command buffer", "VkResult:", result); - throw getVkException(result, "Failed to record 2D - command buffer", "VulkanInstance::beginFrameDraw"); + throw getVkException(result, "Failed to record command buffer", "VulkanInstance::beginFrameDraw"); } commandBuffersToSubmitThisFrame.push_back(commandBuffersBegin[currentFrame]); @@ -204,8 +197,13 @@ using std::uint32_t; if (result != vk::Result::eSuccess) { throw getVkException(result, "Failed to begin command buffer", "VulkanInstance::endFrameDraw"); } +#ifdef VULKAN_INSTANCE_SWAP_CHAIN_AS_TRANSFER_DST + vk::ImageLayout currentLayout = vk::ImageLayout::eTransferDstOptimal; +#elif defined VULKAN_INSTANCE_SWAP_CHAIN_AS_RENDER_TARGET + vk::ImageLayout currentLayout = vk::ImageLayout::eColorAttachmentOptimal; +#endif // transition to present layout - transitionImageLayout(scImages[imageIndex], scImageFormat, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::ePresentSrcKHR, &commandBuffersEnd[currentFrame]); + transitionImageLayout(scImages[imageIndex], scImageFormat, currentLayout, vk::ImageLayout::ePresentSrcKHR, &commandBuffersEnd[currentFrame]); result = commandBuffersEnd[currentFrame].end(); if (result != vk::Result::eSuccess) { throw getVkException(result, "Failed to record command buffer", "VulkanInstance::endFrameDraw"); @@ -274,6 +272,34 @@ using std::uint32_t; } + void VulkanInstance::copyToDeviceBuffer(const void* srcData, vk::DeviceSize srcSize, vk::Buffer& dstBuffer, MemoryInfo& dstMemory, uint32_t dstOffset) { + // create staging buffer + vk::Buffer stagingBuffer; + MemoryInfo stagingBufferMemory; + createBuffer(srcSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory); + // fill staging buffer + void* stagingMemory; + vk::Result result = device.mapMemory(stagingBufferMemory.memory, stagingBufferMemory.offset, srcSize, NO_MEM_FLAGS, &stagingMemory); + if (result != vk::Result::eSuccess) { + throw getVkException(result, "Failed to map staging buffer for vertices", "VulkanInstance::copyToDeviceBuffer"); + } + + memcpy(stagingMemory, srcData, srcSize); + device.unmapMemory(stagingBufferMemory.memory); + // copy to device local memory + vk::CommandBuffer cmdBuffer = beginSingleTimeCommands(POOL_TRANSFER); + vk::BufferCopy copyRegion { + .srcOffset = 0, + .dstOffset = dstOffset, + .size = srcSize, + }; + cmdBuffer.copyBuffer(stagingBuffer, dstBuffer, copyRegion); + endSingleTimeCommands(cmdBuffer, POOL_TRANSFER); + destroyBuffer(stagingBuffer, stagingBufferMemory); + vLog.log0("copyToDeviceBuffer: copied data at", srcData, "of size", srcSize, "to dstBuffer"); + } + + vk::CommandBuffer VulkanInstance::beginSingleTimeCommands(InstanceCommandPool commandPool) { vk::CommandBufferAllocateInfo allocI { .level = vk::CommandBufferLevel::ePrimary, @@ -336,44 +362,6 @@ using std::uint32_t; } - void VulkanInstance::loadModel(const std::string& path, VerticesAndIndices& model) { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - std::string warnings, errors; - if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warnings, &errors, path.c_str())) { - vLog.warning("Warning from tinyobj::LoadObj: ", warnings); - throw gz::Exception("Error loading obj: " + errors, "loadModel"); - } - if (!warnings.empty()) { - vLog.warning("Warning from tinyobj::LoadObj: ", warnings); - } - std::unordered_map uniqueVertices; - for (const auto& shape : shapes) { - for (const auto& index : shape.mesh.indices) { - Vertex3D vertex; - vertex.pos = { - attrib.vertices[3 * index.vertex_index + 0], - attrib.vertices[3 * index.vertex_index + 1], - attrib.vertices[3 * index.vertex_index + 2] - }; - vertex.texCoord = { - attrib.vertices[2 * index.texcoord_index + 0], - // obj: y = 0 means bottom, vulkan: y = 0 means top - 1.0f - attrib.vertices[2 * index.texcoord_index + 1], - }; - vertex.color = { 1.0f, 1.0f, 1.0f }; - if (!uniqueVertices.contains(vertex)) { - uniqueVertices.insert({ vertex, model.vertices.size() }); - model.vertices.push_back(vertex); - } - /* model.vertices.push_back(vertex); */ - model.indices.push_back(uniqueVertices[vertex]); - } - } - } - - // PUBLIC INTERFACE: CREATION AND DESTRUCTION // COMMAND BUFFER void VulkanInstance::createCommandBuffers(std::vector& commandBuffers) { @@ -397,7 +385,7 @@ using std::uint32_t; // GRAPHICS PIPELINE template - void VulkanInstance::createGraphicsPipeline(vk::GraphicsPipelineCreateInfo&& pipelineCI, const std::vector* descriptorSetLayouts, bool useDepthStencil, Pipeline& pipeline) { + void VulkanInstance::createGraphicsPipeline(vk::GraphicsPipelineCreateInfo&& pipelineCI, const std::vector& descriptorSetLayouts, const std::vector& pushConstantRanges, bool useDepthStencil, Pipeline& pipeline) { // 1) sanity checks if (pipelineCI.pStages == nullptr) { throw VkUserError("pStages is nullptr", "VulkanInstance::createGraphicsPipeline"); @@ -406,7 +394,32 @@ using std::uint32_t; throw VkUserError("renderPass is VK_NULL_HANDLE", "VulkanInstance::createGraphicsPipeline"); } - // 2) construct default create infos + // 2) create layout + vk::PipelineLayoutCreateInfo pipelineLayoutCI; + // TODO + /* pipelineLayoutCI.setPushConstantRanges() */ + if (pushConstantRanges.size() > 0) { + pipelineLayoutCI.setPushConstantRanges(pushConstantRanges); + } + + if (descriptorSetLayouts.size() > 0) { + pipelineLayoutCI.setSetLayouts(descriptorSetLayouts); + } + + VkPipelineLayoutCreateInfo pipelineLayoutCI2 = static_cast(pipelineLayoutCI); + vLog.log0("createGraphicsPipeline: ", pipelineLayoutCI2.setLayoutCount); + + + vk::Result result = device.createPipelineLayout(&pipelineLayoutCI, nullptr, &pipeline.layout); + if (result != vk::Result::eSuccess) { + throw getVkException(result, "Failed to create pipeline layout", "VulkanInstance::createGraphicsPipeline"); + } + vLog.log0("createGraphicsPipeline: created layout: ( descriptorSetLayouts:", descriptorSetLayouts.size(), ")"); + vLog.log0("createGraphicsPipeline:", pipelineLayoutCI.setLayoutCount); + + pipelineCI.setLayout(pipeline.layout); + + // 3) construct default create infos // default vertexInputStateCI vk::VertexInputBindingDescription bindingD = VertexT::getBindingDescription(); // array @@ -512,20 +525,6 @@ using std::uint32_t; /* .back = {}, */ }; - vk::PipelineLayoutCreateInfo pipelineLayoutCI; - // TODO - /* pipelineLayoutCI.setPushConstantRanges() */ - - if (descriptorSetLayouts != nullptr) { - pipelineLayoutCI.setSetLayouts(*descriptorSetLayouts); - } - vk::Result result = device.createPipelineLayout(&pipelineLayoutCI, nullptr, &pipeline.layout); - if (result != vk::Result::eSuccess) { - throw getVkException(result, "Failed to create pipeline layout", "VulkanInstance::createGraphicsPipeline"); - } - - pipelineCI.setLayout(pipeline.layout); - if (pipelineCI.pVertexInputState == nullptr) { pipelineCI.setPVertexInputState(&vertexInputStateCI); } if (pipelineCI.pInputAssemblyState == nullptr) { pipelineCI.setPInputAssemblyState(&inputAssemblyStateCI); } if (pipelineCI.pViewportState == nullptr) { pipelineCI.setPViewportState(&viewportStateCI); } @@ -541,10 +540,10 @@ using std::uint32_t; throw getVkException(result, "Could not create graphics pipeline", "createGraphicsPipeline"); } - vLog.log0("createGraphicsPipeline: Created graphics pipeline."); + vLog.log0("createGraphicsPipeline: Created graphics pipeline ( useDepthStencil:", useDepthStencil, ")" ); } - template void VulkanInstance::createGraphicsPipeline(vk::GraphicsPipelineCreateInfo&& pipelineCI, const std::vector* descriptorSetLayouts, bool useDepthStencil, Pipeline& pipeline); - template void VulkanInstance::createGraphicsPipeline(vk::GraphicsPipelineCreateInfo&& pipelineCI, const std::vector* descriptorSetLayouts, bool useDepthStencil, Pipeline& pipeline); + template void VulkanInstance::createGraphicsPipeline(vk::GraphicsPipelineCreateInfo&& pipelineCI, const std::vector& descriptorSetLayouts, const std::vector& pushConstantRanges, bool useDepthStencil, Pipeline& pipeline); + template void VulkanInstance::createGraphicsPipeline(vk::GraphicsPipelineCreateInfo&& pipelineCI, const std::vector& descriptorSetLayouts, const std::vector& pushConstantRanges, bool useDepthStencil, Pipeline& pipeline); // SHADER MODULE @@ -742,7 +741,7 @@ using std::uint32_t; if (result != vk::Result::eSuccess) { throw getVkException(result, "Could not create descriptorSetLayout", "VulkanInstance::createDescriptorSetLayout"); } - vLog.log0("createDescriptorSetLayout: Created descriptor set layout with", bindings.size(), "bindings"); + vLog.log0("createDescriptorSetLayout: Created descriptor set layout with the following bindings:", bindings); } @@ -838,7 +837,7 @@ using std::uint32_t; // IMAGE VIEW - void VulkanInstance::createImageView(vk::Format format, vk::Image& image, vk::ImageView& imageView, vk::ImageAspectFlags aspectFlags) { + void VulkanInstance::createImageView(vk::Format format, const vk::Image& image, vk::ImageView& imageView, vk::ImageAspectFlags aspectFlags) { vk::ImageViewCreateInfo imageViewCI { .image = image, .viewType = vk::ImageViewType::e2D, @@ -861,6 +860,7 @@ using std::uint32_t; if (result != vk::Result::eSuccess) { throw getVkException(result, "Could not create image view", "VulkanInstance::createImageViews"); } + vLog.log0("createImageView: Created image view with format", format); } @@ -955,13 +955,13 @@ using std::uint32_t; barrier.dstAccessMask = vk::AccessFlagBits2::eTransferWrite; commandPool = POOL_TRANSFER; } - else if (oldLayout == vk::ImageLayout::eTransferDstOptimal and - newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) { - barrier.srcStageMask = vk::PipelineStageFlagBits2::eTransfer; - barrier.srcAccessMask = vk::AccessFlagBits2::eTransferWrite; - barrier.dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader; - barrier.dstAccessMask = vk::AccessFlagBits2::eShaderRead; - commandPool = POOL_GRAPHICS; + else if (oldLayout == vk::ImageLayout::eUndefined and + newLayout == vk::ImageLayout::eColorAttachmentOptimal) { + barrier.srcStageMask = vk::PipelineStageFlagBits2::eNone; + barrier.srcAccessMask = vk::AccessFlagBits2::eNone; + barrier.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput; + barrier.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite; + commandPool = POOL_TRANSFER; } else if (oldLayout == vk::ImageLayout::eShaderReadOnlyOptimal and newLayout == vk::ImageLayout::eTransferDstOptimal) { @@ -971,6 +971,14 @@ using std::uint32_t; barrier.dstAccessMask = vk::AccessFlagBits2::eTransferWrite; commandPool = POOL_GRAPHICS; } + else if (oldLayout == vk::ImageLayout::eTransferDstOptimal and + newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) { + barrier.srcStageMask = vk::PipelineStageFlagBits2::eTransfer; + barrier.srcAccessMask = vk::AccessFlagBits2::eTransferWrite; + barrier.dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader; + barrier.dstAccessMask = vk::AccessFlagBits2::eShaderRead; + commandPool = POOL_GRAPHICS; + } else if (oldLayout == vk::ImageLayout::eTransferDstOptimal and newLayout == vk::ImageLayout::ePresentSrcKHR) { barrier.srcStageMask = vk::PipelineStageFlagBits2::eTransfer; @@ -979,6 +987,22 @@ using std::uint32_t; barrier.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput; commandPool = POOL_GRAPHICS; } + else if (oldLayout == vk::ImageLayout::eColorAttachmentOptimal and + newLayout == vk::ImageLayout::ePresentSrcKHR) { + barrier.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput; + barrier.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite; + barrier.dstAccessMask = vk::AccessFlagBits2::eMemoryRead; + barrier.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput; + commandPool = POOL_GRAPHICS; + } + else if (oldLayout == vk::ImageLayout::eColorAttachmentOptimal and + newLayout == vk::ImageLayout::eTransferSrcOptimal) { + barrier.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput; + barrier.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite; + barrier.dstAccessMask = vk::AccessFlagBits2::eTransferRead; + barrier.dstStageMask = vk::PipelineStageFlagBits2::eTransfer; + commandPool = POOL_GRAPHICS; + } else { throw VkUserError(std::string("Unsupported layout transition") + ::toString(oldLayout) + "->" + ::toString(newLayout), "VulkanInstance::transitionImageLayout"); } @@ -1001,7 +1025,14 @@ using std::uint32_t; // // STATIC: DEBUG LOG // - gz::Log VulkanInstance::vLog("vulkan.log", true, true, "Vulkan", settings::VULKAN_MESSAGE_PREFIX_COLOR, true, settings::VULKAN_MESSAGE_TIME_COLOR); + gz::Log VulkanInstance::vLog(LogCreateInfo { + .logfile = "vulkan.log", + .showLog = true, + .storeLog = true, + .prefix = "Vulkan", + .prefixColor = settings::VULKAN_MESSAGE_PREFIX_COLOR, + .showTime = true, + .timeColor = settings::VULKAN_MESSAGE_TIME_COLOR}); VKAPI_ATTR VkBool32 VKAPI_CALL VulkanInstance::debugLog( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverety, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -1389,13 +1420,16 @@ using std::uint32_t; qFamiliyIndicesSet.insert(qFamilyIndices.transferFamily.value()); } + std::vector qPriorities; + // make sure no reallocation happes as that would invalidate the pointers + qPriorities.reserve(qFamiliyIndicesSet.size()); std::vector deviceQueueCI; for (uint32_t index : qFamiliyIndicesSet) { deviceQueueCI.push_back(vk::DeviceQueueCreateInfo()); deviceQueueCI.back().queueFamilyIndex = index; deviceQueueCI.back().queueCount = 1; - float qPriority = 1.0f; - deviceQueueCI.back().pQueuePriorities = &qPriority; + qPriorities.emplace_back(1.0f); + deviceQueueCI.back().pQueuePriorities = &(qPriorities.back()); } PhysicalDeviceFeatures enabledFeatures = getRequiredDeviceFeatures(); @@ -1719,41 +1753,46 @@ using std::uint32_t; // HANDLE OWNERSHIP bool VulkanInstance::lastColorWhite; std::vector> VulkanInstance::objectsUsingVulkan; + std::set VulkanInstance::foundHandles; - const std::regex reAddress(R"(0x[0-9a-f]+)"); + const std::regex reAddress(R"(handle = (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->contains(address)) { - handleOwnerString += it->str() + " - " + obj->getName(); - address = 0; - foundHandle = true; - } + /* for (auto& obj : objectsUsingVulkan) { vLog(obj); }; */ + handleOwnerString = "Handle ownerships: [ "; + + foundHandles.clear(); + while (re::regex_search(message, match, reAddress)) { + /* vLog("getHandleOwnerString: found", match.size(), "addresses"); */ + uint64_t address; + std::stringstream ss; + ss << std::hex << match.str(1); + ss >> address; + // filter duplicates + if (foundHandles.contains(address)) { + goto stopOwnerSearch; + } + for (auto& obj : objectsUsingVulkan) { + if (obj->contains(address)) { + handleOwnerString += match.str(1) + " - " + obj->getName() + ", "; + foundHandles.insert(address); + address = 0; + goto stopOwnerSearch; } - if (address != 0) { - handleOwnerString += "Unknown"; - } - handleOwnerString += ", "; - } - if (foundHandle) { - handleOwnerString.erase(handleOwnerString.size()-2); - handleOwnerString += " ]"; - } - else { - handleOwnerString.clear(); } + // if not found + handleOwnerString += match.str(1) + " - Unknown, "; +stopOwnerSearch: + message = std::string_view(message.begin() + match.position() + match.length(), message.end()); + } // while + if (foundHandles.size() > 0) { + handleOwnerString.erase(handleOwnerString.size() - 2); + handleOwnerString += " ]"; + } + else { + handleOwnerString.clear(); } }