From 52fa006beb9dc6f640bab6af6e459fd76ba2d5b3 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 10 Jun 2025 13:53:25 +0100 Subject: [PATCH] When descriptor buffers are used, intercept swapchain images * This is the only way we can handle the potential for creating descriptors for swapchain images as there is no way otherwise to fetch and replay the opaque descriptor data on the image that would be needed. We add an extra interception step and let the application write to a 'real' image that we allocate, prevent it from going into the present layout, and on present do a copy & fixup. --- renderdoc/driver/vulkan/vk_common.cpp | 27 +- renderdoc/driver/vulkan/vk_common.h | 4 + renderdoc/driver/vulkan/vk_core.cpp | 17 +- renderdoc/driver/vulkan/vk_resources.h | 25 +- .../driver/vulkan/wrappers/vk_cmd_funcs.cpp | 10 + .../driver/vulkan/wrappers/vk_misc_funcs.cpp | 53 +- .../driver/vulkan/wrappers/vk_sync_funcs.cpp | 22 + .../driver/vulkan/wrappers/vk_wsi_funcs.cpp | 484 +++++++++++++++--- 8 files changed, 542 insertions(+), 100 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index 4d2a02246..8d7976e04 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -433,7 +433,7 @@ bool VkInitParams::IsSupportedVersion(uint64_t ver) if(ver == CurrentVersion) return true; - // 0x16 -> 0x17 - added indication of reserved descriptors + // 0x16 -> 0x17 - added indication of reserved descriptors and descriptor buffer support for swapchains if(ver == 0x16) return true; @@ -489,6 +489,13 @@ bool VkInitParams::IsSupportedVersion(uint64_t ver) return false; } +void SanitiseDescriptorBufferImageLayout(VkImageLayout &layout) +{ + // for descriptor buffers we intercept swapchain images so we have to remap present layouts to general + if(layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR || layout == VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) + layout = VK_IMAGE_LAYOUT_GENERAL; +} + void SanitiseReplayImageLayout(VkImageLayout &layout) { // we don't replay with present layouts since we don't create actual swapchains. So change any @@ -1060,6 +1067,24 @@ void OpaqueDataForSerialising::fill(VkDevice wrappedDevice, VkBuffer wrappedBuff RDCERR("Couldn't get opaque capture/replay data: %s", ToStr(opaqueQuery).c_str()); } +void OpaqueDataForSerialising::fillUnwrapped(VkDevice wrappedDevice, VkImage unwrappedImage, + VkPhysicalDeviceDescriptorBufferPropertiesEXT &props) +{ + VkImageCaptureDescriptorDataInfoEXT getInfo = { + VK_STRUCTURE_TYPE_IMAGE_CAPTURE_DESCRIPTOR_DATA_INFO_EXT, + NULL, + unwrappedImage, + }; + + sz = props.imageCaptureReplayDescriptorDataSize; + + VkResult opaqueQuery = + ObjDisp(wrappedDevice) + ->GetImageOpaqueCaptureDescriptorDataEXT(Unwrap(wrappedDevice), &getInfo, data); + if(opaqueQuery != VK_SUCCESS) + RDCERR("Couldn't get opaque capture/replay data: %s", ToStr(opaqueQuery).c_str()); +} + void OpaqueDataForSerialising::fill(VkDevice wrappedDevice, VkImage wrappedImage, VkPhysicalDeviceDescriptorBufferPropertiesEXT &props) { diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index e055f3fbc..af63a403b 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -123,6 +123,7 @@ rdcstr HumanDriverName(VkDriverId driverId); void SanitiseOldImageLayout(VkImageLayout &layout); void SanitiseNewImageLayout(VkImageLayout &layout); void SanitiseReplayImageLayout(VkImageLayout &layout); +void SanitiseDescriptorBufferImageLayout(VkImageLayout &layout); void CombineDepthStencilLayouts(rdcarray &barriers); @@ -946,6 +947,9 @@ struct OpaqueDataForSerialising : VkOpaqueCaptureDescriptorDataCreateInfoEXT void fill(VkDevice wrappedDevice, VkAccelerationStructureKHR wrappedAS, VkPhysicalDeviceDescriptorBufferPropertiesEXT &props); + void fillUnwrapped(VkDevice wrappedDevice, VkImage unwrappedImage, + VkPhysicalDeviceDescriptorBufferPropertiesEXT &props); + void addForSerialising(VkBaseInStructure *serialisedCreateInfo); byte data[FixedOpaqueDescriptorCaptureSize] = {}; diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 59c37ddd1..44e2fe14c 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -2672,7 +2672,7 @@ bool WrappedVulkan::EndFrameCapture(DeviceOwnedWindow devWnd) const SwapchainInfo &swapInfo = *swaprecord->swapInfo; - backbuffer = swapInfo.images[swapInfo.lastPresent.imageIndex].im; + backbuffer = swapInfo.images[swapInfo.lastPresent.imageIndex].userSwapImage; swapImageInfo = &swapInfo.imageInfo; swapQueueIndex = GetRecord(swapInfo.lastPresent.presentQueue)->queueFamilyIndex; swapLayout = @@ -2680,8 +2680,12 @@ bool WrappedVulkan::EndFrameCapture(DeviceOwnedWindow devWnd) // mark all images referenced as well for(size_t i = 0; i < swapInfo.images.size(); i++) - GetResourceManager()->MarkResourceFrameReferenced(GetResID(swapInfo.images[i].im), + GetResourceManager()->MarkResourceFrameReferenced(GetResID(swapInfo.images[i].userSwapImage), eFrameRef_Read); + + // we're using the fake image for descriptor buffers, which is in layout general not present + if(DescriptorBuffers()) + swapLayout = VK_IMAGE_LAYOUT_GENERAL; } else { @@ -2697,7 +2701,7 @@ bool WrappedVulkan::EndFrameCapture(DeviceOwnedWindow devWnd) const SwapchainInfo &swapInfo = *swaprecord->swapInfo; - backbuffer = swapInfo.images[swapInfo.lastPresent.imageIndex].im; + backbuffer = swapInfo.images[swapInfo.lastPresent.imageIndex].userSwapImage; swapImageInfo = &swapInfo.imageInfo; swapQueueIndex = GetRecord(swapInfo.lastPresent.presentQueue)->queueFamilyIndex; swapLayout = @@ -2705,8 +2709,11 @@ bool WrappedVulkan::EndFrameCapture(DeviceOwnedWindow devWnd) // mark all images referenced as well for(size_t i = 0; i < swapInfo.images.size(); i++) - GetResourceManager()->MarkResourceFrameReferenced(GetResID(swapInfo.images[i].im), - eFrameRef_Read); + GetResourceManager()->MarkResourceFrameReferenced( + GetResID(swapInfo.images[i].userSwapImage), eFrameRef_Read); + + if(DescriptorBuffers()) + swapLayout = VK_IMAGE_LAYOUT_GENERAL; } else if(VRBackbufferRecord) { diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index 16f2844fb..364aeba61 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -979,9 +979,9 @@ DECLARE_REFLECTION_STRUCT(ImageInfo); struct PresentInfo { - VkQueue presentQueue; + VkQueue presentQueue = VK_NULL_HANDLE; rdcarray waitSemaphores; - uint32_t imageIndex; + uint32_t imageIndex = 0; }; struct SwapchainInfo @@ -996,16 +996,25 @@ struct SwapchainInfo struct SwapImage { - VkImage im; + // the handle returned to the user, whether real or fake + VkImage userSwapImage = VK_NULL_HANDLE; + // if we made a fake swap image then this is the real unwrapped swapchain image + VkImage unwrappedRealSwapImage = VK_NULL_HANDLE; - VkImageView view; - VkFramebuffer fb; + uint64_t fakeImageMemoryOffset = 0; - VkFence fence; - VkCommandBuffer cmd; - VkSemaphore overlaydone; + VkImageView view = VK_NULL_HANDLE; + VkFramebuffer fb = VK_NULL_HANDLE; + + VkFence fence = VK_NULL_HANDLE; + VkCommandBuffer cmd = VK_NULL_HANDLE; + VkSemaphore overlaydone = VK_NULL_HANDLE; }; rdcarray images; + VkDeviceMemory imageMemory = VK_NULL_HANDLE; + uint32_t imageMemoryIndex = ~0U; + uint64_t imageMemorySize = 0; + PresentInfo lastPresent; }; diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index c898361b3..c7566740d 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -4182,6 +4182,9 @@ void WrappedVulkan::vkCmdPipelineBarrier( { im[i] = pImageMemoryBarriers[i]; im[i].image = Unwrap(im[i].image); + + SanitiseDescriptorBufferImageLayout(im[i].newLayout); + SanitiseDescriptorBufferImageLayout(im[i].newLayout); } SERIALISE_TIME_CALL(ObjDisp(commandBuffer) @@ -4416,6 +4419,13 @@ void WrappedVulkan::vkCmdPipelineBarrier2(VkCommandBuffer commandBuffer, byte *tempMem = GetTempMemory(GetNextPatchSize(pDependencyInfo)); VkDependencyInfo *unwrappedInfo = UnwrapStructAndChain(m_State, tempMem, pDependencyInfo); + for(uint32_t im = 0; im < unwrappedInfo->imageMemoryBarrierCount; im++) + { + VkImageMemoryBarrier2 &b = (VkImageMemoryBarrier2 &)unwrappedInfo->pImageMemoryBarriers[im]; + SanitiseDescriptorBufferImageLayout(b.newLayout); + SanitiseDescriptorBufferImageLayout(b.newLayout); + } + SERIALISE_TIME_CALL( ObjDisp(commandBuffer)->CmdPipelineBarrier2(Unwrap(commandBuffer), unwrappedInfo)); diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index de7410698..fab3fe7a0 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -348,15 +348,18 @@ void WrappedVulkan::vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR obj, for(size_t i = 0; i < info.images.size(); i++) { VkFramebuffer unwrappedFB = Unwrap(info.images[i].fb); + VkImage unwrappedImage = Unwrap(info.images[i].userSwapImage); VkImageView unwrappedView = Unwrap(info.images[i].view); VkSemaphore unwrappedSem = Unwrap(info.images[i].overlaydone); VkFence unwrappedFence = Unwrap(info.images[i].fence); GetResourceManager()->ReleaseWrappedResource(info.images[i].fb, true); - // note, image doesn't have to be destroyed, just untracked - GetResourceManager()->ReleaseWrappedResource(info.images[i].im, true); + GetResourceManager()->ReleaseWrappedResource(info.images[i].userSwapImage, true); GetResourceManager()->ReleaseWrappedResource(info.images[i].view, true); GetResourceManager()->ReleaseWrappedResource(info.images[i].overlaydone); GetResourceManager()->ReleaseWrappedResource(info.images[i].fence); + // if we had a separate real image, then the unwrappedImage is a fake one we created and must be destroyed + if(info.images[i].unwrappedRealSwapImage != VK_NULL_HANDLE) + ObjDisp(device)->DestroyImage(Unwrap(device), unwrappedImage, NULL); ObjDisp(device)->DestroyFramebuffer(Unwrap(device), unwrappedFB, NULL); ObjDisp(device)->DestroyImageView(Unwrap(device), unwrappedView, NULL); ObjDisp(device)->DestroySemaphore(Unwrap(device), unwrappedSem, NULL); @@ -365,6 +368,12 @@ void WrappedVulkan::vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR obj, // return the command buffers to the pool AddFreeCommandBuffer(info.images[i].cmd); } + + if(info.imageMemory != VK_NULL_HANDLE) + { + ObjDisp(device)->FreeMemory(Unwrap(device), Unwrap(info.imageMemory), NULL); + GetResourceManager()->ReleaseWrappedResource(info.imageMemory, true); + } } VkSwapchainKHR unwrappedObj = Unwrap(obj); @@ -1313,9 +1322,27 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass(SerialiserType &ser, VkDevice d VkResult WrappedVulkan::vkCreateRenderPass(VkDevice device, const VkRenderPassCreateInfo *pCreateInfo, const VkAllocationCallbacks *, VkRenderPass *pRenderPass) { + VkRenderPassCreateInfo patchedCreateInfo = *pCreateInfo; + + // descriptor buffers intercepts all swapchain images during capture so we change any + // reference to PRESENT layout into GENERAL since that's what is valid for our image + if(DescriptorBuffers()) + { + byte *tempMem = GetTempMemory(GetNextPatchSize(patchedCreateInfo.pNext)); + CopyNextChainForPatching("VkRenderPassCreateInfo", tempMem, + (VkBaseInStructure *)&patchedCreateInfo); + + for(uint32_t a = 0; a < patchedCreateInfo.attachmentCount; a++) + { + VkAttachmentDescription &att = (VkAttachmentDescription &)patchedCreateInfo.pAttachments[a]; + SanitiseDescriptorBufferImageLayout(att.initialLayout); + SanitiseDescriptorBufferImageLayout(att.finalLayout); + } + } + VkResult ret; - SERIALISE_TIME_CALL( - ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), pCreateInfo, NULL, pRenderPass)); + SERIALISE_TIME_CALL(ret = ObjDisp(device)->CreateRenderPass(Unwrap(device), &patchedCreateInfo, + NULL, pRenderPass)); if(ret == VK_SUCCESS) { @@ -1573,6 +1600,24 @@ VkResult WrappedVulkan::vkCreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, const VkAllocationCallbacks *, VkRenderPass *pRenderPass) { + VkRenderPassCreateInfo2 patchedCreateInfo = *pCreateInfo; + + // descriptor buffers intercepts all swapchain images during capture so we change any + // reference to PRESENT layout into GENERAL since that's what is valid for our image + if(DescriptorBuffers()) + { + byte *tempMem = GetTempMemory(GetNextPatchSize(patchedCreateInfo.pNext)); + CopyNextChainForPatching("VkRenderPassCreateInfo2", tempMem, + (VkBaseInStructure *)&patchedCreateInfo); + + for(uint32_t a = 0; a < patchedCreateInfo.attachmentCount; a++) + { + VkAttachmentDescription2 &att = (VkAttachmentDescription2 &)patchedCreateInfo.pAttachments[a]; + SanitiseDescriptorBufferImageLayout(att.initialLayout); + SanitiseDescriptorBufferImageLayout(att.finalLayout); + } + } + VkResult ret; SERIALISE_TIME_CALL( ret = ObjDisp(device)->CreateRenderPass2(Unwrap(device), pCreateInfo, NULL, pRenderPass)); diff --git a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp index c1a9d0e1d..c0ceb9e5c 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp @@ -923,6 +923,14 @@ void WrappedVulkan::vkCmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t even { im[i] = pImageMemoryBarriers[i]; im[i].image = Unwrap(im[i].image); + + // descriptor buffers intercepts all swapchain images during capture so we change any + // reference to PRESENT layout into GENERAL since that's what is valid for our image + if(DescriptorBuffers()) + { + SanitiseDescriptorBufferImageLayout(im[i].newLayout); + SanitiseDescriptorBufferImageLayout(im[i].newLayout); + } } SERIALISE_TIME_CALL(ObjDisp(commandBuffer) @@ -1182,6 +1190,13 @@ void WrappedVulkan::vkCmdSetEvent2(VkCommandBuffer commandBuffer, VkEvent event, byte *tempMem = GetTempMemory(GetNextPatchSize(pDependencyInfo)); VkDependencyInfo *unwrappedInfo = UnwrapStructAndChain(m_State, tempMem, pDependencyInfo); + for(uint32_t im = 0; im < unwrappedInfo->imageMemoryBarrierCount; im++) + { + VkImageMemoryBarrier2 &b = (VkImageMemoryBarrier2 &)unwrappedInfo->pImageMemoryBarriers[im]; + SanitiseDescriptorBufferImageLayout(b.newLayout); + SanitiseDescriptorBufferImageLayout(b.newLayout); + } + SERIALISE_TIME_CALL( ObjDisp(commandBuffer)->CmdSetEvent2(Unwrap(commandBuffer), Unwrap(event), unwrappedInfo)); @@ -1431,6 +1446,13 @@ void WrappedVulkan::vkCmdWaitEvents2(VkCommandBuffer commandBuffer, uint32_t eve { ev[i] = Unwrap(pEvents[i]); depInfo[i] = *UnwrapStructAndChain(m_State, tempMem, &pDependencyInfos[i]); + + for(uint32_t im = 0; im < depInfo[i].imageMemoryBarrierCount; im++) + { + VkImageMemoryBarrier2 &b = (VkImageMemoryBarrier2 &)depInfo[i].pImageMemoryBarriers[im]; + SanitiseDescriptorBufferImageLayout(b.newLayout); + SanitiseDescriptorBufferImageLayout(b.newLayout); + } } SERIALISE_TIME_CALL( diff --git a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp index 1295b817e..fc2a2e5d6 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp @@ -238,7 +238,8 @@ bool WrappedVulkan::Serialise_vkGetSwapchainImagesKHR(SerialiserType &ser, VkDev RDCASSERT(SwapchainImageIndex < swapInfo.images.size(), SwapchainImageIndex, swapInfo.images.size()); - GetResourceManager()->AddLiveResource(SwapchainImage, swapInfo.images[SwapchainImageIndex].im); + GetResourceManager()->AddLiveResource(SwapchainImage, + swapInfo.images[SwapchainImageIndex].userSwapImage); AddResource(SwapchainImage, ResourceType::SwapchainImage, "Swapchain Image"); DerivedResource(device, SwapchainImage); @@ -248,7 +249,7 @@ bool WrappedVulkan::Serialise_vkGetSwapchainImagesKHR(SerialiserType &ser, VkDev GetResourceDesc(Swapchain).derivedResources.push_back(SwapchainImage); GetResourceDesc(SwapchainImage).parentResources.push_back(Swapchain); - m_CreationInfo.m_Image[GetResID(swapInfo.images[SwapchainImageIndex].im)] = + m_CreationInfo.m_Image[GetResID(swapInfo.images[SwapchainImageIndex].userSwapImage)] = m_CreationInfo.m_Image[Swapchain]; } @@ -267,23 +268,45 @@ VkResult WrappedVulkan::vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR SERIALISE_TIME_CALL(ret = ObjDisp(device)->GetSwapchainImagesKHR( Unwrap(device), Unwrap(swapchain), pCount, pSwapchainImages)); + // during swapchain create this function is called once to initialise and wrap the images, + // thereafter we just return the already wrapped image handles. When descriptor buffers are + // enabled we are wrapping fake images we created to be able to descriptor-ise as well as + // initialising the real image handles if(pSwapchainImages && IsCaptureMode(m_State)) { uint32_t numImages = *pCount; VkResourceRecord *swapRecord = GetRecord(swapchain); + const bool fakeBackbuffers = AccelerationStructures() || DescriptorBuffers(); + for(uint32_t i = 0; i < numImages; i++) { - // these were all wrapped and serialised on swapchain create - we just have to - // return the wrapped image in that case - if(swapRecord->swapInfo->images[i].im != VK_NULL_HANDLE) + // for descriptor buffers or AS, if we already have the unwrapped real image then we've fully + // initialised so we can return the user swap image + if(fakeBackbuffers && swapRecord->swapInfo->images[i].unwrappedRealSwapImage != VK_NULL_HANDLE) { - pSwapchainImages[i] = swapRecord->swapInfo->images[i].im; + pSwapchainImages[i] = swapRecord->swapInfo->images[i].userSwapImage; + } + // if we're not using fake backbuffers and we've fetched & wrapped the user swap image we + // can also return that too. + else if(!fakeBackbuffers && swapRecord->swapInfo->images[i].userSwapImage != VK_NULL_HANDLE) + { + pSwapchainImages[i] = swapRecord->swapInfo->images[i].userSwapImage; } else { - ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), pSwapchainImages[i]); + VkImage imageToWrap = pSwapchainImages[i]; + + // if using fake backbuffers, we store the real swapchain image and wrap our fake + if(fakeBackbuffers) + { + swapRecord->swapInfo->images[i].unwrappedRealSwapImage = pSwapchainImages[i]; + imageToWrap = swapRecord->swapInfo->images[i].userSwapImage; + RDCASSERT(imageToWrap != VK_NULL_HANDLE); + } + + ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), imageToWrap); Chunk *chunk = NULL; @@ -291,12 +314,12 @@ VkResult WrappedVulkan::vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CHUNK(VulkanChunk::vkGetSwapchainImagesKHR); - Serialise_vkGetSwapchainImagesKHR(ser, device, swapchain, &i, &pSwapchainImages[i]); + Serialise_vkGetSwapchainImagesKHR(ser, device, swapchain, &i, &imageToWrap); chunk = scope.Get(); } - VkResourceRecord *record = GetResourceManager()->AddResourceRecord(pSwapchainImages[i]); + VkResourceRecord *record = GetResourceManager()->AddResourceRecord(imageToWrap); VkResourceRecord *swaprecord = GetRecord(swapchain); record->InternalResource = true; @@ -312,6 +335,8 @@ VkResult WrappedVulkan::vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR // swapchain, if the image is referenced the swapchain (and thus all the getimages) will be // included. swaprecord->AddChunk(chunk); + + pSwapchainImages[i] = imageToWrap; } } } @@ -351,6 +376,51 @@ bool WrappedVulkan::Serialise_vkCreateSwapchainKHR(SerialiserType &ser, VkDevice SERIALISE_ELEMENT(NumImages); + rdcarray opaqueDataFetch; + + rdcarray opaqueData; + rdcarray imageMemOffsets; + uint64_t opaqueMemAddress = 0; + + const bool fakeBackbuffers = AccelerationStructures() || DescriptorBuffers(); + + if(IsCaptureMode(m_State) && fakeBackbuffers) + { + if(DescriptorBuffers()) + { + opaqueDataFetch.resize(NumImages); + opaqueData.resize(NumImages); + } + imageMemOffsets.resize(NumImages); + + for(uint32_t i = 0; i < NumImages; i++) + { + if(DescriptorBuffers()) + { + opaqueDataFetch[i].fillUnwrapped(device, + GetRecord(*pSwapChain)->swapInfo->images[i].userSwapImage, + m_DescriptorBufferProperties); + opaqueData[i] = opaqueDataFetch[i]; + } + imageMemOffsets[i] = GetRecord(*pSwapChain)->swapInfo->images[i].fakeImageMemoryOffset; + } + + VkDeviceMemoryOpaqueCaptureAddressInfo getInfo = { + VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO, + NULL, + Unwrap(GetRecord(*pSwapChain)->swapInfo->imageMemory), + }; + + opaqueMemAddress = ObjDisp(device)->GetDeviceMemoryOpaqueCaptureAddress(Unwrap(device), &getInfo); + } + + if(ser.VersionAtLeast(0x17)) + { + SERIALISE_ELEMENT(opaqueData).Hidden(); + SERIALISE_ELEMENT(imageMemOffsets).Hidden(); + SERIALISE_ELEMENT(opaqueMemAddress).Hidden(); + } + SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading()) @@ -374,7 +444,7 @@ bool WrappedVulkan::Serialise_vkCreateSwapchainKHR(SerialiserType &ser, VkDevice if(CreateInfo.flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) imageFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; - const VkImageCreateInfo imInfo = { + VkImageCreateInfo imInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, imageFlags, @@ -400,12 +470,21 @@ bool WrappedVulkan::Serialise_vkCreateSwapchainKHR(SerialiserType &ser, VkDevice queueFamiles[q] = m_QueueRemapping[queueFamiles[q]][0].family; } + uint32_t memIndices = ~0U; + uint64_t memSize = 0; + + rdcarray ims; + ims.resize(NumImages); + for(uint32_t i = 0; i < NumImages; i++) { - VkDeviceMemory mem = VK_NULL_HANDLE; - VkImage im = VK_NULL_HANDLE; + if(DescriptorBuffers()) + { + RDCASSERT(i < opaqueData.size(), i, opaqueData.size()); + imInfo.pNext = &opaqueData[i]; + } - VkResult vkr = ObjDisp(device)->CreateImage(Unwrap(device), &imInfo, NULL, &im); + VkResult vkr = ObjDisp(device)->CreateImage(Unwrap(device), &imInfo, NULL, &ims[i]); CHECK_VKR(this, vkr); if(vkr != VK_SUCCESS) @@ -416,49 +495,80 @@ bool WrappedVulkan::Serialise_vkCreateSwapchainKHR(SerialiserType &ser, VkDevice return false; } - ResourceId liveId = GetResourceManager()->WrapResource(Unwrap(device), im); + ResourceId liveId = GetResourceManager()->WrapResource(Unwrap(device), ims[i]); VkMemoryRequirements mrq = {0}; + ObjDisp(device)->GetImageMemoryRequirements(Unwrap(device), Unwrap(ims[i]), &mrq); - ObjDisp(device)->GetImageMemoryRequirements(Unwrap(device), Unwrap(im), &mrq); + memSize = AlignUp(memSize, mrq.alignment); + if(i < imageMemOffsets.size()) + { + RDCASSERT(imageMemOffsets[i] == memSize); + } + else + { + RDCASSERT(imageMemOffsets.size() == i); + imageMemOffsets.push_back(memSize); + } + + memSize += mrq.size; + memIndices &= mrq.memoryTypeBits; + } + + VkDeviceMemory mem = VK_NULL_HANDLE; + ResourceId memid; + + for(uint32_t i = 0; i < NumImages; i++) + { VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, - mrq.size, - GetGPULocalMemoryIndex(mrq.memoryTypeBits), + memSize, + GetGPULocalMemoryIndex(memIndices), }; VkMemoryAllocateFlagsInfo memFlags = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO}; - if(DescriptorBuffers()) + VkMemoryOpaqueCaptureAddressAllocateInfo memoryDeviceAddress = { + VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO, + }; + if(fakeBackbuffers) { allocInfo.pNext = &memFlags; memFlags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT | VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT; + + memFlags.pNext = &memoryDeviceAddress; + memoryDeviceAddress.opaqueCaptureAddress = opaqueMemAddress; } - vkr = ObjDisp(device)->AllocateMemory(Unwrap(device), &allocInfo, NULL, &mem); - CHECK_VKR(this, vkr); - - if(vkr != VK_SUCCESS) + VkResult vkr; + if(mem == VK_NULL_HANDLE) { - SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, - "Failed allocating fake backbuffer texture memory, VkResult: %s", - ToStr(vkr).c_str()); - return false; + vkr = ObjDisp(device)->AllocateMemory(Unwrap(device), &allocInfo, NULL, &mem); + CHECK_VKR(this, vkr); + + if(vkr != VK_SUCCESS) + { + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, + "Failed allocating fake backbuffer texture memory, VkResult: %s", + ToStr(vkr).c_str()); + return false; + } + + memid = GetResourceManager()->WrapResource(Unwrap(device), mem); + // register as a live-only resource, so it is cleaned up properly + GetResourceManager()->AddLiveResource(memid, mem); } - ResourceId memid = GetResourceManager()->WrapResource(Unwrap(device), mem); - // register as a live-only resource, so it is cleaned up properly - GetResourceManager()->AddLiveResource(memid, mem); - - vkr = ObjDisp(device)->BindImageMemory(Unwrap(device), Unwrap(im), Unwrap(mem), 0); + vkr = ObjDisp(device)->BindImageMemory(Unwrap(device), Unwrap(ims[i]), Unwrap(mem), + imageMemOffsets[i]); CHECK_VKR(this, vkr); // image live ID will be assigned separately in Serialise_vkGetSwapChainInfoWSI // memory doesn't have a live ID - swapinfo.images[i].im = im; + swapinfo.images[i].userSwapImage = ims[i]; // fill out image info so we track resource state barriers // sneaky-cheeky use of the swapchain's ID here (it's not a live ID because @@ -478,11 +588,11 @@ bool WrappedVulkan::Serialise_vkCreateSwapchainKHR(SerialiserType &ser, VkDevice iminfo.cube = false; iminfo.samples = VK_SAMPLE_COUNT_1_BIT; - m_CreationInfo.m_Names[liveId] = StringFormat::Fmt("Presentable Image %u", i); + m_CreationInfo.m_Names[GetResID(ims[i])] = StringFormat::Fmt("Presentable Image %u", i); { - LockedImageStateRef state = - InsertImageState(im, liveId, ImageInfo(swapinfo.imageInfo), eFrameRef_Unknown); + LockedImageStateRef state = InsertImageState( + ims[i], GetResID(ims[i]), ImageInfo(swapinfo.imageInfo), eFrameRef_Unknown); state->isMemoryBound = true; } } @@ -501,17 +611,7 @@ void WrappedVulkan::WrapAndProcessCreatedSwapchain(VkDevice device, { Chunk *chunk = NULL; - { - CACHE_THREAD_SERIALISER(); - - SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCreateSwapchainKHR); - Serialise_vkCreateSwapchainKHR(ser, device, pCreateInfo, NULL, pSwapChain); - - chunk = scope.Get(); - } - VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pSwapChain); - record->AddChunk(chunk); record->swapInfo = new SwapchainInfo(); SwapchainInfo &swapInfo = *record->swapInfo; @@ -530,7 +630,123 @@ void WrappedVulkan::WrapAndProcessCreatedSwapchain(VkDevice device, swapInfo.shared = (pCreateInfo->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR || pCreateInfo->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR); - VkResult vkr = VK_SUCCESS; + uint32_t numSwapImages = 0; + VkResult vkr; + + vkr = ObjDisp(device)->GetSwapchainImagesKHR(Unwrap(device), Unwrap(*pSwapChain), + &numSwapImages, NULL); + CHECK_VKR(this, vkr); + + swapInfo.images.resize(numSwapImages); + + const bool fakeBackbuffers = AccelerationStructures() || DescriptorBuffers(); + + // create fake images and memory if we're using descriptor buffers so that the user's handles + // that views and descriptors are created from are replayable + if(fakeBackbuffers) + { + VkImageCreateFlags imageFlags = DefaultImageCreateFlags(); + + if(pCreateInfo->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) + imageFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + + VkImageCreateInfo imInfo = { + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + NULL, + imageFlags, + VK_IMAGE_TYPE_2D, + pCreateInfo->imageFormat, + {pCreateInfo->imageExtent.width, pCreateInfo->imageExtent.height, 1}, + 1, + pCreateInfo->imageArrayLayers, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_TILING_OPTIMAL, + // we add SAMPLED_BIT to help hint that views created should be capture/replayable (as + // this could be used for a descriptor) + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | pCreateInfo->imageUsage, + pCreateInfo->imageSharingMode, + pCreateInfo->queueFamilyIndexCount, + pCreateInfo->pQueueFamilyIndices, + VK_IMAGE_LAYOUT_UNDEFINED, + }; + + if(pCreateInfo->imageSharingMode == VK_SHARING_MODE_CONCURRENT) + { + uint32_t *queueFamiles = (uint32_t *)pCreateInfo->pQueueFamilyIndices; + for(uint32_t q = 0; q < pCreateInfo->queueFamilyIndexCount; q++) + queueFamiles[q] = m_QueueRemapping[queueFamiles[q]][0].family; + } + + uint64_t memOffs = 0; + uint32_t memIndices = ~0U; + for(uint32_t i = 0; i < numSwapImages; i++) + { + vkr = ObjDisp(device)->CreateImage(Unwrap(device), &imInfo, NULL, + &swapInfo.images[i].userSwapImage); + CHECK_VKR(this, vkr); + + if(vkr != VK_SUCCESS) + { + RDCERR("Failed creating fake backbuffer textures, VkResult: %s", ToStr(vkr).c_str()); + return; + } + + // we don't wrap the image, vkGetSwapchainImagesKHR below will do that + + VkMemoryRequirements mrq = {0}; + ObjDisp(device)->GetImageMemoryRequirements(Unwrap(device), + swapInfo.images[i].userSwapImage, &mrq); + + memOffs = AlignUp(memOffs, mrq.alignment); + swapInfo.images[i].fakeImageMemoryOffset = memOffs; + + memOffs += mrq.size; + memIndices &= mrq.memoryTypeBits; + } + + VkMemoryAllocateFlagsInfo memFlags = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, + NULL, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT | VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT, + }; + VkMemoryAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + &memFlags, + memOffs, + GetGPULocalMemoryIndex(memIndices), + }; + + vkr = ObjDisp(device)->AllocateMemory(Unwrap(device), &allocInfo, NULL, &swapInfo.imageMemory); + CHECK_VKR(this, vkr); + + if(vkr != VK_SUCCESS) + { + RDCERR("Failed allocating fake backbuffer texture memory, VkResult: %s", ToStr(vkr).c_str()); + return; + } + + GetResourceManager()->WrapResource(Unwrap(device), swapInfo.imageMemory); + GetResourceManager()->SetInternalResource(GetResID(swapInfo.imageMemory)); + + for(uint32_t i = 0; i < numSwapImages; i++) + { + vkr = ObjDisp(device)->BindImageMemory(Unwrap(device), swapInfo.images[i].userSwapImage, + Unwrap(swapInfo.imageMemory), + swapInfo.images[i].fakeImageMemoryOffset); + CHECK_VKR(this, vkr); + } + } + + { + CACHE_THREAD_SERIALISER(); + + SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCreateSwapchainKHR); + Serialise_vkCreateSwapchainKHR(ser, device, pCreateInfo, NULL, pSwapChain); + + chunk = scope.Get(); + } + record->AddChunk(chunk); const VkDevDispatchTable *vt = ObjDisp(device); @@ -579,26 +795,11 @@ void WrappedVulkan::WrapAndProcessCreatedSwapchain(VkDevice device, // serialise out the swap chain images { - uint32_t numSwapImages; - vkr = vt->GetSwapchainImagesKHR(Unwrap(device), Unwrap(*pSwapChain), &numSwapImages, NULL); - CHECK_VKR(this, vkr); - - swapInfo.lastPresent.imageIndex = 0; - swapInfo.lastPresent.presentQueue = VK_NULL_HANDLE; - swapInfo.lastPresent.waitSemaphores.clear(); - - swapInfo.images.resize(numSwapImages); - for(uint32_t i = 0; i < numSwapImages; i++) - { - swapInfo.images[i].im = VK_NULL_HANDLE; - swapInfo.images[i].view = VK_NULL_HANDLE; - swapInfo.images[i].fb = VK_NULL_HANDLE; - } - - VkImage *images = new VkImage[numSwapImages]; + rdcarray images; + images.resize(numSwapImages); // go through our own function so we assign these images IDs - vkr = vkGetSwapchainImagesKHR(device, *pSwapChain, &numSwapImages, images); + vkr = vkGetSwapchainImagesKHR(device, *pSwapChain, &numSwapImages, images.data()); CHECK_VKR(this, vkr); for(uint32_t i = 0; i < numSwapImages; i++) @@ -606,7 +807,7 @@ void WrappedVulkan::WrapAndProcessCreatedSwapchain(VkDevice device, SwapchainInfo::SwapImage &swapImInfo = swapInfo.images[i]; // memory doesn't exist for genuine WSI created images - swapImInfo.im = images[i]; + swapImInfo.userSwapImage = images[i]; ResourceId imid = GetResID(images[i]); @@ -649,12 +850,16 @@ void WrappedVulkan::WrapAndProcessCreatedSwapchain(VkDevice device, GetResourceManager()->SetInternalResource(GetResID(swapImInfo.overlaydone)); } + VkImage renderImage = Unwrap(images[i]); + if(swapImInfo.unwrappedRealSwapImage != VK_NULL_HANDLE) + renderImage = swapImInfo.unwrappedRealSwapImage; + { VkImageViewCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, DefaultImageViewCreateFlags(), - Unwrap(images[i]), + renderImage, VK_IMAGE_VIEW_TYPE_2D, pCreateInfo->imageFormat, {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, @@ -687,8 +892,6 @@ void WrappedVulkan::WrapAndProcessCreatedSwapchain(VkDevice device, GetResourceManager()->SetInternalResource(GetResID(swapImInfo.fb)); } } - - SAFE_DELETE_ARRAY(images); } } else @@ -756,7 +959,7 @@ bool WrappedVulkan::Serialise_vkQueuePresentKHR(SerialiserType &ser, VkQueue que const bool activeWindow = RenderDoc::Inst().IsActiveWindow(devWnd); if(activeWindow || PresentedImage == ResourceId()) - PresentedImage = GetResID(swapInfo.images[pPresentInfo->pImageIndices[i]].im); + PresentedImage = GetResID(swapInfo.images[pPresentInfo->pImageIndices[i]].userSwapImage); if(activeWindow) break; @@ -971,10 +1174,13 @@ void WrappedVulkan::HandlePresent(VkQueue queue, const VkPresentInfoKHR *pPresen { uint32_t overlay = RenderDoc::Inst().GetOverlayBits(); + const bool fakeBackbuffers = AccelerationStructures() || DescriptorBuffers(); + if(overlay & eRENDERDOC_Overlay_Enabled) { VkRenderPass rp = swapInfo.rp; - VkImage im = swapInfo.images[imgIndex].im; + VkImage unwrappedRealSwapImage = swapInfo.images[imgIndex].unwrappedRealSwapImage; + VkImage userSwapImage = swapInfo.images[imgIndex].userSwapImage; VkFramebuffer fb = swapInfo.images[imgIndex].fb; VkCommandBuffer cmd = swapInfo.images[imgIndex].cmd; VkFence imfence = swapInfo.images[imgIndex].fence; @@ -1010,29 +1216,55 @@ void WrappedVulkan::HandlePresent(VkQueue queue, const VkPresentInfoKHR *pPresen vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); CHECK_VKR(this, vkr); + VkImageLayout presentLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + if(swapInfo.shared) + presentLayout = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR; + VkImageMemoryBarrier bbBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, 0, 0, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + presentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, swapQueueIndex, m_QueueFamilyIdx, - Unwrap(im), + Unwrap(userSwapImage), {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, }; - if(swapInfo.shared) - bbBarrier.oldLayout = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR; - if(swapInfo.concurrent) bbBarrier.srcQueueFamilyIndex = bbBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - bbBarrier.srcAccessMask = VK_ACCESS_ALL_READ_BITS; - bbBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + bbBarrier.srcAccessMask = VK_ACCESS_ALL_READ_BITS | VK_ACCESS_ALL_WRITE_BITS; - DoPipelineBarrier(cmd, 1, &bbBarrier); + // if using descriptor buffers, we barrier both images - real and fake backbuffer to be ready + // to copy from fake to real + if(fakeBackbuffers) + { + bbBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + // we fully overwrite with a copy, so we can transition from undefined + bbBarrier.image = unwrappedRealSwapImage; + bbBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + bbBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + DoPipelineBarrier(cmd, 1, &bbBarrier); + + bbBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + bbBarrier.image = Unwrap(userSwapImage); + bbBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + bbBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + DoPipelineBarrier(cmd, 1, &bbBarrier); + } + else + { + bbBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + DoPipelineBarrier(cmd, 1, &bbBarrier); + } rdcarray waitStage; waitStage.fill(unwrappedWaitSems.size(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); @@ -1067,7 +1299,28 @@ void WrappedVulkan::HandlePresent(VkQueue queue, const VkPresentInfoKHR *pPresen vkr = vt->BeginCommandBuffer(Unwrap(extQCmd), &beginInfo); CHECK_VKR(this, vkr); - DoPipelineBarrier(extQCmd, 1, &bbBarrier); + // if using fake backbuffers, we barrier both images - real and fake backbuffer + if(fakeBackbuffers) + { + bbBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + bbBarrier.image = unwrappedRealSwapImage; + bbBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + bbBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + DoPipelineBarrier(extQCmd, 1, &bbBarrier); + + bbBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + bbBarrier.image = Unwrap(userSwapImage); + bbBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + bbBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + DoPipelineBarrier(extQCmd, 1, &bbBarrier); + } + else + { + DoPipelineBarrier(extQCmd, 1, &bbBarrier); + } ObjDisp(extQCmd)->EndCommandBuffer(Unwrap(extQCmd)); @@ -1107,6 +1360,30 @@ void WrappedVulkan::HandlePresent(VkQueue queue, const VkPresentInfoKHR *pPresen overlayText += StringFormat::Fmt("\nCapture failed: %s", ResultDetails(m_LastCaptureError).Message().c_str()); + if(fakeBackbuffers) + { + VkImageCopy region = {}; + region.dstSubresource.aspectMask = region.srcSubresource.aspectMask = + VK_IMAGE_ASPECT_COLOR_BIT; + region.dstSubresource.layerCount = region.srcSubresource.layerCount = 1; + region.extent = swapInfo.imageInfo.extent; + ObjDisp(cmd)->CmdCopyImage(Unwrap(cmd), Unwrap(userSwapImage), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, unwrappedRealSwapImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + VkImageMemoryBarrier tmpBarrier = bbBarrier; + // remove any queue family transfer + tmpBarrier.srcQueueFamilyIndex = tmpBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + + // now do what the barrier above would have done, put the real swapchain in color attachment ready to render to + tmpBarrier.image = unwrappedRealSwapImage; + tmpBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + tmpBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + tmpBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + tmpBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + DoPipelineBarrier(cmd, 1, &tmpBarrier); + } + if(!overlayText.empty()) { m_TextRenderer->BeginText(textstate); @@ -1118,10 +1395,31 @@ void WrappedVulkan::HandlePresent(VkQueue queue, const VkPresentInfoKHR *pPresen std::swap(bbBarrier.srcQueueFamilyIndex, bbBarrier.dstQueueFamilyIndex); std::swap(bbBarrier.oldLayout, bbBarrier.newLayout); - bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; bbBarrier.dstAccessMask = VK_ACCESS_ALL_READ_BITS; - DoPipelineBarrier(textstate.cmd, 1, &bbBarrier); + if(fakeBackbuffers) + { + bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + bbBarrier.image = unwrappedRealSwapImage; + bbBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + bbBarrier.newLayout = presentLayout; + + DoPipelineBarrier(textstate.cmd, 1, &bbBarrier); + + bbBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + bbBarrier.image = Unwrap(userSwapImage); + bbBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + bbBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + DoPipelineBarrier(textstate.cmd, 1, &bbBarrier); + } + else + { + bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + DoPipelineBarrier(textstate.cmd, 1, &bbBarrier); + } ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd)); @@ -1141,7 +1439,29 @@ void WrappedVulkan::HandlePresent(VkQueue queue, const VkPresentInfoKHR *pPresen vkr = vt->BeginCommandBuffer(Unwrap(extQCmd), &beginInfo); CHECK_VKR(this, vkr); - DoPipelineBarrier(extQCmd, 1, &bbBarrier); + if(fakeBackbuffers) + { + bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + bbBarrier.image = unwrappedRealSwapImage; + bbBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + bbBarrier.newLayout = presentLayout; + + DoPipelineBarrier(extQCmd, 1, &bbBarrier); + + bbBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + bbBarrier.image = Unwrap(userSwapImage); + bbBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + bbBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + DoPipelineBarrier(extQCmd, 1, &bbBarrier); + } + else + { + bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + DoPipelineBarrier(extQCmd, 1, &bbBarrier); + } ObjDisp(extQCmd)->EndCommandBuffer(Unwrap(extQCmd));