Browse Source

vulkan: memory barriers for buffer copy/write operations.

Sasha Szpakowski 10 months ago
parent
commit
64e4eaec74

+ 1 - 1
src/modules/graphics/Graphics.cpp

@@ -110,7 +110,7 @@ namespace opengl { extern love::graphics::Graphics *createInstance(); }
 namespace metal { extern love::graphics::Graphics *createInstance(); }
 #endif
 #ifdef LOVE_GRAPHICS_VULKAN
-namespace vulkan { extern love::graphics::Graphics* createInstance(); }
+namespace vulkan { extern love::graphics::Graphics *createInstance(); }
 #endif
 
 static const Renderer rendererOrder[] = {

+ 63 - 4
src/modules/graphics/vulkan/Buffer.cpp

@@ -84,7 +84,11 @@ bool Buffer::loadVolatile()
 		throw love::Exception("failed to create buffer");
 
 	if (zeroInitialize)
-		vkCmdFillBuffer(vgfx->getCommandBufferForDataTransfer(), buffer, 0, VK_WHOLE_SIZE, 0);
+	{
+		auto cmd = vgfx->getCommandBufferForDataTransfer();
+		vkCmdFillBuffer(cmd, buffer, 0, VK_WHOLE_SIZE, 0);
+		postGPUWriteBarrier(cmd);
+	}
 
 	if (initialData)
 		fill(0, size, initialData);
@@ -235,7 +239,10 @@ bool Buffer::fill(size_t offset, size_t size, const void *data)
 	bufferCopy.dstOffset = offset;
 	bufferCopy.size = size;
 
-	vkCmdCopyBuffer(vgfx->getCommandBufferForDataTransfer(), fillBuffer, buffer, 1, &bufferCopy);
+	auto cmd = vgfx->getCommandBufferForDataTransfer();
+	vkCmdCopyBuffer(cmd, fillBuffer, buffer, 1, &bufferCopy);
+
+	postGPUWriteBarrier(cmd);
 
 	vgfx->queueCleanUp([allocator = allocator, fillBuffer = fillBuffer, fillAllocation = fillAllocation]() {
 		vmaDestroyBuffer(allocator, fillBuffer, fillAllocation);
@@ -258,7 +265,10 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize)
 		if (~memoryProperties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
 			vmaFlushAllocation(allocator, stagingAllocation, bufferCopy.srcOffset, usedsize);
 
-		vkCmdCopyBuffer(vgfx->getCommandBufferForDataTransfer(), stagingBuffer, buffer, 1, &bufferCopy);
+		auto cmd = vgfx->getCommandBufferForDataTransfer();
+		vkCmdCopyBuffer(cmd, stagingBuffer, buffer, 1, &bufferCopy);
+
+		postGPUWriteBarrier(cmd);
 
 		vgfx->queueCleanUp([allocator = allocator, stagingBuffer = stagingBuffer, stagingAllocation = stagingAllocation]() {
 			vmaDestroyBuffer(allocator, stagingBuffer, stagingAllocation);
@@ -268,7 +278,9 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize)
 
 void Buffer::clearInternal(size_t offset, size_t size)
 {
-	vkCmdFillBuffer(vgfx->getCommandBufferForDataTransfer(), buffer, offset, size, 0);
+	auto cmd = vgfx->getCommandBufferForDataTransfer();
+	vkCmdFillBuffer(cmd, buffer, offset, size, 0);
+	postGPUWriteBarrier(cmd);
 }
 
 void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
@@ -281,6 +293,53 @@ void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t de
 	bufferCopy.size = size;
 
 	vkCmdCopyBuffer(commandBuffer, buffer, (VkBuffer) dest->getHandle(), 1, &bufferCopy);
+
+	((Buffer *)dest)->postGPUWriteBarrier(commandBuffer);
+}
+
+void Buffer::postGPUWriteBarrier(VkCommandBuffer cmd)
+{
+	VkMemoryBarrier barrier{};
+	barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+	barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+	VkPipelineStageFlags dstStageMask = 0;
+	addPostGPUWriteBarrierFlags(barrier.dstAccessMask, dstStageMask);
+
+	vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask, 0, 1, &barrier, 0, nullptr, 0, nullptr);
+}
+
+void Buffer::addPostGPUWriteBarrierFlags(VkAccessFlags &dstAccessFlags, VkPipelineStageFlags &dstStageFlags)
+{
+	// All buffers can be copied to and from.
+	dstAccessFlags |= VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
+	dstStageFlags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
+
+	if (usageFlags & BUFFERUSAGEFLAG_VERTEX)
+	{
+		dstAccessFlags |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
+		dstStageFlags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
+	}
+	if (usageFlags & BUFFERUSAGEFLAG_INDEX)
+	{
+		dstAccessFlags |= VK_ACCESS_INDEX_READ_BIT;
+		dstStageFlags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
+	}
+	if (usageFlags & BUFFERUSAGEFLAG_TEXEL)
+	{
+		dstAccessFlags |= VK_ACCESS_SHADER_READ_BIT;
+		dstStageFlags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+	}
+	if (usageFlags & BUFFERUSAGEFLAG_SHADER_STORAGE)
+	{
+		dstAccessFlags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+		dstStageFlags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+	}
+	if (usageFlags & BUFFERUSAGEFLAG_INDIRECT_ARGUMENTS)
+	{
+		dstAccessFlags |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+		dstStageFlags |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
+	}
 }
 
 } // vulkan

+ 3 - 0
src/modules/graphics/vulkan/Buffer.h

@@ -55,6 +55,9 @@ public:
 	ptrdiff_t getHandle() const override;
 	ptrdiff_t getTexelBufferHandle() const override;
 
+	void postGPUWriteBarrier(VkCommandBuffer cmd);
+	void addPostGPUWriteBarrierFlags(VkAccessFlags &dstAccessFlags, VkPipelineStageFlags &dstStageFlags);
+
 private:
 
 	void clearInternal(size_t offset, size_t size) override;

+ 4 - 0
src/modules/graphics/vulkan/Texture.cpp

@@ -21,6 +21,7 @@
 #include "Texture.h"
 #include "Graphics.h"
 #include "Vulkan.h"
+#include "Buffer.h"
 
 #include <limits>
 
@@ -683,6 +684,9 @@ void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const
 	}
 	else
 		vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, &region);
+
+	// TODO: This could be combined with the cmdTransitionImageLayout barrier.
+	((Buffer *)dest)->postGPUWriteBarrier(commandBuffer);
 }
 
 } // vulkan