diff --git a/dependencies/Vulkan-Headers b/dependencies/Vulkan-Headers index 9b9fd871..01393c3d 160000 --- a/dependencies/Vulkan-Headers +++ b/dependencies/Vulkan-Headers @@ -1 +1 @@ -Subproject commit 9b9fd871b08110cd8f0b74e721b03213d9cc3081 +Subproject commit 01393c3df0e5285b54ee6527466513f9e614be94 diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.cpp index 8a999000..20c88b28 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.cpp @@ -126,7 +126,10 @@ void CachedFBOVk::InitDynamicRenderingData() continue; } else + { + m_vkColorAttachments[i].imageLayout = static_cast(buffer.texture->baseTexture)->GetDefaultLayout(); m_vkColorAttachments[i].imageView = cbView->m_textureImageView; + } } m_vkRenderingInfo.pColorAttachments = m_vkColorAttachments; @@ -167,6 +170,9 @@ void CachedFBOVk::InitDynamicRenderingData() // setup depth and stencil attachment if (depthStencilView) { + auto depthTexVk = static_cast(depthBuffer.texture->baseTexture); + m_vkDepthAttachment.imageLayout = depthTexVk->GetDefaultLayout(); + m_vkStencilAttachment.imageLayout = depthTexVk->GetDefaultLayout(); m_vkDepthAttachment.imageView = depthStencilView->m_textureImageView; m_vkDepthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; m_vkDepthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -189,41 +195,46 @@ void CachedFBOVk::InitDynamicRenderingData() m_vkRenderingInfo.layerCount = 1; } +static uint32 s_selfDependencyCheckIndex = 1; -uint32 s_currentCollisionCheckIndex = 1; - -bool CachedFBOVk::CheckForCollision(VkDescriptorSetInfo* vsDS, VkDescriptorSetInfo* gsDS, VkDescriptorSetInfo* psDS) const +CachedFBOVk::RendertargetSelfDependencyMask CachedFBOVk::CheckForSelfDependency(VkDescriptorSetInfo* vsDS, VkDescriptorSetInfo* gsDS, VkDescriptorSetInfo* psDS) const { - s_currentCollisionCheckIndex++; - const uint32 curColIndex = s_currentCollisionCheckIndex; - for (auto& itr : m_referencedTextures) + s_selfDependencyCheckIndex++; + const uint32 curColIndex = s_selfDependencyCheckIndex; + for (auto& colorAttachment : colorBuffer) { - LatteTextureVk* vkTex = (LatteTextureVk*)itr; - vkTex->m_collisionCheckIndex = curColIndex; - } - if (vsDS) - { - for (auto& itr : vsDS->list_fboCandidates) + if (colorAttachment.texture) { - if (itr->m_collisionCheckIndex == curColIndex) - return true; + LatteTextureVk* vkTex = static_cast(colorAttachment.texture->baseTexture); + vkTex->m_selfDependencyCheckIndex = curColIndex; + vkTex->m_selfDependencyCheckAspectMask = VK_IMAGE_ASPECT_COLOR_BIT; } } - if (gsDS) + if (depthBuffer.texture) { - for (auto& itr : gsDS->list_fboCandidates) - { - if (itr->m_collisionCheckIndex == curColIndex) - return true; - } + LatteTextureVk* vkTex = static_cast(depthBuffer.texture->baseTexture); + vkTex->m_selfDependencyCheckIndex = curColIndex; + vkTex->m_selfDependencyCheckAspectMask = depthBuffer.hasStencil ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT): VK_IMAGE_ASPECT_DEPTH_BIT; } - if (psDS) + + auto getSelfDependencyMask = [curColIndex](VkDescriptorSetInfo* ds) -> VkImageAspectFlags { - for (auto& itr : psDS->list_fboCandidates) + VkImageAspectFlags aspectMask = 0; + if (!ds) + return aspectMask; + for (auto& itr : ds->list_fboCandidates) { - if (itr->m_collisionCheckIndex == curColIndex) - return true; + if (itr->m_selfDependencyCheckIndex == curColIndex) + aspectMask |= itr->m_selfDependencyCheckAspectMask; } - } - return false; -} \ No newline at end of file + return aspectMask; + }; + + RendertargetSelfDependencyMask selfDepInfo{}; + VkImageAspectFlags vertexAspectFlags = getSelfDependencyMask(vsDS); + VkImageAspectFlags geometryAspectFlags = getSelfDependencyMask(gsDS); + VkImageAspectFlags pixelAspectFlags = getSelfDependencyMask(psDS); + selfDepInfo.aspectMaskFlags = vertexAspectFlags | geometryAspectFlags | pixelAspectFlags; + selfDepInfo.hasNonPixelSelfDependency = (vertexAspectFlags | geometryAspectFlags) != 0; + return selfDepInfo; +} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h index bf72996e..5a603579 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h @@ -51,8 +51,29 @@ public: [[nodiscard]] const VkExtent2D& GetExtend() const { return m_extend;} + struct RendertargetSelfDependencyMask + { + VkImageAspectFlags aspectMaskFlags{}; // aspect flags which are simultaneously sampled and written + bool hasNonPixelSelfDependency{false}; + + VkImageAspectFlags GetAspectMask() const + { + return aspectMaskFlags; + } + + bool HasSelfDependency() const + { + return GetAspectMask() != 0; + } + + bool HasVertexOrGeometrySelfDependency() const + { + return hasNonPixelSelfDependency; // vertex or geometry shader samples texture which is written to + } + }; + // checks if any of the sampled textures are output by the FBO - bool CheckForCollision(VkDescriptorSetInfo* vsDS, VkDescriptorSetInfo* gsDS, VkDescriptorSetInfo* psDS) const; + RendertargetSelfDependencyMask CheckForSelfDependency(VkDescriptorSetInfo* vsDS, VkDescriptorSetInfo* gsDS, VkDescriptorSetInfo* psDS) const; private: diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp index 88d3291d..c6a9b274 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp @@ -67,6 +67,12 @@ LatteTextureVk::LatteTextureVk(class VulkanRenderer* vkRenderer, Latte::E_DIM di imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; } + if (m_vkr->UseAttachmentFeedbackLoop() && (imageInfo.usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) != 0) + { + imageInfo.usage |= VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT; + m_defaultLayout = VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT; + } + if (dim == Latte::E_DIM::DIM_2D) imageInfo.imageType = VK_IMAGE_TYPE_2D; else if (dim == Latte::E_DIM::DIM_1D) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h index 612e2e70..b0d74879 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h @@ -20,6 +20,7 @@ public: VkFormat GetFormat() const { return vkObjTex->m_format; } VkImageAspectFlags GetImageAspect() const { return vkObjTex->m_imageAspect; } + VkImageLayout GetDefaultLayout() const { return m_defaultLayout; } VkImageLayout GetImageLayout(VkImageSubresource& subresource) { @@ -83,12 +84,14 @@ public: uint64 m_vkFlushIndex_read{}; uint64 m_vkFlushIndex_write{}; - uint32 m_collisionCheckIndex{}; // used to track if texture is being both sampled and output to during drawcall + uint32 m_selfDependencyCheckIndex{}; // used to track if texture is being both sampled and output to during drawcall + VkImageAspectFlags m_selfDependencyCheckAspectMask{}; private: class VulkanRenderer* m_vkr; VKRObjectTexture* vkObjTex{}; + VkImageLayout m_defaultLayout{ VK_IMAGE_LAYOUT_GENERAL }; // the targetted long term layout of the texture. Can be either VK_IMAGE_LAYOUT_GENERAL or VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT for potential rendertargets if supported std::vector m_layouts; uint32 m_layoutsMips; uint32 m_layoutsDepth; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp index bce23b59..81281ebc 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp @@ -122,15 +122,15 @@ void LatteTextureReadbackInfoVk::StartTransfer() const auto renderer = VulkanRenderer::GetInstance(); renderer->draw_endRenderPass(); - renderer->barrier_image(baseTexture, region.imageSubresource, VK_IMAGE_LAYOUT_GENERAL); + renderer->barrier_image(baseTexture, region.imageSubresource, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); renderer->barrier_sequentializeTransfer(); - vkCmdCopyImageToBuffer(renderer->getCurrentCommandBuffer(), baseTexture->GetImageObj()->m_image, VK_IMAGE_LAYOUT_GENERAL, m_buffer, 1, ®ion); + vkCmdCopyImageToBuffer(renderer->getCurrentCommandBuffer(), baseTexture->GetImageObj()->m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1, ®ion); renderer->barrier_sequentializeTransfer(); - renderer->barrier_image(baseTexture, region.imageSubresource, VK_IMAGE_LAYOUT_GENERAL); // make sure transfer is finished before image is modified + renderer->barrier_image(baseTexture, region.imageSubresource, baseTexture->GetDefaultLayout()); // make sure transfer is finished before image is modified renderer->barrier_bufferRange(m_buffer, m_buffer_offset, m_image_size); // make sure transfer is finished before result is read m_associatedCommandBufferId = renderer->GetCurrentCommandBufferId(); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h index c44a7b1c..f503c108 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h @@ -189,6 +189,9 @@ VKFUNC_DEVICE(vkCmdPipelineBarrier2KHR); VKFUNC_DEVICE(vkCmdBeginRenderingKHR); VKFUNC_DEVICE(vkCmdEndRenderingKHR); +// ext_attachment_feedback_loop_dynamic_state +VKFUNC_DEVICE(vkCmdSetAttachmentFeedbackLoopEnableEXT); + // khr_present_wait VKFUNC_DEVICE(vkWaitForPresentKHR); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp index ca166e69..2800b81e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp @@ -815,6 +815,10 @@ void PipelineCompiler::InitDynamicState(PipelineInfo* pipelineInfo, bool usesBle dynamicStates.emplace_back(VK_DYNAMIC_STATE_DEPTH_BIAS); pipelineInfo->usesDepthBias = true; } + if (VulkanRenderer::GetInstance()->UseAttachmentFeedbackLoop()) + { + dynamicStates.emplace_back(VK_DYNAMIC_STATE_ATTACHMENT_FEEDBACK_LOOP_ENABLE_EXT); + } dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicState.dynamicStateCount = dynamicStates.size(); @@ -1135,4 +1139,4 @@ void PipelineCompiler::CompileThreadPool_Stop() void PipelineCompiler::CompileThreadPool_QueueCompilation(PipelineCompiler* v) { s_pipelineCompileRequests.push(v); -} \ No newline at end of file +} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 136cc76b..bf9ae10c 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -50,7 +50,9 @@ const std::vector kOptionalDeviceExtensions = VK_KHR_PRESENT_WAIT_EXTENSION_NAME, VK_KHR_PRESENT_ID_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME, - VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME + VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME, + VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME, + VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME }; const std::vector kRequiredDeviceExtensions = @@ -271,6 +273,22 @@ void VulkanRenderer::GetDeviceFeatures() prevStruct = &pprf; } + VkPhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT attachmentFeedbackLoopDynamicStateFeature{}; + if (m_featureControl.deviceExtensions.attachment_feedback_loop_dynamic_state) + { + attachmentFeedbackLoopDynamicStateFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_FEATURES_EXT; + attachmentFeedbackLoopDynamicStateFeature.pNext = prevStruct; + prevStruct = &attachmentFeedbackLoopDynamicStateFeature; + } + + VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachmentFeedbackLoopLayoutFeature{}; + if (m_featureControl.deviceExtensions.attachment_feedback_loop_layout) + { + attachmentFeedbackLoopLayoutFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT; + attachmentFeedbackLoopLayoutFeature.pNext = prevStruct; + prevStruct = &attachmentFeedbackLoopLayoutFeature; + } + VkPhysicalDeviceFeatures2 physicalDeviceFeatures2{}; physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; physicalDeviceFeatures2.pNext = prevStruct; @@ -330,6 +348,12 @@ void VulkanRenderer::GetDeviceFeatures() if ( pprf.pipelineRobustness != VK_TRUE ) m_featureControl.deviceExtensions.pipeline_robustness = false; } + if (m_featureControl.deviceExtensions.attachment_feedback_loop_layout) + m_featureControl.deviceExtensions.attachment_feedback_loop_layout = attachmentFeedbackLoopLayoutFeature.attachmentFeedbackLoopLayout == VK_TRUE; + if (m_featureControl.deviceExtensions.attachment_feedback_loop_dynamic_state && m_featureControl.deviceExtensions.attachment_feedback_loop_layout) + m_featureControl.deviceExtensions.attachment_feedback_loop_dynamic_state = attachmentFeedbackLoopDynamicStateFeature.attachmentFeedbackLoopDynamicState == VK_TRUE; + if (!UseAttachmentFeedbackLoop()) + cemuLog_log(LogType::Force, "VK_EXT_attachment_feedback_loop_layout(_dynamic_state) not supported"); // get limits m_featureControl.limits.minUniformBufferOffsetAlignment = std::max(prop2.properties.limits.minUniformBufferOffsetAlignment, (VkDeviceSize)4); m_featureControl.limits.nonCoherentAtomSize = std::max(prop2.properties.limits.nonCoherentAtomSize, (VkDeviceSize)4); @@ -698,6 +722,21 @@ VulkanRenderer::VulkanRenderer() deviceExtensionFeatures = &pipelineRobustnessFeature; pipelineRobustnessFeature.pipelineRobustness = VK_TRUE; } + // enable attachment feedback loop layout + dynamic state if both are supported + VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachmentFeedbackLoopLayoutFeature{}; + VkPhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT attachmentFeedbackLoopDynamicStateFeature{}; + if (UseAttachmentFeedbackLoop()) + { + attachmentFeedbackLoopLayoutFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT; + attachmentFeedbackLoopLayoutFeature.pNext = deviceExtensionFeatures; + deviceExtensionFeatures = &attachmentFeedbackLoopLayoutFeature; + attachmentFeedbackLoopLayoutFeature.attachmentFeedbackLoopLayout = VK_TRUE; + + attachmentFeedbackLoopDynamicStateFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_FEATURES_EXT; + attachmentFeedbackLoopDynamicStateFeature.pNext = deviceExtensionFeatures; + deviceExtensionFeatures = &attachmentFeedbackLoopDynamicStateFeature; + attachmentFeedbackLoopDynamicStateFeature.attachmentFeedbackLoopDynamicState = VK_TRUE; + } std::vector used_extensions; VkDeviceCreateInfo createInfo = CreateDeviceCreateInfo(queueCreateInfos, deviceFeatures, deviceExtensionFeatures, used_extensions); @@ -1122,7 +1161,7 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad range.mipLevel = 0; range.baseArrayLayer = texViewVk->firstSlice; range.layerCount = 1; - barrier_image(baseImageTex, range, VK_IMAGE_LAYOUT_GENERAL); + barrier_image(baseImageTex, range, baseImageTex->GetDefaultLayout()); } format = VK_FORMAT_R8G8B8A8_UNORM; @@ -1178,6 +1217,10 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad } vkCmdCopyImageToBuffer(m_state.currentCommandBuffer, dumpImage, VK_IMAGE_LAYOUT_GENERAL, buffer, 1, ®ion); + if (dumpImage == baseImage) + { + barrier_image(baseImageTex, region.imageSubresource, baseImageTex->GetDefaultLayout()); + } SubmitCommandBuffer(); WaitCommandBufferFinished(GetCurrentCommandBufferId()); @@ -1282,6 +1325,11 @@ VkDeviceCreateInfo VulkanRenderer::CreateDeviceCreateInfo(const std::vector(texViewVk->baseTexture)->GetDefaultLayout(); imageInfo.imageView = texViewVk->GetViewRGBA()->m_textureImageView; imageInfo.sampler = texViewVk->GetDefaultTextureSampler(useLinearTexFilter); @@ -3395,7 +3445,7 @@ void VulkanRenderer::texture_clearSlice(LatteTexture* hostTexture, sint32 sliceI else { cemu_assert_debug(vkTexture->dim != Latte::E_DIM::DIM_3D); - ClearColorImage(vkTexture, sliceIndex, mipIndex, { 0,0,0,0 }, VK_IMAGE_LAYOUT_GENERAL); + ClearColorImage(vkTexture, sliceIndex, mipIndex, { 0,0,0,0 }, vkTexture->GetDefaultLayout()); } } @@ -3406,7 +3456,7 @@ void VulkanRenderer::texture_clearColorSlice(LatteTexture* hostTexture, sint32 s { cemu_assert_unimplemented(); } - ClearColorImage(vkTexture, sliceIndex, mipIndex, {r, g, b, a}, VK_IMAGE_LAYOUT_GENERAL); + ClearColorImage(vkTexture, sliceIndex, mipIndex, {r, g, b, a}, vkTexture->GetDefaultLayout()); } void VulkanRenderer::texture_clearDepthSlice(LatteTexture* hostTexture, uint32 sliceIndex, sint32 mipIndex, bool clearDepth, bool clearStencil, float depthValue, uint32 stencilValue) @@ -3447,7 +3497,7 @@ void VulkanRenderer::texture_clearDepthSlice(LatteTexture* hostTexture, uint32 s vkCmdClearDepthStencilImage(m_state.currentCommandBuffer, imageObj->m_image, VK_IMAGE_LAYOUT_GENERAL, &depthStencilValue, 1, &range); - barrier_image(vkTexture, subresourceRange, VK_IMAGE_LAYOUT_GENERAL); + barrier_image(vkTexture, subresourceRange, vkTexture->GetDefaultLayout()); } void VulkanRenderer::texture_loadSlice(LatteTexture* hostTexture, sint32 width, sint32 height, sint32 depth, void* pixelData, sint32 sliceIndex, sint32 mipIndex, uint32 compressedImageSize) @@ -3550,7 +3600,7 @@ void VulkanRenderer::texture_loadSlice(LatteTexture* hostTexture, sint32 width, vkCmdCopyBufferToImage(m_state.currentCommandBuffer, uploadResv.vkBuffer, vkImageObj->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageRegionCount, imageRegion); - barrier_image(vkTexture, barrierSubresourceRange, VK_IMAGE_LAYOUT_GENERAL); + barrier_image(vkTexture, barrierSubresourceRange, vkTexture->GetDefaultLayout()); } LatteTexture* VulkanRenderer::texture_createTextureEx(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, @@ -3628,13 +3678,11 @@ void VulkanRenderer::texture_copyImageSubData(LatteTexture* src, sint32 srcMip, sint32 mipWidth = std::max(dst->width >> dstMip, 1); sint32 mipHeight = std::max(dst->height >> dstMip, 1); - if (mipWidth < 4 || mipHeight < 4) { cemuLog_logDebug(LogType::Force, "vkCmdCopyImage - blocked copy for unsupported uncompressed->compressed copy with dst smaller than 4x4"); return; } - } // make sure all write operations to the src image have finished @@ -3645,7 +3693,8 @@ void VulkanRenderer::texture_copyImageSubData(LatteTexture* src, sint32 srcMip, vkCmdCopyImage(m_state.currentCommandBuffer, srcVkObj->m_image, VK_IMAGE_LAYOUT_GENERAL, dstVkObj->m_image, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion); // make sure the transfer is finished before the image is read or written - barrier_image(dstVk, region.dstSubresource, VK_IMAGE_LAYOUT_GENERAL); + barrier_image(srcVk, region.srcSubresource, srcVk->GetDefaultLayout()); + barrier_image(dstVk, region.dstSubresource, dstVk->GetDefaultLayout()); } LatteTextureReadbackInfo* VulkanRenderer::texture_createReadback(LatteTextureView* textureView) @@ -4064,6 +4113,10 @@ void VKRObjectSampler::DestroyCache() VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint32 colorAttachmentCount) { + VulkanRenderer* vkRenderer = VulkanRenderer::GetInstance(); + bool useAttachmentFeedbackLoop = vkRenderer->UseAttachmentFeedbackLoop(); + VkImageLayout attachmentLayout = useAttachmentFeedbackLoop ? VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT : VK_IMAGE_LAYOUT_GENERAL; + // generate helper hash for pipeline state uint64 stateHash = 0; for (int i = 0; i < Latte::GPU_LIMITS::NUM_COLOR_ATTACHMENTS; ++i) @@ -4097,7 +4150,7 @@ VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint3 m_colorAttachmentFormat[i] = attachmentInfo.colorAttachment[i].format; color_attachments_references[i].attachment = (uint32)attachments_descriptions.size(); - color_attachments_references[i].layout = VK_IMAGE_LAYOUT_GENERAL; + color_attachments_references[i].layout = attachmentLayout; VkAttachmentDescription entry{}; entry.format = attachmentInfo.colorAttachment[i].format; @@ -4106,8 +4159,8 @@ VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint3 entry.storeOp = VK_ATTACHMENT_STORE_OP_STORE; entry.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; entry.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - entry.initialLayout = VK_IMAGE_LAYOUT_GENERAL; - entry.finalLayout = VK_IMAGE_LAYOUT_GENERAL; + entry.initialLayout = attachmentLayout; + entry.finalLayout = attachmentLayout; attachments_descriptions.emplace_back(entry); numColorAttachments = i + 1; @@ -4124,7 +4177,7 @@ VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint3 { hasDepthStencilAttachment = true; depth_stencil_attachments_references.attachment = (uint32)attachments_descriptions.size(); - depth_stencil_attachments_references.layout = VK_IMAGE_LAYOUT_GENERAL; + depth_stencil_attachments_references.layout = attachmentLayout; m_depthAttachmentFormat = attachmentInfo.depthAttachment.format; VkAttachmentDescription entry{}; @@ -4142,8 +4195,8 @@ VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint3 entry.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; entry.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; } - entry.initialLayout = VK_IMAGE_LAYOUT_GENERAL; - entry.finalLayout = VK_IMAGE_LAYOUT_GENERAL; + entry.initialLayout = attachmentLayout; + entry.finalLayout = attachmentLayout; attachments_descriptions.emplace_back(entry); } @@ -4164,12 +4217,29 @@ VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint3 renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; - renderPassInfo.pDependencies = nullptr; - renderPassInfo.dependencyCount = 0; + VkSubpassDependency feedbackLoopDependency{}; + if (useAttachmentFeedbackLoop) + { + feedbackLoopDependency.srcSubpass = 0; + feedbackLoopDependency.dstSubpass = 0; + feedbackLoopDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + feedbackLoopDependency.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + feedbackLoopDependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + feedbackLoopDependency.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + feedbackLoopDependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT | VK_DEPENDENCY_FEEDBACK_LOOP_BIT_EXT; + renderPassInfo.pDependencies = &feedbackLoopDependency; + renderPassInfo.dependencyCount = 1; + } + else + { + renderPassInfo.pDependencies = nullptr; + renderPassInfo.dependencyCount = 0; + } // before Cemu 1.25.5 we used zero here, which means implicit synchronization. For 1.25.5 it was changed to 2 (using the subpass dependencies above) // Reverted this again to zero for Cemu 1.25.5b as the performance cost is just too high. Manual synchronization is preferred + // as of Cemu 2.7 we are now using VK_EXT_attachment_feedback_loop_layout with a matching renderpass dependency if supported. Otherwise we are falling back to the above - if (vkCreateRenderPass(VulkanRenderer::GetInstance()->GetLogicalDevice(), &renderPassInfo, nullptr, &m_renderPass) != VK_SUCCESS) + if (vkCreateRenderPass(vkRenderer->GetLogicalDevice(), &renderPassInfo, nullptr, &m_renderPass) != VK_SUCCESS) { cemuLog_log(LogType::Force, "Vulkan-Error: Failed to create render pass"); throw std::runtime_error("failed to create render pass!"); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index dc90eb09..4581e9e7 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -380,7 +380,7 @@ private: VkDescriptorSetInfo* activePixelDS{ nullptr }; VkDescriptorSetInfo* activeGeometryDS{ nullptr }; bool descriptorSetsChanged{ false }; - bool hasRenderSelfDependency{ false }; // set if current drawcall samples textures which are also output as a rendertarget + VkImageAspectFlags feedbackLoopImageAspect{0xFFFFFFFF}; // viewport and scissor box VkViewport currentViewport{}; VkRect2D currentScissorRect{}; @@ -414,6 +414,7 @@ private: activeIndexType = Renderer::INDEX_TYPE::NONE; activeIndexBufferIndex = std::numeric_limits::max(); activeIndexBufferOffset = std::numeric_limits::max(); + feedbackLoopImageAspect = 0xFFFFFFFF; } // invalidation / flushing @@ -465,6 +466,8 @@ private: bool present_wait = false; // VK_KHR_present_wait bool depth_clip_enable = false; // VK_EXT_depth_clip_enable bool pipeline_robustness = false; // VK_EXT_pipeline_robustness + bool attachment_feedback_loop_layout = false; // VK_EXT_attachment_feedback_loop_layout + bool attachment_feedback_loop_dynamic_state = false; // VK_EXT_attachment_feedback_loop_dynamic_state (this is forced to false if VK_EXT_attachment_feedback_loop_layout is not supported) }deviceExtensions; struct @@ -553,7 +556,7 @@ private: void draw_handleSpecialState5(); // draw synchronization helper - void sync_inputTexturesChanged(); + void sync_inputTexturesChanged(bool withinFeedbackLoopRenderPass = false); void sync_RenderPassLoadTextures(CachedFBOVk* fboVk); void sync_RenderPassStoreTextures(CachedFBOVk* fboVk); @@ -952,6 +955,7 @@ public: bool HasSPRIVRoundingModeRTE32() const { return m_featureControl.shaderFloatControls.shaderRoundingModeRTEFloat32; } bool IsDebugMarkersEnabled() const { return m_featureControl.usingDebugMarkerTool; } bool IsTracingToolEnabled() const { return m_featureControl.usingTracingTool; } + bool UseAttachmentFeedbackLoop() const { return m_featureControl.deviceExtensions.attachment_feedback_loop_dynamic_state; } private: diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index 01295a4d..717d14fe 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -711,14 +711,13 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo* continue; } - info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - VkSamplerCustomBorderColorCreateInfoEXT samplerCustomBorderColor{}; VkSamplerCreateInfo samplerInfo{}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - LatteTexture* baseTexture = textureView->baseTexture; + LatteTextureVk* baseTexture = static_cast(textureView->baseTexture); + info.imageLayout = baseTexture->GetDefaultLayout(); // get texture register word 0 uint32 word4 = LatteGPUState.contextRegister[texUnitRegIndex + 4]; @@ -731,7 +730,7 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo* textureView->AddDescriptorSetReference(dsInfo); if (!baseTexture->IsCompressedFormat()) - vectorAppendUnique(dsInfo->list_fboCandidates, (LatteTextureVk*)baseTexture); + vectorAppendUnique(dsInfo->list_fboCandidates, baseTexture); uint32 stageSamplerIndex = shader->textureUnitSamplerAssignment[relative_textureUnit]; if (stageSamplerIndex != LATTE_DECOMPILER_SAMPLER_NONE) @@ -1001,7 +1000,7 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo* return dsInfo; } -void VulkanRenderer::sync_inputTexturesChanged() +void VulkanRenderer::sync_inputTexturesChanged(bool withinFeedbackLoopRenderPass) { bool writeFlushRequired = false; @@ -1050,14 +1049,27 @@ void VulkanRenderer::sync_inputTexturesChanged() srcStage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; memoryBarrier.srcAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - // dst - dstStage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - memoryBarrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; + if (withinFeedbackLoopRenderPass) + { + // this renderpass has a pixel self dependency. Feedback loop extension allows barriers during renderpass, but they must be BY_REGION + dstStage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; - dstStage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - memoryBarrier.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; + dstStage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; + } + else + { + // dst + dstStage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; - vkCmdPipelineBarrier(m_state.currentCommandBuffer, srcStage, dstStage, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr); + dstStage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT; + } + + VkDependencyFlags dependencyFlags = withinFeedbackLoopRenderPass ? VK_DEPENDENCY_BY_REGION_BIT : 0; + vkCmdPipelineBarrier(m_state.currentCommandBuffer, srcStage, dstStage, dependencyFlags, 1, &memoryBarrier, 0, nullptr, 0, nullptr); performanceMonitor.vk.numDrawBarriersPerFrame.increment(); @@ -1180,22 +1192,32 @@ bool s_syncOnNextDraw = false; void VulkanRenderer::draw_setRenderPass() { CachedFBOVk* fboVk = m_state.activeFBO; + // note - pixel self dependency can be handled via feedback_loop extension + // vertex/geometry self dependency needs renderpass split + CachedFBOVk::RendertargetSelfDependencyMask renderSelfDependencyInfo{}; - // update self-dependency flag + // update self-dependency state if (m_state.descriptorSetsChanged || m_state.activeRenderpassFBO != fboVk) { - m_state.hasRenderSelfDependency = fboVk->CheckForCollision(m_state.activeVertexDS, m_state.activeGeometryDS, m_state.activePixelDS); + renderSelfDependencyInfo = fboVk->CheckForSelfDependency(m_state.activeVertexDS, m_state.activeGeometryDS, m_state.activePixelDS); } auto vkObjRenderPass = fboVk->GetRenderPassObj(); auto vkObjFramebuffer = fboVk->GetFramebufferObj(); - bool overridePassReuse = m_state.hasRenderSelfDependency && (GetConfig().vk_accurate_barriers || m_state.activePipelineInfo->neverSkipAccurateBarrier); + bool feedbackLoopHandlesSelfDependency = UseAttachmentFeedbackLoop() && renderSelfDependencyInfo.HasSelfDependency() && !renderSelfDependencyInfo.HasVertexOrGeometrySelfDependency(); + bool selfDependencyNeedsPassSplit = renderSelfDependencyInfo.HasSelfDependency() && !feedbackLoopHandlesSelfDependency; + bool overridePassReuse = selfDependencyNeedsPassSplit && (GetConfig().vk_accurate_barriers || m_state.activePipelineInfo->neverSkipAccurateBarrier); if (!overridePassReuse && m_state.activeRenderpassFBO == fboVk) { if (m_state.descriptorSetsChanged) - sync_inputTexturesChanged(); + sync_inputTexturesChanged(feedbackLoopHandlesSelfDependency); + if (UseAttachmentFeedbackLoop() && renderSelfDependencyInfo.GetAspectMask() != m_state.feedbackLoopImageAspect) + { + m_state.feedbackLoopImageAspect = renderSelfDependencyInfo.GetAspectMask(); + vkCmdSetAttachmentFeedbackLoopEnableEXT(m_state.currentCommandBuffer, renderSelfDependencyInfo.GetAspectMask()); + } return; } draw_endRenderPass(); @@ -1203,7 +1225,7 @@ void VulkanRenderer::draw_setRenderPass() sync_inputTexturesChanged(); // assume that FBO changed, update self-dependency state - m_state.hasRenderSelfDependency = fboVk->CheckForCollision(m_state.activeVertexDS, m_state.activeGeometryDS, m_state.activePixelDS); + renderSelfDependencyInfo = fboVk->CheckForSelfDependency(m_state.activeVertexDS, m_state.activeGeometryDS, m_state.activePixelDS); sync_RenderPassLoadTextures(fboVk); @@ -1228,6 +1250,11 @@ void VulkanRenderer::draw_setRenderPass() } m_state.activeRenderpassFBO = fboVk; + if (UseAttachmentFeedbackLoop() && renderSelfDependencyInfo.GetAspectMask() != m_state.feedbackLoopImageAspect) + { + m_state.feedbackLoopImageAspect = renderSelfDependencyInfo.GetAspectMask(); + vkCmdSetAttachmentFeedbackLoopEnableEXT(m_state.currentCommandBuffer, renderSelfDependencyInfo.GetAspectMask()); + } vkObjRenderPass->flagForCurrentCommandBuffer(); vkObjFramebuffer->flagForCurrentCommandBuffer(); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanSurfaceCopy.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanSurfaceCopy.cpp index e3e42012..d102a89e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanSurfaceCopy.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanSurfaceCopy.cpp @@ -557,7 +557,7 @@ VKRObjectDescriptorSet* VulkanRenderer::surfaceCopy_getOrCreateDescriptorSet(VkC descriptorImageInfo.sampler = vkObjImageView->m_textureDefaultSampler[0]; descriptorImageInfo.imageView = vkObjImageView->m_textureImageView; - descriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + descriptorImageInfo.imageLayout = state.sourceTexture->GetDefaultLayout(); VkWriteDescriptorSet write_descriptor{}; write_descriptor.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; @@ -673,8 +673,8 @@ void VulkanRenderer::surfaceCopy_viaDrawcall(LatteTextureVk* srcTextureVk, sint3 cemu_assert_debug(srcTextureVk->GetImageObj()->m_image != dstTextureVk->GetImageObj()->m_image); - barrier_image(srcTextureVk, srcImageSubresource, VK_IMAGE_LAYOUT_GENERAL); // wait for any modifying operations on source image to complete - barrier_image(dstTextureVk, dstImageSubresource, VK_IMAGE_LAYOUT_GENERAL); // wait for any operations on destination image to complete + barrier_image(srcTextureVk, srcImageSubresource, srcTextureVk->GetDefaultLayout()); // wait for any modifying operations on source image to complete + barrier_image(dstTextureVk, dstImageSubresource, dstTextureVk->GetDefaultLayout()); // wait for any operations on destination image to complete vkCmdBeginRenderPass(m_state.currentCommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); @@ -692,8 +692,8 @@ void VulkanRenderer::surfaceCopy_viaDrawcall(LatteTextureVk* srcTextureVk, sint3 vkCmdEndRenderPass(m_state.currentCommandBuffer); - barrier_image(srcTextureVk, srcImageSubresource, VK_IMAGE_LAYOUT_GENERAL); // wait for drawcall to complete before any other operations on the source image - barrier_image(dstTextureVk, dstImageSubresource, VK_IMAGE_LAYOUT_GENERAL); // wait for drawcall to complete before any other operations on the destination image + barrier_image(srcTextureVk, srcImageSubresource, srcTextureVk->GetDefaultLayout()); // wait for drawcall to complete before any other operations on the source image + barrier_image(dstTextureVk, dstImageSubresource, dstTextureVk->GetDefaultLayout()); // wait for drawcall to complete before any other operations on the destination image // restore viewport and scissor box vkCmdSetViewport(m_state.currentCommandBuffer, 0, 1, &m_state.currentViewport);