mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-17 13:07:14 +00:00
Optimize Apply_InitialState for VkDeviceMemory resources
This change reduces the amount of data copied into VkDeviceMemory resources in `Apply_InitialState`; this uses the new (interval-based) reference tracking. On a large capture from a recent game, this change reduces the amount of copied data from 2.2GB to 720MB (plus 100MB cleared to 0). This reduces the time spent in `ApplyInitialContents` from 1800ms to 1500ms. Ranges of memory are initialized/reset by either clearing to 0, or copying in the initial state data, according to the `FrameRefType`, as follows: - Read-only (`eFrameRef_Read`) regions of memory are initialized by copying once, before the first replay. - Read-before-write (`eFrameRef_ReadBeforeWrite`) regions of memory are reset by copying before each replay. - Write-only (`eFrameRef_PartialWrite` and `eFrameRef_CompleteWrite`) regions of memory are cleared to 0 before each replay. This is intended to avoid possible user confusion when inspecting these regions of memory, as they might otherwise show data written later in the frame. - Unused (`eFrameRef_None`) regions of memory are cleared to 0 once, before the first replay.
This commit is contained in:
committed by
Baldur Karlsson
parent
3abab3bbb9
commit
cc8aa679e9
@@ -188,6 +188,8 @@ public:
|
||||
inline iterator begin() { return Wrap(StartPoints.begin()); }
|
||||
inline const_iterator begin() const { return Wrap(StartPoints.begin()); }
|
||||
inline const_iterator end() const { return Wrap(StartPoints.end()); }
|
||||
typedef typename std::map<uint64_t, T>::size_type size_type;
|
||||
inline size_type size() const { return StartPoints.size(); }
|
||||
// Find the interval containing `x`.
|
||||
iterator find(uint64_t x)
|
||||
{
|
||||
|
||||
@@ -1975,28 +1975,81 @@ void WrappedVulkan::Apply_InitialState(WrappedVkRes *live, VkInitialContents ini
|
||||
}
|
||||
else if(type == eResDeviceMemory)
|
||||
{
|
||||
Intervals<InitReqType> resetReq;
|
||||
ResourceId orig = GetResourceManager()->GetOriginalID(id);
|
||||
MemRefs *memRefs = NULL;
|
||||
if(GetResourceManager()->OptimizeInitialState())
|
||||
memRefs = GetResourceManager()->FindMemRefs(orig);
|
||||
if(!memRefs)
|
||||
{
|
||||
// No information about the memory usage in the frame.
|
||||
// Pessimistically assume the entire memory needs to be reset.
|
||||
resetReq.update(0, initial.mem.size, eInitReq_Reset,
|
||||
[](InitReqType x, InitReqType y) -> InitReqType { return std::max(x, y); });
|
||||
}
|
||||
else
|
||||
{
|
||||
bool initialized = memRefs->initializedLiveRes == live;
|
||||
memRefs->initializedLiveRes = live;
|
||||
for(auto it = memRefs->rangeRefs.begin(); it != memRefs->rangeRefs.end(); it++)
|
||||
{
|
||||
InitReqType t = InitReq(it->value());
|
||||
if(t == eInitReq_Reset || (t == eInitReq_InitOnce && !initialized))
|
||||
resetReq.update(it->start(), it->finish(), eInitReq_Reset,
|
||||
[](InitReqType x, InitReqType y) -> InitReqType { return std::max(x, y); });
|
||||
else if(t == eInitReq_Clear || (t == eInitReq_None && !initialized))
|
||||
resetReq.update(it->start(), it->finish(), eInitReq_Clear,
|
||||
[](InitReqType x, InitReqType y) -> InitReqType { return std::max(x, y); });
|
||||
}
|
||||
}
|
||||
|
||||
VkResult vkr = VK_SUCCESS;
|
||||
|
||||
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
|
||||
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
|
||||
|
||||
VkBuffer srcBuf = initial.buf;
|
||||
VkDeviceSize datasize = initial.mem.size;
|
||||
VkDeviceSize dstMemOffs = 0;
|
||||
|
||||
VkBuffer dstBuf = m_CreationInfo.m_Memory[id].wholeMemBuf;
|
||||
if(dstBuf == VK_NULL_HANDLE)
|
||||
{
|
||||
RDCERR("Whole memory buffer not present for %llu", id);
|
||||
return;
|
||||
}
|
||||
|
||||
if(resetReq.size() == 1 && resetReq.begin()->value() == eInitReq_None)
|
||||
{
|
||||
RDCDEBUG("Apply_InitialState (Mem %llu): skipped", orig);
|
||||
return; // no copy or clear required
|
||||
}
|
||||
|
||||
VkCommandBuffer cmd = GetNextCmd();
|
||||
|
||||
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
|
||||
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
||||
|
||||
VkBuffer dstBuf = m_CreationInfo.m_Memory[id].wholeMemBuf;
|
||||
|
||||
VkBufferCopy region = {0, dstMemOffs, datasize};
|
||||
|
||||
if(dstBuf != VK_NULL_HANDLE)
|
||||
ObjDisp(cmd)->CmdCopyBuffer(Unwrap(cmd), Unwrap(srcBuf), Unwrap(dstBuf), 1, ®ion);
|
||||
else
|
||||
RDCERR("Whole memory buffer not present for %llu", id);
|
||||
std::vector<VkBufferCopy> regions;
|
||||
uint32_t fillCount = 0;
|
||||
for(auto it = resetReq.begin(); it != resetReq.end(); it++)
|
||||
{
|
||||
if(it->start() >= initial.mem.size)
|
||||
continue;
|
||||
VkDeviceSize finish = RDCMIN(it->finish(), initial.mem.size);
|
||||
VkDeviceSize size = finish - it->start();
|
||||
switch(it->value())
|
||||
{
|
||||
case eInitReq_Clear:
|
||||
ObjDisp(cmd)->CmdFillBuffer(Unwrap(cmd), Unwrap(dstBuf), it->start(), size, 0);
|
||||
fillCount++;
|
||||
break;
|
||||
case eInitReq_Reset: regions.push_back({it->start(), it->start(), size}); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
RDCDEBUG("Apply_InitialState (Mem %llu): %d fills, %d copies", orig, fillCount, regions.size());
|
||||
if(regions.size() > 0)
|
||||
ObjDisp(cmd)->CmdCopyBuffer(Unwrap(cmd), Unwrap(srcBuf), Unwrap(dstBuf),
|
||||
(uint32_t)regions.size(), regions.data());
|
||||
|
||||
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
|
||||
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
||||
|
||||
@@ -702,6 +702,15 @@ void VulkanResourceManager::ClearReferencedMemory()
|
||||
m_MemFrameRefs.clear();
|
||||
}
|
||||
|
||||
MemRefs *VulkanResourceManager::FindMemRefs(ResourceId mem)
|
||||
{
|
||||
auto it = m_MemFrameRefs.find(mem);
|
||||
if(it != m_MemFrameRefs.end())
|
||||
return &it->second;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool VulkanResourceManager::Force_InitialState(WrappedVkRes *res, bool prepare)
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -429,7 +429,9 @@ public:
|
||||
|
||||
void MergeReferencedMemory(std::map<ResourceId, MemRefs> &memRefs);
|
||||
void ClearReferencedMemory();
|
||||
MemRefs *FindMemRefs(ResourceId mem);
|
||||
|
||||
inline bool OptimizeInitialState() { return m_OptimizeInitialState; }
|
||||
private:
|
||||
bool ResourceTypeRelease(WrappedVkRes *res);
|
||||
|
||||
@@ -446,4 +448,5 @@ private:
|
||||
CaptureState m_State;
|
||||
WrappedVulkan *m_Core;
|
||||
std::map<ResourceId, MemRefs> m_MemFrameRefs;
|
||||
bool m_OptimizeInitialState = false;
|
||||
};
|
||||
|
||||
@@ -1006,8 +1006,10 @@ struct AttachmentInfo
|
||||
struct MemRefs
|
||||
{
|
||||
Intervals<FrameRefType> rangeRefs;
|
||||
inline MemRefs() {}
|
||||
WrappedVkRes *initializedLiveRes;
|
||||
inline MemRefs() : initializedLiveRes(NULL) {}
|
||||
inline MemRefs(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType)
|
||||
: initializedLiveRes(NULL)
|
||||
{
|
||||
rangeRefs.update(offset, offset + size, refType, ComposeFrameRefs);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user