Browse Source

vulkan: fix video rendering

niki 3 years ago
parent
commit
94c58e1599

+ 30 - 8
src/modules/graphics/vulkan/Graphics.cpp

@@ -142,6 +142,9 @@ namespace love {
 				}
 				imagesInFlight[imageIndex] = inFlightFences[currentFrame];
 
+				// all data transfers should happen before any draw calls.
+				std::vector<VkCommandBuffer> submitCommandbuffers = { dataTransferCommandBuffers.at(currentFrame), commandBuffers.at(imageIndex) };
+
 				VkSubmitInfo submitInfo{};
 				submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
 
@@ -151,8 +154,8 @@ namespace love {
 				submitInfo.pWaitSemaphores = waitSemaphores;
 				submitInfo.pWaitDstStageMask = waitStages;
 
-				submitInfo.commandBufferCount = 1;
-				submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
+				submitInfo.commandBufferCount = static_cast<uint32_t>(submitCommandbuffers.size());
+				submitInfo.pCommandBuffers = submitCommandbuffers.data();
 
 				VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) };
 				submitInfo.signalSemaphoreCount = 1;
@@ -213,10 +216,10 @@ namespace love {
 				initCapabilities();
 				createSwapChain();
 				createImageViews();
-				createDefaultShaders();
 				createCommandPool();
 				createCommandBuffers();
 				createDefaultTexture();
+				createDefaultShaders();
 				createQuadIndexBuffer();
 				createSyncObjects();
 				startRecordingGraphicsCommands();
@@ -510,6 +513,9 @@ namespace love {
 				if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) {
 					throw love::Exception("failed to begin recording command buffer");
 				}
+				if (vkBeginCommandBuffer(dataTransferCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) {
+					throw love::Exception("failed to begin recording data transfer command buffer");
+				}
 
 				Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
 
@@ -519,8 +525,6 @@ namespace love {
 			}
 
 			void Graphics::endRecordingGraphicsCommands() {
-				const auto& commandBuffer = commandBuffers.at(imageIndex);
-
 				endRenderPass();
 
 				Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
@@ -528,6 +532,9 @@ namespace love {
 				if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) {
 					throw love::Exception("failed to record command buffer");
 				}
+				if (vkEndCommandBuffer(dataTransferCommandBuffers.at(currentFrame)) != VK_SUCCESS) {
+					throw love::Exception("failed to record data transfer command buffer");
+				}
 			}
 
 			void Graphics::updatedBatchedDrawBuffers() {
@@ -547,12 +554,16 @@ namespace love {
 				return minUniformBufferOffsetAlignment;
 			}
 
+			graphics::Texture* Graphics::getDefaultTexture() const {
+				return dynamic_cast<graphics::Texture*>(standardTexture.get());
+			}
+
 			const PFN_vkCmdPushDescriptorSetKHR Graphics::getVkCmdPushDescriptorSetKHRFunctionPointer() const {
 				return vkCmdPushDescriptorSet;
 			}
 
-			void Graphics::executeCommand(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp) {
-				command(commandBuffers.at(imageIndex));
+			void Graphics::queueDatatransfer(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp) {
+				command(dataTransferCommandBuffers.at(currentFrame));
 				cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp));
 			}
 
@@ -1207,7 +1218,6 @@ namespace love {
 				else {
 					configuration.shader->setMainTex(texture);
 				}
-				configuration.shader->setVideoTextures(standardTexture.get(), standardTexture.get(), standardTexture.get());
 
 				ensureGraphicsPipelineConfiguration(configuration);
 
@@ -1416,6 +1426,7 @@ namespace love {
 
 			void Graphics::createCommandBuffers() {
 				commandBuffers.resize(swapChainImages.size());
+				dataTransferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
 
 				VkCommandBufferAllocateInfo allocInfo{};
 				allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
@@ -1426,6 +1437,16 @@ namespace love {
 				if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
 					throw love::Exception("failed to allocate command buffers");
 				}
+
+				VkCommandBufferAllocateInfo dataTransferAllocInfo{};
+				dataTransferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+				dataTransferAllocInfo.commandPool = commandPool;
+				dataTransferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+				dataTransferAllocInfo.commandBufferCount = (uint32_t)MAX_FRAMES_IN_FLIGHT;
+
+				if (vkAllocateCommandBuffers(device, &dataTransferAllocInfo, dataTransferCommandBuffers.data()) != VK_SUCCESS) {
+					throw love::Exception("failed to allocate data transfer command buffers");
+				}
 			}
 
 			void Graphics::createSyncObjects() {
@@ -1565,6 +1586,7 @@ namespace love {
 			void Graphics::cleanupSwapChain() {
 				vkDestroyDescriptorPool(device, descriptorPool, nullptr);
 				vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
+				vkFreeCommandBuffers(device, commandPool, MAX_FRAMES_IN_FLIGHT, dataTransferCommandBuffers.data());
 				for (auto const& p : graphicsPipelines) {
 					vkDestroyPipeline(device, p.second, nullptr);
 				}

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

@@ -114,8 +114,7 @@ namespace love {
 				GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) override { return nullptr;  };
 				GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) { return nullptr; }
 
-				// fixme: better naming for these two functions?
-				void executeCommand(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp);
+				void queueDatatransfer(std::function<void(VkCommandBuffer)> command, std::function<void()> cleanUp);
 				void queueCleanUp(std::function<void()> cleanUp);
 
 				VkCommandBuffer beginSingleTimeCommands();
@@ -124,6 +123,7 @@ namespace love {
 				uint32_t getNumImagesInFlight() const;
 				const PFN_vkCmdPushDescriptorSetKHR getVkCmdPushDescriptorSetKHRFunctionPointer() const;
 				const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
+				graphics::Texture* getDefaultTexture() const;
 
 			protected:
 				graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { 
@@ -190,6 +190,7 @@ namespace love {
 				std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines;	// FIXME improve performance by using a hash map
 				VkCommandPool commandPool = VK_NULL_HANDLE;
 				std::vector<VkCommandBuffer> commandBuffers;
+				std::vector<VkCommandBuffer> dataTransferCommandBuffers;
 				VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
 				std::vector<VkSemaphore> imageAvailableSemaphores;
 				std::vector<VkSemaphore> renderFinishedSemaphores;

+ 5 - 0
src/modules/graphics/vulkan/Shader.cpp

@@ -311,6 +311,11 @@ namespace love {
 				auto vgfx = (Graphics*)gfx;
 				device = vgfx->getDevice();
 
+				mainTex = vgfx->getDefaultTexture();
+				ytexture = vgfx->getDefaultTexture();
+				crtexture = vgfx->getDefaultTexture();
+				cbtexture = vgfx->getDefaultTexture();
+
 				for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) {
 					if (!stages[i])
 						continue;

+ 7 - 3
src/modules/graphics/vulkan/Texture.cpp

@@ -43,6 +43,7 @@ namespace love {
 				if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) {
 					throw love::Exception("failed to create image");
 				}
+				// fixme: we should use VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL as the default image layout instead of VK_IMAGE_LAYOUT_GENERAL.
 				transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
 
 				if (data) {
@@ -253,22 +254,25 @@ namespace love {
 						static_cast<uint32_t>(r.h), 1
 					};
 
-					// fixme: use VK_IMAGE_LAYOUT_DST_OPTIMAL
+					Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+
 					vkCmdCopyBufferToImage(
 						commandBuffer,
 						buffer,
 						image,
-						VK_IMAGE_LAYOUT_GENERAL,
+						VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
 						1,
 						&region
 					);
+
+					Vulkan::cmdTransitionImageLayout(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
 				};
 
 				auto cleanUp = [allocator = allocator, stagingBuffer, vmaAllocation]() {
 					vmaDestroyBuffer(allocator, stagingBuffer, vmaAllocation);
 				};
 
-				vgfx->executeCommand(command, cleanUp);
+				vgfx->queueDatatransfer(command, cleanUp);
 			}
 		}
 	}

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

@@ -491,6 +491,13 @@ namespace love {
 					sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
 					destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
 				}
+				else if (oldLayout == VK_IMAGE_LAYOUT_GENERAL && 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_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_GENERAL) {
 					barrier.srcAccessMask = 0;
 					barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;