Browse Source

vulkan: memory barrier after compute dispatches.

Sasha Szpakowski 10 months ago
parent
commit
1ee89154c0

+ 32 - 37
src/modules/graphics/vulkan/Buffer.cpp

@@ -62,6 +62,36 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
 	, vgfx(dynamic_cast<Graphics*>(gfx))
 	, usageFlags(settings.usageFlags)
 {
+	// All buffers can be copied to and from.
+	barrierDstAccessFlags = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
+	barrierDstStageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT;
+
+	if (usageFlags & BUFFERUSAGEFLAG_VERTEX)
+	{
+		barrierDstAccessFlags |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
+		barrierDstStageFlags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
+	}
+	if (usageFlags & BUFFERUSAGEFLAG_INDEX)
+	{
+		barrierDstAccessFlags |= VK_ACCESS_INDEX_READ_BIT;
+		barrierDstStageFlags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
+	}
+	if (usageFlags & BUFFERUSAGEFLAG_TEXEL)
+	{
+		barrierDstAccessFlags |= VK_ACCESS_SHADER_READ_BIT;
+		barrierDstStageFlags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+	}
+	if (usageFlags & BUFFERUSAGEFLAG_SHADER_STORAGE)
+	{
+		barrierDstAccessFlags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+		barrierDstStageFlags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+	}
+	if (usageFlags & BUFFERUSAGEFLAG_INDIRECT_ARGUMENTS)
+	{
+		barrierDstAccessFlags |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+		barrierDstStageFlags |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
+	}
+
 	loadVolatile();
 }
 
@@ -302,44 +332,9 @@ void Buffer::postGPUWriteBarrier(VkCommandBuffer cmd)
 	VkMemoryBarrier barrier{};
 	barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
 	barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+	barrier.dstAccessMask = barrierDstAccessFlags;
 
-	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;
-	}
+	vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, barrierDstStageFlags, 0, 1, &barrier, 0, nullptr, 0, nullptr);
 }
 
 } // vulkan

+ 6 - 1
src/modules/graphics/vulkan/Buffer.h

@@ -56,7 +56,9 @@ public:
 	ptrdiff_t getTexelBufferHandle() const override;
 
 	void postGPUWriteBarrier(VkCommandBuffer cmd);
-	void addPostGPUWriteBarrierFlags(VkAccessFlags &dstAccessFlags, VkPipelineStageFlags &dstStageFlags);
+
+	VkAccessFlags getBarrierDstAccessFlags() const { return barrierDstAccessFlags; }
+	VkPipelineStageFlags getBarrierDstStageFlags() const { return barrierDstStageFlags; }
 
 private:
 
@@ -76,6 +78,9 @@ private:
 	BufferUsageFlags usageFlags;
 	Range mappedRange;
 	bool coherent;
+
+	VkAccessFlags barrierDstAccessFlags = 0;
+	VkPipelineStageFlags barrierDstStageFlags = 0;
 };
 
 } // vulkan

+ 78 - 20
src/modules/graphics/vulkan/Graphics.cpp

@@ -351,7 +351,7 @@ void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallback
 				Vulkan::cmdTransitionImageLayout(
 					commandBuffers.at(currentFrame),
 					backbufferImage,
-					swapChainPixelFormat,
+					swapChainPixelFormat, true,
 					VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
 					VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
 			}
@@ -382,7 +382,7 @@ void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallback
 			Vulkan::cmdTransitionImageLayout(
 				commandBuffers.at(currentFrame),
 				backbufferImage,
-				swapChainPixelFormat,
+				swapChainPixelFormat, true,
 				VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
 				VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
 
@@ -405,7 +405,7 @@ void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallback
 			Vulkan::cmdTransitionImageLayout(
 				commandBuffers.at(currentFrame),
 				backbufferImage,
-				swapChainPixelFormat,
+				swapChainPixelFormat, true,
 				VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
 				fakeBackbuffer == nullptr ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
 		}
@@ -1194,8 +1194,56 @@ graphics::StreamBuffer *Graphics::newStreamBuffer(BufferUsage type, size_t size)
 	return new StreamBuffer(this, type, size);
 }
 
+static bool computeDispatchBarrierFlags(Shader *shader, VkAccessFlags &dstAccessFlags, VkPipelineStageFlags &dstStageFlags)
+{
+	for (const auto &info : shader->getActiveTextureInfo())
+	{
+		if ((info.access & Shader::ACCESS_WRITE) == 0)
+			continue;
+
+		if (info.texture == nullptr)
+			return false;
+
+		auto tex = (Texture *) info.texture;
+
+		// All writable images use the GENERAL layout.
+		// TODO: this is pretty messy.
+		VkAccessFlags texAccessFlags = 0;
+		VkPipelineStageFlags texStageFlags = 0;
+		const PixelFormatInfo &info = getPixelFormatInfo(tex->getPixelFormat());
+		Vulkan::setImageLayoutTransitionOptions(false, tex->isRenderTarget(), info, VK_IMAGE_LAYOUT_GENERAL, texAccessFlags, texStageFlags);
+		
+		dstAccessFlags |= texAccessFlags;
+		dstStageFlags |= texStageFlags;
+	}
+
+	for (const auto &info : shader->getActiveStorageBufferInfo())
+	{
+		if ((info.access & Shader::ACCESS_WRITE) == 0)
+			continue;
+
+		if (info.buffer == nullptr)
+			return false;
+
+		auto b = (Buffer *) info.buffer;
+		dstAccessFlags |= b->getBarrierDstAccessFlags();
+		dstStageFlags |= b->getBarrierDstStageFlags();
+	}
+
+	return true;
+}
+
 bool Graphics::dispatch(love::graphics::Shader *shader, int x, int y, int z)
 {
+	auto computeShader = (Shader *) shader;
+
+	VkMemoryBarrier barrier{};
+	barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+	barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
+	VkPipelineStageFlags dstStageMask = 0;
+	if (!computeDispatchBarrierFlags(computeShader, barrier.dstAccessMask, dstStageMask))
+		return false;
+
 	usedShadersInFrame.insert(computeShader);
 
 	if (renderPassState.active)
@@ -1205,14 +1253,26 @@ bool Graphics::dispatch(love::graphics::Shader *shader, int x, int y, int z)
 
 	computeShader->cmdPushDescriptorSets(commandBuffers.at(currentFrame), VK_PIPELINE_BIND_POINT_COMPUTE);
 
-	// TODO: does this need any layout transitions?
 	vkCmdDispatch(commandBuffers.at(currentFrame), (uint32) x, (uint32) y, (uint32) z);
 
+	// Image layout transitions aren't needed, every writable image will be in the GENERAL layout.
+	if (barrier.dstAccessMask != 0 || dstStageMask != 0)
+		vkCmdPipelineBarrier(commandBuffers.at(currentFrame), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, dstStageMask, 0, 1, &barrier, 0, nullptr, 0, nullptr);
+
 	return true;
 }
 
 bool Graphics::dispatch(love::graphics::Shader *shader, love::graphics::Buffer *indirectargs, size_t argsoffset)
 {
+	auto computeShader = (Shader *) shader;
+
+	VkMemoryBarrier barrier{};
+	barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+	barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
+	VkPipelineStageFlags dstStageMask = 0;
+	if (!computeDispatchBarrierFlags(computeShader, barrier.dstAccessMask, dstStageMask))
+		return false;
+
 	usedShadersInFrame.insert(computeShader);
 
 	if (renderPassState.active)
@@ -1222,9 +1282,12 @@ bool Graphics::dispatch(love::graphics::Shader *shader, love::graphics::Buffer *
 
 	computeShader->cmdPushDescriptorSets(commandBuffers.at(currentFrame), VK_PIPELINE_BIND_POINT_COMPUTE);
 
-	// TODO: does this need any layout transitions?
 	vkCmdDispatchIndirect(commandBuffers.at(currentFrame), (VkBuffer) indirectargs->getHandle(), argsoffset);
 
+	// Image layout transitions aren't needed, every writable image will be in the GENERAL layout.
+	if (barrier.dstAccessMask != 0 || dstStageMask != 0)
+		vkCmdPipelineBarrier(commandBuffers.at(currentFrame), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, dstStageMask, 0, 1, &barrier, 0, nullptr, 0, nullptr);
+
 	return true;
 }
 
@@ -1315,7 +1378,7 @@ void Graphics::beginFrame()
 		Vulkan::cmdTransitionImageLayout(
 			commandBuffers.at(currentFrame),
 			swapChainImages[imageIndex],
-			swapChainPixelFormat,
+			swapChainPixelFormat, true,
 			VK_IMAGE_LAYOUT_UNDEFINED,
 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
 	}
@@ -1326,7 +1389,7 @@ void Graphics::beginFrame()
 			Vulkan::cmdTransitionImageLayout(
 				commandBuffers.at(currentFrame),
 				depthImage,
-				depthStencilPixelFormat,
+				depthStencilPixelFormat, true,
 				VK_IMAGE_LAYOUT_UNDEFINED,
 				VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
 
@@ -1334,7 +1397,7 @@ void Graphics::beginFrame()
 			Vulkan::cmdTransitionImageLayout(
 				commandBuffers.at(currentFrame),
 				colorImage,
-				swapChainPixelFormat,
+				swapChainPixelFormat, true,
 				VK_IMAGE_LAYOUT_UNDEFINED,
 				VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
 
@@ -2503,7 +2566,7 @@ void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, b
 
 	FramebufferConfiguration configuration{};
 
-	std::vector<std::tuple<VkImage, PixelFormat, VkImageLayout, VkImageLayout, int, int>> transitionImages;
+	std::vector<std::tuple<VkImage, PixelFormat, bool, VkImageLayout, VkImageLayout, int, int>> transitionImages;
 
 	for (const auto &color : rts.colors)
 	{
@@ -2513,7 +2576,7 @@ void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, b
 		VkImageLayout imagelayout = tex->getImageLayout();
 		if (imagelayout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
 		{
-			transitionImages.push_back({ (VkImage)tex->getHandle(), tex->getPixelFormat(), imagelayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+			transitionImages.push_back({ (VkImage)tex->getHandle(), tex->getPixelFormat(), tex->isRenderTarget(), imagelayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
 				viewinfo.startMipmap + color.mipmap, viewinfo.startLayer + color.slice });
 		}
 	}
@@ -2525,7 +2588,7 @@ void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, b
 		VkImageLayout imagelayout = tex->getImageLayout();
 		if (imagelayout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
 		{
-			transitionImages.push_back({ (VkImage)tex->getHandle(), tex->getPixelFormat(), imagelayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+			transitionImages.push_back({ (VkImage)tex->getHandle(), tex->getPixelFormat(), tex->isRenderTarget(), imagelayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
 				viewinfo.startMipmap + rts.depthStencil.mipmap, viewinfo.startLayer + rts.depthStencil.slice });
 		}
 	}
@@ -2581,8 +2644,8 @@ void Graphics::startRenderPass()
 	renderPassState.framebufferConfiguration.staticData.renderPass = renderPassState.beginInfo.renderPass;
 	renderPassState.beginInfo.framebuffer = getFramebuffer(renderPassState.framebufferConfiguration);
 
-	for (const auto &[image, format, imageLayout, renderLayout, rootmip, rootlayer] : renderPassState.transitionImages)
-		Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), image, format, imageLayout, renderLayout, rootmip, 1, rootlayer, 1);
+	for (const auto &[image, format, renderTarget, imageLayout, renderLayout, rootmip, rootlayer] : renderPassState.transitionImages)
+		Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), image, format, renderTarget, imageLayout, renderLayout, rootmip, 1, rootlayer, 1);
 
 	vkCmdBeginRenderPass(commandBuffers.at(currentFrame), &renderPassState.beginInfo, VK_SUBPASS_CONTENTS_INLINE);
 
@@ -2595,8 +2658,8 @@ void Graphics::endRenderPass()
 
 	vkCmdEndRenderPass(commandBuffers.at(currentFrame));
 
-	for (const auto &[image, format, imageLayout, renderLayout, rootmip, rootlayer] : renderPassState.transitionImages)
-		Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), image, format, renderLayout, imageLayout, rootmip, 1, rootlayer, 1);
+	for (const auto &[image, format, renderTarget, imageLayout, renderLayout, rootmip, rootlayer] : renderPassState.transitionImages)
+		Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), image, format, renderTarget, renderLayout, imageLayout, rootmip, 1, rootlayer, 1);
 
 	for (auto &colorAttachment : renderPassState.renderPassConfiguration.colorAttachments)
 		colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
@@ -2686,11 +2749,6 @@ void Graphics::requestSwapchainRecreation()
 	}
 }
 
-void Graphics::setComputeShader(Shader *shader)
-{
-	computeShader = shader;
-}
-
 VkSampler Graphics::getCachedSampler(const SamplerState &samplerState)
 {
 	auto samplerkey = samplerState.toKey();

+ 1 - 3
src/modules/graphics/vulkan/Graphics.h

@@ -196,7 +196,7 @@ struct RenderpassState
 	RenderPassConfiguration renderPassConfiguration{};
 	FramebufferConfiguration framebufferConfiguration{};
 	VkPipeline pipeline = VK_NULL_HANDLE;
-	std::vector<std::tuple<VkImage, PixelFormat, VkImageLayout, VkImageLayout, int, int>> transitionImages;
+	std::vector<std::tuple<VkImage, PixelFormat, bool, VkImageLayout, VkImageLayout, int, int>> transitionImages;
 	uint32_t numColorAttachments = 0;
 	uint64 packedColorAttachmentFormats = 0;
 	float width = 0.0f;
@@ -267,7 +267,6 @@ public:
 	void addReadbackCallback(std::function<void()> callback);
 	void submitGpuCommands(SubmitMode, void *screenshotCallbackData = nullptr);
 	VkSampler getCachedSampler(const SamplerState &sampler);
-	void setComputeShader(Shader *computeShader);
 	graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
 	const OptionalDeviceExtensions &getEnabledOptionalDeviceExtensions() const;
 	const OptionalInstanceExtensions &getEnabledOptionalInstanceExtensions() const;
@@ -377,7 +376,6 @@ private:
 	std::unordered_map<uint64, VkSampler> samplers;
 	VkCommandPool commandPool = VK_NULL_HANDLE;
 	std::vector<VkCommandBuffer> commandBuffers;
-	Shader *computeShader = nullptr;
 	std::vector<VkSemaphore> imageAvailableSemaphores;
 	std::vector<VkSemaphore> renderFinishedSemaphores;
 	std::vector<VkFence> inFlightFences;

+ 14 - 2
src/modules/graphics/vulkan/Shader.cpp

@@ -337,8 +337,6 @@ void Shader::attach()
 			Vulkan::shaderSwitch();
 		}
 	}
-	else
-		vgfx->setComputeShader(this);
 }
 
 int Shader::getVertexAttributeIndex(const std::string &name)
@@ -777,12 +775,18 @@ void Shader::compileShaders()
 	descriptorBufferViews.clear();
 	descriptorBufferViews.reserve(numBufferViews);
 
+	allTextureInfo.clear();
+	allTextureInfo.reserve(numTextures);
+	storageBufferInfo.clear();
+	storageBufferInfo.reserve(numBuffers);
+
 	if (localUniformData.size() > 0)
 	{
 		VkDescriptorBufferInfo bufferInfo{};
 		bufferInfo.range = localUniformData.size();
 
 		descriptorBuffers.push_back(bufferInfo);
+		storageBufferInfo.push_back({ nullptr, ACCESS_READ }); // Dummy value.
 
 		VkWriteDescriptorSet write{};
 		write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
@@ -807,6 +811,8 @@ void Shader::compileShaders()
 			VkDescriptorImageInfo imageInfo{};
 			descriptorImages.push_back(imageInfo);
 
+			allTextureInfo.push_back({ nullptr, info.access });
+
 			auto texture = activeTextures[info.resourceIndex + i];
 			if (texture != nullptr)
 				setTextureDescriptor(&info, texture, i);
@@ -836,6 +842,8 @@ void Shader::compileShaders()
 			VkDescriptorImageInfo imageInfo{};
 			descriptorImages.push_back(imageInfo);
 
+			allTextureInfo.push_back({ nullptr, info.access });
+
 			auto texture = activeTextures[info.resourceIndex + i];
 			if (texture != nullptr)
 				setTextureDescriptor(&info, texture, i);
@@ -893,6 +901,8 @@ void Shader::compileShaders()
 			VkDescriptorBufferInfo bufferInfo{};
 			descriptorBuffers.push_back(bufferInfo);
 
+			storageBufferInfo.push_back({ nullptr, info.access });
+
 			auto buffer = activeBuffers[info.resourceIndex + i];
 			if (buffer != nullptr)
 				setBufferDescriptor(&info, buffer, i);
@@ -1039,6 +1049,7 @@ void Shader::setTextureDescriptor(const UniformInfo *info, love::graphics::Textu
 	{
 		imageInfo.imageLayout = vkTexture != nullptr ? vkTexture->getImageLayout() : VK_IMAGE_LAYOUT_UNDEFINED;
 		imageInfo.imageView = view;
+		allTextureInfo[info->bindingStartIndex + index].texture = texture;
 		resourceDescriptorsDirty = true;
 	}
 }
@@ -1055,6 +1066,7 @@ void Shader::setBufferDescriptor(const UniformInfo *info, love::graphics::Buffer
 			bufferInfo.buffer = vkbuffer;
 			bufferInfo.offset = 0;
 			bufferInfo.range = range;
+			storageBufferInfo[info->bindingStartIndex + index].buffer = buffer;
 			resourceDescriptorsDirty = true;
 		}
 	}

+ 18 - 0
src/modules/graphics/vulkan/Shader.h

@@ -124,6 +124,18 @@ public:
 		DataBaseType baseType;
 	};
 
+	struct TextureInfo
+	{
+		love::graphics::Texture *texture;
+		Access access;
+	};
+
+	struct BufferInfo
+	{
+		love::graphics::Buffer *buffer;
+		Access access;
+	};
+
 	Shader(StrongRef<love::graphics::ShaderStage> stages[], const CompileOptions &options);
 	virtual ~Shader();
 
@@ -158,6 +170,9 @@ public:
 	VkPipeline getCachedGraphicsPipeline(Graphics *vgfx, const GraphicsPipelineConfigurationCore &configuration);
 	VkPipeline getCachedGraphicsPipeline(Graphics *vgfx, const GraphicsPipelineConfigurationFull &configuration);
 
+	const std::vector<TextureInfo> &getActiveTextureInfo() const { return allTextureInfo; }
+	const std::vector<BufferInfo> &getActiveStorageBufferInfo() const { return storageBufferInfo; }
+
 private:
 	void compileShaders();
 	void createDescriptorSetLayout();
@@ -189,6 +204,9 @@ private:
 	std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
 	std::vector<VkShaderModule> shaderModules;
 
+	std::vector<TextureInfo> allTextureInfo;
+	std::vector<BufferInfo> storageBufferInfo;
+
 	Graphics *vgfx = nullptr;
 	VkDevice device = VK_NULL_HANDLE;
 

+ 10 - 10
src/modules/graphics/vulkan/Texture.cpp

@@ -169,7 +169,7 @@ bool Texture::loadVolatile()
 		else // TODO: is there a better layout for this situation?
 			imageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
 
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
 			VK_IMAGE_LAYOUT_UNDEFINED, imageLayout,
 			0, VK_REMAINING_MIP_LEVELS,
 			0, VK_REMAINING_ARRAY_LAYERS);
@@ -383,7 +383,7 @@ void Texture::clear()
 
 	if (clearLayout != imageLayout)
 	{
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
 			imageLayout, clearLayout,
 			0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
 	}
@@ -403,7 +403,7 @@ void Texture::clear()
 
 	if (clearLayout != imageLayout)
 	{
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
 			clearLayout, imageLayout,
 			0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
 	}
@@ -451,7 +451,7 @@ void Texture::generateMipmapsInternal()
 	auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
 
 	if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
 			imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 
 			rootView.startMipmap, static_cast<uint32_t>(getMipmapCount()),
 			rootView.startLayer, static_cast<uint32_t>(layerCount));
@@ -589,7 +589,7 @@ void Texture::uploadByteData(const void *data, size_t size, int level, int slice
 
 	if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
 	{
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
 			imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 
 			level, 1, baseLayer, 1);
 
@@ -602,7 +602,7 @@ void Texture::uploadByteData(const void *data, size_t size, int level, int slice
 			&region
 		);
 
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format,
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
 			VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout,
 			level, 1, baseLayer, 1);
 	}
@@ -643,11 +643,11 @@ void Texture::copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int
 
 	if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
 	{
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1);
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1);
 
 		vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
 
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1);
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1);
 	}
 	else
 		vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
@@ -676,11 +676,11 @@ void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const
 
 	if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
 	{
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1);
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1);
 
 		vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, (VkBuffer) dest->getHandle(), 1, &region);
 
-		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1);
+		Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1);
 	}
 	else
 		vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, &region);

+ 21 - 7
src/modules/graphics/vulkan/Vulkan.cpp

@@ -822,7 +822,7 @@ VkIndexType Vulkan::getVulkanIndexBufferType(IndexDataType type)
 	}
 }
 
-static void setImageLayoutTransitionOptions(bool previous, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags)
+void Vulkan::setImageLayoutTransitionOptions(bool previous, bool renderTarget, const PixelFormatInfo &info, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags)
 {
 	switch (layout)
 	{
@@ -837,7 +837,20 @@ static void setImageLayoutTransitionOptions(bool previous, VkImageLayout layout,
 		// We use the general image layout for images that are both compute write and readable.
 		// todo: can we optimize this?
 		accessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
-		stageFlags = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
+		stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
+		if (renderTarget)
+		{
+			if (info.color)
+			{
+				accessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+				stageFlags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+			}
+			if (info.depth || info.stencil)
+			{
+				accessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
+				stageFlags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+			}
+		}
 		break;
 	case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
 		accessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
@@ -849,7 +862,7 @@ static void setImageLayoutTransitionOptions(bool previous, VkImageLayout layout,
 		break;
 	case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
 		accessMask = VK_ACCESS_SHADER_READ_BIT;
-		stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+		stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
 		break;
 	case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
 		accessMask = VK_ACCESS_TRANSFER_READ_BIT;
@@ -868,7 +881,7 @@ static void setImageLayoutTransitionOptions(bool previous, VkImageLayout layout,
 	}
 }
 
-void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, PixelFormat format, 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;
@@ -882,13 +895,14 @@ void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage ima
 	barrier.subresourceRange.baseArrayLayer = baseLayer;
 	barrier.subresourceRange.layerCount = layerCount;
 
+	const PixelFormatInfo &info = getPixelFormatInfo(format);
+
 	VkPipelineStageFlags sourceStage;
 	VkPipelineStageFlags destinationStage;
 
-	setImageLayoutTransitionOptions(true, oldLayout, barrier.srcAccessMask, sourceStage);
-	setImageLayoutTransitionOptions(false, newLayout, barrier.dstAccessMask, destinationStage);
+	setImageLayoutTransitionOptions(true, renderTarget, info, oldLayout, barrier.srcAccessMask, sourceStage);
+	setImageLayoutTransitionOptions(false, renderTarget, info, newLayout, barrier.dstAccessMask, destinationStage);
 
-	const PixelFormatInfo &info = getPixelFormatInfo(format);
 	if (info.color)
 		barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;
 	if (info.depth)

+ 4 - 1
src/modules/graphics/vulkan/Vulkan.h

@@ -80,8 +80,11 @@ public:
 	static VkStencilOp getStencilOp(StencilAction);
 	static VkIndexType getVulkanIndexBufferType(IndexDataType type);
 
+	static void setImageLayoutTransitionOptions(
+		bool previous, bool renderTarget, const PixelFormatInfo &info, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags);
+
 	static void cmdTransitionImageLayout(
-		VkCommandBuffer, VkImage, PixelFormat format, VkImageLayout oldLayout, VkImageLayout newLayout,
+		VkCommandBuffer, VkImage, PixelFormat format, bool renderTarget, VkImageLayout oldLayout, VkImageLayout newLayout,
 		uint32_t baseLevel = 0, uint32_t levelCount = VK_REMAINING_MIP_LEVELS, uint32_t baseLayer = 0, uint32_t layerCount = VK_REMAINING_ARRAY_LAYERS);
 };