diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index c0afe8b54..9a1fea77c 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -844,6 +844,9 @@ private: template T *UnwrapInfos(const T *infos, uint32_t count); + void PatchAttachment(VkFramebufferAttachmentImageInfo *att, VkFormat imgFormat, + VkSampleCountFlagBits samples); + VkIndirectPatchData FetchIndirectData(VkIndirectPatchType type, VkCommandBuffer commandBuffer, VkBuffer dataBuffer, VkDeviceSize dataOffset, uint32_t count, uint32_t stride = 0, VkBuffer counterBuffer = VK_NULL_HANDLE, diff --git a/renderdoc/driver/vulkan/vk_next_chains.cpp b/renderdoc/driver/vulkan/vk_next_chains.cpp index 3f68bf348..5c845591e 100644 --- a/renderdoc/driver/vulkan/vk_next_chains.cpp +++ b/renderdoc/driver/vulkan/vk_next_chains.cpp @@ -162,9 +162,6 @@ static void AppendModifiedChainedStruct(byte *&tempMem, VkStruct *outputStruct, COPY_STRUCT(VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, VkFenceCreateInfo); \ COPY_STRUCT(VK_STRUCTURE_TYPE_FILTER_CUBIC_IMAGE_VIEW_IMAGE_FORMAT_PROPERTIES_EXT, \ VkFilterCubicImageViewImageFormatPropertiesEXT); \ - COPY_STRUCT(VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO, \ - VkFramebufferAttachmentsCreateInfo) \ - COPY_STRUCT(VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO, VkFramebufferAttachmentImageInfo) \ COPY_STRUCT(VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, VkFormatProperties2); \ COPY_STRUCT(VK_STRUCTURE_TYPE_HDR_METADATA_EXT, VkHdrMetadataEXT) \ COPY_STRUCT(VK_STRUCTURE_TYPE_IMAGE_BLIT_2_KHR, VkImageBlit2KHR); \ @@ -897,6 +894,32 @@ size_t GetNextPatchSize(const void *pNext) memSize += info->attachmentCount * sizeof(VkImageView); break; } + // this struct doesn't really need to be unwrapped but we allocate space for it since it + // contains arrays that we will very commonly need to patch, to adjust image info/formats. + // this saves us needing to iterate it outside and allocate extra space + case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO: + { + memSize += sizeof(VkFramebufferAttachmentsCreateInfo); + + VkFramebufferAttachmentsCreateInfo *info = (VkFramebufferAttachmentsCreateInfo *)next; + memSize += info->attachmentImageInfoCount * sizeof(VkFramebufferAttachmentImageInfo); + + for(uint32_t i = 0; i < info->attachmentImageInfoCount; i++) + memSize += GetNextPatchSize(&info->pAttachmentImageInfos[i]); + + break; + } + case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO: + { + memSize += sizeof(VkFramebufferAttachmentImageInfo); + + // we add space for an extra VkFormat so we can push one onto the list + VkFramebufferAttachmentImageInfo *info = (VkFramebufferAttachmentImageInfo *)next; + if(info->viewFormatCount > 0) + memSize += (info->viewFormatCount + 1) * sizeof(VkFormat); + + break; + } case VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO: { memSize += sizeof(VkGraphicsPipelineCreateInfo); @@ -1624,6 +1647,56 @@ void UnwrapNextChain(CaptureState state, const char *structName, byte *&tempMem, break; } + // this struct doesn't really need to be unwrapped but we allocate space for it since it + // contains arrays that we will very commonly need to patch, to adjust image info/formats. + // this saves us needing to iterate it outside and allocate extra space + case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO: + { + const VkFramebufferAttachmentsCreateInfo *in = + (const VkFramebufferAttachmentsCreateInfo *)nextInput; + VkFramebufferAttachmentsCreateInfo *out = (VkFramebufferAttachmentsCreateInfo *)tempMem; + + // append immediately so tempMem is incremented + AppendModifiedChainedStruct(tempMem, out, nextChainTail); + + // allocate unwrapped array + VkFramebufferAttachmentImageInfo *outAtts = (VkFramebufferAttachmentImageInfo *)tempMem; + tempMem += sizeof(VkFramebufferAttachmentImageInfo) * in->attachmentImageInfoCount; + + *out = *in; + out->pAttachmentImageInfos = outAtts; + for(uint32_t i = 0; i < in->attachmentImageInfoCount; i++) + { + outAtts[i] = in->pAttachmentImageInfos[i]; + UnwrapNextChain(state, "VkFramebufferAttachmentImageInfo", tempMem, + (VkBaseInStructure *)&outAtts[i]); + } + + break; + } + case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO: + { + const VkFramebufferAttachmentImageInfo *in = + (const VkFramebufferAttachmentImageInfo *)nextInput; + VkFramebufferAttachmentImageInfo *out = (VkFramebufferAttachmentImageInfo *)tempMem; + + // append immediately so tempMem is incremented + AppendModifiedChainedStruct(tempMem, out, nextChainTail); + + *out = *in; + + // allocate extra array + if(in->viewFormatCount > 0) + { + VkFormat *outFormats = (VkFormat *)tempMem; + tempMem += sizeof(VkFormat) * (in->viewFormatCount + 1); + + out->pViewFormats = outFormats; + memcpy(outFormats, in->pViewFormats, sizeof(VkFormat) * in->viewFormatCount); + } + + break; + } case VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO: { const VkGraphicsPipelineCreateInfo *in = (const VkGraphicsPipelineCreateInfo *)nextInput; @@ -2192,6 +2265,14 @@ void CopyNextChainForPatching(const char *structName, byte *&tempMem, VkBaseInSt case VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO: CopyNextChainedStruct(sizeof(VkFramebufferCreateInfo), tempMem, nextInput, nextChainTail); break; + case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO: + CopyNextChainedStruct(sizeof(VkFramebufferAttachmentsCreateInfo), tempMem, nextInput, + nextChainTail); + break; + case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO: + CopyNextChainedStruct(sizeof(VkFramebufferAttachmentImageInfo), tempMem, nextInput, + nextChainTail); + break; case VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO: CopyNextChainedStruct(sizeof(VkGraphicsPipelineCreateInfo), tempMem, nextInput, nextChainTail); diff --git a/renderdoc/driver/vulkan/vk_resources.cpp b/renderdoc/driver/vulkan/vk_resources.cpp index 5fa3e5576..30f8ab3b2 100644 --- a/renderdoc/driver/vulkan/vk_resources.cpp +++ b/renderdoc/driver/vulkan/vk_resources.cpp @@ -3251,6 +3251,8 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo &ci) imageAttachments[i].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageAttachments[i].barrier.oldLayout = ci.pAttachments[i].initialLayout; imageAttachments[i].barrier.newLayout = ci.pAttachments[i].finalLayout; + imageAttachments[i].format = ci.pAttachments[i].format; + imageAttachments[i].samples = ci.pAttachments[i].samples; } // VK_KHR_multiview @@ -3383,6 +3385,8 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo2 &ci) imageAttachments[a].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageAttachments[a].barrier.oldLayout = ci.pAttachments[i].initialLayout; imageAttachments[a].barrier.newLayout = ci.pAttachments[i].finalLayout; + imageAttachments[i].format = ci.pAttachments[i].format; + imageAttachments[i].samples = ci.pAttachments[i].samples; indexRemapTable[i] = a; diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index 6aa5a81dc..fba59d303 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -1147,6 +1147,9 @@ struct AttachmentInfo { VkResourceRecord *record; + VkFormat format; + VkSampleCountFlagBits samples; + // the implicit barrier applied from initialLayout to finalLayout across a render pass // for render passes this is partial (doesn't contain the image pointer), the image // and subresource range are filled in when creating the framebuffer, which is what is diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index a04964695..dd77b7a56 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include "../vk_core.h" +#include "../vk_debug.h" static void PatchSeparateStencil(VkAttachmentDescription &att, const VkAttachmentReference *ref) { @@ -144,24 +145,6 @@ static void MakeSubpassLoadRP(RPCreateInfo &info, const RPCreateInfo *origInfo, } } -template <> -VkFramebufferCreateInfo WrappedVulkan::UnwrapInfo(const VkFramebufferCreateInfo *info) -{ - VkFramebufferCreateInfo ret = *info; - - if((ret.flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) == 0) - { - VkImageView *unwrapped = GetTempArray(info->attachmentCount); - for(uint32_t i = 0; i < info->attachmentCount; i++) - unwrapped[i] = Unwrap(info->pAttachments[i]); - ret.pAttachments = unwrapped; - } - - ret.renderPass = Unwrap(ret.renderPass); - - return ret; -} - // note, for threading reasons we ensure to release the wrappers before // releasing the underlying object. Otherwise after releasing the vulkan object // that same handle could be returned by create on another thread, and we @@ -653,6 +636,36 @@ VkResult WrappedVulkan::vkCreateSampler(VkDevice device, const VkSamplerCreateIn return ret; } +void WrappedVulkan::PatchAttachment(VkFramebufferAttachmentImageInfo *att, VkFormat imgFormat, + VkSampleCountFlagBits samples) +{ + // this matches the mutations we do to images, so see vkCreateImage + att->usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + att->usage |= VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + att->usage &= ~VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; + + if(IsYUVFormat(imgFormat)) + att->flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + + if(samples != VK_SAMPLE_COUNT_1_BIT) + { + att->usage |= VK_IMAGE_USAGE_SAMPLED_BIT; + att->flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + + if(!IsDepthOrStencilFormat(imgFormat)) + { + if(GetDebugManager() && GetShaderCache()->IsArray2MSSupported()) + att->usage |= VK_IMAGE_USAGE_STORAGE_BIT; + } + else + { + att->usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + } + + att->flags &= ~VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT; +} + template bool WrappedVulkan::Serialise_vkCreateFramebuffer(SerialiserType &ser, VkDevice device, const VkFramebufferCreateInfo *pCreateInfo, @@ -670,8 +683,25 @@ bool WrappedVulkan::Serialise_vkCreateFramebuffer(SerialiserType &ser, VkDevice { VkFramebuffer fb = VK_NULL_HANDLE; - VkFramebufferCreateInfo unwrapped = UnwrapInfo(&CreateInfo); - VkResult ret = ObjDisp(device)->CreateFramebuffer(Unwrap(device), &unwrapped, NULL, &fb); + byte *tempMem = GetTempMemory(GetNextPatchSize(&CreateInfo)); + VkFramebufferCreateInfo *unwrappedInfo = UnwrapStructAndChain(m_State, tempMem, &CreateInfo); + + const VulkanCreationInfo::RenderPass &rpinfo = + m_CreationInfo.m_RenderPass[GetResID(CreateInfo.renderPass)]; + + VkFramebufferAttachmentsCreateInfo *attachmentsInfo = + (VkFramebufferAttachmentsCreateInfo *)FindNextStruct( + unwrappedInfo, VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO); + + for(uint32_t a = 0; attachmentsInfo && a < attachmentsInfo->attachmentImageInfoCount; a++) + { + VkFramebufferAttachmentImageInfo *att = + (VkFramebufferAttachmentImageInfo *)&attachmentsInfo->pAttachmentImageInfos[a]; + + PatchAttachment(att, rpinfo.attachments[a].format, rpinfo.attachments[a].samples); + } + + VkResult ret = ObjDisp(device)->CreateFramebuffer(Unwrap(device), unwrappedInfo, NULL, &fb); if(ret != VK_SUCCESS) { @@ -701,17 +731,14 @@ bool WrappedVulkan::Serialise_vkCreateFramebuffer(SerialiserType &ser, VkDevice VulkanCreationInfo::Framebuffer fbinfo; fbinfo.Init(GetResourceManager(), m_CreationInfo, &CreateInfo); - const VulkanCreationInfo::RenderPass &rpinfo = - m_CreationInfo.m_RenderPass[GetResID(CreateInfo.renderPass)]; - fbinfo.loadFBs.resize(rpinfo.loadRPs.size()); // create a render pass for each subpass that maintains attachment layouts for(size_t s = 0; s < fbinfo.loadFBs.size(); s++) { - unwrapped.renderPass = Unwrap(rpinfo.loadRPs[s]); + unwrappedInfo->renderPass = Unwrap(rpinfo.loadRPs[s]); - ret = ObjDisp(device)->CreateFramebuffer(Unwrap(device), &unwrapped, NULL, + ret = ObjDisp(device)->CreateFramebuffer(Unwrap(device), unwrappedInfo, NULL, &fbinfo.loadFBs[s]); RDCASSERTEQUAL(ret, VK_SUCCESS); @@ -761,9 +788,34 @@ VkResult WrappedVulkan::vkCreateFramebuffer(VkDevice device, const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer) { - VkFramebufferCreateInfo unwrapped = UnwrapInfo(pCreateInfo); + byte *tempMem = GetTempMemory(GetNextPatchSize(pCreateInfo)); + VkFramebufferCreateInfo *unwrappedInfo = UnwrapStructAndChain(m_State, tempMem, pCreateInfo); + + VkFramebufferAttachmentsCreateInfo *attachmentsInfo = + (VkFramebufferAttachmentsCreateInfo *)FindNextStruct( + unwrappedInfo, VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO); + + AttachmentInfo *capAtts = NULL; + VulkanCreationInfo::RenderPass::Attachment *replayAtts = NULL; + if(IsCaptureMode(m_State)) + capAtts = GetRecord(pCreateInfo->renderPass)->renderPassInfo->imageAttachments; + else + replayAtts = m_CreationInfo.m_RenderPass[GetResID(pCreateInfo->renderPass)].attachments.data(); + + // this matches the mutations we do to images, so see vkCreateImage + for(uint32_t a = 0; attachmentsInfo && a < attachmentsInfo->attachmentImageInfoCount; a++) + { + VkFramebufferAttachmentImageInfo *att = + (VkFramebufferAttachmentImageInfo *)&attachmentsInfo->pAttachmentImageInfos[a]; + + if(IsCaptureMode(m_State)) + PatchAttachment(att, capAtts[a].format, capAtts[a].samples); + else + PatchAttachment(att, replayAtts[a].format, replayAtts[a].samples); + } + VkResult ret; - SERIALISE_TIME_CALL(ret = ObjDisp(device)->CreateFramebuffer(Unwrap(device), &unwrapped, + SERIALISE_TIME_CALL(ret = ObjDisp(device)->CreateFramebuffer(Unwrap(device), unwrappedInfo, pAllocator, pFramebuffer)); if(ret == VK_SUCCESS) @@ -795,6 +847,8 @@ VkResult WrappedVulkan::vkCreateFramebuffer(VkDevice device, for(uint32_t i = 0, a = 0; i < pCreateInfo->attachmentCount; i++, a++) { fbInfo->imageAttachments[a].barrier = rpInfo->imageAttachments[a].barrier; + fbInfo->imageAttachments[a].format = rpInfo->imageAttachments[a].format; + fbInfo->imageAttachments[a].samples = rpInfo->imageAttachments[a].samples; if((pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) == 0) { @@ -854,10 +908,10 @@ VkResult WrappedVulkan::vkCreateFramebuffer(VkDevice device, // create a render pass for each subpass that maintains attachment layouts for(size_t s = 0; s < fbinfo.loadFBs.size(); s++) { - unwrapped.renderPass = Unwrap(rpinfo.loadRPs[s]); + unwrappedInfo->renderPass = Unwrap(rpinfo.loadRPs[s]); - ret = - ObjDisp(device)->CreateFramebuffer(Unwrap(device), &unwrapped, NULL, &fbinfo.loadFBs[s]); + ret = ObjDisp(device)->CreateFramebuffer(Unwrap(device), unwrappedInfo, NULL, + &fbinfo.loadFBs[s]); RDCASSERTEQUAL(ret, VK_SUCCESS); ResourceId loadFBid = GetResourceManager()->WrapResource(Unwrap(device), fbinfo.loadFBs[s]); diff --git a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp index 6dfae7812..bba6b57c0 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp @@ -668,6 +668,18 @@ VkResult WrappedVulkan::vkCreateSwapchainKHR(VkDevice device, // make sure we can readback to get the screenshot, and render to it for the text overlay createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + // if the surface supports them, add usage bits we don't need to look more like normal images + // (which is important when patching imageless framebuffer usage) + VkSurfaceCapabilitiesKHR surfCap = {}; + ObjDisp(m_PhysicalDevice) + ->GetPhysicalDeviceSurfaceCapabilitiesKHR(Unwrap(m_PhysicalDevice), + Unwrap(createInfo.surface), &surfCap); + if(surfCap.supportedUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) + createInfo.imageUsage |= VK_IMAGE_USAGE_SAMPLED_BIT; + if(surfCap.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) + createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + createInfo.surface = Unwrap(createInfo.surface); createInfo.oldSwapchain = Unwrap(createInfo.oldSwapchain); diff --git a/util/test/demos/vk/vk_imageless_framebuffer.cpp b/util/test/demos/vk/vk_imageless_framebuffer.cpp index 9dad3f7a2..979d8adb2 100644 --- a/util/test/demos/vk/vk_imageless_framebuffer.cpp +++ b/util/test/demos/vk/vk_imageless_framebuffer.cpp @@ -174,6 +174,9 @@ void main() Present(); } + vkDeviceWaitIdle(device); + vkDestroyFramebuffer(device, fb, NULL); + return 0; } };