From 58de1b43fb84293846793b1838af27288e2e5893 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Wed, 18 Dec 2024 22:12:43 -0400 Subject: [PATCH] vulkan: add memory barriers for compute-writable textures. --- src/modules/graphics/vulkan/Texture.cpp | 142 +++++++++++------------- src/modules/graphics/vulkan/Vulkan.cpp | 68 ++++++++---- 2 files changed, 109 insertions(+), 101 deletions(-) diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp index 2555f3636..ed2821094 100644 --- a/src/modules/graphics/vulkan/Texture.cpp +++ b/src/modules/graphics/vulkan/Texture.cpp @@ -462,12 +462,9 @@ void Texture::clear(const VulkanImageData &data) VkImageLayout clearLayout = data.layout == VK_IMAGE_LAYOUT_GENERAL ? data.layout : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - if (clearLayout != data.layout) - { - Vulkan::cmdTransitionImageLayout(commandBuffer, data.image, format, renderTarget, - data.layout, clearLayout, - 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS); - } + Vulkan::cmdTransitionImageLayout(commandBuffer, data.image, format, renderTarget, + data.layout, clearLayout, + 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS); if (isPixelFormatDepthStencil(format)) { @@ -482,12 +479,9 @@ void Texture::clear(const VulkanImageData &data) vkCmdClearColorImage(commandBuffer, data.image, clearLayout, &clearColor, 1, &range); } - if (clearLayout != data.layout) - { - Vulkan::cmdTransitionImageLayout(commandBuffer, data.image, format, renderTarget, - clearLayout, data.layout, - 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS); - } + Vulkan::cmdTransitionImageLayout(commandBuffer, data.image, format, renderTarget, + clearLayout, data.layout, + 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS); } VkClearColorValue Texture::getClearColor(love::graphics::Texture *texture, const ColorD &color) @@ -531,11 +525,13 @@ void Texture::generateMipmapsInternal() { auto commandBuffer = vgfx->getCommandBufferForDataTransfer(); - if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) - Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, - imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - rootView.startMipmap, static_cast(getMipmapCount()), - rootView.startLayer, static_cast(layerCount)); + auto dstPipelineStages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT + | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; + + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, + imageData.layout, imageData.layout == VK_IMAGE_LAYOUT_GENERAL ? imageData.layout : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + rootView.startMipmap, static_cast(getMipmapCount()), + rootView.startLayer, static_cast(layerCount)); VkImageMemoryBarrier barrier{}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -559,10 +555,23 @@ void Texture::generateMipmapsInternal() barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) + { vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + else + { + VkMemoryBarrier memoryBarrier{}; + memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + memoryBarrier.srcAccessMask = barrier.srcAccessMask; + memoryBarrier.dstAccessMask = barrier.dstAccessMask; + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + 1, &memoryBarrier, + 0, nullptr, + 0, nullptr); + } VkImageBlit blit{}; blit.srcOffsets[0] = { 0, 0, 0 }; @@ -596,30 +605,27 @@ void Texture::generateMipmapsInternal() VK_FILTER_LINEAR); } - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.newLayout = imageData.layout; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + // This sucks, but we want the whole texture to be VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL because we + // transition the whole thing back to the original layout after the loop. + if (i == mipLevels - 1 && imageData.layout != VK_IMAGE_LAYOUT_GENERAL) + { + barrier.subresourceRange.baseMipLevel = rootView.startMipmap + i; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) - vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } } - barrier.subresourceRange.baseMipLevel = rootView.startMipmap + mipLevels - 1; - barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.newLayout = imageData.layout; - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) - vkCmdPipelineBarrier(commandBuffer, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, - 0, nullptr, - 0, nullptr, - 1, &barrier); + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, + imageData.layout == VK_IMAGE_LAYOUT_GENERAL ? imageData.layout : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageData.layout, + rootView.startMipmap, static_cast(getMipmapCount()), + rootView.startLayer, static_cast(layerCount)); } void Texture::uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r) @@ -668,34 +674,24 @@ void Texture::uploadByteData(const void *data, size_t size, int level, int slice auto commandBuffer = vgfx->getCommandBufferForDataTransfer(); - if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) - { - Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, - imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - level, 1, baseLayer, 1); + VkImageLayout copyDstLayout = imageData.layout == VK_IMAGE_LAYOUT_GENERAL ? imageData.layout : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - vkCmdCopyBufferToImage( - commandBuffer, - stagingBuffer, - imageData.image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - ®ion - ); + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, + imageData.layout, copyDstLayout, + level, 1, baseLayer, 1); - Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageData.layout, - level, 1, baseLayer, 1); - } - else - vkCmdCopyBufferToImage( - commandBuffer, - stagingBuffer, - imageData.image, - imageData.layout, - 1, - ®ion - ); + vkCmdCopyBufferToImage( + commandBuffer, + stagingBuffer, + imageData.image, + copyDstLayout, + 1, + ®ion + ); + + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, + copyDstLayout, imageData.layout, + level, 1, baseLayer, 1); vgfx->queueCleanUp([allocator = allocator, stagingBuffer, vmaAllocation]() { vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation); @@ -722,16 +718,12 @@ void Texture::copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int region.imageExtent.width = static_cast(rect.w); region.imageExtent.height = static_cast(rect.h); - if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) - { - Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1); + VkImageLayout copyDstLayout = imageData.layout == VK_IMAGE_LAYOUT_GENERAL ? imageData.layout : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, imageData.layout, copyDstLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1); - vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), imageData.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), imageData.image, copyDstLayout, 1, ®ion); - Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageData.layout, layers.mipLevel, 1, layers.baseArrayLayer, 1); - } - else - vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), imageData.image, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion); + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, copyDstLayout, imageData.layout, layers.mipLevel, 1, layers.baseArrayLayer, 1); } void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size) @@ -755,16 +747,12 @@ void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const region.imageExtent.height = static_cast(rect.h); region.imageExtent.depth = 1; - if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) - { - Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1); + VkImageLayout copySrcLayout = imageData.layout == VK_IMAGE_LAYOUT_GENERAL ? imageData.layout : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, imageData.layout, copySrcLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1); - vkCmdCopyImageToBuffer(commandBuffer, imageData.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, (VkBuffer) dest->getHandle(), 1, ®ion); + vkCmdCopyImageToBuffer(commandBuffer, imageData.image, copySrcLayout, (VkBuffer) dest->getHandle(), 1, ®ion); - Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageData.layout, layers.mipLevel, 1, layers.baseArrayLayer, 1); - } - else - vkCmdCopyImageToBuffer(commandBuffer, imageData.image, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, ®ion); + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, copySrcLayout, imageData.layout, layers.mipLevel, 1, layers.baseArrayLayer, 1); // TODO: This could be combined with the cmdTransitionImageLayout barrier. ((Buffer *)dest)->postGPUWriteBarrier(commandBuffer); diff --git a/src/modules/graphics/vulkan/Vulkan.cpp b/src/modules/graphics/vulkan/Vulkan.cpp index 224335734..804f952e4 100644 --- a/src/modules/graphics/vulkan/Vulkan.cpp +++ b/src/modules/graphics/vulkan/Vulkan.cpp @@ -883,41 +883,61 @@ void Vulkan::addImageLayoutTransitionOptions(bool previous, bool renderTarget, b void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, PixelFormat format, bool renderTarget, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t baseLevel, uint32_t levelCount, uint32_t baseLayer, uint32_t layerCount) { - VkImageMemoryBarrier barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = oldLayout; - barrier.newLayout = newLayout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.baseMipLevel = baseLevel; - barrier.subresourceRange.levelCount = levelCount; - barrier.subresourceRange.baseArrayLayer = baseLayer; - barrier.subresourceRange.layerCount = layerCount; + VkImageMemoryBarrier imageBarrier{}; + imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageBarrier.oldLayout = oldLayout; + imageBarrier.newLayout = newLayout; + imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.image = image; + imageBarrier.subresourceRange.baseMipLevel = baseLevel; + imageBarrier.subresourceRange.levelCount = levelCount; + imageBarrier.subresourceRange.baseArrayLayer = baseLayer; + imageBarrier.subresourceRange.layerCount = layerCount; const PixelFormatInfo &info = getPixelFormatInfo(format); VkPipelineStageFlags sourceStage = 0; VkPipelineStageFlags destinationStage = 0; - addImageLayoutTransitionOptions(true, renderTarget, info.depth || info.stencil, oldLayout, barrier.srcAccessMask, sourceStage); - addImageLayoutTransitionOptions(false, renderTarget, info.depth || info.stencil, newLayout, barrier.dstAccessMask, destinationStage); + addImageLayoutTransitionOptions(true, renderTarget, info.depth || info.stencil, oldLayout, imageBarrier.srcAccessMask, sourceStage); + addImageLayoutTransitionOptions(false, renderTarget, info.depth || info.stencil, newLayout, imageBarrier.dstAccessMask, destinationStage); if (info.color) - barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; + imageBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; if (info.depth) - barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + imageBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; if (info.stencil) - barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + imageBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - vkCmdPipelineBarrier( - commandBuffer, - sourceStage, destinationStage, - 0, - 0, nullptr, - 0, nullptr, - 1, &barrier - ); + if (oldLayout != newLayout) + { + vkCmdPipelineBarrier( + commandBuffer, + sourceStage, destinationStage, + 0, + 0, nullptr, + 0, nullptr, + 1, &imageBarrier + ); + } + else + { + // No layout transition needed, but we do still need a memory barrier. + VkMemoryBarrier memoryBarrier{}; + memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + memoryBarrier.srcAccessMask = imageBarrier.srcAccessMask; + memoryBarrier.dstAccessMask = imageBarrier.dstAccessMask; + + vkCmdPipelineBarrier( + commandBuffer, + sourceStage, destinationStage, + 0, + 1, &memoryBarrier, + 0, nullptr, + 0, nullptr + ); + } } } // vulkan