diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 3c0385d5a..0cb070ca8 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/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(cmd.indexCount), static_cast(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(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(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(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 bufferVector; std::vector offsets; @@ -1234,6 +1233,56 @@ namespace love { vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, static_cast(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; } diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index f945a2e6e..fe35d08f5 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/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>> descriptorSetsMap; VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL; - VkCommandBuffer offscreenRendertargetCommandBuffer; + VkFormat currentFramebufferOutputFormat = VK_FORMAT_UNDEFINED; + Texture* renderTargetTexture = nullptr; + float currentViewportWidth = 0; + float currentViewportHeight = 0; }; } } diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp index 9cdf05ada..3e778eb05 100644 --- a/src/modules/graphics/vulkan/Texture.cpp +++ b/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(r.w), static_cast(r.h), 1 }; - + + // fixme: we should use VK_IMAGE_LAYOUT_DST_OPTIMAL for transfer vkCmdCopyBufferToImage( commandBuffer, buffer, diff --git a/src/modules/graphics/vulkan/Vulkan.cpp b/src/modules/graphics/vulkan/Vulkan.cpp index 21e7e15ec..0aae7dad9 100644 --- a/src/modules/graphics/vulkan/Vulkan.cpp +++ b/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!"); }