Browse Source

Enable interoperability between rendering backends and other APIs (#18450)

* Enable exporting of memory and synchronization primitives from Vulkan/Dx12

Signed-off-by: Martin Winter <[email protected]>
Co-authored-by: Martin Sattlecker <[email protected]>
Co-authored-by: Martin Winter <[email protected]>
Martin Winter 9 tháng trước cách đây
mục cha
commit
1c6c3e49e7
41 tập tin đã thay đổi với 861 bổ sung83 xóa
  1. 1 1
      Gems/Atom/RHI/Code/Include/Atom/RHI/AliasedAttachmentAllocator.h
  2. 4 0
      Gems/Atom/RHI/Code/Include/Atom/RHI/DeviceFence.h
  3. 36 0
      Gems/Atom/RHI/Code/Include/Atom/RHI/RHIBus.h
  4. 2 1
      Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp
  5. 1 0
      Gems/Atom/RHI/Code/atom_rhi_public_files.cmake
  6. 16 0
      Gems/Atom/RHI/DX12/Code/CMakeLists.txt
  7. 54 0
      Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Interface/DX12/RHIDX12Interface.h
  8. 34 0
      Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/DX12Bus.h
  9. 5 0
      Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/PhysicalDevice_Windows.cpp
  10. 116 0
      Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/RHIDX12Interface.cpp
  11. 13 11
      Gems/Atom/RHI/DX12/Code/Source/RHI/AliasedHeap.cpp
  12. 16 0
      Gems/Atom/RHI/DX12/Code/Source/RHI/BufferD3D12MemoryAllocator.cpp
  13. 24 20
      Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp
  14. 5 1
      Gems/Atom/RHI/DX12/Code/Source/RHI/Fence.cpp
  15. 1 0
      Gems/Atom/RHI/DX12/Code/Source/RHI/Fence.h
  16. 38 5
      Gems/Atom/RHI/DX12/Code/Source/RHI/MemoryView.cpp
  17. 19 2
      Gems/Atom/RHI/DX12/Code/Source/RHI/MemoryView.h
  18. 21 4
      Gems/Atom/RHI/DX12/Code/Source/RHI/TransientAttachmentPool.cpp
  19. 12 0
      Gems/Atom/RHI/DX12/Code/atom_rhi_dx12_interface_common_files.cmake
  20. 2 0
      Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp
  21. 17 0
      Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt
  22. 51 0
      Gems/Atom/RHI/Vulkan/Code/Include/Atom/RHI.Interface/Vulkan/RHIVulkanInterface.h
  23. 18 0
      Gems/Atom/RHI/Vulkan/Code/Include/Atom/RHI.Reflect/Vulkan/VulkanBus.h
  24. 140 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI.Interface/RHIVulkanInterface.cpp
  25. 1 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/AliasedHeap.cpp
  26. 43 6
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/BufferMemory.cpp
  27. 4 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/BufferMemory.h
  28. 5 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/BufferMemoryView.h
  29. 44 4
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp
  30. 32 5
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h
  31. 5 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/Fence.cpp
  32. 1 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/Fence.h
  33. 16 17
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/Image.cpp
  34. 2 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/Image.h
  35. 11 6
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp
  36. 5 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/MemoryView.h
  37. 12 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/TimelineSemaphoreFence.cpp
  38. 11 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/TransientAttachmentPool.cpp
  39. 8 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/VulkanMemoryAllocation.cpp
  40. 3 0
      Gems/Atom/RHI/Vulkan/Code/Source/RHI/VulkanMemoryAllocation.h
  41. 12 0
      Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_interface_files.cmake

+ 1 - 1
Gems/Atom/RHI/Code/Include/Atom/RHI/AliasedAttachmentAllocator.h

@@ -515,7 +515,7 @@ namespace AZ::RHI
     template<class Heap>
     AliasedHeap* AliasedAttachmentAllocator<Heap>::AddAliasedHeapPage(size_t sizeInBytes, uint32_t heapIndex)
     {
-        size_t newHeapSize = static_cast<size_t>(GetHeapPageScaleFactor() * sizeInBytes);
+        size_t newHeapSize = static_cast<size_t>(AlignUp(GetHeapPageScaleFactor() * sizeInBytes, m_descriptor.m_alignment));
 
         typename Heap::Descriptor heapDescriptor = m_descriptor;
         heapDescriptor.m_budgetInBytes = newHeapSize;

+ 4 - 0
Gems/Atom/RHI/Code/Include/Atom/RHI/DeviceFence.h

@@ -49,6 +49,10 @@ namespace AZ::RHI
         /// is invoked when the fence completes.
         ResultCode WaitOnCpuAsync(SignalCallback callback);
 
+        /// BinaryFences in Vulkan need their dependent TimelineSemaphore Fences to be
+        /// signalled. This is currently only implemented in Vulkan
+        virtual void SetExternallySignalled(){};
+
     protected:
         bool ValidateIsInitialized() const;
 

+ 36 - 0
Gems/Atom/RHI/Code/Include/Atom/RHI/RHIBus.h

@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <Atom/RHI/PhysicalDevice.h>
+#include <AzCore/EBus/EBus.h>
+
+namespace AZ::RHI
+{
+    class Device;
+    //! Ebus to collect requirements for creating a PhysicalDevice.
+    class RHIRequirementsRequest : public AZ::EBusTraits
+    {
+    public:
+        virtual ~RHIRequirementsRequest() = default;
+
+        //! Removes PhysicalDevices that are not supported from a list of available devices.
+        virtual void FilterSupportedPhysicalDevices(
+            [[maybe_unused]] PhysicalDeviceList& supportedDevices, [[maybe_unused]] AZ::RHI::APIIndex apiIndex){};
+
+        virtual size_t GetRequiredAlignment([[maybe_unused]] const Device& device)
+        {
+            return 0;
+        };
+
+    public:
+        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+    };
+
+    using RHIRequirementRequestBus = AZ::EBus<RHIRequirementsRequest>;
+} // namespace AZ::RHI

+ 2 - 1
Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp

@@ -280,8 +280,9 @@ namespace AZ::RHI
     {
         AZ_PROFILE_SCOPE(RHI, "FrameScheduler: CompileProducers");
 
-        for (ScopeProducer* scopeProducer : m_scopeProducers)
+        for (auto scope : m_frameGraph->GetScopes())
         {
+            auto scopeProducer = FindScopeProducer(scope->GetId());
             const FrameGraphCompileContext context(scopeProducer->GetScopeId(), m_frameGraph->GetAttachmentDatabase());
             scopeProducer->CompileResources(context);
         }

+ 1 - 0
Gems/Atom/RHI/Code/atom_rhi_public_files.cmake

@@ -196,6 +196,7 @@ set(FILES
     Source/RHI/DeviceResourcePool.cpp
     Source/RHI/ResourcePool.cpp
     Source/RHI/ResourcePoolDatabase.cpp
+    Include/Atom/RHI/RHIBus.h
     Include/Atom/RHI/MemoryAllocation.h
     Include/Atom/RHI/MemorySubAllocator.h
     Include/Atom/RHI/MemoryLinearSubAllocator.h

+ 16 - 0
Gems/Atom/RHI/DX12/Code/CMakeLists.txt

@@ -154,6 +154,22 @@ ly_add_target(
             
 )
 
+ly_add_target(
+    NAME ${gem_name}.Interface STATIC
+    NAMESPACE Gem
+    FILES_CMAKE
+        atom_rhi_dx12_interface_common_files.cmake
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Source
+            ${pal_source_dir}
+        PUBLIC
+            Include
+    BUILD_DEPENDENCIES
+        PUBLIC
+            Gem::${gem_name}.Private.Static
+)
+
 ly_add_target(
     NAME ${gem_name}.Private ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
     NAMESPACE Gem

+ 54 - 0
Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Interface/DX12/RHIDX12Interface.h

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <Atom/RHI/Device.h>
+#include <Atom/RHI/DeviceBuffer.h>
+#include <Atom/RHI/DeviceFence.h>
+#include <Atom/RHI/DeviceImage.h>
+#include <Atom/RHI/PhysicalDevice.h>
+
+#include <AzCore/PlatformIncl.h>
+#include <d3d12.h>
+#include <dxgi.h>
+#include <dxgi1_4.h>
+
+namespace AZ
+{
+    namespace DX12
+    {
+        //! It is important to note that the usage of these functions requires care from the user.
+        //! This includes:
+        //! - Synchronizing with the renderer by waiting on a fence from the FrameGraph before
+        //!   starting execution as well as signaling the FrameGraph to continue execution
+        //! - Leaving the GPU in a valid state
+        //! - Returning resources in a valid state
+
+        //! Provide access to native device handles
+        ID3D12Device5* GetDeviceNativeHandle(RHI::Device& device);
+        IDXGIAdapter3* GetPhysicalDeviceNativeHandle(const RHI::PhysicalDevice& device);
+
+        //! Provide access to native fence handle and value
+        ID3D12Fence* GetFenceNativeHandle(RHI::DeviceFence& fence);
+        uint64_t GetFencePendingValue(RHI::DeviceFence& fence);
+
+        //! Provide access to native buffer resource, heap as well as size and offset
+        ID3D12Resource* GetBufferResource(RHI::DeviceBuffer& buffer);
+        ID3D12Heap* GetBufferHeap(RHI::DeviceBuffer& buffer);
+        size_t GetBufferMemoryViewSize(RHI::DeviceBuffer& buffer);
+        size_t GetBufferAllocationOffset(RHI::DeviceBuffer& buffer);
+        size_t GetBufferHeapOffset(RHI::DeviceBuffer& buffer);
+
+        //! Provide access to native image resource, heap as well as size and offset
+        ID3D12Resource* GetImageResource(RHI::DeviceImage& image);
+        ID3D12Heap* GetImageHeap(RHI::DeviceImage& image);
+        size_t GetImageMemoryViewSize(RHI::DeviceImage& image);
+        size_t GetImageAllocationOffset(RHI::DeviceImage& image);
+        size_t GetImageHeapOffset(RHI::DeviceImage& image);
+    } // namespace DX12
+} // namespace AZ

+ 34 - 0
Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/DX12Bus.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AzCore/EBus/EBus.h>
+
+#include <AzCore/PlatformIncl.h>
+#include <d3d12.h>
+
+namespace AZ::DX12
+{
+    //! Ebus for collecting external handle requirements for creating memory/semaphores
+    class DX12RequirementsRequest : public AZ::EBusTraits
+    {
+    public:
+        virtual ~DX12RequirementsRequest() = default;
+
+        //! Collects requirements for external memory when allocating memory
+        virtual void CollectTransientAttachmentPoolHeapFlags([[maybe_unused]] D3D12_HEAP_FLAGS& flags){};
+        virtual void CollectAllocatorExtraHeapFlags([[maybe_unused]] D3D12_HEAP_FLAGS& flags, [[maybe_unused]] D3D12_HEAP_TYPE heapType){};
+        virtual void CollectFenceFlags([[maybe_unused]] D3D12_FENCE_FLAGS& flags){};
+
+        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+        using MutexType = AZStd::mutex;
+        static constexpr bool LocklessDispatch = true;
+    };
+
+    using DX12RequirementBus = AZ::EBus<DX12RequirementsRequest>;
+} // namespace AZ::DX12

+ 5 - 0
Gems/Atom/RHI/DX12/Code/Source/Platform/Windows/RHI/PhysicalDevice_Windows.cpp

@@ -9,6 +9,8 @@
 #include <RHI/PhysicalDevice_Windows.h>
 #include <AzCore/std/string/conversions.h>
 
+#include <Atom/RHI/RHIBus.h>
+
 namespace AZ
 {
     namespace DX12
@@ -40,6 +42,9 @@ namespace AZ
                 physicalDeviceList.emplace_back(physicalDevice);
             }
 
+            RHI::RHIRequirementRequestBus::Broadcast(
+                &RHI::RHIRequirementsRequest::FilterSupportedPhysicalDevices, physicalDeviceList, RHI::APIIndex::DX12);
+
             return physicalDeviceList;
         }
 

+ 116 - 0
Gems/Atom/RHI/DX12/Code/Source/RHI.Reflect/RHIDX12Interface.cpp

@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <Atom/RHI.Interface/DX12/RHIDX12Interface.h>
+#include <RHI/DX12.h>
+
+#include <RHI/Buffer.h>
+#include <RHI/Device.h>
+#include <RHI/Fence.h>
+#include <RHI/Image.h>
+#include <RHI/PhysicalDevice.h>
+
+namespace AZ
+{
+    namespace DX12
+    {
+        ID3D12Device5* GetDeviceNativeHandle(RHI::Device& device)
+        {
+            AZ_Assert(azrtti_cast<Device*>(&device), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            return static_cast<Device&>(device).GetDevice();
+        }
+
+        IDXGIAdapter3* GetPhysicalDeviceNativeHandle(const RHI::PhysicalDevice& device)
+        {
+            AZ_Assert(azrtti_cast<const PhysicalDevice*>(&device), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            return static_cast<const PhysicalDevice*>(&device)->GetAdapter();
+        }
+
+        ID3D12Fence* GetFenceNativeHandle(RHI::DeviceFence& fence)
+        {
+            AZ_Assert(azrtti_cast<FenceImpl*>(&fence), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            return static_cast<FenceImpl&>(fence).Get().Get();
+        }
+
+        uint64_t GetFencePendingValue(RHI::DeviceFence& fence)
+        {
+            AZ_Assert(azrtti_cast<FenceImpl*>(&fence), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            return static_cast<FenceImpl&>(fence).Get().GetPendingValue();
+        }
+
+        ID3D12Resource* GetBufferResource(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Buffer = static_cast<Buffer&>(buffer);
+            return dx12Buffer.GetMemoryView().GetMemory();
+        }
+
+        ID3D12Heap* GetBufferHeap(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Buffer = static_cast<Buffer&>(buffer);
+            return dx12Buffer.GetMemoryView().GetHeap();
+        }
+
+        size_t GetBufferMemoryViewSize(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Buffer = static_cast<Buffer&>(buffer);
+            return dx12Buffer.GetMemoryView().GetSize();
+        }
+
+        size_t GetBufferAllocationOffset(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Buffer = static_cast<Buffer&>(buffer);
+            return dx12Buffer.GetMemoryView().GetOffset();
+        }
+
+        size_t GetBufferHeapOffset(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Buffer = static_cast<Buffer&>(buffer);
+            return dx12Buffer.GetMemoryView().GetHeapOffset();
+        }
+
+        ID3D12Resource* GetImageResource(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Image = static_cast<Image&>(image);
+            return dx12Image.GetMemoryView().GetMemory();
+        }
+
+        ID3D12Heap* GetImageHeap(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Image = static_cast<Image&>(image);
+            return dx12Image.GetMemoryView().GetHeap();
+        }
+
+        size_t GetImageMemoryViewSize(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Image = static_cast<Image&>(image);
+            return dx12Image.GetMemoryView().GetSize();
+        }
+
+        size_t GetImageAllocationOffset(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Image = static_cast<Image&>(image);
+            return dx12Image.GetMemoryView().GetOffset();
+        }
+
+        size_t GetImageHeapOffset(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a DX12 RHI object", __FUNCTION__);
+            auto& dx12Image = static_cast<Image&>(image);
+            return dx12Image.GetMemoryView().GetHeapOffset();
+        }
+    } // namespace DX12
+} // namespace AZ

+ 13 - 11
Gems/Atom/RHI/DX12/Code/Source/RHI/AliasedHeap.cpp

@@ -23,16 +23,18 @@ namespace AZ
         static size_t CalculateHeapAlignment(D3D12_HEAP_FLAGS heapFlags)
         {
             /**
-            * Heap alignment is the alignment of the actual heap we are allocating, not the base alignment of
-            * of sub-allocations from the heap. That is confusing in the D3D12 docs. The heap itself is
-            * required to be 4MB aligned if it holds MSAA textures. Therefore, this simple metric just
-            * forces 4MB alignment of the heap for all textures, because our chances of having an MSAA target
-            * across the whole frame is high, and the amount of internal fragmentation is low relative to the full
-            * heap size.
-            */
-            if (D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES == heapFlags ||
-                D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES == heapFlags ||
-                D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES == heapFlags)
+             * Heap alignment is the alignment of the actual heap we are allocating, not the base alignment of
+             * of sub-allocations from the heap. That is confusing in the D3D12 docs. The heap itself is
+             * required to be 4MB aligned if it holds MSAA textures. Therefore, this simple metric just
+             * forces 4MB alignment of the heap for all textures, because our chances of having an MSAA target
+             * across the whole frame is high, and the amount of internal fragmentation is low relative to the full
+             * heap size.
+             * To simply test the flags for equality, we mask the D3D12_HEAP_FLAG_SHARED prior to testing.
+             */
+            auto maskedFlags = heapFlags & ~(D3D12_HEAP_FLAG_SHARED);
+            if (D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES == maskedFlags ||
+                D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES == maskedFlags ||
+                D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES == maskedFlags)
             {
                 return D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT;
             }
@@ -60,7 +62,7 @@ namespace AZ
             m_descriptor = static_cast<const Descriptor&>(descriptor);
             
             D3D12_HEAP_DESC heapDesc;
-            heapDesc.Alignment = CalculateHeapAlignment(m_descriptor.m_heapFlags);
+            heapDesc.Alignment = AZStd::max(CalculateHeapAlignment(m_descriptor.m_heapFlags), descriptor.m_alignment);
             // Even the dx12 document said 'but non-aligned SizeInBytes is also supported', but it may lead to TDR issue with some graphics cards
             // Such as nvidia 2070, 2080
             heapDesc.SizeInBytes = RHI::AlignUp<size_t>(descriptor.m_budgetInBytes, heapDesc.Alignment);

+ 16 - 0
Gems/Atom/RHI/DX12/Code/Source/RHI/BufferD3D12MemoryAllocator.cpp

@@ -5,6 +5,7 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
+#include <Atom/RHI/RHIBus.h>
 #include <RHI/BufferD3D12MemoryAllocator.h>
 #include <RHI/Conversions.h>
 #include <RHI/Device.h>
@@ -38,6 +39,21 @@ namespace AZ
             AZ_PROFILE_FUNCTION(RHI);
 
 #ifdef USE_AMD_D3D12MA
+            struct
+            {
+                size_t m_alignment = 0;
+                void operator=(size_t value)
+                {
+                    m_alignment = AZStd::max(m_alignment, value);
+                }
+            } alignment;
+            RHI::RHIRequirementRequestBus::BroadcastResult(
+                alignment, &RHI::RHIRequirementsRequest::GetRequiredAlignment, *m_descriptor.m_device);
+            if (alignment.m_alignment && sizeInBytes > alignment.m_alignment)
+            {
+                sizeInBytes = ((sizeInBytes + alignment.m_alignment - 1) / alignment.m_alignment) * alignment.m_alignment;
+            }
+
             RHI::BufferDescriptor bufferDescriptor;
             bufferDescriptor.m_byteCount = azlossy_caster(sizeInBytes);
             bufferDescriptor.m_bindFlags = m_descriptor.m_bindFlags;

+ 24 - 20
Gems/Atom/RHI/DX12/Code/Source/RHI/Device.cpp

@@ -22,6 +22,8 @@
 #include <AzCore/Memory/SystemAllocator.h>
 #include <AzCore/Memory/AllocatorInstance.h>
 
+#include <Atom/RHI.Reflect/DX12/DX12Bus.h>
+
 namespace AZ
 {
     namespace DX12
@@ -29,7 +31,7 @@ namespace AZ
 #ifdef USE_AMD_D3D12MA
         namespace
         {
-            constexpr D3D12MA::ALLOCATOR_FLAGS s_D3d12maAllocatorFlags = D3D12MA::ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED;
+            constexpr D3D12MA::ALLOCATOR_FLAGS s_D3d12maAllocatorFlags = static_cast<D3D12MA::ALLOCATOR_FLAGS>(D3D12MA::ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED);
             D3D12MA::ALLOCATION_CALLBACKS s_AllocationCallbacks = {};
 
             // constant value attached to D3D12MA cpu memory allocations
@@ -533,15 +535,17 @@ namespace AZ
                 clearValue = ConvertClearValue(imageDescriptor.m_format, *optimizedClearValue);
             }
 
+            D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE;
+            DX12RequirementBus::Broadcast(&DX12RequirementBus::Events::CollectAllocatorExtraHeapFlags, heapFlags, heapType);
+
             Microsoft::WRL::ComPtr<ID3D12Resource> resource;
             HRESULT result = m_dx12Device->CreateCommittedResource(
-                &heapProperties, 
-                D3D12_HEAP_FLAG_NONE, 
-                &resourceDesc, 
-                initialState, 
-                (isOutputMergerAttachment && optimizedClearValue) ? &clearValue : nullptr, 
-                IID_GRAPHICS_PPV_ARGS(resource.GetAddressOf())
-            );
+                &heapProperties,
+                heapFlags,
+                &resourceDesc,
+                initialState,
+                (isOutputMergerAttachment && optimizedClearValue) ? &clearValue : nullptr,
+                IID_GRAPHICS_PPV_ARGS(resource.GetAddressOf()));
 
             AZ_RHI_DUMP_POOL_INFO_ON_FAIL(SUCCEEDED(result));
             AssertSuccess(result);
@@ -549,7 +553,7 @@ namespace AZ
             D3D12_RESOURCE_ALLOCATION_INFO allocationInfo;
             GetImageAllocationInfo(imageDescriptor, allocationInfo);
 
-            return MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Image);
+            return MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Image, nullptr, 0);
         }
 
         void Device::ConvertBufferDescriptorToResourceDesc(
@@ -577,6 +581,7 @@ namespace AZ
 
             D3D12MA::ALLOCATION_DESC allocDesc = {};
             allocDesc.HeapType = heapType;
+            DX12RequirementBus::Broadcast(&DX12RequirementBus::Events::CollectAllocatorExtraHeapFlags, allocDesc.ExtraHeapFlags, heapType);
 
             D3D12MA::Allocation* allocation = nullptr;
             Microsoft::WRL::ComPtr<ID3D12Resource> resource;
@@ -600,16 +605,13 @@ namespace AZ
             D3D12_RESOURCE_DESC resourceDesc;
             ConvertBufferDescriptorToResourceDesc(bufferDescriptor, initialState, resourceDesc);
 
+            D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE;
+            DX12RequirementBus::Broadcast(&DX12RequirementBus::Events::CollectAllocatorExtraHeapFlags, heapFlags, heapType);
+
             CD3DX12_HEAP_PROPERTIES heapProperties(heapType);
             Microsoft::WRL::ComPtr<ID3D12Resource> resource;
             HRESULT result = m_dx12Device->CreateCommittedResource(
-                &heapProperties, 
-                D3D12_HEAP_FLAG_NONE, 
-                &resourceDesc, 
-                initialState, 
-                nullptr, 
-                IID_GRAPHICS_PPV_ARGS(resource.GetAddressOf())
-            );
+                &heapProperties, heapFlags, &resourceDesc, initialState, nullptr, IID_GRAPHICS_PPV_ARGS(resource.GetAddressOf()));
             AZ_RHI_DUMP_POOL_INFO_ON_FAIL(SUCCEEDED(result));
             AssertSuccess(result);
 
@@ -617,7 +619,7 @@ namespace AZ
             allocationInfo.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
             allocationInfo.SizeInBytes = RHI::AlignUp(resourceDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT);
 
-            return MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Buffer);
+            return MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Buffer, nullptr, 0);
         }
 
         MemoryView Device::CreateBufferPlaced(
@@ -645,7 +647,8 @@ namespace AZ
             AZ_RHI_DUMP_POOL_INFO_ON_FAIL(SUCCEEDED(result));
             AssertSuccess(result);
 
-            return MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Buffer);
+            return MemoryView(
+                resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Buffer, heap, heapByteOffset);
         }
 
         static uint64_t GetPlacedTextureAlignment(const RHI::ImageDescriptor& imageDescriptor)
@@ -701,7 +704,8 @@ namespace AZ
             AZ_RHI_DUMP_POOL_INFO_ON_FAIL(SUCCEEDED(result));
             AssertSuccess(result);
 
-            return MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Image);
+            return MemoryView(
+                resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Image, heap, heapByteOffset);
         }
 
         MemoryView Device::CreateImageReserved(
@@ -748,7 +752,7 @@ namespace AZ
             D3D12_RESOURCE_ALLOCATION_INFO allocationInfo;
             GetImageAllocationInfo(imageDescriptor, allocationInfo);
 
-            return MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Image);
+            return MemoryView(resource.Get(), 0, allocationInfo.SizeInBytes, allocationInfo.Alignment, MemoryViewType::Image, nullptr, 0);
         }
 
         void Device::GetImageAllocationInfo(

+ 5 - 1
Gems/Atom/RHI/DX12/Code/Source/RHI/Fence.cpp

@@ -8,6 +8,8 @@
 #include <RHI/Fence.h>
 #include <RHI/Device.h>
 
+#include <Atom/RHI.Reflect/DX12/DX12Bus.h>
+
 namespace AZ
 {
     namespace DX12
@@ -32,7 +34,9 @@ namespace AZ
         RHI::ResultCode Fence::Init(ID3D12DeviceX* dx12Device, RHI::FenceState initialState)
         {
             Microsoft::WRL::ComPtr<ID3D12Fence> fencePtr;
-            if (!AssertSuccess(dx12Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(fencePtr.GetAddressOf()))))
+            D3D12_FENCE_FLAGS flags = D3D12_FENCE_FLAG_NONE;
+            DX12RequirementBus::Broadcast(&DX12RequirementBus::Events::CollectFenceFlags, flags);
+            if (!AssertSuccess(dx12Device->CreateFence(0, flags, IID_GRAPHICS_PPV_ARGS(fencePtr.GetAddressOf()))))
             {
                 AZ_Error("Fence", false, "Failed to initialize ID3D12Fence.");
                 return RHI::ResultCode::Fail;

+ 1 - 0
Gems/Atom/RHI/DX12/Code/Source/RHI/Fence.h

@@ -113,6 +113,7 @@ namespace AZ
         {
         public:
             AZ_CLASS_ALLOCATOR(FenceImpl, AZ::SystemAllocator);
+            AZ_RTTI(FenceImpl, "{6CD62A6F-FF00-4F6D-990B-59E220083939}", RHI::DeviceFence);
 
             static RHI::Ptr<FenceImpl> Create();
 

+ 38 - 5
Gems/Atom/RHI/DX12/Code/Source/RHI/MemoryView.cpp

@@ -7,11 +7,11 @@
  */
 #include <RHI/MemoryView.h>
 
-#include <AzCore/Debug/Profiler.h>
 #include <AzCore/Casting/numeric_cast.h>
 #include <AzCore/Debug/Profiler.h>
-#include <AzCore/std/string/string.h>
 #include <AzCore/std/string/conversions.h>
+#include <AzCore/std/string/string.h>
+#include <dx12ma/D3D12MemAlloc.h>
 
 AZ_DECLARE_BUDGET(RHI);
 
@@ -19,15 +19,24 @@ namespace AZ
 {
     namespace DX12
     {
-        MemoryView::MemoryView(const MemoryAllocation& memAllocation, MemoryViewType viewType)
+        MemoryView::MemoryView(const MemoryAllocation& memAllocation, MemoryViewType viewType, ID3D12Heap* heap, size_t heapOffset)
             : m_memoryAllocation(memAllocation)
             , m_viewType(viewType)
+            , m_heap(heap)
+            , m_heapOffset(heapOffset)
         {
             Construct();
         }
 
-        MemoryView::MemoryView(RHI::Ptr<Memory> memory, size_t offset, size_t size, size_t alignment, MemoryViewType viewType)
-            : MemoryView(MemoryAllocation(memory, offset, size, alignment), viewType)
+        MemoryView::MemoryView(
+            RHI::Ptr<Memory> memory,
+            size_t offset,
+            size_t size,
+            size_t alignment,
+            MemoryViewType viewType,
+            ID3D12Heap* heap,
+            size_t heapOffset)
+            : MemoryView(MemoryAllocation(memory, offset, size, alignment), viewType, heap, heapOffset)
         {
         }
 
@@ -140,6 +149,30 @@ namespace AZ
             }
         }
 
+        ID3D12Heap* MemoryView::GetHeap()
+        {
+            if (m_d3d12maAllocation)
+            {
+                return m_d3d12maAllocation->GetHeap();
+            }
+            else
+            {
+                return m_heap;
+            }
+        }
+
+        size_t MemoryView::GetHeapOffset()
+        {
+            if (m_d3d12maAllocation)
+            {
+                return m_d3d12maAllocation->GetOffset();
+            }
+            else
+            {
+                return m_heapOffset;
+            }
+        }
+
         void MemoryView::Construct()
         {
             if (m_memoryAllocation.m_memory)

+ 19 - 2
Gems/Atom/RHI/DX12/Code/Source/RHI/MemoryView.h

@@ -47,9 +47,17 @@ namespace AZ
 
         public:
             MemoryView() = default;
-            MemoryView(RHI::Ptr<Memory> memory, size_t offset, size_t size, size_t alignment, MemoryViewType viewType);
+            MemoryView(
+                RHI::Ptr<Memory> memory,
+                size_t offset,
+                size_t size,
+                size_t alignment,
+                MemoryViewType viewType,
+                ID3D12Heap* heap = nullptr,
+                size_t heapOffset = 0ull);
             MemoryView(D3D12MA::Allocation* allocation, RHI::Ptr<Memory> memory, size_t offset, size_t size, size_t alignment, MemoryViewType viewType);
-            MemoryView(const MemoryAllocation& memAllocation, MemoryViewType viewType);
+            MemoryView(
+                const MemoryAllocation& memAllocation, MemoryViewType viewType, ID3D12Heap* heap = nullptr, size_t heapOffset = 0ull);
 
             /// Supports copy and move construction / assignment.
             MemoryView(const MemoryView& rhs) = default;
@@ -89,6 +97,12 @@ namespace AZ
             /// Sets the name of the ID3D12Resource.
             void SetName(const AZStd::wstring_view& name);
 
+            // Heap the Memory was allocated in. Will be nullptr for committed resources
+            ID3D12Heap* GetHeap();
+
+            // Offset in the heap that the Memory is allocated in. Will be zero for committed resources
+            size_t GetHeapOffset();
+
         private:
             void Construct();
 
@@ -99,6 +113,9 @@ namespace AZ
 
             MemoryViewType m_viewType;
 
+            ID3D12Heap* m_heap = nullptr;
+            size_t m_heapOffset = 0;
+
             D3D12MA::Allocation* m_d3d12maAllocation = nullptr; //filled in for allocations created through D3D12MA
         };
     }

+ 21 - 4
Gems/Atom/RHI/DX12/Code/Source/RHI/TransientAttachmentPool.cpp

@@ -5,15 +5,18 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
-#include <RHI/TransientAttachmentPool.h>
 #include <Atom/RHI.Reflect/TransientAttachmentStatistics.h>
 #include <Atom/RHI.Reflect/TransientBufferDescriptor.h>
 #include <Atom/RHI.Reflect/TransientImageDescriptor.h>
+#include <Atom/RHI/RHIBus.h>
+#include <AzCore/std/sort.h>
 #include <RHI/Buffer.h>
 #include <RHI/Device.h>
 #include <RHI/Image.h>
 #include <RHI/Scope.h>
-#include <AzCore/std/sort.h>
+#include <RHI/TransientAttachmentPool.h>
+
+#include <Atom/RHI.Reflect/DX12/DX12Bus.h>
 
 //#define DX12_TRANSIENT_ATTACHMENT_POOL_DEBUG_LOG
 
@@ -43,10 +46,24 @@ namespace AZ
                 AliasedAttachmentAllocator::Descriptor heapAllocatorDesc;
                 heapAllocatorDesc.m_cacheSize = ObjectCacheSize;
                 heapAllocatorDesc.m_heapFlags = D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES;
-                heapAllocatorDesc.m_budgetInBytes = descriptor.m_bufferBudgetInBytes + descriptor.m_imageBudgetInBytes + descriptor.m_renderTargetBudgetInBytes;
-                heapAllocatorDesc.m_alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
+
+                DX12RequirementBus::Broadcast(
+                    &DX12RequirementBus::Events::CollectTransientAttachmentPoolHeapFlags, heapAllocatorDesc.m_heapFlags);
+
+                heapAllocatorDesc.m_budgetInBytes =
+                    descriptor.m_bufferBudgetInBytes + descriptor.m_imageBudgetInBytes + descriptor.m_renderTargetBudgetInBytes;
                 heapAllocatorDesc.m_resourceTypeMask = RHI::AliasedResourceTypeFlags::All;
                 heapAllocatorDesc.m_allocationParameters = descriptor.m_heapParameters;
+                struct
+                {
+                    size_t m_alignment = 0;
+                    void operator=(size_t value)
+                    {
+                        m_alignment = AZStd::max(m_alignment, value);
+                    }
+                } alignment;
+                RHI::RHIRequirementRequestBus::BroadcastResult(alignment, &RHI::RHIRequirementsRequest::GetRequiredAlignment, device);
+                heapAllocatorDesc.m_alignment = AZStd::max<size_t>(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, alignment.m_alignment);
 
                 RHI::Ptr<AliasedAttachmentAllocator> allocator = AliasedAttachmentAllocator::Create();
                 allocator->SetName(Name("TransientAttachmentPool_[Shared]"));

+ 12 - 0
Gems/Atom/RHI/DX12/Code/atom_rhi_dx12_interface_common_files.cmake

@@ -0,0 +1,12 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    Include/Atom/RHI.Interface/DX12/RHIDX12Interface.h
+    Source/RHI.Reflect/RHIDX12Interface.cpp
+)

+ 2 - 0
Gems/Atom/RHI/Metal/Code/Source/Platform/Mac/RHI/Metal_RHI_Mac.cpp

@@ -41,6 +41,8 @@ namespace Platform
                 physicalDeviceList.emplace_back(physicalDevice);
             }
         }
+        RHI::RHIRequirementRequestBus::Broadcast(
+            &RHI::RHIRequirementsRequest::FilterSupportedPhysicalDevices, physicalDeviceList, RHI::APIIndex::Metal);
         return physicalDeviceList;
     }
     

+ 17 - 0
Gems/Atom/RHI/Vulkan/Code/CMakeLists.txt

@@ -157,6 +157,23 @@ ly_add_target(
             ${DISABLE_TIMELINE_SEMAPHORES_DEFINE}
 )
 
+ly_add_target(
+    NAME ${gem_name}.Interface STATIC
+    NAMESPACE Gem
+    FILES_CMAKE
+        atom_rhi_vulkan_interface_files.cmake
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Source
+            ${pal_source_dir}
+        PUBLIC
+            Include
+            ${pal_include_dir}
+    BUILD_DEPENDENCIES
+        PUBLIC
+            Gem::${gem_name}.Private.Static
+)
+
 ly_add_target(
     NAME ${gem_name}.Private ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
     NAMESPACE Gem

+ 51 - 0
Gems/Atom/RHI/Vulkan/Code/Include/Atom/RHI.Interface/Vulkan/RHIVulkanInterface.h

@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <Atom/RHI/Device.h>
+#include <Atom/RHI/DeviceBuffer.h>
+#include <Atom/RHI/DeviceFence.h>
+#include <Atom/RHI/DeviceImage.h>
+#include <Atom/RHI/PhysicalDevice.h>
+
+#include <vulkan/vulkan.h>
+
+namespace AZ
+{
+    namespace Vulkan
+    {
+        //! It is important to note that the usage of these functions requires care from the user.
+        //! This includes:
+        //! - Synchronizing with the renderer by waiting on a fence from the FrameGraph before
+        //!   starting execution as well as signaling the FrameGraph to continue execution
+        //! - Leaving the GPU in a valid state
+        //! - Returning resources in a valid state
+
+        //! Provide access to native device handles
+        VkDevice GetDeviceNativeHandle(RHI::Device& device);
+        VkPhysicalDevice GetPhysicalDeviceNativeHandle(const RHI::PhysicalDevice& device);
+
+        //! Provide access to native fence handle and value
+        VkSemaphore GetFenceNativeHandle(RHI::DeviceFence& fence);
+        uint64_t GetFencePendingValue(RHI::DeviceFence& fence);
+
+        //! Provide access to native VkBuffer, VKDeviceMemory as well as size and offset
+        VkBuffer GetNativeBuffer(RHI::DeviceBuffer& buffer);
+        VkDeviceMemory GetBufferMemory(RHI::DeviceBuffer& buffer);
+        size_t GetBufferMemoryViewSize(RHI::DeviceBuffer& buffer);
+        size_t GetBufferAllocationSize(RHI::DeviceBuffer& buffer);
+        size_t GetBufferAllocationOffset(RHI::DeviceBuffer& buffer);
+
+        //! Provide access to native VkImage, VKDeviceMemory as well as size and offset
+        VkImage GetNativeImage(RHI::DeviceImage& image);
+        VkDeviceMemory GetImageMemory(RHI::DeviceImage& image);
+        size_t GetImageMemoryViewSize(RHI::DeviceImage& image);
+        size_t GetImageAllocationSize(RHI::DeviceImage& image);
+        size_t GetImageAllocationOffset(RHI::DeviceImage& image);
+    } // namespace Vulkan
+} // namespace AZ

+ 18 - 0
Gems/Atom/RHI/Vulkan/Code/Include/Atom/RHI.Reflect/Vulkan/VulkanBus.h

@@ -50,6 +50,24 @@ namespace AZ::Vulkan
 
     using DeviceRequirementBus = AZ::EBus<DeviceRequirementsRequest>;
 
+    //! Ebus for collecting requirements external handle requirements for creating memory/semaphores
+    class ExternalHandleRequirementsRequest : public AZ::EBusTraits
+    {
+    public:
+        virtual ~ExternalHandleRequirementsRequest() = default;
+
+        //! Collects requirements for external memory when allocating memory
+        virtual void CollectExternalMemoryRequirements([[maybe_unused]] VkExternalMemoryHandleTypeFlagsKHR& flags){};
+        //! Collect requirements for semaphore export when creating timeline semaphores
+        virtual void CollectSemaphoreExportHandleTypes([[maybe_unused]] VkExternalSemaphoreHandleTypeFlags& flags){};
+
+        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+        using MutexType = AZStd::mutex;
+        static constexpr bool LocklessDispatch = true;
+    };
+
+    using ExternalHandleRequirementBus = AZ::EBus<ExternalHandleRequirementsRequest>;
+
     //! Notifications related to Vulkan instance operations.
     class InstanceNotification
         : public AZ::EBusTraits

+ 140 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI.Interface/RHIVulkanInterface.cpp

@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <Atom/RHI.Interface/Vulkan/RHIVulkanInterface.h>
+
+#include <RHI/Buffer.h>
+#include <RHI/Device.h>
+#include <RHI/Fence.h>
+#include <RHI/Image.h>
+#include <RHI/PhysicalDevice.h>
+#include <RHI/TimelineSemaphoreFence.h>
+
+namespace AZ
+{
+    namespace Vulkan
+    {
+        VkDevice GetDeviceNativeHandle(RHI::Device& device)
+        {
+            AZ_Assert(azrtti_cast<Device*>(&device), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            return static_cast<Device&>(device).GetNativeDevice();
+        }
+
+        VkPhysicalDevice GetPhysicalDeviceNativeHandle(const RHI::PhysicalDevice& device)
+        {
+            AZ_Assert(azrtti_cast<const PhysicalDevice*>(&device), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            return static_cast<const PhysicalDevice&>(device).GetNativePhysicalDevice();
+        }
+
+        VkSemaphore GetFenceNativeHandle(RHI::DeviceFence& fence)
+        {
+            AZ_Assert(azrtti_cast<Fence*>(&fence), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            auto& vulkanFence = static_cast<Fence&>(fence);
+            auto timelineSemaphoreFence = azrtti_cast<TimelineSemaphoreFence*>(&vulkanFence.GetFenceBase());
+            AZ_Assert(timelineSemaphoreFence, "Cannot return a VkSemaphore from a binary fence");
+            return timelineSemaphoreFence->GetNativeSemaphore();
+        }
+
+        uint64_t GetFencePendingValue(RHI::DeviceFence& fence)
+        {
+            AZ_Assert(azrtti_cast<Fence*>(&fence), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            auto& vulkanFence = static_cast<Fence&>(fence);
+            auto timelineSemaphoreFence = azrtti_cast<TimelineSemaphoreFence*>(&vulkanFence.GetFenceBase());
+            AZ_Assert(timelineSemaphoreFence, "Cannot return a VkSemaphore from a binary fence");
+            return timelineSemaphoreFence->GetPendingValue();
+        }
+
+        VkBuffer GetNativeBuffer(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            return static_cast<Buffer&>(buffer).GetBufferMemoryView()->GetNativeBuffer();
+        }
+
+        VkDeviceMemory GetBufferMemory(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            return static_cast<Buffer&>(buffer).GetBufferMemoryView()->GetNativeDeviceMemory();
+        }
+
+        size_t GetBufferMemoryViewSize(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            return static_cast<Buffer&>(buffer).GetBufferMemoryView()->GetSize();
+        }
+
+        size_t GetBufferAllocationSize(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            auto& vulkanBuffer = static_cast<Buffer&>(buffer);
+            if (vulkanBuffer.GetBufferMemoryView()->GetAllocation()->GetMemoryView().GetAllocation()->GetVmaAllocation())
+            {
+                return vulkanBuffer.GetBufferMemoryView()->GetAllocation()->GetMemoryView().GetAllocation()->GetBlockSize();
+            }
+            else
+            {
+                return vulkanBuffer.GetBufferMemoryView()->GetAllocation()->GetAllocationSize();
+            }
+        }
+
+        size_t GetBufferAllocationOffset(RHI::DeviceBuffer& buffer)
+        {
+            AZ_Assert(azrtti_cast<Buffer*>(&buffer), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            auto& vulkanBuffer = static_cast<Buffer&>(buffer);
+            if (vulkanBuffer.GetBufferMemoryView()->GetAllocation()->GetMemoryView().GetAllocation()->GetVmaAllocation())
+            {
+                return vulkanBuffer.GetBufferMemoryView()->GetAllocation()->GetMemoryView().GetAllocation()->GetOffset() +
+                    vulkanBuffer.GetBufferMemoryView()->GetAllocation()->GetMemoryViewOffset() +
+                    vulkanBuffer.GetBufferMemoryView()->GetOffset();
+            }
+            else
+            {
+                return vulkanBuffer.GetBufferMemoryView()->GetAllocation()->GetMemoryViewOffset() +
+                    vulkanBuffer.GetBufferMemoryView()->GetOffset();
+            }
+        }
+
+        VkImage GetNativeImage(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            return static_cast<Image&>(image).GetNativeImage();
+        }
+
+        VkDeviceMemory GetImageMemory(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            return static_cast<Image&>(image).GetMemoryView().GetNativeDeviceMemory();
+        }
+
+        size_t GetImageMemoryViewSize(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            return static_cast<Image&>(image).GetMemoryView().GetSize();
+        }
+
+        size_t GetImageAllocationSize(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            auto& vulkanImage = static_cast<Image&>(image);
+            if (vulkanImage.GetMemoryView().GetAllocation()->GetVmaAllocation())
+            {
+                return vulkanImage.GetMemoryView().GetAllocation()->GetBlockSize();
+            }
+            else
+            {
+                return vulkanImage.GetMemoryView().GetAllocation()->GetSize();
+            }
+        }
+
+        size_t GetImageAllocationOffset(RHI::DeviceImage& image)
+        {
+            AZ_Assert(azrtti_cast<Image*>(&image), "%s can only be called with a Vulkan RHI object", __FUNCTION__);
+            auto& vulkanImage = static_cast<Image&>(image);
+            return vulkanImage.GetMemoryView().GetAllocation()->GetOffset() + vulkanImage.GetMemoryView().GetOffset();
+        }
+    } // namespace Vulkan
+} // namespace AZ

+ 1 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/AliasedHeap.cpp

@@ -43,6 +43,7 @@ namespace AZ
 
             VmaAllocation vmaAllocation;
             VkMemoryRequirements memReq = m_descriptor.m_memoryRequirements;
+            memReq.alignment = AZStd::max(memReq.alignment, descriptor.m_alignment);
             memReq.size = descriptor.m_budgetInBytes;
             VkResult vkResult = vmaAllocateMemory(device.GetVmaAllocator(), &memReq, &allocInfo, &vmaAllocation, nullptr);
             AssertSuccess(vkResult);

+ 43 - 6
Gems/Atom/RHI/Vulkan/Code/Source/RHI/BufferMemory.cpp

@@ -6,12 +6,13 @@
  *
  */
 
+#include <Atom/RHI.Reflect/VkAllocator.h>
+#include <Atom/RHI.Reflect/Vulkan/Conversion.h>
+#include <Atom/RHI/RHIBus.h>
 #include <Atom_RHI_Vulkan_Platform.h>
 #include <RHI/BufferMemory.h>
 #include <RHI/Conversion.h>
 #include <RHI/Device.h>
-#include <Atom/RHI.Reflect/Vulkan/Conversion.h>
-#include <Atom/RHI.Reflect/VkAllocator.h>
 
 namespace AZ
 {
@@ -31,7 +32,7 @@ namespace AZ
                 device.GetVmaAllocator(),
                 memoryView.GetAllocation()->GetVmaAllocation(),
                 memoryView.GetOffset(),
-                &createInfo.m_vkCreateInfo,
+                createInfo.GetCreateInfo(),
                 &m_vkBuffer);
 
             AssertSuccess(vkResult);
@@ -50,19 +51,35 @@ namespace AZ
             Base::Init(device);
             m_descriptor = descriptor;
             BufferCreateInfo createInfo = device.BuildBufferCreateInfo(descriptor);
+            struct
+            {
+                size_t m_alignment = 0;
+                void operator=(size_t value)
+                {
+                    m_alignment = AZStd::max(m_alignment, value);
+                }
+            } alignment;
+            RHI::RHIRequirementRequestBus::BroadcastResult(alignment, &RHI::RHIRequirementsRequest::GetRequiredAlignment, device);
+            auto updatedCreateInfo{ createInfo.GetCreateInfo() };
+            if (alignment.m_alignment && updatedCreateInfo->size > alignment.m_alignment)
+            {
+                updatedCreateInfo->size =
+                    ((updatedCreateInfo->size + alignment.m_alignment - 1) / alignment.m_alignment) * alignment.m_alignment;
+            }
+
             VmaAllocationCreateInfo allocInfo = GetVmaAllocationCreateInfo(descriptor.m_heapMemoryLevel);
             VmaAllocation vmaAlloc;
             VkResult vkResult;
             // Creates the buffer, allocates new memory and bind it to the buffer.
             vkResult = vmaCreateBufferWithAlignment(
                 device.GetVmaAllocator(),
-                &createInfo.m_vkCreateInfo,
+                createInfo.GetCreateInfo(),
                 &allocInfo,
                 RHI::IsPowerOfTwo(descriptor.m_alignment) ? descriptor.m_alignment : 1,
                 &m_vkBuffer,
                 &vmaAlloc,
                 nullptr);
-            
+
             AssertSuccess(vkResult);
             RHI::ResultCode result = ConvertResult(vkResult);
             RETURN_RESULT_IF_UNSUCCESSFUL(result);
@@ -70,7 +87,7 @@ namespace AZ
             RHI::Ptr<VulkanMemoryAllocation> alloc = VulkanMemoryAllocation::Create();
             alloc->Init(device, vmaAlloc);
             m_memoryView = MemoryView(alloc);
-            m_sharingMode = createInfo.m_vkCreateInfo.sharingMode;
+            m_sharingMode = createInfo.GetCreateInfo()->sharingMode;
             return RHI::ResultCode::Success;
         }
 
@@ -104,6 +121,26 @@ namespace AZ
             return m_memoryView.GetSize();
         }
 
+        const MemoryView& BufferMemory::GetMemoryView() const
+        {
+            return m_memoryView;
+        }
+
+        size_t BufferMemory::GetAllocationSize() const
+        {
+            return m_memoryView.GetAllocation()->GetSize();
+        }
+
+        VkDeviceMemory BufferMemory::GetNativeDeviceMemory() const
+        {
+            return m_memoryView.GetNativeDeviceMemory();
+        }
+
+        size_t BufferMemory::GetMemoryViewOffset() const
+        {
+            return m_memoryView.GetOffset();
+        }
+
         void BufferMemory::SetNameInternal(const AZStd::string_view& name)
         {
             if (IsInitialized() && !name.empty())

+ 4 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/BufferMemory.h

@@ -61,7 +61,11 @@ namespace AZ
             const Descriptor& GetDescriptor() const;
             VkSharingMode GetSharingMode() const;
 
+            VkDeviceMemory GetNativeDeviceMemory() const;
+            size_t GetMemoryViewOffset() const;
+            size_t GetAllocationSize() const;
             size_t GetSize() const;
+            const MemoryView& GetMemoryView() const;
 
         private:
             BufferMemory() = default;

+ 5 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/BufferMemoryView.h

@@ -33,6 +33,11 @@ namespace AZ
             {
                 return GetAllocation()->GetNativeBuffer();
             }
+
+            VkDeviceMemory GetNativeDeviceMemory() const
+            {
+                return GetAllocation()->GetNativeDeviceMemory();
+            }
         };
     }
 }

+ 44 - 4
Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.cpp

@@ -498,7 +498,8 @@ namespace AZ
                 // This will not allocate or bind memory.
                 ImageCreateInfo createInfo = BuildImageCreateInfo(descriptor);
                 VkImage vkImage = VK_NULL_HANDLE;
-                VkResult vkResult = GetContext().CreateImage(GetNativeDevice(), &createInfo.m_vkCreateInfo, VkSystemAllocator::Get(), &vkImage);
+                VkResult vkResult =
+                    GetContext().CreateImage(GetNativeDevice(), createInfo.GetCreateInfo(), VkSystemAllocator::Get(), &vkImage);
                 AssertSuccess(vkResult);
 
                 VkMemoryRequirements memoryRequirements = {};
@@ -525,7 +526,8 @@ namespace AZ
                 // This will not allocate or bind memory.
                 BufferCreateInfo createInfo = BuildBufferCreateInfo(descriptor);
                 VkBuffer vkBuffer = VK_NULL_HANDLE;
-                VkResult vkResult = GetContext().CreateBuffer(GetNativeDevice(), &createInfo.m_vkCreateInfo, VkSystemAllocator::Get(), &vkBuffer);
+                VkResult vkResult =
+                    GetContext().CreateBuffer(GetNativeDevice(), createInfo.GetCreateInfo(), VkSystemAllocator::Get(), &vkBuffer);
                 AssertSuccess(vkResult);
 
                 VkMemoryRequirements memoryRequirements = {};
@@ -1348,6 +1350,17 @@ namespace AZ
             allocatorInfo.pVulkanFunctions = &vulkanFunctions;
             allocatorInfo.pAllocationCallbacks = VkSystemAllocator::Get();
 
+            VkExternalMemoryHandleTypeFlagsKHR externalHandleTypeFlags = 0;
+            ExternalHandleRequirementBus::Broadcast(
+                &ExternalHandleRequirementBus::Events::CollectExternalMemoryRequirements, externalHandleTypeFlags);
+            AZStd::vector<VkExternalMemoryHandleTypeFlagsKHR> externalTypes;
+            if (externalHandleTypeFlags != 0)
+            {
+                externalTypes = AZStd::vector<VkExternalMemoryHandleTypeFlagsKHR>(
+                    physicalDevice.GetMemoryProperties().memoryTypeCount, externalHandleTypeFlags);
+                allocatorInfo.pTypeExternalMemoryHandleTypes = externalTypes.data();
+            }
+
             if (GetContext().GetBufferMemoryRequirements2 && GetContext().GetImageMemoryRequirements2)
             {
                 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
@@ -1492,7 +1505,7 @@ namespace AZ
             AZ_Assert(descriptor.m_sharedQueueMask != RHI::HardwareQueueClassMask::None, "Invalid shared queue mask");
             createInfo.m_queueFamilyIndices = GetCommandQueueContext().GetQueueFamilyIndices(descriptor.m_sharedQueueMask);
 
-            auto& vkCreateInfo = createInfo.m_vkCreateInfo;
+            VkBufferCreateInfo vkCreateInfo = {};
             vkCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
             vkCreateInfo.pNext = nullptr;
             vkCreateInfo.flags = 0;
@@ -1509,6 +1522,20 @@ namespace AZ
                 : VK_SHARING_MODE_CONCURRENT;
             vkCreateInfo.queueFamilyIndexCount = static_cast<uint32_t>(createInfo.m_queueFamilyIndices.size());
             vkCreateInfo.pQueueFamilyIndices = createInfo.m_queueFamilyIndices.empty() ? nullptr : createInfo.m_queueFamilyIndices.data();
+            createInfo.SetCreateInfo(vkCreateInfo);
+
+            VkExternalMemoryHandleTypeFlagsKHR externalHandleTypeFlags = 0;
+            ExternalHandleRequirementBus::Broadcast(
+                &ExternalHandleRequirementBus::Events::CollectExternalMemoryRequirements, externalHandleTypeFlags);
+            if (externalHandleTypeFlags != 0)
+            {
+                VkExternalMemoryBufferCreateInfo externalInfo = {};
+                externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO;
+                externalInfo.handleTypes = externalHandleTypeFlags;
+                externalInfo.pNext = nullptr;
+                createInfo.SetExternalCreateInfo(externalInfo);
+            }
+
             return createInfo;
         }
 
@@ -1518,7 +1545,7 @@ namespace AZ
 
             ImageCreateInfo createInfo;
 
-            auto& vkCreateInfo = createInfo.m_vkCreateInfo;
+            VkImageCreateInfo vkCreateInfo = {};
             vkCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
             vkCreateInfo.pNext = nullptr;
             vkCreateInfo.format = ConvertFormat(descriptor.m_format);
@@ -1567,6 +1594,19 @@ namespace AZ
             vkCreateInfo.queueFamilyIndexCount = static_cast<uint32_t>(createInfo.m_queueFamilyIndices.size());
             vkCreateInfo.pQueueFamilyIndices = createInfo.m_queueFamilyIndices.empty() ? nullptr : createInfo.m_queueFamilyIndices.data();
             vkCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+            createInfo.SetCreateInfo(vkCreateInfo);
+
+            VkExternalMemoryHandleTypeFlagsKHR externalHandleTypeFlags = 0;
+            ExternalHandleRequirementBus::Broadcast(
+                &ExternalHandleRequirementBus::Events::CollectExternalMemoryRequirements, externalHandleTypeFlags);
+            if (externalHandleTypeFlags != 0)
+            {
+                VkExternalMemoryImageCreateInfo externalInfo = {};
+                externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
+                externalInfo.handleTypes = externalHandleTypeFlags;
+                externalInfo.pNext = nullptr;
+                createInfo.SetExternalCreateInfo(externalInfo);
+            }
 
             return createInfo;
         }

+ 32 - 5
Gems/Atom/RHI/Vulkan/Code/Source/RHI/Device.h

@@ -17,6 +17,7 @@
 #include <AtomCore/std/containers/lru_cache.h>
 #include <AzCore/Memory/SystemAllocator.h>
 #include <AzCore/RTTI/TypeInfo.h>
+#include <AzCore/base.h>
 #include <AzCore/std/containers/array.h>
 #include <AzCore/std/containers/list.h>
 #include <AzCore/std/containers/unordered_map.h>
@@ -54,17 +55,43 @@ namespace AZ
         //! Helper class to contain a vulkan create info structure
         //! Since the create info points into memory arrays, we need to keep the
         //! arrays alive when returning the create info from a function.
-        template<class T>
+        template<class T, class ExtT>
         struct CreateInfoContainer
         {
-            //! Vulkan create info structure
-            T m_vkCreateInfo = {};
+            CreateInfoContainer() = default;
+            AZ_DEFAULT_COPY_MOVE(CreateInfoContainer);
+
+            void SetCreateInfo(const T& createInfo)
+            {
+                m_vkCreateInfo = createInfo;
+            }
+            void SetExternalCreateInfo(const ExtT& createInfo)
+            {
+                m_vkExtCreateInfo = createInfo;
+            }
+
+            T* GetCreateInfo()
+            {
+                if (m_vkExtCreateInfo.sType != 0)
+                {
+                    // Add the external info to the createInfo if it exists
+                    // We do this here, so the pointer is always valid, even after copying this struct
+                    m_vkCreateInfo.pNext = &m_vkExtCreateInfo;
+                }
+                return &m_vkCreateInfo;
+            }
+
             //! Vector of queue families that the create info structure points to
             AZStd::vector<uint32_t> m_queueFamilyIndices;
+
+        private:
+            //! Vulkan create info structure
+            T m_vkCreateInfo = {};
+            ExtT m_vkExtCreateInfo = {};
         };
 
-        using BufferCreateInfo = CreateInfoContainer<VkBufferCreateInfo>;
-        using ImageCreateInfo = CreateInfoContainer<VkImageCreateInfo>;
+        using BufferCreateInfo = CreateInfoContainer<VkBufferCreateInfo, VkExternalMemoryBufferCreateInfo>;
+        using ImageCreateInfo = CreateInfoContainer<VkImageCreateInfo, VkExternalMemoryImageCreateInfo>;
 
         class Device final
             : public RHI::Device

+ 5 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/Fence.cpp

@@ -91,5 +91,10 @@ namespace AZ
         {
             return m_fenceImpl->GetFenceStateInternal();
         }
+
+        void Fence::SetExternallySignalled()
+        {
+            m_fenceImpl->SignalEvent();
+        }
     } // namespace Vulkan
 } // namespace AZ

+ 1 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/Fence.h

@@ -55,6 +55,7 @@ namespace AZ
             void WaitOnCpuInternal() const override;
             void ResetInternal() override;
             RHI::FenceState GetFenceStateInternal() const override;
+            void SetExternallySignalled() override;
             //////////////////////////////////////////////////////////////////////
 
             RHI::Ptr<FenceBase> m_fenceImpl;

+ 16 - 17
Gems/Atom/RHI/Vulkan/Code/Source/RHI/Image.cpp

@@ -553,6 +553,11 @@ namespace AZ
             return m_sharingMode;
         }
 
+        const MemoryView& Image::GetMemoryView()
+        {
+            return m_memoryView;
+        }
+
         void Image::OnPreInit(Device& device, const RHI::ImageDescriptor& descriptor, Image::InitFlags flags)
         {
             RHI::DeviceObject::Init(device);
@@ -775,17 +780,17 @@ namespace AZ
 
             ImageCreateInfo createInfo = device.BuildImageCreateInfo(descriptor);
             // Add flags for sparse binding and sparse memory residency
-            createInfo.m_vkCreateInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT; 
+            createInfo.GetCreateInfo()->flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
 
             // We will always handles image's ownership
-            createInfo.m_vkCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-            createInfo.m_vkCreateInfo.queueFamilyIndexCount = 1;
-            createInfo.m_vkCreateInfo.pQueueFamilyIndices = nullptr;
+            createInfo.GetCreateInfo()->sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+            createInfo.GetCreateInfo()->queueFamilyIndexCount = 1;
+            createInfo.GetCreateInfo()->pQueueFamilyIndices = nullptr;
 
-            createInfo.m_vkCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+            createInfo.GetCreateInfo()->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
 
             const VkResult vkResult =
-                device.GetContext().CreateImage(device.GetNativeDevice(), &createInfo.m_vkCreateInfo, VkSystemAllocator::Get(), &m_vkImage);
+                device.GetContext().CreateImage(device.GetNativeDevice(), createInfo.GetCreateInfo(), VkSystemAllocator::Get(), &m_vkImage);
 
             AssertSuccess(vkResult);
             RHI::ResultCode result = ConvertResult(vkResult);
@@ -794,7 +799,7 @@ namespace AZ
             m_sparseImageInfo = AZStd::make_unique<SparseImageInfo>();
             m_sparseImageInfo->Init(device, m_vkImage, descriptor);
             m_isOwnerOfNativeImage = true;
-            m_sharingMode = createInfo.m_vkCreateInfo.sharingMode;
+            m_sharingMode = createInfo.GetCreateInfo()->sharingMode;
 
             return result;
         }
@@ -807,8 +812,8 @@ namespace AZ
             const RHI::ImageDescriptor& descriptor = GetDescriptor();
 
             ImageCreateInfo createInfo = device.BuildImageCreateInfo(descriptor);
-            m_usageFlags = createInfo.m_vkCreateInfo.usage;
-            m_sharingMode = createInfo.m_vkCreateInfo.sharingMode;
+            m_usageFlags = createInfo.GetCreateInfo()->usage;
+            m_sharingMode = createInfo.GetCreateInfo()->sharingMode;
 
             VkResult result = VK_SUCCESS;
             if (memoryView)
@@ -819,7 +824,7 @@ namespace AZ
                     device.GetVmaAllocator(),
                     memoryView->GetAllocation()->GetVmaAllocation(),
                     memoryView->GetOffset(),
-                    &createInfo.m_vkCreateInfo,
+                    createInfo.GetCreateInfo(),
                     &m_vkImage);
 
                 if (result == VK_SUCCESS)
@@ -833,13 +838,7 @@ namespace AZ
                 VmaAllocationCreateInfo allocInfo = GetVmaAllocationCreateInfo(RHI::HeapMemoryLevel::Device);
                 VmaAllocation vmaAlloc;
                 // Creates a new image, allocates a new memory for it, and binds the memory to the image.
-                result = vmaCreateImage(
-                    device.GetVmaAllocator(),
-                    &createInfo.m_vkCreateInfo,
-                    &allocInfo,
-                    &m_vkImage,
-                    &vmaAlloc,
-                    nullptr);
+                result = vmaCreateImage(device.GetVmaAllocator(), createInfo.GetCreateInfo(), &allocInfo, &m_vkImage, &vmaAlloc, nullptr);
 
                 if (result == VK_SUCCESS)
                 {

+ 2 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/Image.h

@@ -180,6 +180,8 @@ namespace AZ
                 TrySparse = AZ_BIT(0)  // Try to create a sparse image first
             };
 
+            const MemoryView& GetMemoryView();
+
         private:
             Image() = default;
 

+ 11 - 6
Gems/Atom/RHI/Vulkan/Code/Source/RHI/Instance.cpp

@@ -5,16 +5,17 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
-#include <RHI/Vulkan.h>
-#include <RHI/Device.h>
-#include <RHI/Instance.h>
-#include <RHI/PhysicalDevice.h>
-#include <Atom/RHI/RHIUtils.h>
+#include <Atom/RHI.Reflect/VkAllocator.h>
 #include <Atom/RHI.Reflect/Vulkan/VulkanBus.h>
 #include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
-#include <Atom/RHI.Reflect/VkAllocator.h>
+#include <Atom/RHI/RHIBus.h>
+#include <Atom/RHI/RHIUtils.h>
 #include <AzCore/Debug/Trace.h>
 #include <AzCore/Utils/Utils.h>
+#include <RHI/Device.h>
+#include <RHI/Instance.h>
+#include <RHI/PhysicalDevice.h>
+#include <RHI/Vulkan.h>
 
 namespace AZ
 {
@@ -333,6 +334,10 @@ namespace AZ
 
                 it = shouldIgnore ? supportedDevices.erase(it) : it + 1;
             }
+
+            RHI::RHIRequirementRequestBus::Broadcast(
+                &RHI::RHIRequirementsRequest::FilterSupportedPhysicalDevices, supportedDevices, RHI::APIIndex::Vulkan);
+
             return supportedDevices;
         }
 

+ 5 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/MemoryView.h

@@ -34,6 +34,11 @@ namespace AZ
             {
                 return GetAllocation()->GetOffset() + GetOffset();
             }
+
+            VkDeviceMemory GetNativeDeviceMemory() const
+            {
+                return GetAllocation()->GetNativeDeviceMemory();
+            }
         };
     }
 }

+ 12 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/TimelineSemaphoreFence.cpp

@@ -7,6 +7,7 @@
  */
 #include <Atom/RHI.Reflect/VkAllocator.h>
 #include <Atom/RHI.Reflect/Vulkan/Conversion.h>
+#include <Atom/RHI.Reflect/Vulkan/VulkanBus.h>
 #include <RHI/Device.h>
 #include <RHI/TimelineSemaphoreFence.h>
 
@@ -50,6 +51,17 @@ namespace AZ
             timelineCreateInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
             timelineCreateInfo.initialValue = 0;
 
+            VkExternalSemaphoreHandleTypeFlags externalHandleTypeFlags = 0;
+            ExternalHandleRequirementBus::Broadcast(
+                &ExternalHandleRequirementBus::Events::CollectSemaphoreExportHandleTypes, externalHandleTypeFlags);
+            VkExportSemaphoreCreateInfoKHR createExport{};
+            if (externalHandleTypeFlags != 0)
+            {
+                createExport.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+                createExport.handleTypes = externalHandleTypeFlags;
+                timelineCreateInfo.pNext = &createExport;
+            }
+
             VkSemaphoreCreateInfo createInfo{};
             createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
             createInfo.pNext = &timelineCreateInfo;

+ 11 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/TransientAttachmentPool.cpp

@@ -7,6 +7,7 @@
  */
 #include <Atom/RHI.Reflect/TransientBufferDescriptor.h>
 #include <Atom/RHI.Reflect/TransientImageDescriptor.h>
+#include <Atom/RHI/RHIBus.h>
 #include <RHI/Buffer.h>
 #include <RHI/Device.h>
 #include <RHI/Image.h>
@@ -226,6 +227,16 @@ namespace AZ
             heapDesc.m_budgetInBytes = budgetInBytes;
             heapDesc.m_resourceTypeMask = resourceTypeMask;
             heapDesc.m_allocationParameters = heapParameters;
+            struct
+            {
+                size_t m_alignment = 0;
+                void operator=(size_t value)
+                {
+                    m_alignment = AZStd::max(m_alignment, value);
+                }
+            } alignment;
+            RHI::RHIRequirementRequestBus::BroadcastResult(alignment, &RHI::RHIRequirementsRequest::GetRequiredAlignment, device);
+            heapDesc.m_alignment = AZStd::max(alignment.m_alignment, heapDesc.m_alignment);
 
             auto result = allocator->Init(device, heapDesc);
             if (result != RHI::ResultCode::Success)

+ 8 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/VulkanMemoryAllocation.cpp

@@ -36,6 +36,14 @@ namespace AZ
             return m_size;
         }
 
+        size_t VulkanMemoryAllocation::GetBlockSize() const
+        {
+            auto& device = static_cast<Device&>(GetDevice());
+            VmaAllocationInfo2 info;
+            vmaGetAllocationInfo2(device.GetVmaAllocator(), m_vmaAllocation, &info);
+            return info.blockSize;
+        }
+
         CpuVirtualAddress VulkanMemoryAllocation::Map(size_t offset, size_t size, RHI::HostMemoryAccess hostAccess)
         {
             Device& device = static_cast<Device&>(GetDevice());

+ 3 - 0
Gems/Atom/RHI/Vulkan/Code/Source/RHI/VulkanMemoryAllocation.h

@@ -35,6 +35,9 @@ namespace AZ
             //! Returns the size of the memory region in bytes.
             size_t GetSize() const;
 
+            //! Returns the size of the memory block in bytes.
+            size_t GetBlockSize() const;
+
             //! A convenience method to map the resource region spanned by the view for CPU access.
             CpuVirtualAddress Map(size_t offset, size_t size, RHI::HostMemoryAccess hostAccess);
 

+ 12 - 0
Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_interface_files.cmake

@@ -0,0 +1,12 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(FILES
+    Source/RHI.Interface/RHIVulkanInterface.cpp
+    Include/Atom/RHI.Interface/Vulkan/RHIVulkanInterface.h
+)