From e38b4e0fffca1f04fe8613c14925584bdcb8ae14 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 14 Oct 2022 12:06:09 +0100 Subject: [PATCH] Add support for VK_EXT_mutable_descriptor_type --- .../VulkanPipelineStateViewer.cpp | 86 ++-- renderdoc/api/replay/pipestate.inl | 68 +-- renderdoc/api/replay/renderdoc_tostr.inl | 1 + renderdoc/api/replay/replay_enums.h | 15 - renderdoc/api/replay/vk_pipestate.h | 26 +- renderdoc/driver/vulkan/extension_support.md | 4 +- renderdoc/driver/vulkan/vk_common.cpp | 137 +++--- renderdoc/driver/vulkan/vk_common.h | 266 ++++++++--- renderdoc/driver/vulkan/vk_core.cpp | 63 +-- renderdoc/driver/vulkan/vk_core.h | 2 +- renderdoc/driver/vulkan/vk_debug.cpp | 419 ++++++++++-------- renderdoc/driver/vulkan/vk_info.cpp | 333 +++++++++++++- renderdoc/driver/vulkan/vk_info.h | 26 +- renderdoc/driver/vulkan/vk_initstate.cpp | 278 +++--------- renderdoc/driver/vulkan/vk_next_chains.cpp | 6 +- renderdoc/driver/vulkan/vk_replay.cpp | 285 ++++++------ renderdoc/driver/vulkan/vk_serialise.cpp | 192 ++++++-- .../driver/vulkan/vk_shader_feedback.cpp | 4 +- renderdoc/driver/vulkan/vk_shaderdebug.cpp | 130 +++--- renderdoc/driver/vulkan/vk_state.cpp | 57 +-- .../driver/vulkan/wrappers/vk_cmd_funcs.cpp | 28 +- .../vulkan/wrappers/vk_descriptor_funcs.cpp | 107 ++--- .../vulkan/wrappers/vk_device_funcs.cpp | 7 + .../driver/vulkan/wrappers/vk_queue_funcs.cpp | 10 +- renderdoc/replay/renderdoc_serialise.inl | 4 +- 25 files changed, 1524 insertions(+), 1030 deletions(-) diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index bd63c15b6..916740999 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -1182,7 +1182,6 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, slotBinds = &pipe.descriptorSets[bindset].bindings[bind].binds; firstUsedBind = pipe.descriptorSets[bindset].bindings[bind].firstUsedIndex; lastUsedBind = pipe.descriptorSets[bindset].bindings[bind].lastUsedIndex; - bindType = pipe.descriptorSets[bindset].bindings[bind].type; stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags; } else @@ -1208,9 +1207,6 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, if(!usedSlot && !stageBitsIncluded) return; - if(bindType == BindType::ConstantBuffer) - return; - // TODO - check compatibility between bindType and shaderRes.resType ? // consider it filled if any array element is filled @@ -1218,6 +1214,7 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, for(int32_t idx = firstUsedBind; slotBinds != NULL && !filledSlot && idx <= lastUsedBind && idx < slotBinds->count(); idx++) { + bindType = (*slotBinds)[idx].type; filledSlot |= (*slotBinds)[idx].resourceResourceId != ResourceId(); if(bindType == BindType::Sampler || bindType == BindType::ImageSampler) filledSlot |= (*slotBinds)[idx].samplerResourceId != ResourceId(); @@ -1250,7 +1247,7 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, else arrayLength = (bindMap->arraySize == ~0U ? -1 : (int)bindMap->arraySize); - // for arrays, add a parent element that we add the real cbuffers below + // for arrays, add a parent element that we add the real resources below if(arrayLength > 1 || arrayLength < 0) { RDTreeWidgetItem *node = @@ -1267,10 +1264,6 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, if(!usedSlot) setInactiveRow(node); - resources->addTopLevelItem(node); - - // show the tree column - resources->showColumn(0); parentNode = node; } @@ -1283,12 +1276,17 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, { descriptorBind = &(*slotBinds)[idx]; + bindType = descriptorBind->type; + dynamicUsed &= descriptorBind->dynamicallyUsed; if(!showNode(dynamicUsed, filledSlot)) continue; } + if(bindType == BindType::ConstantBuffer) + continue; + if(arrayLength > 1) { if(shaderRes && !shaderRes->name.isEmpty()) @@ -1587,6 +1585,26 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, if(samplerNode) parentNode->addChild(samplerNode); } + + // if we were adding to an array node, add it now + if(parentNode != resources->invisibleRootItem()) + { + // as long as it has children - if it has no children then delete it and don't add anything + // this is possible e.g. if this node is a constant buffer - we couldn't tell that until we + // iterated all the descriptors to see if there were any non-constant buffers, given they may + // be mutably typed + if(parentNode->childCount() > 0) + { + // show the tree column + resources->showColumn(0); + // add the root item + resources->addTopLevelItem(parentNode); + } + else + { + delete parentNode; + } + } } } @@ -1616,7 +1634,6 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta } const rdcarray *slotBinds = NULL; - BindType bindType = BindType::ConstantBuffer; ShaderStageMask stageBits = ShaderStageMask::Unknown; uint32_t dynamicallyUsedCount = ~0U; int32_t firstUsedBind = 0; @@ -1631,7 +1648,6 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta slotBinds = &pipe.descriptorSets[bindset].bindings[bind].binds; firstUsedBind = pipe.descriptorSets[bindset].bindings[bind].firstUsedIndex; lastUsedBind = pipe.descriptorSets[bindset].bindings[bind].lastUsedIndex; - bindType = pipe.descriptorSets[bindset].bindings[bind].type; stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags; } @@ -1648,9 +1664,6 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta if(!usedSlot && !stageBitsIncluded) return; - if(bindType != BindType::ConstantBuffer) - return; - // consider it filled if any array element is filled (or it's push constants) bool filledSlot = cblock != NULL && !cblock->bufferBacked; for(int32_t idx = firstUsedBind; @@ -1702,10 +1715,6 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta if(!usedSlot) setInactiveRow(node); - ubos->addTopLevelItem(node); - - // show the tree column - ubos->showColumn(0); parentNode = node; } @@ -1718,6 +1727,9 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta if(!showNode(usedSlot && descriptorBind->dynamicallyUsed, filledSlot)) continue; + + if(descriptorBind->type != BindType::ConstantBuffer) + continue; } tag.arrayIdx = (uint32_t)idx; @@ -1818,6 +1830,26 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta parentNode->addChild(node); } + + // if we were adding to an array node, add it now + if(parentNode != ubos->invisibleRootItem()) + { + // as long as it has children - if it has no children then delete it and don't add anything + // this is possible e.g. if this node is not a constant buffer - we couldn't tell that until + // we iterated all the descriptors to see if there were any constant buffers, given they may + // be mutably typed + if(parentNode->childCount() > 0) + { + // show the tree column + ubos->showColumn(0); + // add the root item + ubos->addTopLevelItem(parentNode); + } + else + { + delete parentNode; + } + } } } @@ -3682,13 +3714,13 @@ void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const VKPipe:: viewParams = tr("Byte Range: %1").arg(formatByteRange(buf, &descriptorBind)); } - if(bind.type != BindType::Sampler) - rows.push_back({setname, slotname, name, ToQStr(bind.type), (qulonglong)w, h, d, arr, - format, viewParams}); + if(descriptorBind.type != BindType::Sampler) + rows.push_back({setname, slotname, name, ToQStr(descriptorBind.type), (qulonglong)w, h, d, + arr, format, viewParams}); - if(bind.type == BindType::ImageSampler || bind.type == BindType::Sampler) + if(descriptorBind.type == BindType::ImageSampler || descriptorBind.type == BindType::Sampler) { - if(bind.type == BindType::ImageSampler) + if(descriptorBind.type == BindType::ImageSampler) setname = slotname = QString(); QString samplerName = m_Ctx.GetResourceName(descriptorBind.samplerResourceId); @@ -3697,8 +3729,8 @@ void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const VKPipe:: samplerName = tr("Empty"); QVariantList sampDetails = makeSampler(QString(), QString(), descriptorBind); - rows.push_back({setname, slotname, samplerName, ToQStr(bind.type), QString(), QString(), - QString(), QString(), sampDetails[5], sampDetails[6]}); + rows.push_back({setname, slotname, samplerName, ToQStr(descriptorBind.type), QString(), + QString(), QString(), QString(), sampDetails[5], sampDetails[6]}); } } } @@ -3794,8 +3826,8 @@ void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const VKPipe:: viewParams = tr("Byte Range: %1").arg(formatByteRange(buf, &descriptorBind)); } - rows.push_back({setname, slotname, name, ToQStr(bind.type), (qulonglong)w, h, d, arr, - format, viewParams}); + rows.push_back({setname, slotname, name, ToQStr(descriptorBind.type), (qulonglong)w, h, d, + arr, format, viewParams}); } } diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index bdd156dc5..7424278f5 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -1243,20 +1243,23 @@ rdcarray PipeState::GetSamplers(ShaderStage stage) const for(int slot = 0; slot < descset.bindings.count(); slot++) { const VKPipe::DescriptorBinding &bind = descset.bindings[slot]; - if((bind.type == BindType::Sampler || bind.type == BindType::ImageSampler) && - (bind.stageFlags & mask) == mask) + if((bind.stageFlags & mask) == mask) { ret.push_back(BoundResourceArray()); ret.back().bindPoint = Bindpoint(set, slot); rdcarray &val = ret.back().resources; - val.resize(bind.descriptorCount); - - ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount; for(uint32_t i = 0; i < bind.descriptorCount; i++) { - val[i].resourceId = bind.binds[i].samplerResourceId; + if(bind.binds[i].type == BindType::Sampler || + bind.binds[i].type == BindType::ImageSampler) + { + val.push_back(BoundResource(bind.binds[i].samplerResourceId)); + + if(bind.binds[i].dynamicallyUsed) + ret.back().dynamicallyUsedCount++; + } } } } @@ -1399,9 +1402,7 @@ rdcarray PipeState::GetReadOnlyResources(ShaderStage stage, for(int slot = 0; slot < descset.bindings.count(); slot++) { const VKPipe::DescriptorBinding &bind = descset.bindings[slot]; - if((bind.type == BindType::ImageSampler || bind.type == BindType::InputAttachment || - bind.type == BindType::ReadOnlyImage || bind.type == BindType::ReadOnlyTBuffer) && - (bind.stageFlags & mask) == mask) + if((bind.stageFlags & mask) == mask) { ret.push_back(BoundResourceArray()); ret.back().bindPoint = Bindpoint(set, slot); @@ -1420,17 +1421,26 @@ rdcarray PipeState::GetReadOnlyResources(ShaderStage stage, val.reserve(count); ret.back().firstIndex = (int32_t)firstIdx; - ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount; + ret.back().dynamicallyUsedCount = 0; BoundResource res; for(uint32_t i = firstIdx; i < firstIdx + count; i++) { - res.resourceId = bind.binds[i].resourceResourceId; - res.dynamicallyUsed = bind.binds[i].dynamicallyUsed; - res.firstMip = (int)bind.binds[i].firstMip; - res.firstSlice = (int)bind.binds[i].firstSlice; - res.typeCast = bind.binds[i].viewFormat.compType; - val.push_back(res); + if(bind.binds[i].type == BindType::ImageSampler || + bind.binds[i].type == BindType::InputAttachment || + bind.binds[i].type == BindType::ReadOnlyImage || + bind.binds[i].type == BindType::ReadOnlyTBuffer) + { + res.resourceId = bind.binds[i].resourceResourceId; + res.dynamicallyUsed = bind.binds[i].dynamicallyUsed; + res.firstMip = (int)bind.binds[i].firstMip; + res.firstSlice = (int)bind.binds[i].firstSlice; + res.typeCast = bind.binds[i].viewFormat.compType; + val.push_back(res); + + if(bind.binds[i].dynamicallyUsed) + ret.back().dynamicallyUsedCount++; + } } } } @@ -1617,9 +1627,7 @@ rdcarray PipeState::GetReadWriteResources(ShaderStage stage, for(int slot = 0; slot < descset.bindings.count(); slot++) { const VKPipe::DescriptorBinding &bind = descset.bindings[slot]; - if((bind.type == BindType::ReadWriteBuffer || bind.type == BindType::ReadWriteImage || - bind.type == BindType::ReadWriteTBuffer) && - (bind.stageFlags & mask) == mask) + if((bind.stageFlags & mask) == mask) { ret.push_back(BoundResourceArray()); ret.back().bindPoint = Bindpoint(set, slot); @@ -1638,17 +1646,25 @@ rdcarray PipeState::GetReadWriteResources(ShaderStage stage, val.reserve(count); ret.back().firstIndex = (int32_t)firstIdx; - ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount; + ret.back().dynamicallyUsedCount = 0; BoundResource res; for(uint32_t i = firstIdx; i < firstIdx + count; i++) { - res.resourceId = bind.binds[i].resourceResourceId; - res.dynamicallyUsed = bind.binds[i].dynamicallyUsed; - res.firstMip = (int)bind.binds[i].firstMip; - res.firstSlice = (int)bind.binds[i].firstSlice; - res.typeCast = bind.binds[i].viewFormat.compType; - val.push_back(res); + if(bind.binds[i].type == BindType::ReadWriteBuffer || + bind.binds[i].type == BindType::ReadWriteImage || + bind.binds[i].type == BindType::ReadWriteTBuffer) + { + res.resourceId = bind.binds[i].resourceResourceId; + res.dynamicallyUsed = bind.binds[i].dynamicallyUsed; + res.firstMip = (int)bind.binds[i].firstMip; + res.firstSlice = (int)bind.binds[i].firstSlice; + res.typeCast = bind.binds[i].viewFormat.compType; + val.push_back(res); + + if(bind.binds[i].dynamicallyUsed) + ret.back().dynamicallyUsedCount++; + } } } } diff --git a/renderdoc/api/replay/renderdoc_tostr.inl b/renderdoc/api/replay/renderdoc_tostr.inl index bc75f8c6b..478408844 100644 --- a/renderdoc/api/replay/renderdoc_tostr.inl +++ b/renderdoc/api/replay/renderdoc_tostr.inl @@ -699,6 +699,7 @@ rdcstr DoStringise(const BindType &el) { BEGIN_ENUM_STRINGISE(BindType) { + STRINGISE_ENUM_CLASS_NAMED(Unknown, "Unknown"); STRINGISE_ENUM_CLASS_NAMED(ConstantBuffer, "Constants"); STRINGISE_ENUM_CLASS_NAMED(Sampler, "Sampler"); STRINGISE_ENUM_CLASS_NAMED(ImageSampler, "Image&Sampler"); diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index d8991db31..25b526bcd 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -4680,27 +4680,12 @@ DOCUMENT(R"(INTERNAL: A set of flags giving details of the current status of And .. data:: RootAccess The device being targeted has root access. - -.. data:: MissingTools - - When patching, some necessary tools were not found. - -.. data:: ManifestPatchFailure - - When patching, modifying the manifest file to include the debuggable flag failed. - -.. data:: RepackagingAPKFailure - - When patching, repackaging, signing and installing the new package failed. )"); enum class AndroidFlags : uint32_t { NoFlags = 0x0, Debuggable = 0x1, RootAccess = 0x2, - MissingTools = 0x1000, - ManifestPatchFailure = 0x2000, - RepackagingAPKFailure = 0x4000, }; BITMASK_OPERATORS(AndroidFlags); diff --git a/renderdoc/api/replay/vk_pipestate.h b/renderdoc/api/replay/vk_pipestate.h index 4796c1230..6fd8eeeb2 100644 --- a/renderdoc/api/replay/vk_pipestate.h +++ b/renderdoc/api/replay/vk_pipestate.h @@ -38,19 +38,21 @@ struct BindingElement bool operator==(const BindingElement &o) const { - return dynamicallyUsed == o.dynamicallyUsed && viewResourceId == o.viewResourceId && - resourceResourceId == o.resourceResourceId && samplerResourceId == o.samplerResourceId && - immutableSampler == o.immutableSampler && inlineBlock == o.inlineBlock && - viewFormat == o.viewFormat && swizzle == o.swizzle && firstMip == o.firstMip && - firstSlice == o.firstSlice && numMips == o.numMips && numSlices == o.numSlices && - byteOffset == o.byteOffset && byteSize == o.byteSize && filter == o.filter && - addressU == o.addressU && addressV == o.addressV && addressW == o.addressW && - mipBias == o.mipBias && maxAnisotropy == o.maxAnisotropy && + return type == o.type && dynamicallyUsed == o.dynamicallyUsed && + viewResourceId == o.viewResourceId && resourceResourceId == o.resourceResourceId && + samplerResourceId == o.samplerResourceId && immutableSampler == o.immutableSampler && + inlineBlock == o.inlineBlock && viewFormat == o.viewFormat && swizzle == o.swizzle && + firstMip == o.firstMip && firstSlice == o.firstSlice && numMips == o.numMips && + numSlices == o.numSlices && byteOffset == o.byteOffset && byteSize == o.byteSize && + filter == o.filter && addressU == o.addressU && addressV == o.addressV && + addressW == o.addressW && mipBias == o.mipBias && maxAnisotropy == o.maxAnisotropy && compareFunction == o.compareFunction && minLOD == o.minLOD && maxLOD == o.maxLOD && borderColor == o.borderColor && unnormalized == o.unnormalized; } bool operator<(const BindingElement &o) const { + if(!(type == o.type)) + return type < o.type; if(!(dynamicallyUsed == o.dynamicallyUsed)) return dynamicallyUsed < o.dynamicallyUsed; if(!(viewResourceId == o.viewResourceId)) @@ -104,6 +106,8 @@ struct BindingElement return false; } + DOCUMENT("The :class:`BindType` of this binding element."); + BindType type = BindType::Unknown; DOCUMENT("The :class:`ResourceId` of the current view object, if one is in use."); ResourceId viewResourceId; // bufferview, imageview, attachmentview DOCUMENT("The :class:`ResourceId` of the current underlying buffer or image object."); @@ -232,7 +236,7 @@ struct DescriptorBinding { return descriptorCount == o.descriptorCount && dynamicallyUsedCount == o.dynamicallyUsedCount && firstUsedIndex == o.firstUsedIndex && lastUsedIndex == o.lastUsedIndex && - type == o.type && stageFlags == o.stageFlags && binds == o.binds; + stageFlags == o.stageFlags && binds == o.binds; } bool operator<(const DescriptorBinding &o) const { @@ -244,8 +248,6 @@ struct DescriptorBinding return firstUsedIndex < o.firstUsedIndex; if(!(lastUsedIndex == o.lastUsedIndex)) return lastUsedIndex < o.lastUsedIndex; - if(!(type == o.type)) - return type < o.type; if(!(stageFlags == o.stageFlags)) return stageFlags < o.stageFlags; if(!(binds == o.binds)) @@ -278,8 +280,6 @@ to avoid redundant iteration in very large descriptor arrays with a small subset For more information see :data:`VKBindingElement.dynamicallyUsed`. )"); int32_t lastUsedIndex = 0x7fffffff; - DOCUMENT("The :class:`BindType` of this binding."); - BindType type = BindType::Unknown; DOCUMENT("The :class:`ShaderStageMask` where this binding is visible."); ShaderStageMask stageFlags = ShaderStageMask::Unknown; diff --git a/renderdoc/driver/vulkan/extension_support.md b/renderdoc/driver/vulkan/extension_support.md index 72c40d2ba..93c452521 100644 --- a/renderdoc/driver/vulkan/extension_support.md +++ b/renderdoc/driver/vulkan/extension_support.md @@ -66,6 +66,7 @@ Maintainers can update this file by updating vk.xml in this folder and running ` * `VK_EXT_memory_priority` * `VK_EXT_metal_surface` * `VK_EXT_multisampled_render_to_single_sampled` +* `VK_EXT_mutable_descriptor_type` * `VK_EXT_pci_bus_info` * `VK_EXT_pipeline_creation_cache_control` * `VK_EXT_pipeline_creation_feedback` @@ -206,6 +207,7 @@ Maintainers can update this file by updating vk.xml in this folder and running ` * `VK_NV_shader_subgroup_partitioned` * `VK_NV_viewport_array2` * `VK_NV_win32_keyed_mutex` +* `VK_VALVE_mutable_descriptor_type` # Unsupported @@ -252,7 +254,6 @@ Ray tracing extensions are now standard and will likely be supported at some poi * `VK_EXT_mesh_shader` * `VK_EXT_metal_objects` * `VK_EXT_multi_draw` -* `VK_EXT_mutable_descriptor_type` * `VK_EXT_non_seamless_cube_map` * `VK_EXT_opacity_micromap` * `VK_EXT_pageable_device_local_memory` @@ -343,7 +344,6 @@ Ray tracing extensions are now standard and will likely be supported at some poi ### Valve -* `VK_VALVE_mutable_descriptor_type` * `VK_VALVE_descriptor_set_host_mapping` ## WSI for other platforms diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index 3bfc171ad..e124541c9 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -24,6 +24,7 @@ #include "vk_common.h" #include "vk_core.h" +#include "vk_debug.h" #include "vk_manager.h" #include "vk_resources.h" @@ -355,6 +356,10 @@ bool VkInitParams::IsSupportedVersion(uint64_t ver) if(ver == CurrentVersion) return true; + // 0x14 -> 0x15 - added support for mutable descriptors + if(ver == 0x14) + return true; + // 0x13 -> 0x14 - added missing VkCommandBufferInheritanceRenderingInfo::flags if(ver == 0x13) return true; @@ -1040,84 +1045,55 @@ VkDriverInfo::VkDriverInfo(const VkPhysicalDeviceProperties &physProps, bool act } } -FrameRefType GetRefType(VkDescriptorType descType) +FrameRefType GetRefType(DescriptorSlotType descType) { switch(descType) { - case VK_DESCRIPTOR_TYPE_SAMPLER: - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: return eFrameRef_Read; - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: return eFrameRef_ReadBeforeWrite; + case DescriptorSlotType::Unwritten: + case DescriptorSlotType::Sampler: + case DescriptorSlotType::CombinedImageSampler: + case DescriptorSlotType::SampledImage: + case DescriptorSlotType::UniformTexelBuffer: + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::UniformBufferDynamic: + case DescriptorSlotType::InputAttachment: + case DescriptorSlotType::InlineBlock: return eFrameRef_Read; + case DescriptorSlotType::StorageImage: + case DescriptorSlotType::StorageTexelBuffer: + case DescriptorSlotType::StorageBuffer: + case DescriptorSlotType::StorageBufferDynamic: return eFrameRef_ReadBeforeWrite; default: RDCERR("Unexpected descriptor type"); } return eFrameRef_Read; } -bool IsValid(bool allowNULLDescriptors, const VkWriteDescriptorSet &write, uint32_t arrayElement) +void DescriptorSetSlot::SetBuffer(VkDescriptorType writeType, const VkDescriptorBufferInfo &bufInfo) { - if(write.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) - return true; - - // this makes assumptions that only hold within the context of Serialise_InitialState below, - // specifically that if pTexelBufferView/pBufferInfo is set then we are using them. In the general - // case they can be garbage and we must ignore them based on the descriptorType - - if(write.pTexelBufferView) - return allowNULLDescriptors ? true : write.pTexelBufferView[arrayElement] != VK_NULL_HANDLE; - - if(write.pBufferInfo) - return allowNULLDescriptors ? true : write.pBufferInfo[arrayElement].buffer != VK_NULL_HANDLE; - - if(write.pImageInfo) - { - // only these two types need samplers - bool needSampler = (write.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || - write.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - - // but all types that aren't just a sampler need an image - bool needImage = (write.descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER); - - if(allowNULLDescriptors) - needImage = false; - - if(needSampler && write.pImageInfo[arrayElement].sampler == VK_NULL_HANDLE) - return false; - - if(needImage && write.pImageInfo[arrayElement].imageView == VK_NULL_HANDLE) - return false; - - return true; - } - - RDCERR("Encountered VkWriteDescriptorSet with no data!"); - - return false; -} - -void DescriptorSetSlotBufferInfo::SetFrom(const VkDescriptorBufferInfo &bufInfo) -{ - buffer = GetResID(bufInfo.buffer); + type = convert(writeType); + resource = GetResID(bufInfo.buffer); offset = bufInfo.offset; range = bufInfo.range; + if(bufInfo.range > VK_WHOLE_SIZE) + RDCWARN("Unrepresentable buffer range size: %llx", bufInfo.range); } -void DescriptorSetSlotImageInfo::SetFrom(const VkDescriptorImageInfo &imInfo, bool setSampler, - bool setImageView) +void DescriptorSetSlot::SetImage(VkDescriptorType writeType, const VkDescriptorImageInfo &imInfo, + bool useSampler) { - if(setSampler) + type = convert(writeType); + if(useSampler && + (type == DescriptorSlotType::CombinedImageSampler || type == DescriptorSlotType::Sampler)) sampler = GetResID(imInfo.sampler); - if(setImageView) - imageView = GetResID(imInfo.imageView); - imageLayout = imInfo.imageLayout; + if(type != DescriptorSlotType::Sampler) + resource = GetResID(imInfo.imageView); + imageLayout = convert(imInfo.imageLayout); +} + +void DescriptorSetSlot::SetTexelBuffer(VkDescriptorType writeType, ResourceId id) +{ + type = convert(writeType); + resource = id; } void AddBindFrameRef(DescriptorBindRefs &refs, ResourceId id, FrameRefType ref) @@ -1166,17 +1142,32 @@ void AddMemFrameRef(DescriptorBindRefs &refs, ResourceId mem, VkDeviceSize offse p = ComposeFrameRefsDisjoint(p, maxRef); } -void DescriptorSetSlot::AccumulateBindRefs(DescriptorBindRefs &refs, VulkanResourceManager *rm, - FrameRefType ref) const +void DescriptorSetSlot::AccumulateBindRefs(DescriptorBindRefs &refs, VulkanResourceManager *rm) const { + RDCCOMPILE_ASSERT(uint64_t(DescriptorSlotImageLayout::Count) <= 0xff, + "DescriptorSlotImageLayout is no longer 8-bit"); + RDCCOMPILE_ASSERT(uint64_t(DescriptorSlotType::Count) <= 0xff, + "DescriptorSlotType is no longer 8-bit"); + RDCCOMPILE_ASSERT(sizeof(DescriptorSetSlot) == 32, "DescriptorSetSlot is no longer 32 bytes"); + VkResourceRecord *bufView = NULL, *imgView = NULL, *buffer = NULL; - if(texelBufferView != ResourceId()) - bufView = rm->GetResourceRecord(texelBufferView); - if(imageInfo.imageView != ResourceId()) - imgView = rm->GetResourceRecord(imageInfo.imageView); - if(bufferInfo.buffer != ResourceId()) - buffer = rm->GetResourceRecord(bufferInfo.buffer); + switch(type) + { + case DescriptorSlotType::UniformTexelBuffer: + case DescriptorSlotType::StorageTexelBuffer: bufView = rm->GetResourceRecord(resource); break; + case DescriptorSlotType::StorageBuffer: + case DescriptorSlotType::StorageBufferDynamic: + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::UniformBufferDynamic: buffer = rm->GetResourceRecord(resource); break; + case DescriptorSlotType::CombinedImageSampler: + case DescriptorSlotType::SampledImage: + case DescriptorSlotType::StorageImage: + case DescriptorSlotType::InputAttachment: imgView = rm->GetResourceRecord(resource); break; + default: break; + } + + FrameRefType ref = GetRefType(type); if(bufView) { @@ -1194,13 +1185,13 @@ void DescriptorSetSlot::AccumulateBindRefs(DescriptorBindRefs &refs, VulkanResou { AddImgFrameRef(refs, imgView, ref); } - if(imageInfo.sampler != ResourceId()) + if(sampler != ResourceId()) { - AddBindFrameRef(refs, imageInfo.sampler, eFrameRef_Read); + AddBindFrameRef(refs, sampler, eFrameRef_Read); } if(buffer) { - AddBindFrameRef(refs, bufferInfo.buffer, eFrameRef_Read); + AddBindFrameRef(refs, resource, eFrameRef_Read); if(buffer->resInfo && buffer->resInfo->IsSparse()) refs.sparseRefs.insert(buffer); if(buffer->baseResource != ResourceId()) diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index bc683496b..78e063b19 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -460,45 +460,208 @@ struct MemoryAllocation struct VkResourceRecord; class VulkanResourceManager; -FrameRefType GetRefType(VkDescriptorType descType); +// we inherit from uint64_t to make this more bitfield-able but we intend for this to fit in uint8_t +enum class DescriptorSlotType : uint64_t +{ + // we want an unwritten type as 0 so that zero-initialised descriptors that haven't been written + // don't look like samplers, so these unfortunately don't match VkDescriptorType in value. + Unwritten = 0, + Sampler, + CombinedImageSampler, + SampledImage, + StorageImage, + UniformTexelBuffer, + StorageTexelBuffer, + UniformBuffer, + StorageBuffer, + UniformBufferDynamic, + StorageBufferDynamic, + InputAttachment, + InlineBlock, + Count, +}; + +FrameRefType GetRefType(DescriptorSlotType descType); + +constexpr VkDescriptorType convert(DescriptorSlotType type) +{ + // temporarily disable clang-format to make this more readable. + // Ideally we'd use a simple switch() but VS2015 doesn't support that :(. + // clang-format off + return type == DescriptorSlotType::Unwritten ? VK_DESCRIPTOR_TYPE_MAX_ENUM + : type == DescriptorSlotType::Sampler ? VK_DESCRIPTOR_TYPE_SAMPLER + : type == DescriptorSlotType::CombinedImageSampler ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + : type == DescriptorSlotType::SampledImage ? VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE + : type == DescriptorSlotType::StorageImage ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE + : type == DescriptorSlotType::UniformTexelBuffer ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER + : type == DescriptorSlotType::StorageTexelBuffer ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER + : type == DescriptorSlotType::UniformBuffer ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER + : type == DescriptorSlotType::StorageBuffer ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER + : type == DescriptorSlotType::UniformBufferDynamic ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC + : type == DescriptorSlotType::StorageBufferDynamic ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC + : type == DescriptorSlotType::InputAttachment ? VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT + : type == DescriptorSlotType::InlineBlock ? VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK + : VK_DESCRIPTOR_TYPE_MAX_ENUM; + // clang-format on +} + +constexpr DescriptorSlotType convert(VkDescriptorType type) +{ + // temporarily disable clang-format to make this more readable. + // Ideally we'd use a simple switch() but VS2015 doesn't support that :(. + // clang-format off + return type == VK_DESCRIPTOR_TYPE_SAMPLER ? DescriptorSlotType::Sampler + : type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ? DescriptorSlotType::CombinedImageSampler + : type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ? DescriptorSlotType::SampledImage + : type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ? DescriptorSlotType::StorageImage + : type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER ? DescriptorSlotType::UniformTexelBuffer + : type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER ? DescriptorSlotType::StorageTexelBuffer + : type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ? DescriptorSlotType::UniformBuffer + : type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ? DescriptorSlotType::StorageBuffer + : type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ? DescriptorSlotType::UniformBufferDynamic + : type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC ? DescriptorSlotType::StorageBufferDynamic + : type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT ? DescriptorSlotType::InputAttachment + : type == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK ? DescriptorSlotType::InlineBlock + : DescriptorSlotType::Unwritten; + // clang-format on +} + +// we inherit from uint64_t to make this more bitfield-able but we intend for this to fit in uint8_t +enum class DescriptorSlotImageLayout : uint64_t +{ + // these match the core types + Undefined = 0, + General = 1, + ColorAttach = 2, + DepthStencilAttach = 3, + DepthStencilRead = 4, + ShaderRead = 5, + TransferSrc = 6, + TransferDst = 7, + Preinit = 8, + // these are extensions + DepthReadStencilAttach, + DepthAttachStencilRead, + DepthAttach, + DepthRead, + StencilAttach, + StencilRead, + Read, + Attach, + Present, + SharedPresent, + FragmentDensity, + FragmentShadingRate, + FeedbackLoop, + + Count, +}; + +constexpr VkImageLayout convert(DescriptorSlotImageLayout layout) +{ + // temporarily disable clang-format to make this more readable. + // Ideally we'd use a simple switch() but VS2015 doesn't support that :(. + // clang-format off + return layout == DescriptorSlotImageLayout::Undefined ? VK_IMAGE_LAYOUT_UNDEFINED + : layout == DescriptorSlotImageLayout::General ? VK_IMAGE_LAYOUT_GENERAL + : layout == DescriptorSlotImageLayout::ColorAttach ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + : layout == DescriptorSlotImageLayout::DepthStencilAttach ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL + : layout == DescriptorSlotImageLayout::DepthStencilRead ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL + : layout == DescriptorSlotImageLayout::ShaderRead ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + : layout == DescriptorSlotImageLayout::TransferSrc ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL + : layout == DescriptorSlotImageLayout::TransferDst ? VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL + : layout == DescriptorSlotImageLayout::Preinit ? VK_IMAGE_LAYOUT_PREINITIALIZED + : layout == DescriptorSlotImageLayout::DepthReadStencilAttach ? VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL + : layout == DescriptorSlotImageLayout::DepthAttachStencilRead ? VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL + : layout == DescriptorSlotImageLayout::DepthAttach ? VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL + : layout == DescriptorSlotImageLayout::DepthRead ? VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL + : layout == DescriptorSlotImageLayout::StencilAttach ? VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL + : layout == DescriptorSlotImageLayout::StencilRead ? VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL + : layout == DescriptorSlotImageLayout::Read ? VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL + : layout == DescriptorSlotImageLayout::Attach ? VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL + : layout == DescriptorSlotImageLayout::Present ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + : layout == DescriptorSlotImageLayout::SharedPresent ? VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR + : layout == DescriptorSlotImageLayout::FragmentDensity ? VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT + : layout == DescriptorSlotImageLayout::FragmentShadingRate ? VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR + : layout == DescriptorSlotImageLayout::FeedbackLoop ? VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT + : VK_IMAGE_LAYOUT_MAX_ENUM; + // clang-format on +} + +constexpr DescriptorSlotImageLayout convert(VkImageLayout layout) +{ + // temporarily disable clang-format to make this more readable. + // Ideally we'd use a simple switch() but VS2015 doesn't support that :(. + // clang-format off + return layout == VK_IMAGE_LAYOUT_UNDEFINED ? DescriptorSlotImageLayout::Undefined + : layout == VK_IMAGE_LAYOUT_GENERAL ? DescriptorSlotImageLayout::General + : layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ? DescriptorSlotImageLayout::ColorAttach + : layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ? DescriptorSlotImageLayout::DepthStencilAttach + : layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL ? DescriptorSlotImageLayout::DepthStencilRead + : layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ? DescriptorSlotImageLayout::ShaderRead + : layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ? DescriptorSlotImageLayout::TransferSrc + : layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ? DescriptorSlotImageLayout::TransferDst + : layout == VK_IMAGE_LAYOUT_PREINITIALIZED ? DescriptorSlotImageLayout::Preinit + : layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL ? DescriptorSlotImageLayout::DepthReadStencilAttach + : layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL ? DescriptorSlotImageLayout::DepthAttachStencilRead + : layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL ? DescriptorSlotImageLayout::DepthAttach + : layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL ? DescriptorSlotImageLayout::DepthRead + : layout == VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL ? DescriptorSlotImageLayout::StencilAttach + : layout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL ? DescriptorSlotImageLayout::StencilRead + : layout == VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL ? DescriptorSlotImageLayout::Read + : layout == VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL ? DescriptorSlotImageLayout::Attach + : layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ? DescriptorSlotImageLayout::Present + : layout == VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR ? DescriptorSlotImageLayout::SharedPresent + : layout == VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT ? DescriptorSlotImageLayout::FragmentDensity + : layout == VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR ? DescriptorSlotImageLayout::FragmentShadingRate + : layout == VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT ? DescriptorSlotImageLayout::FeedbackLoop + : DescriptorSlotImageLayout::Count; + // clang-format on +} + +struct DescriptorBindRefs; // tracking for descriptor set slots. Needed because if we use something without IDs for tracking // binding elements the handles may be deleted and recreated, and stale bindings could interfere // with new bindings -struct DescriptorSetSlotBufferInfo -{ - void SetFrom(const VkDescriptorBufferInfo &bufInfo); - - ResourceId buffer; - VkDeviceSize offset; - VkDeviceSize range; -}; - -struct DescriptorSetSlotImageInfo -{ - void SetFrom(const VkDescriptorImageInfo &imInfo, bool setSampler, bool setImageView); - - ResourceId sampler; - ResourceId imageView; - VkImageLayout imageLayout; -}; - -struct DescriptorBindRefs; - +// this struct is crushed as much as possible in size to minimise memory overhead for descriptor +// tracking when applications allocate many many millions of descriptors struct DescriptorSetSlot { - void AccumulateBindRefs(DescriptorBindRefs &refs, VulkanResourceManager *rm, FrameRefType ref) const; + void AccumulateBindRefs(DescriptorBindRefs &refs, VulkanResourceManager *rm) const; - // VkDescriptorBufferInfo - DescriptorSetSlotBufferInfo bufferInfo; + void SetBuffer(VkDescriptorType writeType, const VkDescriptorBufferInfo &bufInfo); + void SetImage(VkDescriptorType writeType, const VkDescriptorImageInfo &imInfo, bool useSampler); + void SetTexelBuffer(VkDescriptorType writeType, ResourceId id); - // VkDescriptorImageInfo - DescriptorSetSlotImageInfo imageInfo; + // 48-bit truncated VK_WHOLE_SIZE + static const VkDeviceSize WholeSizeRange = 0xFFFFFFFFFFFF; + VkDeviceSize GetRange() const { return range == WholeSizeRange ? VK_WHOLE_SIZE : range; } + // used for buffers, we assume the max buffer size is less than 1<<48. + // this is placed first to allow writes to just mask the top bits on read or write and remain + // aligned, then the type/layout below can be accessed directly as bytes. + VkDeviceSize range : 48; + // mutable type - for simplicity we treat all descriptors as mutable. It penalises all + // applications for mutable descriptors, but there's little point in having a separate path for + // normal descriptors. + DescriptorSlotType type : 8; + // used for images, the image layout + DescriptorSlotImageLayout imageLayout : 8; - ResourceId texelBufferView; + // used for buffers and inline blocks. We could steal some bits here if we needed them since 48 + // bits would be plenty for a long time. + VkDeviceSize offset; - // inline uniform block - uint32_t inlineOffset; + // resource IDs are kept separate rather than overlapping/union'ing with other types. This + // prevents a potential problem where a descriptor has a resource ID written in, then is re-used + // as a different type and the resource ID is partly trampled. Since these are disjoint we know + // that even if they're stale they're valid IDs. + + // main contents: buffer, image, texel buffer view. NOT the sampler for sampler-only descriptors, + // just to avoid confusion + ResourceId resource; + // sampler for sampler-only descriptors, or sampler for combined image-sampler descriptors + ResourceId sampler; }; struct BindingStorage @@ -544,12 +707,8 @@ private: friend struct DescSetLayout; }; -DECLARE_REFLECTION_STRUCT(DescriptorSetSlotBufferInfo); -DECLARE_REFLECTION_STRUCT(DescriptorSetSlotImageInfo); DECLARE_REFLECTION_STRUCT(DescriptorSetSlot); -bool IsValid(bool allowNULLDescriptors, const VkWriteDescriptorSet &write, uint32_t arrayElement); - #define NUM_VK_IMAGE_ASPECTS 4 #define VK_ACCESS_ALL_READ_BITS \ (VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_INDEX_READ_BIT | \ @@ -817,8 +976,8 @@ DECLARE_REFLECTION_STRUCT(VkCalibratedTimestampInfoEXT); DECLARE_REFLECTION_STRUCT(VkCommandBufferAllocateInfo); DECLARE_REFLECTION_STRUCT(VkCommandBufferBeginInfo); DECLARE_REFLECTION_STRUCT(VkCommandBufferInheritanceConditionalRenderingInfoEXT); -DECLARE_REFLECTION_STRUCT(VkCommandBufferInheritanceRenderingInfo); DECLARE_REFLECTION_STRUCT(VkCommandBufferInheritanceInfo); +DECLARE_REFLECTION_STRUCT(VkCommandBufferInheritanceRenderingInfo); DECLARE_REFLECTION_STRUCT(VkCommandBufferSubmitInfo); DECLARE_REFLECTION_STRUCT(VkCommandPoolCreateInfo); DECLARE_REFLECTION_STRUCT(VkComputePipelineCreateInfo); @@ -934,8 +1093,9 @@ DECLARE_REFLECTION_STRUCT(VkMemoryGetFdInfoKHR); DECLARE_REFLECTION_STRUCT(VkMemoryOpaqueCaptureAddressAllocateInfo); DECLARE_REFLECTION_STRUCT(VkMemoryPriorityAllocateInfoEXT); DECLARE_REFLECTION_STRUCT(VkMemoryRequirements2); -DECLARE_REFLECTION_STRUCT(VkMultisamplePropertiesEXT); DECLARE_REFLECTION_STRUCT(VkMultisampledRenderToSingleSampledInfoEXT); +DECLARE_REFLECTION_STRUCT(VkMultisamplePropertiesEXT); +DECLARE_REFLECTION_STRUCT(VkMutableDescriptorTypeCreateInfoEXT); DECLARE_REFLECTION_STRUCT(VkPastPresentationTimingGOOGLE); DECLARE_REFLECTION_STRUCT(VkPerformanceCounterDescriptionKHR); DECLARE_REFLECTION_STRUCT(VkPerformanceCounterKHR); @@ -962,31 +1122,31 @@ DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceDescriptorIndexingProperties) DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceDiscardRectanglePropertiesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceDriverProperties); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceDynamicRenderingFeatures); -DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceExtendedDynamicState2FeaturesEXT); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceExternalBufferInfo); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceExternalFenceInfo); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceExternalImageFormatInfo); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceExternalSemaphoreInfo); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFeatures2); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFloatControlsProperties); -DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentDensityMapFeaturesEXT); -DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentDensityMapPropertiesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentDensityMap2FeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentDensityMap2PropertiesEXT); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentDensityMapFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentDensityMapOffsetPropertiesQCOM); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentDensityMapPropertiesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentShadingRateFeaturesKHR); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentShadingRateKHR); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentShadingRatePropertiesKHR); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceGroupProperties); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceHostQueryResetFeatures); -DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentShadingRateKHR); -DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentShadingRateFeaturesKHR); -DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceFragmentShadingRatePropertiesKHR); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceIDProperties); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceImageFormatInfo2); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceImagelessFramebufferFeatures); @@ -1006,6 +1166,7 @@ DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMemoryProperties2); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMultiviewFeatures); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMultiviewProperties); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDevicePCIBusInfoPropertiesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDevicePerformanceQueryFeaturesKHR); DECLARE_REFLECTION_STRUCT(VkPhysicalDevicePerformanceQueryPropertiesKHR); @@ -1029,8 +1190,8 @@ DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceSamplerFilterMinmaxProperties); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceSamplerYcbcrConversionFeatures); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceScalarBlockLayoutFeatures); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures); -DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceShaderAtomicFloatFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceShaderAtomicFloatFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceShaderAtomicInt64Features); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceShaderClockFeaturesKHR); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceShaderCorePropertiesAMD); @@ -1204,8 +1365,8 @@ DECLARE_DESERIALISE_TYPE(VkCalibratedTimestampInfoEXT); DECLARE_DESERIALISE_TYPE(VkCommandBufferAllocateInfo); DECLARE_DESERIALISE_TYPE(VkCommandBufferBeginInfo); DECLARE_DESERIALISE_TYPE(VkCommandBufferInheritanceConditionalRenderingInfoEXT); -DECLARE_DESERIALISE_TYPE(VkCommandBufferInheritanceRenderingInfo); DECLARE_DESERIALISE_TYPE(VkCommandBufferInheritanceInfo); +DECLARE_DESERIALISE_TYPE(VkCommandBufferInheritanceRenderingInfo); DECLARE_DESERIALISE_TYPE(VkCommandBufferSubmitInfo); DECLARE_DESERIALISE_TYPE(VkCommandPoolCreateInfo); DECLARE_DESERIALISE_TYPE(VkComputePipelineCreateInfo); @@ -1320,8 +1481,9 @@ DECLARE_DESERIALISE_TYPE(VkMemoryGetFdInfoKHR); DECLARE_DESERIALISE_TYPE(VkMemoryOpaqueCaptureAddressAllocateInfo); DECLARE_DESERIALISE_TYPE(VkMemoryPriorityAllocateInfoEXT); DECLARE_DESERIALISE_TYPE(VkMemoryRequirements2); -DECLARE_DESERIALISE_TYPE(VkMultisamplePropertiesEXT); DECLARE_DESERIALISE_TYPE(VkMultisampledRenderToSingleSampledInfoEXT); +DECLARE_DESERIALISE_TYPE(VkMultisamplePropertiesEXT); +DECLARE_DESERIALISE_TYPE(VkMutableDescriptorTypeCreateInfoEXT); DECLARE_DESERIALISE_TYPE(VkPerformanceCounterDescriptionKHR); DECLARE_DESERIALISE_TYPE(VkPerformanceCounterKHR); DECLARE_DESERIALISE_TYPE(VkPerformanceQuerySubmitInfoKHR); @@ -1345,31 +1507,31 @@ DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceDescriptorIndexingProperties) DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceDiscardRectanglePropertiesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceDriverProperties); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceDynamicRenderingFeatures); -DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceExtendedDynamicState2FeaturesEXT); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceExternalBufferInfo); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceExternalFenceInfo); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceExternalImageFormatInfo); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceExternalSemaphoreInfo); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFeatures2); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFloatControlsProperties); -DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapFeaturesEXT); -DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapPropertiesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMap2FeaturesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMap2PropertiesEXT); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapFeaturesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapOffsetPropertiesQCOM); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapPropertiesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRateFeaturesKHR); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRateKHR); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRatePropertiesKHR); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceGroupProperties); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceHostQueryResetFeatures); -DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRateKHR); -DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRateFeaturesKHR); -DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRatePropertiesKHR); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceIDProperties); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceImageFormatInfo2); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceImagelessFramebufferFeatures); @@ -1389,6 +1551,7 @@ DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMemoryProperties2); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMultiviewFeatures); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMultiviewProperties); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDevicePCIBusInfoPropertiesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDevicePerformanceQueryFeaturesKHR); DECLARE_DESERIALISE_TYPE(VkPhysicalDevicePerformanceQueryPropertiesKHR); @@ -1412,8 +1575,8 @@ DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceSamplerFilterMinmaxProperties); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceSamplerYcbcrConversionFeatures); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceScalarBlockLayoutFeatures); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures); -DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceShaderAtomicFloatFeaturesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceShaderAtomicFloatFeaturesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceShaderAtomicInt64Features); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceShaderClockFeaturesKHR); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceShaderCorePropertiesAMD); @@ -1597,6 +1760,7 @@ DECLARE_REFLECTION_STRUCT(VkInputAttachmentAspectReference); DECLARE_REFLECTION_STRUCT(VkMemoryHeap); DECLARE_REFLECTION_STRUCT(VkMemoryRequirements); DECLARE_REFLECTION_STRUCT(VkMemoryType); +DECLARE_REFLECTION_STRUCT(VkMutableDescriptorTypeListEXT); DECLARE_REFLECTION_STRUCT(VkOffset2D); DECLARE_REFLECTION_STRUCT(VkOffset3D); DECLARE_REFLECTION_STRUCT(VkPerformanceCounterResultKHR); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 98c7242c8..ec0a89b14 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -1006,6 +1006,9 @@ static const VkExtensionProperties supportedExtensions[] = { VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME, VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_SPEC_VERSION, }, + { + VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME, VK_EXT_MUTABLE_DESCRIPTOR_TYPE_SPEC_VERSION, + }, { VK_EXT_PCI_BUS_INFO_EXTENSION_NAME, VK_EXT_PCI_BUS_INFO_SPEC_VERSION, }, @@ -1500,6 +1503,9 @@ static const VkExtensionProperties supportedExtensions[] = { { VK_QCOM_RENDER_PASS_STORE_OPS_EXTENSION_NAME, VK_QCOM_RENDER_PASS_STORE_OPS_SPEC_VERSION, }, + { + VK_VALVE_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME, VK_VALVE_MUTABLE_DESCRIPTOR_TYPE_SPEC_VERSION, + }, }; // this is the list of extensions we provide - regardless of whether the ICD supports them @@ -4738,16 +4744,8 @@ void WrappedVulkan::AddUsage(VulkanActionTreeNode &actionNode, rdcarray descWrites; - struct AllocedWrites { ~AllocedWrites() { - for(VkDescriptorImageInfo *a : imgWrites) - delete[] a; for(VkDescriptorBufferInfo *a : bufWrites) delete[] a; - for(VkBufferView *a : bufViewWrites) - delete[] a; for(VkWriteDescriptorSetInlineUniformBlock *a : inlineWrites) delete a; } - rdcarray imgWrites; rdcarray bufWrites; - rdcarray bufViewWrites; rdcarray inlineWrites; } alloced; - rdcarray &allocImgWrites = alloced.imgWrites; rdcarray &allocBufWrites = alloced.bufWrites; - rdcarray &allocBufViewWrites = alloced.bufViewWrites; rdcarray &allocInlineWrites = alloced.inlineWrites; // one for each descriptor type. 1 of each to start with, we then increment for each descriptor // we need to allocate - VkDescriptorPoolSize poolSizes[12] = { + rdcarray poolSizes = { {VK_DESCRIPTOR_TYPE_SAMPLER, 1}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1}, @@ -2400,23 +2390,84 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, {VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK, 0}, }; + // array of descriptor types, used for generating lists for binding data. Each unique bitmask + // will have an offset (see below) pointing into this array where that bitmask's list of + // descriptors is + rdcarray mutableTypeArray; + + // array of unique bitmasks encountered + rdcarray mutablePoolsizeBitmask; + // parallel array to mutablePoolsizeBitmask with the [offset,range] in mutableTypeArray where the + // bitmask's type list is. + rdcarray> mutableBitmaskArrayRange; + + // populate mutable bitmasks. This loop is the same as the one below which is more commented + for(size_t i = 0; i < setLayouts.size(); i++) + { + if(i < pipeInfo.descSetLayouts.size() && i < pipe.descSets.size() && + pipe.descSets[i].pipeLayout != ResourceId()) + { + const VulkanCreationInfo::PipelineLayout &pipelineLayoutInfo = + creationInfo.m_PipelineLayout[pipe.descSets[i].pipeLayout]; + + if(pipelineLayoutInfo.descSetLayouts[i] == ResourceId()) + continue; + + const DescSetLayout &origLayout = + creationInfo.m_DescSetLayout[pipelineLayoutInfo.descSetLayouts[i]]; + + for(size_t b = 0; b < origLayout.bindings.size(); b++) + { + uint64_t mutableBitmask = origLayout.mutableBitmasks[b]; + + int bitmaskIdx = mutablePoolsizeBitmask.indexOf(mutableBitmask); + if(bitmaskIdx == -1) + { + bitmaskIdx = mutablePoolsizeBitmask.count(); + mutablePoolsizeBitmask.push_back(mutableBitmask); + poolSizes.push_back({VK_DESCRIPTOR_TYPE_MUTABLE_EXT, 0}); + + uint32_t count = 0; + for(uint64_t m = 0; m < 64; m++) + { + if(((1ULL << m) & mutableBitmask) == 0) + continue; + + mutableTypeArray.push_back(convert(DescriptorSlotType(m))); + count++; + } + mutableBitmaskArrayRange.push_back({mutableTypeArray.size() - count, count}); + } + } + } + } + VkDescriptorPoolInlineUniformBlockCreateInfo inlineCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO, }; + static const uint32_t NormalDescriptorCount = 11; static const uint32_t InlinePoolIndex = 11; + static const uint32_t MutablePoolStart = 12; - uint32_t poolSizeCount = InlinePoolIndex; + uint32_t poolSizeCount = NormalDescriptorCount; // count up our own for(size_t i = 0; i < newBindingsCount; i++) { - RDCASSERT(newBindings[i].descriptorType < ARRAY_COUNT(poolSizes), newBindings[i].descriptorType); + RDCASSERT((uint32_t)newBindings[i].descriptorType < NormalDescriptorCount, + newBindings[i].descriptorType); poolSizes[newBindings[i].descriptorType].descriptorCount += newBindings[i].descriptorCount; } + VkMutableDescriptorTypeCreateInfoEXT mutableCreateInfo = { + VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT, + }; + // need to add our added bindings to the first descriptor set rdcarray bindings(newBindings, newBindingsCount); + // this is a per-bindings array, only used for mutable descriptors + rdcarray mutableTypeLists; // if there are fewer sets bound than were declared in the pipeline layout, only process the // bound sets (as otherwise we'd fail to copy from them). Assume the application knew what it @@ -2566,6 +2617,9 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, if(i > 0) bindings.clear(); + // clear any mutable type lists + mutableTypeLists.clear(); + // if the shader had no descriptor sets at all, i will be invalid, so just skip and add a set // with only our own bindings. if(i < pipeInfo.descSetLayouts.size() && i < pipe.descSets.size() && @@ -2591,26 +2645,38 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, for(size_t b = 0; !error && b < origLayout.bindings.size(); b++) { - const DescSetLayout::Binding &bind = origLayout.bindings[b]; + const DescSetLayout::Binding &layoutBind = origLayout.bindings[b]; // skip empty bindings - if(bind.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) continue; - uint32_t descriptorCount = bind.descriptorCount; + uint32_t descriptorCount = layoutBind.descriptorCount; - if(bind.variableSize) + if(layoutBind.variableSize) descriptorCount = setInfo.data.variableDescriptorCount; // make room in the pool - if(bind.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { poolSizes[InlinePoolIndex].descriptorCount += descriptorCount; inlineCreateInfo.maxInlineUniformBlockBindings++; } + else if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_MUTABLE_EXT) + { + int bitmaskIdx = mutablePoolsizeBitmask.indexOf(origLayout.mutableBitmasks[b]); + RDCASSERT(bitmaskIdx >= 0); + poolSizes[MutablePoolStart + bitmaskIdx].descriptorCount += descriptorCount; + + // each mutable descriptor needs a type list + mutableTypeLists.resize_for_index(b); + mutableTypeLists[b].descriptorTypeCount = mutableBitmaskArrayRange[bitmaskIdx].second; + mutableTypeLists[b].pDescriptorTypes = + mutableTypeArray.data() + mutableBitmaskArrayRange[bitmaskIdx].first; + } else { - poolSizes[bind.descriptorType].descriptorCount += descriptorCount; + poolSizes[layoutBind.layoutDescType].descriptorCount += descriptorCount; } VkDescriptorSetLayoutBinding newBind; @@ -2618,7 +2684,7 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, // conditionally patch shader bindings depending on which set they're in. newBind.binding = uint32_t(b + newBindingsCount); newBind.descriptorCount = descriptorCount; - newBind.descriptorType = bind.descriptorType; + newBind.descriptorType = layoutBind.layoutDescType; // we only need it available for compute, just make all bindings visible otherwise dynamic // buffer offsets could be indexed wrongly. Consider the case where we have binding 0 as a @@ -2631,73 +2697,95 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, if(patchedBindingStage != 0) newBind.stageFlags = patchedBindingStage; else - newBind.stageFlags = bind.stageFlags; + newBind.stageFlags = layoutBind.stageFlags; - switch(bind.descriptorType) + // mutable descriptors count against all limits they can be used against. This loop will + // only execute for mutable descriptors, others will just execute once using their real type + for(uint64_t m = 0; m < 64; m++) { - case VK_DESCRIPTOR_TYPE_SAMPLER: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSamplers); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSamplers); + VkDescriptorType descType = layoutBind.layoutDescType; + + if(descType == VK_DESCRIPTOR_TYPE_MUTABLE_EXT) + { + // if this type's bit isn't set in the bitmask of available descriptors then continue + if(((1ULL << m) & origLayout.mutableBitmasks[b]) == 0) + continue; + + // this type is allowed, convert it to an enum and check it against the limits below + descType = convert(DescriptorSlotType(m)); + } + + switch(descType) + { + case VK_DESCRIPTOR_TYPE_SAMPLER: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSamplers); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSamplers); + break; + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSampledImages); + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSamplers); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSamplers); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSampledImages); + break; + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSampledImages); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSampledImages); + break; + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageImages); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageImages); + break; + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSampledImages); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSampledImages); + break; + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageImages); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageImages); + break; + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetUniformBuffers); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorUniformBuffers); + break; + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageBuffers); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageBuffers); + break; + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetUniformBuffersDynamic); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorUniformBuffers); + break; + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageBuffersDynamic); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageBuffers); + break; + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetInputAttachments); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorInputAttachments); + break; + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: + descriptorCount = 1; + UPDATE_AND_CHECK_LIMIT(maxDescriptorSetInlineUniformBlocks); + UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorInlineUniformBlocks); + break; + default: break; + } + + // we're only looping for mutables + if(layoutBind.layoutDescType != VK_DESCRIPTOR_TYPE_MUTABLE_EXT) break; - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSampledImages); - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSamplers); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSamplers); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSampledImages); - break; - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSampledImages); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSampledImages); - break; - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageImages); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageImages); - break; - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSampledImages); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSampledImages); - break; - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageImages); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageImages); - break; - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetUniformBuffers); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorUniformBuffers); - break; - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageBuffers); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageBuffers); - break; - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetUniformBuffersDynamic); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorUniformBuffers); - break; - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageBuffersDynamic); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageBuffers); - break; - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetInputAttachments); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorInputAttachments); - break; - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: - descriptorCount = 1; - UPDATE_AND_CHECK_LIMIT(maxDescriptorSetInlineUniformBlocks); - UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorInlineUniformBlocks); - break; - default: break; } UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageResources); - if(bind.immutableSampler) + if(layoutBind.immutableSampler) { hasImmutableSamplers = true; - VkSampler *samplers = new VkSampler[bind.descriptorCount]; + VkSampler *samplers = new VkSampler[layoutBind.descriptorCount]; newBind.pImmutableSamplers = samplers; - for(uint32_t s = 0; s < bind.descriptorCount; s++) - samplers[s] = GetResourceManager()->GetCurrentHandle(bind.immutableSampler[s]); + for(uint32_t s = 0; s < layoutBind.descriptorCount; s++) + samplers[s] = + GetResourceManager()->GetCurrentHandle(layoutBind.immutableSampler[s]); } else { @@ -2716,6 +2804,13 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, bindings.data(), }; + if(!mutableTypeLists.empty()) + { + descsetLayoutInfo.pNext = &mutableCreateInfo; + mutableCreateInfo.mutableDescriptorTypeListCount = (uint32_t)mutableTypeLists.size(); + mutableCreateInfo.pMutableDescriptorTypeLists = mutableTypeLists.data(); + } + if(!error) { // create new offseted descriptor layout @@ -2739,7 +2834,7 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, poolCreateInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; poolCreateInfo.maxSets = (uint32_t)setLayouts.size(); poolCreateInfo.poolSizeCount = poolSizeCount; - poolCreateInfo.pPoolSizes = poolSizes; + poolCreateInfo.pPoolSizes = poolSizes.data(); if(inlineCreateInfo.maxInlineUniformBlockBindings > 0) { @@ -2747,6 +2842,25 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, poolCreateInfo.pNext = &inlineCreateInfo; } + poolCreateInfo.poolSizeCount += mutablePoolsizeBitmask.count(); + + if(!mutablePoolsizeBitmask.empty()) + { + mutableTypeLists.clear(); + mutableTypeLists.resize(poolCreateInfo.poolSizeCount); + + for(size_t i = 0; i < mutablePoolsizeBitmask.size(); i++) + { + mutableTypeLists[MutablePoolStart + i].pDescriptorTypes = + mutableTypeArray.data() + mutableBitmaskArrayRange[i].first; + mutableTypeLists[MutablePoolStart + i].descriptorTypeCount = mutableBitmaskArrayRange[i].second; + } + + poolCreateInfo.pNext = &mutableCreateInfo; + mutableCreateInfo.mutableDescriptorTypeListCount = (uint32_t)mutableTypeLists.size(); + mutableCreateInfo.pMutableDescriptorTypeLists = mutableTypeLists.data(); + } + // create descriptor pool with enough space for our descriptors vkr = m_pDriver->vkCreateDescriptorPool(dev, &poolCreateInfo, NULL, &descpool); CheckVkResult(vkr); @@ -2763,6 +2877,8 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, descSets.resize(setLayouts.size()); m_pDriver->vkAllocateDescriptorSets(dev, &descSetAllocInfo, descSets.data()); + rdcarray descWrites; + // copy the data across from the real descriptors into our adjusted bindings for(size_t i = 0; i < boundDescs; i++) { @@ -2786,146 +2902,63 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, m_pDriver->m_DescriptorSetState[pipe.descSets[i].descSet]; { - // push descriptors don't have a source to copy from, we need to add writes - VkWriteDescriptorSet write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; - write.dstSet = descSets[i]; - // Only write bindings that actually exist in the current descriptor // set. If there are bindings that aren't set, assume the app knows // what it's doing and the remaining bindings are unused. - for(size_t b = 0; b < setInfo.data.binds.size(); b++) + for(size_t bind = 0; bind < setInfo.data.binds.size(); bind++) { - const DescSetLayout::Binding &bind = origLayout.bindings[b]; + const DescSetLayout::Binding &layoutBind = origLayout.bindings[bind]; // skip empty bindings - if(bind.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) continue; - uint32_t descriptorCount = bind.descriptorCount; + uint32_t descriptorCount = layoutBind.descriptorCount; - if(bind.variableSize) + if(layoutBind.variableSize) descriptorCount = setInfo.data.variableDescriptorCount; - DescriptorSetSlot *slot = setInfo.data.binds[b]; + if(descriptorCount == 0) + continue; - write.dstBinding = uint32_t(b + newBindingsCount); - write.dstArrayElement = 0; - write.descriptorCount = descriptorCount; - write.descriptorType = bind.descriptorType; - - switch(write.descriptorType) - { - case VK_DESCRIPTOR_TYPE_SAMPLER: - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - { - VkDescriptorImageInfo *out = new VkDescriptorImageInfo[write.descriptorCount]; - for(uint32_t w = 0; w < write.descriptorCount; w++) - { - const DescriptorSetSlotImageInfo &src = slot[w].imageInfo; - - out[w].imageLayout = src.imageLayout; - out[w].sampler = GetResourceManager()->GetCurrentHandle(src.sampler); - out[w].imageView = GetResourceManager()->GetCurrentHandle(src.imageView); - } - - write.pImageInfo = out; - allocImgWrites.push_back(out); - break; - } - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - { - VkBufferView *out = new VkBufferView[write.descriptorCount]; - for(uint32_t w = 0; w < write.descriptorCount; w++) - out[w] = GetResourceManager()->GetCurrentHandle(slot[w].texelBufferView); - write.pTexelBufferView = out; - allocBufViewWrites.push_back(out); - break; - } - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - { - VkDescriptorBufferInfo *out = new VkDescriptorBufferInfo[write.descriptorCount]; - for(uint32_t w = 0; w < write.descriptorCount; w++) - { - const DescriptorSetSlotBufferInfo &src = slot[w].bufferInfo; - - out[w].offset = src.offset; - out[w].range = src.range; - out[w].buffer = GetResourceManager()->GetCurrentHandle(src.buffer); - } - write.pBufferInfo = out; - allocBufWrites.push_back(out); - break; - } - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: - { - allocInlineWrites.push_back(new VkWriteDescriptorSetInlineUniformBlock); - VkWriteDescriptorSetInlineUniformBlock *inlineWrite = allocInlineWrites.back(); - inlineWrite->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK; - inlineWrite->pNext = NULL; - inlineWrite->dataSize = descriptorCount; - inlineWrite->pData = setInfo.data.inlineBytes.data() + slot[0].inlineOffset; - write.pNext = inlineWrite; - break; - } - default: RDCERR("Unexpected descriptor type %d", write.descriptorType); - } + DescriptorSetSlot *slots = setInfo.data.binds[bind]; // skip validity check for inline uniform block as the descriptor count means something // different - if(write.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { + allocInlineWrites.push_back(new VkWriteDescriptorSetInlineUniformBlock); + VkWriteDescriptorSetInlineUniformBlock *inlineWrite = allocInlineWrites.back(); + inlineWrite->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK; + inlineWrite->pNext = NULL; + inlineWrite->pData = setInfo.data.inlineBytes.data() + slots->offset; + inlineWrite->dataSize = descriptorCount; + + VkWriteDescriptorSet write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; + write.pNext = inlineWrite; + write.dstSet = descSets[i]; + write.descriptorType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK; + write.dstBinding = uint32_t(bind + newBindingsCount); write.descriptorCount = descriptorCount; + descWrites.push_back(write); continue; } - // start with no descriptors - write.descriptorCount = 0; - - for(uint32_t w = 0; w < descriptorCount; w++) + // skip single descriptors that are not valid + if(!m_pDriver->NULLDescriptorsAllowed() && descriptorCount == 1 && + slots->resource == ResourceId() && slots->sampler == ResourceId()) { - // if this write is valid, we increment the descriptor count and continue - if(IsValid(m_pDriver->NULLDescriptorsAllowed(), write, w - write.dstArrayElement)) - { - write.descriptorCount++; - } - else - { - // if this write isn't valid, then we first check to see if we had any previous - // pending writes in the array we were going to batch together, if so we add them. - if(write.descriptorCount > 0) - descWrites.push_back(write); - - // skip past any previous descriptors we just wrote, as well as the current invalid - // one - if(write.pBufferInfo) - write.pBufferInfo += write.descriptorCount + 1; - if(write.pImageInfo) - write.pImageInfo += write.descriptorCount + 1; - if(write.pTexelBufferView) - write.pTexelBufferView += write.descriptorCount + 1; - - // now start again from 0 descriptors, at the next array element - write.dstArrayElement += write.descriptorCount + 1; - write.descriptorCount = 0; - } + // do nothing - don't increment bind so that the same write descriptor is used next time. + continue; } - // if there are any left, add them here - if(write.descriptorCount > 0) - descWrites.push_back(write); + VkDescriptorBufferInfo *writeScratch = new VkDescriptorBufferInfo[descriptorCount]; + allocBufWrites.push_back(writeScratch); - // don't leak the arrays and cause double deletes, NULL them after each time - write.pImageInfo = NULL; - write.pBufferInfo = NULL; - write.pTexelBufferView = NULL; + CreateDescriptorWritesForSlotData(m_pDriver, descWrites, writeScratch, slots, + descriptorCount, descSets[i], + uint32_t(bind + newBindingsCount), layoutBind); } } } diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index 161c72af5..452c541be 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -25,6 +25,7 @@ #include "vk_info.h" #include "core/settings.h" #include "lz4/lz4.h" +#include "vk_core.h" // for compatibility we use the same DXBC name since it's now configured by the UI RDOC_EXTERN_CONFIG(rdcarray, DXBC_Debug_SearchDirPaths); @@ -304,6 +305,10 @@ void DescSetLayout::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo inlineCount = 0; inlineByteSize = 0; + const VkMutableDescriptorTypeCreateInfoEXT *mutableInfo = + (const VkMutableDescriptorTypeCreateInfoEXT *)FindNextStruct( + pCreateInfo, VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT); + flags = pCreateInfo->flags; anyStageFlags = 0; @@ -328,27 +333,44 @@ void DescSetLayout::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo { uint32_t b = pCreateInfo->pBindings[i].binding; // expand to fit the binding - if(b >= bindings.size()) - bindings.resize(b + 1); + bindings.resize_for_index(b); + + if(mutableInfo && i < mutableInfo->mutableDescriptorTypeListCount) + { + const VkMutableDescriptorTypeListEXT &mutableTypes = + mutableInfo->pMutableDescriptorTypeLists[i]; + + RDCCOMPILE_ASSERT(uint64_t(DescriptorSlotType::Count) < 64, + "Descriptor types don't fit in 64-bit bitmask anymore"); + + // encode a bitmask with the available types for this descriptor + uint64_t mask = 0; + for(uint32_t m = 0; m < mutableTypes.descriptorTypeCount; m++) + mask |= (1ULL << uint64_t(convert(mutableTypes.pDescriptorTypes[m]))); + + mutableBitmasks.resize_for_index(b); + mutableBitmasks[b] = mask; + } + + VkDescriptorType type = pCreateInfo->pBindings[i].descriptorType; bindings[b].descriptorCount = pCreateInfo->pBindings[i].descriptorCount; - bindings[b].descriptorType = pCreateInfo->pBindings[i].descriptorType; + bindings[b].layoutDescType = type; bindings[b].stageFlags = pCreateInfo->pBindings[i].stageFlags; anyStageFlags |= bindings[b].stageFlags; - if(bindings[b].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || - bindings[b].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) + if(type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || + type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) dynamicCount += bindings[b].descriptorCount; - if(bindings[b].descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(type == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { inlineCount++; inlineByteSize = AlignUp4(inlineByteSize + bindings[b].descriptorCount); } - if((bindings[b].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || - bindings[b].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) && + if((type == VK_DESCRIPTOR_TYPE_SAMPLER || type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) && pCreateInfo->pBindings[i].pImmutableSamplers) { bindings[b].immutableSampler = new ResourceId[bindings[b].descriptorCount]; @@ -377,7 +399,7 @@ void DescSetLayout::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo if(bindings[b].variableSize) break; - if(bindings[b].descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(bindings[b].layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { elemOffset++; } @@ -413,9 +435,11 @@ void DescSetLayout::CreateBindingsArray(BindingStorage &bindingStorage, uint32_t { bindingStorage.binds[i] = bindingStorage.elems.data() + bindings[i].elemOffset; - if(bindings[i].descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(bindings[i].layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { - bindingStorage.binds[i]->inlineOffset = inlineOffset; + bindingStorage.binds[i]->type = DescriptorSlotType::InlineBlock; + bindingStorage.binds[i]->offset = inlineOffset; + bindingStorage.binds[i]->range = bindings[i].descriptorCount; inlineOffset = AlignUp4(inlineOffset + bindings[i].descriptorCount); } } @@ -462,9 +486,11 @@ void DescSetLayout::UpdateBindingsArray(const DescSetLayout &prevLayout, { DescriptorSetSlot *newSlots = newElems.data() + bindings[i].elemOffset; - if(bindings[i].descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(bindings[i].layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { - bindingStorage.binds[i]->inlineOffset = inlineOffset; + bindingStorage.binds[i]->type = DescriptorSlotType::InlineBlock; + bindingStorage.binds[i]->offset = inlineOffset; + bindingStorage.binds[i]->range = bindings[i].descriptorCount; inlineOffset = AlignUp4(inlineOffset + bindings[i].descriptorCount); } else @@ -486,7 +512,7 @@ void DescSetLayout::UpdateBindingsArray(const DescSetLayout &prevLayout, } } -bool DescSetLayout::operator==(const DescSetLayout &other) const +bool DescSetLayout::isCompatible(const DescSetLayout &other) const { // shortcut for equality to ourselves if(this == &other) @@ -503,7 +529,7 @@ bool DescSetLayout::operator==(const DescSetLayout &other) const const Binding &b = other.bindings[i]; // if the type/stages/count are different, the layout is different - if(a.descriptorCount != b.descriptorCount || a.descriptorType != b.descriptorType || + if(a.descriptorCount != b.descriptorCount || a.layoutDescType != b.layoutDescType || a.stageFlags != b.stageFlags) return false; @@ -525,6 +551,221 @@ bool DescSetLayout::operator==(const DescSetLayout &other) const return true; } +bool IsValid(bool allowNULLDescriptors, const VkWriteDescriptorSet &write, uint32_t arrayElement) +{ + if(write.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + return true; + + // this makes assumptions that only hold within the context of Serialise_InitialState below, + // specifically that if pTexelBufferView/pBufferInfo is set then we are using them. In the general + // case they can be garbage and we must ignore them based on the descriptorType + + if(write.pTexelBufferView) + return allowNULLDescriptors ? true : write.pTexelBufferView[arrayElement] != VK_NULL_HANDLE; + + if(write.pBufferInfo) + return allowNULLDescriptors ? true : write.pBufferInfo[arrayElement].buffer != VK_NULL_HANDLE; + + if(write.pImageInfo) + { + // only these two types need samplers + bool needSampler = (write.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || + write.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + + // but all types that aren't just a sampler need an image + bool needImage = (write.descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER); + + if(allowNULLDescriptors) + needImage = false; + + if(needSampler && write.pImageInfo[arrayElement].sampler == VK_NULL_HANDLE) + return false; + + if(needImage && write.pImageInfo[arrayElement].imageView == VK_NULL_HANDLE) + return false; + + return true; + } + + RDCERR("Encountered VkWriteDescriptorSet with no data!"); + + return false; +} + +bool CreateDescriptorWritesForSlotData(WrappedVulkan *vk, rdcarray &writes, + VkDescriptorBufferInfo *&writeScratch, + const DescriptorSetSlot *slots, uint32_t descriptorCount, + VkDescriptorSet set, uint32_t dstBind, + const DescSetLayout::Binding &layoutBind) +{ + bool ret = true; + + RDCCOMPILE_ASSERT(sizeof(VkDescriptorImageInfo) <= sizeof(VkDescriptorBufferInfo), + "VkDescriptorBufferInfo should be large enough for all descriptor write types"); + RDCCOMPILE_ASSERT(sizeof(VkBufferView) <= sizeof(VkDescriptorBufferInfo), + "VkDescriptorBufferInfo should be large enough for all descriptor write types"); + + ResourceId *immutableSamplers = layoutBind.immutableSampler; + + VkWriteDescriptorSet templateWrite = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; + templateWrite.dstSet = set; + templateWrite.dstBinding = dstBind; + + writes.push_back(templateWrite); + + VulkanResourceManager *rm = vk->GetResourceManager(); + + // loop over every slot in this descriptor array + for(uint32_t slot = 0; slot < descriptorCount; slot++) + { + VkDescriptorType descType = convert(slots[slot].type); + + // if the previous write had some contents, and is a different type to this one, + // finish + // it off and start a new one. In theory if this descriptor were invalid and NULL + // descriptors are supported we could write NULL and continue to combine, but we don't + // do that. + // note that the previous write is from THIS descriptor as we don't batch across + // descriptors - even though there is 'rollover' behaviour allowing this. + if(writes.back().descriptorCount > 0 && writes.back().descriptorType != descType) + { + // consume the write arrays used + writeScratch += writes.back().descriptorCount; + + writes.push_back(templateWrite); + writes.back().dstArrayElement = slot; + } + + // skip unwritten descriptors + if(descType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + continue; + + // the current write is either empty, in which case we can just set it to what we + // want, + // or it's the same type in which case we're appending to its array + + VkDescriptorBufferInfo *writeBuffer = writeScratch; + VkDescriptorImageInfo *writeImage = (VkDescriptorImageInfo *)writeScratch; + VkBufferView *writeTexelBuffer = (VkBufferView *)writeScratch; + + // set the type and base of this write if the previous one is as-yet unused. + if(writes.back().descriptorCount == 0) + { + writes.back().descriptorType = descType; + writes.back().dstArrayElement = slot; + } + + // array index in the above writeArrays, relative to the start of the current + // contiguous + // range starting at dstArrayElement + uint32_t arrayIdx = slot - writes.back().dstArrayElement; + + switch(descType) + { + case VK_DESCRIPTOR_TYPE_SAMPLER: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + { + if(descType != VK_DESCRIPTOR_TYPE_SAMPLER && rm->HasLiveResource(slots[slot].resource)) + writeImage[arrayIdx].imageView = rm->GetLiveHandle(slots[slot].resource); + else + writeImage[arrayIdx].imageView = VK_NULL_HANDLE; + + if((descType == VK_DESCRIPTOR_TYPE_SAMPLER || + descType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) && + rm->HasLiveResource(slots[slot].sampler)) + writeImage[arrayIdx].sampler = rm->GetLiveHandle(slots[slot].sampler); + else + writeImage[arrayIdx].sampler = VK_NULL_HANDLE; + + writeImage[arrayIdx].imageLayout = convert(slots[slot].imageLayout); + + // if we're not updating a SAMPLER descriptor fill in immutable samplers so that + // our + // validity checking doesn't have to look them up. + if(immutableSamplers && descType != VK_DESCRIPTOR_TYPE_SAMPLER) + { + writeImage[arrayIdx].sampler = rm->GetCurrentHandle(immutableSamplers[slot]); + } + + // set the write array (possibly redundant if we're collating as writeImage only + // moves when we commit some writes) + writes.back().pImageInfo = writeImage; + break; + } + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + { + if(rm->HasLiveResource(slots[slot].resource)) + writeTexelBuffer[arrayIdx] = rm->GetLiveHandle(slots[slot].resource); + else + writeTexelBuffer[arrayIdx] = VK_NULL_HANDLE; + + writes.back().pTexelBufferView = writeTexelBuffer; + break; + } + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + { + if(rm->HasLiveResource(slots[slot].resource)) + writeBuffer[arrayIdx].buffer = rm->GetLiveHandle(slots[slot].resource); + else + writeBuffer[arrayIdx].buffer = VK_NULL_HANDLE; + writeBuffer[arrayIdx].offset = slots[slot].offset; + writeBuffer[arrayIdx].range = slots[slot].GetRange(); + + writes.back().pBufferInfo = writeBuffer; + break; + } + default: + { + RDCERR("Unexpected descriptor type %d", descType); + ret = false; + } + } + + // if we made a valid write, count it + if(IsValid(vk->NULLDescriptorsAllowed(), writes.back(), arrayIdx)) + { + writes.back().descriptorCount++; + } + else + { + // otherwise this is an invalid write. If we previously had valid writes we need to + // end them to ensure we don't skip + if(writes.back().descriptorCount > 0) + { + // consume the write arrays used + writeScratch += writes.back().descriptorCount; + + writes.push_back(templateWrite); + writes.back().dstArrayElement = slot; + } + } + } + + // take any final descriptor write that had descriptors and commit them. E.g. if all + // writes succeeded and were the same type then the above loop won't have committed them + // yet, since it only commits when it needs to break a range of writes (due to one being + // unwritten, or due to types differing) + if(writes.back().descriptorCount > 0) + { + // consume the write arrays used + writeScratch += writes.back().descriptorCount; + } + else if(writes.back().descriptorCount == 0) + { + // similarly remove the last write if it was unused + writes.pop_back(); + } + + return ret; +} + void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, ResourceId id, const VkGraphicsPipelineCreateInfo *pCreateInfo) @@ -2000,6 +2241,29 @@ void VulkanCreationInfo::DescSetPool::Init(VulkanResourceManager *resourceMan, { maxSets = pCreateInfo->maxSets; poolSizes.assign(pCreateInfo->pPoolSizes, pCreateInfo->poolSizeCount); + + const VkMutableDescriptorTypeCreateInfoEXT *mutableInfo = + (const VkMutableDescriptorTypeCreateInfoEXT *)FindNextStruct( + pCreateInfo, VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT); + + if(mutableInfo) + { + for(uint32_t i = 0; + i < RDCMIN(pCreateInfo->poolSizeCount, mutableInfo->mutableDescriptorTypeListCount); i++) + { + const VkMutableDescriptorTypeListEXT &mutableTypes = + mutableInfo->pMutableDescriptorTypeLists[i]; + + RDCCOMPILE_ASSERT(uint64_t(DescriptorSlotType::Count) < 64, + "Descriptor types don't fit in 64-bit bitmask anymore"); + + mutableBitmasks.resize_for_index(i); + + // encode a bitmask with the available types for this descriptor + for(uint32_t m = 0; m < mutableTypes.descriptorTypeCount; m++) + mutableBitmasks[i] |= (1ULL << uint64_t(convert(mutableTypes.pDescriptorTypes[m]))); + } + } } void VulkanCreationInfo::DescSetPool::CreateOverflow(VkDevice device, @@ -2014,6 +2278,45 @@ void VulkanCreationInfo::DescSetPool::CreateOverflow(VkDevice device, &poolSizes[0], }; + VkMutableDescriptorTypeCreateInfoEXT mutableCreateInfo = { + VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT, + }; + + // conservative resizing, not every descriptor type will be used but they can't overlap so at + // most we'll see 64 different types + VkDescriptorType mutableTypes[64]; + rdcarray mutableLists; + + if(!mutableBitmasks.empty()) + { + poolInfo.pNext = &mutableCreateInfo; + + mutableLists.resize(poolInfo.poolSizeCount); + + VkDescriptorType *cur = mutableTypes; + for(size_t i = 0; i < mutableBitmasks.size(); i++) + { + // list of descriptors starts here + mutableLists[i].pDescriptorTypes = cur; + mutableLists[i].descriptorTypeCount = 0; + + // loop over every type + for(uint64_t m = 0; m < 64; m++) + { + // skip types not in this bitmask + if(((1ULL << m) & mutableBitmasks[i]) == 0) + continue; + + // for types included, write them into the list and increment + *(cur++) = convert(DescriptorSlotType(m)); + mutableLists[i].descriptorTypeCount++; + } + } + + mutableCreateInfo.mutableDescriptorTypeListCount = poolInfo.poolSizeCount; + mutableCreateInfo.pMutableDescriptorTypeLists = mutableLists.data(); + } + VkDescriptorPool pool; VkResult ret = ObjDisp(device)->CreateDescriptorPool(Unwrap(device), &poolInfo, NULL, &pool); diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index 4845e8b9c..65ef51089 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -122,7 +122,7 @@ struct DescSetLayout // some elements could be untouched. We set stageFlags to 0 so the UI ignores these // elements Binding() - : descriptorType(VK_DESCRIPTOR_TYPE_MAX_ENUM), + : layoutDescType(VK_DESCRIPTOR_TYPE_MAX_ENUM), elemOffset(0), descriptorCount(0), stageFlags(0), @@ -132,7 +132,7 @@ struct DescSetLayout } // move the immutable sampler Binding(Binding &&b) - : descriptorType(b.descriptorType), + : layoutDescType(b.layoutDescType), elemOffset(b.elemOffset), descriptorCount(b.descriptorCount), stageFlags(b.stageFlags), @@ -143,7 +143,7 @@ struct DescSetLayout } // Copy the immutable sampler Binding(const Binding &b) - : descriptorType(b.descriptorType), + : layoutDescType(b.layoutDescType), elemOffset(b.elemOffset), descriptorCount(b.descriptorCount), stageFlags(b.stageFlags), @@ -161,7 +161,7 @@ struct DescSetLayout if(this == &b) return *this; - descriptorType = b.descriptorType; + layoutDescType = b.layoutDescType; elemOffset = b.elemOffset; descriptorCount = b.descriptorCount; stageFlags = b.stageFlags; @@ -175,7 +175,9 @@ struct DescSetLayout return *this; } ~Binding() { SAFE_DELETE_ARRAY(immutableSampler); } - VkDescriptorType descriptorType; + // this is the layout-declared type, but since it may be mutable in most cases this is not used + // - only push descriptors use this + VkDescriptorType layoutDescType; uint32_t elemOffset; uint32_t descriptorCount; VkShaderStageFlags stageFlags : 31; @@ -184,6 +186,9 @@ struct DescSetLayout }; rdcarray bindings; + // parallel array to bindings, with a bitmask of mutable types + rdcarray mutableBitmasks; + uint32_t totalElems; uint32_t dynamicCount; VkDescriptorSetLayoutCreateFlags flags; @@ -194,10 +199,16 @@ struct DescSetLayout // the cummulative stageFlags for all bindings in this layout VkShaderStageFlags anyStageFlags; - bool operator==(const DescSetLayout &other) const; - bool operator!=(const DescSetLayout &other) const { return !(*this == other); } + bool isCompatible(const DescSetLayout &other) const; }; +bool IsValid(bool allowNULLDescriptors, const VkWriteDescriptorSet &write, uint32_t arrayElement); +bool CreateDescriptorWritesForSlotData(WrappedVulkan *vk, rdcarray &writes, + VkDescriptorBufferInfo *&writeScratch, + const DescriptorSetSlot *slots, uint32_t descriptorCount, + VkDescriptorSet set, uint32_t dstBind, + const DescSetLayout::Binding &layoutBind); + struct DescUpdateTemplateApplication { rdcarray bufInfo; @@ -731,6 +742,7 @@ struct VulkanCreationInfo uint32_t maxSets; rdcarray poolSizes; + rdcarray mutableBitmasks; void CreateOverflow(VkDevice device, VulkanResourceManager *resourceMan); diff --git a/renderdoc/driver/vulkan/vk_initstate.cpp b/renderdoc/driver/vulkan/vk_initstate.cpp index 9ff73571f..26304bff6 100644 --- a/renderdoc/driver/vulkan/vk_initstate.cpp +++ b/renderdoc/driver/vulkan/vk_initstate.cpp @@ -999,6 +999,8 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, V WrappedVkRes *res = GetResourceManager()->GetLiveResource(id); ResourceId liveid = GetResourceManager()->GetLiveID(id); + VkDescriptorSet set = (VkDescriptorSet)(uint64_t)res; + const DescSetLayout &layout = m_CreationInfo.m_DescSetLayout[m_DescriptorSetState[liveid].layout]; @@ -1008,9 +1010,37 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, V return true; } + if(!ser.VersionAtLeast(0x15)) + { + // this is from before mutable descriptors, so we serialised the bindings contents above but + // we need to set their types + DescriptorSetSlot *bind = Bindings; + for(uint32_t b = 0; b < (uint32_t)layout.bindings.size(); b++) + { + const DescSetLayout::Binding &layoutBind = layout.bindings[b]; + + uint32_t descriptorCount = layoutBind.descriptorCount; + + if(layoutBind.variableSize) + descriptorCount = m_DescriptorSetState[liveid].data.variableDescriptorCount; + + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + { + bind->type = DescriptorSlotType::InlineBlock; + bind++; + continue; + } + + for(uint32_t d = 0; d < descriptorCount; d++) + { + bind->type = convert(layoutBind.layoutDescType); + bind++; + } + } + } + VkInitialContents initialContents(type, VkInitialContents::DescriptorSet); - initialContents.numDescriptors = (uint32_t)layout.bindings.size(); initialContents.descriptorInfo = new VkDescriptorBufferInfo[NumBindings]; initialContents.inlineInfo = NULL; @@ -1022,30 +1052,23 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, V memcpy(initialContents.inlineData, InlineData.data(), InlineData.size()); } - // if we have partially-valid arrays, we need to split up writes. The worst case will never be - // == number of bindings since that implies all arrays are valid, but it is an upper bound as - // we'll never need more writes than bindings - initialContents.descriptorWrites = new VkWriteDescriptorSet[NumBindings]; - RDCCOMPILE_ASSERT(sizeof(VkDescriptorBufferInfo) >= sizeof(VkDescriptorImageInfo), "Descriptor structs sizes are unexpected, ensure largest size is used"); - VkWriteDescriptorSet *writes = initialContents.descriptorWrites; - VkDescriptorBufferInfo *dstData = initialContents.descriptorInfo; - VkWriteDescriptorSetInlineUniformBlock *dstInline = initialContents.inlineInfo; - DescriptorSetSlot *srcData = Bindings; + rdcarray writes; + VkDescriptorBufferInfo *writeScratch = initialContents.descriptorInfo; + VkWriteDescriptorSetInlineUniformBlock *dstInline = initialContents.inlineInfo; + DescriptorSetSlot *srcBindings = Bindings; byte *srcInlineData = initialContents.inlineData; - // validBinds counts up as we make a valid VkWriteDescriptorSet, so can be used to index into - // writes[] along the way as the 'latest' write. - uint32_t bind = 0; - - for(uint32_t j = 0; j < initialContents.numDescriptors; j++) + for(uint32_t bind = 0; bind < (uint32_t)layout.bindings.size(); bind++) { - uint32_t descriptorCount = layout.bindings[j].descriptorCount; + const DescSetLayout::Binding &layoutBind = layout.bindings[bind]; - if(layout.bindings[j].variableSize) + uint32_t descriptorCount = layoutBind.descriptorCount; + + if(layoutBind.variableSize) descriptorCount = m_DescriptorSetState[liveid].data.variableDescriptorCount; if(descriptorCount == 0) @@ -1053,47 +1076,14 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, V uint32_t inlineSize = 0; - if(layout.bindings[j].descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { inlineSize = descriptorCount; descriptorCount = 1; } - writes[bind].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writes[bind].pNext = NULL; - - // template for this write. We will expand it to include more descriptors as we find valid - // descriptors to update. - writes[bind].dstSet = (VkDescriptorSet)(uint64_t)res; - writes[bind].dstBinding = j; - writes[bind].dstArrayElement = 0; - // descriptor count starts at 0. We increment it as we find valid descriptors - writes[bind].descriptorCount = 0; - writes[bind].descriptorType = layout.bindings[j].descriptorType; - - ResourceId *immutableSamplers = layout.bindings[j].immutableSampler; - - DescriptorSetSlot *src = srcData; - srcData += descriptorCount; - - // will be cast to the appropriate type, we just need to increment - // the dstData pointer by worst case size - VkDescriptorBufferInfo *dstBuffer = dstData; - VkDescriptorImageInfo *dstImage = (VkDescriptorImageInfo *)dstData; - VkBufferView *dstTexelBuffer = (VkBufferView *)dstData; - dstData += descriptorCount; - - RDCCOMPILE_ASSERT( - sizeof(VkDescriptorImageInfo) <= sizeof(VkDescriptorBufferInfo), - "VkDescriptorBufferInfo should be large enough for all descriptor write types"); - RDCCOMPILE_ASSERT( - sizeof(VkBufferView) <= sizeof(VkDescriptorBufferInfo), - "VkDescriptorBufferInfo should be large enough for all descriptor write types"); - - // the correct one will be set below - writes[bind].pBufferInfo = NULL; - writes[bind].pImageInfo = NULL; - writes[bind].pTexelBufferView = NULL; + DescriptorSetSlot *slots = srcBindings; + srcBindings += descriptorCount; // check that the resources we need for this write are present, as some might have been // skipped due to stale descriptor set slots or otherwise unreferenced objects (the @@ -1103,177 +1093,51 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, V // gets a write, or not, in which case we skip. // For the array case we batch up updates as much as possible, iterating along the array and // skipping any invalid descriptors. + // We also use this loop for handling mutable descriptor types, since descriptors in a + // mutable array could have various different types and each contiguous block will need a + // separate write. - if(writes[bind].descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + // inline block can't be mutable and can't be arrayed, handle it directly here + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { // handle inline uniform block specially because the descriptorCount doesn't mean what it // normally means in the write. - dstInline->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK; dstInline->pNext = NULL; - dstInline->pData = srcInlineData + src->inlineOffset; + dstInline->pData = srcInlineData + slots->offset; dstInline->dataSize = inlineSize; - writes[bind].pNext = dstInline; - writes[bind].descriptorCount = inlineSize; - bind++; + VkWriteDescriptorSet write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; + write.pNext = dstInline; + write.dstSet = set; + write.descriptorType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK; + write.dstBinding = bind; + write.descriptorCount = inlineSize; + + writes.push_back(write); dstInline++; } // quick check for slots that were completely uninitialised and so don't have valid data else if(!NULLDescriptorsAllowed() && descriptorCount == 1 && - src->texelBufferView == ResourceId() && src->imageInfo.sampler == ResourceId() && - src->imageInfo.imageView == ResourceId() && src->bufferInfo.buffer == ResourceId()) + slots->resource == ResourceId() && slots->sampler == ResourceId()) { // do nothing - don't increment bind so that the same write descriptor is used next time. continue; } else { - // first we copy the right data over unconditionally - switch(writes[bind].descriptorType) - { - case VK_DESCRIPTOR_TYPE_SAMPLER: - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - { - for(uint32_t d = 0; d < descriptorCount; d++) - { - if(writes[bind].descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER && - GetResourceManager()->HasLiveResource(src[d].imageInfo.imageView)) - dstImage[d].imageView = - GetResourceManager()->GetLiveHandle(src[d].imageInfo.imageView); - else - dstImage[d].imageView = VK_NULL_HANDLE; - - if((writes[bind].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || - writes[bind].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) && - GetResourceManager()->HasLiveResource(src[d].imageInfo.sampler)) - dstImage[d].sampler = - GetResourceManager()->GetLiveHandle(src[d].imageInfo.sampler); - else - dstImage[d].sampler = VK_NULL_HANDLE; - - dstImage[d].imageLayout = src[d].imageInfo.imageLayout; - } - - // if we're not updating a SAMPLER descriptor fill in immutable samplers so that our - // validity checking doesn't have to look them up. - if(immutableSamplers && writes[bind].descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER) - { - for(uint32_t d = 0; d < descriptorCount; d++) - dstImage[d].sampler = - GetResourceManager()->GetCurrentHandle(immutableSamplers[d]); - } - - writes[bind].pImageInfo = dstImage; - // NULL the others - dstBuffer = NULL; - dstTexelBuffer = NULL; - break; - } - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - { - for(uint32_t d = 0; d < descriptorCount; d++) - { - if(GetResourceManager()->HasLiveResource(src[d].texelBufferView)) - dstTexelBuffer[d] = - GetResourceManager()->GetLiveHandle(src[d].texelBufferView); - else - dstTexelBuffer[d] = VK_NULL_HANDLE; - } - - writes[bind].pTexelBufferView = dstTexelBuffer; - // NULL the others - dstBuffer = NULL; - dstImage = NULL; - break; - } - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - { - for(uint32_t d = 0; d < descriptorCount; d++) - { - if(GetResourceManager()->HasLiveResource(src[d].bufferInfo.buffer)) - dstBuffer[d].buffer = - GetResourceManager()->GetLiveHandle(src[d].bufferInfo.buffer); - else - dstBuffer[d].buffer = VK_NULL_HANDLE; - dstBuffer[d].offset = src[d].bufferInfo.offset; - dstBuffer[d].range = src[d].bufferInfo.range; - } - - writes[bind].pBufferInfo = dstBuffer; - // NULL the others - dstImage = NULL; - dstTexelBuffer = NULL; - break; - } - default: - { - RDCERR("Unexpected descriptor type %d", writes[bind].descriptorType); - ret = false; - } - } - - // iterate over all the descriptors coalescing valid writes. At all times writes[bind] is - // the 'current' batched update - for(uint32_t d = 0; d < descriptorCount; d++) - { - // is this array element in the write valid? Note that below when we encounter an - // invalid write, the next one starts from a later point in the array, so we need to - // check relative to the dstArrayElement - if(IsValid(NULLDescriptorsAllowed(), writes[bind], d - writes[bind].dstArrayElement)) - { - // if this descriptor is valid, just increment the number of descriptors. The data - // and dstArrayElement is pointing to the start of the valid range - writes[bind].descriptorCount++; - } - else - { - // if this descriptor is *invalid* we must skip it. First see if we have some - // previously valid range and commit it - if(writes[bind].descriptorCount) - { - bind++; - - // copy over the previous data for the sake of the things that won't be reset below - writes[bind] = writes[bind - 1]; - } - - // now offset to the next potentially valid descriptor. Note that at the end of the - // iteration there is no next descriptor so these pointer values will be off the end - // of the array, but descriptorCount will be 0 so this will be treated as invalid and - // skipped - writes[bind].dstArrayElement = d + 1; - - // start counting from 0 again - writes[bind].descriptorCount = 0; - - // offset the array being used - if(dstBuffer) - writes[bind].pBufferInfo = dstBuffer + d + 1; - else if(dstImage) - writes[bind].pImageInfo = dstImage + d + 1; - else if(dstTexelBuffer) - writes[bind].pTexelBufferView = dstTexelBuffer + d + 1; - } - } - - // after the loop there may be a valid write which hasn't been accounted for. If the - // current write has a descriptor count that means it has some descriptors, so - // increment i and validBinds so that it's accounted for. - if(writes[bind].descriptorCount) - bind++; + bool success = CreateDescriptorWritesForSlotData(this, writes, writeScratch, slots, + descriptorCount, set, bind, layoutBind); + if(!success) + ret = false; } } - initialContents.numDescriptors = bind; + initialContents.descriptorWrites = new VkWriteDescriptorSet[writes.size()]; + memcpy(initialContents.descriptorWrites, writes.data(), writes.byteSize()); + + initialContents.numDescriptors = (uint32_t)writes.size(); GetResourceManager()->SetInitialContents(id, initialContents); } @@ -1732,8 +1596,8 @@ void WrappedVulkan::Apply_InitialState(WrappedVkRes *live, const VkInitialConten VkWriteDescriptorSetInlineUniformBlock *inlineWrite = (VkWriteDescriptorSetInlineUniformBlock *)FindNextStruct( &writes[i], VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK); - memcpy(inlineData.data() + bind->inlineOffset + writes[i].dstArrayElement, - inlineWrite->pData, inlineWrite->dataSize); + memcpy(inlineData.data() + bind->offset + writes[i].dstArrayElement, inlineWrite->pData, + inlineWrite->dataSize); continue; } @@ -1744,20 +1608,20 @@ void WrappedVulkan::Apply_InitialState(WrappedVkRes *live, const VkInitialConten if(writes[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || writes[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { - bind[idx].texelBufferView = GetResID(writes[i].pTexelBufferView[d]); + bind[idx].SetTexelBuffer(writes[i].descriptorType, GetResID(writes[i].pTexelBufferView[d])); } else if(writes[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || writes[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || writes[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || writes[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) { - bind[idx].bufferInfo.SetFrom(writes[i].pBufferInfo[d]); + bind[idx].SetBuffer(writes[i].descriptorType, writes[i].pBufferInfo[d]); } else { // we don't ever pass invalid parameters so we can unconditionally set both. Invalid // elements are set to VK_NULL_HANDLE which is safe - bind[idx].imageInfo.SetFrom(writes[i].pImageInfo[d], true, true); + bind[idx].SetImage(writes[i].descriptorType, writes[i].pImageInfo[d], true); } } } diff --git a/renderdoc/driver/vulkan/vk_next_chains.cpp b/renderdoc/driver/vulkan/vk_next_chains.cpp index d66fe01aa..f8c3d5fdc 100644 --- a/renderdoc/driver/vulkan/vk_next_chains.cpp +++ b/renderdoc/driver/vulkan/vk_next_chains.cpp @@ -195,6 +195,8 @@ static void AppendModifiedChainedStruct(byte *&tempMem, VkStruct *outputStruct, COPY_STRUCT(VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT, VkMultisamplePropertiesEXT); \ COPY_STRUCT(VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT, \ VkMultisampledRenderToSingleSampledInfoEXT); \ + COPY_STRUCT(VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT, \ + VkMutableDescriptorTypeCreateInfoEXT); \ COPY_STRUCT(VK_STRUCTURE_TYPE_PERFORMANCE_COUNTER_DESCRIPTION_KHR, \ VkPerformanceCounterDescriptionKHR); \ COPY_STRUCT(VK_STRUCTURE_TYPE_PERFORMANCE_COUNTER_KHR, VkPerformanceCounterKHR); \ @@ -326,6 +328,8 @@ static void AppendModifiedChainedStruct(byte *&tempMem, VkStruct *outputStruct, VkPhysicalDeviceMultiviewFeatures); \ COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, \ VkPhysicalDeviceMultiviewProperties); \ + COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT, \ + VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT); \ COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT, \ VkPhysicalDeviceLineRasterizationFeaturesEXT) \ COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_PROPERTIES_EXT, \ @@ -816,7 +820,6 @@ static void AppendModifiedChainedStruct(byte *&tempMem, VkStruct *outputStruct, case VK_STRUCTURE_TYPE_MICROMAP_CREATE_INFO_EXT: \ case VK_STRUCTURE_TYPE_MICROMAP_VERSION_INFO_EXT: \ case VK_STRUCTURE_TYPE_MULTIVIEW_PER_VIEW_ATTRIBUTES_INFO_NVX: \ - case VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_VALVE: \ case VK_STRUCTURE_TYPE_OPTICAL_FLOW_EXECUTE_INFO_NV: \ case VK_STRUCTURE_TYPE_OPTICAL_FLOW_IMAGE_FORMAT_INFO_NV: \ case VK_STRUCTURE_TYPE_OPTICAL_FLOW_IMAGE_FORMAT_PROPERTIES_NV: \ @@ -870,7 +873,6 @@ static void AppendModifiedChainedStruct(byte *&tempMem, VkStruct *outputStruct, case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX: \ - case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_VALVE: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_FEATURES_EXT: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPACITY_MICROMAP_PROPERTIES_EXT: \ diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index a580552fa..75fedbe97 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -1915,43 +1915,44 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) for(size_t i = 0; i < srcs[p]->size(); i++) { - ResourceId src = (*srcs[p])[i].descSet; + ResourceId sourceSet = (*srcs[p])[i].descSet; const uint32_t *srcOffset = (*srcs[p])[i].offsets.begin(); - VKPipe::DescriptorSet &dst = (*dsts[p])[i]; + VKPipe::DescriptorSet &destSet = (*dsts[p])[i]; - if(src == ResourceId()) + if(sourceSet == ResourceId()) { - dst.inlineData.clear(); - dst.descriptorSetResourceId = ResourceId(); - dst.pushDescriptor = false; - dst.layoutResourceId = ResourceId(); - dst.bindings.clear(); + destSet.inlineData.clear(); + destSet.descriptorSetResourceId = ResourceId(); + destSet.pushDescriptor = false; + destSet.layoutResourceId = ResourceId(); + destSet.bindings.clear(); continue; } - dst.inlineData = m_pDriver->m_DescriptorSetState[src].data.inlineBytes; + destSet.inlineData = m_pDriver->m_DescriptorSetState[sourceSet].data.inlineBytes; curBind.bindset = (uint32_t)i; - ResourceId layoutId = m_pDriver->m_DescriptorSetState[src].layout; + ResourceId layoutId = m_pDriver->m_DescriptorSetState[sourceSet].layout; // push descriptors don't have a real descriptor set backing them if(c.m_DescSetLayout[layoutId].flags & VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR) { - dst.descriptorSetResourceId = ResourceId(); - dst.pushDescriptor = true; + destSet.descriptorSetResourceId = ResourceId(); + destSet.pushDescriptor = true; } else { - dst.descriptorSetResourceId = rm->GetOriginalID(src); - dst.pushDescriptor = false; + destSet.descriptorSetResourceId = rm->GetOriginalID(sourceSet); + destSet.pushDescriptor = false; } - dst.layoutResourceId = rm->GetOriginalID(layoutId); - dst.bindings.resize(m_pDriver->m_DescriptorSetState[src].data.binds.size()); - for(size_t b = 0; b < m_pDriver->m_DescriptorSetState[src].data.binds.size(); b++) + destSet.layoutResourceId = rm->GetOriginalID(layoutId); + destSet.bindings.resize(m_pDriver->m_DescriptorSetState[sourceSet].data.binds.size()); + for(size_t b = 0; b < m_pDriver->m_DescriptorSetState[sourceSet].data.binds.size(); b++) { - DescriptorSetSlot *info = m_pDriver->m_DescriptorSetState[src].data.binds[b]; + DescriptorSetSlot *sourceSlots = m_pDriver->m_DescriptorSetState[sourceSet].data.binds[b]; + VKPipe::DescriptorBinding &destSlots = destSet.bindings[b]; const DescSetLayout::Binding &layoutBind = c.m_DescSetLayout[layoutId].bindings[b]; curBind.bind = (uint32_t)b; @@ -1959,60 +1960,24 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) uint32_t descriptorCount = layoutBind.descriptorCount; if(layoutBind.variableSize) - descriptorCount = m_pDriver->m_DescriptorSetState[src].data.variableDescriptorCount; + descriptorCount = m_pDriver->m_DescriptorSetState[sourceSet].data.variableDescriptorCount; - dst.bindings[b].descriptorCount = descriptorCount; + destSlots.descriptorCount = descriptorCount; - dst.bindings[b].stageFlags = (ShaderStageMask)layoutBind.stageFlags; - switch(layoutBind.descriptorType) + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + destSlots.descriptorCount = 1; + + destSlots.stageFlags = (ShaderStageMask)layoutBind.stageFlags; + + destSlots.firstUsedIndex = -1; + destSlots.lastUsedIndex = -1; + destSlots.dynamicallyUsedCount = 0; + + destSlots.binds.resize(destSlots.descriptorCount); + for(uint32_t a = 0; a < destSlots.descriptorCount; a++) { - case VK_DESCRIPTOR_TYPE_SAMPLER: dst.bindings[b].type = BindType::Sampler; break; - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - dst.bindings[b].type = BindType::ImageSampler; - break; - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - dst.bindings[b].type = BindType::ReadOnlyImage; - break; - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - dst.bindings[b].type = BindType::ReadWriteImage; - break; - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - dst.bindings[b].type = BindType::ReadOnlyTBuffer; - break; - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - dst.bindings[b].type = BindType::ReadWriteTBuffer; - break; - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - dst.bindings[b].type = BindType::ConstantBuffer; - break; - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: - dst.bindings[b].type = BindType::ReadWriteBuffer; - break; - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - dst.bindings[b].type = BindType::ConstantBuffer; - break; - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - dst.bindings[b].type = BindType::ReadWriteBuffer; - break; - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - dst.bindings[b].type = BindType::InputAttachment; - break; - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: - dst.bindings[b].descriptorCount = 1; - dst.bindings[b].type = BindType::ConstantBuffer; - break; - case VK_DESCRIPTOR_TYPE_MAX_ENUM: dst.bindings[b].type = BindType::Unknown; break; - default: dst.bindings[b].type = BindType::Unknown; RDCERR("Unexpected descriptor type"); - } - - dst.bindings[b].firstUsedIndex = -1; - dst.bindings[b].lastUsedIndex = -1; - dst.bindings[b].dynamicallyUsedCount = 0; - - dst.bindings[b].binds.resize(dst.bindings[b].descriptorCount); - for(uint32_t a = 0; a < dst.bindings[b].descriptorCount; a++) - { - VKPipe::BindingElement &dstel = dst.bindings[b].binds[a]; + const DescriptorSetSlot &srcel = sourceSlots[a]; + VKPipe::BindingElement &dstel = destSlots.binds[a]; // clear it so we don't have to manually reset all elements back to normal memset(&dstel, 0, sizeof(dstel)); @@ -2021,7 +1986,7 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) // if we have a list of used binds, and this is an array descriptor (so would be // expected to be in the list), check it for dynamic usage. - if(dst.bindings[b].descriptorCount > 1 && hasUsedBinds) + if(destSlots.descriptorCount > 1 && hasUsedBinds) { // if we exhausted the list, all other elements are unused if(usedBindsSize == 0) @@ -2061,32 +2026,70 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) if(dstel.dynamicallyUsed) { - dst.bindings[b].dynamicallyUsedCount++; + destSlots.dynamicallyUsedCount++; // we iterate in forward order, so we can unconditinoally set the last bind to the // current one, and only set the first bind if we haven't encountered one before - dst.bindings[b].lastUsedIndex = a; + destSlots.lastUsedIndex = a; - if(dst.bindings[b].firstUsedIndex < 0) - dst.bindings[b].firstUsedIndex = a; + if(destSlots.firstUsedIndex < 0) + destSlots.firstUsedIndex = a; + } + + DescriptorSlotType descriptorType = srcel.type; + + // immutable samplers cannot be used with mutable descriptors, so if we have immutable + // samplers set the type from the layout. That way even if the descriptor is never + // written we still process immutable samplers properly. + if(layoutBind.immutableSampler) + descriptorType = convert(layoutBind.layoutDescType); + + switch(descriptorType) + { + case DescriptorSlotType::Sampler: dstel.type = BindType::Sampler; break; + case DescriptorSlotType::CombinedImageSampler: + dstel.type = BindType::ImageSampler; + break; + case DescriptorSlotType::SampledImage: dstel.type = BindType::ReadOnlyImage; break; + case DescriptorSlotType::StorageImage: dstel.type = BindType::ReadWriteImage; break; + case DescriptorSlotType::UniformTexelBuffer: + dstel.type = BindType::ReadOnlyTBuffer; + break; + case DescriptorSlotType::StorageTexelBuffer: + dstel.type = BindType::ReadWriteTBuffer; + break; + case DescriptorSlotType::UniformBuffer: dstel.type = BindType::ConstantBuffer; break; + case DescriptorSlotType::StorageBuffer: dstel.type = BindType::ReadWriteBuffer; break; + case DescriptorSlotType::UniformBufferDynamic: + dstel.type = BindType::ConstantBuffer; + break; + case DescriptorSlotType::StorageBufferDynamic: + dstel.type = BindType::ReadWriteBuffer; + break; + case DescriptorSlotType::InputAttachment: + dstel.type = BindType::InputAttachment; + break; + case DescriptorSlotType::InlineBlock: dstel.type = BindType::ConstantBuffer; break; + case DescriptorSlotType::Unwritten: + case DescriptorSlotType::Count: dstel.type = BindType::Unknown; break; } // first handle the sampler separately because it might be in a combined descriptor - if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || - layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) + if(descriptorType == DescriptorSlotType::Sampler || + descriptorType == DescriptorSlotType::CombinedImageSampler) { if(layoutBind.immutableSampler) { - dst.bindings[b].binds[a].samplerResourceId = layoutBind.immutableSampler[a]; - dst.bindings[b].binds[a].immutableSampler = true; + destSlots.binds[a].samplerResourceId = layoutBind.immutableSampler[a]; + destSlots.binds[a].immutableSampler = true; } - else if(info[a].imageInfo.sampler != ResourceId()) + else if(srcel.sampler != ResourceId()) { - dst.bindings[b].binds[a].samplerResourceId = info[a].imageInfo.sampler; + destSlots.binds[a].samplerResourceId = srcel.sampler; } - if(dst.bindings[b].binds[a].samplerResourceId != ResourceId()) + if(destSlots.binds[a].samplerResourceId != ResourceId()) { - VKPipe::BindingElement &el = dst.bindings[b].binds[a]; + VKPipe::BindingElement &el = destSlots.binds[a]; const VulkanCreationInfo::Sampler &sampl = c.m_Sampler[el.samplerResourceId]; ResourceId liveId = el.samplerResourceId; @@ -2138,99 +2141,96 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) } // now look at the 'base' type. Sampler is excluded from these ifs - if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || - layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || - layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT || - layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) + if(descriptorType == DescriptorSlotType::SampledImage || + descriptorType == DescriptorSlotType::CombinedImageSampler || + descriptorType == DescriptorSlotType::InputAttachment || + descriptorType == DescriptorSlotType::StorageImage) { - ResourceId viewid = info[a].imageInfo.imageView; + ResourceId viewid = srcel.resource; if(viewid != ResourceId()) { - dst.bindings[b].binds[a].viewResourceId = rm->GetOriginalID(viewid); - dst.bindings[b].binds[a].resourceResourceId = + destSlots.binds[a].viewResourceId = rm->GetOriginalID(viewid); + destSlots.binds[a].resourceResourceId = rm->GetOriginalID(c.m_ImageView[viewid].image); - dst.bindings[b].binds[a].viewFormat = - MakeResourceFormat(c.m_ImageView[viewid].format); + destSlots.binds[a].viewFormat = MakeResourceFormat(c.m_ImageView[viewid].format); - Convert(dst.bindings[b].binds[a].swizzle, c.m_ImageView[viewid].componentMapping); - dst.bindings[b].binds[a].firstMip = c.m_ImageView[viewid].range.baseMipLevel; - dst.bindings[b].binds[a].firstSlice = c.m_ImageView[viewid].range.baseArrayLayer; - dst.bindings[b].binds[a].numMips = c.m_ImageView[viewid].range.levelCount; - dst.bindings[b].binds[a].numSlices = c.m_ImageView[viewid].range.layerCount; + Convert(destSlots.binds[a].swizzle, c.m_ImageView[viewid].componentMapping); + destSlots.binds[a].firstMip = c.m_ImageView[viewid].range.baseMipLevel; + destSlots.binds[a].firstSlice = c.m_ImageView[viewid].range.baseArrayLayer; + destSlots.binds[a].numMips = c.m_ImageView[viewid].range.levelCount; + destSlots.binds[a].numSlices = c.m_ImageView[viewid].range.layerCount; // temporary hack, store image layout enum in byteOffset as it's not used for images - dst.bindings[b].binds[a].byteOffset = info[a].imageInfo.imageLayout; + destSlots.binds[a].byteOffset = convert(srcel.imageLayout); } else { - dst.bindings[b].binds[a].viewResourceId = ResourceId(); - dst.bindings[b].binds[a].resourceResourceId = ResourceId(); - dst.bindings[b].binds[a].firstMip = 0; - dst.bindings[b].binds[a].firstSlice = 0; - dst.bindings[b].binds[a].numMips = 1; - dst.bindings[b].binds[a].numSlices = 1; + destSlots.binds[a].viewResourceId = ResourceId(); + destSlots.binds[a].resourceResourceId = ResourceId(); + destSlots.binds[a].firstMip = 0; + destSlots.binds[a].firstSlice = 0; + destSlots.binds[a].numMips = 1; + destSlots.binds[a].numSlices = 1; } } - else if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER || - layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER) + else if(descriptorType == DescriptorSlotType::UniformTexelBuffer || + descriptorType == DescriptorSlotType::StorageTexelBuffer) { - ResourceId viewid = info[a].texelBufferView; + ResourceId viewid = srcel.resource; if(viewid != ResourceId()) { - dst.bindings[b].binds[a].viewResourceId = rm->GetOriginalID(viewid); - dst.bindings[b].binds[a].resourceResourceId = + destSlots.binds[a].viewResourceId = rm->GetOriginalID(viewid); + destSlots.binds[a].resourceResourceId = rm->GetOriginalID(c.m_BufferView[viewid].buffer); - dst.bindings[b].binds[a].byteOffset = c.m_BufferView[viewid].offset; - dst.bindings[b].binds[a].viewFormat = - MakeResourceFormat(c.m_BufferView[viewid].format); - dst.bindings[b].binds[a].byteSize = c.m_BufferView[viewid].size; + destSlots.binds[a].byteOffset = c.m_BufferView[viewid].offset; + destSlots.binds[a].viewFormat = MakeResourceFormat(c.m_BufferView[viewid].format); + destSlots.binds[a].byteSize = c.m_BufferView[viewid].size; } else { - dst.bindings[b].binds[a].viewResourceId = ResourceId(); - dst.bindings[b].binds[a].resourceResourceId = ResourceId(); - dst.bindings[b].binds[a].byteOffset = 0; - dst.bindings[b].binds[a].byteSize = 0; + destSlots.binds[a].viewResourceId = ResourceId(); + destSlots.binds[a].resourceResourceId = ResourceId(); + destSlots.binds[a].byteOffset = 0; + destSlots.binds[a].byteSize = 0; } } - else if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + else if(descriptorType == DescriptorSlotType::InlineBlock) { - dst.bindings[b].binds[a].viewResourceId = ResourceId(); - dst.bindings[b].binds[a].resourceResourceId = ResourceId(); - dst.bindings[b].binds[a].inlineBlock = true; - dst.bindings[b].binds[a].byteOffset = info[a].inlineOffset; - dst.bindings[b].binds[a].byteSize = descriptorCount; + destSlots.binds[a].viewResourceId = ResourceId(); + destSlots.binds[a].resourceResourceId = ResourceId(); + destSlots.binds[a].inlineBlock = true; + destSlots.binds[a].byteOffset = srcel.offset; + destSlots.binds[a].byteSize = descriptorCount; } - else if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || - layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC || - layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || - layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) + else if(descriptorType == DescriptorSlotType::StorageBuffer || + descriptorType == DescriptorSlotType::StorageBufferDynamic || + descriptorType == DescriptorSlotType::UniformBuffer || + descriptorType == DescriptorSlotType::UniformBufferDynamic) { - dst.bindings[b].binds[a].viewResourceId = ResourceId(); + destSlots.binds[a].viewResourceId = ResourceId(); - if(info[a].bufferInfo.buffer != ResourceId()) - dst.bindings[b].binds[a].resourceResourceId = - rm->GetOriginalID(info[a].bufferInfo.buffer); + if(srcel.resource != ResourceId()) + destSlots.binds[a].resourceResourceId = rm->GetOriginalID(srcel.resource); - dst.bindings[b].binds[a].byteOffset = info[a].bufferInfo.offset; - if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC || - layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) + destSlots.binds[a].byteOffset = srcel.offset; + if(descriptorType == DescriptorSlotType::StorageBufferDynamic || + descriptorType == DescriptorSlotType::UniformBufferDynamic) { - dst.bindings[b].binds[a].byteOffset += *srcOffset; + destSlots.binds[a].byteOffset += *srcOffset; srcOffset++; } - dst.bindings[b].binds[a].byteSize = info[a].bufferInfo.range; + destSlots.binds[a].byteSize = srcel.GetRange(); } } // if no bindings were set these will still be negative. Set them to something sensible. - if(dst.bindings[b].firstUsedIndex < 0) + if(destSlots.firstUsedIndex < 0) { - dst.bindings[b].firstUsedIndex = 0; - dst.bindings[b].lastUsedIndex = 0x7fffffff; + destSlots.firstUsedIndex = 0; + destSlots.lastUsedIndex = 0x7fffffff; } } } @@ -2339,13 +2339,12 @@ void VulkanReplay::FillCBufferVariables(ResourceId pipeline, ResourceId shader, const DescSetLayout::Binding &layoutBind = m_pDriver->m_CreationInfo.m_DescSetLayout[layoutId].bindings[bind.bind]; - if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { bytebuf inlineData; - inlineData.assign( - setData.data.inlineBytes.data() + setData.data.binds[bind.bind]->inlineOffset, - layoutBind.variableSize ? setData.data.variableDescriptorCount - : layoutBind.descriptorCount); + inlineData.assign(setData.data.inlineBytes.data() + setData.data.binds[bind.bind]->offset, + layoutBind.variableSize ? setData.data.variableDescriptorCount + : layoutBind.descriptorCount); StandardFillCBufferVariables(refl.resourceId, c.variables, outvars, inlineData); return; } diff --git a/renderdoc/driver/vulkan/vk_serialise.cpp b/renderdoc/driver/vulkan/vk_serialise.cpp index 3c51210df..6d4a84160 100644 --- a/renderdoc/driver/vulkan/vk_serialise.cpp +++ b/renderdoc/driver/vulkan/vk_serialise.cpp @@ -739,6 +739,12 @@ SERIALISE_VK_HANDLES(); VkPhysicalDeviceMemoryPriorityFeaturesEXT) \ PNEXT_STRUCT(VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT, VkMemoryPriorityAllocateInfoEXT) \ \ + /* VK_EXT_mutable_descriptor_type */ \ + PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT, \ + VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT) \ + PNEXT_STRUCT(VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT, \ + VkMutableDescriptorTypeCreateInfoEXT) \ + \ /* VK_EXT_pci_bus_info */ \ PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT, \ VkPhysicalDevicePCIBusInfoPropertiesEXT) \ @@ -1439,10 +1445,6 @@ SERIALISE_VK_HANDLES(); PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT) \ PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT) \ \ - /* VK_EXT_mutable_descriptor_type */ \ - PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT) \ - PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT) \ - \ /* VK_EXT_non_seamless_cube_map */ \ PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT) \ \ @@ -4717,8 +4719,27 @@ void Deserialise(const VkDebugUtilsMessengerCreateInfoEXT &el) DeserialiseNext(el.pNext); } -// this isn't a real vulkan type, it's our own "anything that could be in a descriptor" -// structure that +struct DescriptorSetSlotBufferInfo +{ + ResourceId buffer; + VkDeviceSize offset; + VkDeviceSize range; +}; + +struct DescriptorSetSlotImageInfo +{ + ResourceId sampler; + ResourceId imageView; + VkImageLayout imageLayout; +}; + +DECLARE_REFLECTION_STRUCT(DescriptorSetSlotBufferInfo); +DECLARE_REFLECTION_STRUCT(DescriptorSetSlotImageInfo); + +// this is only kept for legacy reasons, before support for mutable descriptors. At that time +// the type was known via the layout and not a member of the struct. We can serialise these into the +// struct since there's no ambiguity, only one is expected to have actual data since they were +// strongly typed, we should not encounter multiple things wanting to go in the 'resource' member template void DoSerialise(SerialiserType &ser, DescriptorSetSlotImageInfo &el) { @@ -4747,17 +4768,93 @@ void DoSerialise(SerialiserType &ser, DescriptorSetSlot &el) ser.SetStructArg( uint64_t(VkDescriptorImageInfoValidity::Sampler | VkDescriptorImageInfoValidity::ImageView)); - SERIALISE_MEMBER(bufferInfo).TypedAs("VkDescriptorBufferInfo"_lit); - SERIALISE_MEMBER(imageInfo).TypedAs("VkDescriptorImageInfo"_lit); - SERIALISE_MEMBER(texelBufferView).TypedAs("VkBufferView"_lit); + if(ser.VersionAtLeast(0x15)) + { + // mutable descriptor path - if(ser.VersionAtLeast(0x12)) - { - SERIALISE_MEMBER(inlineOffset).Named("InlineDataOffset"_lit); + // serialise the type as VkDescriptorType + VkDescriptorType type = convert(el.type); + SERIALISE_ELEMENT(type); + el.type = convert(type); + + // serialise sampler, if the type needs it + if(el.type == DescriptorSlotType::Sampler || el.type == DescriptorSlotType::CombinedImageSampler) + { + SERIALISE_MEMBER(sampler); + } + + // almost all types have a resource, serialise that + if(el.type != DescriptorSlotType::Unwritten && el.type != DescriptorSlotType::InlineBlock && + el.type != DescriptorSlotType::Count) + { + SERIALISE_MEMBER(resource); + } + + // serialise image layout, for image types + if(el.type == DescriptorSlotType::CombinedImageSampler || + el.type == DescriptorSlotType::SampledImage || el.type == DescriptorSlotType::StorageImage || + el.type == DescriptorSlotType::InputAttachment) + { + VkImageLayout imageLayout = convert(el.imageLayout); + SERIALISE_ELEMENT(imageLayout); + el.imageLayout = convert(imageLayout); + } + + // serialise buffer range, for buffer types and inline block + if(el.type == DescriptorSlotType::UniformBuffer || + el.type == DescriptorSlotType::UniformBufferDynamic || + el.type == DescriptorSlotType::StorageBuffer || + el.type == DescriptorSlotType::StorageBufferDynamic || + el.type == DescriptorSlotType::InlineBlock) + { + VkDeviceSize offset = el.offset; + VkDeviceSize range = el.GetRange(); + SERIALISE_ELEMENT(offset); + SERIALISE_ELEMENT(range); + el.offset = offset; + el.range = range; + } } - else if(ser.IsReading()) + else { - el.inlineOffset = 0; + DescriptorSetSlotBufferInfo bufferInfo; + DescriptorSetSlotImageInfo imageInfo; + ResourceId texelBufferView; + SERIALISE_ELEMENT(bufferInfo).TypedAs("VkDescriptorBufferInfo"_lit); + SERIALISE_ELEMENT(imageInfo).TypedAs("VkDescriptorImageInfo"_lit); + SERIALISE_ELEMENT(texelBufferView).TypedAs("VkBufferView"_lit); + + uint32_t inlineOffset = 0; + if(ser.VersionAtLeast(0x12)) + { + SERIALISE_ELEMENT(inlineOffset).Named("InlineDataOffset"_lit); + } + + // after reading that in, now figure out how to fill in the slot. + if(texelBufferView != ResourceId()) + { + el.resource = texelBufferView; + } + else if(bufferInfo.buffer != ResourceId()) + { + el.resource = bufferInfo.buffer; + el.offset = bufferInfo.offset; + el.range = bufferInfo.range; + } + else if(imageInfo.imageView != ResourceId() || imageInfo.sampler != ResourceId()) + { + el.resource = imageInfo.imageView; + el.sampler = imageInfo.sampler; + el.imageLayout = convert(imageInfo.imageLayout); + } + else + { + // this isn't quite scientific, it could be a descriptor of another type with no (valid) + // contents. But in that case it's safe to set the inline offset which will be zero anyway. In + // the calling code that serialised this, we'll be filling in the type from the layout. + // if this IS an inline block then we'll need to set this. + el.offset = inlineOffset; + } } } @@ -9178,6 +9275,49 @@ void Deserialise(const VkMemoryPriorityAllocateInfoEXT &el) DeserialiseNext(el.pNext); } +template +void DoSerialise(SerialiserType &ser, VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT &el) +{ + RDCASSERT(ser.IsReading() || + el.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT); + SerialiseNext(ser, el.sType, el.pNext); + + SERIALISE_MEMBER(mutableDescriptorType); +} + +template <> +void Deserialise(const VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT &el) +{ + DeserialiseNext(el.pNext); +} + +template +void DoSerialise(SerialiserType &ser, VkMutableDescriptorTypeListEXT &el) +{ + SERIALISE_MEMBER(descriptorTypeCount); + SERIALISE_MEMBER_ARRAY(pDescriptorTypes, descriptorTypeCount); +} + +template +void DoSerialise(SerialiserType &ser, VkMutableDescriptorTypeCreateInfoEXT &el) +{ + RDCASSERT(ser.IsReading() || el.sType == VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT); + SerialiseNext(ser, el.sType, el.pNext); + + SERIALISE_MEMBER(mutableDescriptorTypeListCount); + SERIALISE_MEMBER_ARRAY(pMutableDescriptorTypeLists, mutableDescriptorTypeListCount); +} + +template <> +void Deserialise(const VkMutableDescriptorTypeCreateInfoEXT &el) +{ + DeserialiseNext(el.pNext); + + for(uint32_t i = 0; i < el.mutableDescriptorTypeListCount; i++) + delete[] el.pMutableDescriptorTypeLists[i].pDescriptorTypes; + delete[] el.pMutableDescriptorTypeLists; +} + template void DoSerialise(SerialiserType &ser, VkPhysicalDevicePCIBusInfoPropertiesEXT &el) { @@ -10620,8 +10760,8 @@ INSTANTIATE_SERIALISE_TYPE(VkCalibratedTimestampInfoEXT); INSTANTIATE_SERIALISE_TYPE(VkCommandBufferAllocateInfo); INSTANTIATE_SERIALISE_TYPE(VkCommandBufferBeginInfo); INSTANTIATE_SERIALISE_TYPE(VkCommandBufferInheritanceConditionalRenderingInfoEXT); -INSTANTIATE_SERIALISE_TYPE(VkCommandBufferInheritanceRenderingInfo); INSTANTIATE_SERIALISE_TYPE(VkCommandBufferInheritanceInfo); +INSTANTIATE_SERIALISE_TYPE(VkCommandBufferInheritanceRenderingInfo); INSTANTIATE_SERIALISE_TYPE(VkCommandBufferSubmitInfo); INSTANTIATE_SERIALISE_TYPE(VkCommandPoolCreateInfo); INSTANTIATE_SERIALISE_TYPE(VkComputePipelineCreateInfo); @@ -10736,8 +10876,9 @@ INSTANTIATE_SERIALISE_TYPE(VkMemoryGetFdInfoKHR); INSTANTIATE_SERIALISE_TYPE(VkMemoryOpaqueCaptureAddressAllocateInfo); INSTANTIATE_SERIALISE_TYPE(VkMemoryPriorityAllocateInfoEXT); INSTANTIATE_SERIALISE_TYPE(VkMemoryRequirements2); -INSTANTIATE_SERIALISE_TYPE(VkMultisamplePropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkMultisampledRenderToSingleSampledInfoEXT); +INSTANTIATE_SERIALISE_TYPE(VkMultisamplePropertiesEXT); +INSTANTIATE_SERIALISE_TYPE(VkMutableDescriptorTypeCreateInfoEXT); INSTANTIATE_SERIALISE_TYPE(VkPastPresentationTimingGOOGLE); INSTANTIATE_SERIALISE_TYPE(VkPerformanceCounterDescriptionKHR); INSTANTIATE_SERIALISE_TYPE(VkPerformanceCounterKHR); @@ -10746,6 +10887,7 @@ INSTANTIATE_SERIALISE_TYPE(VkPhysicalDevice16BitStorageFeatures); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDevice4444FormatsFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDevice8BitStorageFeatures); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceASTCDecodeFeaturesEXT) +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceBufferDeviceAddressFeatures); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceBufferDeviceAddressFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceCoherentMemoryFeaturesAMD); @@ -10763,31 +10905,31 @@ INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceDescriptorIndexingProperties) INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceDiscardRectanglePropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceDriverProperties); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceDynamicRenderingFeatures); -INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceExtendedDynamicState2FeaturesEXT); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceExternalBufferInfo); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceExternalFenceInfo); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceExternalImageFormatInfo); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceExternalSemaphoreInfo); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFeatures2); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFloatControlsProperties); -INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapFeaturesEXT); -INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapPropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMap2FeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMap2PropertiesEXT); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapOffsetPropertiesQCOM); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentDensityMapPropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRateFeaturesKHR); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRateKHR); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRatePropertiesKHR); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceGroupProperties); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceHostQueryResetFeatures); -INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRateKHR); -INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRateFeaturesKHR); -INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceFragmentShadingRatePropertiesKHR); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceIDProperties); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceImageFormatInfo2); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceImagelessFramebufferFeatures); @@ -10805,6 +10947,7 @@ INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMemoryProperties2); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMultiviewFeatures); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMultiviewProperties); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDevicePCIBusInfoPropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDevicePerformanceQueryFeaturesKHR); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDevicePerformanceQueryPropertiesKHR); @@ -10828,8 +10971,8 @@ INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceSamplerFilterMinmaxProperties); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceSamplerYcbcrConversionFeatures); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceScalarBlockLayoutFeatures); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures); -INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderAtomicFloatFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderAtomicFloatFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderAtomicInt64Features); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderClockFeaturesKHR); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderCorePropertiesAMD); @@ -10843,7 +10986,6 @@ INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderIntegerDotProductProperties); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceShaderTerminateInvocationFeatures); -INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceSparseImageFormatInfo2); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceSubgroupProperties); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceSubgroupSizeControlFeatures); @@ -10955,8 +11097,8 @@ INSTANTIATE_SERIALISE_TYPE(VkSubpassBeginInfo); INSTANTIATE_SERIALISE_TYPE(VkSubpassDependency2); INSTANTIATE_SERIALISE_TYPE(VkSubpassDescription2); INSTANTIATE_SERIALISE_TYPE(VkSubpassDescriptionDepthStencilResolve); -INSTANTIATE_SERIALISE_TYPE(VkSubpassFragmentDensityMapOffsetEndInfoQCOM); INSTANTIATE_SERIALISE_TYPE(VkSubpassEndInfo); +INSTANTIATE_SERIALISE_TYPE(VkSubpassFragmentDensityMapOffsetEndInfoQCOM); INSTANTIATE_SERIALISE_TYPE(VkSubpassResolvePerformanceQueryEXT); INSTANTIATE_SERIALISE_TYPE(VkSubpassSampleLocationsEXT); INSTANTIATE_SERIALISE_TYPE(VkSurfaceCapabilities2EXT); diff --git a/renderdoc/driver/vulkan/vk_shader_feedback.cpp b/renderdoc/driver/vulkan/vk_shader_feedback.cpp index 88f49d9a0..c13546195 100644 --- a/renderdoc/driver/vulkan/vk_shader_feedback.cpp +++ b/renderdoc/driver/vulkan/vk_shader_feedback.cpp @@ -1464,12 +1464,12 @@ void VulkanReplay::FetchShaderFeedback(uint32_t eventId) const DescSetLayout::Binding &bindData = layout.bindings[binding]; // skip empty bindings - if(bindData.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + if(bindData.layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) continue; // only process array bindings if(bindData.descriptorCount > 1 && - bindData.descriptorType != VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + bindData.layoutDescType != VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { key.binding = (uint32_t)binding; diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp index 320d8dcca..6dc90a267 100644 --- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp +++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp @@ -239,88 +239,76 @@ public: DescSetBindingSnapshot &dstBind = dstSet.bindings[bind]; - switch(bindLayout.descriptorType) + if(bindLayout.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { - case VK_DESCRIPTOR_TYPE_SAMPLER: - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + // push directly into the buffer cache from the inline data + BindpointIndex idx; + idx.bindset = (int32_t)set; + idx.bind = (int32_t)bind; + idx.arrayIndex = 0; + bufferCache[idx].assign(curInline.data() + curSlots->offset, descriptorCount); + } + else + { + for(uint32_t i = 0; i < descriptorCount; i++) { - dstBind.imageInfos.resize(descriptorCount); - for(uint32_t i = 0; i < descriptorCount; i++) - { - dstBind.imageInfos[i].imageLayout = curSlots[i].imageInfo.imageLayout; - dstBind.imageInfos[i].imageView = - m_pDriver->GetResourceManager()->GetCurrentHandle( - curSlots[i].imageInfo.imageView); - dstBind.imageInfos[i].sampler = - m_pDriver->GetResourceManager()->GetCurrentHandle( - bindLayout.immutableSampler ? bindLayout.immutableSampler[i] - : curSlots[i].imageInfo.sampler); - } - break; - } - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - { - dstBind.texelBuffers.resize(descriptorCount); - for(uint32_t i = 0; i < descriptorCount; i++) - { - dstBind.texelBuffers[i] = - m_pDriver->GetResourceManager()->GetCurrentHandle( - curSlots[i].texelBufferView); - } - break; - } - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: - { - dstBind.buffers.resize(descriptorCount); - for(uint32_t i = 0; i < descriptorCount; i++) - { - dstBind.buffers[i].offset = curSlots[i].bufferInfo.offset; - dstBind.buffers[i].range = curSlots[i].bufferInfo.range; - dstBind.buffers[i].buffer = - m_pDriver->GetResourceManager()->GetCurrentHandle( - curSlots[i].bufferInfo.buffer); - } - break; - } - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: - { - // push directly into the buffer cache from the inline data - BindpointIndex idx; - idx.bindset = (int32_t)set; - idx.bind = (int32_t)bind; - idx.arrayIndex = 0; - bufferCache[idx].assign(curInline.data() + curSlots->inlineOffset, descriptorCount); - break; - } - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - { - dstBind.buffers.resize(descriptorCount); - for(uint32_t i = 0; i < descriptorCount; i++) - { - dstBind.buffers[i].offset = curSlots[i].bufferInfo.offset; - dstBind.buffers[i].range = curSlots[i].bufferInfo.range; - dstBind.buffers[i].buffer = - m_pDriver->GetResourceManager()->GetCurrentHandle( - curSlots[i].bufferInfo.buffer); + const DescriptorSetSlot &slot = curSlots[i]; - dstBind.buffers[i].offset += descSets[set].offsets[dynamicOffset++]; + switch(slot.type) + { + case DescriptorSlotType::Sampler: + case DescriptorSlotType::CombinedImageSampler: + case DescriptorSlotType::SampledImage: + case DescriptorSlotType::StorageImage: + case DescriptorSlotType::InputAttachment: + { + dstBind.imageInfos.resize(descriptorCount); + dstBind.imageInfos[i].imageLayout = convert(slot.imageLayout); + dstBind.imageInfos[i].imageView = + m_pDriver->GetResourceManager()->GetCurrentHandle(slot.resource); + dstBind.imageInfos[i].sampler = + m_pDriver->GetResourceManager()->GetCurrentHandle( + bindLayout.immutableSampler ? bindLayout.immutableSampler[i] + : slot.sampler); + break; + } + case DescriptorSlotType::UniformTexelBuffer: + case DescriptorSlotType::StorageTexelBuffer: + { + dstBind.texelBuffers.resize(descriptorCount); + dstBind.texelBuffers[i] = + m_pDriver->GetResourceManager()->GetCurrentHandle(slot.resource); + break; + } + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::StorageBuffer: + case DescriptorSlotType::UniformBufferDynamic: + case DescriptorSlotType::StorageBufferDynamic: + { + dstBind.buffers.resize(descriptorCount); + dstBind.buffers[i].offset = slot.offset; + dstBind.buffers[i].range = slot.GetRange(); + dstBind.buffers[i].buffer = + m_pDriver->GetResourceManager()->GetCurrentHandle(slot.resource); + + if(slot.type == DescriptorSlotType::UniformBufferDynamic || + slot.type == DescriptorSlotType::StorageBufferDynamic) + dstBind.buffers[i].offset += descSets[set].offsets[dynamicOffset++]; + break; + } + case DescriptorSlotType::Unwritten: break; + default: RDCERR("Unexpected descriptor type"); } - break; } - default: RDCERR("Unexpected descriptor type"); } } else { // still need to skip past dynamic offsets for stages that aren't of interest + // we can use the layout descriptor type here because mutable descriptors aren't allowed + // to be dynamic - switch(bindLayout.descriptorType) + switch(bindLayout.layoutDescType) { case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: dynamicOffset += descriptorCount; break; diff --git a/renderdoc/driver/vulkan/vk_state.cpp b/renderdoc/driver/vulkan/vk_state.cpp index 31db4d898..6b40a6820 100644 --- a/renderdoc/driver/vulkan/vk_state.cpp +++ b/renderdoc/driver/vulkan/vk_state.cpp @@ -638,7 +638,7 @@ void VulkanRenderState::BindDescriptorSetsForPipeline(WrappedVulkan *vk, VkComma const DescSetLayout &createdDescLayout = vk->GetDebugManager()->GetDescSetLayout(createdDescSetLayoutId); - if(descLayout != createdDescLayout) + if(!descLayout.isCompatible(createdDescLayout)) { // this set is incompatible, don't rebind it. Assume the application knows the shader // doesn't need this set, and the binding is just stale @@ -730,9 +730,13 @@ void VulkanRenderState::BindDescriptorSetsWithoutPipeline(WrappedVulkan *vk, VkC // behaviour to use it anyway. If this binding *should* be valid, it will still be // valid at the end. + const DescSetLayout &iDescLayout = + vk->GetDebugManager()->GetDescSetLayout(iPipeLayout.descSetLayouts[i]); + const DescSetLayout &refDescLayout = + vk->GetDebugManager()->GetDescSetLayout(refPipeLayout.descSetLayouts[i]); + if(iPipeLayout.descSetLayouts[i] != refPipeLayout.descSetLayouts[i] && - vk->GetDebugManager()->GetDescSetLayout(iPipeLayout.descSetLayouts[i]) != - vk->GetDebugManager()->GetDescSetLayout(refPipeLayout.descSetLayouts[i])) + !iDescLayout.isCompatible(refDescLayout)) { // set is incompatible, don't rebind it continue; @@ -753,9 +757,13 @@ void VulkanRenderState::BindDescriptorSetsWithoutPipeline(WrappedVulkan *vk, VkC if(j >= refPipeLayout.descSetLayouts.size()) break; + const DescSetLayout &iDescLayout = + vk->GetDebugManager()->GetDescSetLayout(iPipeLayout.descSetLayouts[i]); + const DescSetLayout &refDescLayout = + vk->GetDebugManager()->GetDescSetLayout(refPipeLayout.descSetLayouts[i]); + if(iPipeLayout.descSetLayouts[j] != refPipeLayout.descSetLayouts[j] && - vk->GetDebugManager()->GetDescSetLayout(iPipeLayout.descSetLayouts[j]) != - vk->GetDebugManager()->GetDescSetLayout(refPipeLayout.descSetLayouts[j])) + !iDescLayout.isCompatible(refDescLayout)) { compatible = false; break; @@ -842,19 +850,22 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout VkWriteDescriptorSet push = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; + VulkanResourceManager *rm = vk->GetResourceManager(); + for(size_t b = 0; b < descLayout.bindings.size(); b++) { - const DescSetLayout::Binding &bind = descLayout.bindings[b]; + const DescSetLayout::Binding &layoutBind = descLayout.bindings[b]; // skip if this binding isn't used - if(bind.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + if(layoutBind.layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) continue; // push.dstSet; // unused for push descriptors push.dstBinding = (uint32_t)b; push.dstArrayElement = 0; - push.descriptorType = bind.descriptorType; - push.descriptorCount = bind.descriptorCount; + // we can use the layout type here, since push descriptors are not allowed to be mutable + push.descriptorType = layoutBind.layoutDescType; + push.descriptorCount = layoutBind.descriptorCount; const DescriptorSetSlot *slots = setInfo.data.binds[b]; @@ -864,8 +875,7 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout VkBufferView *dst = new VkBufferView[push.descriptorCount]; for(uint32_t a = 0; a < push.descriptorCount; a++) - dst[a] = Unwrap( - vk->GetResourceManager()->GetCurrentHandle(slots[a].texelBufferView)); + dst[a] = Unwrap(rm->GetCurrentHandle(slots[a].resource)); push.pTexelBufferView = dst; allocBufViewWrites.push_back(dst); @@ -880,12 +890,9 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout for(uint32_t a = 0; a < push.descriptorCount; a++) { - const DescriptorSetSlotImageInfo &src = slots[a].imageInfo; - - dst[a].imageLayout = src.imageLayout; - dst[a].sampler = Unwrap(vk->GetResourceManager()->GetCurrentHandle(src.sampler)); - dst[a].imageView = - Unwrap(vk->GetResourceManager()->GetCurrentHandle(src.imageView)); + dst[a].imageLayout = convert(slots[a].imageLayout); + dst[a].sampler = Unwrap(rm->GetCurrentHandle(slots[a].sampler)); + dst[a].imageView = Unwrap(rm->GetCurrentHandle(slots[a].resource)); } push.pImageInfo = dst; @@ -897,11 +904,11 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout VkWriteDescriptorSetInlineUniformBlock *inlineWrite = allocInlineWrites.back(); inlineWrite->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK; inlineWrite->pNext = NULL; - inlineWrite->dataSize = bind.descriptorCount; - inlineWrite->pData = setInfo.data.inlineBytes.data() + slots[0].inlineOffset; + inlineWrite->dataSize = layoutBind.descriptorCount; + inlineWrite->pData = setInfo.data.inlineBytes.data() + slots[0].offset; push.pNext = inlineWrite; - push.descriptorCount = bind.descriptorCount; + push.descriptorCount = layoutBind.descriptorCount; writes.push_back(push); // skip validity checks @@ -913,11 +920,9 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout for(uint32_t a = 0; a < push.descriptorCount; a++) { - const DescriptorSetSlotBufferInfo &src = slots[a].bufferInfo; - - dst[a].offset = src.offset; - dst[a].range = src.range; - dst[a].buffer = Unwrap(vk->GetResourceManager()->GetCurrentHandle(src.buffer)); + dst[a].offset = slots[a].offset; + dst[a].range = slots[a].GetRange(); + dst[a].buffer = Unwrap(rm->GetCurrentHandle(slots[a].resource)); } push.pBufferInfo = dst; @@ -927,7 +932,7 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout // start with no descriptors push.descriptorCount = 0; - for(uint32_t w = 0; w < bind.descriptorCount; w++) + for(uint32_t w = 0; w < layoutBind.descriptorCount; w++) { // if this push is valid, we increment the descriptor count and continue if(IsValid(vk->NULLDescriptorsAllowed(), push, w - push.dstArrayElement)) diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index 1fe089762..e70eb5e46 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -5136,7 +5136,8 @@ void WrappedVulkan::ApplyPushDescriptorWrites(VkPipelineBindPoint pipelineBindPo curIdx = 0; } - (*bind)[curIdx].texelBufferView = GetResID(writeDesc.pTexelBufferView[d]); + (*bind)[curIdx].SetTexelBuffer(writeDesc.descriptorType, + GetResID(writeDesc.pTexelBufferView[d])); } } else if(writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || @@ -5156,19 +5157,8 @@ void WrappedVulkan::ApplyPushDescriptorWrites(VkPipelineBindPoint pipelineBindPo curIdx = 0; } - bool sampler = true; - bool imageView = true; - - // ignore descriptors not part of the write, as they might not even point to a valid - // object so trying to get their ID could crash - if(layoutBinding->immutableSampler || - (writeDesc.descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER && - writeDesc.descriptorType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)) - sampler = false; - if(writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) - imageView = false; - - (*bind)[curIdx].imageInfo.SetFrom(writeDesc.pImageInfo[d], sampler, imageView); + (*bind)[curIdx].SetImage(writeDesc.descriptorType, writeDesc.pImageInfo[d], + layoutBinding->immutableSampler == NULL); } } else if(writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) @@ -5176,8 +5166,8 @@ void WrappedVulkan::ApplyPushDescriptorWrites(VkPipelineBindPoint pipelineBindPo VkWriteDescriptorSetInlineUniformBlock *inlineWrite = (VkWriteDescriptorSetInlineUniformBlock *)FindNextStruct( &writeDesc, VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK); - memcpy(inlineData.data() + (*bind)->inlineOffset + writeDesc.dstArrayElement, - inlineWrite->pData, inlineWrite->dataSize); + memcpy(inlineData.data() + (*bind)->offset + writeDesc.dstArrayElement, inlineWrite->pData, + inlineWrite->dataSize); } else { @@ -5192,7 +5182,7 @@ void WrappedVulkan::ApplyPushDescriptorWrites(VkPipelineBindPoint pipelineBindPo curIdx = 0; } - (*bind)[curIdx].bufferInfo.SetFrom(writeDesc.pBufferInfo[d]); + (*bind)[curIdx].SetBuffer(writeDesc.descriptorType, writeDesc.pBufferInfo[d]); } } } @@ -5425,7 +5415,7 @@ void WrappedVulkan::vkCmdPushDescriptorSetKHR(VkCommandBuffer commandBuffer, { const VkWriteDescriptorSet &write = pDescriptorWrites[i]; - FrameRefType ref = GetRefType(write.descriptorType); + FrameRefType ref = GetRefType(convert(write.descriptorType)); for(uint32_t d = 0; d < write.descriptorCount; d++) { @@ -5631,7 +5621,7 @@ void WrappedVulkan::vkCmdPushDescriptorSetWithTemplateKHR( byte *dst = memory + entry.offset; const byte *src = (const byte *)pData + entry.offset; - FrameRefType ref = GetRefType(entry.descriptorType); + FrameRefType ref = GetRefType(convert(entry.descriptorType)); if(entry.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || entry.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) diff --git a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp index 20aecb939..f4fc9468d 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp @@ -783,7 +783,7 @@ void WrappedVulkan::ReplayDescriptorSetWrite(VkDevice device, const VkWriteDescr curIdx = 0; // skip past invalid padding descriptors to get to the next real one - while(layoutBinding->descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + while(layoutBinding->layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) { layoutBinding++; } @@ -857,14 +857,15 @@ void WrappedVulkan::ReplayDescriptorSetWrite(VkDevice device, const VkWriteDescr curIdx = 0; // skip past invalid padding descriptors to get to the next real one - while(layoutBinding->descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + while(layoutBinding->layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) { layoutBinding++; bind++; } } - (*bind)[curIdx].texelBufferView = GetResID(writeDesc.pTexelBufferView[d]); + (*bind)[curIdx].SetTexelBuffer(writeDesc.descriptorType, + GetResID(writeDesc.pTexelBufferView[d])); } } else if(writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || @@ -884,26 +885,15 @@ void WrappedVulkan::ReplayDescriptorSetWrite(VkDevice device, const VkWriteDescr curIdx = 0; // skip past invalid padding descriptors to get to the next real one - while(layoutBinding->descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + while(layoutBinding->layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) { layoutBinding++; bind++; } } - bool sampler = true; - bool imageView = true; - - // ignore descriptors not part of the write, as they might not even point to a valid - // object so trying to get their ID could crash - if(layoutBinding->immutableSampler || - (writeDesc.descriptorType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER && - writeDesc.descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER)) - sampler = false; - if(writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) - imageView = false; - - (*bind)[curIdx].imageInfo.SetFrom(writeDesc.pImageInfo[d], sampler, imageView); + (*bind)[curIdx].SetImage(writeDesc.descriptorType, writeDesc.pImageInfo[d], + layoutBinding->immutableSampler == NULL); } } else if(writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) @@ -911,8 +901,8 @@ void WrappedVulkan::ReplayDescriptorSetWrite(VkDevice device, const VkWriteDescr VkWriteDescriptorSetInlineUniformBlock *inlineWrite = (VkWriteDescriptorSetInlineUniformBlock *)FindNextStruct( &writeDesc, VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK); - memcpy(inlineData.data() + (*bind)->inlineOffset + writeDesc.dstArrayElement, - inlineWrite->pData, inlineWrite->dataSize); + memcpy(inlineData.data() + (*bind)->offset + writeDesc.dstArrayElement, inlineWrite->pData, + inlineWrite->dataSize); } else { @@ -927,14 +917,14 @@ void WrappedVulkan::ReplayDescriptorSetWrite(VkDevice device, const VkWriteDescr curIdx = 0; // skip past invalid padding descriptors to get to the next real one - while(layoutBinding->descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + while(layoutBinding->layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) { layoutBinding++; bind++; } } - (*bind)[curIdx].bufferInfo.SetFrom(writeDesc.pBufferInfo[d]); + (*bind)[curIdx].SetBuffer(writeDesc.descriptorType, writeDesc.pBufferInfo[d]); } } } @@ -977,7 +967,7 @@ void WrappedVulkan::ReplayDescriptorSetCopy(VkDevice device, const VkCopyDescrip for(uint32_t d = 0; d < copyDesc.descriptorCount; d++, curSrcIdx++, curDstIdx++) { - if(layoutSrcBinding->descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(layoutSrcBinding->layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { // inline uniform blocks are special, the descriptor count is a byte count. The layouts may // not match so inline offsets might not match, so we just copy the data and break. @@ -985,8 +975,8 @@ void WrappedVulkan::ReplayDescriptorSetCopy(VkDevice device, const VkCopyDescrip bytebuf &dstInlineData = m_DescriptorSetState[dstSetId].data.inlineBytes; bytebuf &srcInlineData = m_DescriptorSetState[srcSetId].data.inlineBytes; - memcpy(dstInlineData.data() + (*dstbind)[0].inlineOffset + copyDesc.dstArrayElement, - srcInlineData.data() + (*srcbind)[0].inlineOffset + copyDesc.srcArrayElement, + memcpy(dstInlineData.data() + (*dstbind)[0].offset + copyDesc.dstArrayElement, + srcInlineData.data() + (*srcbind)[0].offset + copyDesc.srcArrayElement, copyDesc.descriptorCount); break; @@ -1257,8 +1247,6 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, const DescSetLayout::Binding *layoutBinding = &layout.bindings[descWrite.dstBinding]; - FrameRefType ref = GetRefType(layoutBinding->descriptorType); - // We need to handle the cases where these bindings are stale: // ie. image handle 0xf00baa is allocated // bound into a descriptor set @@ -1300,13 +1288,11 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, curIdx = 0; // skip past invalid padding descriptors to get to the next real one - while(layoutBinding->descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + while(layoutBinding->layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) { layoutBinding++; binding++; } - - ref = GetRefType(layoutBinding->descriptorType); } DescriptorSetSlot &bind = (*binding)[curIdx]; @@ -1314,7 +1300,7 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, if(descWrite.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || descWrite.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { - bind.texelBufferView = GetResID(descWrite.pTexelBufferView[d]); + bind.SetTexelBuffer(descWrite.descriptorType, GetResID(descWrite.pTexelBufferView[d])); } else if(descWrite.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || descWrite.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || @@ -1322,26 +1308,15 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, descWrite.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || descWrite.descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) { - bool sampler = true; - bool imageView = true; - - // ignore descriptors not part of the write, as they might not even point to a valid - // object so trying to get their ID could crash - if(layoutBinding->immutableSampler || - (descWrite.descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER && - descWrite.descriptorType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)) - sampler = false; - if(descWrite.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) - imageView = false; - - bind.imageInfo.SetFrom(descWrite.pImageInfo[d], sampler, imageView); + bind.SetImage(descWrite.descriptorType, descWrite.pImageInfo[d], + layoutBinding->immutableSampler == NULL); } else if(descWrite.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { VkWriteDescriptorSetInlineUniformBlock *inlineWrite = (VkWriteDescriptorSetInlineUniformBlock *)FindNextStruct( &descWrite, VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK); - memcpy(inlineData.data() + (*binding)->inlineOffset + descWrite.dstArrayElement, + memcpy(inlineData.data() + (*binding)->offset + descWrite.dstArrayElement, inlineWrite->pData, inlineWrite->dataSize); // break now because the descriptorCount is not the number of descriptors @@ -1349,7 +1324,7 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, } else { - bind.bufferInfo.SetFrom(descWrite.pBufferInfo[d]); + bind.SetBuffer(descWrite.descriptorType, descWrite.pBufferInfo[d]); } } } @@ -1381,8 +1356,6 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, const DescSetLayout::Binding *srclayoutBinding = &srclayout.bindings[pDescriptorCopies[i].srcBinding]; - FrameRefType ref = GetRefType(dstlayoutBinding->descriptorType); - // allow roll-over between consecutive bindings. See above in the plain write case for more // explanation uint32_t curSrcIdx = pDescriptorCopies[i].srcArrayElement; @@ -1390,7 +1363,7 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, for(uint32_t d = 0; d < pDescriptorCopies[i].descriptorCount; d++, curSrcIdx++, curDstIdx++) { - if(srclayoutBinding->descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + if(srclayoutBinding->layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { // inline uniform blocks are special, the descriptor count is a byte count. The layouts // may not match so inline offsets might not match, so we just copy the data and break. @@ -1398,11 +1371,10 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, bytebuf &dstInlineData = dstrecord->descInfo->data.inlineBytes; bytebuf &srcInlineData = srcrecord->descInfo->data.inlineBytes; - memcpy(dstInlineData.data() + (*dstbinding)[0].inlineOffset + - pDescriptorCopies[i].dstArrayElement, - srcInlineData.data() + (*srcbinding)[0].inlineOffset + - pDescriptorCopies[i].srcArrayElement, - pDescriptorCopies[i].descriptorCount); + memcpy( + dstInlineData.data() + (*dstbinding)[0].offset + pDescriptorCopies[i].dstArrayElement, + srcInlineData.data() + (*srcbinding)[0].offset + pDescriptorCopies[i].srcArrayElement, + pDescriptorCopies[i].descriptorCount); break; } @@ -1412,8 +1384,6 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, dstlayoutBinding++; dstbinding++; curDstIdx = 0; - - ref = GetRefType(dstlayoutBinding->descriptorType); } // dst and src indices must roll-over independently @@ -1687,8 +1657,6 @@ void WrappedVulkan::vkUpdateDescriptorSetWithTemplate( const DescSetLayout::Binding *layoutBinding = &layout.bindings[entry.dstBinding]; - FrameRefType ref = GetRefType(layoutBinding->descriptorType); - // start at the dstArrayElement uint32_t curIdx = entry.dstArrayElement; @@ -1715,13 +1683,11 @@ void WrappedVulkan::vkUpdateDescriptorSetWithTemplate( curIdx = 0; // skip past invalid padding descriptors to get to the next real one - while(layoutBinding->descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + while(layoutBinding->layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM) { layoutBinding++; binding++; } - - ref = GetRefType(layoutBinding->descriptorType); } const byte *src = (const byte *)pData + entry.offset + entry.stride * d; @@ -1731,7 +1697,7 @@ void WrappedVulkan::vkUpdateDescriptorSetWithTemplate( if(entry.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || entry.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { - bind.texelBufferView = GetResID(*(const VkBufferView *)src); + bind.SetTexelBuffer(entry.descriptorType, GetResID(*(const VkBufferView *)src)); } else if(entry.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || entry.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || @@ -1741,31 +1707,18 @@ void WrappedVulkan::vkUpdateDescriptorSetWithTemplate( { const VkDescriptorImageInfo &srcInfo = *(const VkDescriptorImageInfo *)src; - bool sampler = true; - bool imageView = true; - - // ignore descriptors not part of the write, as they might not even point to a valid - // object so trying to get their ID could crash - if(layoutBinding->immutableSampler || - (entry.descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER && - entry.descriptorType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)) - sampler = false; - if(entry.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) - imageView = false; - - bind.imageInfo.SetFrom(srcInfo, sampler, imageView); + bind.SetImage(entry.descriptorType, srcInfo, layoutBinding->immutableSampler == NULL); } else if(entry.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) { - memcpy(inlineData.data() + bind.inlineOffset + entry.dstArrayElement, src, - entry.descriptorCount); + memcpy(inlineData.data() + bind.offset + entry.dstArrayElement, src, entry.descriptorCount); // break now because the descriptorCount is not the number of descriptors break; } else { - bind.bufferInfo.SetFrom(*(const VkDescriptorBufferInfo *)src); + bind.SetBuffer(entry.descriptorType, *(const VkDescriptorBufferInfo *)src); } } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp index 320ab78ab..f9c0cfc81 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp @@ -2990,6 +2990,13 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi (ext->attachmentFragmentShadingRate != VK_FALSE); } END_PHYS_EXT_CHECK(); + + BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT); + { + CHECK_PHYS_EXT_FEATURE(mutableDescriptorType); + } + END_PHYS_EXT_CHECK(); } if(availFeatures.depthClamp) diff --git a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp index 83a5d67f0..d1c9e1521 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp @@ -1009,19 +1009,17 @@ void WrappedVulkan::CaptureQueueSubmit(VkQueue queue, { const DescSetLayout::Binding &bind = layout->bindings[b]; - // skip empty bindings - if(bind.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM || - bind.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + // skip empty bindings or inline uniform blocks + if(bind.layoutDescType == VK_DESCRIPTOR_TYPE_MAX_ENUM || + bind.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) continue; uint32_t count = bind.descriptorCount; if(bind.variableSize) count = setrecord->descInfo->data.variableDescriptorCount; - FrameRefType ref = GetRefType(bind.descriptorType); - for(uint32_t a = 0; a < count; a++) - setrecord->descInfo->data.binds[b][a].AccumulateBindRefs(refs, rm, ref); + setrecord->descInfo->data.binds[b][a].AccumulateBindRefs(refs, rm); } for(auto refit = refs.bindFrameRefs.begin(); refit != refs.bindFrameRefs.end(); ++refit) diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 3fdac74e7..30146c1fe 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -1922,6 +1922,7 @@ void DoSerialise(SerialiserType &ser, GLPipe::State &el) template void DoSerialise(SerialiserType &ser, VKPipe::BindingElement &el) { + SERIALISE_MEMBER(type); SERIALISE_MEMBER(viewResourceId); SERIALISE_MEMBER(resourceResourceId); SERIALISE_MEMBER(samplerResourceId); @@ -1960,7 +1961,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::BindingElement &el) SERIALISE_MEMBER(chromaFilter); SERIALISE_MEMBER(forceExplicitReconstruction); - SIZE_CHECK(184); + SIZE_CHECK(192); }; template @@ -1970,7 +1971,6 @@ void DoSerialise(SerialiserType &ser, VKPipe::DescriptorBinding &el) SERIALISE_MEMBER(dynamicallyUsedCount); SERIALISE_MEMBER(firstUsedIndex); SERIALISE_MEMBER(lastUsedIndex); - SERIALISE_MEMBER(type); SERIALISE_MEMBER(stageFlags); SERIALISE_MEMBER(binds);