From 2c583b66f636d74f824c8fbd5198c11b76772088 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 20 Dec 2018 11:36:51 +0000 Subject: [PATCH] Add initial VK_EXT_buffer_address capture/replay implementation * The extension is not yet whitelisted as there is no solution currently for feedback on used bindings or handling of pointers in data structures within the UI or shader reflection. --- renderdoc/driver/vulkan/vk_common.h | 3 + renderdoc/driver/vulkan/vk_core.cpp | 41 +++ renderdoc/driver/vulkan/vk_core.h | 45 +++ renderdoc/driver/vulkan/vk_hookset_defs.h | 9 +- renderdoc/driver/vulkan/vk_serialise.cpp | 63 +++- .../vulkan/wrappers/vk_device_funcs.cpp | 30 ++ .../driver/vulkan/wrappers/vk_get_funcs.cpp | 8 + .../driver/vulkan/wrappers/vk_misc_funcs.cpp | 1 + .../vulkan/wrappers/vk_resource_funcs.cpp | 79 ++++- util/test/demos/CMakeLists.txt | 1 + util/test/demos/demos.vcxproj | 1 + util/test/demos/demos.vcxproj.filters | 3 + util/test/demos/vk/vk_buffer_address.cpp | 275 ++++++++++++++++++ util/test/demos/vk/vk_helpers.h | 6 + 14 files changed, 557 insertions(+), 8 deletions(-) create mode 100644 util/test/demos/vk/vk_buffer_address.cpp diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index f4a973624..1a2c843ba 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -589,6 +589,8 @@ DECLARE_REFLECTION_STRUCT(VkBindImageMemorySwapchainInfoKHR); DECLARE_REFLECTION_STRUCT(VkBindImagePlaneMemoryInfo); DECLARE_REFLECTION_STRUCT(VkBindSparseInfo); DECLARE_REFLECTION_STRUCT(VkBufferCreateInfo); +DECLARE_REFLECTION_STRUCT(VkBufferDeviceAddressInfoEXT); +DECLARE_REFLECTION_STRUCT(VkBufferDeviceAddressCreateInfoEXT); DECLARE_REFLECTION_STRUCT(VkBufferMemoryBarrier); DECLARE_REFLECTION_STRUCT(VkBufferMemoryRequirementsInfo2); DECLARE_REFLECTION_STRUCT(VkBufferViewCreateInfo); @@ -688,6 +690,7 @@ DECLARE_REFLECTION_STRUCT(VkMultisamplePropertiesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDevice16BitStorageFeatures); DECLARE_REFLECTION_STRUCT(VkPhysicalDevice8BitStorageFeaturesKHR); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceASTCDecodeFeaturesEXT); +DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceBufferAddressFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceConditionalRenderingFeaturesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceConservativeRasterizationPropertiesEXT); DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceDepthClipEnableFeaturesEXT); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index c063bc1e3..172347aeb 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -1141,6 +1141,36 @@ VkResult WrappedVulkan::FilterDeviceExtensionProperties(VkPhysicalDevice physDev continue; } + if(!strcmp(it->extensionName, VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) + { + // require GPDP2 + if(instDevInfo->ext_KHR_get_physical_device_properties2) + { + VkPhysicalDeviceBufferAddressFeaturesEXT bufaddr = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT}; + VkPhysicalDeviceFeatures2 base = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; + base.pNext = &bufaddr; + ObjDisp(physDev)->GetPhysicalDeviceFeatures2(Unwrap(physDev), &base); + + if(bufaddr.bufferDeviceAddressCaptureReplay) + { + // supported + ++it; + continue; + } + else + { + RDCWARN( + "VkPhysicalDeviceBufferAddressFeaturesEXT.bufferDeviceAddressCaptureReplay is " + "false, can't support capture of VK_EXT_buffer_device_address"); + } + } + + // if it wasn't supported, remove the extension + it = filtered.erase(it); + continue; + } + ++it; } @@ -1372,6 +1402,17 @@ void WrappedVulkan::StartFrameCapture(void *dev, void *wnd) GetResourceManager()->MarkResourceFrameReferenced(GetResID(m_Device), eFrameRef_Read); GetResourceManager()->MarkResourceFrameReferenced(GetResID(m_Queue), eFrameRef_Read); + std::map forced = GetForcedReferences(); + + // Note we force read-before-write because this resource is implicitly untracked so we have no + // way of knowing how it's used + for(auto it = forced.begin(); it != forced.end(); ++it) + { + GetResourceManager()->MarkResourceFrameReferenced(it->first, eFrameRef_Read); + if(it->second != eFrameRef_Read) + GetResourceManager()->MarkResourceFrameReferenced(it->first, it->second); + } + // need to do all this atomically so that no other commands // will check to see if they need to markdirty or markpendingdirty // and go into the frame record. diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 96cb2171c..32395e274 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -699,6 +699,47 @@ private: vector m_CoherentMaps; Threading::CriticalSection m_CoherentMapsLock; + std::map m_ForcedReferences; + Threading::CriticalSection m_ForcedReferencesLock; + + std::map GetForcedReferences() + { + std::map ret; + + { + SCOPED_LOCK(m_ForcedReferencesLock); + ret = m_ForcedReferences; + } + + return ret; + } + + bool IsForcedReference(ResourceId id) + { + bool ret = false; + + { + SCOPED_LOCK(m_ForcedReferencesLock); + ret = (m_ForcedReferences.find(id) != m_ForcedReferences.end()); + } + + return ret; + } + + void AddForcedReference(ResourceId id, FrameRefType ref) + { + SCOPED_LOCK(m_ForcedReferencesLock); + m_ForcedReferences[id] = ref; + + // also add it immediately in case we're mid-way through a frame, and the forced references have + // already been processed for this frame. + // Note we force read-before-write because this resource is implicitly untracked so we have no + // way of knowing how it's used + GetResourceManager()->MarkResourceFrameReferenced(id, eFrameRef_Read); + if(ref != eFrameRef_Read) + GetResourceManager()->MarkResourceFrameReferenced(id, ref); + } + // used both on capture and replay side to track image layouts. Only locked // in capture map m_ImageLayouts; @@ -2025,4 +2066,8 @@ public: IMPLEMENT_FUNCTION_SERIALISED(void, vkResetQueryPoolEXT, VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); + + // VK_EXT_buffer_device_address + VkDeviceAddress vkGetBufferDeviceAddressEXT(VkDevice device, + const VkBufferDeviceAddressInfoEXT *pInfo); }; diff --git a/renderdoc/driver/vulkan/vk_hookset_defs.h b/renderdoc/driver/vulkan/vk_hookset_defs.h index 6d8560f91..96101ce74 100644 --- a/renderdoc/driver/vulkan/vk_hookset_defs.h +++ b/renderdoc/driver/vulkan/vk_hookset_defs.h @@ -371,7 +371,8 @@ DeclExt(EXT_sample_locations); \ DeclExt(EXT_discard_rectangles); \ DeclExt(EXT_calibrated_timestamps); \ - DeclExt(EXT_host_query_reset); + DeclExt(EXT_host_query_reset); \ + DeclExt(EXT_buffer_device_address); // for simplicity and since the check itself is platform agnostic, // these aren't protected in platform defines @@ -444,7 +445,8 @@ CheckExt(EXT_sample_locations, VKXX); \ CheckExt(EXT_discard_rectangles, VKXX); \ CheckExt(EXT_calibrated_timestamps, VKXX); \ - CheckExt(EXT_host_query_reset, VKXX); + CheckExt(EXT_host_query_reset, VKXX); \ + CheckExt(EXT_buffer_device_address, VKXX); #define HookInitVulkanInstanceExts() \ HookInitExtension(KHR_surface, DestroySurfaceKHR); \ @@ -579,6 +581,7 @@ HookInitExtension(EXT_discard_rectangles, CmdSetDiscardRectangleEXT); \ HookInitExtension(EXT_calibrated_timestamps, GetCalibratedTimestampsEXT); \ HookInitExtension(EXT_host_query_reset, ResetQueryPoolEXT); \ + HookInitExtension(EXT_buffer_device_address, GetBufferDeviceAddressEXT); \ HookInitDevice_PlatformSpecific() #define DefineHooks() \ @@ -1149,6 +1152,8 @@ uint64_t *, pMaxDeviation); \ HookDefine4(void, vkResetQueryPoolEXT, VkDevice, device, VkQueryPool, queryPool, uint32_t, \ firstQuery, uint32_t, queryCount); \ + HookDefine2(VkDeviceAddress, vkGetBufferDeviceAddressEXT, VkDevice, device, \ + VkBufferDeviceAddressInfoEXT *, pInfo); \ HookDefine_PlatformSpecific() struct VkLayerInstanceDispatchTableExtended : VkLayerInstanceDispatchTable diff --git a/renderdoc/driver/vulkan/vk_serialise.cpp b/renderdoc/driver/vulkan/vk_serialise.cpp index 57bc9d7c4..099e51e54 100644 --- a/renderdoc/driver/vulkan/vk_serialise.cpp +++ b/renderdoc/driver/vulkan/vk_serialise.cpp @@ -414,6 +414,13 @@ SERIALISE_VK_HANDLES(); PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT, \ VkPhysicalDeviceASTCDecodeFeaturesEXT) \ \ + /* VK_EXT_buffer_device_address */ \ + PNEXT_STRUCT(VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT, VkBufferDeviceAddressInfoEXT) \ + PNEXT_STRUCT(VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT, \ + VkBufferDeviceAddressCreateInfoEXT) \ + PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT, \ + VkPhysicalDeviceBufferAddressFeaturesEXT) \ + \ /* VK_EXT_calibrated_timestamps */ \ PNEXT_STRUCT(VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT, VkCalibratedTimestampInfoEXT) \ \ @@ -827,11 +834,6 @@ SERIALISE_VK_HANDLES(); PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT) \ PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT) \ \ - /* VK_EXT_buffer_device_address */ \ - PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT) \ - PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT) \ - PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT) \ - \ /* VK_EXT_descriptor_indexing */ \ PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT) \ PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT) \ @@ -4229,6 +4231,54 @@ void Deserialise(const VkPhysicalDeviceASTCDecodeFeaturesEXT &el) DeserialiseNext(el.pNext); } +template +void DoSerialise(SerialiserType &ser, VkBufferDeviceAddressInfoEXT &el) +{ + RDCASSERT(ser.IsReading() || el.sType == VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT); + SerialiseNext(ser, el.sType, el.pNext); + + SERIALISE_MEMBER(buffer); +} + +template <> +void Deserialise(const VkBufferDeviceAddressInfoEXT &el) +{ + DeserialiseNext(el.pNext); +} + +template +void DoSerialise(SerialiserType &ser, VkBufferDeviceAddressCreateInfoEXT &el) +{ + RDCASSERT(ser.IsReading() || el.sType == VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT); + SerialiseNext(ser, el.sType, el.pNext); + + SERIALISE_MEMBER(deviceAddress); +} + +template <> +void Deserialise(const VkBufferDeviceAddressCreateInfoEXT &el) +{ + DeserialiseNext(el.pNext); +} + +template +void DoSerialise(SerialiserType &ser, VkPhysicalDeviceBufferAddressFeaturesEXT &el) +{ + RDCASSERT(ser.IsReading() || + el.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT); + SerialiseNext(ser, el.sType, el.pNext); + + SERIALISE_MEMBER(bufferDeviceAddress); + SERIALISE_MEMBER(bufferDeviceAddressCaptureReplay); + SERIALISE_MEMBER(bufferDeviceAddressMultiDevice); +} + +template <> +void Deserialise(const VkPhysicalDeviceBufferAddressFeaturesEXT &el) +{ + DeserialiseNext(el.pNext); +} + template void DoSerialise(SerialiserType &ser, VkValidationCacheCreateInfoEXT &el) { @@ -6390,6 +6440,8 @@ INSTANTIATE_SERIALISE_TYPE(VkBindImageMemorySwapchainInfoKHR); INSTANTIATE_SERIALISE_TYPE(VkBindImagePlaneMemoryInfo); INSTANTIATE_SERIALISE_TYPE(VkBindSparseInfo); INSTANTIATE_SERIALISE_TYPE(VkBufferCreateInfo); +INSTANTIATE_SERIALISE_TYPE(VkBufferDeviceAddressInfoEXT); +INSTANTIATE_SERIALISE_TYPE(VkBufferDeviceAddressCreateInfoEXT); INSTANTIATE_SERIALISE_TYPE(VkBufferMemoryBarrier); INSTANTIATE_SERIALISE_TYPE(VkBufferMemoryRequirementsInfo2); INSTANTIATE_SERIALISE_TYPE(VkBufferViewCreateInfo); @@ -6488,6 +6540,7 @@ INSTANTIATE_SERIALISE_TYPE(VkMultisamplePropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDevice16BitStorageFeatures); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDevice8BitStorageFeaturesKHR); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceASTCDecodeFeaturesEXT); +INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceBufferAddressFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceConditionalRenderingFeaturesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceConservativeRasterizationPropertiesEXT); INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceDepthStencilResolvePropertiesKHR); diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp index dcca771bd..c649eb1c7 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp @@ -1895,6 +1895,25 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi CHECK_PHYS_EXT_FEATURE(ycbcrImageArrays); } END_PHYS_EXT_CHECK(); + + BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceBufferAddressFeaturesEXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT); + { + CHECK_PHYS_EXT_FEATURE(bufferDeviceAddress); + CHECK_PHYS_EXT_FEATURE(bufferDeviceAddressCaptureReplay); + CHECK_PHYS_EXT_FEATURE(bufferDeviceAddressMultiDevice); + + if(ext->bufferDeviceAddress && !avail.bufferDeviceAddressCaptureReplay) + { + m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; + RDCERR( + "Capture requires bufferDeviceAddress support, which is available, but " + "bufferDeviceAddressCaptureReplay support is not available which is required to " + "replay"); + return false; + } + } + END_PHYS_EXT_CHECK(); } if(availFeatures.depthClamp) @@ -2343,6 +2362,17 @@ VkResult WrappedVulkan::vkCreateDevice(VkPhysicalDevice physicalDevice, fragmentDensityMapFeatures->fragmentDensityMapNonSubsampledImages = true; } + VkPhysicalDeviceBufferAddressFeaturesEXT *bufferAddressFeatures = + (VkPhysicalDeviceBufferAddressFeaturesEXT *)FindNextStruct( + &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT); + + if(bufferAddressFeatures) + { + // we must turn on bufferDeviceAddressCaptureReplay. We verified that this feature was available + // before we whitelisted the extension + bufferAddressFeatures->bufferDeviceAddressCaptureReplay = VK_TRUE; + } + VkResult ret; SERIALISE_TIME_CALL(ret = createFunc(Unwrap(physicalDevice), &createInfo, pAllocator, pDevice)); diff --git a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp index b694bc0db..0403da4bb 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp @@ -720,4 +720,12 @@ VkResult WrappedVulkan::vkGetCalibratedTimestampsEXT(VkDevice device, uint32_t t { return ObjDisp(device)->GetCalibratedTimestampsEXT(Unwrap(device), timestampCount, pTimestampInfos, pTimestamps, pMaxDeviation); +} + +VkDeviceAddress WrappedVulkan::vkGetBufferDeviceAddressEXT(VkDevice device, + const VkBufferDeviceAddressInfoEXT *pInfo) +{ + VkBufferDeviceAddressInfoEXT unwrappedInfo = *pInfo; + unwrappedInfo.buffer = Unwrap(unwrappedInfo.buffer); + return ObjDisp(device)->GetBufferDeviceAddressEXT(Unwrap(device), &unwrappedInfo); } \ No newline at end of file diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index e45d5b45f..045aff946 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -95,6 +95,7 @@ VkFramebufferCreateInfo WrappedVulkan::UnwrapInfo(const VkFramebufferCreateInfo if(obj == VK_NULL_HANDLE) \ return; \ type unwrappedObj = Unwrap(obj); \ + m_ForcedReferences.erase(GetResID(obj)); \ GetResourceManager()->ReleaseWrappedResource(obj, true); \ ObjDisp(device)->func(Unwrap(device), unwrappedObj, pAllocator); \ } diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index 490140f38..367eff774 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -511,6 +511,8 @@ void WrappedVulkan::vkFreeMemory(VkDevice device, VkDeviceMemory memory, } } + m_ForcedReferences.erase(GetResID(memory)); + GetResourceManager()->ReleaseWrappedResource(memory); ObjDisp(device)->FreeMemory(Unwrap(device), unwrappedMem, pAllocator); @@ -908,6 +910,25 @@ VkResult WrappedVulkan::vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkD record->AddParent(GetRecord(memory)); record->baseResource = GetResID(memory); record->memOffset = memoryOffset; + + // if the buffer was force-referenced, do the same with the memory + if(IsForcedReference(GetResID(buffer))) + { + AddForcedReference(GetResID(memory), eFrameRef_ReadBeforeWrite); + + // the memory is immediately dirty because we have no way of tracking writes to it + bool capframe = false; + + { + SCOPED_LOCK(m_CapTransitionLock); + capframe = IsActiveCapturing(m_State); + } + + if(capframe) + GetResourceManager()->MarkPendingDirty(GetResID(memory)); + else + GetResourceManager()->MarkDirtyResource(GetResID(memory)); + } } return ret; @@ -1073,6 +1094,11 @@ VkResult WrappedVulkan::vkCreateBuffer(VkDevice device, const VkBufferCreateInfo // on replay, so that the memory requirements are the same adjusted_info.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + // If we're using this buffer for device addresses, ensure we force on capture replay bit. + // We ensured the physical device can support this feature before whitelisting the extension. + if(adjusted_info.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) + adjusted_info.flags |= VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_EXT; + byte *tempMem = GetTempMemory(GetNextPatchSize(adjusted_info.pNext)); UnwrapNextChain(m_State, "VkBufferCreateInfo", tempMem, (VkBaseInStructure *)&adjusted_info); @@ -1089,11 +1115,43 @@ VkResult WrappedVulkan::vkCreateBuffer(VkDevice device, const VkBufferCreateInfo { Chunk *chunk = NULL; + VkBufferCreateInfo serialisedCreateInfo = *pCreateInfo; + VkBufferDeviceAddressCreateInfoEXT bufferDeviceAddress = { + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT, + }; + + // if we're using VK_EXT_buffer_device_address, we fetch the device address that's been + // allocated and insert it into the next chain and patch the flags so that it replays + // naturally. + if(GetRecord(device)->instDevInfo->ext_EXT_buffer_device_address && + (pCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0) + { + VkBufferDeviceAddressInfoEXT getInfo = { + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT, NULL, Unwrap(*pBuffer), + }; + + bufferDeviceAddress.deviceAddress = + ObjDisp(device)->GetBufferDeviceAddressEXT(Unwrap(device), &getInfo); + + RDCASSERT(bufferDeviceAddress.deviceAddress); + + // push this struct onto the start of the chain + bufferDeviceAddress.pNext = serialisedCreateInfo.pNext; + serialisedCreateInfo.pNext = &bufferDeviceAddress; + + // tell the driver we're giving it a pre-allocated address to use + serialisedCreateInfo.flags |= VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_EXT; + + // this buffer must be forced to be in any captures, since we can't track when it's used by + // address + AddForcedReference(GetResID(*pBuffer), eFrameRef_Read); + } + { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCreateBuffer); - Serialise_vkCreateBuffer(ser, device, pCreateInfo, NULL, pBuffer); + Serialise_vkCreateBuffer(ser, device, &serialisedCreateInfo, NULL, pBuffer); chunk = scope.Get(); } @@ -1940,6 +1998,25 @@ VkResult WrappedVulkan::vkBindBufferMemory2(VkDevice device, uint32_t bindInfoCo bufrecord->AddParent(memrecord); bufrecord->baseResource = memrecord->GetResourceID(); bufrecord->memOffset = pBindInfos[i].memoryOffset; + + // if the buffer was force-referenced, do the same with the memory + if(IsForcedReference(GetResID(pBindInfos[i].buffer))) + { + AddForcedReference(GetResID(pBindInfos[i].memory), eFrameRef_ReadBeforeWrite); + + // the memory is immediately dirty because we have no way of tracking writes to it + bool capframe = false; + + { + SCOPED_LOCK(m_CapTransitionLock); + capframe = IsActiveCapturing(m_State); + } + + if(capframe) + GetResourceManager()->MarkPendingDirty(GetResID(pBindInfos[i].memory)); + else + GetResourceManager()->MarkDirtyResource(GetResID(pBindInfos[i].memory)); + } } } diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt index 40ea32cee..c2e4ce034 100644 --- a/util/test/demos/CMakeLists.txt +++ b/util/test/demos/CMakeLists.txt @@ -8,6 +8,7 @@ set(VULKAN_SRC vk/vk_test.cpp vk/vk_adv_cbuffer_zoo.cpp vk/vk_awkward_triangle.cpp + vk/vk_buffer_address.cpp vk/vk_cbuffer_zoo.cpp vk/vk_discard_rects.cpp vk/vk_draw_zoo.cpp diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 0c619c7f4..d36dd4758 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -193,6 +193,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 2e6e7564a..a86fcb2e9 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -255,6 +255,9 @@ Vulkan\demos + + Vulkan\demos + D3D11\demos diff --git a/util/test/demos/vk/vk_buffer_address.cpp b/util/test/demos/vk/vk_buffer_address.cpp new file mode 100644 index 000000000..04d5773c1 --- /dev/null +++ b/util/test/demos/vk/vk_buffer_address.cpp @@ -0,0 +1,275 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2018 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" + +// only support on 64-bit, just because it's easier to share CPU & GPU structs if pointer size is +// identical + +#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || \ + defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + +struct VK_Buffer_Address : VulkanGraphicsTest +{ + static constexpr const char *Description = "Test capture and replay of VK_EXT_buffer_reference"; + + // should match definition below in GLSL + struct DrawData + { + DefaultA2V *vert_data; + // no alignment on Vec4f, use scalar block layout + Vec4f tint; + Vec2f offset; + Vec2f scale; + // padding to make the struct size 16 to make aligning the buffer easier. + Vec2f padding; + }; + + std::string common = R"EOSHADER( + +#version 460 core + +#extension GL_EXT_buffer_reference : require +#extension GL_EXT_scalar_block_layout : require + +struct v2f +{ + vec4 pos; + vec4 col; + vec4 uv; +}; + +struct DefaultA2V { + vec3 pos; + vec4 col; + vec2 uv; +}; + +layout(bufferreference, scalar, bufferreferencealign = 16) buffer TriangleData { + DefaultA2V verts[3]; +}; + +layout(bufferreference, scalar, bufferreferencealign = 16) buffer DrawData { + TriangleData tri; + vec4 tint; + vec2 offset; + vec2 scale; +}; + +layout(push_constant) uniform PushData { + DrawData data_ptr; +} push; + +)EOSHADER"; + + const std::string vertex = R"EOSHADER( + +layout(location = 0) out v2f vertOut; + +void main() +{ + DrawData draw = push.data_ptr; + DefaultA2V vert = draw.tri.verts[gl_VertexIndex]; + + gl_Position = vertOut.pos = vec4(vert.pos*vec3(draw.scale,1) + vec3(draw.offset, 0), 1); + vertOut.col = vert.col; + vertOut.uv = vec4(vert.uv, 0, 1); +} + +)EOSHADER"; + + const std::string pixel = R"EOSHADER( + +layout(location = 0) in v2f vertIn; + +layout(location = 0, index = 0) out vec4 Color; + +void main() +{ + DrawData draw = push.data_ptr; + + Color = vertIn.col * draw.tint; +} + +)EOSHADER"; + + int main(int argc, char **argv) + { + VkPhysicalDeviceBufferAddressFeaturesEXT bufaddrFeatures = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT, + }; + + bufaddrFeatures.bufferDeviceAddress = VK_TRUE; + + devInfoNext = &bufaddrFeatures; + + instExts.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + devExts.push_back(VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + devExts.push_back(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME); + + // initialise, create window, create context, etc + if(!Init(argc, argv)) + return 3; + + VkPipelineLayout layout = createPipelineLayout( + vkh::PipelineLayoutCreateInfo({}, {vkh::PushConstantRange(VK_SHADER_STAGE_ALL, 0, 8)})); + + vkh::GraphicsPipelineCreateInfo pipeCreateInfo; + + pipeCreateInfo.layout = layout; + pipeCreateInfo.renderPass = swapRenderPass; + + pipeCreateInfo.stages = { + CompileShaderModule(common + vertex, ShaderLang::glsl, ShaderStage::vert, "main"), + CompileShaderModule(common + pixel, ShaderLang::glsl, ShaderStage::frag, "main"), + }; + + VkPipeline pipe = createGraphicsPipeline(pipeCreateInfo); + + vkh::BufferCreateInfo bufinfo(0x100000, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT); + + AllocatedBuffer databuf(allocator, bufinfo, + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + // north-facing primary colours triangle + const DefaultA2V tri1[3] = { + {Vec3f(-0.5f, -0.5f, 0.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.5f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.5f, -0.5f, 0.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f), Vec2f(1.0f, 0.0f)}, + }; + + // north-west-facing triangle + const DefaultA2V tri2[3] = { + {Vec3f(-0.5f, 0.5f, 0.0f), Vec4f(1.0f, 0.2f, 1.0f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.5f, 0.5f, 0.0f), Vec4f(0.7f, 0.85f, 1.0f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(-0.5f, -0.5f, 0.0f), Vec4f(1.0f, 1.0f, 0.4f, 1.0f), Vec2f(1.0f, 0.0f)}, + }; + + VkBufferDeviceAddressInfoEXT info = {VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT}; + info.buffer = databuf.buffer; + + VkDeviceAddress baseAddr = vkGetBufferDeviceAddressEXT(device, &info); + byte *gpuptr = (byte *)baseAddr; // not a valid cpu pointer but useful for avoiding casting + + byte *cpuptr = databuf.map(); + + // put triangle data first + memcpy(cpuptr, tri1, sizeof(tri1)); + DefaultA2V *gputri1 = (DefaultA2V *)gpuptr; + cpuptr += sizeof(tri1); + gpuptr += sizeof(tri1); + + // align to 16 bytes + cpuptr = AlignUpPtr(cpuptr, 16); + gpuptr = AlignUpPtr(gpuptr, 16); + + memcpy(cpuptr, tri2, sizeof(tri2)); + DefaultA2V *gputri2 = (DefaultA2V *)gpuptr; + cpuptr += sizeof(tri2); + gpuptr += sizeof(tri2); + + // align to 16 bytes + cpuptr = AlignUpPtr(cpuptr, 16); + gpuptr = AlignUpPtr(gpuptr, 16); + + DrawData *drawscpu = (DrawData *)cpuptr; + DrawData *drawsgpu = (DrawData *)gpuptr; + + drawscpu[0].vert_data = gputri1; + drawscpu[0].offset = Vec2f(-0.5f, 0.0f); + drawscpu[0].scale = Vec2f(0.5f, 0.5f); + drawscpu[0].tint = Vec4f(1.0f, 0.5f, 0.5f, 1.0f); // tint red + + drawscpu[1].vert_data = gputri1; + drawscpu[1].offset = Vec2f(0.0f, 0.0f); + drawscpu[1].scale = Vec2f(0.5f, -0.5f); // flip vertically + drawscpu[1].tint = Vec4f(0.2f, 0.5f, 1.0f, 1.0f); // tint blue + + drawscpu[2].vert_data = gputri2; // use second triangle + drawscpu[2].offset = Vec2f(0.6f, 0.0f); + drawscpu[2].scale = Vec2f(0.5f, 0.5f); + drawscpu[2].tint = Vec4f(1.0f, 1.0f, 1.0f, 1.0f); + + float time = 0.0f; + + while(Running()) + { + 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.4f, 0.5f, 0.6f, 1.0f), 1, + vkh::ImageSubresourceRange()); + + vkCmdBeginRenderPass( + cmd, vkh::RenderPassBeginInfo(swapRenderPass, swapFramebuffers[swapIndex], scissor), + VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); + vkCmdSetViewport(cmd, 0, 1, &viewport); + vkCmdSetScissor(cmd, 0, 1, &scissor); + + // look ma, no binds + DrawData *bindptr = drawsgpu; + drawscpu[0].scale.x = (abs(sinf(time)) + 0.1f) * 0.5f; + vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_ALL, 0, 8, &bindptr); + vkCmdDraw(cmd, 3, 1, 0, 0); + + bindptr++; + drawscpu[1].scale.y = (abs(cosf(time)) + 0.1f) * 0.5f; + vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_ALL, 0, 8, &bindptr); + vkCmdDraw(cmd, 3, 1, 0, 0); + + bindptr++; + drawscpu[2].tint = Vec4f(cosf(time) * 0.5f + 0.5f, sinf(time) * 0.5f + 0.5f, + cosf(time + 3.14f) * 0.5f + 0.5f, 1.0f); + vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_ALL, 0, 8, &bindptr); + 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(); + + time += 0.1f; + } + + databuf.unmap(); + + return 0; + } +}; + +REGISTER_TEST(VK_Buffer_Address); + +#endif // if 64-bit \ No newline at end of file diff --git a/util/test/demos/vk/vk_helpers.h b/util/test/demos/vk/vk_helpers.h index 161407d42..832b3cb8c 100644 --- a/util/test/demos/vk/vk_helpers.h +++ b/util/test/demos/vk/vk_helpers.h @@ -556,6 +556,12 @@ struct DescriptorSetLayoutCreateInfo : public VkDescriptorSetLayoutCreateInfo this->pBindings = bindings.data(); } + DescriptorSetLayoutCreateInfo &next(const void *next) + { + this->pNext = next; + return *this; + } + operator const VkDescriptorSetLayoutCreateInfo *() const { return this; } };