Browse Source

Merge pull request #1885 from nikeinikei/12.0-development

Vulkan fixes
Sasha Szpakowski 2 years ago
parent
commit
bcc4a45e46

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

@@ -42,7 +42,7 @@ class Buffer final
 	, public Volatile
 {
 public:
-	Buffer(love::graphics::Graphics *gfx, const Settings& settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength);
+	Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength);
 	virtual ~Buffer();
 
 	virtual bool loadVolatile() override;

+ 103 - 121
src/modules/graphics/vulkan/Graphics.cpp

@@ -81,8 +81,6 @@ Graphics::Graphics()
 		throw love::Exception("could not find vulkan");
 
 	volkInitializeCustom((PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr());
-
-	instanceVersion = volkGetInstanceVersion();
 }
 
 Graphics::~Graphics()
@@ -360,7 +358,7 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData)
 		}
 	}
 
-	endRecordingGraphicsCommands(present);
+	endRecordingGraphicsCommands();
 
 	if (imagesInFlight[imageIndex] != VK_NULL_HANDLE)
 		vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX);
@@ -412,7 +410,7 @@ void Graphics::submitGpuCommands(bool present, void *screenshotCallbackData)
 			callbacks.clear();
 		}
 
-		startRecordingGraphicsCommands(false);
+		startRecordingGraphicsCommands();
 	}
 }
 
@@ -438,9 +436,9 @@ void Graphics::present(void *screenshotCallbackdata)
 
 	VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo);
 
-	if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized)
+	if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || swapChainRecreationRequested)
 	{
-		framebufferResized = false;
+		swapChainRecreationRequested = false;
 		recreateSwapChain();
 	}
 	else if (result != VK_SUCCESS)
@@ -465,6 +463,9 @@ 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))
+		requestSwapchainRecreation();
+
 	this->width = width;
 	this->height = height;
 	this->pixelWidth = pixelwidth;
@@ -601,7 +602,7 @@ void Graphics::unSetMode()
 	renderPassUsages.clear();
 	framebufferUsages.clear();
 	pipelineUsages.clear();
-
+	
 	created = false;
 	vkDeviceWaitIdle(device);
 	Volatile::unloadAll();
@@ -675,34 +676,7 @@ Graphics::RendererInfo Graphics::getRendererInfo() const
 
 	Graphics::RendererInfo info;
 
-	if (isDebugEnabled())
-	{
-		std::stringstream ss;
-		ss << "Vulkan( ";
-		ss << renderPasses.size() << " ";
-		ss << framebuffers.size() << " ";
-		ss << graphicsPipelines.size() << " ";
-		if (optionalDeviceFeatures.extendedDynamicState)
-			ss << "eds ";
-		if (optionalDeviceFeatures.memoryRequirements2)
-			ss << "mr2 ";
-		if (optionalDeviceFeatures.dedicatedAllocation)
-			ss << "da ";
-		if (optionalDeviceFeatures.bufferDeviceAddress)
-			ss << "bda ";
-		if (optionalDeviceFeatures.memoryBudget)
-			ss << "mb ";
-		if (optionalDeviceFeatures.shaderFloatControls)
-			ss << "sfc ";
-		if (optionalDeviceFeatures.spirv14)
-			ss << "spv14 ";
-		ss << ")";
-
-		info.name = ss.str();
-	}
-	else
-		info.name = "Vulkan";
-
+	info.name = "Vulkan";
 	info.device = deviceProperties.deviceName;
 	info.vendor = Vulkan::getVendorName(deviceProperties.vendorID);
 	info.version = Vulkan::getVulkanApiVersion(deviceProperties.apiVersion);
@@ -832,11 +806,7 @@ void Graphics::setScissor(const Rect &rect)
 {
 	flushBatchedDraws();
 
-	VkRect2D scissor = computeScissor(rect,
-                                      static_cast<double>(swapChainExtent.width),
-                                      static_cast<double>(swapChainExtent.height),
-									  getCurrentDPIScale(),
-                                      preTransform);
+	VkRect2D scissor = computeScissor(rect, static_cast<double>(swapChainExtent.width), static_cast<double>(swapChainExtent.height), getCurrentDPIScale(), preTransform);
 	vkCmdSetScissor(commandBuffers.at(currentFrame), 0, 1, &scissor);
 
 	states.back().scissor = true;
@@ -1133,7 +1103,7 @@ void Graphics::beginFrame()
 		cleanUpFn();
 	cleanUpFunctions.at(currentFrame).clear();
 
-	startRecordingGraphicsCommands(true);
+	startRecordingGraphicsCommands();
 
 	Vulkan::cmdTransitionImageLayout(
 		commandBuffers.at(currentFrame),
@@ -1164,7 +1134,7 @@ void Graphics::beginFrame()
 	usedShadersInFrame.clear();
 }
 
-void Graphics::startRecordingGraphicsCommands(bool newFrame)
+void Graphics::startRecordingGraphicsCommands()
 {
 	VkCommandBufferBeginInfo beginInfo{};
 	beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
@@ -1179,7 +1149,7 @@ void Graphics::startRecordingGraphicsCommands(bool newFrame)
 	setDefaultRenderPass();
 }
 
-void Graphics::endRecordingGraphicsCommands(bool present) {
+void Graphics::endRecordingGraphicsCommands() {
 	if (renderPassState.active)
 		endRenderPass();
 
@@ -1187,16 +1157,6 @@ void Graphics::endRecordingGraphicsCommands(bool present) {
 		throw love::Exception("failed to record command buffer");
 }
 
-uint32_t Graphics::getNumImagesInFlight() const
-{
-	return MAX_FRAMES_IN_FLIGHT;
-}
-
-uint32_t Graphics::getFrameIndex() const
-{
-	return static_cast<uint32_t>(currentFrame);
-}
-
 const VkDeviceSize Graphics::getMinUniformBufferOffsetAlignment() const
 {
 	return minUniformBufferOffsetAlignment;
@@ -1297,7 +1257,7 @@ void Graphics::createVulkanInstance()
 	appInfo.applicationVersion = VK_MAKE_API_VERSION(0, 1, 0, 0);	// get this version from somewhere else?
 	appInfo.pEngineName = "LOVE Game Framework";
 	appInfo.engineVersion = VK_MAKE_API_VERSION(0, VERSION_MAJOR, VERSION_MINOR, VERSION_REV);
-	appInfo.apiVersion = instanceVersion;
+	appInfo.apiVersion = VK_API_VERSION_1_3;
 
 	VkInstanceCreateInfo createInfo{};
 	createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
@@ -1351,7 +1311,7 @@ bool Graphics::checkValidationSupport()
 	{
 		bool layerFound = false;
 
-		for (const auto& layerProperties : availableLayers)
+		for (const auto &layerProperties : availableLayers)
 		{
 			if (strcmp(layerName, layerProperties.layerName) == 0)
 			{
@@ -1546,8 +1506,8 @@ void Graphics::createLogicalDevice()
 
 	// sanity check for dependencies.
 
-    if (optionalDeviceFeatures.extendedDynamicState && !optionalInstanceExtensions.physicalDeviceProperties2)
-        optionalDeviceFeatures.extendedDynamicState = false;
+	if (optionalDeviceFeatures.extendedDynamicState && !optionalInstanceExtensions.physicalDeviceProperties2)
+		optionalDeviceFeatures.extendedDynamicState = false;
 	if (optionalDeviceFeatures.dedicatedAllocation && !optionalDeviceFeatures.memoryRequirements2)
 		optionalDeviceFeatures.dedicatedAllocation = false;
 	if (optionalDeviceFeatures.bufferDeviceAddress && !optionalInstanceExtensions.physicalDeviceProperties2)
@@ -1606,7 +1566,7 @@ void Graphics::createLogicalDevice()
 	if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS)
 		throw love::Exception("failed to create logical device");
 
-    volkLoadDevice(device);
+	volkLoadDevice(device);
 
 	vkGetDeviceQueue(device, indices.graphicsFamily.value, 0, &graphicsQueue);
 	vkGetDeviceQueue(device, indices.presentFamily.value, 0, &presentQueue);
@@ -1705,15 +1665,15 @@ void Graphics::createSwapChain()
 	VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
 	VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
 
-    if ((swapChainSupport.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) ||
-        (swapChainSupport.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR))
+	if ((swapChainSupport.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) ||
+		(swapChainSupport.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR))
 	{
-        uint32_t width, height;
-        width = extent.width;
-        height = extent.height;
-        extent.width = height;
-        extent.height = width;
-    }
+		uint32_t width, height;
+		width = extent.width;
+		height = extent.height;
+		extent.width = height;
+		extent.height = width;
+	}
 
 	auto currentTransform = swapChainSupport.capabilities.currentTransform;
 	constexpr float PI = 3.14159265358979323846f;
@@ -1789,7 +1749,7 @@ VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector<VkSurface
 	// TODO: turn off GammaCorrect if a sRGB format can't be found?
 	if (isGammaCorrect())
 	{
-		for (const auto& availableFormat : availableFormats)
+		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)
 				return availableFormat;
@@ -1805,8 +1765,6 @@ VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector<VkSurface
 
 VkPresentModeKHR Graphics::chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes)
 {
-	int vsync = Vulkan::getVsync();
-
 	const auto begin = availablePresentModes.begin();
 	const auto end = availablePresentModes.end();
 
@@ -1871,12 +1829,12 @@ VkCompositeAlphaFlagBitsKHR Graphics::chooseCompositeAlpha(const VkSurfaceCapabi
 		return VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
 	else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
 		return VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
-    else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
-        return VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
-    else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
-        return VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
-    else
-        throw love::Exception("failed to find composite alpha");
+	else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
+		return VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
+	else if (capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
+		return VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
+	else
+		throw love::Exception("failed to find composite alpha");
 }
 
 void Graphics::createImageViews()
@@ -2002,7 +1960,7 @@ VkFramebuffer Graphics::createFramebuffer(FramebufferConfiguration &configuratio
 {
 	std::vector<VkImageView> attachments;
 
-	for (const auto& colorView : configuration.colorViews)
+	for (const auto &colorView : configuration.colorViews)
 		attachments.push_back(colorView);
 
 	if (configuration.staticData.depthView)
@@ -2062,8 +2020,8 @@ void Graphics::createDefaultShaders()
 
 VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration)
 {
-    VkSubpassDescription subPass{};
-    subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+	VkSubpassDescription subPass{};
+	subPass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
 
 	std::vector<VkAttachmentDescription> attachments;
 	std::vector<VkAttachmentReference> colorAttachmentRefs;
@@ -2091,8 +2049,8 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration)
 		attachments.push_back(colorDescription);
 	}
 
-    subPass.colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size());
-    subPass.pColorAttachments = colorAttachmentRefs.data();
+	subPass.colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size());
+	subPass.pColorAttachments = colorAttachmentRefs.data();
 
 	VkAttachmentReference depthStencilAttachmentRef{};
 	if (configuration.staticData.depthAttachment.format != VK_FORMAT_UNDEFINED)
@@ -2156,20 +2114,20 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration)
 
 	std::array<VkSubpassDependency, 2> dependencies = { dependency, readbackDependency };
 
-    VkRenderPassCreateInfo createInfo{};
-    createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-    createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
-    createInfo.pAttachments = attachments.data();
-    createInfo.subpassCount = 1;
-    createInfo.pSubpasses = &subPass;
+	VkRenderPassCreateInfo createInfo{};
+	createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+	createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
+	createInfo.pAttachments = attachments.data();
+	createInfo.subpassCount = 1;
+	createInfo.pSubpasses = &subPass;
 	createInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
 	createInfo.pDependencies = dependencies.data();
 
-    VkRenderPass renderPass;
-    if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS)
-        throw love::Exception("failed to create render pass");
+	VkRenderPass renderPass;
+	if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS)
+		throw love::Exception("failed to create render pass");
 
-    return renderPass;
+	return renderPass;
 }
 
 bool Graphics::usesConstantVertexColor(const VertexAttributes &vertexAttributes)
@@ -2261,7 +2219,7 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi
 
 	GraphicsPipelineConfiguration configuration{};
 
-    configuration.renderPass = renderPassState.beginInfo.renderPass;
+	configuration.renderPass = renderPassState.beginInfo.renderPass;
 	configuration.vertexAttributes = attributes;
 	configuration.shader = (Shader*)Shader::current;
 	configuration.wireFrame = states.back().wireframe;
@@ -2295,7 +2253,7 @@ void Graphics::prepareDraw(const VertexAttributes &attributes, const BufferBindi
 		}
 	}
 
-	if (usesConstantVertexColor(attributes))
+	if (!usesConstantVertexColor(attributes))
 	{
 		bufferVector.push_back((VkBuffer)defaultConstantColor->getHandle());
 		offsets.push_back((VkDeviceSize)0);
@@ -2328,35 +2286,15 @@ void Graphics::setDefaultRenderPass()
 	renderPassState.msaa = msaaSamples;
 	renderPassState.numColorAttachments = 1;
 	renderPassState.transitionImages.clear();
-
-	VkViewport viewport{};
-	viewport.x = 0.0f;
-	viewport.y = 0.0f;
-	viewport.width = renderPassState.width;
-	viewport.height = renderPassState.height;
-	viewport.minDepth = 0.0f;
-	viewport.maxDepth = 1.0f;
-
-	vkCmdSetViewport(commandBuffers.at(currentFrame), 0, 1, &viewport);
 }
 
 void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture)
 {
-	VkViewport viewport{};
-	viewport.x = 0.0f;
-	viewport.y = 0.0f;
-	viewport.width = static_cast<float>(pixelw);
-	viewport.height = static_cast<float>(pixelh);
-	viewport.minDepth = 0.0f;
-	viewport.maxDepth = 1.0f;
-
-	vkCmdSetViewport(commandBuffers.at(currentFrame), 0, 1, &viewport);
-
 	auto currentCommandBuffer = commandBuffers.at(currentFrame);
 
 	// fixme: hasSRGBtexture
 	RenderPassConfiguration renderPassConfiguration{};
-	for (const auto& color : rts.colors)
+	for (const auto &color : rts.colors)
 		renderPassConfiguration.colorAttachments.push_back({ 
 			Vulkan::getTextureFormat(color.texture->getPixelFormat(), isPixelFormatSRGB(color.texture->getPixelFormat())).internalFormat,
 			false, 
@@ -2406,6 +2344,16 @@ void Graphics::startRenderPass()
 {
 	renderPassState.active = true;
 
+	VkViewport viewport{};
+	viewport.x = 0.0f;
+	viewport.y = 0.0f;
+	viewport.width = renderPassState.width;
+	viewport.height = renderPassState.height;
+	viewport.minDepth = 0.0f;
+	viewport.maxDepth = 1.0f;
+
+	vkCmdSetViewport(commandBuffers.at(currentFrame), 0, 1, &viewport);
+
 	if (renderPassState.useConfigurations)
 	{
 		auto &renderPassConfiguration = renderPassState.renderPassConfiguration;
@@ -2518,6 +2466,14 @@ void Graphics::cleanupUnusedObjects()
 	eraseUnusedObjects(graphicsPipelines, pipelineUsages, vkDestroyPipeline, device);
 }
 
+void Graphics::requestSwapchainRecreation()
+{
+	if (swapChain != VK_NULL_HANDLE)
+	{
+		swapChainRecreationRequested = true;
+	}
+}
+
 void Graphics::setComputeShader(Shader *shader)
 {
 	computeShader = shader;
@@ -2688,7 +2644,7 @@ VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration &confi
 	pipelineInfo.subpass = 0;
 	pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 	pipelineInfo.basePipelineIndex = -1;
-    pipelineInfo.renderPass = configuration.renderPass;
+	pipelineInfo.renderPass = configuration.renderPass;
 
 	VkPipeline graphicsPipeline;
 	if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS)
@@ -2740,6 +2696,26 @@ VkSampleCountFlagBits Graphics::getMsaaCount(int requestedMsaa) const
 		return VK_SAMPLE_COUNT_1_BIT;
 }
 
+void Graphics::setVsync(int vsync)
+{
+	if (vsync != this->vsync)
+	{
+		this->vsync = vsync;
+
+		// With the extension VK_EXT_swapchain_maintenance1 a swapchain recreation might not be needed
+		// https://github.com/KhronosGroup/Vulkan-Docs/blob/main/proposals/VK_EXT_swapchain_maintenance1.adoc
+		// However, there are not any drivers that support it, yet.
+		// Reevaluate again in the future.
+
+		requestSwapchainRecreation();
+	}
+}
+
+int Graphics::getVsync() const
+{
+	return vsync;
+}
+
 void Graphics::createColorResources()
 {
 	if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
@@ -2770,7 +2746,8 @@ void Graphics::createColorResources()
 		allocationInfo.usage = VMA_MEMORY_USAGE_AUTO;
 		allocationInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
 
-		vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &colorImage, &colorImageAllocation, nullptr);
+		if (vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &colorImage, &colorImageAllocation, nullptr))
+			throw love::Exception("failed to create color image");
 
 		VkImageViewCreateInfo imageViewInfo{};
 		imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -2787,7 +2764,8 @@ void Graphics::createColorResources()
 		imageViewInfo.subresourceRange.baseArrayLayer = 0;
 		imageViewInfo.subresourceRange.layerCount = 1;
 
-		vkCreateImageView(device, &imageViewInfo, nullptr, &colorImageView);
+		if (vkCreateImageView(device, &imageViewInfo, nullptr, &colorImageView) != VK_SUCCESS)
+			throw love::Exception("failed to create color image view");
 	}
 }
 
@@ -2838,7 +2816,8 @@ void Graphics::createDepthResources()
 	allocationInfo.usage = VMA_MEMORY_USAGE_AUTO;
 	allocationInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
 
-	vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &depthImage, &depthImageAllocation, nullptr);
+	if (vmaCreateImage(vmaAllocator, &imageInfo, &allocationInfo, &depthImage, &depthImageAllocation, nullptr) != VK_SUCCESS)
+		throw love::Exception("failed to create depth image");
 
 	VkImageViewCreateInfo imageViewInfo{};
 	imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -2857,7 +2836,8 @@ void Graphics::createDepthResources()
 	imageViewInfo.subresourceRange.baseArrayLayer = 0;
 	imageViewInfo.subresourceRange.layerCount = 1;
 
-	vkCreateImageView(device, &imageViewInfo, nullptr, &depthImageView);
+	if (vkCreateImageView(device, &imageViewInfo, nullptr, &depthImageView) != VK_SUCCESS)
+		throw love::Exception("failed to create depth image view");
 }
 
 void Graphics::createCommandPool()
@@ -2972,10 +2952,12 @@ void Graphics::cleanupSwapChain()
 	vmaDestroyImage(vmaAllocator, colorImage, colorImageAllocation);
 	vkDestroyImageView(device, depthImageView, nullptr);
 	vmaDestroyImage(vmaAllocator, depthImage, depthImageAllocation);
-    for (const auto &swapChainImageView : swapChainImageViews)
-        vkDestroyImageView(device, swapChainImageView, nullptr);
-    swapChainImageViews.clear();
+	for (const auto &swapChainImageView : swapChainImageViews)
+		vkDestroyImageView(device, swapChainImageView, nullptr);
+	swapChainImageViews.clear();
 	vkDestroySwapchainKHR(device, swapChain, nullptr);
+
+	swapChain = VK_NULL_HANDLE;
 }
 
 void Graphics::recreateSwapChain()

+ 20 - 18
src/modules/graphics/vulkan/Graphics.h

@@ -70,23 +70,23 @@ struct RenderPassConfiguration
 		bool resolve = false;
 	} staticData;
 
-    bool operator==(const RenderPassConfiguration &conf) const
+	bool operator==(const RenderPassConfiguration &conf) const
 	{
 		return colorAttachments == conf.colorAttachments && 
 			(memcmp(&staticData, &conf.staticData, sizeof(StaticRenderPassConfiguration)) == 0);
-    }
+	}
 };
 
 struct RenderPassConfigurationHasher
 {
-    size_t operator()(const RenderPassConfiguration &configuration) const
+	size_t operator()(const RenderPassConfiguration &configuration) const
 	{
 		size_t hashes[] = { 
 			XXH32(configuration.colorAttachments.data(), configuration.colorAttachments.size() * sizeof(VkFormat), 0),
 			XXH32(&configuration.staticData, sizeof(configuration.staticData), 0),
 		};
 		return XXH32(hashes, sizeof(hashes), 0);
-    }
+	}
 };
 
 struct FramebufferConfiguration
@@ -126,7 +126,8 @@ struct FramebufferConfigurationHasher
 
 struct OptionalInstanceExtensions
 {
-    bool physicalDeviceProperties2 = false;
+	// VK_KHR_get_physical_device_properties2
+	bool physicalDeviceProperties2 = false;
 };
 
 struct OptionalDeviceFeatures
@@ -155,7 +156,7 @@ struct OptionalDeviceFeatures
 
 struct GraphicsPipelineConfiguration
 {
-    VkRenderPass renderPass;
+	VkRenderPass renderPass;
 	VertexAttributes vertexAttributes;
 	Shader *shader = nullptr;
 	bool wireFrame;
@@ -288,8 +289,6 @@ public:
 	void queueCleanUp(std::function<void()> cleanUp);
 	void addReadbackCallback(std::function<void()> callback);
 	void submitGpuCommands(bool present, void *screenshotCallbackData = nullptr);
-	uint32_t getNumImagesInFlight() const;
-	uint32_t getFrameIndex() const;
 	const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
 	graphics::Texture *getDefaultTexture() const;
 	VkSampler getCachedSampler(const SamplerState &sampler);
@@ -298,6 +297,8 @@ public:
 	graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
 	const OptionalDeviceFeatures &getEnabledOptionalDeviceExtensions() const;
 	VkSampleCountFlagBits getMsaaCount(int requestedMsaa) const;
+	void setVsync(int vsync);
+	int getVsync() const;
 
 protected:
 	graphics::ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
@@ -328,10 +329,10 @@ private:
 	void createScreenshotCallbackBuffers();
 	void createDefaultRenderPass();
 	void createDefaultFramebuffers();
-    VkFramebuffer createFramebuffer(FramebufferConfiguration &configuration);
-    VkFramebuffer getFramebuffer(FramebufferConfiguration &configuration);
+	VkFramebuffer createFramebuffer(FramebufferConfiguration &configuration);
+	VkFramebuffer getFramebuffer(FramebufferConfiguration &configuration);
 	void createDefaultShaders();
-    VkRenderPass createRenderPass(RenderPassConfiguration &configuration);
+	VkRenderPass createRenderPass(RenderPassConfiguration &configuration);
 	VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration &configuration);
 	void createColorResources();
 	VkFormat findSupportedFormat(const std::vector<VkFormat> &candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
@@ -346,8 +347,8 @@ private:
 	void recreateSwapChain();
 	void initDynamicState();
 	void beginFrame();
-	void startRecordingGraphicsCommands(bool newFrame);
-	void endRecordingGraphicsCommands(bool present);
+	void startRecordingGraphicsCommands();
+	void endRecordingGraphicsCommands();
 	void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration &configuration);
 	bool usesConstantVertexColor(const VertexAttributes &attribs);
 	void createVulkanVertexFormat(
@@ -364,8 +365,8 @@ private:
 	void endRenderPass();
 	VkSampler createSampler(const SamplerState &sampler);
 	void cleanupUnusedObjects();
+	void requestSwapchainRecreation();
 
-	uint32_t instanceVersion = VK_API_VERSION_1_0;
 	VkInstance instance = VK_NULL_HANDLE;
 	VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
 	uint32_t deviceApiVersion = VK_API_VERSION_1_0;
@@ -377,7 +378,7 @@ private:
 	VkQueue graphicsQueue = VK_NULL_HANDLE;
 	VkQueue presentQueue = VK_NULL_HANDLE;
 	VkSurfaceKHR surface = VK_NULL_HANDLE;
-    VkSwapchainKHR swapChain = VK_NULL_HANDLE;
+	VkSwapchainKHR swapChain = VK_NULL_HANDLE;
 	VkSurfaceTransformFlagBitsKHR preTransform = {};
 	Matrix4 displayRotation;
 	std::vector<VkImage> swapChainImages;
@@ -393,7 +394,7 @@ private:
 	VmaAllocation depthImageAllocation = VK_NULL_HANDLE;
 	VkRenderPass defaultRenderPass = VK_NULL_HANDLE;
 	std::vector<VkFramebuffer> defaultFramebuffers;
-    std::unordered_map<RenderPassConfiguration, VkRenderPass, RenderPassConfigurationHasher> renderPasses;
+	std::unordered_map<RenderPassConfiguration, VkRenderPass, RenderPassConfigurationHasher> renderPasses;
 	std::unordered_map<FramebufferConfiguration, VkFramebuffer, FramebufferConfigurationHasher> framebuffers;
 	std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
 	std::unordered_map<VkRenderPass, bool> renderPassUsages;
@@ -402,17 +403,18 @@ private:
 	std::unordered_map<uint64, VkSampler> samplers;
 	VkCommandPool commandPool = VK_NULL_HANDLE;
 	std::vector<VkCommandBuffer> commandBuffers;
-	Shader* computeShader = nullptr;
+	Shader *computeShader = nullptr;
 	std::vector<VkSemaphore> imageAvailableSemaphores;
 	std::vector<VkSemaphore> renderFinishedSemaphores;
 	std::vector<VkFence> inFlightFences;
 	std::vector<VkFence> imagesInFlight;
+	int vsync = 1;
 	VkDeviceSize minUniformBufferOffsetAlignment = 0;
 	bool imageRequested = false;
 	uint32_t frameCounter = 0;
 	size_t currentFrame = 0;
 	uint32_t imageIndex = 0;
-	bool framebufferResized = false;
+	bool swapChainRecreationRequested = false;
 	bool transitionColorDepthLayouts = false;
 	VmaAllocator vmaAllocator = VK_NULL_HANDLE;
 	StrongRef<love::graphics::Texture> defaultTexture;

+ 46 - 24
src/modules/graphics/vulkan/Shader.cpp

@@ -196,7 +196,7 @@ bool Shader::loadVolatile()
 	createPipelineLayout();
 	createDescriptorPoolSizes();
 	createStreamBuffers();
-	descriptorSetsVector.resize(vgfx->getNumImagesInFlight());
+	descriptorSetsVector.resize(MAX_FRAMES_IN_FLIGHT);
 	currentFrame = 0;
 	currentUsedUniformStreamBuffersCount = 0;
 	currentUsedDescriptorSetsCount = 0;
@@ -234,8 +234,7 @@ void Shader::unloadVolatile()
 		}
 	}
 
-	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
-	gfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout, descriptorPools = descriptorPools, computePipeline = computePipeline](){
+	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 shaderModule : shaderModules)
@@ -274,10 +273,11 @@ VkPipeline Shader::getComputePipeline() const
 	return computePipeline;
 }
 
-void Shader::newFrame(uint32_t frameIndex)
+void Shader::newFrame()
 {
-	currentFrame = frameIndex;
+	currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
 
+	updatedUniforms.clear();
 	currentUsedUniformStreamBuffersCount = 0;
 	currentUsedDescriptorSetsCount = 0;
 
@@ -295,12 +295,10 @@ void Shader::newFrame(uint32_t frameIndex)
 	else
 		streamBuffers.at(0)->nextFrame();
 
-	if (currentUsedDescriptorSetsCount >= static_cast<uint32_t>(descriptorSetsVector.at(currentFrame).size()))
+	if (descriptorSetsVector.at(currentFrame).size() == 0)
 		descriptorSetsVector.at(currentFrame).push_back(allocateDescriptorSet());
 
-	currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount);
-
-	initDescriptorSet();
+	currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(0);
 }
 
 void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint bindPoint)
@@ -336,15 +334,17 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
 		VkWriteDescriptorSet uniformWrite{};
 		uniformWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 		uniformWrite.dstSet = currentDescriptorSet;
-		uniformWrite.dstBinding = uniformLocation;
+		uniformWrite.dstBinding = localUniformLocation;
 		uniformWrite.dstArrayElement = 0;
 		uniformWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
 		uniformWrite.descriptorCount = 1;
-		uniformWrite.pBufferInfo = &bufferInfo;			
+		uniformWrite.pBufferInfo = &bufferInfo;
 
 		vkUpdateDescriptorSets(device, 1, &uniformWrite, 0, nullptr);
 
 		currentUsedUniformStreamBuffersCount++;
+
+		updatedUniforms.insert(localUniformLocation);
 	}
 
 	static const std::vector<BuiltinUniform> builtinUniformTextures = {
@@ -365,18 +365,28 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
 			imageInfo.imageView = (VkImageView)texture->getRenderTargetHandle();
 			imageInfo.sampler = (VkSampler)texture->getSamplerHandle();
 
+			auto location = builtinUniformInfo[builtin]->location;
+
 			VkWriteDescriptorSet textureWrite{};
 			textureWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 			textureWrite.dstSet = currentDescriptorSet;
-			textureWrite.dstBinding = builtinUniformInfo[builtin]->location;
+			textureWrite.dstBinding = location;
 			textureWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
 			textureWrite.descriptorCount = 1;
 			textureWrite.pImageInfo = &imageInfo;
 
 			vkUpdateDescriptorSets(device, 1, &textureWrite, 0, nullptr);
+
+			updatedUniforms.insert(location);
 		}
 	}
 
+	for (const auto &u : uniformInfos)
+	{
+		if (updatedUniforms.find(u.second.location) == updatedUniforms.end())
+			updateUniform(&u.second, u.second.count);
+	}
+
 	vkCmdBindDescriptorSets(commandBuffer, bindPoint, pipelineLayout, 0, 1, &currentDescriptorSet, 0, nullptr);
 
 	currentUsedDescriptorSetsCount++;
@@ -386,7 +396,7 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind
 
 	currentDescriptorSet = descriptorSetsVector.at(currentFrame).at(currentUsedDescriptorSetsCount);
 
-	initDescriptorSet();
+	updatedUniforms.clear();
 }
 
 Shader::~Shader()
@@ -399,7 +409,7 @@ void Shader::attach()
 	auto &usedShadersInFrame = vgfx->getUsedShadersInFrame();
 	if (usedShadersInFrame.find(this) == usedShadersInFrame.end())
 	{
-		newFrame(vgfx->getFrameIndex());
+		newFrame();
 		usedShadersInFrame.insert(this);
 	}
 
@@ -463,6 +473,9 @@ void Shader::updateUniform(const UniformInfo* info, int count, bool internal)
 		{
 			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();
@@ -501,6 +514,9 @@ void Shader::updateUniform(const UniformInfo* info, int count, bool internal)
 
 		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;
@@ -526,12 +542,19 @@ void Shader::updateUniform(const UniformInfo* info, int count, bool internal)
 		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)
@@ -544,6 +567,8 @@ 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)
@@ -556,6 +581,8 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
 		if (oldBuffer)
 			oldBuffer->release();
 	}
+
+	updateUniform(info, count);
 }
 
 void Shader::calculateUniformBufferSizeAligned()
@@ -568,13 +595,6 @@ void Shader::calculateUniformBufferSizeAligned()
 	uniformBufferSizeAligned = factor * minAlignment;
 }
 
-void Shader::initDescriptorSet()
-{
-	for (const auto &entry : uniformInfos)
-		if (Vulkan::getDescriptorType(entry.second.baseType) != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
-			updateUniform(&entry.second, entry.second.count, true);
-}
-
 void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::SPIRType &type, size_t baseoff, const std::string &basename)
 {
 	using namespace spirv_cross;
@@ -776,7 +796,7 @@ void Shader::compileShaders()
 				auto defaultUniformBlockSize = comp.get_declared_struct_size(type);
 				localUniformStagingData.resize(defaultUniformBlockSize);
 				localUniformData.resize(defaultUniformBlockSize);
-				uniformLocation = comp.get_decoration(resource.id, spv::DecorationBinding);
+				localUniformLocation = comp.get_decoration(resource.id, spv::DecorationBinding);
 
 				memset(localUniformStagingData.data(), 0, defaultUniformBlockSize);
 				memset(localUniformData.data(), 0, defaultUniformBlockSize);
@@ -956,7 +976,7 @@ void Shader::createDescriptorSetLayout()
 	if (!localUniformStagingData.empty())
 	{
 		VkDescriptorSetLayoutBinding uniformBinding{};
-		uniformBinding.binding = uniformLocation;
+		uniformBinding.binding = localUniformLocation;
 		uniformBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
 		uniformBinding.descriptorCount = 1;
 		uniformBinding.stageFlags = stageFlags;
@@ -1023,7 +1043,9 @@ void Shader::createDescriptorPoolSizes()
 
 void Shader::createStreamBuffers()
 {
-	streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned));
+	size_t size = STREAMBUFFER_DEFAULT_SIZE * uniformBufferSizeAligned;
+	if (size > 0)
+		streamBuffers.push_back(new StreamBuffer(vgfx, BUFFERUSAGE_UNIFORM, size));
 }
 
 void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture)

+ 6 - 4
src/modules/graphics/vulkan/Shader.h

@@ -35,6 +35,7 @@
 #include <memory>
 #include <unordered_map>
 #include <queue>
+#include <set>
 
 
 namespace love
@@ -63,7 +64,7 @@ public:
 
 	const VkPipelineLayout getGraphicsPipelineLayout() const;
 
-	void newFrame(uint32_t frameIndex);
+	void newFrame();
 
 	void cmdPushDescriptorSets(VkCommandBuffer, VkPipelineBindPoint);
 
@@ -101,8 +102,7 @@ private:
 		const spirv_cross::SPIRType &type, 
 		size_t baseoff, 
 		const std::string &basename);
-	void initDescriptorSet();
-	void updateUniform(const UniformInfo* info, int count, bool internal);
+	void updateUniform(const UniformInfo *info, int count, bool internal);
 
 	VkDescriptorSet allocateDescriptorSet();
 
@@ -121,6 +121,8 @@ private:
 	std::queue<VkDescriptorSet> freeDescriptorSets;
 	std::vector<std::vector<VkDescriptorSet>> descriptorSetsVector;
 
+	std::set<uint32_t> updatedUniforms;
+
 	std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
 	std::vector<VkShaderModule> shaderModules;
 
@@ -135,7 +137,7 @@ private:
 	std::unique_ptr<StreamBuffer> uniformBufferObjectBuffer;
 	std::vector<uint8> localUniformData;
 	std::vector<uint8> localUniformStagingData;
-	uint32_t uniformLocation;
+	uint32_t localUniformLocation;
 	OptionalInt builtinUniformDataOffset;
 
 	std::unordered_map<std::string, int> attributes;

+ 0 - 11
src/modules/graphics/vulkan/Vulkan.cpp

@@ -31,7 +31,6 @@ namespace vulkan
 {
 
 static uint32_t numShaderSwitches;
-static int vsync = 1;
 
 void Vulkan::shaderSwitch()
 {
@@ -48,16 +47,6 @@ void Vulkan::resetShaderSwitches()
 	numShaderSwitches = 0;
 }
 
-void Vulkan::setVsync(int value)
-{
-	vsync = value;
-}
-
-int Vulkan::getVsync()
-{
-	return vsync;
-}
-
 VkFormat Vulkan::getVulkanVertexFormat(DataFormat format)
 {
 	switch (format)

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

@@ -58,9 +58,6 @@ public:
 	static uint32_t getNumShaderSwitches();
 	static void resetShaderSwitches();
 
-	static void setVsync(int vsync);
-	static int getVsync();
-
 	static VkFormat getVulkanVertexFormat(DataFormat format);
 	static TextureFormat getTextureFormat(PixelFormat, bool sRGB);
 	static std::string getVendorName(uint32_t vendorId);

+ 9 - 3
src/modules/window/sdl/Window.cpp

@@ -1100,8 +1100,11 @@ void Window::setVSync(int vsync)
 	}
 
 #ifdef LOVE_GRAPHICS_VULKAN
-	// TODO: this doesn't update the swap-chain, but it should.
-	love::graphics::vulkan::Vulkan::setVsync(vsync);
+	if (windowRenderer == love::graphics::RENDERER_VULKAN)
+	{
+		auto vgfx = dynamic_cast<love::graphics::vulkan::Graphics*>(graphics.get());
+		vgfx->setVsync(vsync);
+	}
 #endif
 
 #if defined(LOVE_GRAPHICS_METAL) && defined(LOVE_MACOS)
@@ -1132,7 +1135,10 @@ int Window::getVSync() const
 
 #ifdef LOVE_GRAPHICS_VULKAN
 	if (windowRenderer == love::graphics::RENDERER_VULKAN)
-		return love::graphics::vulkan::Vulkan::getVsync();
+	{
+		auto vgfx = dynamic_cast<love::graphics::vulkan::Graphics*>(graphics.get());
+		return vgfx->getVsync();
+	}
 #endif
 
 	return 0;