Browse Source

vulkan: add memory barriers for compute-writable textures.

Sasha Szpakowski 8 months ago
parent
commit
58de1b43fb
2 changed files with 112 additions and 104 deletions
  1. 67 79
      src/modules/graphics/vulkan/Texture.cpp
  2. 45 25
      src/modules/graphics/vulkan/Vulkan.cpp

+ 67 - 79
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;
 	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))
 	if (isPixelFormatDepthStencil(format))
 	{
 	{
@@ -482,12 +479,9 @@ void Texture::clear(const VulkanImageData &data)
 		vkCmdClearColorImage(commandBuffer, data.image, clearLayout, &clearColor, 1, &range);
 		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)
 VkClearColorValue Texture::getClearColor(love::graphics::Texture *texture, const ColorD &color)
@@ -531,11 +525,13 @@ void Texture::generateMipmapsInternal()
 {
 {
 	auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
 	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<uint32_t>(getMipmapCount()),
-			rootView.startLayer, static_cast<uint32_t>(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<uint32_t>(getMipmapCount()),
+		rootView.startLayer, static_cast<uint32_t>(layerCount));
 
 
 	VkImageMemoryBarrier barrier{};
 	VkImageMemoryBarrier barrier{};
 	barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
 	barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
@@ -559,10 +555,23 @@ void Texture::generateMipmapsInternal()
 		barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
 		barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
 
 
 		if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
 		if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
+		{
 			vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
 			vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
 				0, nullptr,
 				0, nullptr,
 				0, nullptr,
 				0, nullptr,
 				1, &barrier);
 				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{};
 		VkImageBlit blit{};
 		blit.srcOffsets[0] = { 0, 0, 0 };
 		blit.srcOffsets[0] = { 0, 0, 0 };
@@ -596,30 +605,27 @@ void Texture::generateMipmapsInternal()
 				VK_FILTER_LINEAR);
 				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,
 				0, nullptr,
 				0, nullptr,
 				1, &barrier);
 				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<uint32_t>(getMipmapCount()),
+		rootView.startLayer, static_cast<uint32_t>(layerCount));
 }
 }
 
 
 void Texture::uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r)
 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();
 	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);
-
-		vkCmdCopyBufferToImage(
-			commandBuffer,
-			stagingBuffer,
-			imageData.image,
-			VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-			1,
-			&region
-		);
-
-		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,
-			&region
-		);
+	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,
+		level, 1, baseLayer, 1);
+
+	vkCmdCopyBufferToImage(
+		commandBuffer,
+		stagingBuffer,
+		imageData.image,
+		copyDstLayout,
+		1,
+		&region
+	);
+
+	Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget,
+		copyDstLayout, imageData.layout,
+		level, 1, baseLayer, 1);
 
 
 	vgfx->queueCleanUp([allocator = allocator, stagingBuffer, vmaAllocation]() {
 	vgfx->queueCleanUp([allocator = allocator, stagingBuffer, vmaAllocation]() {
 		vmaDestroyBuffer(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<uint32_t>(rect.w);
 	region.imageExtent.width = static_cast<uint32_t>(rect.w);
 	region.imageExtent.height = static_cast<uint32_t>(rect.h);
 	region.imageExtent.height = static_cast<uint32_t>(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, &region);
+	vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), imageData.image, copyDstLayout, 1, &region);
 
 
-		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, &region);
+	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)
 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<uint32_t>(rect.h);
 	region.imageExtent.height = static_cast<uint32_t>(rect.h);
 	region.imageExtent.depth = 1;
 	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, &region);
+	vkCmdCopyImageToBuffer(commandBuffer, imageData.image, copySrcLayout, (VkBuffer) dest->getHandle(), 1, &region);
 
 
-		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, &region);
+	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.
 	// TODO: This could be combined with the cmdTransitionImageLayout barrier.
 	((Buffer *)dest)->postGPUWriteBarrier(commandBuffer);
 	((Buffer *)dest)->postGPUWriteBarrier(commandBuffer);

+ 45 - 25
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)
 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);
 	const PixelFormatInfo &info = getPixelFormatInfo(format);
 
 
 	VkPipelineStageFlags sourceStage = 0;
 	VkPipelineStageFlags sourceStage = 0;
 	VkPipelineStageFlags destinationStage = 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)
 	if (info.color)
-		barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;
+		imageBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;
 	if (info.depth)
 	if (info.depth)
-		barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
+		imageBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
 	if (info.stencil)
 	if (info.stencil)
-		barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
-
-	vkCmdPipelineBarrier(
-		commandBuffer,
-		sourceStage, destinationStage,
-		0,
-		0, nullptr,
-		0, nullptr,
-		1, &barrier
-	);
+		imageBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
+
+	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
 } // vulkan