Expose memory bindings for buffers/textures on Vulkan and D3D12

* This only applies to explicit memory binding APIs - so D3D11 and GL are
  excluded. On D3D12, committed resources will not have anything listed here.
This commit is contained in:
baldurk
2026-08-13 21:05:10 +01:00
parent d4130ce42a
commit 8ca1aa2c8d
7 changed files with 130 additions and 15 deletions
+41 -2
View File
@@ -975,7 +975,8 @@ struct BufferDescription
bool operator==(const BufferDescription &o) const
{
return resourceId == o.resourceId && creationFlags == o.creationFlags &&
gpuAddress == o.gpuAddress && length == o.length;
gpuAddress == o.gpuAddress && length == o.length && memory == o.memory &&
memoryOffset == o.memoryOffset;
}
bool operator<(const BufferDescription &o) const
{
@@ -987,6 +988,10 @@ struct BufferDescription
return gpuAddress < o.gpuAddress;
if(!(length == o.length))
return length < o.length;
if(!(memory == o.memory))
return memory < o.memory;
if(!(memoryOffset == o.memoryOffset))
return memoryOffset < o.memoryOffset;
return false;
}
DOCUMENT(R"(The unique :class:`ResourceId` that identifies this buffer.
@@ -1012,6 +1017,21 @@ struct BufferDescription
:type: int
)");
uint64_t length = 0;
DOCUMENT(R"(The unique :class:`ResourceId` that identifies the memory object this
buffer has a fixed binding to. For objects that do not have an explicit memory binding
or for sparse buffers, this will not be set.
:type: ResourceId
)");
ResourceId memory;
DOCUMENT(R"(The byte offset in :data:`memory` where the buffer is bound. If
:data:`memory` is unset, this will be 0.
:type: int
)");
uint64_t memoryOffset = 0;
};
DECLARE_REFLECTION_STRUCT(BufferDescription);
@@ -1035,7 +1055,7 @@ struct TextureDescription
height == o.height && depth == o.depth && resourceId == o.resourceId &&
cubemap == o.cubemap && mips == o.mips && arraysize == o.arraysize &&
creationFlags == o.creationFlags && msQual == o.msQual && msSamp == o.msSamp &&
byteSize == o.byteSize;
byteSize == o.byteSize && memory == o.memory && memoryOffset == o.memoryOffset;
}
bool operator<(const TextureDescription &o) const
{
@@ -1067,6 +1087,10 @@ struct TextureDescription
return msSamp < o.msSamp;
if(!(byteSize == o.byteSize))
return byteSize < o.byteSize;
if(!(memory == o.memory))
return memory < o.memory;
if(!(memoryOffset == o.memoryOffset))
return memoryOffset < o.memoryOffset;
return false;
}
DOCUMENT(R"(The format of each pixel in the texture.
@@ -1152,6 +1176,21 @@ struct TextureDescription
:type: int
)");
uint64_t byteSize;
DOCUMENT(R"(The unique :class:`ResourceId` that identifies the memory object this
texture has a fixed binding to. For objects that do not have an explicit memory binding
or for sparse textures, this will not be set.
:type: ResourceId
)");
ResourceId memory;
DOCUMENT(R"(The byte offset in :data:`memory` where the texture is bound. If
:data:`memory` is unset, this will be 0.
:type: int
)");
uint64_t memoryOffset = 0;
};
DECLARE_REFLECTION_STRUCT(TextureDescription);
+29 -5
View File
@@ -432,6 +432,12 @@ BufferDescription D3D12Replay::GetBuffer(ResourceId id)
ret.creationFlags = BufferCategory::NoFlags;
ret.gpuAddress = it->second->GetOriginalVA();
if(it->second->GetHeap())
{
ret.memory = it->second->GetHeap()->GetResourceID();
ret.memoryOffset = it->second->GetHeapOffset();
}
const rdcarray<EventUsage> &usage = m_pDevice->GetQueue()->GetUsage(id);
for(size_t i = 0; i < usage.size(); i++)
@@ -479,11 +485,29 @@ TextureDescription D3D12Replay::GetTexture(ResourceId id)
ret.mips = desc.MipLevels;
ret.msQual = desc.SampleDesc.Quality;
ret.msSamp = RDCMAX(1U, desc.SampleDesc.Count);
ret.byteSize = 0;
for(uint32_t i = 0; i < ret.mips; i++)
ret.byteSize += GetByteSize(ret.width, ret.height, ret.depth, desc.Format, i);
ret.byteSize *= ret.arraysize;
ret.byteSize *= ret.msSamp;
if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
ret.byteSize = desc.Width;
}
else
{
ret.byteSize = m_pDevice->GetResourceAllocationInfo(0, 1, &desc).SizeInBytes;
}
if(ret.byteSize == 0)
{
for(uint32_t i = 0; i < ret.mips; i++)
ret.byteSize += GetByteSize(ret.width, ret.height, ret.depth, desc.Format, i);
ret.byteSize *= ret.arraysize;
ret.byteSize *= ret.msSamp;
}
if(it->second->GetHeap())
{
ret.memory = it->second->GetHeap()->GetResourceID();
ret.memoryOffset = it->second->GetHeapOffset();
}
switch(ret.dimension)
{
+3
View File
@@ -1480,6 +1480,7 @@ class WrappedID3D12Resource
size_t DeleteOverlappingAccStructsInRangeAtOffset(D3D12BufferOffset bufferOffset);
WrappedID3D12Heap *m_Heap = NULL;
uint64_t m_HeapOffset = 0;
Threading::CriticalSection m_accStructResourcesCS;
rdcflatmap<D3D12BufferOffset, D3D12AccelerationStructure *> m_accelerationStructMap;
@@ -1498,6 +1499,7 @@ public:
}
WrappedID3D12Heap *GetHeap() { return m_Heap; }
uint64_t GetHeapOffset() { return m_HeapOffset; }
ID3D12Pageable *UnwrappedResidencyPageable()
{
@@ -1575,6 +1577,7 @@ public:
device->AddReplayResource(GetResourceID(), this);
m_Heap = (WrappedID3D12Heap *)heap;
m_HeapOffset = HeapOffset;
SAFE_ADDREF(m_Heap);
// assuming only valid for buffers
+3
View File
@@ -656,6 +656,9 @@ struct VulkanCreationInfo
VkMemoryRequirements mrq;
ResourceId boundMemory;
uint64_t boundMemoryOffset;
ResourceId inlineDescriptorId;
};
std::unordered_map<ResourceId, Buffer> m_Buffer;
+42 -6
View File
@@ -345,7 +345,16 @@ rdcarray<BufferDescription> VulkanReplay::GetBuffers()
TextureDescription VulkanReplay::GetTexture(ResourceId id)
{
VulkanCreationInfo::Image &iminfo = m_pDriver->m_CreationInfo.m_Image[id];
auto imit = m_pDriver->m_CreationInfo.m_Image.find(id);
if(imit == m_pDriver->m_CreationInfo.m_Image.end())
{
RDCERR("Invalid image id");
return {};
}
VulkanCreationInfo::Image &iminfo = imit->second;
VkImage liveWrappedImage = GetResourceManager()->GetHandle<VkImage>(id);
TextureDescription ret = {};
ret.resourceId = id;
@@ -357,10 +366,27 @@ TextureDescription VulkanReplay::GetTexture(ResourceId id)
ret.depth = iminfo.extent.depth;
ret.mips = iminfo.mipLevels;
ret.byteSize = 0;
for(uint32_t s = 0; s < ret.mips; s++)
ret.byteSize += GetByteSize(ret.width, ret.height, ret.depth, iminfo.format, s);
ret.byteSize *= ret.arraysize;
VkMemoryRequirements mrq = {0};
VkDevice dev = m_pDriver->GetDev();
// some entries in m_Images don't have a live handle - e.g. swapchains
if(liveWrappedImage != VK_NULL_HANDLE)
ObjDisp(dev)->GetImageMemoryRequirements(Unwrap(dev), Unwrap(liveWrappedImage), &mrq);
ret.byteSize = mrq.size;
if(ret.byteSize == 0)
{
for(uint32_t s = 0; s < ret.mips; s++)
ret.byteSize += GetByteSize(ret.width, ret.height, ret.depth, iminfo.format, s);
ret.byteSize *= ret.arraysize;
}
LockedConstImageStateRef lockedImage = m_pDriver->FindConstImageState(id);
if(lockedImage && lockedImage->isMemoryBound)
{
ret.memory = lockedImage->boundMemory;
ret.memoryOffset = lockedImage->boundMemoryOffset;
}
ret.msQual = 0;
ret.msSamp = RDCMAX(1U, (uint32_t)iminfo.samples);
@@ -399,7 +425,14 @@ TextureDescription VulkanReplay::GetTexture(ResourceId id)
BufferDescription VulkanReplay::GetBuffer(ResourceId id)
{
VulkanCreationInfo::Buffer &bufinfo = m_pDriver->m_CreationInfo.m_Buffer[id];
auto bufit = m_pDriver->m_CreationInfo.m_Buffer.find(id);
if(bufit == m_pDriver->m_CreationInfo.m_Buffer.end())
{
RDCERR("Invalid image id");
return {};
}
VulkanCreationInfo::Buffer &bufinfo = bufit->second;
BufferDescription ret;
ret.resourceId = id;
@@ -407,6 +440,9 @@ BufferDescription VulkanReplay::GetBuffer(ResourceId id)
ret.creationFlags = BufferCategory::NoFlags;
ret.gpuAddress = bufinfo.gpuAddress;
ret.memory = bufinfo.boundMemory;
ret.memoryOffset = bufinfo.boundMemoryOffset;
if(bufinfo.usage & (VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT))
ret.creationFlags |= BufferCategory::ReadWrite;
if(bufinfo.usage & (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT))
@@ -1734,6 +1734,9 @@ bool WrappedVulkan::Serialise_vkBindBufferMemory(SerialiserType &ser, VkDevice d
if(bufInfo.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)
TrackReplayBufferAddress(device, buffer, memory, memoryOffset);
bufInfo.boundMemory = memId;
bufInfo.boundMemoryOffset = memoryOffset;
m_CreationInfo.m_Memory[GetResID(memory)].BindMemory(memoryOffset, mrq.size,
VulkanCreationInfo::Memory::Linear);
}
@@ -3534,6 +3537,9 @@ bool WrappedVulkan::Serialise_vkBindBufferMemory2(SerialiserType &ser, VkDevice
if(bufInfo.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)
TrackReplayBufferAddress(device, bindInfo.buffer, bindInfo.memory, bindInfo.memoryOffset);
bufInfo.boundMemory = memId;
bufInfo.boundMemoryOffset = bindInfo.memoryOffset;
// the memory is immediately dirty because we don't use dirty tracking, it's too expensive to
// follow all frame refs in the background and it's pointless because memory almost always
// immediately becomes dirty anyway. The one case we might care about non-dirty memory is
+6 -2
View File
@@ -465,8 +465,10 @@ void DoSerialise(SerialiserType &ser, TextureDescription &el)
SERIALISE_MEMBER(msQual);
SERIALISE_MEMBER(msSamp);
SERIALISE_MEMBER(byteSize);
SERIALISE_MEMBER(memory);
SERIALISE_MEMBER(memoryOffset);
SIZE_CHECK(72);
SIZE_CHECK(88);
}
template <typename SerialiserType>
@@ -476,8 +478,10 @@ void DoSerialise(SerialiserType &ser, BufferDescription &el)
SERIALISE_MEMBER(creationFlags);
SERIALISE_MEMBER(gpuAddress);
SERIALISE_MEMBER(length);
SERIALISE_MEMBER(memory);
SERIALISE_MEMBER(memoryOffset);
SIZE_CHECK(32);
SIZE_CHECK(48);
}
template <typename SerialiserType>