From 94c58e15996aa723b3e6e0aabd01a7ba62c59b2d Mon Sep 17 00:00:00 2001 From: niki Date: Sat, 30 Jul 2022 00:26:49 +0200 Subject: [PATCH] vulkan: fix video rendering --- src/modules/graphics/vulkan/Graphics.cpp | 38 +++++++++++++++++++----- src/modules/graphics/vulkan/Graphics.h | 5 ++-- src/modules/graphics/vulkan/Shader.cpp | 5 ++++ src/modules/graphics/vulkan/Texture.cpp | 10 +++++-- src/modules/graphics/vulkan/Vulkan.cpp | 7 +++++ 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index b3e94d753..7a559f19a 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -142,6 +142,9 @@ namespace love { } imagesInFlight[imageIndex] = inFlightFences[currentFrame]; + // all data transfers should happen before any draw calls. + std::vector submitCommandbuffers = { dataTransferCommandBuffers.at(currentFrame), commandBuffers.at(imageIndex) }; + VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -151,8 +154,8 @@ namespace love { submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffers[imageIndex]; + submitInfo.commandBufferCount = static_cast(submitCommandbuffers.size()); + submitInfo.pCommandBuffers = submitCommandbuffers.data(); VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) }; submitInfo.signalSemaphoreCount = 1; @@ -213,10 +216,10 @@ namespace love { initCapabilities(); createSwapChain(); createImageViews(); - createDefaultShaders(); createCommandPool(); createCommandBuffers(); createDefaultTexture(); + createDefaultShaders(); createQuadIndexBuffer(); createSyncObjects(); startRecordingGraphicsCommands(); @@ -510,6 +513,9 @@ namespace love { if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) { throw love::Exception("failed to begin recording command buffer"); } + if (vkBeginCommandBuffer(dataTransferCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) { + throw love::Exception("failed to begin recording data transfer command buffer"); + } Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); @@ -519,8 +525,6 @@ namespace love { } void Graphics::endRecordingGraphicsCommands() { - const auto& commandBuffer = commandBuffers.at(imageIndex); - endRenderPass(); Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); @@ -528,6 +532,9 @@ namespace love { if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) { throw love::Exception("failed to record command buffer"); } + if (vkEndCommandBuffer(dataTransferCommandBuffers.at(currentFrame)) != VK_SUCCESS) { + throw love::Exception("failed to record data transfer command buffer"); + } } void Graphics::updatedBatchedDrawBuffers() { @@ -547,12 +554,16 @@ namespace love { return minUniformBufferOffsetAlignment; } + graphics::Texture* Graphics::getDefaultTexture() const { + return dynamic_cast(standardTexture.get()); + } + const PFN_vkCmdPushDescriptorSetKHR Graphics::getVkCmdPushDescriptorSetKHRFunctionPointer() const { return vkCmdPushDescriptorSet; } - void Graphics::executeCommand(std::function command, std::function cleanUp) { - command(commandBuffers.at(imageIndex)); + void Graphics::queueDatatransfer(std::function command, std::function cleanUp) { + command(dataTransferCommandBuffers.at(currentFrame)); cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp)); } @@ -1207,7 +1218,6 @@ namespace love { else { configuration.shader->setMainTex(texture); } - configuration.shader->setVideoTextures(standardTexture.get(), standardTexture.get(), standardTexture.get()); ensureGraphicsPipelineConfiguration(configuration); @@ -1416,6 +1426,7 @@ namespace love { void Graphics::createCommandBuffers() { commandBuffers.resize(swapChainImages.size()); + dataTransferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT); VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; @@ -1426,6 +1437,16 @@ namespace love { if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { throw love::Exception("failed to allocate command buffers"); } + + VkCommandBufferAllocateInfo dataTransferAllocInfo{}; + dataTransferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + dataTransferAllocInfo.commandPool = commandPool; + dataTransferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + dataTransferAllocInfo.commandBufferCount = (uint32_t)MAX_FRAMES_IN_FLIGHT; + + if (vkAllocateCommandBuffers(device, &dataTransferAllocInfo, dataTransferCommandBuffers.data()) != VK_SUCCESS) { + throw love::Exception("failed to allocate data transfer command buffers"); + } } void Graphics::createSyncObjects() { @@ -1565,6 +1586,7 @@ namespace love { void Graphics::cleanupSwapChain() { vkDestroyDescriptorPool(device, descriptorPool, nullptr); vkFreeCommandBuffers(device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + vkFreeCommandBuffers(device, commandPool, MAX_FRAMES_IN_FLIGHT, dataTransferCommandBuffers.data()); for (auto const& p : graphicsPipelines) { vkDestroyPipeline(device, p.second, nullptr); } diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 4a0fb1cd9..5cfbcb36c 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -114,8 +114,7 @@ namespace love { GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) override { return nullptr; }; GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) { return nullptr; } - // fixme: better naming for these two functions? - void executeCommand(std::function command, std::function cleanUp); + void queueDatatransfer(std::function command, std::function cleanUp); void queueCleanUp(std::function cleanUp); VkCommandBuffer beginSingleTimeCommands(); @@ -124,6 +123,7 @@ namespace love { uint32_t getNumImagesInFlight() const; const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const; const VkDeviceSize getMinUniformBufferOffsetAlignment() const; + graphics::Texture* getDefaultTexture() const; protected: graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { @@ -190,6 +190,7 @@ namespace love { std::vector> graphicsPipelines; // FIXME improve performance by using a hash map VkCommandPool commandPool = VK_NULL_HANDLE; std::vector commandBuffers; + std::vector dataTransferCommandBuffers; VkDescriptorPool descriptorPool = VK_NULL_HANDLE; std::vector imageAvailableSemaphores; std::vector renderFinishedSemaphores; diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index ea8a279f7..52d0b5195 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -311,6 +311,11 @@ namespace love { auto vgfx = (Graphics*)gfx; device = vgfx->getDevice(); + mainTex = vgfx->getDefaultTexture(); + ytexture = vgfx->getDefaultTexture(); + crtexture = vgfx->getDefaultTexture(); + cbtexture = vgfx->getDefaultTexture(); + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) { if (!stages[i]) continue; diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp index f92cd3f1d..d27a13a2c 100644 --- a/src/modules/graphics/vulkan/Texture.cpp +++ b/src/modules/graphics/vulkan/Texture.cpp @@ -43,6 +43,7 @@ namespace love { if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) { throw love::Exception("failed to create image"); } + // fixme: we should use VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL as the default image layout instead of VK_IMAGE_LAYOUT_GENERAL. transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); if (data) { @@ -253,22 +254,25 @@ namespace love { static_cast(r.h), 1 }; - // fixme: use VK_IMAGE_LAYOUT_DST_OPTIMAL + Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vkCmdCopyBufferToImage( commandBuffer, buffer, image, - VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion ); + + Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); }; auto cleanUp = [allocator = allocator, stagingBuffer, vmaAllocation]() { vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation); }; - vgfx->executeCommand(command, cleanUp); + vgfx->queueDatatransfer(command, cleanUp); } } } diff --git a/src/modules/graphics/vulkan/Vulkan.cpp b/src/modules/graphics/vulkan/Vulkan.cpp index c10b2bdaa..8f62011a8 100644 --- a/src/modules/graphics/vulkan/Vulkan.cpp +++ b/src/modules/graphics/vulkan/Vulkan.cpp @@ -491,6 +491,13 @@ namespace love { sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } + else if (oldLayout == VK_IMAGE_LAYOUT_GENERAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL) { barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;