Batch prepare and allow limited buffering for vulkan initial states

* We add a capture option that defines a soft limit we aim to keep under for
  memory overhead during capture, excess initial states after that will be
  stored temporarily on disk.
This commit is contained in:
baldurk
2023-06-02 18:53:45 +01:00
parent 1ec0606ce8
commit 0f851df6c2
31 changed files with 586 additions and 188 deletions
+2 -2
View File
@@ -123,7 +123,7 @@ CaptureContext::CaptureContext(PersistantConfig &cfg) : m_Config(cfg)
"a connection issue.")
.arg(err.Message());
}
else if(err.code == ResultCode::ReplayDeviceLost)
else if(err.code == ResultCode::DeviceLost)
{
title = tr("Device Lost error");
text = tr("%1.\n\n"
@@ -133,7 +133,7 @@ CaptureContext::CaptureContext(PersistantConfig &cfg) : m_Config(cfg)
"API usage errors can cause this kind of problem.")
.arg(err.Message());
}
else if(err.code == ResultCode::ReplayOutOfMemory)
else if(err.code == ResultCode::OutOfMemory)
{
title = tr("Out of memory");
text =
@@ -106,6 +106,7 @@ CaptureSettings::operator QVariant() const
opts[lit("refAllResources")] = options.refAllResources;
opts[lit("captureAllCmdLists")] = options.captureAllCmdLists;
opts[lit("debugOutputMute")] = options.debugOutputMute;
opts[lit("softMemoryLimit")] = options.softMemoryLimit;
ret[lit("options")] = opts;
ret[lit("queuedFrameCap")] = queuedFrameCap;
@@ -149,6 +150,7 @@ CaptureSettings::CaptureSettings(const QVariant &v)
options.refAllResources = opts[lit("refAllResources")].toBool();
options.captureAllCmdLists = opts[lit("captureAllCmdLists")].toBool();
options.debugOutputMute = opts[lit("debugOutputMute")].toBool();
options.softMemoryLimit = opts[lit("softMemoryLimit")].toUInt();
if(data.contains(lit("queuedFrameCap")))
queuedFrameCap = data[lit("queuedFrameCap")].toUInt();
@@ -928,6 +928,7 @@ void CaptureDialog::SetSettings(CaptureSettings settings)
ui->DelayForDebugger->setValue(settings.options.delayForDebugger);
ui->VerifyBufferAccess->setChecked(settings.options.verifyBufferAccess);
ui->AutoStart->setChecked(settings.autoStart);
ui->SoftMemoryLimit->setValue(settings.options.softMemoryLimit);
// force flush this state
on_CaptureCallstacks_toggled(ui->CaptureCallstacks->isChecked());
@@ -975,6 +976,7 @@ CaptureSettings CaptureDialog::Settings()
ret.options.captureAllCmdLists = ui->CaptureAllCmdLists->isChecked();
ret.options.delayForDebugger = (uint32_t)ui->DelayForDebugger->value();
ret.options.verifyBufferAccess = ui->VerifyBufferAccess->isChecked();
ret.options.softMemoryLimit = (uint32_t)ui->SoftMemoryLimit->value();
if(ui->queueFrameCap->isChecked())
{
@@ -528,6 +528,85 @@
<string>Verify Buffer Access</string>
</property>
</widget>
</item><item>
<widget class="QFrame" name="SoftMemoryLimitFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout" name="SoftMemoryLimitLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QDoubleSpinBox" name="SoftMemoryLimit">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Specifies a soft memory limit which some APIs may use to limit graphics memory use and use disk space instead for storing temporary data.</string>
</property>
<property name="suffix">
<string comment="seconds"> MB</string>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="maximum">
<double>99999.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="SoftMemoryLimitLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Specifies a soft memory limit which some APIs may use to limit graphics memory use and use disk space instead for storing temporary data.</string>
</property>
<property name="text">
<string>Soft Mem Limit</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="AutoStart">
+13
View File
@@ -214,6 +214,19 @@ typedef enum RENDERDOC_CaptureOption {
// necessary as directed by a RenderDoc developer.
eRENDERDOC_Option_AllowUnsupportedVendorExtensions = 12,
// Define a soft memory limit which some APIs may aim to keep overhead under where
// possible. Anything above this limit will where possible be saved directly to disk during
// capture.
// This will cause increased disk space use (which may cause a capture to fail if disk space is
// exhausted) as well as slower capture times.
//
// Not all memory allocations may be deferred like this so it is not a guarantee of a memory
// limit.
//
// Units are in MBs, suggested values would range from 200MB to 1000MB.
//
// Default - 0 Megabytes
eRENDERDOC_Option_SoftMemoryLimit = 13,
} RENDERDOC_CaptureOption;
// Sets an option that controls how RenderDoc behaves on capture.
+13
View File
@@ -209,6 +209,19 @@ Default - enabled
``False`` - API debugging is displayed as normal.
)");
bool debugOutputMute;
DOCUMENT(R"(Define a soft memory limit which some APIs may aim to keep overhead under where
possible. Anything above this limit will where possible be saved directly to disk during capture.
This will cause increased disk space use (which may cause a capture to fail if disk space is
exhausted) as well as slower capture times.
Not all memory allocations may be deferred like this so it is not a guarantee of a memory limit.
Units are in MBs, suggested values would range from 200MB to 1000MB.
Default - 0 Megabytes
)");
uint32_t softMemoryLimit;
};
DECLARE_REFLECTION_STRUCT(CaptureOptions);
+2 -2
View File
@@ -92,8 +92,8 @@ rdcstr DoStringise(const ResultCode &el)
STRINGISE_ENUM_CLASS_NAMED(AndroidAPKVerifyFailed,
"Failed to verify installed Android remote server");
STRINGISE_ENUM_CLASS_NAMED(RemoteServerConnectionLost, "Connection lost to remote server");
STRINGISE_ENUM_CLASS_NAMED(ReplayOutOfMemory, "Encountered an out of memory error");
STRINGISE_ENUM_CLASS_NAMED(ReplayDeviceLost, "Encountered a GPU device lost error");
STRINGISE_ENUM_CLASS_NAMED(OutOfMemory, "Encountered an out of memory error");
STRINGISE_ENUM_CLASS_NAMED(DeviceLost, "Encountered a GPU device lost error");
STRINGISE_ENUM_CLASS_NAMED(DataNotAvailable,
"Data was requested through RenderDoc's API which is not available");
STRINGISE_ENUM_CLASS_NAMED(InvalidParameter,
+6 -6
View File
@@ -3861,13 +3861,13 @@ a remote server.
While replaying on a remote server, the connection was lost.
.. data:: ReplayOutOfMemory
.. data:: OutOfMemory
While replaying, an out of memory error was encountered.
An out of memory error was encountered.
.. data:: ReplayDeviceLost
.. data:: DeviceLost
While replaying a device lost fatal error was encountered.
A device lost fatal error was encountered.
.. data:: DataNotAvailable
@@ -3909,8 +3909,8 @@ enum class ResultCode : uint32_t
AndroidAPKInstallFailed,
AndroidAPKVerifyFailed,
RemoteServerConnectionLost,
ReplayOutOfMemory,
ReplayDeviceLost,
OutOfMemory,
DeviceLost,
DataNotAvailable,
InvalidParameter,
CompressionFailed,
+1 -1
View File
@@ -731,7 +731,7 @@ void ImageViewer::RefreshFile()
if(!data)
{
SET_ERROR_RESULT(m_Error, ResultCode::ReplayOutOfMemory,
SET_ERROR_RESULT(m_Error, ResultCode::OutOfMemory,
"Allocation for %zu bytes failed for EXR data", datasize);
return;
}
+72 -11
View File
@@ -610,6 +610,7 @@ public:
InitialContentData GetInitialContents(ResourceId id);
void SetInitialContents(ResourceId id, InitialContentData contents);
void SetInitialChunk(ResourceId id, Chunk *chunk);
void SetInitialFileStore(ResourceId id, const rdcstr &filename, uint64_t start, uint64_t end);
// generate chunks for initial contents and insert.
void InsertInitialContentsChunks(WriteSerialiser &ser);
@@ -692,7 +693,9 @@ protected:
{
return true;
}
virtual void Begin_PrepareInitialBatch() {}
virtual bool Prepare_InitialState(WrappedResourceType res) = 0;
virtual void End_PrepareInitialBatch() {}
virtual uint64_t GetSize_InitialState(ResourceId id, const InitialContentData &initial) = 0;
virtual bool Serialise_InitialState(WriteSerialiser &ser, ResourceId id, RecordType *record,
const InitialContentData *initialData) = 0;
@@ -727,10 +730,12 @@ protected:
// used during capture - holds resources marked as dirty, needing initial contents
std::set<ResourceId> m_DirtyResources;
struct InitialContentDataOrChunk
struct InitialContentStorage
{
Chunk *chunk = NULL;
InitialContentData data;
rdcstr filename;
uint64_t fileStart = 0, fileEnd = 0;
void Free(ResourceManager *mgr)
{
@@ -745,7 +750,7 @@ protected:
};
// used during capture or replay - holds initial contents
std::unordered_map<ResourceId, InitialContentDataOrChunk> m_InitialContents;
std::unordered_map<ResourceId, InitialContentStorage> m_InitialContents;
// used during capture or replay - map of resources currently alive with their real IDs, used in
// capture and replay.
@@ -930,9 +935,7 @@ void ResourceManager<Configuration>::MarkResourceFrameReferenced(ResourceId id,
SkipOrPostponeOrPrepare_InitialState(id, refType);
if(IsDirtyFrameRef(refType))
{
Prepare_InitialStateIfPostponed(id, true);
}
}
UpdateLastWriteTime(id, refType);
@@ -1002,7 +1005,7 @@ void ResourceManager<Configuration>::SetInitialChunk(ResourceId id, Chunk *chunk
RDCASSERT(id != ResourceId());
RDCASSERT(chunk->GetChunkType<SystemChunk>() == SystemChunk::InitialContents);
InitialContentDataOrChunk &data = m_InitialContents[id];
InitialContentStorage &data = m_InitialContents[id];
if(data.chunk)
data.chunk->Delete();
@@ -1010,6 +1013,23 @@ void ResourceManager<Configuration>::SetInitialChunk(ResourceId id, Chunk *chunk
data.chunk = chunk;
}
template <typename Configuration>
void ResourceManager<Configuration>::SetInitialFileStore(ResourceId id, const rdcstr &filename,
uint64_t start, uint64_t end)
{
SCOPED_LOCK_OPTIONAL(m_Lock, m_Capturing);
RDCASSERT(id != ResourceId());
RDCASSERT(!filename.empty());
RDCASSERT(end > start);
InitialContentStorage &data = m_InitialContents[id];
data.filename = filename;
data.fileStart = start;
data.fileEnd = end;
}
template <typename Configuration>
typename Configuration::InitialContentData ResourceManager<Configuration>::GetInitialContents(
ResourceId id)
@@ -1121,11 +1141,17 @@ void ResourceManager<Configuration>::Prepare_InitialStateIfPostponed(ResourceId
return;
if(midframe)
{
RDCLOG("Preparing resource %s after it has been postponed.", ToStr(id).c_str());
Begin_PrepareInitialBatch();
}
WrappedResourceType res = GetCurrentResource(id);
Prepare_InitialState(res);
if(midframe)
End_PrepareInitialBatch();
m_PostponedResourceIDs.erase(id);
}
@@ -1166,7 +1192,9 @@ void ResourceManager<Configuration>::SkipOrPostponeOrPrepare_InitialState(Resour
WrappedResourceType res = GetCurrentResource(id);
RDCDEBUG("Preparing resource %s after it has been skipped on refType of %s", ToStr(id).c_str(),
ToStr(refType).c_str());
Begin_PrepareInitialBatch();
Prepare_InitialState(res);
End_PrepareInitialBatch();
}
}
@@ -1349,7 +1377,7 @@ void ResourceManager<Configuration>::ApplyInitialContents()
for(auto it = resources.begin(); it != resources.end(); ++it)
{
ResourceId id = *it;
const InitialContentDataOrChunk &data = m_InitialContents[id];
const InitialContentStorage &data = m_InitialContents[id];
WrappedResourceType live = GetLiveResource(id);
Apply_InitialState(live, data.data);
}
@@ -1446,6 +1474,8 @@ void ResourceManager<Configuration>::PrepareInitialContents()
float num = float(m_DirtyResources.size());
float idx = 0.0f;
Begin_PrepareInitialBatch();
for(auto it = m_DirtyResources.begin(); it != m_DirtyResources.end(); ++it)
{
ResourceId id = *it;
@@ -1491,6 +1521,8 @@ void ResourceManager<Configuration>::PrepareInitialContents()
Prepare_InitialState(res);
}
End_PrepareInitialBatch();
RDCLOG("Prepared %u dirty resources, postponed %u, skipped %u", prepared, postponed, skipped);
}
@@ -1508,6 +1540,32 @@ void ResourceManager<Configuration>::InsertInitialContentsChunks(WriteSerialiser
float num = float(m_InitialContents.size());
float idx = 0.0f;
Begin_PrepareInitialBatch();
for(auto it = m_InitialContents.begin(); it != m_InitialContents.end(); ++it)
{
ResourceId id = it->first;
if(m_FrameReferencedResources.find(id) == m_FrameReferencedResources.end() &&
!RenderDoc::Inst().GetCaptureOptions().refAllResources)
{
continue;
}
RecordType *record = GetResourceRecord(id);
if(record == NULL)
continue;
if(record->InternalResource)
continue;
// Load postponed resource if needed.
Prepare_InitialStateIfPostponed(id, false);
}
End_PrepareInitialBatch();
for(auto it = m_InitialContents.begin(); it != m_InitialContents.end(); ++it)
{
ResourceId id = it->first;
@@ -1547,9 +1605,6 @@ void ResourceManager<Configuration>::InsertInitialContentsChunks(WriteSerialiser
RDCDEBUG("Serialising dirty Resource %s", ToStr(id).c_str());
#endif
// Load postponed resource if needed.
Prepare_InitialStateIfPostponed(id, false);
dirty++;
if(!Need_InitialStateChunk(id, it->second.data))
@@ -1563,6 +1618,14 @@ void ResourceManager<Configuration>::InsertInitialContentsChunks(WriteSerialiser
{
it->second.chunk->Write(ser);
}
else if(!it->second.filename.empty())
{
FILE *f = FileIO::fopen(it->second.filename, FileIO::ReadBinary);
FileIO::fseek64(f, it->second.fileStart, SEEK_SET);
StreamReader reader(f, it->second.fileEnd - it->second.fileStart, Ownership::Stream);
StreamTransfer(ser.GetWriter(), &reader, NULL);
}
else
{
uint64_t size = GetSize_InitialState(id, it->second.data);
@@ -1895,9 +1958,7 @@ void ResourceManager<Configuration>::ReleaseCurrentResource(ResourceId id)
// We potentially need to prepare this resource on Active Capture,
// if it was postponed, but is about to go away.
if(IsActiveCapturing(m_State))
{
Prepare_InitialStateIfPostponed(id, true);
}
m_CurrentResourceMap.erase(id);
m_DirtyResources.erase(id);
+5 -5
View File
@@ -1535,7 +1535,7 @@ RDResult WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStru
return m_FatalError;
if(m_pDevice && m_pDevice->GetDeviceRemovedReason() != S_OK)
RETURN_ERROR_RESULT(ResultCode::ReplayDeviceLost, "Device lost during load: %s",
RETURN_ERROR_RESULT(ResultCode::DeviceLost, "Device lost during load: %s",
ToStr(m_pDevice->GetDeviceRemovedReason()).c_str());
return ResultCode::Succeeded;
@@ -1587,7 +1587,7 @@ void WrappedID3D11Device::ReplayLog(uint32_t startEventID, uint32_t endEventID,
D3D11MarkerRegion::Set("!!!!RenderDoc Internal: Done replay");
if(m_pDevice->GetDeviceRemovedReason() != S_OK)
SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayDeviceLost, "Device lost during replay: %s",
SET_ERROR_RESULT(m_FatalError, ResultCode::DeviceLost, "Device lost during replay: %s",
ToStr(m_pDevice->GetDeviceRemovedReason()).c_str());
}
@@ -2594,8 +2594,8 @@ void WrappedID3D11Device::CheckHRESULT(HRESULT hr)
if(hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET ||
hr == DXGI_ERROR_DEVICE_HUNG || hr == DXGI_ERROR_DRIVER_INTERNAL_ERROR)
{
SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayDeviceLost,
"Logging device lost fatal error for %s", ToStr(hr).c_str());
SET_ERROR_RESULT(m_FatalError, ResultCode::DeviceLost, "Logging device lost fatal error for %s",
ToStr(hr).c_str());
}
else if(hr == E_OUTOFMEMORY)
{
@@ -2605,7 +2605,7 @@ void WrappedID3D11Device::CheckHRESULT(HRESULT hr)
}
else
{
SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayOutOfMemory,
SET_ERROR_RESULT(m_FatalError, ResultCode::OutOfMemory,
"Logging out of memory fatal error for %s", ToStr(hr).c_str());
}
}
+1 -1
View File
@@ -625,7 +625,7 @@ rdcarray<CounterResult> D3D12Replay::FetchCounters(const rdcarray<GPUCounter> &c
{
RDResult err;
SET_ERROR_RESULT(
err, ResultCode::ReplayDeviceLost,
err, ResultCode::DeviceLost,
"D3D12 counters require Win10 developer mode enabled: Settings > Update & Security "
"> For Developers > Developer Mode");
m_pDevice->ReportFatalError(err);
+4 -4
View File
@@ -3338,8 +3338,8 @@ void WrappedID3D12Device::CheckHRESULT(HRESULT hr)
if(hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET ||
hr == DXGI_ERROR_DEVICE_HUNG || hr == DXGI_ERROR_DRIVER_INTERNAL_ERROR)
{
SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayDeviceLost,
"Logging device lost fatal error for %s", ToStr(hr).c_str());
SET_ERROR_RESULT(m_FatalError, ResultCode::DeviceLost, "Logging device lost fatal error for %s",
ToStr(hr).c_str());
}
else if(hr == E_OUTOFMEMORY)
{
@@ -3350,7 +3350,7 @@ void WrappedID3D12Device::CheckHRESULT(HRESULT hr)
else
{
RDCLOG("Logging out of memory fatal error for %s", ToStr(hr).c_str());
m_FatalError = ResultCode::ReplayOutOfMemory;
m_FatalError = ResultCode::OutOfMemory;
}
}
else
@@ -4632,7 +4632,7 @@ RDResult WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStru
return m_FatalError;
if(m_pDevice && m_pDevice->GetDeviceRemovedReason() != S_OK)
RETURN_ERROR_RESULT(ResultCode::ReplayDeviceLost, "Device lost during load: %s",
RETURN_ERROR_RESULT(ResultCode::DeviceLost, "Device lost during load: %s",
ToStr(m_pDevice->GetDeviceRemovedReason()).c_str());
return ResultCode::Succeeded;
+27 -7
View File
@@ -232,6 +232,11 @@ VkCommandBuffer WrappedVulkan::GetInitStateCmd()
VkMarkerRegion::Begin("!!!!RenderDoc Internal: ApplyInitialContents batched list",
initStateCurCmd);
}
else
{
VkMarkerRegion::Begin("!!!!RenderDoc Internal: PrepareInitialContents batched list",
initStateCurCmd);
}
}
initStateCurBatch++;
@@ -1863,6 +1868,8 @@ void WrappedVulkan::StartFrameCapture(DeviceOwnedWindow devWnd)
if(!IsBackgroundCapturing(m_State))
return;
m_CaptureFailure = false;
RDCLOG("Starting capture");
if(m_Queue == VK_NULL_HANDLE && m_QueueFamilyIdx != ~0U)
@@ -1930,11 +1937,8 @@ void WrappedVulkan::StartFrameCapture(DeviceOwnedWindow devWnd)
CheckVkResult(vkr);
}
m_PreparedNotSerialisedInitStates.clear();
GetResourceManager()->PrepareInitialContents();
SubmitAndFlushImageStateBarriers(m_setupImageBarriers);
SubmitCmds();
FlushQ();
SubmitAndFlushImageStateBarriers(m_cleanupImageBarriers);
{
SCOPED_LOCK(m_CapDescriptorsLock);
@@ -1979,6 +1983,14 @@ bool WrappedVulkan::EndFrameCapture(DeviceOwnedWindow devWnd)
if(!IsActiveCapturing(m_State))
return true;
if(m_CaptureFailure)
{
m_LastCaptureFailed = Timing::GetUnixTimestamp();
return DiscardFrameCapture(devWnd);
}
m_CaptureFailure = false;
VkSwapchainKHR swap = VK_NULL_HANDLE;
if(devWnd.windowHandle)
@@ -2432,6 +2444,9 @@ bool WrappedVulkan::EndFrameCapture(DeviceOwnedWindow devWnd)
GetResourceManager()->FreeInitialContents();
FreeAllMemory(MemoryScope::InitialContents);
for(rdcstr &fn : m_InitTempFiles)
FileIO::Delete(fn);
m_InitTempFiles.clear();
return true;
}
@@ -2441,6 +2456,8 @@ bool WrappedVulkan::DiscardFrameCapture(DeviceOwnedWindow devWnd)
if(!IsActiveCapturing(m_State))
return true;
m_CaptureFailure = false;
RDCLOG("Discarding frame capture.");
RenderDoc::Inst().FinishCaptureWriting(NULL, m_CapturedFrames.back().frameNumber);
@@ -2486,6 +2503,9 @@ bool WrappedVulkan::DiscardFrameCapture(DeviceOwnedWindow devWnd)
GetResourceManager()->FreeInitialContents();
FreeAllMemory(MemoryScope::InitialContents);
for(rdcstr &fn : m_InitTempFiles)
FileIO::Delete(fn);
m_InitTempFiles.clear();
return true;
}
@@ -4266,8 +4286,8 @@ void WrappedVulkan::CheckErrorVkResult(VkResult vkr)
if(vkr == VK_ERROR_INITIALIZATION_FAILED || vkr == VK_ERROR_DEVICE_LOST || vkr == VK_ERROR_UNKNOWN)
{
SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayDeviceLost,
"Logging device lost fatal error for %s", ToStr(vkr).c_str());
SET_ERROR_RESULT(m_FatalError, ResultCode::DeviceLost, "Logging device lost fatal error for %s",
ToStr(vkr).c_str());
m_FailedReplayResult = m_FatalError;
}
else if(vkr == VK_ERROR_OUT_OF_HOST_MEMORY || vkr == VK_ERROR_OUT_OF_DEVICE_MEMORY)
@@ -4278,7 +4298,7 @@ void WrappedVulkan::CheckErrorVkResult(VkResult vkr)
}
else
{
SET_ERROR_RESULT(m_FatalError, ResultCode::ReplayOutOfMemory,
SET_ERROR_RESULT(m_FatalError, ResultCode::OutOfMemory,
"Logging out of memory fatal error for %s", ToStr(vkr).c_str());
m_FailedReplayResult = m_FatalError;
}
+12
View File
@@ -312,6 +312,9 @@ private:
rdcarray<DebugMessage> GetDebugMessages();
void AddDebugMessage(DebugMessage msg);
bool m_CaptureFailure = false;
uint64_t m_LastCaptureFailed = 0;
RDResult m_LastCaptureError = ResultCode::Succeeded;
int m_OOMHandler = 0;
RDResult m_FatalError = ResultCode::Succeeded;
CaptureState m_State;
@@ -574,6 +577,12 @@ private:
static const int initialStateMaxBatch = 100;
int initStateCurBatch = 0;
bool m_PrepareInitStateBatching = false;
// list of resources which have been prepared but haven't been serialised
rdcarray<ResourceId> m_PreparedNotSerialisedInitStates;
rdcarray<rdcstr> m_InitTempFiles;
VkCommandBuffer initStateCurCmd = VK_NULL_HANDLE;
rdcarray<std::function<void()>> m_PendingCleanups;
@@ -590,6 +599,7 @@ private:
VkDeviceSize m_MemoryBlockSize[arraydim<MemoryScope>()] = {};
void FreeAllMemory(MemoryScope scope);
uint64_t CurMemoryUsage(MemoryScope scope);
void ResetMemoryBlocks(MemoryScope scope);
void FreeMemoryAllocation(MemoryAllocation alloc);
@@ -1083,6 +1093,8 @@ public:
CaptureState GetState() { return m_State; }
VulkanReplay *GetReplay() { return m_Replay; }
// replay interface
void Begin_PrepareInitialBatch();
void End_PrepareInitialBatch();
bool Prepare_InitialState(WrappedVkRes *res);
uint64_t GetSize_InitialState(ResourceId id, const VkInitialContents &initial);
template <typename SerialiserType>
+6 -6
View File
@@ -63,9 +63,9 @@ public:
void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &ret);
void CopyTex2DMSToBuffer(VkBuffer destBuffer, VkImage srcMS, VkExtent3D extent,
uint32_t baseSlice, uint32_t numSlices, uint32_t baseSample,
uint32_t numSamples, VkFormat fmt);
void CopyTex2DMSToBuffer(VkCommandBuffer cmd, VkBuffer destBuffer, VkImage srcMS,
VkExtent3D extent, uint32_t baseSlice, uint32_t numSlices,
uint32_t baseSample, uint32_t numSamples, VkFormat fmt);
void CopyBufferToTex2DMS(VkCommandBuffer cmd, VkImage destMS, VkBuffer srcBuffer,
VkExtent3D extent, uint32_t numSlices, uint32_t numSamples, VkFormat fmt);
@@ -163,9 +163,9 @@ private:
VkPipeline TexPipeline = VK_NULL_HANDLE;
} m_Custom;
void CopyDepthTex2DMSToBuffer(VkBuffer destBuffer, VkImage srcMS, VkExtent3D extent,
uint32_t baseSlice, uint32_t numSlices, uint32_t baseSample,
uint32_t numSamples, VkFormat fmt);
void CopyDepthTex2DMSToBuffer(VkCommandBuffer cmd, VkBuffer destBuffer, VkImage srcMS,
VkExtent3D extent, uint32_t baseSlice, uint32_t numSlices,
uint32_t baseSample, uint32_t numSamples, VkFormat fmt);
void CopyDepthBufferToTex2DMS(VkCommandBuffer cmd, VkImage destMS, VkBuffer srcBuffer,
VkExtent3D extent, uint32_t numSlices, uint32_t numSamples,
+179 -68
View File
@@ -23,6 +23,7 @@
******************************************************************************/
#include "core/settings.h"
#include "strings/string_utils.h"
#include "vk_core.h"
#include "vk_debug.h"
@@ -45,12 +46,107 @@ void DoSerialise(SerialiserType &ser, AspectSparseTable &el)
SERIALISE_MEMBER(table);
}
void WrappedVulkan::Begin_PrepareInitialBatch()
{
m_PrepareInitStateBatching = true;
}
void WrappedVulkan::End_PrepareInitialBatch()
{
CloseInitStateCmd();
SubmitAndFlushImageStateBarriers(m_setupImageBarriers);
SubmitCmds();
FlushQ();
SubmitAndFlushImageStateBarriers(m_cleanupImageBarriers);
m_PrepareInitStateBatching = false;
}
bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
{
ResourceId id = GetResourceManager()->GetID(res);
RDCASSERT(m_PrepareInitStateBatching);
VkResourceType type = IdentifyTypeByPtr(res);
uint64_t estimatedSize = 0;
if(type == eResDeviceMemory)
{
VkResourceRecord *record = GetResourceManager()->GetResourceRecord(id);
if(record)
estimatedSize = record->Length;
}
else if(type == eResImage)
{
WrappedVkImage *im = (WrappedVkImage *)res;
const ResourceInfo &resInfo = *im->record->resInfo;
const ImageInfo &imageInfo = resInfo.imageInfo;
estimatedSize = GetByteSize(imageInfo.extent.width, imageInfo.extent.height,
imageInfo.extent.depth, imageInfo.format, 0);
if(imageInfo.sampleCount > 1)
estimatedSize *= imageInfo.sampleCount;
if(imageInfo.layerCount > 1)
estimatedSize *= imageInfo.layerCount;
// conservative estimate of full mip chain impact
if(imageInfo.levelCount > 1)
estimatedSize *= 2;
}
uint32_t softMemoryLimit = RenderDoc::Inst().GetCaptureOptions().softMemoryLimit;
if(softMemoryLimit > 0 && !m_PreparedNotSerialisedInitStates.empty() &&
CurMemoryUsage(MemoryScope::InitialContents) + estimatedSize > softMemoryLimit * 1024 * 1024ULL)
{
CloseInitStateCmd();
SubmitAndFlushImageStateBarriers(m_setupImageBarriers);
SubmitCmds();
FlushQ();
SubmitAndFlushImageStateBarriers(m_cleanupImageBarriers);
RDCLOG("Flushing batch of initial states to disk with %llu bytes allocated",
CurMemoryUsage(MemoryScope::InitialContents));
rdcstr tempFile = StringFormat::Fmt(
"%s/rdoc_%llu_%llu.bin", get_dirname(RenderDoc::Inst().GetCaptureFileTemplate()).c_str(),
Timing::GetTick(), Threading::GetCurrentID());
FileIO::CreateParentDirectory(tempFile);
m_InitTempFiles.push_back(tempFile);
WriteSerialiser ser(
new StreamWriter(FileIO::fopen(tempFile, FileIO::WriteBinary), Ownership::Stream),
Ownership::Stream);
for(ResourceId flushId : m_PreparedNotSerialisedInitStates)
{
VkInitialContents initData = GetResourceManager()->GetInitialContents(flushId);
GetResourceManager()->SetInitialContents(flushId, VkInitialContents());
uint64_t start = ser.GetWriter()->GetOffset();
{
uint64_t size = GetSize_InitialState(id, initData);
SCOPED_SERIALISE_CHUNK(SystemChunk::InitialContents, size);
// record is not needed on vulkan
Serialise_InitialState(ser, flushId, NULL, &initData);
}
uint64_t end = ser.GetWriter()->GetOffset();
if(ser.IsErrored())
break;
GetResourceManager()->SetInitialFileStore(flushId, tempFile, start, end);
}
m_PreparedNotSerialisedInitStates.clear();
if(ser.IsErrored())
{
m_CaptureFailure = true;
m_LastCaptureError = ser.GetError();
return false;
}
}
if(type == eResDescriptorSet)
{
VkResourceRecord *record = GetResourceManager()->GetResourceRecord(id);
@@ -115,8 +211,7 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
}
VkDevice d = GetDev();
// INITSTATEBATCH
VkCommandBuffer cmd = GetNextCmd();
VkCommandBuffer cmd = GetInitStateCmd();
// must ensure offset remains valid. Must be multiple of block size, or 4, depending on format
VkDeviceSize bufAlignment = 4;
@@ -211,31 +306,26 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
}
// since this happens during capture, we don't want to start serialising extra buffer creates,
// so we manually create & then just wrap.
// leave this buffer as unwrapped
VkBuffer dstBuf;
vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &dstBuf);
CheckVkResult(vkr);
GetResourceManager()->WrapResource(Unwrap(d), dstBuf);
MemoryAllocation readbackmem =
AllocateMemoryForResource(dstBuf, MemoryScope::InitialContents, MemoryType::Readback);
VkMemoryRequirements dstBufMrq = {};
ObjDisp(d)->GetBufferMemoryRequirements(Unwrap(d), dstBuf, &dstBufMrq);
MemoryAllocation readbackmem = AllocateMemoryForResource(
true, dstBufMrq, MemoryScope::InitialContents, MemoryType::Readback);
if(readbackmem.mem == VK_NULL_HANDLE)
{
RDCERR("Couldn't allocate readback memory");
SET_ERROR_RESULT(m_LastCaptureError, ResultCode::OutOfMemory,
"Couldn't allocate readback memory");
m_CaptureFailure = true;
return false;
}
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(dstBuf), Unwrap(readbackmem.mem),
readbackmem.offs);
CheckVkResult(vkr);
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
vkr = ObjDisp(d)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), dstBuf, Unwrap(readbackmem.mem), readbackmem.offs);
CheckVkResult(vkr);
VkImageAspectFlags aspectFlags = FormatImageAspects(imageInfo.format);
@@ -252,18 +342,10 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
m_setupImageBarriers.Merge(setupBarriers);
if(wasms)
{
vkr = ObjDisp(d)->EndCommandBuffer(Unwrap(cmd));
CheckVkResult(vkr);
GetDebugManager()->CopyTex2DMSToBuffer(Unwrap(dstBuf), realim, imageInfo.extent, 0,
GetDebugManager()->CopyTex2DMSToBuffer(cmd, dstBuf, realim, imageInfo.extent, 0,
imageInfo.layerCount, 0, imageInfo.sampleCount,
imageInfo.format);
cmd = GetNextCmd();
vkr = ObjDisp(d)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
CheckVkResult(vkr);
VkBufferMemoryBarrier bufBarrier = {
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
NULL,
@@ -271,7 +353,7 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
VK_ACCESS_HOST_READ_BIT,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(dstBuf),
dstBuf,
0,
bufInfo.size,
};
@@ -321,9 +403,8 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
bufOffset += GetPlaneByteSize(imageInfo.extent.width, imageInfo.extent.height,
imageInfo.extent.depth, sizeFormat, m, i);
ObjDisp(d)->CmdCopyImageToBuffer(Unwrap(cmd), realim,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Unwrap(dstBuf),
1, &region);
ObjDisp(d)->CmdCopyImageToBuffer(
Unwrap(cmd), realim, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstBuf, 1, &region);
}
}
else
@@ -340,7 +421,7 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
imageInfo.extent.depth, sizeFormat, m);
ObjDisp(d)->CmdCopyImageToBuffer(
Unwrap(cmd), realim, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Unwrap(dstBuf), 1, &region);
Unwrap(cmd), realim, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstBuf, 1, &region);
if(aspectFlags == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
{
@@ -353,9 +434,8 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
bufOffset += GetByteSize(imageInfo.extent.width, imageInfo.extent.height,
imageInfo.extent.depth, VK_FORMAT_S8_UINT, m);
ObjDisp(d)->CmdCopyImageToBuffer(Unwrap(cmd), realim,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Unwrap(dstBuf),
1, &region);
ObjDisp(d)->CmdCopyImageToBuffer(
Unwrap(cmd), realim, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstBuf, 1, &region);
}
}
@@ -372,16 +452,16 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
InlineCleanupImageBarriers(cmd, cleanupBarriers);
m_cleanupImageBarriers.Merge(cleanupBarriers);
vkr = ObjDisp(d)->EndCommandBuffer(Unwrap(cmd));
CheckVkResult(vkr);
if(Vulkan_Debug_SingleSubmitFlushing())
{
CloseInitStateCmd();
SubmitAndFlushImageStateBarriers(m_setupImageBarriers);
SubmitCmds();
FlushQ();
SubmitAndFlushImageStateBarriers(m_cleanupImageBarriers);
}
SubmitAndFlushImageStateBarriers(m_setupImageBarriers);
SubmitCmds();
FlushQ();
SubmitAndFlushImageStateBarriers(m_cleanupImageBarriers);
ObjDisp(d)->DestroyBuffer(Unwrap(d), Unwrap(dstBuf), NULL);
GetResourceManager()->ReleaseWrappedResource(dstBuf);
AddPendingObjectCleanup([d, dstBuf]() { ObjDisp(d)->DestroyBuffer(Unwrap(d), dstBuf, NULL); });
VkInitialContents initialContents(type, readbackmem);
@@ -392,6 +472,7 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
}
GetResourceManager()->SetInitialContents(id, initialContents);
m_PreparedNotSerialisedInitStates.push_back(id);
return true;
}
@@ -410,8 +491,7 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
VkResult vkr = VK_SUCCESS;
VkDevice d = GetDev();
// INITSTATEBATCH
VkCommandBuffer cmd = GetNextCmd();
VkCommandBuffer cmd = GetInitStateCmd();
VkDeviceMemory datamem = ToUnwrappedHandle<VkDeviceMemory>(res);
VkDeviceSize datasize = record->Length;
@@ -443,51 +523,45 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
// since this happens during capture, we don't want to start serialising extra buffer creates,
// so we manually create & then just wrap.
// leave this buffer as unwrapped
VkBuffer dstBuf;
bufInfo.size = datasize;
vkr = ObjDisp(d)->CreateBuffer(Unwrap(d), &bufInfo, NULL, &dstBuf);
CheckVkResult(vkr);
GetResourceManager()->WrapResource(Unwrap(d), dstBuf);
MemoryAllocation readbackmem =
AllocateMemoryForResource(dstBuf, MemoryScope::InitialContents, MemoryType::Readback);
VkMemoryRequirements dstBufMrq = {};
ObjDisp(d)->GetBufferMemoryRequirements(Unwrap(d), dstBuf, &dstBufMrq);
MemoryAllocation readbackmem = AllocateMemoryForResource(
true, dstBufMrq, MemoryScope::InitialContents, MemoryType::Readback);
if(readbackmem.mem == VK_NULL_HANDLE)
{
RDCERR("Couldn't allocate readback memory");
SET_ERROR_RESULT(m_LastCaptureError, ResultCode::OutOfMemory,
"Couldn't allocate readback memory");
m_CaptureFailure = true;
return false;
}
CheckVkResult(vkr);
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), Unwrap(dstBuf), Unwrap(readbackmem.mem),
readbackmem.offs);
CheckVkResult(vkr);
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
vkr = ObjDisp(d)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
vkr = ObjDisp(d)->BindBufferMemory(Unwrap(d), dstBuf, Unwrap(readbackmem.mem), readbackmem.offs);
CheckVkResult(vkr);
VkBufferCopy region = {0, 0, datasize};
ObjDisp(d)->CmdCopyBuffer(Unwrap(cmd), Unwrap(record->memMapState->wholeMemBuf), Unwrap(dstBuf),
1, &region);
ObjDisp(d)->CmdCopyBuffer(Unwrap(cmd), Unwrap(record->memMapState->wholeMemBuf), dstBuf, 1,
&region);
vkr = ObjDisp(d)->EndCommandBuffer(Unwrap(cmd));
CheckVkResult(vkr);
AddPendingObjectCleanup([d, dstBuf]() { ObjDisp(d)->DestroyBuffer(Unwrap(d), dstBuf, NULL); });
// INITSTATEBATCH
SubmitCmds();
FlushQ();
ObjDisp(d)->DestroyBuffer(Unwrap(d), Unwrap(dstBuf), NULL);
GetResourceManager()->ReleaseWrappedResource(dstBuf);
if(Vulkan_Debug_SingleSubmitFlushing())
{
SubmitCmds();
FlushQ();
}
GetResourceManager()->SetInitialContents(id, VkInitialContents(type, readbackmem));
m_PreparedNotSerialisedInitStates.push_back(id);
return true;
}
@@ -496,6 +570,8 @@ bool WrappedVulkan::Prepare_InitialState(WrappedVkRes *res)
RDCERR("Unhandled resource type %d", type);
}
m_CaptureFailure = true;
SET_ERROR_RESULT(m_LastCaptureError, ResultCode::InternalError, "Unknown resource encountered");
return false;
}
@@ -969,6 +1045,41 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, V
AddResourceCurChunk(id);
}
// we will never have a case where we prepare some resources, serialise one, and then don't
// serialise all the rest of those resources before any further prepares. Whenever we see a
// serialise we can then reset the memory for re-use in any prepares that do then come later.
//
// The expected order is:
// 1. Prepare all non-postponed resources.
// As part of this in Prepare_initialState we might serialise all pending resources if we
// bump into the memory limit. This means memory can be re-used so complies with our
// stipulations above
// 2. Prepare postponed resources last minute
// These are individually synchronised to the GPU (not batched) to ensure we have the right
// data, but otherwise are treated as above - they will be left pending unless flushed due to
// the memory limit and if they are flushed they work exactly the same way
// 3. At the end of the frame, all postponed resources are prepared in a batch
// 4. Finally any resources that must be serialised are.
//
// Note that with no memory limit, all prepares will happen in steps 1-3 (in two large batches 1
// and 3 and a batch-per-resource in 2) before any serialises in 4, so this reset is effectively
// redundant at that point.
//
// Note also that with a memory limit resources may overlap between these cases - the only
// Serialise calls happen when the memory limit is hit and we flush everything pending, but it is
// likely that some resources will be prepared in step 1, and not be flushed and so still be
// pending through some or all of the last-minute prepares in step 2 and into step 3.
// This is still fine though as Serialise is not called in between there for other resources.
if(IsCaptureMode(m_State))
{
ResetMemoryBlocks(MemoryScope::InitialContents);
// if got here through 'natural' serialises, this array will not be cleared otherwise so we
// clear it here just for sanity. It should not be used otherwise though
m_PreparedNotSerialisedInitStates.clear();
}
if(type == eResDescriptorSet)
{
DescriptorSetSlot *Bindings = NULL;
+10
View File
@@ -996,6 +996,16 @@ MemRefs *VulkanResourceManager::FindMemRefs(ResourceId mem)
return NULL;
}
void VulkanResourceManager::Begin_PrepareInitialBatch()
{
return m_Core->Begin_PrepareInitialBatch();
}
void VulkanResourceManager::End_PrepareInitialBatch()
{
return m_Core->End_PrepareInitialBatch();
}
bool VulkanResourceManager::Prepare_InitialState(WrappedVkRes *res)
{
return m_Core->Prepare_InitialState(res);
+6
View File
@@ -315,7 +315,11 @@ public:
void PreFreeMemory(ResourceId id)
{
if(IsActiveCapturing(m_State))
{
ResourceManager::Begin_PrepareInitialBatch();
ResourceManager::Prepare_InitialStateIfPostponed(id, true);
ResourceManager::End_PrepareInitialBatch();
}
}
template <typename realtype>
@@ -452,6 +456,8 @@ private:
bool ResourceTypeRelease(WrappedVkRes *res);
bool Prepare_InitialState(WrappedVkRes *res);
void Begin_PrepareInitialBatch();
void End_PrepareInitialBatch();
uint64_t GetSize_InitialState(ResourceId id, const VkInitialContents &initial);
bool Serialise_InitialState(WriteSerialiser &ser, ResourceId id, VkResourceRecord *record,
const VkInitialContents *initial);
+20 -2
View File
@@ -280,6 +280,10 @@ MemoryAllocation WrappedVulkan::AllocateMemoryForResource(bool buffer, VkMemoryR
break;
}
uint64_t initStateLimitMB = RenderDoc::Inst().GetCaptureOptions().softMemoryLimit;
if(initStateLimitMB > 0)
allocSize = RDCMAX(initStateLimitMB, allocSize);
uint32_t memoryTypeIndex = 0;
// Upload heaps are sometimes limited in size. To prevent OOM issues, deselect any memory types
@@ -323,8 +327,11 @@ MemoryAllocation WrappedVulkan::AllocateMemoryForResource(bool buffer, VkMemoryR
{
// if we get an over-sized allocation, first try to immediately jump to the largest block
// size.
allocSize = 256;
info.allocationSize = allocSize * 1024 * 1024;
if(initStateLimitMB == 0)
{
allocSize = 256;
info.allocationSize = allocSize * 1024 * 1024;
}
// if it's still over-sized, just allocate precisely enough and give it a dedicated allocation
if(ret.size > info.allocationSize)
@@ -401,6 +408,17 @@ MemoryAllocation WrappedVulkan::AllocateMemoryForResource(VkBuffer buf, MemorySc
return AllocateMemoryForResource(true, mrq, scope, type);
}
uint64_t WrappedVulkan::CurMemoryUsage(MemoryScope scope)
{
rdcarray<MemoryAllocation> &allocList = m_MemoryBlocks[(size_t)scope];
uint64_t ret = 0;
for(MemoryAllocation &alloc : allocList)
ret += alloc.offs;
return ret;
}
void WrappedVulkan::FreeAllMemory(MemoryScope scope)
{
rdcarray<MemoryAllocation> &allocList = m_MemoryBlocks[(size_t)scope];
+44 -40
View File
@@ -30,13 +30,14 @@
#include "data/glsl/glsl_globals.h"
#include "data/glsl/glsl_ubos_cpp.h"
void VulkanDebugManager::CopyTex2DMSToBuffer(VkBuffer destBuffer, VkImage srcMS, VkExtent3D extent,
uint32_t baseSlice, uint32_t numSlices,
uint32_t baseSample, uint32_t numSamples, VkFormat fmt)
void VulkanDebugManager::CopyTex2DMSToBuffer(VkCommandBuffer cmd, VkBuffer destBuffer,
VkImage srcMS, VkExtent3D extent, uint32_t baseSlice,
uint32_t numSlices, uint32_t baseSample,
uint32_t numSamples, VkFormat fmt)
{
if(IsDepthOrStencilFormat(fmt))
{
CopyDepthTex2DMSToBuffer(destBuffer, srcMS, extent, baseSlice, numSlices, baseSample,
CopyDepthTex2DMSToBuffer(cmd, destBuffer, srcMS, extent, baseSlice, numSlices, baseSample,
numSamples, fmt);
return;
}
@@ -87,16 +88,22 @@ void VulkanDebugManager::CopyTex2DMSToBuffer(VkBuffer destBuffer, VkImage srcMS,
CheckVkResult(vkr);
NameUnwrappedVulkanObject(srcView, "MS -> Buffer srcView");
VkCommandBuffer cmd = VK_NULL_HANDLE;
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
cmd = m_pDriver->GetNextCmd();
bool endCommand = false;
if(cmd == VK_NULL_HANDLE)
{
cmd = m_pDriver->GetNextCmd();
if(cmd != VK_NULL_HANDLE)
ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
endCommand = true;
}
if(cmd == VK_NULL_HANDLE)
return;
ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
{
VkMarkerRegion region(cmd, "CopyTex2DMSToBuffer");
@@ -159,24 +166,21 @@ void VulkanDebugManager::CopyTex2DMSToBuffer(VkBuffer destBuffer, VkImage srcMS,
}
}
}
ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
cmd = VK_NULL_HANDLE;
if(endCommand)
ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
// submit cmds and wait for idle so we can readback
m_pDriver->SubmitCmds();
m_pDriver->FlushQ();
RDCASSERT(cmd == VK_NULL_HANDLE);
ObjDisp(dev)->DestroyImageView(Unwrap(dev), srcView, NULL);
ResetBufferMSDescriptorPools();
m_pDriver->AddPendingObjectCleanup([this, dev, srcView]() {
ObjDisp(dev)->DestroyImageView(Unwrap(dev), srcView, NULL);
ResetBufferMSDescriptorPools();
});
}
void VulkanDebugManager::CopyDepthTex2DMSToBuffer(VkBuffer destBuffer, VkImage srcMS,
VkExtent3D extent, uint32_t baseSlice,
uint32_t numSlices, uint32_t baseSample,
uint32_t numSamples, VkFormat fmt)
void VulkanDebugManager::CopyDepthTex2DMSToBuffer(VkCommandBuffer cmd, VkBuffer destBuffer,
VkImage srcMS, VkExtent3D extent,
uint32_t baseSlice, uint32_t numSlices,
uint32_t baseSample, uint32_t numSamples,
VkFormat fmt)
{
if(m_DepthMS2BufferPipe == VK_NULL_HANDLE)
return;
@@ -242,17 +246,22 @@ void VulkanDebugManager::CopyDepthTex2DMSToBuffer(VkBuffer destBuffer, VkImage s
NameUnwrappedVulkanObject(srcStencilView, "Depth MS -> Array srcStencilView");
}
VkCommandBuffer cmd = VK_NULL_HANDLE;
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
cmd = m_pDriver->GetNextCmd();
bool endCommand = false;
if(cmd == VK_NULL_HANDLE)
{
cmd = m_pDriver->GetNextCmd();
if(cmd != VK_NULL_HANDLE)
ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
endCommand = true;
}
if(cmd == VK_NULL_HANDLE)
return;
ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE,
Unwrap(m_DepthMS2BufferPipe));
@@ -344,21 +353,16 @@ void VulkanDebugManager::CopyDepthTex2DMSToBuffer(VkBuffer destBuffer, VkImage s
}
}
ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
if(endCommand)
ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
cmd = VK_NULL_HANDLE;
// submit cmds and wait for idle so we can readback
m_pDriver->SubmitCmds();
m_pDriver->FlushQ();
RDCASSERT(cmd == VK_NULL_HANDLE);
if(srcDepthView != VK_NULL_HANDLE)
ObjDisp(dev)->DestroyImageView(Unwrap(dev), srcDepthView, NULL);
if(srcStencilView != VK_NULL_HANDLE)
ObjDisp(dev)->DestroyImageView(Unwrap(dev), srcStencilView, NULL);
ResetBufferMSDescriptorPools();
m_pDriver->AddPendingObjectCleanup([this, dev, srcDepthView, srcStencilView]() {
if(srcDepthView != VK_NULL_HANDLE)
ObjDisp(dev)->DestroyImageView(Unwrap(dev), srcDepthView, NULL);
if(srcStencilView != VK_NULL_HANDLE)
ObjDisp(dev)->DestroyImageView(Unwrap(dev), srcStencilView, NULL);
ResetBufferMSDescriptorPools();
});
}
void VulkanDebugManager::CopyBufferToTex2DMS(VkCommandBuffer cmd, VkImage destMS,
+2 -14
View File
@@ -3788,20 +3788,8 @@ void VulkanReplay::GetTextureData(ResourceId tex, const Subresource &sub,
m_pDriver->InlineSetupImageBarriers(cmd, setupBarriers);
m_pDriver->SubmitAndFlushImageStateBarriers(setupBarriers);
vkr = vt->EndCommandBuffer(Unwrap(cmd));
CheckVkResult(vkr);
GetDebugManager()->CopyTex2DMSToBuffer(readbackBuf, srcImage, imCreateInfo.extent, s.slice, 1,
s.sample, 1, imCreateInfo.format);
// fetch a new command buffer for copy & readback
cmd = m_pDriver->GetNextCmd();
if(cmd == VK_NULL_HANDLE)
return;
vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
CheckVkResult(vkr);
GetDebugManager()->CopyTex2DMSToBuffer(cmd, readbackBuf, srcImage, imCreateInfo.extent, s.slice,
1, s.sample, 1, imCreateInfo.format);
m_pDriver->InlineCleanupImageBarriers(cmd, cleanupBarriers);
@@ -4221,6 +4221,17 @@ VkResult WrappedVulkan::vkCreateDevice(VkPhysicalDevice physicalDevice,
m_PhysicalDeviceData.driverInfo =
VkDriverInfo(m_PhysicalDeviceData.props, m_PhysicalDeviceData.driverProps, true);
// hack for steamdeck, set soft memory limit to 200MB if it's not specified
if(m_PhysicalDeviceData.driverProps.driverID == VK_DRIVER_ID_MESA_RADV &&
m_PhysicalDeviceData.props.vendorID == 0x1002 && m_PhysicalDeviceData.props.deviceID == 0x163F)
{
CaptureOptions opts = RenderDoc::Inst().GetCaptureOptions();
if(opts.softMemoryLimit == 0)
opts.softMemoryLimit = 200;
RenderDoc::Inst().SetCaptureOptions(opts);
RDCLOG("Forcing 200MB soft memory limit");
}
ChooseMemoryIndices();
m_PhysicalDeviceData.queueCount = (uint32_t)queueProps.size();
@@ -1090,6 +1090,10 @@ void WrappedVulkan::HandlePresent(VkQueue queue, const VkPresentInfoKHR *pPresen
rdcstr overlayText =
RenderDoc::Inst().GetOverlayText(RDCDriver::Vulkan, devWnd, m_FrameCounter, 0);
if(m_LastCaptureFailed > 0 && Timing::GetUnixTimestamp() - m_LastCaptureFailed < 5)
overlayText += StringFormat::Fmt("\nCapture failed: %s",
ResultDetails(m_LastCaptureError).Message().c_str());
if(!overlayText.empty())
{
m_TextRenderer->BeginText(textstate);
+7
View File
@@ -56,6 +56,7 @@ int RENDERDOC_CC SetCaptureOptionU32(RENDERDOC_CaptureOption opt, uint32_t val)
else
RDCWARN("AllowUnsupportedVendorExtensions unexpected parameter %x", val);
break;
case eRENDERDOC_Option_SoftMemoryLimit: opts.softMemoryLimit = val; break;
default: RDCLOG("Unrecognised capture option '%d'", opt); return 0;
}
@@ -88,6 +89,7 @@ int RENDERDOC_CC SetCaptureOptionF32(RENDERDOC_CaptureOption opt, float val)
case eRENDERDOC_Option_AllowUnsupportedVendorExtensions:
RDCWARN("AllowUnsupportedVendorExtensions unexpected parameter %f", val);
break;
case eRENDERDOC_Option_SoftMemoryLimit: opts.softMemoryLimit = (uint32_t)val; break;
default: RDCLOG("Unrecognised capture option '%d'", opt); return 0;
}
@@ -125,6 +127,8 @@ uint32_t RENDERDOC_CC GetCaptureOptionU32(RENDERDOC_CaptureOption opt)
case eRENDERDOC_Option_DebugOutputMute:
return (RenderDoc::Inst().GetCaptureOptions().debugOutputMute ? 1 : 0);
case eRENDERDOC_Option_AllowUnsupportedVendorExtensions: return 0;
case eRENDERDOC_Option_SoftMemoryLimit:
return (RenderDoc::Inst().GetCaptureOptions().softMemoryLimit);
default: break;
}
@@ -162,6 +166,8 @@ float RENDERDOC_CC GetCaptureOptionF32(RENDERDOC_CaptureOption opt)
case eRENDERDOC_Option_DebugOutputMute:
return (RenderDoc::Inst().GetCaptureOptions().debugOutputMute ? 1.0f : 0.0f);
case eRENDERDOC_Option_AllowUnsupportedVendorExtensions: return 0.0f;
case eRENDERDOC_Option_SoftMemoryLimit:
return (RenderDoc::Inst().GetCaptureOptions().softMemoryLimit * 1.0f);
default: break;
}
@@ -184,6 +190,7 @@ CaptureOptions::CaptureOptions()
refAllResources = false;
captureAllCmdLists = false;
debugOutputMute = true;
softMemoryLimit = 0;
}
#if ENABLED(ENABLE_UNIT_TESTS)
+2 -1
View File
@@ -102,8 +102,9 @@ void DoSerialise(SerialiserType &ser, CaptureOptions &el)
SERIALISE_MEMBER(refAllResources);
SERIALISE_MEMBER(captureAllCmdLists);
SERIALISE_MEMBER(debugOutputMute);
SERIALISE_MEMBER(softMemoryLimit);
SIZE_CHECK(20);
SIZE_CHECK(24);
}
template <typename SerialiserType>
+22 -14
View File
@@ -1019,7 +1019,7 @@ rdcstr DoStringise(const bool &el)
}
Chunk *Chunk::Create(Serialiser<SerialiserMode::Writing> &ser, uint16_t chunkType,
ChunkAllocator *allocator)
ChunkAllocator *allocator, bool stealDataFromWriter)
{
RDCCOMPILE_ASSERT(sizeof(Chunk) <= 16, "Chunk should be no more than 16 bytes");
@@ -1027,24 +1027,32 @@ Chunk *Chunk::Create(Serialiser<SerialiserMode::Writing> &ser, uint16_t chunkTyp
uint32_t length = (uint32_t)ser.GetWriter()->GetOffset();
byte *data = NULL;
if(allocator)
if(stealDataFromWriter)
{
// try to allocate from the allocator
data = allocator->AllocAlignedBuffer(length);
// if we couldn't satisfy the allocation then pretend we never had an allocator in the first
// place. We'll externally allocate the chunk and the data.
if(!data)
allocator = NULL;
data = ser.GetWriter()->StealDataAndRewind();
}
else
{
if(allocator)
{
// try to allocate from the allocator
data = allocator->AllocAlignedBuffer(length);
// if we don't have an allocator or we gave up on it above, allocate the data externally
if(!allocator)
data = AllocAlignedBuffer(length);
// if we couldn't satisfy the allocation then pretend we never had an allocator in the first
// place. We'll externally allocate the chunk and the data.
if(!data)
allocator = NULL;
}
memcpy(data, ser.GetWriter()->GetData(), (size_t)length);
// if we don't have an allocator or we gave up on it above, allocate the data externally
if(!allocator)
data = AllocAlignedBuffer(length);
ser.GetWriter()->Rewind();
memcpy(data, ser.GetWriter()->GetData(), (size_t)length);
ser.GetWriter()->Rewind();
}
Chunk *ret = NULL;
+8 -2
View File
@@ -1807,7 +1807,7 @@ public:
// grab current contents of the serialiser into a new chunk
static Chunk *Create(Serialiser<SerialiserMode::Writing> &ser, uint16_t chunkType,
ChunkAllocator *allocator = NULL);
ChunkAllocator *allocator = NULL, bool stealDataFromWriter = false);
byte *GetData() const { return m_Data; }
Chunk *Duplicate()
@@ -1867,10 +1867,16 @@ public:
End();
}
Chunk *Steal()
{
End();
return Chunk::Create(m_Ser, m_Idx, NULL, true);
}
Chunk *Get(ChunkAllocator *allocator = NULL)
{
End();
return Chunk::Create(m_Ser, m_Idx, allocator);
return Chunk::Create(m_Ser, m_Idx, allocator, false);
}
private:
+9 -2
View File
@@ -380,8 +380,15 @@ bool StreamReader::ReadFromExternal(void *buffer, uint64_t length)
success = (numRead == length);
if(!success)
SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "Error reading from file: %s",
FileIO::ErrorString().c_str());
{
if(FileIO::feof(m_File))
SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed,
"Error reading from file: hit end of file unexpectedly. Out of disk space "
"or truncated file?");
else
SET_ERROR_RESULT(m_Error, ResultCode::FileIOFailed, "Error reading from file: %s",
FileIO::ErrorString().c_str());
}
}
else if(m_Sock)
{
+11
View File
@@ -393,6 +393,17 @@ public:
uint64_t GetOffset() { return m_WriteSize; }
const byte *GetData() { return m_BufferBase; }
byte *StealDataAndRewind()
{
byte *ret = m_BufferBase;
const uint64_t bufferSize = m_BufferEnd - m_BufferBase;
m_BufferBase = m_BufferHead = AllocAlignedBuffer(bufferSize);
m_BufferEnd = m_BufferBase + bufferSize;
m_WriteSize = 0;
return ret;
}
template <uint64_t alignment>
bool AlignTo()
{
+4
View File
@@ -1639,6 +1639,9 @@ int renderdoccmd(GlobalEnvironment &env, std::vector<std::string> &argv)
"Capturing Option: Include all live resources, not just those used by a frame.");
cmd.add("opt-capture-all-cmd-lists", 0,
"Capturing Option: In D3D11, record all command lists from application start.");
cmd.add<int>("opt-soft-memory-limit", 0,
"Capturing Option: Specify a soft memory limit to try to respect.", false, 0,
cmdline::range(0, 10000));
}
cmd.parse_check(argv, true);
@@ -1670,6 +1673,7 @@ int renderdoccmd(GlobalEnvironment &env, std::vector<std::string> &argv)
opts.captureAllCmdLists = true;
opts.delayForDebugger = (uint32_t)cmd.get<int>("opt-delay-for-debugger");
opts.softMemoryLimit = (uint32_t)cmd.get<int>("opt-soft-memory-limit");
}
if(!it->second->HandlesUsageManually() && cmd.exist("help"))