mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-18 04:26:49 +00:00
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:
@@ -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">
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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, ®ion);
|
||||
ObjDisp(d)->CmdCopyImageToBuffer(
|
||||
Unwrap(cmd), realim, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstBuf, 1, ®ion);
|
||||
}
|
||||
}
|
||||
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, ®ion);
|
||||
Unwrap(cmd), realim, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstBuf, 1, ®ion);
|
||||
|
||||
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, ®ion);
|
||||
ObjDisp(d)->CmdCopyImageToBuffer(
|
||||
Unwrap(cmd), realim, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstBuf, 1, ®ion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, ®ion);
|
||||
ObjDisp(d)->CmdCopyBuffer(Unwrap(cmd), Unwrap(record->memMapState->wholeMemBuf), dstBuf, 1,
|
||||
®ion);
|
||||
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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"))
|
||||
|
||||
Reference in New Issue
Block a user