Browse Source

Merge pull request #1958 from nikeinikei/12.0-development

vk: maintenance
Sasha Szpakowski 2 years ago
parent
commit
98663ec0db

+ 67 - 28
src/modules/graphics/vulkan/Graphics.cpp

@@ -514,7 +514,7 @@ void Graphics::present(void *screenshotCallbackdata)
 
 void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
 {
-	if (swapChain != VK_NULL_HANDLE && (pixelWidth != this->pixelWidth || pixelHeight != this->pixelHeight || width != this->width || height != this->height))
+	if (swapChain != VK_NULL_HANDLE && (pixelwidth != this->pixelWidth || pixelheight != this->pixelHeight || width != this->width || height != this->height))
 		requestSwapchainRecreation();
 
 	this->width = width;
@@ -559,8 +559,6 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
 
 	beginFrame();
 
-	uint8 whiteColor[] = { 255, 255, 255, 255 };
-
 	if (batchedDrawState.vb[0] == nullptr)
 	{
 		// Initial sizes that should be good enough for most cases. It will
@@ -570,9 +568,19 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
 		batchedDrawState.indexBuffer = new StreamBuffer(this, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
 	}
 
+	// sometimes the VertexTexCoord is not set, so we manually adjust it to (0, 0)
+	if (defaultConstantTexCoord == nullptr)
+	{
+		float zeroTexCoord[2] = { 0.0f, 0.0f };
+		Buffer::DataDeclaration format("ConstantTexCoord", DATAFORMAT_FLOAT_VEC2);
+		Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC);
+		defaultConstantTexCoord = newBuffer(settings, { format }, zeroTexCoord, sizeof(zeroTexCoord), 1);
+	}
+
 	// sometimes the VertexColor is not set, so we manually adjust it to white color
 	if (defaultConstantColor == nullptr)
 	{
+		uint8 whiteColor[] = { 255, 255, 255, 255 };
 		Buffer::DataDeclaration format("ConstantColor", DATAFORMAT_UNORM8_VEC4);
 		Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_STATIC);
 		defaultConstantColor = newBuffer(settings, { format }, whiteColor, sizeof(whiteColor), 1);
@@ -1173,7 +1181,6 @@ void Graphics::beginFrame()
 
 	if (frameCounter >= USAGES_POLL_INTERVAL)
 	{
-		vkDeviceWaitIdle(device);
 		cleanupUnusedObjects();
 		frameCounter = 0;
 	}
@@ -1263,11 +1270,6 @@ const VkDeviceSize Graphics::getMinUniformBufferOffsetAlignment() const
 	return minUniformBufferOffsetAlignment;
 }
 
-graphics::Texture *Graphics::getDefaultTexture() const
-{
-	return defaultTexture;
-}
-
 VkCommandBuffer Graphics::getCommandBufferForDataTransfer()
 {
 	if (renderPassState.active)
@@ -1653,10 +1655,11 @@ void Graphics::createLogicalDevice()
 
 	VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extendedDynamicStateFeatures{};
 	extendedDynamicStateFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
-	extendedDynamicStateFeatures.extendedDynamicState = Vulkan::getBool(optionalDeviceExtensions.extendedDynamicState);
+	extendedDynamicStateFeatures.extendedDynamicState = VK_TRUE;
 	extendedDynamicStateFeatures.pNext = nullptr;
 
-	createInfo.pNext = &extendedDynamicStateFeatures;
+	if (optionalDeviceExtensions.extendedDynamicState)
+		createInfo.pNext = &extendedDynamicStateFeatures;
 
 	if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS)
 		throw love::Exception("failed to create logical device");
@@ -1850,20 +1853,34 @@ void Graphics::createSwapChain()
 
 VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats)
 {
+	std::vector<VkFormat> formatOrder;
+
 	// TODO: turn off GammaCorrect if a sRGB format can't be found?
+	// TODO: does every platform have these formats?
 	if (isGammaCorrect())
+	{
+		formatOrder = {
+				VK_FORMAT_B8G8R8A8_SRGB,
+				VK_FORMAT_R8G8B8A8_SRGB,
+		};
+	}
+	else
+	{
+		formatOrder = {
+				VK_FORMAT_B8G8R8A8_UNORM,
+				VK_FORMAT_R8G8B8A8_SNORM,
+		};
+	}
+
+	for (const auto format : formatOrder)
 	{
 		for (const auto &availableFormat : availableFormats)
-			// fixme: what if this format and colorspace is not available?
-			if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
+		{
+			if (availableFormat.format == format && availableFormat.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR)
 				return availableFormat;
+		}
 	}
-
-	for (const auto &availableFormat : availableFormats)
-		// fixme: what if this format and colorspace is not available?
-		if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
-			return availableFormat;
-
+	
 	return availableFormats[0];
 }
 
@@ -2208,11 +2225,6 @@ VkRenderPass Graphics::getRenderPass(RenderPassConfiguration &configuration)
 	return renderPass;
 }
 
-bool Graphics::usesConstantVertexColor(const VertexAttributes &vertexAttributes)
-{
-	return !!(vertexAttributes.enableBits & (1u << ATTRIB_COLOR));
-}
-
 void Graphics::createVulkanVertexFormat(
 	VertexAttributes vertexAttributes,
 	std::vector<VkVertexInputBindingDescription> &bindingDescriptions,
@@ -2224,6 +2236,7 @@ void Graphics::createVulkanVertexFormat(
 	auto allBits = enableBits;
 
 	bool usesColor = false;
+	bool usesTexCoord = false;
 
 	uint8_t highestBufferBinding = 0;
 
@@ -2233,6 +2246,8 @@ void Graphics::createVulkanVertexFormat(
 		uint32 bit = 1u << i;
 		if (enableBits & bit)
 		{
+			if (i == ATTRIB_TEXCOORD)
+				usesTexCoord = true;
 			if (i == ATTRIB_COLOR)
 				usesColor = true;
 
@@ -2267,11 +2282,31 @@ void Graphics::createVulkanVertexFormat(
 		allBits >>= 1;
 	}
 
+	if (!usesTexCoord)
+	{
+		// FIXME: is there a case where gaps happen between buffer bindings?
+		// then this doesn't work. We might need to enable null buffers again.
+		const auto constantTexCoordBufferBinding = ++highestBufferBinding;
+
+		VkVertexInputBindingDescription bindingDescription{};
+		bindingDescription.binding = constantTexCoordBufferBinding;
+		bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+		bindingDescription.stride = 0;	// no stride, will always read the same coord multiple times.
+		bindingDescriptions.push_back(bindingDescription);
+
+		VkVertexInputAttributeDescription attributeDescription{};
+		attributeDescription.binding = constantTexCoordBufferBinding;
+		attributeDescription.location = ATTRIB_TEXCOORD;
+		attributeDescription.offset = 0;
+		attributeDescription.format = VK_FORMAT_R32G32_SFLOAT;
+		attributeDescriptions.push_back(attributeDescription);
+	}
+
 	if (!usesColor)
 	{
 		// FIXME: is there a case where gaps happen between buffer bindings?
 		// then this doesn't work. We might need to enable null buffers again.
-		const auto constantColorBufferBinding = highestBufferBinding + 1;
+		const auto constantColorBufferBinding = ++highestBufferBinding;
 
 		VkVertexInputBindingDescription bindingDescription{};
 		bindingDescription.binding = constantColorBufferBinding;
@@ -2331,7 +2366,13 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi
 		}
 	}
 
-	if (!usesConstantVertexColor(attributes))
+	if (!(attributes.enableBits & (1u << ATTRIB_TEXCOORD)))
+	{
+		bufferVector.push_back((VkBuffer)defaultConstantTexCoord->getHandle());
+		offsets.push_back((VkDeviceSize)0);
+	}
+
+	if (!(attributes.enableBits & (1u << ATTRIB_COLOR)))
 	{
 		bufferVector.push_back((VkBuffer)defaultConstantColor->getHandle());
 		offsets.push_back((VkDeviceSize)0);
@@ -2402,8 +2443,6 @@ void Graphics::setDefaultRenderPass()
 
 void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture)
 {
-	auto currentCommandBuffer = commandBuffers.at(currentFrame);
-
 	// fixme: hasSRGBtexture
 	RenderPassConfiguration renderPassConfiguration{};
 	for (const auto &color : rts.colors)

+ 6 - 9
src/modules/graphics/vulkan/Graphics.h

@@ -262,13 +262,12 @@ public:
 	Graphics();
 	~Graphics();
 
-	const char *getName() const override;
-	const VkDevice getDevice() const;
-	const VmaAllocator getVmaAllocator() const;
-
 	// implementation for virtual functions
+	const char *getName() const override;
 	love::graphics::Texture *newTexture(const love::graphics::Texture::Settings &settings, const love::graphics::Texture::Slices *data) override;
 	love::graphics::Buffer *newBuffer(const love::graphics::Buffer::Settings &settings, const std::vector<love::graphics::Buffer::DataDeclaration>& format, const void *data, size_t size, size_t arraylength) override;
+	graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset) override;
+	graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty) override;
 	void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override;
 	void clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth) override;
 	Matrix4 computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const override;
@@ -299,17 +298,15 @@ public:
 	void draw(const DrawIndexedCommand &cmd) override;
 	void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, graphics::Texture *texture) override;
 
-	graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset) override;
-	graphics::GraphicsReadback *newReadbackInternal(ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty) override;
-
 	// internal functions.
 
+	const VkDevice getDevice() const;
+	const VmaAllocator getVmaAllocator() const;
 	VkCommandBuffer getCommandBufferForDataTransfer();
 	void queueCleanUp(std::function<void()> cleanUp);
 	void addReadbackCallback(std::function<void()> callback);
 	void submitGpuCommands(bool present, void *screenshotCallbackData = nullptr);
 	const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
-	graphics::Texture *getDefaultTexture() const;
 	VkSampler getCachedSampler(const SamplerState &sampler);
 	void setComputeShader(Shader *computeShader);
 	graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
@@ -369,7 +366,6 @@ private:
 	void startRecordingGraphicsCommands();
 	void endRecordingGraphicsCommands();
 	void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration &configuration);
-	bool usesConstantVertexColor(const VertexAttributes &attribs);
 	void createVulkanVertexFormat(
 		VertexAttributes vertexAttributes, 
 		std::vector<VkVertexInputBindingDescription> &bindingDescriptions, 
@@ -438,6 +434,7 @@ private:
 	VmaAllocator vmaAllocator = VK_NULL_HANDLE;
 	StrongRef<love::graphics::Texture> defaultTexture;
 	StrongRef<love::graphics::Buffer> defaultConstantColor;
+	StrongRef<love::graphics::Buffer> defaultConstantTexCoord;
 	// functions that need to be called to cleanup objects that were needed for rendering a frame.
 	// We need a vector for each frame in flight.
 	std::vector<std::vector<std::function<void()>>> cleanUpFunctions;

+ 217 - 224
src/modules/graphics/vulkan/Shader.cpp

@@ -18,6 +18,7 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
+#include "graphics/vertex.h"
 #include "Shader.h"
 #include "Graphics.h"
 
@@ -140,7 +141,7 @@ static const TBuiltInResource defaultTBuiltInResource = {
 };
 
 static const uint32_t STREAMBUFFER_DEFAULT_SIZE = 16;
-static const uint32_t DESCRIPTOR_POOL_SIZE = 1;
+static const uint32_t DESCRIPTOR_POOL_SIZE = 1000;
 
 class BindingMapper
 {
@@ -178,24 +179,17 @@ public:
 
 
 private:
-	uint32_t getFreeBinding() {
+	uint32_t getFreeBinding()
+	{
 		for (uint32_t i = 0;; i++)
 		{
-			bool free = true;
-			for (const auto &entry : bindingMappings)
-			{
-				if (entry.second == i)
-				{
-					free = false;
-					break;
-				}
-			}
-			if (free)
+			if (isFreeBinding(i))
 				return i;
 		}
 	}
 
-	bool isFreeBinding(uint32_t binding) {
+	bool isFreeBinding(uint32_t binding)
+	{
 		for (const auto &entry : bindingMappings)
 		{
 			if (entry.second == binding)
@@ -218,8 +212,8 @@ static VkShaderStageFlagBits getStageBit(ShaderStageType type)
 		return VK_SHADER_STAGE_FRAGMENT_BIT;
 	case SHADERSTAGE_COMPUTE:
 		return VK_SHADER_STAGE_COMPUTE_BIT;
-    default:
-	    throw love::Exception("invalid type");
+	default:
+		throw love::Exception("invalid type");
 	}
 }
 
@@ -238,6 +232,15 @@ static EShLanguage getGlslShaderType(ShaderStageType stage)
 	}
 }
 
+static bool usesLocalUniformData(const graphics::Shader::UniformInfo *info)
+{
+	return info->baseType == graphics::Shader::UNIFORM_BOOL ||
+		info->baseType == graphics::Shader::UNIFORM_FLOAT ||
+		info->baseType == graphics::Shader::UNIFORM_INT ||
+		info->baseType == graphics::Shader::UNIFORM_MATRIX ||
+		info->baseType == graphics::Shader::UNIFORM_UINT;
+}
+
 Shader::Shader(StrongRef<love::graphics::ShaderStage> stages[])
 	: graphics::Shader(stages)
 {
@@ -262,10 +265,9 @@ bool Shader::loadVolatile()
 	createPipelineLayout();
 	createDescriptorPoolSizes();
 	createStreamBuffers();
-	descriptorSetsVector.resize(MAX_FRAMES_IN_FLIGHT);
+	descriptorPools.resize(MAX_FRAMES_IN_FLIGHT);
 	currentFrame = 0;
 	currentUsedUniformStreamBuffersCount = 0;
-	currentUsedDescriptorSetsCount = 0;
 	newFrame();
 
 	return true;
@@ -302,8 +304,11 @@ void Shader::unloadVolatile()
 	}
 
 	vgfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout, descriptorPools = descriptorPools, computePipeline = computePipeline](){
-		for (const auto pool : descriptorPools)
-			vkDestroyDescriptorPool(device, pool, nullptr);
+		for (const auto &pools : descriptorPools)
+		{
+			for (const auto pool : pools)
+				vkDestroyDescriptorPool(device, pool, nullptr);
+		}
 		for (const auto shaderModule : shaderModules)
 			vkDestroyShaderModule(device, shaderModule, nullptr);
 		vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
@@ -312,9 +317,6 @@ void Shader::unloadVolatile()
 			vkDestroyPipeline(device, computePipeline, nullptr);
 	});
 
-	while (!freeDescriptorSets.empty())
-		freeDescriptorSets.pop();
-
 	for (const auto streamBuffer : streamBuffers)
 		streamBuffer->release();
 
@@ -322,7 +324,6 @@ void Shader::unloadVolatile()
 	shaderStages.clear();
 	streamBuffers.clear();
 	descriptorPools.clear();
-	descriptorSetsVector.clear();
 }
 
 const std::vector<VkPipelineShaderStageCreateInfo> &Shader::getShaderStages() const
@@ -344,9 +345,8 @@ void Shader::newFrame()
 {
 	currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
 
-	updatedUniforms.clear();
 	currentUsedUniformStreamBuffersCount = 0;
-	currentUsedDescriptorSetsCount = 0;
+	currentDescriptorPool = 0;
 
 	if (streamBuffers.size() > 1)
 	{
@@ -362,14 +362,25 @@ void Shader::newFrame()
 	else
 		streamBuffers.at(0)->nextFrame();
 
-	if (descriptorSetsVector.at(currentFrame).size() == 0)
-		descriptorSetsVector.at(currentFrame).push_back(allocateDescriptorSet());
-
-	currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(0);
+	for (VkDescriptorPool pool : descriptorPools[currentFrame])
+		vkResetDescriptorPool(device, pool, 0);
 }
 
 void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint bindPoint)
 {
+	VkDescriptorSet currentDescriptorSet = allocateDescriptorSet();
+
+	std::vector<VkDescriptorBufferInfo> bufferInfos;
+	bufferInfos.reserve(numBuffers);
+
+	std::vector<VkDescriptorImageInfo> imageInfos;
+	imageInfos.reserve(numTextures);
+
+	std::vector<VkBufferView> bufferViews;
+	bufferViews.reserve(numBufferViews);
+
+	std::vector<VkWriteDescriptorSet> descriptorWrites;
+
 	if (!localUniformData.empty())
 	{
 		auto usedStreamBufferMemory = currentUsedUniformStreamBuffersCount * uniformBufferSizeAligned;
@@ -398,6 +409,8 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
 		bufferInfo.offset = offset;
 		bufferInfo.range = localUniformData.size();
 
+		bufferInfos.push_back(bufferInfo);
+
 		VkWriteDescriptorSet uniformWrite{};
 		uniformWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 		uniformWrite.dstSet = currentDescriptorSet;
@@ -405,65 +418,109 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
 		uniformWrite.dstArrayElement = 0;
 		uniformWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
 		uniformWrite.descriptorCount = 1;
-		uniformWrite.pBufferInfo = &bufferInfo;
+		uniformWrite.pBufferInfo = &bufferInfos[bufferInfos.size() - 1];
 
-		vkUpdateDescriptorSets(device, 1, &uniformWrite, 0, nullptr);
+		descriptorWrites.push_back(uniformWrite);
 
 		currentUsedUniformStreamBuffersCount++;
-
-		updatedUniforms.insert(localUniformLocation);
 	}
 
-	static const std::vector<BuiltinUniform> builtinUniformTextures = {
-		BUILTIN_TEXTURE_MAIN,
-		BUILTIN_TEXTURE_VIDEO_Y,
-		BUILTIN_TEXTURE_VIDEO_CB,
-		BUILTIN_TEXTURE_VIDEO_CR,
-	};
-
-	for (const auto &builtin : builtinUniformTextures)
+	for (const auto &u : uniformInfos)
 	{
-		if (builtinUniformInfo[builtin] != nullptr)
+		auto &info = u.second;
+
+		if (usesLocalUniformData(&info))
+			continue;
+
+		if (info.baseType == UNIFORM_SAMPLER || info.baseType == UNIFORM_STORAGETEXTURE)
 		{
-			auto texture = dynamic_cast<Texture*>(builtinUniformInfo[builtin]->textures[0]);
+			bool isSampler = info.baseType == UNIFORM_SAMPLER;
+
+			for (int i = 0; i < info.count; i++)
+			{
+				auto vkTexture = dynamic_cast<Texture*>(info.textures[i]);
+
+				if (vkTexture == nullptr)
+					throw love::Exception("uniform variable %s is not set.", info.name.c_str());
 
-			VkDescriptorImageInfo imageInfo{};
-			imageInfo.imageLayout = texture->getImageLayout();
-			imageInfo.imageView = (VkImageView)texture->getRenderTargetHandle();
-			imageInfo.sampler = (VkSampler)texture->getSamplerHandle();
+				VkDescriptorImageInfo imageInfo{};
 
-			auto location = builtinUniformInfo[builtin]->location;
+				imageInfo.imageLayout = vkTexture->getImageLayout();
+				imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
+				if (isSampler)
+					imageInfo.sampler = (VkSampler)vkTexture->getSamplerHandle();
 
-			VkWriteDescriptorSet textureWrite{};
-			textureWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-			textureWrite.dstSet = currentDescriptorSet;
-			textureWrite.dstBinding = location;
-			textureWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-			textureWrite.descriptorCount = 1;
-			textureWrite.pImageInfo = &imageInfo;
+				imageInfos.push_back(imageInfo);
+			}
 
-			vkUpdateDescriptorSets(device, 1, &textureWrite, 0, nullptr);
+			VkWriteDescriptorSet write{};
+			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+			write.dstSet = currentDescriptorSet;
+			write.dstBinding = info.location;
+			write.dstArrayElement = 0;
+			if (isSampler)
+				write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+			else
+				write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+			write.descriptorCount = static_cast<uint32_t>(info.count);
+			write.pImageInfo = &imageInfos[imageInfos.size() - info.count];
 
-			updatedUniforms.insert(location);
+			descriptorWrites.push_back(write);
 		}
-	}
+		if (info.baseType == UNIFORM_STORAGEBUFFER)
+		{
+			VkWriteDescriptorSet write{};
+			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+			write.dstSet = currentDescriptorSet;
+			write.dstBinding = info.location;
+			write.dstArrayElement = 0;
+			write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+			write.descriptorCount = info.count;
+
+			for (int i = 0; i < info.count; i++)
+			{
+				if (info.buffers[i] == nullptr)
+					throw love::Exception("uniform variable %s is not set.", info.name.c_str());
 
-	for (const auto &u : uniformInfos)
-	{
-		if (updatedUniforms.find(u.second.location) == updatedUniforms.end())
-			updateUniform(&u.second, u.second.count, true);
-	}
+				VkDescriptorBufferInfo bufferInfo{};
+				bufferInfo.buffer = (VkBuffer)info.buffers[i]->getHandle();;
+				bufferInfo.offset = 0;
+				bufferInfo.range = info.buffers[i]->getSize();
 
-	vkCmdBindDescriptorSets(commandBuffer, bindPoint, pipelineLayout, 0, 1, &currentDescriptorSet, 0, nullptr);
+				bufferInfos.push_back(bufferInfo);
+			}
 
-	currentUsedDescriptorSetsCount++;
+			write.pBufferInfo = &bufferInfos[bufferInfos.size() - info.count];
 
-	if (currentUsedDescriptorSetsCount >= static_cast<uint32_t>(descriptorSetsVector.at(currentFrame).size()))
-		descriptorSetsVector.at(currentFrame).push_back(allocateDescriptorSet());
+			descriptorWrites.push_back(write);
+		}
+		if (info.baseType == UNIFORM_TEXELBUFFER)
+		{
+			VkWriteDescriptorSet write{};
+			write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+			write.dstSet = currentDescriptorSet;
+			write.dstBinding = info.location;
+			write.dstArrayElement = 0;
+			write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+			write.descriptorCount = info.count;
+
+			for (int i = 0; i < info.count; i++)
+			{
+				if (info.buffers[i] == nullptr)
+					throw love::Exception("uniform variable %s is not set.", info.name.c_str());
+
+				bufferViews.push_back((VkBufferView)info.buffers[i]->getTexelBufferHandle());
+			}
 
-	currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount);
+			write.pTexelBufferView = &bufferViews[bufferViews.size() - info.count];
 
-	updatedUniforms.clear();
+			descriptorWrites.push_back(write);
+		}
+	}
+
+	vkUpdateDescriptorSets(device, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr);
+
+	vkCmdBindDescriptorSets(commandBuffer, bindPoint, pipelineLayout, 0, 1, &currentDescriptorSet, 0, nullptr);
 }
 
 Shader::~Shader()
@@ -502,119 +559,13 @@ const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const
 	return builtinUniformInfo[builtin];
 }
 
-static bool usesLocalUniformData(const graphics::Shader::UniformInfo *info)
-{
-	return info->baseType == graphics::Shader::UNIFORM_BOOL ||
-		info->baseType == graphics::Shader::UNIFORM_FLOAT ||
-		info->baseType == graphics::Shader::UNIFORM_INT ||
-		info->baseType == graphics::Shader::UNIFORM_MATRIX ||
-		info->baseType == graphics::Shader::UNIFORM_UINT;
-}
-
 void Shader::updateUniform(const UniformInfo *info, int count)
 {
-	updateUniform(info, count, false);
-}
-
-void Shader::updateUniform(const UniformInfo* info, int count, bool internal)
-{
-	if (!internal && current == this)
+	if (current == this)
 		Graphics::flushBatchedDrawsGlobal();
 
 	if (usesLocalUniformData(info))
 		memcpy(localUniformData.data(), localUniformStagingData.data(), localUniformStagingData.size());
-	if (info->baseType == UNIFORM_SAMPLER || info->baseType == UNIFORM_STORAGETEXTURE)
-	{
-		bool isSampler = info->baseType == UNIFORM_SAMPLER;
-
-		std::vector<VkDescriptorImageInfo> imageInfos;
-
-		for (int i = 0; i < count; i++)
-		{
-			auto vkTexture = dynamic_cast<Texture*>(info->textures[i]);
-
-			if (vkTexture == nullptr)
-				throw love::Exception("uniform variable %s is not set.", info->name.c_str());
-
-			VkDescriptorImageInfo imageInfo{};
-
-			imageInfo.imageLayout = vkTexture->getImageLayout();
-			imageInfo.imageView = (VkImageView)vkTexture->getRenderTargetHandle();
-			if (isSampler)
-				imageInfo.sampler = (VkSampler)vkTexture->getSamplerHandle();
-
-			imageInfos.push_back(imageInfo);
-		}
-
-		VkWriteDescriptorSet write{};
-		write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-		write.dstSet = currentDescriptorSet;
-		write.dstBinding = info->location;
-		write.dstArrayElement = 0;
-		if (isSampler)
-			write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-		else
-			write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
-		write.descriptorCount = static_cast<uint32_t>(count);
-		write.pImageInfo = imageInfos.data();
-
-		vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
-	}
-	if (info->baseType == UNIFORM_STORAGEBUFFER)
-	{
-		VkWriteDescriptorSet write{};
-		write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-		write.dstSet = currentDescriptorSet;
-		write.dstBinding = info->location;
-		write.dstArrayElement = 0;
-		write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
-		write.descriptorCount = info->count;
-
-		std::vector<VkDescriptorBufferInfo> bufferInfos;
-
-		for (int i = 0; i < info->count; i++)
-		{
-			if (info->buffers[i] == nullptr)
-				throw love::Exception("uniform variable %s is not set.", info->name.c_str());
-
-			VkDescriptorBufferInfo bufferInfo{};
-			bufferInfo.buffer = (VkBuffer)info->buffers[i]->getHandle();;
-			bufferInfo.offset = 0;
-			bufferInfo.range = info->buffers[i]->getSize();
-
-			bufferInfos.push_back(bufferInfo);
-		}
-
-		write.pBufferInfo = bufferInfos.data();
-
-		vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
-	}
-	if (info->baseType == UNIFORM_TEXELBUFFER)
-	{
-		VkWriteDescriptorSet write{};
-		write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-		write.dstSet = currentDescriptorSet;
-		write.dstBinding = info->location;
-		write.dstArrayElement = 0;
-		write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
-		write.descriptorCount = info->count;
-
-		std::vector<VkBufferView> bufferViews;
-
-		for (int i = 0; i < info->count; i++)
-		{
-			if (info->buffers[i] == nullptr)
-				throw love::Exception("uniform variable %s is not set.", info->name.c_str());
-
-			bufferViews.push_back((VkBufferView)info->buffers[i]->getTexelBufferHandle());
-		}
-
-		write.pTexelBufferView = bufferViews.data();
-
-		vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
-	}
-
-	updatedUniforms.insert(info->location);
 }
 
 void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures, int count)
@@ -627,8 +578,6 @@ void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures,
 		if (oldTexture)
 			oldTexture->release();
 	}
-
-	updateUniform(info, count);
 }
 
 void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
@@ -641,17 +590,13 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
 		if (oldBuffer)
 			oldBuffer->release();
 	}
-
-	updateUniform(info, count);
 }
 
 void Shader::calculateUniformBufferSizeAligned()
 {
 	auto minAlignment = vgfx->getMinUniformBufferOffsetAlignment();
 	size_t size = localUniformStagingData.size();
-	auto factor = static_cast<VkDeviceSize>(std::ceil(
-		static_cast<float>(size) / static_cast<float>(minAlignment)
-	));
+	auto factor = static_cast<VkDeviceSize>(std::ceil(static_cast<float>(size) / static_cast<float>(minAlignment)));
 	uniformBufferSizeAligned = factor * minAlignment;
 }
 
@@ -830,9 +775,9 @@ void Shader::compileShaders()
 		{
 			if (resource.name == "gl_DefaultUniformBlock")
 			{
-				const auto& type = comp.get_type(resource.base_type_id);
-				size_t uniformBufferObjectSize = comp.get_declared_struct_size(type);
-				auto defaultUniformBlockSize = comp.get_declared_struct_size(type);
+				const auto &type = comp.get_type(resource.base_type_id);
+				size_t defaultUniformBlockSize = comp.get_declared_struct_size(type);
+
 				localUniformStagingData.resize(defaultUniformBlockSize);
 				localUniformData.resize(defaultUniformBlockSize);
 				localUniformLocation = bindingMapper(comp, spirv, resource.name, resource.id);
@@ -910,11 +855,9 @@ void Shader::compileShaders()
 			}
 			else
 			{
-				auto tex = vgfx->getDefaultTexture();
 				for (int i = 0; i < info.count; i++)
 				{
-					info.textures[i] = tex;
-					tex->retain();
+					info.textures[i] = nullptr;
 				}
 			}
 
@@ -970,11 +913,25 @@ void Shader::compileShaders()
 
 		if (shaderStage == SHADERSTAGE_VERTEX)
 		{
+			int nextAttributeIndex = ATTRIB_MAX_ENUM;
+
 			for (const auto &r : shaderResources.stage_inputs)
 			{
-				const auto &name = r.name;
-				const int attributeLocation = static_cast<int>(comp.get_decoration(r.id, spv::DecorationLocation));
-				attributes[name] = attributeLocation;
+				int index;
+
+				BuiltinVertexAttribute builtinAttribute;
+				if (graphics::getConstant(r.name.c_str(), builtinAttribute))
+					index = (int)builtinAttribute;
+				else
+					index = nextAttributeIndex++;
+
+				uint32_t locationOffset;
+				if (!comp.get_binary_offset_for_decoration(r.id, spv::DecorationLocation, locationOffset))
+					throw love::Exception("could not get binary offset for location");
+
+				spirv[locationOffset] = (uint32_t)index;
+
+				attributes[r.name] = index;
 			}
 		}
 
@@ -998,6 +955,32 @@ void Shader::compileShaders()
 
 		shaderStages.push_back(shaderStageInfo);
 	}
+
+	numBuffers = 0;
+	numTextures = 0;
+	numBufferViews = 0;
+
+	if (localUniformData.size() > 0)
+		numBuffers++;
+
+	for (const auto &u : uniformInfos)
+	{
+		switch (u.second.baseType)
+		{
+		case UNIFORM_SAMPLER:
+		case UNIFORM_STORAGETEXTURE:
+			numTextures++;
+			break;
+		case UNIFORM_STORAGEBUFFER:
+			numBuffers++;
+			break;
+		case UNIFORM_TEXELBUFFER:
+			numBufferViews++;
+			break;
+		default:
+			continue;
+		}
+	}
 }
 
 void Shader::createDescriptorSetLayout()
@@ -1085,9 +1068,9 @@ void Shader::createDescriptorPoolSizes()
 	{
 		VkDescriptorPoolSize size{};
 		auto type = Vulkan::getDescriptorType(entry.second.baseType);
-		if (type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) {
+		if (type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
 			continue;
-		}
+
 		size.type = type;
 		size.descriptorCount = 1;
 		descriptorPoolSizes.push_back(size);
@@ -1120,7 +1103,8 @@ void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cb
 		if (builtinUniformInfo[builtIns[i]] != nullptr)
 		{
 			textures[i]->retain();
-			builtinUniformInfo[builtIns[i]]->textures[0]->release();
+			if (builtinUniformInfo[builtIns[i]]->textures[0])
+				builtinUniformInfo[builtIns[i]]->textures[0]->release();
 			builtinUniformInfo[builtIns[i]]->textures[0] = textures[i];
 		}
 	}
@@ -1136,47 +1120,56 @@ void Shader::setMainTex(graphics::Texture *texture)
 	if (builtinUniformInfo[BUILTIN_TEXTURE_MAIN] != nullptr)
 	{
 		texture->retain();
-		builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0]->release();
+		if (builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0]) 
+			builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0]->release();
 		builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0] = texture;
 	}
 }
 
-VkDescriptorSet Shader::allocateDescriptorSet()
+void Shader::createDescriptorPool()
 {
-	if (freeDescriptorSets.empty())
-	{
-		VkDescriptorPoolCreateInfo createInfo{};
-		createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
-		createInfo.maxSets = DESCRIPTOR_POOL_SIZE;
-		createInfo.poolSizeCount = static_cast<uint32_t>(descriptorPoolSizes.size());
-		createInfo.pPoolSizes = descriptorPoolSizes.data();
+	VkDescriptorPoolCreateInfo createInfo{};
+	createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+	createInfo.maxSets = DESCRIPTOR_POOL_SIZE;
+	createInfo.poolSizeCount = static_cast<uint32_t>(descriptorPoolSizes.size());
+	createInfo.pPoolSizes = descriptorPoolSizes.data();
 
-		VkDescriptorPool pool;
-		if (vkCreateDescriptorPool(device, &createInfo, nullptr, &pool) != VK_SUCCESS)
-			throw love::Exception("failed to create descriptor pool");
-		descriptorPools.push_back(pool);
+	VkDescriptorPool pool;
+	if (vkCreateDescriptorPool(device, &createInfo, nullptr, &pool) != VK_SUCCESS)
+		throw love::Exception("failed to create descriptor pool");
 
-		std::vector<VkDescriptorSetLayout> layouts(DESCRIPTOR_POOL_SIZE, descriptorSetLayout);
+	descriptorPools[currentFrame].push_back(pool);
+}
+
+VkDescriptorSet Shader::allocateDescriptorSet()
+{
+	if (descriptorPools[currentFrame].empty())
+		createDescriptorPool();
 
+	while (true)
+	{
 		VkDescriptorSetAllocateInfo allocInfo{};
 		allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
-		allocInfo.descriptorPool = pool;
-		allocInfo.descriptorSetCount = DESCRIPTOR_POOL_SIZE;
-		allocInfo.pSetLayouts = layouts.data();
-
-		std::vector<VkDescriptorSet> descriptorSet;
-		descriptorSet.resize(DESCRIPTOR_POOL_SIZE);
-		VkResult result = vkAllocateDescriptorSets(device, &allocInfo, descriptorSet.data());
-		if (result != VK_SUCCESS)
-			throw love::Exception("failed to allocate descriptor set");
+		allocInfo.descriptorPool = descriptorPools[currentFrame][currentDescriptorPool];
+		allocInfo.descriptorSetCount = 1;
+		allocInfo.pSetLayouts = &descriptorSetLayout;
 
-		for (const auto ds : descriptorSet)
-			freeDescriptorSets.push(ds);
-	}
+		VkDescriptorSet descriptorSet;
+		VkResult result = vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet);
 
-	auto ds = freeDescriptorSets.front();
-	freeDescriptorSets.pop();
-	return ds;
+		switch (result)
+		{
+		case VK_SUCCESS:
+			return descriptorSet;
+		case VK_ERROR_OUT_OF_POOL_MEMORY:
+			currentDescriptorPool++;
+			if (descriptorPools[currentFrame].size() <= currentDescriptorPool)
+				createDescriptorPool();
+			continue;
+		default:
+			throw love::Exception("failed to allocate descriptor set");
+		}
+	}
 }
 
 } // vulkan

+ 9 - 16
src/modules/graphics/vulkan/Shader.h

@@ -70,7 +70,7 @@ public:
 
 	void attach() override;
 
-	ptrdiff_t getHandle() const { return 0; }
+	ptrdiff_t getHandle() const override { return 0; }
 
 	std::string getWarnings() const override { return ""; }
 
@@ -97,19 +97,18 @@ private:
 	void createPipelineLayout();
 	void createDescriptorPoolSizes();
 	void createStreamBuffers();
-	void buildLocalUniforms(
-		spirv_cross::Compiler &comp, 
-		const spirv_cross::SPIRType &type, 
-		size_t baseoff, 
-		const std::string &basename);
-	void updateUniform(const UniformInfo *info, int count, bool internal);
-
+	void buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename);
+	void createDescriptorPool();
 	VkDescriptorSet allocateDescriptorSet();
 
 	VkDeviceSize uniformBufferSizeAligned;
 
 	VkPipeline computePipeline;
 
+	uint32_t numTextures;
+	uint32_t numBuffers;
+	uint32_t numBufferViews;
+
 	VkDescriptorSetLayout descriptorSetLayout;
 	VkPipelineLayout pipelineLayout;
 	std::vector<VkDescriptorPoolSize> descriptorPoolSizes;
@@ -117,11 +116,7 @@ private:
 	// we don't know how much memory we need per frame for the uniform buffer descriptors
 	// we keep a vector of stream buffers that gets dynamically increased if more memory is needed
 	std::vector<StreamBuffer*> streamBuffers;
-	std::vector<VkDescriptorPool> descriptorPools;
-	std::queue<VkDescriptorSet> freeDescriptorSets;
-	std::vector<std::vector<VkDescriptorSet>> descriptorSetsVector;
-
-	std::set<uint32_t> updatedUniforms;
+	std::vector<std::vector<VkDescriptorPool>> descriptorPools;
 
 	std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
 	std::vector<VkShaderModule> shaderModules;
@@ -142,11 +137,9 @@ private:
 
 	std::unordered_map<std::string, int> attributes;
 
-	VkDescriptorSet currentDescriptorSet;
-
 	uint32_t currentFrame;
 	uint32_t currentUsedUniformStreamBuffersCount;
-	uint32_t currentUsedDescriptorSetsCount;
+	uint32_t currentDescriptorPool;
 };
 
 }

+ 53 - 161
src/modules/graphics/vulkan/Vulkan.cpp

@@ -747,8 +747,54 @@ VkIndexType Vulkan::getVulkanIndexBufferType(IndexDataType type)
 	}
 }
 
-void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout,
-	uint32_t baseLevel, uint32_t levelCount, uint32_t baseLayer, uint32_t layerCount)
+static void setImageLayoutTransitionOptions(bool previous, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags, bool &depthStencil)
+{
+	switch (layout)
+	{
+	case VK_IMAGE_LAYOUT_UNDEFINED:
+		accessMask = 0;
+		if (previous)
+			stageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+		else
+			stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+		break;
+	case VK_IMAGE_LAYOUT_GENERAL:
+		// 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;
+		break;
+	case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+		accessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+		stageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+		break;
+	case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
+		depthStencil = true;
+		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_SHADER_READ_ONLY_OPTIMAL:
+		accessMask = VK_ACCESS_SHADER_READ_BIT;
+		stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+		break;
+	case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
+		accessMask = VK_ACCESS_TRANSFER_READ_BIT;
+		stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT;
+		break;
+	case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+		accessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+		stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT;
+		break;
+	case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
+		accessMask = 0;
+		stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+		break;
+	default:
+		throw love::Exception("unimplemented image layout");
+	}
+}
+
+void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, 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;
@@ -757,176 +803,22 @@ void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage ima
 	barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
 	barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
 	barrier.image = image;
-	barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 	barrier.subresourceRange.baseMipLevel = baseLevel;
 	barrier.subresourceRange.levelCount = levelCount;
 	barrier.subresourceRange.baseArrayLayer = baseLayer;
 	barrier.subresourceRange.layerCount = layerCount;
 
+	bool depthStencil = false;
 	VkPipelineStageFlags sourceStage;
 	VkPipelineStageFlags destinationStage;
 
-	if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
-	{
-		barrier.srcAccessMask = 0;
-		barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-		destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
-	{
-		barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-		barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-		destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && 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_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
-	{
-		barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-		barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-		destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_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_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
-	{
-		barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-		barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-		destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
-	{
-		barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
-		barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-		destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
-	{
-		barrier.srcAccessMask = 0;
-		barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-		destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
-	{
-		barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-		barrier.dstAccessMask = 0;
-
-		sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-		destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
-	}
-	// we use general for images that are both sampled and compute write
-	else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL)
-	{
-		barrier.srcAccessMask = 0;
-		barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-		destinationStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
-	{
-		barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+	setImageLayoutTransitionOptions(true, oldLayout, barrier.srcAccessMask, sourceStage, depthStencil);
+	setImageLayoutTransitionOptions(false, newLayout, barrier.dstAccessMask, destinationStage, depthStencil);
 
-		barrier.srcAccessMask = 0;
-		barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-		destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
-	{
+	if (depthStencil)
 		barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
-
-		barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
-		barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
-		destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
-	{
-		barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
-
-		barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-		barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-		destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
-	{
-		barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-		barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-		destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
-	{
-		barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
-		barrier.dstAccessMask = 0;
-		
-		sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-		destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
-	{
-		barrier.srcAccessMask = 0;
-		barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-		destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
-	{
-		barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-		barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-		destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
-	{
-		barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
-		barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
-
-		sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-		destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-	}
-	else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_UNDEFINED)
-	{
-		barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
-		barrier.dstAccessMask = 0;
-
-		sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
-		destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
-	}
 	else
-		throw std::invalid_argument("unsupported layout transition!");
+		barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 
 	vkCmdPipelineBarrier(
 		commandBuffer,