Browse Source

vulkan: implement setRenderTargetsInternal
basic usage of canvases now works.
Colors probably aren't correct yet.

niki 3 years ago
parent
commit
95952c691d

+ 101 - 41
src/modules/graphics/vulkan/Graphics.cpp

@@ -200,6 +200,11 @@ namespace love {
 				Shader::current = Shader::standardShaders[graphics::Shader::StandardShader::STANDARD_DEFAULT];
 				currentPolygonMode = VK_POLYGON_MODE_FILL;
 				restoreState(states.back());
+				
+				setViewportSize(width, height, pixelwidth, pixelheight);
+				renderTargetTexture = nullptr;
+				currentViewportWidth = 0.0f;
+				currentViewportHeight = 0.0f;
 
 				return true;
 			}
@@ -317,6 +322,24 @@ namespace love {
 				vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(cmd.indexCount), static_cast<uint32_t>(cmd.instanceCount), 0, 0, 0);
 			}
 
+			void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) {
+				const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
+				const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
+
+				prepareDraw(attributes, buffers, texture, PRIMITIVE_TRIANGLES, CULL_BACK);
+
+				vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)quadIndexBuffer->getHandle(), 0, getVulkanIndexBufferType(INDEX_UINT16));
+
+				int baseVertex = start * 4;
+
+				for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) {
+					int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
+
+					vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(quadcount * 6), 1, 0, baseVertex, 0);
+					baseVertex += quadcount * 4;
+				}
+			}
+
 			void Graphics::setColor(Colorf c) {
 				c.r = std::min(std::max(c.r, 0.0f), 1.0f);
 				c.g = std::min(std::max(c.g, 0.0f), 1.0f);
@@ -363,24 +386,6 @@ namespace love {
 				return RENDERER_VULKAN;
 			}
 
-			void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) {
-				const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
-				const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
-
-				prepareDraw(attributes, buffers, texture, PRIMITIVE_TRIANGLES, CULL_BACK);
-
-				vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)quadIndexBuffer->getHandle(), 0, getVulkanIndexBufferType(INDEX_UINT16));
-				
-				int baseVertex = start * 4;
-
-				for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) {
-					int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
-
-					vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(quadcount * 6), 1, 0, baseVertex, 0);
-					baseVertex += quadcount * 4;
-				}
-			}
-
 			graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
 				return new StreamBuffer(this, type, size);
 			}
@@ -391,6 +396,14 @@ namespace love {
 			}
 			
 			void Graphics::setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) {
+				endRenderPass();
+
+				if (rts.colors.size() == 0) {
+					startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height);
+				} else {
+					auto firstRenderTarget = rts.getFirstTarget();
+					startRenderPass(static_cast<Texture*>(firstRenderTarget.texture), pixelw, pixelh);
+				}
 			}
 
 			// END IMPLEMENTATION OVERRIDDEN FUNCTIONS
@@ -420,34 +433,17 @@ namespace love {
 					throw love::Exception("failed to begin recording command buffer");
 				}
 
-				VkRenderingAttachmentInfo colorAttachmentInfo{};
-				colorAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
-				colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
-				colorAttachmentInfo.imageView = swapChainImageViews[imageIndex];
-				colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
-				colorAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-				colorAttachmentInfo.clearValue = clearColor;
-
-				VkRenderingInfo renderingInfo{};
-				renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
-				renderingInfo.renderArea.extent = swapChainExtent;
-				renderingInfo.layerCount = 1;
-				renderingInfo.colorAttachmentCount = 1;
-				renderingInfo.pColorAttachments = &colorAttachmentInfo;
-
 				Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
 
-				vkCmdBeginRendering(commandBuffers.at(imageIndex), &renderingInfo);
-
-				currentGraphicsPipeline = VK_NULL_HANDLE;
+				startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height);
 			}
 
 			void Graphics::endRecordingGraphicsCommands() {
 				const auto& commandBuffer = commandBuffers.at(imageIndex);
 
-				vkCmdEndRendering(commandBuffer);
+				endRenderPass();
 
-				Vulkan::cmdTransitionImageLayout(commandBuffer, swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+				Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
 
 				if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) {
 					throw love::Exception("failed to record command buffer");
@@ -1202,6 +1198,9 @@ namespace love {
 				configuration.blendState = states.back().blend;
 				configuration.winding = states.back().winding;
 				configuration.cullmode = cullmode;
+				configuration.framebufferFormat = currentFramebufferOutputFormat;
+				configuration.viewportWidth = currentViewportWidth;
+				configuration.viewportHeight = currentViewportHeight;
 				std::vector<VkBuffer> bufferVector;
 
 				std::vector<VkDeviceSize> offsets;
@@ -1234,6 +1233,56 @@ namespace love {
 				vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, static_cast<uint32_t>(bufferVector.size()), bufferVector.data(), offsets.data());
 			}
 
+			void Graphics::startRenderPass(Texture* texture, uint32_t w, uint32_t h) {
+				VkRenderingAttachmentInfo colorAttachmentInfo{};
+				colorAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
+				colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+				if (texture) {
+					colorAttachmentInfo.imageView = texture->getImageView();
+					colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;	// fixme: we want to clear a canvas sometimes.
+					auto vulkanFormat = Vulkan::getTextureFormat(texture->getPixelFormat());
+					currentFramebufferOutputFormat = vulkanFormat.internalFormat;
+
+					renderTargetTexture = texture;
+				} else {
+					colorAttachmentInfo.imageView = swapChainImageViews[imageIndex];
+					colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+					currentFramebufferOutputFormat = swapChainImageFormat;
+
+					renderTargetTexture = nullptr;
+				}
+				colorAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+				colorAttachmentInfo.clearValue = clearColor;
+
+				VkRenderingInfo renderingInfo{};
+				renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
+				renderingInfo.renderArea.extent.width = w;
+				renderingInfo.renderArea.extent.height = h;
+				renderingInfo.layerCount = 1;
+				renderingInfo.colorAttachmentCount = 1;
+				renderingInfo.pColorAttachments = &colorAttachmentInfo;
+
+				currentViewportWidth = (float)w;
+				currentViewportHeight = (float)h;
+
+				if (renderTargetTexture) {
+					Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)texture->getHandle(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+				}
+
+				vkCmdBeginRendering(commandBuffers.at(imageIndex), &renderingInfo);
+
+				currentGraphicsPipeline = VK_NULL_HANDLE;
+			}
+
+			void Graphics::endRenderPass() {
+				vkCmdEndRendering(commandBuffers.at(imageIndex));
+
+				if (renderTargetTexture) {
+					Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)renderTargetTexture->getHandle(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
+					renderTargetTexture = nullptr;
+				}
+			}
+
 			VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) {
 				auto shader = configuration.shader;
 				auto shaderStages = shader->getShaderStages();
@@ -1253,8 +1302,8 @@ namespace love {
 				VkViewport viewport{};
 				viewport.x = 0.0f;
 				viewport.y = 0.0f;
-				viewport.width = (float)swapChainExtent.width;
-				viewport.height = (float)swapChainExtent.height;
+				viewport.width = configuration.viewportWidth;
+				viewport.height = configuration.viewportHeight;
 				viewport.minDepth = 0.0f;
 				viewport.maxDepth = 1.0f;
 
@@ -1323,10 +1372,12 @@ namespace love {
 				}
 				graphicsPipelineLayouts.push_back(pipelineLayout);
 
+				VkFormat framebufferOutputFormat = configuration.framebufferFormat;
+
 				VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{};
 				pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
 				pipelineRenderingCreateInfo.colorAttachmentCount = 1;
-				pipelineRenderingCreateInfo.pColorAttachmentFormats = &swapChainImageFormat;
+				pipelineRenderingCreateInfo.pColorAttachmentFormats = &framebufferOutputFormat;
 
 				VkGraphicsPipelineCreateInfo pipelineInfo{};
 				pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
@@ -1440,6 +1491,15 @@ namespace love {
 			}
 
 			bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other) {
+				if (first.viewportHeight != other.viewportHeight) {
+					return false;
+				}
+				if (first.viewportWidth != other.viewportWidth) {
+					return false;
+				}
+				if (first.framebufferFormat != other.framebufferFormat) {
+					return false;
+				}
 				if (first.cullmode != other.cullmode) {
 					return false;
 				}

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

@@ -29,6 +29,9 @@ namespace love {
 				ColorChannelMask colorChannelMask;
 				Winding winding;
 				CullMode cullmode;
+				VkFormat framebufferFormat;
+				float viewportWidth;
+				float viewportHeight;
 
 				friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
 			};
@@ -174,6 +177,8 @@ namespace love {
 				graphics::StreamBuffer* getUniformBuffer();
 				void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration);
 				void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
+				void startRenderPass(Texture*, uint32_t w, uint32_t h);
+				void endRenderPass();
 
 				VkInstance instance = VK_NULL_HANDLE;
 				VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
@@ -213,7 +218,10 @@ namespace love {
 				std::vector<std::pair<DecriptorSetConfiguration, std::vector<VkDescriptorSet>>> descriptorSetsMap;
 				VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
 
-				VkCommandBuffer offscreenRendertargetCommandBuffer;
+				VkFormat currentFramebufferOutputFormat = VK_FORMAT_UNDEFINED;
+				Texture* renderTargetTexture = nullptr;
+				float currentViewportWidth = 0;
+				float currentViewportHeight = 0;
 			};
 		}
 	}

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

@@ -19,6 +19,12 @@ namespace love {
 
 				auto vulkanFormat = Vulkan::getTextureFormat(format);
 
+				VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+
+				if (isRenderTarget()) {
+					usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+				}
+
 				VkImageCreateInfo imageInfo{};
 				imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
 				imageInfo.imageType = VK_IMAGE_TYPE_2D;
@@ -30,7 +36,7 @@ namespace love {
 				imageInfo.format = vulkanFormat.internalFormat;
 				imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
 				imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-				imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+				imageInfo.usage = usageFlags;
 				imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
 				imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
 
@@ -39,7 +45,6 @@ namespace love {
 				if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) {
 					throw love::Exception("failed to create image");
 				}
-				// fixme: is there a way we don't use the general image layout?
 				transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
 
 				if (data) {
@@ -62,7 +67,7 @@ namespace love {
 							defaultPixels.push_back(255);
 							defaultPixels.push_back(255);
 							defaultPixels.push_back(255);
-							defaultPixels.push_back(0);
+							defaultPixels.push_back(255);
 						}
 						Rect rect = { 0, 0, width, height };
 						uploadByteData(PIXELFORMAT_RGBA8_UNORM, defaultPixels.data(), defaultPixels.size(), 0, 0, rect);
@@ -174,7 +179,8 @@ namespace love {
 					static_cast<uint32_t>(r.w),
 					static_cast<uint32_t>(r.h), 1
 				};
-
+				
+				// fixme: we should use VK_IMAGE_LAYOUT_DST_OPTIMAL for transfer
 				vkCmdCopyBufferToImage(
 					commandBuffer,
 					buffer,

+ 15 - 2
src/modules/graphics/vulkan/Vulkan.cpp

@@ -279,8 +279,7 @@ namespace love {
 			std::string Vulkan::getVulkanApiVersion(uint32_t version) {
 				std::stringstream ss;
 
-				ss << VK_API_VERSION_VARIANT(version) 
-				   << "." << VK_API_VERSION_MAJOR(version) 
+				ss << "." << VK_API_VERSION_MAJOR(version) 
 				   << "." << VK_API_VERSION_MINOR(version) 
 				   << "." << VK_API_VERSION_PATCH(version);
 
@@ -458,6 +457,20 @@ namespace love {
 					sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
 					destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
 				}
+				else if (oldLayout == VK_IMAGE_LAYOUT_GENERAL && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
+					barrier.srcAccessMask = 0;
+					barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
+					sourceStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+					destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+				}
+				else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_GENERAL) {
+					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;
+				}
 				else {
 					throw std::invalid_argument("unsupported layout transition!");
 				}