diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index d25d67c05..69060e8aa 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -326,7 +326,7 @@ void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallback if (submitMode == SUBMIT_PRESENT) { - VkImage backbufferImage = fakeBackbuffer != nullptr ? (VkImage)fakeBackbuffer->getHandle() : swapChainImages.at(imageIndex); + VkImage backbufferImage = fakeBackbuffer != nullptr ? (VkImage)fakeBackbuffer->getRenderTargetHandle() : swapChainImages.at(imageIndex); if (pendingScreenshotCallbacks.empty()) { @@ -1193,13 +1193,8 @@ static bool computeDispatchBarrierFlags(Shader *shader, VkAccessFlags &dstAccess // All writable images use the GENERAL layout. // TODO: this is pretty messy. - VkAccessFlags texAccessFlags = 0; - VkPipelineStageFlags texStageFlags = 0; bool depthStencil = isPixelFormatDepthStencil(tex->getPixelFormat()); - Vulkan::setImageLayoutTransitionOptions(false, tex->isRenderTarget(), depthStencil, VK_IMAGE_LAYOUT_GENERAL, texAccessFlags, texStageFlags); - - dstAccessFlags |= texAccessFlags; - dstStageFlags |= texStageFlags; + Vulkan::addImageLayoutTransitionOptions(false, tex->isRenderTarget(), depthStencil, VK_IMAGE_LAYOUT_GENERAL, dstAccessFlags, dstStageFlags); } for (const auto &info : shader->getActiveStorageBufferInfo()) @@ -1285,7 +1280,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int pixelw, in if (isWindow) setDefaultRenderPass(); else - setRenderPass(rts, pixelw, pixelh, hasSRGBtexture); + setRenderPass(rts, pixelw, pixelh); } // END IMPLEMENTATION OVERRIDDEN FUNCTIONS @@ -2120,8 +2115,9 @@ VkFramebuffer Graphics::createFramebuffer(FramebufferConfiguration &configuratio if (configuration.staticData.depthView) attachments.push_back(configuration.staticData.depthView); - if (configuration.staticData.resolveView) - attachments.push_back(configuration.staticData.resolveView); + // Resolve attachments after everything else to match createRenderPass. + for (const auto &colorResolveView : configuration.colorResolveViews) + attachments.push_back(colorResolveView); VkFramebufferCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; @@ -2198,6 +2194,7 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration) std::vector attachments; std::vector colorAttachmentRefs; + std::vector colorResolveAttachmentRefs; uint32_t attachment = 0; for (const auto &colorAttachment : configuration.colorAttachments) @@ -2214,24 +2211,33 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration) colorDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorDescription.initialLayout = colorAttachment.layout; - colorDescription.finalLayout = colorAttachment.layout; + if (colorAttachment.msaaSamples > 1) + { + colorDescription.initialLayout = colorAttachment.msaaLayout; + colorDescription.finalLayout = colorAttachment.msaaLayout; + } + else + { + colorDescription.initialLayout = colorAttachment.layout; + colorDescription.finalLayout = colorAttachment.layout; + } attachments.push_back(colorDescription); - VkAccessFlags texBeginAccessFlags = 0; - VkPipelineStageFlags texBeginStageFlags = 0; - Vulkan::setImageLayoutTransitionOptions(true, true, false, colorAttachment.layout, texBeginAccessFlags, texBeginStageFlags); - beginDependency.srcAccessMask |= texBeginAccessFlags; - beginDependency.srcStageMask |= texBeginStageFlags; + // I had a TODO here, but I don't remember why... + if (colorAttachment.layout != VK_IMAGE_LAYOUT_UNDEFINED) + { + Vulkan::addImageLayoutTransitionOptions(true, true, false, colorAttachment.layout, beginDependency.srcAccessMask, beginDependency.srcStageMask); + Vulkan::addImageLayoutTransitionOptions(false, true, false, colorAttachment.layout, endDependency.dstAccessMask, endDependency.dstStageMask); + } - VkAccessFlags texEndAccessFlags = 0; - VkPipelineStageFlags texEndStageFlags = 0; - Vulkan::setImageLayoutTransitionOptions(false, true, false, colorAttachment.layout, texEndAccessFlags, texEndStageFlags); - endDependency.dstAccessMask |= texEndAccessFlags; - endDependency.dstStageMask |= texEndStageFlags; + if (colorAttachment.msaaLayout != VK_IMAGE_LAYOUT_UNDEFINED) + { + Vulkan::addImageLayoutTransitionOptions(true, true, false, colorAttachment.msaaLayout, beginDependency.srcAccessMask, beginDependency.srcStageMask); + Vulkan::addImageLayoutTransitionOptions(false, true, false, colorAttachment.msaaLayout, endDependency.dstAccessMask, endDependency.dstStageMask); + } } - subPass.colorAttachmentCount = static_cast(colorAttachmentRefs.size()); + subPass.colorAttachmentCount = static_cast(configuration.colorAttachments.size()); subPass.pColorAttachments = colorAttachmentRefs.data(); VkAttachmentReference depthStencilAttachmentRef{}; @@ -2252,36 +2258,43 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration) depthStencilAttachment.finalLayout = configuration.staticData.depthStencilAttachment.layout; attachments.push_back(depthStencilAttachment); - VkAccessFlags texBeginAccessFlags = 0; - VkPipelineStageFlags texBeginStageFlags = 0; - Vulkan::setImageLayoutTransitionOptions(true, true, true, configuration.staticData.depthStencilAttachment.layout, texBeginAccessFlags, texBeginStageFlags); - beginDependency.srcAccessMask |= texBeginAccessFlags; - beginDependency.srcStageMask |= texBeginStageFlags; - - VkAccessFlags texEndAccessFlags = 0; - VkPipelineStageFlags texEndStageFlags = 0; - Vulkan::setImageLayoutTransitionOptions(false, true, true, configuration.staticData.depthStencilAttachment.layout, texEndAccessFlags, texEndStageFlags); - endDependency.dstAccessMask |= texEndAccessFlags; - endDependency.dstStageMask |= texEndStageFlags; + Vulkan::addImageLayoutTransitionOptions(true, true, true, configuration.staticData.depthStencilAttachment.layout, beginDependency.srcAccessMask, beginDependency.srcStageMask); + Vulkan::addImageLayoutTransitionOptions(false, true, true, configuration.staticData.depthStencilAttachment.layout, endDependency.dstAccessMask, endDependency.dstStageMask); } - VkAttachmentReference colorAttachmentResolveRef{}; + // Add resolve attachments after everything else to make pClearValues simpler to implement. if (configuration.staticData.resolve) { - colorAttachmentResolveRef.attachment = attachment++; - colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - subPass.pResolveAttachments = &colorAttachmentResolveRef; + for (const auto &colorAttachment : configuration.colorAttachments) + { + VkAttachmentReference reference{}; + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkAttachmentDescription colorAttachmentResolve{}; - colorAttachmentResolve.format = configuration.colorAttachments.at(0).format; - colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachments.push_back(colorAttachmentResolve); + if (colorAttachment.layout == VK_IMAGE_LAYOUT_UNDEFINED) + { + reference.attachment = VK_ATTACHMENT_UNUSED; + colorResolveAttachmentRefs.push_back(reference); + } + else + { + reference.attachment = attachment++; + colorResolveAttachmentRefs.push_back(reference); + + VkAttachmentDescription resolveDescription{}; + resolveDescription.format = colorAttachment.format; + resolveDescription.samples = VK_SAMPLE_COUNT_1_BIT; + resolveDescription.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + resolveDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + resolveDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + resolveDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + resolveDescription.initialLayout = colorAttachment.layout; + resolveDescription.finalLayout = colorAttachment.layout; + + attachments.push_back(resolveDescription); + } + } + + subPass.pResolveAttachments = colorResolveAttachmentRefs.data(); } std::array dependencies = { beginDependency, endDependency }; @@ -2496,8 +2509,6 @@ void Graphics::setDefaultRenderPass() RenderPassConfiguration renderPassConfiguration{}; - renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples }); - VkFormat dsformat = backbufferHasDepth || backbufferHasStencil ? depthStencilFormat : VK_FORMAT_UNDEFINED; renderPassConfiguration.staticData.depthStencilAttachment = { dsformat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples }; if (msaaSamples & VK_SAMPLE_COUNT_1_BIT) @@ -2512,19 +2523,22 @@ void Graphics::setDefaultRenderPass() if (msaaSamples & VK_SAMPLE_COUNT_1_BIT) { + renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples }); + if (!swapChainImageViews.empty()) framebufferConfiguration.colorViews.push_back(swapChainImageViews.at(imageIndex)); else framebufferConfiguration.colorViews.push_back(fakeBackbuffer->getRenderTargetView(0, 0)); - framebufferConfiguration.staticData.resolveView = VK_NULL_HANDLE; } else { + renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples }); + framebufferConfiguration.colorViews.push_back(colorImageView); if (!swapChainImageViews.empty()) - framebufferConfiguration.staticData.resolveView = swapChainImageViews.at(imageIndex); + framebufferConfiguration.colorResolveViews.push_back(swapChainImageViews.at(imageIndex)); else - framebufferConfiguration.staticData.resolveView = fakeBackbuffer->getRenderTargetView(0, 0); + framebufferConfiguration.colorResolveViews.push_back(fakeBackbuffer->getRenderTargetView(0, 0)); } renderPassState.renderPassConfiguration = std::move(renderPassConfiguration); @@ -2554,35 +2568,62 @@ void Graphics::setDefaultRenderPass() } } -void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture) +void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh) { - // fixme: hasSRGBtexture RenderPassConfiguration renderPassConfiguration{}; + VkSampleCountFlagBits msaa = VK_SAMPLE_COUNT_1_BIT; + for (const auto &color : rts.colors) + { + auto tex = (Texture *)color.texture; renderPassConfiguration.colorAttachments.push_back({ - Vulkan::getTextureFormat(color.texture->getPixelFormat()).internalFormat, - ((Texture*)color.texture)->getImageLayout(), + Vulkan::getTextureFormat(tex->getPixelFormat()).internalFormat, + tex->getImageLayout(), + tex->getMSAAImageLayout(), VK_ATTACHMENT_LOAD_OP_LOAD, - dynamic_cast(color.texture)->getMsaaSamples() }); + tex->getMsaaSamples() }); + + if (tex->getMSAAImageLayout() != VK_IMAGE_LAYOUT_UNDEFINED && tex->getImageLayout() != VK_IMAGE_LAYOUT_UNDEFINED) + renderPassConfiguration.staticData.resolve = true; + + msaa = tex->getMsaaSamples(); + } + if (rts.depthStencil.texture != nullptr) + { + auto tex = (Texture *)rts.depthStencil.texture; renderPassConfiguration.staticData.depthStencilAttachment = { Vulkan::getTextureFormat(rts.depthStencil.texture->getPixelFormat()).internalFormat, - ((Texture*)rts.depthStencil.texture)->getImageLayout(), + tex->getImageLayout(), VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_LOAD_OP_LOAD, - dynamic_cast(rts.depthStencil.texture)->getMsaaSamples() }; + tex->getMsaaSamples() }; + + msaa = tex->getMsaaSamples(); + } FramebufferConfiguration configuration{}; for (const auto &color : rts.colors) { auto tex = (Texture*)color.texture; - configuration.colorViews.push_back(tex->getRenderTargetView(color.mipmap, color.slice)); + if (tex->getMSAA() > 1) + { + configuration.colorViews.push_back(tex->getMSAARenderTargetView(color.mipmap, color.slice)); + configuration.colorResolveViews.push_back(tex->getRenderTargetView(color.mipmap, color.slice)); + } + else + { + configuration.colorViews.push_back(tex->getRenderTargetView(color.mipmap, color.slice)); + } } if (rts.depthStencil.texture != nullptr) { auto tex = (Texture*)rts.depthStencil.texture; - configuration.staticData.depthView = tex->getRenderTargetView(rts.depthStencil.mipmap, rts.depthStencil.slice); + if (tex->getMSAA() > 1) + configuration.staticData.depthView = tex->getMSAARenderTargetView(rts.depthStencil.mipmap, rts.depthStencil.slice); + else + configuration.staticData.depthView = tex->getRenderTargetView(rts.depthStencil.mipmap, rts.depthStencil.slice); } configuration.staticData.width = static_cast(pixelw); @@ -2606,7 +2647,7 @@ void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, b renderPassState.pipeline = VK_NULL_HANDLE; renderPassState.width = static_cast(pixelw); renderPassState.height = static_cast(pixelh); - renderPassState.msaa = VK_SAMPLE_COUNT_1_BIT; + renderPassState.msaa = msaa; renderPassState.numColorAttachments = static_cast(rts.colors.size()); renderPassState.packedColorAttachmentFormats = 0; for (size_t i = 0; i < rts.colors.size(); i++) diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index f2b714bca..47fbaebed 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -50,6 +50,7 @@ struct ColorAttachment { VkFormat format = VK_FORMAT_UNDEFINED; VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; + VkImageLayout msaaLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkAttachmentLoadOp loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT; @@ -57,6 +58,7 @@ struct ColorAttachment { return format == attachment.format && layout == attachment.layout && + msaaLayout == attachment.msaaLayout && loadOp == attachment.loadOp && msaaSamples == attachment.msaaSamples; } @@ -112,11 +114,11 @@ struct RenderPassConfigurationHasher struct FramebufferConfiguration { std::vector colorViews; + std::vector colorResolveViews; struct StaticFramebufferConfiguration { VkImageView depthView = VK_NULL_HANDLE; - VkImageView resolveView = VK_NULL_HANDLE; uint32_t width = 0; uint32_t height = 0; @@ -126,7 +128,7 @@ struct FramebufferConfiguration bool operator==(const FramebufferConfiguration &conf) const { - return colorViews == conf.colorViews && + return colorViews == conf.colorViews && colorResolveViews == conf.colorResolveViews && (memcmp(&staticData, &conf.staticData, sizeof(StaticFramebufferConfiguration)) == 0); } }; @@ -137,6 +139,7 @@ struct FramebufferConfigurationHasher { size_t hashes[] = { XXH32(configuration.colorViews.data(), configuration.colorViews.size() * sizeof(VkImageView), 0), + XXH32(configuration.colorResolveViews.data(), configuration.colorResolveViews.size() * sizeof(VkImageView), 0), XXH32(&configuration.staticData, sizeof(configuration.staticData), 0), }; @@ -337,7 +340,7 @@ private: VertexAttributesID attributesID, const BufferBindings &buffers, graphics::Texture *texture, PrimitiveType, CullMode); - void setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture); + void setRenderPass(const RenderTargets &rts, int pixelw, int pixelh); void setDefaultRenderPass(); void startRenderPass(); void endRenderPass(); diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index 34bfe5c85..fc0216635 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -1044,7 +1044,7 @@ void Shader::setTextureDescriptor(const UniformInfo *info, love::graphics::Textu // Samplers may change after this call, so they're set just before the // descriptor set is used instead of here. - VkImageView view = vkTexture != nullptr ? (VkImageView)vkTexture->getRenderTargetHandle() : VK_NULL_HANDLE; + VkImageView view = vkTexture != nullptr ? (VkImageView)vkTexture->getHandle() : VK_NULL_HANDLE; if (view != imageInfo.imageView) { imageInfo.imageLayout = vkTexture != nullptr ? vkTexture->getImageLayout() : VK_IMAGE_LAYOUT_UNDEFINED; diff --git a/src/modules/graphics/vulkan/Texture.cpp b/src/modules/graphics/vulkan/Texture.cpp index 2f64905c0..2555f3636 100644 --- a/src/modules/graphics/vulkan/Texture.cpp +++ b/src/modules/graphics/vulkan/Texture.cpp @@ -24,6 +24,7 @@ #include "Buffer.h" #include +#include namespace love { @@ -132,7 +133,7 @@ bool Texture::loadVolatile() imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = usageFlags; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageInfo.samples = msaaSamples; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; VkImageFormatListCreateInfo viewFormatsInfo{}; viewFormatsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO; @@ -153,26 +154,54 @@ bool Texture::loadVolatile() imageAllocationCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; - if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) - throw love::Exception("failed to create image"); + if ((msaaSamples & VK_SAMPLE_COUNT_1_BIT) != 0 || readable) + { + if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &imageData.image, &imageData.allocation, nullptr) != VK_SUCCESS) + throw love::Exception("failed to create image"); + } + + if ((msaaSamples & VK_SAMPLE_COUNT_1_BIT) == 0) + { + imageInfo.samples = msaaSamples; + if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &msaaImageData.image, &msaaImageData.allocation, nullptr) != VK_SUCCESS) + throw love::Exception("failed to create MSAA image"); + } auto commandBuffer = vgfx->getCommandBufferForDataTransfer(); if (computeWrite) - imageLayout = VK_IMAGE_LAYOUT_GENERAL; + imageData.layout = VK_IMAGE_LAYOUT_GENERAL; else if (readable) - imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageData.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; else if (renderTarget && isPixelFormatDepthStencil(format)) - imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + imageData.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; else if (renderTarget) - imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + imageData.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; else // TODO: is there a better layout for this situation? - imageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imageData.layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, - VK_IMAGE_LAYOUT_UNDEFINED, imageLayout, - 0, VK_REMAINING_MIP_LEVELS, - 0, VK_REMAINING_ARRAY_LAYERS); + if (imageData.image != VK_NULL_HANDLE) + { + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, + VK_IMAGE_LAYOUT_UNDEFINED, imageData.layout, + 0, VK_REMAINING_MIP_LEVELS, + 0, VK_REMAINING_ARRAY_LAYERS); + } + + if (msaaImageData.image != VK_NULL_HANDLE) + { + if (renderTarget && isPixelFormatDepthStencil(format)) + msaaImageData.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + else if (renderTarget) + msaaImageData.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + else // TODO: is there a better layout for this situation? + msaaImageData.layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + + Vulkan::cmdTransitionImageLayout(commandBuffer, msaaImageData.image, format, renderTarget, + VK_IMAGE_LAYOUT_UNDEFINED, msaaImageData.layout, + 0, VK_REMAINING_MIP_LEVELS, + 0, VK_REMAINING_ARRAY_LAYERS); + } bool hasdata = slices.get(0, 0) != nullptr; @@ -195,14 +224,20 @@ bool Texture::loadVolatile() } } else - clear(); + { + if (imageData.image != VK_NULL_HANDLE) + clear(imageData); + if (msaaImageData.image != VK_NULL_HANDLE) + clear(msaaImageData); + } } else { Texture *roottex = (Texture *) rootView.texture; - textureImage = roottex->textureImage; - textureImageAllocation = VK_NULL_HANDLE; - imageLayout = roottex->imageLayout; + imageData = roottex->imageData; + msaaImageData = roottex->msaaImageData; + imageData.allocation = VK_NULL_HANDLE; + msaaImageData.allocation = VK_NULL_HANDLE; msaaSamples = roottex->msaaSamples; } @@ -214,34 +249,8 @@ bool Texture::loadVolatile() if (renderTarget) { - renderTargetImageViews.resize(getMipmapCount()); - for (int mip = 0; mip < getMipmapCount(); mip++) - { - renderTargetImageViews.at(mip).resize(layerCount); - - for (int slice = 0; slice < layerCount; slice++) - { - VkImageViewCreateInfo viewInfo{}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.image = textureImage; - viewInfo.viewType = Vulkan::getImageViewType(getTextureType()); - if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_CUBE) - viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - viewInfo.format = vulkanFormat.internalFormat; - viewInfo.subresourceRange.aspectMask = imageAspect; - viewInfo.subresourceRange.baseMipLevel = mip + rootView.startMipmap; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = slice + rootView.startLayer; - viewInfo.subresourceRange.layerCount = 1; - viewInfo.components.r = vulkanFormat.swizzleR; - viewInfo.components.g = vulkanFormat.swizzleG; - viewInfo.components.b = vulkanFormat.swizzleB; - viewInfo.components.a = vulkanFormat.swizzleA; - - if (vkCreateImageView(device, &viewInfo, nullptr, &renderTargetImageViews.at(mip).at(slice)) != VK_SUCCESS) - throw love::Exception("could not create render target image view"); - } - } + createRenderTargetImageViews(imageData); + createRenderTargetImageViews(msaaImageData); } if (!debugName.empty()) @@ -253,15 +262,25 @@ bool Texture::loadVolatile() if (root) { nameInfo.objectType = VK_OBJECT_TYPE_IMAGE; - nameInfo.objectHandle = (uint64_t)textureImage; + nameInfo.objectHandle = (uint64_t)imageData.image; } else { nameInfo.objectType = VK_OBJECT_TYPE_IMAGE_VIEW; - nameInfo.objectHandle = (uint64_t)textureImageView; + nameInfo.objectHandle = (uint64_t)imageData.imageView; } nameInfo.pObjectName = debugName.c_str(); vkSetDebugUtilsObjectNameEXT(device, &nameInfo); + + if (msaaImageData.image != VK_NULL_HANDLE && root) + { + nameInfo.objectType = VK_OBJECT_TYPE_IMAGE; + nameInfo.objectHandle = (uint64_t)msaaImageData.image; + + std::string msaaName = debugName + " (MSAA buffer)"; + nameInfo.pObjectName = msaaName.c_str(); + vkSetDebugUtilsObjectNameEXT(device, &nameInfo); + } } } @@ -272,26 +291,29 @@ bool Texture::loadVolatile() void Texture::unloadVolatile() { - if (textureImage == VK_NULL_HANDLE) - return; + std::array datas = {&imageData, &msaaImageData}; - vgfx->queueCleanUp([ - device = device, - textureImageView = textureImageView, - allocator = allocator, - textureImage = textureImage, - textureImageAllocation = textureImageAllocation, - textureImageViews = std::move(renderTargetImageViews)] () { - vkDestroyImageView(device, textureImageView, nullptr); - if (textureImageAllocation) - vmaDestroyImage(allocator, textureImage, textureImageAllocation); - for (const auto &views : textureImageViews) - for (const auto &view : views) - vkDestroyImageView(device, view, nullptr); - }); + for (const VulkanImageData *data : datas) + { + if (data->image == VK_NULL_HANDLE) + continue; - textureImage = VK_NULL_HANDLE; - textureImageAllocation = VK_NULL_HANDLE; + vgfx->queueCleanUp([ + device = device, + allocator = allocator, + imageData = std::move(*data)] () { + if (imageData.imageView != VK_NULL_HANDLE) + vkDestroyImageView(device, imageData.imageView, nullptr); + if (imageData.allocation != VK_NULL_HANDLE) + vmaDestroyImage(allocator, imageData.image, imageData.allocation); + for (const auto &views : imageData.renderTargetImageViews) + for (const auto &view : views) + vkDestroyImageView(device, view, nullptr); + }); + } + + imageData = {}; + msaaImageData = {}; updateGraphicsMemorySize(false); } @@ -303,7 +325,7 @@ Texture::~Texture() ptrdiff_t Texture::getRenderTargetHandle() const { - return (ptrdiff_t)textureImageView; + return (ptrdiff_t)imageData.image; } ptrdiff_t Texture::getSamplerHandle() const @@ -313,7 +335,16 @@ ptrdiff_t Texture::getSamplerHandle() const VkImageView Texture::getRenderTargetView(int mip, int layer) { - return renderTargetImageViews.at(mip).at(layer); + if (imageData.renderTargetImageViews.empty()) + return imageData.imageView; + return imageData.renderTargetImageViews.at(mip).at(layer); +} + +VkImageView Texture::getMSAARenderTargetView(int mip, int layer) +{ + if (msaaImageData.renderTargetImageViews.empty()) + return msaaImageData.imageView; + return msaaImageData.renderTargetImageViews.at(mip).at(layer); } VkSampleCountFlagBits Texture::getMsaaSamples() const @@ -328,7 +359,7 @@ int Texture::getMSAA() const ptrdiff_t Texture::getHandle() const { - return (ptrdiff_t)textureImage; + return (ptrdiff_t)imageData.imageView; } void Texture::setSamplerState(const SamplerState &s) @@ -339,7 +370,12 @@ void Texture::setSamplerState(const SamplerState &s) VkImageLayout Texture::getImageLayout() const { - return imageLayout; + return imageData.layout; +} + +VkImageLayout Texture::getMSAAImageLayout() const +{ + return msaaImageData.layout; } void Texture::createTextureImageView() @@ -347,7 +383,7 @@ void Texture::createTextureImageView() auto vulkanFormat = Vulkan::getTextureFormat(format); VkImageViewCreateInfo viewInfo{}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.image = textureImage; + viewInfo.image = imageData.image; viewInfo.viewType = Vulkan::getImageViewType(getTextureType()); viewInfo.format = vulkanFormat.internalFormat; viewInfo.subresourceRange.aspectMask = imageAspect; @@ -364,11 +400,56 @@ void Texture::createTextureImageView() viewInfo.components.b = vulkanFormat.swizzleB; viewInfo.components.a = vulkanFormat.swizzleA; - if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) - throw love::Exception("could not create texture image view"); + imageData.imageView = VK_NULL_HANDLE; + if (imageData.image != VK_NULL_HANDLE && readable) + { + if (vkCreateImageView(device, &viewInfo, nullptr, &imageData.imageView) != VK_SUCCESS) + throw love::Exception("could not create texture image view"); + } } -void Texture::clear() +void Texture::createRenderTargetImageViews(VulkanImageData &data) +{ + if (data.image == VK_NULL_HANDLE || (getMipmapCount() == 1 && layerCount == 1 && data.imageView != VK_NULL_HANDLE)) + { + // Reuse the main image view in this situation, if it exists. + data.renderTargetImageViews.clear(); + return; + } + + auto vulkanFormat = Vulkan::getTextureFormat(format); + + data.renderTargetImageViews.resize(getMipmapCount()); + for (int mip = 0; mip < getMipmapCount(); mip++) + { + data.renderTargetImageViews.at(mip).resize(layerCount); + + for (int slice = 0; slice < layerCount; slice++) + { + VkImageViewCreateInfo viewInfo{}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = data.image; + viewInfo.viewType = Vulkan::getImageViewType(getTextureType()); + if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_CUBE) + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = vulkanFormat.internalFormat; + viewInfo.subresourceRange.aspectMask = imageAspect; + viewInfo.subresourceRange.baseMipLevel = mip + rootView.startMipmap; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = slice + rootView.startLayer; + viewInfo.subresourceRange.layerCount = 1; + viewInfo.components.r = vulkanFormat.swizzleR; + viewInfo.components.g = vulkanFormat.swizzleG; + viewInfo.components.b = vulkanFormat.swizzleB; + viewInfo.components.a = vulkanFormat.swizzleA; + + if (vkCreateImageView(device, &viewInfo, nullptr, &data.renderTargetImageViews.at(mip).at(slice)) != VK_SUCCESS) + throw love::Exception("could not create render target image view"); + } + } +} + +void Texture::clear(const VulkanImageData &data) { auto commandBuffer = vgfx->getCommandBufferForDataTransfer(); @@ -379,12 +460,12 @@ void Texture::clear() range.baseArrayLayer = 0; range.layerCount = VK_REMAINING_ARRAY_LAYERS; - VkImageLayout clearLayout = imageLayout == VK_IMAGE_LAYOUT_GENERAL ? imageLayout : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + VkImageLayout clearLayout = data.layout == VK_IMAGE_LAYOUT_GENERAL ? data.layout : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - if (clearLayout != imageLayout) + if (clearLayout != data.layout) { - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, - imageLayout, clearLayout, + Vulkan::cmdTransitionImageLayout(commandBuffer, data.image, format, renderTarget, + data.layout, clearLayout, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS); } @@ -393,18 +474,18 @@ void Texture::clear() VkClearDepthStencilValue depthStencilColor{}; depthStencilColor.depth = 0.0f; depthStencilColor.stencil = 0; - vkCmdClearDepthStencilImage(commandBuffer, textureImage, clearLayout, &depthStencilColor, 1, &range); + vkCmdClearDepthStencilImage(commandBuffer, data.image, clearLayout, &depthStencilColor, 1, &range); } else { auto clearColor = getClearColor(this, ColorD(0, 0, 0, 0)); - vkCmdClearColorImage(commandBuffer, textureImage, clearLayout, &clearColor, 1, &range); + vkCmdClearColorImage(commandBuffer, data.image, clearLayout, &clearColor, 1, &range); } - if (clearLayout != imageLayout) + if (clearLayout != data.layout) { - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, - clearLayout, imageLayout, + Vulkan::cmdTransitionImageLayout(commandBuffer, data.image, format, renderTarget, + clearLayout, data.layout, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS); } } @@ -450,15 +531,15 @@ void Texture::generateMipmapsInternal() { auto commandBuffer = vgfx->getCommandBufferForDataTransfer(); - if (imageLayout != VK_IMAGE_LAYOUT_GENERAL) - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, - imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, + imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, rootView.startMipmap, static_cast(getMipmapCount()), rootView.startLayer, static_cast(layerCount)); VkImageMemoryBarrier barrier{}; barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.image = textureImage; + barrier.image = imageData.image; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -477,7 +558,7 @@ void Texture::generateMipmapsInternal() barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - if (imageLayout != VK_IMAGE_LAYOUT_GENERAL) + if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, @@ -498,29 +579,29 @@ void Texture::generateMipmapsInternal() blit.dstSubresource.baseArrayLayer = rootView.startLayer; blit.dstSubresource.layerCount = static_cast(layerCount); - if (imageLayout != VK_IMAGE_LAYOUT_GENERAL) + if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) { vkCmdBlitImage(commandBuffer, - textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + imageData.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + imageData.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); } else { vkCmdBlitImage(commandBuffer, - textureImage, VK_IMAGE_LAYOUT_GENERAL, - textureImage, VK_IMAGE_LAYOUT_GENERAL, + imageData.image, VK_IMAGE_LAYOUT_GENERAL, + imageData.image, VK_IMAGE_LAYOUT_GENERAL, 1, &blit, VK_FILTER_LINEAR); } barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - barrier.newLayout = imageLayout; + barrier.newLayout = imageData.layout; barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - if (imageLayout != VK_IMAGE_LAYOUT_GENERAL) + if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, @@ -529,11 +610,11 @@ void Texture::generateMipmapsInternal() barrier.subresourceRange.baseMipLevel = rootView.startMipmap + mipLevels - 1; barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - barrier.newLayout = imageLayout; + barrier.newLayout = imageData.layout; barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - if (imageLayout != VK_IMAGE_LAYOUT_GENERAL) + if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, @@ -587,31 +668,31 @@ void Texture::uploadByteData(const void *data, size_t size, int level, int slice auto commandBuffer = vgfx->getCommandBufferForDataTransfer(); - if (imageLayout != VK_IMAGE_LAYOUT_GENERAL) + if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) { - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, - imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, + imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, level, 1, baseLayer, 1); vkCmdCopyBufferToImage( commandBuffer, stagingBuffer, - textureImage, + imageData.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion ); - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout, + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageData.layout, level, 1, baseLayer, 1); } else vkCmdCopyBufferToImage( commandBuffer, stagingBuffer, - textureImage, - imageLayout, + imageData.image, + imageData.layout, 1, ®ion ); @@ -641,16 +722,16 @@ void Texture::copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int region.imageExtent.width = static_cast(rect.w); region.imageExtent.height = static_cast(rect.h); - if (imageLayout != VK_IMAGE_LAYOUT_GENERAL) + if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) { - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1); + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1); - vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), imageData.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1); + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageData.layout, layers.mipLevel, 1, layers.baseArrayLayer, 1); } else - vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion); + vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), imageData.image, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion); } void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size) @@ -674,16 +755,16 @@ void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const region.imageExtent.height = static_cast(rect.h); region.imageExtent.depth = 1; - if (imageLayout != VK_IMAGE_LAYOUT_GENERAL) + if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL) { - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1); + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1); - vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, (VkBuffer) dest->getHandle(), 1, ®ion); + vkCmdCopyImageToBuffer(commandBuffer, imageData.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, (VkBuffer) dest->getHandle(), 1, ®ion); - Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1); + Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageData.layout, layers.mipLevel, 1, layers.baseArrayLayer, 1); } else - vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, ®ion); + vkCmdCopyImageToBuffer(commandBuffer, imageData.image, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, ®ion); // TODO: This could be combined with the cmdTransitionImageLayout barrier. ((Buffer *)dest)->postGPUWriteBarrier(commandBuffer); diff --git a/src/modules/graphics/vulkan/Texture.h b/src/modules/graphics/vulkan/Texture.h index e9dac8be7..155a848e1 100644 --- a/src/modules/graphics/vulkan/Texture.h +++ b/src/modules/graphics/vulkan/Texture.h @@ -51,6 +51,7 @@ public: void setSamplerState(const SamplerState &s) override; VkImageLayout getImageLayout() const; + VkImageLayout getMSAAImageLayout() const; void copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect) override; void copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size) override; @@ -59,6 +60,7 @@ public: ptrdiff_t getSamplerHandle() const override; VkImageView getRenderTargetView(int mip, int layer); + VkImageView getMSAARenderTargetView(int mip, int layer); VkSampleCountFlagBits getMsaaSamples() const; void uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r) override; @@ -71,18 +73,26 @@ public: static VkClearColorValue getClearColor(love::graphics::Texture *texture, const ColorD &color); private: + + struct VulkanImageData + { + VkImage image = VK_NULL_HANDLE; + VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; + VmaAllocation allocation = VK_NULL_HANDLE; + VkImageView imageView = VK_NULL_HANDLE; + std::vector> renderTargetImageViews; + }; + void createTextureImageView(); - void clear(); + void createRenderTargetImageViews(VulkanImageData &data); + void clear(const VulkanImageData &data); Graphics *vgfx = nullptr; VkDevice device = VK_NULL_HANDLE; VkImageAspectFlags imageAspect; VmaAllocator allocator = VK_NULL_HANDLE; - VkImage textureImage = VK_NULL_HANDLE; - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VmaAllocation textureImageAllocation = VK_NULL_HANDLE; - VkImageView textureImageView = VK_NULL_HANDLE; - std::vector> renderTargetImageViews; + VulkanImageData imageData; + VulkanImageData msaaImageData; VkSampler textureSampler = VK_NULL_HANDLE; Slices slices; int layerCount = 0; diff --git a/src/modules/graphics/vulkan/Vulkan.cpp b/src/modules/graphics/vulkan/Vulkan.cpp index c63723313..e104e09e6 100644 --- a/src/modules/graphics/vulkan/Vulkan.cpp +++ b/src/modules/graphics/vulkan/Vulkan.cpp @@ -822,22 +822,22 @@ VkIndexType Vulkan::getVulkanIndexBufferType(IndexDataType type) } } -void Vulkan::setImageLayoutTransitionOptions(bool previous, bool renderTarget, bool depthStencil, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags) +void Vulkan::addImageLayoutTransitionOptions(bool previous, bool renderTarget, bool depthStencil, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags) { switch (layout) { case VK_IMAGE_LAYOUT_UNDEFINED: - accessMask = 0; + accessMask |= 0; if (previous) - stageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + stageFlags |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; else - stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + 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_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; + 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_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; if (renderTarget) { if (depthStencil) @@ -853,28 +853,28 @@ void Vulkan::setImageLayoutTransitionOptions(bool previous, bool renderTarget, b } break; case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: - accessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - stageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + accessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + stageFlags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: - 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; + 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 | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + accessMask |= VK_ACCESS_SHADER_READ_BIT; + stageFlags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: - accessMask = VK_ACCESS_TRANSFER_READ_BIT; - stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT; + 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; + 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; + accessMask |= 0; + stageFlags |= VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; break; default: throw love::Exception("unimplemented image layout"); @@ -897,11 +897,11 @@ void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage ima const PixelFormatInfo &info = getPixelFormatInfo(format); - VkPipelineStageFlags sourceStage; - VkPipelineStageFlags destinationStage; + VkPipelineStageFlags sourceStage = 0; + VkPipelineStageFlags destinationStage = 0; - setImageLayoutTransitionOptions(true, renderTarget, info.depth || info.stencil, oldLayout, barrier.srcAccessMask, sourceStage); - setImageLayoutTransitionOptions(false, renderTarget, info.depth || info.stencil, newLayout, barrier.dstAccessMask, destinationStage); + addImageLayoutTransitionOptions(true, renderTarget, info.depth || info.stencil, oldLayout, barrier.srcAccessMask, sourceStage); + addImageLayoutTransitionOptions(false, renderTarget, info.depth || info.stencil, newLayout, barrier.dstAccessMask, destinationStage); if (info.color) barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT; diff --git a/src/modules/graphics/vulkan/Vulkan.h b/src/modules/graphics/vulkan/Vulkan.h index 8b182f61e..98676b434 100644 --- a/src/modules/graphics/vulkan/Vulkan.h +++ b/src/modules/graphics/vulkan/Vulkan.h @@ -80,8 +80,8 @@ public: static VkStencilOp getStencilOp(StencilAction); static VkIndexType getVulkanIndexBufferType(IndexDataType type); - static void setImageLayoutTransitionOptions( - bool previous, bool renderTarget, bool depthStencil, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags); + static void addImageLayoutTransitionOptions( + bool previous, bool renderTarget, bool depthStencil, VkImageLayout layout, VkAccessFlags& accessMask, VkPipelineStageFlags& stageFlags); static void cmdTransitionImageLayout( VkCommandBuffer, VkImage, PixelFormat format, bool renderTarget, VkImageLayout oldLayout, VkImageLayout newLayout,