diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index e940083dd..8432829df 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -2020,7 +2020,7 @@ void VulkanPipelineStateViewer::setState() vbuff != NULL ? vbuff->resourceId : ResourceId(), vbuff != NULL ? vbuff->byteOffset : 0, m_Common.GetVBufferFormatString(i)))); - if(!filledSlot || bind == NULL || vbuff == NULL) + if(!filledSlot || bind == NULL || vbuff == NULL || vbuff->resourceId == ResourceId()) { setEmptyRow(node); m_EmptyNodes.push_back(node); diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index 88b53b4e2..b7c4a9b74 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -976,7 +976,7 @@ FrameRefType GetRefType(VkDescriptorType descType) return eFrameRef_Read; } -bool IsValid(const VkWriteDescriptorSet &write, uint32_t arrayElement) +bool IsValid(bool allowNULLDescriptors, const VkWriteDescriptorSet &write, uint32_t arrayElement) { if(write.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) return true; @@ -986,10 +986,10 @@ bool IsValid(const VkWriteDescriptorSet &write, uint32_t arrayElement) // case they can be garbage and we must ignore them based on the descriptorType if(write.pTexelBufferView) - return write.pTexelBufferView[arrayElement] != VK_NULL_HANDLE; + return allowNULLDescriptors ? true : write.pTexelBufferView[arrayElement] != VK_NULL_HANDLE; if(write.pBufferInfo) - return write.pBufferInfo[arrayElement].buffer != VK_NULL_HANDLE; + return allowNULLDescriptors ? true : write.pBufferInfo[arrayElement].buffer != VK_NULL_HANDLE; if(write.pImageInfo) { @@ -1000,6 +1000,9 @@ bool IsValid(const VkWriteDescriptorSet &write, uint32_t arrayElement) // 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; diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index f01bbbd62..5873c1cd2 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -450,7 +450,7 @@ DECLARE_REFLECTION_STRUCT(DescriptorSetSlotBufferInfo); DECLARE_REFLECTION_STRUCT(DescriptorSetSlotImageInfo); DECLARE_REFLECTION_STRUCT(DescriptorSetSlot); -bool IsValid(const VkWriteDescriptorSet &write, uint32_t arrayElement); +bool IsValid(bool allowNULLDescriptors, const VkWriteDescriptorSet &write, uint32_t arrayElement); #define NUM_VK_IMAGE_ASPECTS 4 #define VK_ACCESS_ALL_READ_BITS \ @@ -835,6 +835,8 @@ DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceProperties2); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceProtectedMemoryFeatures); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceProtectedMemoryProperties); DECLARE_REFLECTION_STRUCT(VkPhysicalDevicePushDescriptorPropertiesKHR); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceRobustness2FeaturesEXT); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceRobustness2PropertiesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceSampleLocationsPropertiesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceSamplerFilterMinmaxProperties); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceSamplerYcbcrConversionFeatures); @@ -1129,6 +1131,8 @@ DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceProperties2); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceProtectedMemoryFeatures); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceProtectedMemoryProperties); DECLARE_DESERIALISE_TYPE(VkPhysicalDevicePushDescriptorPropertiesKHR); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceRobustness2FeaturesEXT); +DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceRobustness2PropertiesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceSampleLocationsPropertiesEXT); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceSamplerFilterMinmaxProperties); DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceSamplerYcbcrConversionFeatures); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index b819854da..ce8776921 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -859,6 +859,9 @@ static const VkExtensionProperties supportedExtensions[] = { { VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME, VK_EXT_QUEUE_FAMILY_FOREIGN_SPEC_VERSION, }, + { + VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, VK_EXT_ROBUSTNESS_2_SPEC_VERSION, + }, { VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME, VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION, }, diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index a7382c46c..2f67f810c 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -399,6 +399,7 @@ private: }; bool m_SeparateDepthStencil = false; + bool m_NULLDescriptorsAllowed = false; PFN_vkSetDeviceLoaderData m_SetDeviceLoaderData; @@ -1058,6 +1059,7 @@ public: VkDeviceCreateInfo &createInfo, uint32_t &queueFamilyIndex); bool SeparateDepthStencil() const { return m_SeparateDepthStencil; } + bool NULLDescriptorsAllowed() const { return m_NULLDescriptorsAllowed; } VulkanRenderState &GetRenderState() { return m_RenderState; } void SetDrawcallCB(VulkanDrawcallCallback *cb) { m_DrawcallCallback = cb; } void SetSubmitChain(void *submitChain) { m_SubmitChain = submitChain; } diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index e97414448..4da74e79b 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -2212,7 +2212,7 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, for(uint32_t w = 0; w < bind.descriptorCount; w++) { // if this write is valid, we increment the descriptor count and continue - if(IsValid(write, w - write.dstArrayElement)) + if(IsValid(m_pDriver->NULLDescriptorsAllowed(), write, w - write.dstArrayElement)) { write.descriptorCount++; } diff --git a/renderdoc/driver/vulkan/vk_initstate.cpp b/renderdoc/driver/vulkan/vk_initstate.cpp index 3ede31270..80f6951e8 100644 --- a/renderdoc/driver/vulkan/vk_initstate.cpp +++ b/renderdoc/driver/vulkan/vk_initstate.cpp @@ -793,8 +793,8 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, dstInline++; } // quick check for slots that were completely uninitialised and so don't have valid data - else if(descriptorCount == 1 && src->texelBufferView == ResourceId() && - src->imageInfo.sampler == ResourceId() && + else if(!NULLDescriptorsAllowed() && descriptorCount == 1 && + src->texelBufferView == ResourceId() && src->imageInfo.sampler == ResourceId() && src->imageInfo.imageView == ResourceId() && src->bufferInfo.buffer == ResourceId()) { // do nothing - don't increment bind so that the same write descriptor is used next time. @@ -900,7 +900,7 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, // 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(writes[bind], d - writes[bind].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 diff --git a/renderdoc/driver/vulkan/vk_next_chains.cpp b/renderdoc/driver/vulkan/vk_next_chains.cpp index 7cced11f7..977fb1ef8 100644 --- a/renderdoc/driver/vulkan/vk_next_chains.cpp +++ b/renderdoc/driver/vulkan/vk_next_chains.cpp @@ -267,6 +267,10 @@ static void AppendModifiedChainedStruct(byte *&tempMem, VkStruct *outputStruct, VkPhysicalDeviceProtectedMemoryProperties); \ COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR, \ VkPhysicalDevicePushDescriptorPropertiesKHR); \ + COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT, \ + VkPhysicalDeviceRobustness2FeaturesEXT); \ + COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT, \ + VkPhysicalDeviceRobustness2PropertiesEXT); \ COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT, \ VkPhysicalDeviceSampleLocationsPropertiesEXT); \ COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES, \ @@ -637,8 +641,6 @@ static void AppendModifiedChainedStruct(byte *&tempMem, VkStruct *outputStruct, case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_KHR: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV: \ - case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT: \ - case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL: \ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_FEATURES_NV: \ diff --git a/renderdoc/driver/vulkan/vk_postvs.cpp b/renderdoc/driver/vulkan/vk_postvs.cpp index 52a583979..96c87a82a 100644 --- a/renderdoc/driver/vulkan/vk_postvs.cpp +++ b/renderdoc/driver/vulkan/vk_postvs.cpp @@ -1671,11 +1671,9 @@ void VulkanReplay::FetchVSOut(uint32_t eventId, VulkanRenderState &state) offs += drawcall->vertexOffset * vi->pVertexBindingDescriptions[vb].stride; } + origVBs.push_back(bytebuf()); if(state.vbuffers[binding].buf != ResourceId()) - { - origVBs.push_back(bytebuf()); GetBufferData(state.vbuffers[binding].buf, offs, len, origVBs.back()); - } } for(uint32_t i = 0; i < vi->vertexAttributeDescriptionCount; i++) @@ -1712,7 +1710,8 @@ void VulkanReplay::FetchVSOut(uint32_t eventId, VulkanRenderState &state) } } - RDCASSERT(origVBEnd); + if(origVBBegin == NULL) + continue; // in some limited cases, provided we added the UNIFORM_TEXEL_BUFFER usage bit, we could use // the original buffers here as-is and read out of them. However it is likely that the offset diff --git a/renderdoc/driver/vulkan/vk_serialise.cpp b/renderdoc/driver/vulkan/vk_serialise.cpp index 29b1aa6ee..bbd90ebd0 100644 --- a/renderdoc/driver/vulkan/vk_serialise.cpp +++ b/renderdoc/driver/vulkan/vk_serialise.cpp @@ -615,6 +615,12 @@ SERIALISE_VK_HANDLES(); PNEXT_STRUCT(VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT, \ VkPipelineCreationFeedbackCreateInfoEXT) \ \ + /* VK_EXT_robustness2 */ \ + PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT, \ + VkPhysicalDeviceRobustness2FeaturesEXT) \ + PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT, \ + VkPhysicalDeviceRobustness2PropertiesEXT) \ + \ /* VK_EXT_sampler_filter_minmax */ \ PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES, \ VkPhysicalDeviceSamplerFilterMinmaxProperties) \ @@ -1069,10 +1075,6 @@ SERIALISE_VK_HANDLES(); PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO_EXT) \ PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PRIVATE_DATA_SLOT_CREATE_INFO_EXT) \ \ - /* VK_EXT_robustness2 */ \ - PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT) \ - PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT) \ - \ /* VK_EXT_texture_compression_astc_hdr */ \ PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT) \ \ @@ -5659,6 +5661,41 @@ void Deserialise(const VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT &e DeserialiseNext(el.pNext); } +template +void DoSerialise(SerialiserType &ser, VkPhysicalDeviceRobustness2FeaturesEXT &el) +{ + RDCASSERT(ser.IsReading() || + el.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT); + SerialiseNext(ser, el.sType, el.pNext); + + SERIALISE_MEMBER(robustBufferAccess2); + SERIALISE_MEMBER(robustImageAccess2); + SERIALISE_MEMBER(nullDescriptor); +} + +template <> +void Deserialise(const VkPhysicalDeviceRobustness2FeaturesEXT &el) +{ + DeserialiseNext(el.pNext); +} + +template +void DoSerialise(SerialiserType &ser, VkPhysicalDeviceRobustness2PropertiesEXT &el) +{ + RDCASSERT(ser.IsReading() || + el.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT); + SerialiseNext(ser, el.sType, el.pNext); + + SERIALISE_MEMBER(robustStorageBufferAccessSizeAlignment); + SERIALISE_MEMBER(robustUniformBufferAccessSizeAlignment); +} + +template <> +void Deserialise(const VkPhysicalDeviceRobustness2PropertiesEXT &el) +{ + DeserialiseNext(el.pNext); +} + template void DoSerialise(SerialiserType &ser, VkPhysicalDeviceTransformFeedbackFeaturesEXT &el) { @@ -8458,6 +8495,8 @@ INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceProperties2); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceProtectedMemoryFeatures); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceProtectedMemoryProperties); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDevicePushDescriptorPropertiesKHR); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceRobustness2FeaturesEXT); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceRobustness2PropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceSampleLocationsPropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceSamplerFilterMinmaxProperties); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceSamplerYcbcrConversionFeatures); diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp index ed7e04e46..5aa62b817 100644 --- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp +++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp @@ -1382,8 +1382,11 @@ private: m_ResourcesDirty = false; } - m_pDriver->GetDebugManager()->GetBufferData(GetResID(bufData.buffer), bufData.offset, - bufData.range, data); + if(bufData.buffer != VK_NULL_HANDLE) + { + m_pDriver->GetDebugManager()->GetBufferData(GetResID(bufData.buffer), bufData.offset, + bufData.range, data); + } } } } @@ -1411,54 +1414,57 @@ private: m_ResourcesDirty = false; } - const VulkanCreationInfo::ImageView &viewProps = - m_Creation.m_ImageView[GetResID(imgData.imageView)]; - const VulkanCreationInfo::Image &imageProps = m_Creation.m_Image[viewProps.image]; - - uint32_t mip = viewProps.range.baseMipLevel; - - data.width = RDCMAX(1U, imageProps.extent.width >> mip); - data.height = RDCMAX(1U, imageProps.extent.height >> mip); - if(imageProps.type == VK_IMAGE_TYPE_3D) + if(imgData.imageView != VK_NULL_HANDLE) { - data.depth = RDCMAX(1U, imageProps.extent.depth >> mip); - } - else - { - data.depth = viewProps.range.layerCount; - if(data.depth == VK_REMAINING_ARRAY_LAYERS) - data.depth = imageProps.arrayLayers - viewProps.range.baseArrayLayer; - } + const VulkanCreationInfo::ImageView &viewProps = + m_Creation.m_ImageView[GetResID(imgData.imageView)]; + const VulkanCreationInfo::Image &imageProps = m_Creation.m_Image[viewProps.image]; - data.texelSize = GetByteSize(1, 1, 1, imageProps.format, 0); - data.rowPitch = GetByteSize(data.width, 1, 1, imageProps.format, 0); - data.slicePitch = GetByteSize(data.width, data.height, 1, imageProps.format, 0); - data.samplePitch = GetByteSize(data.width, data.height, data.depth, imageProps.format, 0); + uint32_t mip = viewProps.range.baseMipLevel; - const uint32_t numSlices = imageProps.type == VK_IMAGE_TYPE_3D ? 1 : data.depth; - const uint32_t numSamples = (uint32_t)imageProps.samples; - - data.bytes.reserve(data.samplePitch * numSamples); - - // defaults are fine - no interpretation. Maybe we could use the view's typecast? - const GetTextureDataParams params = GetTextureDataParams(); - - for(uint32_t sample = 0; sample < numSamples; sample++) - { - for(uint32_t slice = 0; slice < numSlices; slice++) + data.width = RDCMAX(1U, imageProps.extent.width >> mip); + data.height = RDCMAX(1U, imageProps.extent.height >> mip); + if(imageProps.type == VK_IMAGE_TYPE_3D) { - bytebuf subBytes; - m_pDriver->GetReplay()->GetTextureData(viewProps.image, Subresource(mip, slice, sample), - params, subBytes); + data.depth = RDCMAX(1U, imageProps.extent.depth >> mip); + } + else + { + data.depth = viewProps.range.layerCount; + if(data.depth == VK_REMAINING_ARRAY_LAYERS) + data.depth = imageProps.arrayLayers - viewProps.range.baseArrayLayer; + } - // fast path, swap into output if there's only one slice and one sample (common case) - if(numSlices == 1 && numSamples == 1) + data.texelSize = GetByteSize(1, 1, 1, imageProps.format, 0); + data.rowPitch = GetByteSize(data.width, 1, 1, imageProps.format, 0); + data.slicePitch = GetByteSize(data.width, data.height, 1, imageProps.format, 0); + data.samplePitch = GetByteSize(data.width, data.height, data.depth, imageProps.format, 0); + + const uint32_t numSlices = imageProps.type == VK_IMAGE_TYPE_3D ? 1 : data.depth; + const uint32_t numSamples = (uint32_t)imageProps.samples; + + data.bytes.reserve(data.samplePitch * numSamples); + + // defaults are fine - no interpretation. Maybe we could use the view's typecast? + const GetTextureDataParams params = GetTextureDataParams(); + + for(uint32_t sample = 0; sample < numSamples; sample++) + { + for(uint32_t slice = 0; slice < numSlices; slice++) { - subBytes.swap(data.bytes); - } - else - { - data.bytes.append(subBytes); + bytebuf subBytes; + m_pDriver->GetReplay()->GetTextureData( + viewProps.image, Subresource(mip, slice, sample), params, subBytes); + + // fast path, swap into output if there's only one slice and one sample (common case) + if(numSlices == 1 && numSamples == 1) + { + subBytes.swap(data.bytes); + } + else + { + data.bytes.append(subBytes); + } } } } @@ -3444,28 +3450,31 @@ ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, u { const VulkanRenderState::VertBuffer &vb = state.vbuffers[bind.vbufferBinding]; - uint32_t vertexOffset = 0; - - if(bind.perInstance) + if(vb.buf != ResourceId()) { - if(bind.instanceDivisor == 0) - vertexOffset = instOffset * bind.bytestride; + uint32_t vertexOffset = 0; + + if(bind.perInstance) + { + if(bind.instanceDivisor == 0) + vertexOffset = instOffset * bind.bytestride; + else + vertexOffset = (instOffset + (instid / bind.instanceDivisor)) * bind.bytestride; + } else - vertexOffset = (instOffset + (instid / bind.instanceDivisor)) * bind.bytestride; - } - else - { - vertexOffset = (idx + vertOffset) * bind.bytestride; - } + { + vertexOffset = (idx + vertOffset) * bind.bytestride; + } - if(Vulkan_Debug_ShaderDebugLogging()) - { - RDCLOG("Fetching from %s at %llu offset %zu bytes", ToStr(vb.buf).c_str(), - vb.offs + attr.byteoffset + vertexOffset, size); - } + if(Vulkan_Debug_ShaderDebugLogging()) + { + RDCLOG("Fetching from %s at %llu offset %zu bytes", ToStr(vb.buf).c_str(), + vb.offs + attr.byteoffset + vertexOffset, size); + } - GetDebugManager()->GetBufferData(vb.buf, vb.offs + attr.byteoffset + vertexOffset, size, - data); + GetDebugManager()->GetBufferData(vb.buf, vb.offs + attr.byteoffset + vertexOffset, size, + data); + } } else if(Vulkan_Debug_ShaderDebugLogging()) { diff --git a/renderdoc/driver/vulkan/vk_state.cpp b/renderdoc/driver/vulkan/vk_state.cpp index d4b3b27d3..b55c3f4b0 100644 --- a/renderdoc/driver/vulkan/vk_state.cpp +++ b/renderdoc/driver/vulkan/vk_state.cpp @@ -233,7 +233,15 @@ void VulkanRenderState::BindPipeline(WrappedVulkan *vk, VkCommandBuffer cmd, for(size_t i = 0; i < vbuffers.size(); i++) { if(vbuffers[i].buf == ResourceId()) + { + if(vk->NULLDescriptorsAllowed()) + { + VkBuffer empty = VK_NULL_HANDLE; + ObjDisp(cmd)->CmdBindVertexBuffers(Unwrap(cmd), (uint32_t)i, 1, &empty, &vbuffers[i].offs); + } + continue; + } ObjDisp(cmd)->CmdBindVertexBuffers( Unwrap(cmd), (uint32_t)i, 1, @@ -485,7 +493,7 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout for(uint32_t w = 0; w < bind.descriptorCount; w++) { // if this push is valid, we increment the descriptor count and continue - if(IsValid(push, w - push.dstArrayElement)) + if(IsValid(vk->NULLDescriptorsAllowed(), push, w - push.dstArrayElement)) { push.descriptorCount++; } diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index 315c044fe..9695d89d7 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -2648,8 +2648,10 @@ void WrappedVulkan::vkCmdBindVertexBuffers(VkCommandBuffer commandBuffer, uint32 record->AddChunk(scope.Get()); for(uint32_t i = 0; i < bindingCount; i++) { - record->MarkBufferFrameReferenced(GetRecord(pBuffers[i]), pOffsets[i], VK_WHOLE_SIZE, - eFrameRef_Read); + // binding NULL is legal with robustness2 + if(pBuffers[i]) + record->MarkBufferFrameReferenced(GetRecord(pBuffers[i]), pOffsets[i], VK_WHOLE_SIZE, + eFrameRef_Read); } } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp index 22e18ed11..3e8547dc0 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp @@ -624,7 +624,9 @@ void WrappedVulkan::ReplayDescriptorSetWrite(VkDevice device, const VkWriteDescr valid &= (writeDesc.pImageInfo[i].sampler != VK_NULL_HANDLE) || (layoutBinding->immutableSampler && layoutBinding->immutableSampler[curIdx] != ResourceId()); - valid &= (writeDesc.pImageInfo[i].imageView != VK_NULL_HANDLE); + + if(!NULLDescriptorsAllowed()) + valid &= (writeDesc.pImageInfo[i].imageView != VK_NULL_HANDLE); } break; } @@ -632,14 +634,14 @@ void WrappedVulkan::ReplayDescriptorSetWrite(VkDevice device, const VkWriteDescr case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { - for(uint32_t i = 0; i < writeDesc.descriptorCount; i++) + for(uint32_t i = 0; !NULLDescriptorsAllowed() && i < writeDesc.descriptorCount; i++) valid &= (writeDesc.pImageInfo[i].imageView != VK_NULL_HANDLE); break; } case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { - for(uint32_t i = 0; i < writeDesc.descriptorCount; i++) + for(uint32_t i = 0; !NULLDescriptorsAllowed() && i < writeDesc.descriptorCount; i++) valid &= (writeDesc.pTexelBufferView[i] != VK_NULL_HANDLE); break; } @@ -648,7 +650,7 @@ void WrappedVulkan::ReplayDescriptorSetWrite(VkDevice device, const VkWriteDescr case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: { - for(uint32_t i = 0; i < writeDesc.descriptorCount; i++) + for(uint32_t i = 0; !NULLDescriptorsAllowed() && i < writeDesc.descriptorCount; i++) valid &= (writeDesc.pBufferInfo[i].buffer != VK_NULL_HANDLE); break; } diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp index a4212ceb0..9fb438956 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp @@ -2539,6 +2539,17 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi CHECK_PHYS_EXT_FEATURE(customBorderColorWithoutFormat); } END_PHYS_EXT_CHECK(); + + BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceRobustness2FeaturesEXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT); + { + CHECK_PHYS_EXT_FEATURE(robustBufferAccess2); + CHECK_PHYS_EXT_FEATURE(robustImageAccess2); + CHECK_PHYS_EXT_FEATURE(nullDescriptor); + + m_NULLDescriptorsAllowed |= (ext->nullDescriptor != VK_FALSE); + } + END_PHYS_EXT_CHECK(); } if(availFeatures.depthClamp) diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt index e5ca1b5e2..a1a2cbe2f 100644 --- a/util/test/demos/CMakeLists.txt +++ b/util/test/demos/CMakeLists.txt @@ -29,6 +29,7 @@ set(VULKAN_SRC vk/vk_parameter_zoo.cpp vk/vk_query_pool.cpp vk/vk_resource_lifetimes.cpp + vk/vk_robustness2.cpp vk/vk_sample_locations.cpp vk/vk_secondary_cmdbuf.cpp vk/vk_separate_depth_stencil_layouts.cpp diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 3532c29fc..9281d22aa 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -266,6 +266,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index d059cac33..51a6f4da4 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -499,6 +499,9 @@ Vulkan\demos + + Vulkan\demos + diff --git a/util/test/demos/vk/vk_robustness2.cpp b/util/test/demos/vk/vk_robustness2.cpp new file mode 100644 index 000000000..239186cf4 --- /dev/null +++ b/util/test/demos/vk/vk_robustness2.cpp @@ -0,0 +1,271 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "vk_test.h" + +RD_TEST(VK_Robustness2, VulkanGraphicsTest) +{ + static constexpr const char *Description = + "Checks handling of NULL descriptors and NULL vertex buffers for VK_EXT_robustness2."; + + std::string common = R"EOSHADER( + +#version 460 core + +struct v2f +{ + vec4 pos; + vec4 col; + vec4 uv; +}; + +)EOSHADER"; + + const std::string vertex = R"EOSHADER( + +layout(location = 0) in vec3 Position; +layout(location = 1) in vec4 Color; +layout(location = 2) in vec4 UV; + +layout(location = 0) out v2f vertOut; + +void main() +{ + vertOut.pos = vec4(Position.xyz*vec3(1,-1,1), 1); + gl_Position = vertOut.pos; + vertOut.col = Color; + vertOut.uv = UV; +} + +)EOSHADER"; + + const std::string pixel = R"EOSHADER( + +layout(push_constant) uniform PushData { + ivec4 coord; +} push; + +layout(set = 0, binding = 2, std430) buffer oobbuftype +{ + vec4 arr[]; +} oobbuf; + +layout(set = 0, binding = 3, rgba32f) uniform coherent image2D oobImage; + +layout(set = 0, binding = 10, std140) uniform constsbuf +{ + vec4 data; +} cbuf; + +layout(set = 0, binding = 11) uniform sampler2D linearSampledImage; + +layout(set = 0, binding = 12, std430) buffer storebuftype +{ + vec4 arr[]; +} storebuf; + +layout(set = 0, binding = 13, rgba32f) uniform coherent image2D storeImage; + + +layout(location = 0) in v2f vertIn; + +layout(location = 0, index = 0) out vec4 Color; + +void main() +{ + imageStore(oobImage, push.coord.xy, vec4(1,2,3,4)); + oobbuf.arr[push.coord.z] = vec4(1,2,3,4); + Color = vertIn.col + storebuf.arr[0] + imageLoad(storeImage, ivec2(0, 0)) + texture(linearSampledImage, vec2(0, 0)); +} + +)EOSHADER"; + + VkPhysicalDeviceRobustness2FeaturesEXT robustnessFeatures = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT, + }; + + void Prepare(int argc, char **argv) + { + // require descriptor indexing + devExts.push_back(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + + features.robustBufferAccess = VK_TRUE; + + features.fragmentStoresAndAtomics = VK_TRUE; + + VulkanGraphicsTest::Prepare(argc, argv); + + if(!Avail.empty()) + return; + + getPhysFeatures2(&robustnessFeatures); + + if(!robustnessFeatures.nullDescriptor) + Avail = "Feature 'nullDescriptor' not available"; + + devInfoNext = &robustnessFeatures; + } + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + VkDescriptorSetLayout setlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + {3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + + {10, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + {11, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + {12, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + {13, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + })); + + VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo( + {setlayout}, + { + vkh::PushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(Vec4i)), + })); + + vkh::GraphicsPipelineCreateInfo pipeCreateInfo; + + pipeCreateInfo.layout = layout; + pipeCreateInfo.renderPass = mainWindow->rp; + + pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V), + vkh::vertexBind(1, DefaultA2V)}; + pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = { + vkh::vertexAttr(0, 0, DefaultA2V, pos), vkh::vertexAttr(1, 1, DefaultA2V, col), + vkh::vertexAttr(2, 1, DefaultA2V, uv), + }; + + pipeCreateInfo.stages = { + CompileShaderModule(common + vertex, ShaderLang::glsl, ShaderStage::vert, "main"), + CompileShaderModule(common + pixel, ShaderLang::glsl, ShaderStage::frag, "main"), + }; + + VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo); + + AllocatedBuffer vb( + this, vkh::BufferCreateInfo(sizeof(DefaultTri), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + vb.upload(DefaultTri); + + AllocatedImage offimg(this, vkh::ImageCreateInfo(4, 4, 0, VK_FORMAT_R32G32B32A32_SFLOAT, + VK_IMAGE_USAGE_STORAGE_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + VkImageView store_view = createImageView(vkh::ImageViewCreateInfo( + offimg.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32B32A32_SFLOAT)); + + AllocatedBuffer store_buffer(this, + vkh::BufferCreateInfo(1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + VkDescriptorSet descset = allocateDescriptorSet(setlayout); + + VkSampler pointsampler = createSampler(vkh::SamplerCreateInfo(VK_FILTER_NEAREST)); + + while(Running()) + { + vkh::updateDescriptorSets( + device, + { + vkh::WriteDescriptorSet(descset, 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(store_buffer.buffer)}), + vkh::WriteDescriptorSet( + descset, 3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + {vkh::DescriptorImageInfo(store_view, VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE)}), + + vkh::WriteDescriptorSet(descset, 10, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(VK_NULL_HANDLE)}), + vkh::WriteDescriptorSet(descset, 11, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + {vkh::DescriptorImageInfo( + VK_NULL_HANDLE, VK_IMAGE_LAYOUT_UNDEFINED, pointsampler)}), + vkh::WriteDescriptorSet(descset, 12, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(VK_NULL_HANDLE)}), + vkh::WriteDescriptorSet(descset, 13, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + {vkh::DescriptorImageInfo( + VK_NULL_HANDLE, VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE)}), + }); + + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + VkImage swapimg = + StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkCmdClearColorImage(cmd, swapimg, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f), 1, + vkh::ImageSubresourceRange()); + + vkh::cmdPipelineBarrier( + cmd, { + vkh::ImageMemoryBarrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL, offimg.image), + }); + + vkCmdBeginRenderPass( + cmd, vkh::RenderPassBeginInfo(mainWindow->rp, mainWindow->GetFB(), mainWindow->scissor), + VK_SUBPASS_CONTENTS_INLINE); + + Vec4i push = {}; + + if(robustnessFeatures.robustBufferAccess2) + push.z = 1000000; + if(robustnessFeatures.robustImageAccess2) + push.x = push.y = 1000000; + + vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, {descset}, {}); + vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(Vec4i), &push); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); + vkCmdSetViewport(cmd, 0, 1, &mainWindow->viewport); + vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor); + vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer, VK_NULL_HANDLE}, {0, 0}); + vkCmdDraw(cmd, 3, 1, 0, 0); + + vkCmdEndRenderPass(cmd); + + FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkEndCommandBuffer(cmd); + + Submit(0, 1, {cmd}); + + Present(); + + // idle the device so we can update descriptor sets every frame without needing to + // double-buffer. + vkDeviceWaitIdle(device); + } + + return 0; + } +}; + +REGISTER_TEST();