Add a system for backends to handle device lost/OOM errors and report it

* The UI will become non-functional and the backend will be replaced with a do-
  nothing one that keeps things alive without needing error bulletproofing
  everywhere in the real backend.
This commit is contained in:
baldurk
2021-08-18 20:12:07 +01:00
parent dc955079ab
commit 620e75c2a1
40 changed files with 1320 additions and 59 deletions
+97 -3
View File
@@ -44,6 +44,7 @@
#include "Windows/ConstantBufferPreviewer.h"
#include "Windows/DebugMessageView.h"
#include "Windows/Dialogs/CaptureDialog.h"
#include "Windows/Dialogs/CrashDialog.h"
#include "Windows/Dialogs/LiveCapture.h"
#include "Windows/Dialogs/SettingsDialog.h"
#include "Windows/EventBrowser.h"
@@ -98,6 +99,98 @@ CaptureContext::CaptureContext(PersistantConfig &cfg) : m_Config(cfg)
m_MainWindow->LoadInitialLayout();
m_Replay.SetFatalErrorCallback([this]() {
GUIInvoke::call(m_MainWindow, [this]() {
RefreshUIStatus({}, true, true);
ReplayStatus err = GetFatalError();
QString title;
QString text;
if(err == ReplayStatus::RemoteServerConnectionLost)
{
QString serverName = Replay().CurrentRemote().Name();
title = tr("Connection lost to %1").arg(serverName);
text =
tr("Connection to %1 was lost while analysing capture.\n"
"This is most commonly caused by a crash, but that can't be verified and it may be "
"a connection issue.")
.arg(serverName);
}
else if(err == ReplayStatus::ReplayDeviceLost)
{
title = tr("Device Lost error");
text =
tr("The capture analysis encountered a device lost error. This may be due to an "
"application bug, a RenderDoc bug, or insufficient resources on the system to "
"analyse the capture.\n\n"
"It is recommended that you run your application with API validation enabled, as "
"API use errors can cause this kind of problem.");
}
else if(err == ReplayStatus::ReplayOutOfMemory)
{
title = tr("Out of GPU memory");
text = tr(
"While analysing the capture it ran out of GPU memory. This is most likely caused by "
"insufficient resources on the system to analyse the capture, which is possible if "
"there is memory pressure from other applications or if the capture is heavyweight.");
}
else
{
title = tr("Unrecoverable error");
text = tr("While analysing the capture we encountered an unrecoverable error:\n\n%1")
.arg(QString(ToStr(err)));
}
text +=
tr("\n\n"
"This error is not recoverable and the analysis cannot continue. The UI will be "
"non-functional until the capture is closed and reloaded.\n\n");
bool add_report = false;
if(CrashDialog::HasCaptureReady(m_Config))
{
text += tr(
"If you think this may be a RenderDoc bug please click the button below to report it, "
"but note that this will require you to upload the capture for reproduction as "
"otherwise it is impossible to tell what the problem may be.");
add_report = true;
}
else
{
text += tr("The capture must be saved locally if you want to report this as a bug. ");
if(Replay().CurrentRemote().IsConnected())
{
text +=
tr("Before closing the capture you can save it to disk and manually report a bug. "
"Please include the capture, or else it will be impossible to tell what the "
"problem may be.");
}
else
{
text += tr("You will need to reconnect to the remote server to save the capture.");
}
}
QMessageBox mb(QMessageBox::Critical, title, text, QMessageBox::Ok, m_MainWindow);
mb.setDefaultButton(QMessageBox::NoButton);
QPushButton *report = NULL;
if(add_report)
report = mb.addButton(tr("Report bug"), QMessageBox::AcceptRole);
RDDialog::show(&mb);
if(mb.clickedButton() == (QAbstractButton *)report)
{
m_MainWindow->sendErrorReport(true);
}
});
});
{
QDir dir(ConfigFilePath("extensions"));
@@ -712,10 +805,11 @@ void CaptureContext::LoadCapture(const rdcstr &captureFile, const ReplayOptions
m_LoadInProgress = true;
if(local)
{
m_Config.CrashReport_LastOpenedCapture = origFilename;
m_Config.Save();
}
else
m_Config.CrashReport_LastOpenedCapture = QString();
m_Config.Save();
bool newCapture = (!temporary && !Config().RecentCaptureFiles.contains(origFilename));
+1
View File
@@ -148,6 +148,7 @@ public:
bool IsCaptureLocal() override { return m_CaptureLocal; }
bool IsCaptureTemporary() override { return m_CaptureTemporary; }
bool IsCaptureLoading() override { return m_LoadInProgress; }
ReplayStatus GetFatalError() override { return m_Replay.GetFatalError(); }
rdcstr GetCaptureFilename() override { return m_CaptureFile; }
CaptureModifications GetCaptureModifications() override { return m_CaptureMods; }
const FrameDescription &FrameInfo() override { return m_FrameInfo; }
+7
View File
@@ -1857,6 +1857,13 @@ temporary and treated like any other capture.
)");
virtual bool IsCaptureLoading() = 0;
DOCUMENT(R"(If a capture is loaded, return the current fatal error status.
:return: If a capture is currently loaded, return the fatal error status.
:rtype: ReplayStatus
)");
virtual ReplayStatus GetFatalError() = 0;
DOCUMENT(R"(Retrieve the filename for the currently loaded capture.
:return: The filename of the current capture.
+10
View File
@@ -48,6 +48,8 @@ void ReplayManager::OpenCapture(const QString &capturefile, const ReplayOptions
if(m_Running)
return;
m_FatalError = ReplayStatus::Succeeded;
// TODO maybe we could expose this choice to the user?
int proxyRenderer = -1;
@@ -296,6 +298,7 @@ void ReplayManager::CancelReplayLoop()
void ReplayManager::CloseThread()
{
m_Running = false;
m_FatalError = ReplayStatus::Succeeded;
m_RenderCondition.wakeAll();
@@ -492,6 +495,13 @@ void ReplayManager::run(int proxyRenderer, const QString &capturefile, const Rep
cmd->method(m_Renderer);
ReplayStatus err = m_Renderer->GetFatalErrorStatus();
if(m_FatalError == ReplayStatus::Succeeded && err != ReplayStatus::Succeeded)
{
m_FatalError = err;
m_FatalErrorCallback();
}
{
QMutexLocker lock(&m_TimerLock);
m_CommandTimer.invalidate();
+4
View File
@@ -54,6 +54,8 @@ public:
bool IsRunning();
ReplayStatus GetCreateStatus() { return m_CreateStatus; }
void SetFatalErrorCallback(std::function<void()> cb) { m_FatalErrorCallback = cb; }
ReplayStatus GetFatalError() { return m_FatalError; }
float GetCurrentProcessingTime();
QString GetCurrentProcessingTag();
// this tagged version is for cases when we might send a request - e.g. to pick a vertex or pixel
@@ -136,6 +138,8 @@ private:
IRemoteServer *m_Remote = NULL;
IRemoteServer *m_OrphanedRemote = NULL;
std::function<void()> m_FatalErrorCallback;
ReplayStatus m_FatalError = ReplayStatus::Succeeded;
volatile bool m_Running;
LambdaThread *m_Thread;
ReplayStatus m_CreateStatus = ReplayStatus::Succeeded;
+9 -3
View File
@@ -96,7 +96,12 @@ void CustomPaintWidget::OnSelectedEventChanged(uint32_t eventId)
void CustomPaintWidget::OnEventChanged(uint32_t eventId)
{
// nothing, we only care about capture loaded/closed events
// if we've encountered a fatal error recreate the widget and take over painting
if(m_Rendering && m_Ctx && m_Ctx->GetFatalError() != ReplayStatus::Succeeded)
{
RecreateInternalWidget();
update();
}
}
void CustomPaintWidget::update()
@@ -130,8 +135,9 @@ void CustomPaintWidget::RecreateInternalWidget()
return;
}
// if no capture is loaded, we're not rendering anymore.
m_Rendering = m_Rendering && m_Ctx && m_Ctx->IsCaptureLoaded();
// if no capture is loaded, or we've encountered a fatal error, we're not rendering anymore.
m_Rendering = m_Rendering && m_Ctx && m_Ctx->IsCaptureLoaded() &&
m_Ctx->GetFatalError() == ReplayStatus::Succeeded;
// we need to recreate the widget if it's not matching out rendering state.
if(m_Internal == NULL || m_Rendering != m_Internal->IsRendering())
+21 -2
View File
@@ -39,6 +39,8 @@
#include "Code/QRDUtils.h"
#include "ui_CrashDialog.h"
const qint64 MaxUploadSize = 2250LL * 1024LL * 1024LL;
CrashDialog::CrashDialog(PersistantConfig &cfg, QVariantMap crashReportJSON, QWidget *parent)
: QDialog(parent), ui(new Ui::CrashDialog), m_Config(cfg)
{
@@ -50,11 +52,13 @@ CrashDialog::CrashDialog(PersistantConfig &cfg, QVariantMap crashReportJSON, QWi
m_ReportMetadata = crashReportJSON;
const bool replayCrash = crashReportJSON[lit("replaycrash")].toUInt() != 0;
const bool forceCapture = crashReportJSON[lit("forcecapture")].toUInt() != 0;
const bool manualReport =
crashReportJSON.contains(lit("manual")) && crashReportJSON[lit("manual")].toUInt() != 0;
// remove metadata we don't send directly
m_ReportMetadata.remove(lit("report"));
m_ReportMetadata.remove(lit("forcecapture"));
m_ReportMetadata.remove(lit("replaycrash"));
setStage(ReportStage::FillingDetails);
@@ -98,7 +102,7 @@ CrashDialog::CrashDialog(PersistantConfig &cfg, QVariantMap crashReportJSON, QWi
cap->Shutdown();
if(capInfo.size() > 2250LL * 1024LL * 1024LL)
if(capInfo.size() > MaxUploadSize)
{
// capture is too large to upload :(
ui->captureFilename->setText(
@@ -108,6 +112,13 @@ CrashDialog::CrashDialog(PersistantConfig &cfg, QVariantMap crashReportJSON, QWi
ui->capturePreviewFrame->hide();
}
if(forceCapture)
{
ui->captureUpload->setChecked(true);
ui->captureUpload->setEnabled(false);
ui->captureUpload->setText(ui->captureUpload->text() + tr(" (required)"));
}
}
else
{
@@ -238,6 +249,13 @@ CrashDialog::~CrashDialog()
delete ui;
}
bool CrashDialog::HasCaptureReady(PersistantConfig &cfg)
{
QFileInfo capInfo(cfg.CrashReport_LastOpenedCapture);
return capInfo.exists() && capInfo.size() <= MaxUploadSize;
}
void CrashDialog::showEvent(QShowEvent *)
{
adjustSize();
@@ -299,7 +317,8 @@ void CrashDialog::on_send_clicked()
if(result != QMessageBox::Yes)
{
// uncheck and return back so they can confirm
ui->captureUpload->setChecked(false);
if(ui->captureUpload->isEnabled())
ui->captureUpload->setChecked(false);
return;
}
}
+2
View File
@@ -46,6 +46,8 @@ public:
explicit CrashDialog(PersistantConfig &cfg, QVariantMap crashReportJSON, QWidget *parent = 0);
~CrashDialog();
static bool HasCaptureReady(PersistantConfig &cfg);
private slots:
// automatic slots
void on_send_clicked();
+6 -10
View File
@@ -1828,16 +1828,6 @@ void MainWindow::messageCheck()
}
GUIInvoke::call(this, [this, disconnected, msgs] {
// if we just got disconnected while replaying a capture, alert the user.
if(disconnected)
{
RDDialog::critical(this, tr("Remote server disconnected"),
tr("Remote server disconnected during replaying of this capture.\n"
"The replay will now be non-functional. To restore you will have "
"to close the capture, allow "
"RenderDoc to reconnect and load the capture again"));
}
if(m_Ctx.Replay().CurrentRemote().IsValid() &&
!m_Ctx.Replay().CurrentRemote().IsServerRunning())
contextChooser->setIcon(Icons::cross());
@@ -2841,6 +2831,11 @@ void MainWindow::on_action_Resource_Inspector_triggered()
}
void MainWindow::on_action_Send_Error_Report_triggered()
{
sendErrorReport(false);
}
void MainWindow::sendErrorReport(bool forceCaptureInclusion)
{
rdcstr report;
RENDERDOC_CreateBugReport(RENDERDOC_GetLogFile(), "", report);
@@ -2851,6 +2846,7 @@ void MainWindow::on_action_Send_Error_Report_triggered()
json[lit("gitcommit")] = QString::fromLatin1(RENDERDOC_GetCommitHash());
json[lit("replaycrash")] = 1;
json[lit("manual")] = 1;
json[lit("forcecapture")] = forceCaptureInclusion ? 1 : 0;
json[lit("report")] = (QString)report;
CrashDialog crash(m_Ctx.Config(), json, this);
+2
View File
@@ -94,6 +94,8 @@ public:
void LoadInitialLayout();
void sendErrorReport(bool forceCaptureInclusion);
void setProgress(float val);
void setRemoteHost(int hostIdx);
void takeCaptureOwnership() { m_OwnTempCapture = true; }
+1
View File
@@ -414,6 +414,7 @@ struct CaptureContextInvoker : ObjectForwarder<ICaptureContext>
virtual bool IsCaptureLocal() override { return m_Obj.IsCaptureLocal(); }
virtual bool IsCaptureTemporary() override { return m_Obj.IsCaptureTemporary(); }
virtual bool IsCaptureLoading() override { return m_Obj.IsCaptureLoading(); }
virtual ReplayStatus GetFatalError() override { return m_Obj.GetFatalError(); }
virtual rdcstr GetCaptureFilename() override { return m_Obj.GetCaptureFilename(); }
virtual CaptureModifications GetCaptureModifications() override
{
+2
View File
@@ -175,6 +175,8 @@ set(sources
replay/app_api.cpp
replay/basic_types_tests.cpp
replay/capture_options.cpp
replay/dummy_driver.cpp
replay/dummy_driver.h
replay/renderdoc_serialise.inl
replay/capture_file.cpp
replay/entry_points.cpp
+16
View File
@@ -762,6 +762,22 @@ newly generated messages will be returned after that.
)");
virtual rdcarray<DebugMessage> GetDebugMessages() = 0;
DOCUMENT(R"(Poll for the current status of the replay.
This function can be used to monitor to see if a fatal error has been encountered and react
appropriately, such as by displaying a message to the user. The replay controller interface should
remain stable and return null/empty data for the most part, but it's recommended for maximum
stability to stop using the controller when a fatal error is encountered.
If there has been no error, this will return :data:`ReplayStatus.Succeeded`. If there has been an
error this will return the error code every time, it will not be 'consumed' so it's safe to have
multiple things checking it.
:return: The current fatal error status.
:rtype: ReplayStatus
)");
virtual ReplayStatus GetFatalErrorStatus() = 0;
DOCUMENT(R"(Retrieve a list of entry points for a shader.
If the given ID doesn't specify a shader, an empty list will be return. On some APIs, the list will
+3
View File
@@ -91,6 +91,9 @@ rdcstr DoStringise(const ReplayStatus &el)
"Failed to install Android remote server for unknown reasons");
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 GPU out of memory error");
STRINGISE_ENUM_CLASS_NAMED(ReplayDeviceLost, "Encountered a GPU device lost error");
}
END_ENUM_STRINGISE();
}
+15
View File
@@ -3656,6 +3656,18 @@ a remote server.
.. data:: AndroidAPKVerifyFailed
Failed to install Android remote server.
.. data:: RemoteServerConnectionLost
While replaying on a remote server, the connection was lost.
.. data:: ReplayOutOfMemory
While replaying, a GPU out of memory error was encountered.
.. data:: ReplayDeviceLost
While replaying a device lost fatal error was encountered.
)");
enum class ReplayStatus : uint32_t
{
@@ -3684,6 +3696,9 @@ enum class ReplayStatus : uint32_t
AndroidAPKFolderNotFound,
AndroidAPKInstallFailed,
AndroidAPKVerifyFailed,
RemoteServerConnectionLost,
ReplayOutOfMemory,
ReplayDeviceLost,
};
DECLARE_REFLECTION_ENUM(ReplayStatus);
+18 -2
View File
@@ -25,6 +25,7 @@
#include "common/dds_readwrite.h"
#include "core/core.h"
#include "maths/formatpacking.h"
#include "replay/dummy_driver.h"
#include "replay/replay_driver.h"
#include "serialise/rdcfile.h"
#include "stb/stb_image.h"
@@ -72,12 +73,27 @@ public:
virtual ~ImageViewer()
{
m_Proxy->Shutdown();
m_Proxy = NULL;
SAFE_DELETE(m_File);
if(m_Proxy)
{
m_Proxy->Shutdown();
m_Proxy = NULL;
}
}
bool IsRemoteProxy() { return true; }
ReplayStatus FatalErrorCheck()
{
// check for errors on the underlying proxy driver
return m_Proxy->FatalErrorCheck();
}
IReplayDriver *MakeDummyDriver()
{
IReplayDriver *ret = new DummyDriver(this, {});
// lose our structured file reference
m_File = NULL;
return ret;
}
void Shutdown() { delete this; }
// pass through necessary operations to proxy
rdcarray<WindowingSystem> GetSupportedWindowSystems()
+69 -15
View File
@@ -26,6 +26,7 @@
#include "replay_proxy.h"
#include <list>
#include "lz4/lz4.h"
#include "replay/dummy_driver.h"
#include "serialise/lz4io.h"
template <>
@@ -128,25 +129,37 @@ rdcstr DoStringise(const ReplayProxyPacket &el)
// begin serialising a return value. We begin a chunk here in either the writing or reading case
// since this chunk is used purely to send/receive the return value and is fully handled within the
// function.
#define SERIALISE_RETURN(retval) \
{ \
ReturnSerialiser &ser = retser; \
PACKET_HEADER(packet); \
SERIALISE_ELEMENT(retval); \
GET_SERIALISER.Serialise("packet"_lit, packet); \
ser.EndChunk(); \
CheckError(packet, expectedPacket); \
#define SERIALISE_RETURN(retval) \
{ \
ReplayStatus fatalStatus = ReplayStatus::Succeeded; \
if(m_RemoteServer) \
fatalStatus = m_Remote->FatalErrorCheck(); \
ReturnSerialiser &ser = retser; \
PACKET_HEADER(packet); \
SERIALISE_ELEMENT(retval); \
GET_SERIALISER.Serialise("fatalStatus"_lit, fatalStatus); \
GET_SERIALISER.Serialise("packet"_lit, packet); \
ser.EndChunk(); \
if(fatalStatus != ReplayStatus::Succeeded && m_FatalError == ReplayStatus::Succeeded) \
m_FatalError = fatalStatus; \
CheckError(packet, expectedPacket); \
}
// similar to the above, but for void functions that don't return anything. We still want to check
// that both sides of the communication are on the same page.
#define SERIALISE_RETURN_VOID() \
{ \
ReturnSerialiser &ser = retser; \
PACKET_HEADER(packet); \
SERIALISE_ELEMENT(packet); \
ser.EndChunk(); \
CheckError(packet, expectedPacket); \
#define SERIALISE_RETURN_VOID() \
{ \
ReplayStatus fatalStatus = ReplayStatus::Succeeded; \
if(m_RemoteServer) \
fatalStatus = m_Remote->FatalErrorCheck(); \
ReturnSerialiser &ser = retser; \
PACKET_HEADER(packet); \
SERIALISE_ELEMENT(packet); \
GET_SERIALISER.Serialise("fatalStatus"_lit, fatalStatus); \
ser.EndChunk(); \
if(fatalStatus != ReplayStatus::Succeeded && m_FatalError == ReplayStatus::Succeeded) \
m_FatalError = fatalStatus; \
CheckError(packet, expectedPacket); \
}
// defines the area where we're executing on the remote host. To avoid timeouts, the remote side
@@ -2766,8 +2779,49 @@ void ReplayProxy::RemoteExecutionThreadEntry()
}
}
ReplayStatus ReplayProxy::FatalErrorCheck()
{
// this isn't proxied since it's called at relatively high frequency. Whenever we proxy a
// function, we also return the the remote side's status
if(m_IsErrored)
{
// if we're error'd due to a network issue (i.e. the other side crashed and disconnected) we
// won't have a status, set a generic one
if(m_FatalError == ReplayStatus::Succeeded)
m_FatalError = ReplayStatus::RemoteServerConnectionLost;
return m_FatalError;
}
return ReplayStatus::Succeeded;
}
IReplayDriver *ReplayProxy::MakeDummyDriver()
{
// gather up the shaders we've allocated to pass to the dummy driver
rdcarray<ShaderReflection *> shaders;
for(auto it = m_ShaderReflectionCache.begin(); it != m_ShaderReflectionCache.end(); ++it)
shaders.push_back(it->second);
m_ShaderReflectionCache.clear();
IReplayDriver *dummy = new DummyDriver(this, shaders);
// the dummy driver now owns the file, remove our reference
m_StructuredFile = NULL;
return dummy;
}
bool ReplayProxy::CheckError(ReplayProxyPacket receivedPacket, ReplayProxyPacket expectedPacket)
{
if(m_FatalError != ReplayStatus::Succeeded)
{
RDCERR("Fatal error detected while processing %s: %s", ToStr(expectedPacket).c_str(),
ToStr(m_FatalError).c_str());
m_IsErrored = true;
return true;
}
if(m_Writer.IsErrored() || m_Reader.IsErrored() || m_IsErrored)
{
RDCERR("Error during processing of %s", ToStr(expectedPacket).c_str());
+5
View File
@@ -103,6 +103,8 @@ enum ReplayProxyPacket
eReplayProxy_ContinueDebug,
eReplayProxy_FreeDebugger,
eReplayProxy_FatalErrorCheck,
};
DECLARE_REFLECTION_ENUM(ReplayProxyPacket);
@@ -140,6 +142,8 @@ public:
void RemoteExecutionThreadEntry();
bool IsRemoteProxy() { return !m_RemoteServer; }
ReplayStatus FatalErrorCheck();
IReplayDriver *MakeDummyDriver();
void Shutdown() { delete this; }
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
@@ -694,6 +698,7 @@ private:
Threading::ThreadHandle m_RemoteExecutionThread = 0;
bool m_IsErrored = false;
ReplayStatus m_FatalError = ReplayStatus::Succeeded;
FrameRecord m_FrameRecord;
APIProperties m_APIProps;
+3
View File
@@ -601,6 +601,7 @@ private:
SDFile *m_StoredStructuredData;
rdcarray<DebugMessage> m_DebugMessages;
ReplayStatus m_FatalError = ReplayStatus::Succeeded;
rdcarray<FrameDescription> m_CapturedFrames;
rdcarray<ActionDescription *> m_Actions;
@@ -660,10 +661,12 @@ public:
void UnlockForChunkRemoval();
SDFile *GetStructuredFile() { return m_StructuredFile; }
void DetachStructuredFile() { m_StoredStructuredData = m_StructuredFile = NULL; }
uint64_t GetTimeBase() { return m_TimeBase; }
double GetTimeFrequency() { return m_TimeFrequency; }
void FirstFrame(IDXGISwapper *swapper);
ReplayStatus FatalErrorCheck() { return m_FatalError; }
rdcarray<DebugMessage> GetDebugMessages();
void AddDebugMessage(DebugMessage msg);
void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d);
+22
View File
@@ -32,6 +32,7 @@
#include "maths/camera.h"
#include "maths/formatpacking.h"
#include "maths/matrix.h"
#include "replay/dummy_driver.h"
#include "serialise/rdcfile.h"
#include "strings/string_utils.h"
#include "d3d11_context.h"
@@ -77,10 +78,31 @@ void D3D11Replay::Shutdown()
m_ProxyResources[i]->Release();
m_ProxyResources.clear();
m_RealState.state.Clear();
// explicitly delete the device, as all the replay resources created will be keeping refs on it
delete m_pDevice;
}
ReplayStatus D3D11Replay::FatalErrorCheck()
{
return m_pDevice->FatalErrorCheck();
}
IReplayDriver *D3D11Replay::MakeDummyDriver()
{
// gather up the shaders we've allocated to pass to the dummy driver
rdcarray<ShaderReflection *> shaders;
WrappedID3D11Shader<ID3D11ComputeShader>::GetReflections(shaders);
IReplayDriver *dummy = new DummyDriver(this, shaders);
// the dummy driver now owns the file, remove our reference
m_pDevice->DetachStructuredFile();
return dummy;
}
void D3D11Replay::CreateResources(IDXGIFactory *factory)
{
bool wrapped =
+3
View File
@@ -109,6 +109,9 @@ public:
bool IsRemoteProxy() { return m_Proxy; }
void Shutdown();
ReplayStatus FatalErrorCheck();
IReplayDriver *MakeDummyDriver();
void CreateResources(IDXGIFactory *factory);
void DestroyResources();
+13
View File
@@ -1041,6 +1041,8 @@ public:
DXBC::DXBCContainer *m_DXBCFile;
ShaderReflection *m_Details;
ShaderBindpointMapping m_Mapping;
friend class WrappedShader;
};
static std::map<ResourceId, ShaderEntry *> m_ShaderList;
@@ -1094,6 +1096,17 @@ public:
return m_ShaderList[m_ID]->GetMapping();
}
static void GetReflections(rdcarray<ShaderReflection *> &refls)
{
SCOPED_LOCK(m_ShaderListLock);
refls.clear();
for(auto it = m_ShaderList.begin(); it != m_ShaderList.end(); ++it)
{
refls.push_back(it->second->m_Details);
it->second->m_Details = NULL;
}
}
private:
ResourceId m_ID;
};
+3 -1
View File
@@ -667,6 +667,7 @@ private:
rdcarray<TempMem *> m_ThreadTempMem;
rdcarray<DebugMessage> m_DebugMessages;
ReplayStatus m_FatalError = ReplayStatus::Succeeded;
uint64_t m_TimeBase = 0;
double m_TimeFrequency = 1.0f;
@@ -799,7 +800,7 @@ public:
void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d);
void AddDebugMessage(const DebugMessage &msg);
rdcarray<DebugMessage> GetDebugMessages();
ReplayStatus FatalErrorCheck() { return m_FatalError; }
ResourceDescription &GetResourceDesc(ResourceId id);
void AddResource(ResourceId id, ResourceType type, const char *defaultNamePrefix);
void DerivedResource(ResourceId parent, ResourceId child);
@@ -926,6 +927,7 @@ public:
m_State = CaptureState::StructuredExport;
}
SDFile *GetStructuredFile() { return m_StructuredFile; }
void DetachStructuredFile() { m_StoredStructuredData = m_StructuredFile = NULL; }
uint64_t GetTimeBase() { return m_TimeBase; }
double GetTimeFrequency() { return m_TimeFrequency; }
// interface for DXGI
+20
View File
@@ -32,6 +32,7 @@
#include "maths/camera.h"
#include "maths/formatpacking.h"
#include "maths/matrix.h"
#include "replay/dummy_driver.h"
#include "serialise/rdcfile.h"
#include "strings/string_utils.h"
#include "d3d12_command_queue.h"
@@ -114,6 +115,25 @@ void D3D12Replay::Initialise(IDXGIFactory1 *factory)
}
}
ReplayStatus D3D12Replay::FatalErrorCheck()
{
return m_pDevice->FatalErrorCheck();
}
IReplayDriver *D3D12Replay::MakeDummyDriver()
{
// gather up the shaders we've allocated to pass to the dummy driver
rdcarray<ShaderReflection *> shaders;
WrappedID3D12Shader::GetReflections(shaders);
IReplayDriver *dummy = new DummyDriver(this, shaders);
// the dummy driver now owns the file, remove our reference
m_pDevice->DetachStructuredFile();
return dummy;
}
void D3D12Replay::CreateResources()
{
m_DebugManager = new D3D12DebugManager(m_pDevice);
+3
View File
@@ -84,6 +84,9 @@ public:
void Initialise(IDXGIFactory1 *factory);
void Shutdown();
ReplayStatus FatalErrorCheck();
IReplayDriver *MakeDummyDriver();
void CreateResources();
void DestroyResources();
DriverInformation GetDriverInfo() { return m_DriverInfo; }
+10
View File
@@ -744,6 +744,16 @@ public:
return false;
}
static void GetReflections(rdcarray<ShaderReflection *> &refls)
{
refls.clear();
for(auto it = m_Shaders.begin(); it != m_Shaders.end(); ++it)
{
refls.push_back(it->second->m_Details);
it->second->m_Details = NULL;
}
}
void GetShaderExtSlot(uint32_t &slot, uint32_t &space)
{
slot = m_ShaderExtSlot;
+3 -1
View File
@@ -111,11 +111,12 @@ private:
GLPlatform &m_Platform;
ReplayStatus m_FatalError = ReplayStatus::Succeeded;
rdcarray<DebugMessage> m_DebugMessages;
template <typename SerialiserType>
void Serialise_DebugMessages(SerialiserType &ser);
rdcarray<DebugMessage> GetDebugMessages();
ReplayStatus FatalErrorCheck() { return m_FatalError; }
rdcstr m_DebugMsgContext;
bool m_SuppressDebugMessages;
@@ -633,6 +634,7 @@ public:
m_State = CaptureState::StructuredExport;
}
SDFile *GetStructuredFile() { return m_StructuredFile; }
void DetachStructuredFile() { m_StoredStructuredData = m_StructuredFile = NULL; }
void SetFetchCounters(bool in) { m_FetchCounters = in; };
void SetDebugMsgContext(const rdcstr &context) { m_DebugMsgContext = context; }
void AddDebugMessage(DebugMessage msg)
+24
View File
@@ -29,6 +29,7 @@
#include "driver/ihv/arm/arm_counters.h"
#include "driver/ihv/intel/intel_gl_counters.h"
#include "maths/matrix.h"
#include "replay/dummy_driver.h"
#include "serialise/rdcfile.h"
#include "strings/string_utils.h"
#include "gl_driver.h"
@@ -86,6 +87,29 @@ void GLReplay::Shutdown()
delete m_pDriver;
}
ReplayStatus GLReplay::FatalErrorCheck()
{
return m_pDriver->FatalErrorCheck();
}
IReplayDriver *GLReplay::MakeDummyDriver()
{
// gather up the shaders we've allocated to pass to the dummy driver
rdcarray<ShaderReflection *> shaders;
for(auto it = m_pDriver->m_Shaders.begin(); it != m_pDriver->m_Shaders.end(); it++)
{
shaders.push_back(it->second.reflection);
it->second.reflection = NULL;
}
IReplayDriver *dummy = new DummyDriver(this, shaders);
// the dummy driver now owns the file, remove our reference
m_pDriver->DetachStructuredFile();
return dummy;
}
ReplayStatus GLReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
MakeCurrentReplayContext(&m_ReplayCtx);
+3
View File
@@ -115,6 +115,9 @@ public:
bool IsRemoteProxy() { return m_Proxy; }
void Shutdown();
ReplayStatus FatalErrorCheck();
IReplayDriver *MakeDummyDriver();
DriverInformation GetDriverInfo() { return m_DriverInfo; }
rdcarray<GPUDevice> GetAvailableGPUs();
APIProperties GetAPIProperties();
+3 -1
View File
@@ -286,10 +286,11 @@ private:
void Serialise_DebugMessages(SerialiserType &ser);
void ProcessDebugMessage(DebugMessage &DebugMessages);
rdcarray<DebugMessage> GetDebugMessages();
void AddDebugMessage(DebugMessage msg);
ReplayStatus m_FatalError = ReplayStatus::Succeeded;
ReplayStatus FatalErrorCheck() { return m_FatalError; }
CaptureState m_State;
bool m_AppControlledCapture = false;
@@ -1040,6 +1041,7 @@ public:
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
SDFile *GetStructuredFile() { return m_StructuredFile; }
void DetachStructuredFile() { m_StoredStructuredData = m_StructuredFile = NULL; }
const APIEvent &GetEvent(uint32_t eventId);
uint32_t GetMaxEID() { return m_Events.back().eventId; }
const ActionDescription *GetAction(uint32_t eventId);
+29
View File
@@ -32,6 +32,7 @@
#include "driver/shaders/spirv/spirv_compile.h"
#include "maths/formatpacking.h"
#include "maths/matrix.h"
#include "replay/dummy_driver.h"
#include "serialise/rdcfile.h"
#include "strings/string_utils.h"
#include "vk_core.h"
@@ -83,6 +84,34 @@ void VulkanReplay::Shutdown()
delete m_pDriver;
}
ReplayStatus VulkanReplay::FatalErrorCheck()
{
return m_pDriver->FatalErrorCheck();
}
IReplayDriver *VulkanReplay::MakeDummyDriver()
{
// gather up the shaders we've allocated to pass to the dummy driver
rdcarray<ShaderReflection *> shaders;
for(auto it = m_pDriver->m_CreationInfo.m_ShaderModule.begin();
it != m_pDriver->m_CreationInfo.m_ShaderModule.end(); it++)
{
for(auto reflit = it->second.m_Reflections.begin(); reflit != it->second.m_Reflections.end();
++reflit)
{
shaders.push_back(reflit->second.refl);
reflit->second.refl = NULL;
}
}
IReplayDriver *dummy = new DummyDriver(this, shaders);
// the dummy driver now owns the file, remove our reference
m_pDriver->DetachStructuredFile();
return dummy;
}
rdcarray<GPUDevice> VulkanReplay::GetAvailableGPUs()
{
rdcarray<GPUDevice> ret;
+3
View File
@@ -267,6 +267,9 @@ public:
bool IsRemoteProxy() { return m_Proxy; }
void Shutdown();
ReplayStatus FatalErrorCheck();
IReplayDriver *MakeDummyDriver();
void CreateResources();
void DestroyResources();
+2
View File
@@ -242,6 +242,7 @@
</ClInclude>
<ClInclude Include="os\win32\dia2_stubs.h" />
<ClInclude Include="os\win32\win32_specific.h" />
<ClInclude Include="replay\dummy_driver.h" />
<ClInclude Include="replay\replay_driver.h" />
<ClInclude Include="replay\replay_controller.h" />
<ClInclude Include="serialise\codecs\vk_cpp_codec_common.h" />
@@ -656,6 +657,7 @@
<ClCompile Include="replay\basic_types_tests.cpp" />
<ClCompile Include="replay\capture_file.cpp" />
<ClCompile Include="replay\capture_options.cpp" />
<ClCompile Include="replay\dummy_driver.cpp" />
<ClCompile Include="replay\entry_points.cpp" />
<ClCompile Include="replay\replay_driver.cpp" />
<ClCompile Include="replay\replay_output.cpp" />
+6
View File
@@ -570,6 +570,9 @@
<ClInclude Include="3rdparty\md5\md5.h">
<Filter>3rdparty\md5</Filter>
</ClInclude>
<ClInclude Include="replay\dummy_driver.h">
<Filter>Replay</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="maths\camera.cpp">
@@ -986,6 +989,9 @@
<ClCompile Include="3rdparty\md5\md5.c">
<Filter>3rdparty\md5</Filter>
</ClCompile>
<ClCompile Include="replay\dummy_driver.cpp">
<Filter>Replay</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="os\win32\comexport.def">
+505
View File
@@ -0,0 +1,505 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2021 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "dummy_driver.h"
DummyDriver::DummyDriver(IReplayDriver *original, const rdcarray<ShaderReflection *> &shaders)
{
m_Shaders = shaders;
m_Props = original->GetAPIProperties();
m_Resources = original->GetResources();
m_Buffers = original->GetBuffers();
m_Textures = original->GetTextures();
m_FrameRecord = original->GetFrameRecord();
m_SDFile = original->GetStructuredFile();
m_TargetEncodings = original->GetTargetShaderEncodings();
m_DriverInfo = original->GetDriverInfo();
m_Proxy = original->IsRemoteProxy();
m_GPUs = original->GetAvailableGPUs();
m_WindowSystems = original->GetSupportedWindowSystems();
m_CustomEncodings = original->GetCustomShaderEncodings();
}
DummyDriver::~DummyDriver()
{
// we own the shaders
for(ShaderReflection *refl : m_Shaders)
delete refl;
// and we own the structured file
delete m_SDFile;
}
void DummyDriver::Shutdown()
{
delete this;
}
APIProperties DummyDriver::GetAPIProperties()
{
return m_Props;
}
rdcarray<ResourceDescription> DummyDriver::GetResources()
{
return m_Resources;
}
rdcarray<BufferDescription> DummyDriver::GetBuffers()
{
return m_Buffers;
}
BufferDescription DummyDriver::GetBuffer(ResourceId id)
{
for(const BufferDescription &buf : m_Buffers)
{
if(buf.resourceId == id)
return buf;
}
return {};
}
rdcarray<TextureDescription> DummyDriver::GetTextures()
{
return m_Textures;
}
TextureDescription DummyDriver::GetTexture(ResourceId id)
{
for(const TextureDescription &tex : m_Textures)
{
if(tex.resourceId == id)
return tex;
}
return {};
}
rdcarray<DebugMessage> DummyDriver::GetDebugMessages()
{
return {};
}
rdcarray<ShaderEntryPoint> DummyDriver::GetShaderEntryPoints(ResourceId shader)
{
return {{"main", ShaderStage::Vertex}};
}
ShaderReflection *DummyDriver::GetShader(ResourceId pipeline, ResourceId shader,
ShaderEntryPoint entry)
{
return NULL;
}
rdcarray<rdcstr> DummyDriver::GetDisassemblyTargets(bool withPipeline)
{
return {"Disassembly"};
}
rdcstr DummyDriver::DisassembleShader(ResourceId pipeline, const ShaderReflection *refl,
const rdcstr &target)
{
return "; No disassembly available due to unrecoverable error analysing capture.";
}
rdcarray<EventUsage> DummyDriver::GetUsage(ResourceId id)
{
return {};
}
void DummyDriver::SetPipelineStates(D3D11Pipe::State *d3d11, D3D12Pipe::State *d3d12,
GLPipe::State *gl, VKPipe::State *vk)
{
}
void DummyDriver::SavePipelineState(uint32_t eventId)
{
}
FrameRecord DummyDriver::GetFrameRecord()
{
return m_FrameRecord;
}
ReplayStatus DummyDriver::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers)
{
return ReplayStatus::APIReplayFailed;
}
void DummyDriver::ReplayLog(uint32_t endEventID, ReplayLogType replayType)
{
}
SDFile *DummyDriver::GetStructuredFile()
{
return m_SDFile;
}
rdcarray<uint32_t> DummyDriver::GetPassEvents(uint32_t eventId)
{
return {eventId};
}
void DummyDriver::InitPostVSBuffers(uint32_t eventId)
{
}
void DummyDriver::InitPostVSBuffers(const rdcarray<uint32_t> &passEvents)
{
}
ResourceId DummyDriver::GetLiveID(ResourceId id)
{
return id;
}
MeshFormat DummyDriver::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID,
MeshDataStage stage)
{
return {};
}
void DummyDriver::GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData)
{
retData.clear();
}
void DummyDriver::GetTextureData(ResourceId tex, const Subresource &sub,
const GetTextureDataParams &params, bytebuf &data)
{
data.clear();
}
void DummyDriver::BuildTargetShader(ShaderEncoding sourceEncoding, const bytebuf &source,
const rdcstr &entry, const ShaderCompileFlags &compileFlags,
ShaderStage type, ResourceId &id, rdcstr &errors)
{
id = ResourceId();
errors = "Unrecoverable error encountered while analysing capture";
}
rdcarray<ShaderEncoding> DummyDriver::GetTargetShaderEncodings()
{
return {ShaderEncoding::HLSL, ShaderEncoding::GLSL};
}
void DummyDriver::ReplaceResource(ResourceId from, ResourceId to)
{
}
void DummyDriver::RemoveReplacement(ResourceId id)
{
}
void DummyDriver::FreeTargetResource(ResourceId id)
{
}
rdcarray<GPUCounter> DummyDriver::EnumerateCounters()
{
return {};
}
CounterDescription DummyDriver::DescribeCounter(GPUCounter counterID)
{
return {};
}
rdcarray<CounterResult> DummyDriver::FetchCounters(const rdcarray<GPUCounter> &counterID)
{
return {};
}
void DummyDriver::FillCBufferVariables(ResourceId pipeline, ResourceId shader, rdcstr entryPoint,
uint32_t cbufSlot, rdcarray<ShaderVariable> &outvars,
const bytebuf &data)
{
outvars.clear();
}
rdcarray<PixelModification> DummyDriver::PixelHistory(rdcarray<EventUsage> events,
ResourceId target, uint32_t x, uint32_t y,
const Subresource &sub, CompType typeCast)
{
return {};
}
ShaderDebugTrace *DummyDriver::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid,
uint32_t idx, uint32_t view)
{
return new ShaderDebugTrace;
}
ShaderDebugTrace *DummyDriver::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample,
uint32_t primitive)
{
return new ShaderDebugTrace;
}
ShaderDebugTrace *DummyDriver::DebugThread(uint32_t eventId,
const rdcfixedarray<uint32_t, 3> &groupid,
const rdcfixedarray<uint32_t, 3> &threadid)
{
return new ShaderDebugTrace;
}
rdcarray<ShaderDebugState> DummyDriver::ContinueDebug(ShaderDebugger *debugger)
{
return {};
}
void DummyDriver::FreeDebugger(ShaderDebugger *debugger)
{
}
ResourceId DummyDriver::RenderOverlay(ResourceId texid, FloatVector clearCol, DebugOverlay overlay,
uint32_t eventId, const rdcarray<uint32_t> &passEvents)
{
return ResourceId();
}
bool DummyDriver::IsRenderOutput(ResourceId id)
{
return false;
}
void DummyDriver::FileChanged()
{
}
bool DummyDriver::NeedRemapForFetch(const ResourceFormat &format)
{
return false;
}
DriverInformation DummyDriver::GetDriverInfo()
{
return m_DriverInfo;
}
rdcarray<GPUDevice> DummyDriver::GetAvailableGPUs()
{
return m_GPUs;
}
bool DummyDriver::IsRemoteProxy()
{
return m_Proxy;
}
ReplayStatus DummyDriver::FatalErrorCheck()
{
return ReplayStatus::Succeeded;
}
IReplayDriver *DummyDriver::MakeDummyDriver()
{
return NULL;
}
rdcarray<WindowingSystem> DummyDriver::GetSupportedWindowSystems()
{
rdcarray<WindowingSystem> ret;
#if ENABLED(RDOC_LINUX)
#if ENABLED(RDOC_XLIB)
ret.push_back(WindowingSystem::Xlib);
#endif
#if ENABLED(RDOC_XCB)
ret.push_back(WindowingSystem::XCB);
#endif
#if ENABLED(RDOC_WAYLAND)
ret.push_back(WindowingSystem::Wayland);
#endif
#elif ENABLED(RDOC_WIN32)
ret.push_back(WindowingSystem::Win32);
#elif ENABLED(RDOC_ANDROID)
ret.push_back(WindowingSystem::Android);
#elif ENABLED(RDOC_APPLE)
ret.push_back(WindowingSystem::MacOS);
#endif
return ret;
}
AMDRGPControl *DummyDriver::GetRGPControl()
{
return NULL;
}
uint64_t DummyDriver::MakeOutputWindow(WindowingData window, bool depth)
{
return 1;
}
void DummyDriver::DestroyOutputWindow(uint64_t id)
{
}
bool DummyDriver::CheckResizeOutputWindow(uint64_t id)
{
return false;
}
void DummyDriver::SetOutputWindowDimensions(uint64_t id, int32_t w, int32_t h)
{
}
void DummyDriver::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h)
{
}
void DummyDriver::GetOutputWindowData(uint64_t id, bytebuf &retData)
{
}
void DummyDriver::ClearOutputWindowColor(uint64_t id, FloatVector col)
{
}
void DummyDriver::ClearOutputWindowDepth(uint64_t id, float depth, uint8_t stencil)
{
}
void DummyDriver::BindOutputWindow(uint64_t id, bool depth)
{
}
bool DummyDriver::IsOutputWindowVisible(uint64_t id)
{
return true;
}
void DummyDriver::FlipOutputWindow(uint64_t id)
{
}
bool DummyDriver::GetMinMax(ResourceId texid, const Subresource &sub, CompType typeCast,
float *minval, float *maxval)
{
*minval = 0.0f;
*maxval = 1.0f;
return false;
}
bool DummyDriver::GetHistogram(ResourceId texid, const Subresource &sub, CompType typeCast,
float minval, float maxval, const rdcfixedarray<bool, 4> &channels,
rdcarray<uint32_t> &histogram)
{
histogram.fill(256, 0);
return false;
}
void DummyDriver::PickPixel(ResourceId texture, uint32_t x, uint32_t y, const Subresource &sub,
CompType typeCast, float pixel[4])
{
}
ResourceId DummyDriver::CreateProxyTexture(const TextureDescription &templateTex)
{
return ResourceId();
}
void DummyDriver::SetProxyTextureData(ResourceId texid, const Subresource &sub, byte *data,
size_t dataSize)
{
}
bool DummyDriver::IsTextureSupported(const TextureDescription &tex)
{
return true;
}
ResourceId DummyDriver::CreateProxyBuffer(const BufferDescription &templateBuf)
{
return ResourceId();
}
void DummyDriver::SetProxyBufferData(ResourceId bufid, byte *data, size_t dataSize)
{
}
void DummyDriver::RenderMesh(uint32_t eventId, const rdcarray<MeshFormat> &secondaryDraws,
const MeshDisplay &cfg)
{
}
bool DummyDriver::RenderTexture(TextureDisplay cfg)
{
return false;
}
void DummyDriver::SetCustomShaderIncludes(const rdcarray<rdcstr> &directories)
{
}
void DummyDriver::BuildCustomShader(ShaderEncoding sourceEncoding, const bytebuf &source,
const rdcstr &entry, const ShaderCompileFlags &compileFlags,
ShaderStage type, ResourceId &id, rdcstr &errors)
{
id = ResourceId();
errors = "Unrecoverable error encountered while analysing capture";
}
rdcarray<ShaderEncoding> DummyDriver::GetCustomShaderEncodings()
{
return {ShaderEncoding::HLSL, ShaderEncoding::GLSL};
}
ResourceId DummyDriver::ApplyCustomShader(ResourceId shader, ResourceId texid,
const Subresource &sub, CompType typeCast)
{
return ResourceId();
}
void DummyDriver::FreeCustomShader(ResourceId id)
{
}
void DummyDriver::RenderCheckerboard(FloatVector dark, FloatVector light)
{
}
void DummyDriver::RenderHighlightBox(float w, float h, float scale)
{
}
uint32_t DummyDriver::PickVertex(uint32_t eventId, int32_t width, int32_t height,
const MeshDisplay &cfg, uint32_t x, uint32_t y)
{
return ~0U;
}
+199
View File
@@ -0,0 +1,199 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2021 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include "replay_driver.h"
// this is a dummy driver that can be swapped to if the underlying replay/remote driver encounters
// some fatal problem and shouldn't be used further. It saves significantly on needing to
// bullet-proof each driver since instead we just need to make error paths clear and short, and rely
// on the replay controller to detect errors and swap to a dummy driver immediately
class DummyDriver : public IReplayDriver
{
public:
DummyDriver(IReplayDriver *original, const rdcarray<ShaderReflection *> &shaders);
void Shutdown();
APIProperties GetAPIProperties();
rdcarray<ResourceDescription> GetResources();
rdcarray<BufferDescription> GetBuffers();
BufferDescription GetBuffer(ResourceId id);
rdcarray<TextureDescription> GetTextures();
TextureDescription GetTexture(ResourceId id);
rdcarray<DebugMessage> GetDebugMessages();
rdcarray<ShaderEntryPoint> GetShaderEntryPoints(ResourceId shader);
ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, ShaderEntryPoint entry);
rdcarray<rdcstr> GetDisassemblyTargets(bool withPipeline);
rdcstr DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, const rdcstr &target);
rdcarray<EventUsage> GetUsage(ResourceId id);
void SetPipelineStates(D3D11Pipe::State *d3d11, D3D12Pipe::State *d3d12, GLPipe::State *gl,
VKPipe::State *vk);
void SavePipelineState(uint32_t eventId);
FrameRecord GetFrameRecord();
ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers);
void ReplayLog(uint32_t endEventID, ReplayLogType replayType);
SDFile *GetStructuredFile();
rdcarray<uint32_t> GetPassEvents(uint32_t eventId);
void InitPostVSBuffers(uint32_t eventId);
void InitPostVSBuffers(const rdcarray<uint32_t> &passEvents);
ResourceId GetLiveID(ResourceId id);
MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID,
MeshDataStage stage);
void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData);
void GetTextureData(ResourceId tex, const Subresource &sub, const GetTextureDataParams &params,
bytebuf &data);
void BuildTargetShader(ShaderEncoding sourceEncoding, const bytebuf &source, const rdcstr &entry,
const ShaderCompileFlags &compileFlags, ShaderStage type, ResourceId &id,
rdcstr &errors);
rdcarray<ShaderEncoding> GetTargetShaderEncodings();
void ReplaceResource(ResourceId from, ResourceId to);
void RemoveReplacement(ResourceId id);
void FreeTargetResource(ResourceId id);
rdcarray<GPUCounter> EnumerateCounters();
CounterDescription DescribeCounter(GPUCounter counterID);
rdcarray<CounterResult> FetchCounters(const rdcarray<GPUCounter> &counterID);
void FillCBufferVariables(ResourceId pipeline, ResourceId shader, rdcstr entryPoint,
uint32_t cbufSlot, rdcarray<ShaderVariable> &outvars,
const bytebuf &data);
rdcarray<PixelModification> PixelHistory(rdcarray<EventUsage> events, ResourceId target, uint32_t x,
uint32_t y, const Subresource &sub, CompType typeCast);
ShaderDebugTrace *DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx,
uint32_t view);
ShaderDebugTrace *DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample,
uint32_t primitive);
ShaderDebugTrace *DebugThread(uint32_t eventId, const rdcfixedarray<uint32_t, 3> &groupid,
const rdcfixedarray<uint32_t, 3> &threadid);
rdcarray<ShaderDebugState> ContinueDebug(ShaderDebugger *debugger);
void FreeDebugger(ShaderDebugger *debugger);
ResourceId RenderOverlay(ResourceId texid, FloatVector clearCol, DebugOverlay overlay,
uint32_t eventId, const rdcarray<uint32_t> &passEvents);
bool IsRenderOutput(ResourceId id);
void FileChanged();
bool NeedRemapForFetch(const ResourceFormat &format);
DriverInformation GetDriverInfo();
rdcarray<GPUDevice> GetAvailableGPUs();
// IReplayDriver
bool IsRemoteProxy();
ReplayStatus FatalErrorCheck();
IReplayDriver *MakeDummyDriver();
rdcarray<WindowingSystem> GetSupportedWindowSystems();
AMDRGPControl *GetRGPControl();
uint64_t MakeOutputWindow(WindowingData window, bool depth);
void DestroyOutputWindow(uint64_t id);
bool CheckResizeOutputWindow(uint64_t id);
void SetOutputWindowDimensions(uint64_t id, int32_t w, int32_t h);
void GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h);
void GetOutputWindowData(uint64_t id, bytebuf &retData);
void ClearOutputWindowColor(uint64_t id, FloatVector col);
void ClearOutputWindowDepth(uint64_t id, float depth, uint8_t stencil);
void BindOutputWindow(uint64_t id, bool depth);
bool IsOutputWindowVisible(uint64_t id);
void FlipOutputWindow(uint64_t id);
bool GetMinMax(ResourceId texid, const Subresource &sub, CompType typeCast, float *minval,
float *maxval);
bool GetHistogram(ResourceId texid, const Subresource &sub, CompType typeCast, float minval,
float maxval, const rdcfixedarray<bool, 4> &channels,
rdcarray<uint32_t> &histogram);
void PickPixel(ResourceId texture, uint32_t x, uint32_t y, const Subresource &sub,
CompType typeCast, float pixel[4]);
ResourceId CreateProxyTexture(const TextureDescription &templateTex);
void SetProxyTextureData(ResourceId texid, const Subresource &sub, byte *data, size_t dataSize);
bool IsTextureSupported(const TextureDescription &tex);
ResourceId CreateProxyBuffer(const BufferDescription &templateBuf);
void SetProxyBufferData(ResourceId bufid, byte *data, size_t dataSize);
void RenderMesh(uint32_t eventId, const rdcarray<MeshFormat> &secondaryDraws,
const MeshDisplay &cfg);
bool RenderTexture(TextureDisplay cfg);
void SetCustomShaderIncludes(const rdcarray<rdcstr> &directories);
void BuildCustomShader(ShaderEncoding sourceEncoding, const bytebuf &source, const rdcstr &entry,
const ShaderCompileFlags &compileFlags, ShaderStage type, ResourceId &id,
rdcstr &errors);
rdcarray<ShaderEncoding> GetCustomShaderEncodings();
ResourceId ApplyCustomShader(ResourceId shader, ResourceId texid, const Subresource &sub,
CompType typeCast);
void FreeCustomShader(ResourceId id);
void RenderCheckerboard(FloatVector dark, FloatVector light);
void RenderHighlightBox(float w, float h, float scale);
uint32_t PickVertex(uint32_t eventId, int32_t width, int32_t height, const MeshDisplay &cfg,
uint32_t x, uint32_t y);
private:
virtual ~DummyDriver();
rdcarray<ShaderReflection *> m_Shaders;
SDFile *m_SDFile;
APIProperties m_Props;
rdcarray<ResourceDescription> m_Resources;
rdcarray<BufferDescription> m_Buffers;
rdcarray<TextureDescription> m_Textures;
FrameRecord m_FrameRecord;
rdcarray<ShaderEncoding> m_TargetEncodings;
DriverInformation m_DriverInfo;
bool m_Proxy;
rdcarray<GPUDevice> m_GPUs;
rdcarray<WindowingSystem> m_WindowSystems;
rdcarray<ShaderEncoding> m_CustomEncodings;
};
+96 -6
View File
@@ -77,11 +77,13 @@ void ReplayController::SetFrameEvent(uint32_t eventId, bool force)
m_EventID = eventId;
m_pDevice->ReplayLog(eventId, eReplay_WithoutDraw);
FatalErrorCheck();
for(size_t i = 0; i < m_Outputs.size(); i++)
m_Outputs[i]->SetFrameEvent(eventId);
m_pDevice->ReplayLog(eventId, eReplay_OnlyDraw);
FatalErrorCheck();
FetchPipelineState(eventId);
}
@@ -154,7 +156,9 @@ rdcstr ReplayController::DisassembleShader(ResourceId pipeline, const ShaderRefl
if(t == target)
return GCNISA::Disassemble(refl->encoding, refl->stage, refl->rawBytes, target);
return m_pDevice->DisassembleShader(m_pDevice->GetLiveID(pipeline), refl, target);
rdcstr ret = m_pDevice->DisassembleShader(m_pDevice->GetLiveID(pipeline), refl, target);
FatalErrorCheck();
return ret;
}
FrameDescription ReplayController::GetFrameInfo()
@@ -414,7 +418,9 @@ rdcarray<CounterResult> ReplayController::FetchCounters(const rdcarray<GPUCounte
RENDERDOC_PROFILEFUNCTION();
return m_pDevice->FetchCounters(counters);
rdcarray<CounterResult> ret = m_pDevice->FetchCounters(counters);
FatalErrorCheck();
return ret;
}
rdcarray<GPUCounter> ReplayController::EnumerateCounters()
@@ -490,17 +496,18 @@ MeshFormat ReplayController::GetPostVSData(uint32_t instID, uint32_t viewID, Mes
ActionDescription *action = GetActionByEID(m_EventID);
MeshFormat ret;
RDCEraseEl(ret);
if(action == NULL || !(action->flags & ActionFlags::Drawcall))
return MeshFormat();
instID = RDCMIN(instID, action->numInstances - 1);
m_pDevice->InitPostVSBuffers(action->eventId);
FatalErrorCheck();
return m_pDevice->GetPostVSBuffers(action->eventId, instID, viewID, stage);
MeshFormat ret = m_pDevice->GetPostVSBuffers(action->eventId, instID, viewID, stage);
FatalErrorCheck();
return ret;
}
bytebuf ReplayController::GetBufferData(ResourceId buff, uint64_t offset, uint64_t len)
@@ -521,6 +528,7 @@ bytebuf ReplayController::GetBufferData(ResourceId buff, uint64_t offset, uint64
}
m_pDevice->GetBufferData(liveId, offset, len, retData);
FatalErrorCheck();
return retData;
}
@@ -541,6 +549,7 @@ bytebuf ReplayController::GetTextureData(ResourceId tex, const Subresource &sub)
}
m_pDevice->GetTextureData(liveId, sub, GetTextureDataParams(), ret);
FatalErrorCheck();
return ret;
}
@@ -856,6 +865,7 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const rdcstr &pa
bytebuf data;
m_pDevice->GetTextureData(liveid, sub, params, data);
FatalErrorCheck();
if(data.empty())
{
@@ -1530,6 +1540,7 @@ rdcarray<PixelModification> ReplayController::PixelHistory(ResourceId target, ui
return ret;
ret = m_pDevice->PixelHistory(events, id, x, y, subresource, typeCast);
FatalErrorCheck();
SetFrameEvent(m_EventID, true);
@@ -1550,6 +1561,7 @@ PixelValue ReplayController::PickPixel(ResourceId tex, uint32_t x, uint32_t y,
return ret;
m_pDevice->PickPixel(m_pDevice->GetLiveID(tex), x, y, sub, typeCast, ret.floatValue.data());
FatalErrorCheck();
return ret;
}
@@ -1564,6 +1576,7 @@ rdcpair<PixelValue, PixelValue> ReplayController::GetMinMax(ResourceId textureId
m_pDevice->GetMinMax(m_pDevice->GetLiveID(textureId), sub, typeCast, &minval.floatValue[0],
&maxval.floatValue[0]);
FatalErrorCheck();
return make_rdcpair(minval, maxval);
}
@@ -1578,6 +1591,7 @@ rdcarray<uint32_t> ReplayController::GetHistogram(ResourceId textureId, const Su
m_pDevice->GetHistogram(m_pDevice->GetLiveID(textureId), sub, typeCast, minval, maxval, channels,
hist);
FatalErrorCheck();
return hist;
}
@@ -1590,9 +1604,13 @@ ShaderDebugTrace *ReplayController::DebugVertex(uint32_t vertid, uint32_t instid
RENDERDOC_PROFILEFUNCTION();
ShaderDebugTrace *ret = m_pDevice->DebugVertex(m_EventID, vertid, instid, idx, view);
FatalErrorCheck();
SetFrameEvent(m_EventID, true);
if(ret->debugger)
m_Debuggers.push_back(ret->debugger);
return ret;
}
@@ -1604,9 +1622,13 @@ ShaderDebugTrace *ReplayController::DebugPixel(uint32_t x, uint32_t y, uint32_t
RENDERDOC_PROFILEFUNCTION();
ShaderDebugTrace *ret = m_pDevice->DebugPixel(m_EventID, x, y, sample, primitive);
FatalErrorCheck();
SetFrameEvent(m_EventID, true);
if(ret->debugger)
m_Debuggers.push_back(ret->debugger);
return ret;
}
@@ -1618,9 +1640,13 @@ ShaderDebugTrace *ReplayController::DebugThread(const rdcfixedarray<uint32_t, 3>
RENDERDOC_PROFILEFUNCTION();
ShaderDebugTrace *ret = m_pDevice->DebugThread(m_EventID, groupid, threadid);
FatalErrorCheck();
SetFrameEvent(m_EventID, true);
if(ret->debugger)
m_Debuggers.push_back(ret->debugger);
return ret;
}
@@ -1631,6 +1657,7 @@ rdcarray<ShaderDebugState> ReplayController::ContinueDebug(ShaderDebugger *debug
RENDERDOC_PROFILEFUNCTION();
rdcarray<ShaderDebugState> ret = m_pDevice->ContinueDebug(debugger);
FatalErrorCheck();
return ret;
}
@@ -1641,6 +1668,7 @@ void ReplayController::FreeTrace(ShaderDebugTrace *trace)
if(trace)
{
m_Debuggers.removeOne(trace->debugger);
m_pDevice->FreeDebugger(trace->debugger);
delete trace;
}
@@ -1659,7 +1687,10 @@ rdcarray<ShaderVariable> ReplayController::GetCBufferVariableContents(
{
buffer = m_pDevice->GetLiveID(buffer);
if(buffer != ResourceId())
{
m_pDevice->GetBufferData(buffer, offset, length, data);
FatalErrorCheck();
}
}
rdcarray<ShaderVariable> v;
@@ -1668,7 +1699,10 @@ rdcarray<ShaderVariable> ReplayController::GetCBufferVariableContents(
shader = m_pDevice->GetLiveID(shader);
if(shader != ResourceId())
{
m_pDevice->FillCBufferVariables(pipeline, shader, entryPoint, cbufslot, v, data);
FatalErrorCheck();
}
return v;
}
@@ -1705,6 +1739,8 @@ rdcstr ReplayController::CreateRGPProfile(WindowingData window)
for(int i = 0; i < 5; i++)
{
m_pDevice->ReplayLog(10000000, eReplay_Full);
if(FatalErrorCheck())
return "";
output->Display();
}
@@ -1730,12 +1766,16 @@ rdcstr ReplayController::CreateRGPProfile(WindowingData window)
output->Display();
m_pDevice->ReplayLog(10000000, eReplay_Full);
if(FatalErrorCheck())
return "";
}
output->Display();
// restore back to where we were
m_pDevice->ReplayLog(m_EventID, eReplay_Full);
if(FatalErrorCheck())
return "";
ShutdownOutput(output);
@@ -1783,12 +1823,14 @@ void ReplayController::ReplayLoop(WindowingData window, ResourceId texid)
while(Atomic::CmpExch32(&m_ReplayLoopCancel, 0, 0) == 0)
{
m_pDevice->ReplayLog(10000000, eReplay_Full);
FatalErrorCheck();
output->Display();
}
// restore back to where we were
m_pDevice->ReplayLog(m_EventID, eReplay_Full);
FatalErrorCheck();
ShutdownOutput(output);
@@ -1865,6 +1907,48 @@ void ReplayController::Shutdown()
delete this;
}
bool ReplayController::FatalErrorCheck()
{
// we've already processed the fatal error if we have a status
if(m_FatalError != ReplayStatus::Succeeded)
return false;
m_FatalError = m_pDevice->FatalErrorCheck();
if(m_FatalError != ReplayStatus::Succeeded)
{
RDCLOG("Fatal error detected: %s", ToStr(m_FatalError).c_str());
IReplayDriver *old = m_pDevice;
// replace our driver with a dummy
m_pDevice = m_pDevice->MakeDummyDriver();
// replace the outputs as well
for(size_t i = 0; i < m_Outputs.size(); i++)
{
old->DestroyOutputWindow(m_Outputs[i]->m_MainOutput.outputID);
old->DestroyOutputWindow(m_Outputs[i]->m_PixelContext.outputID);
m_Outputs[i]->ClearThumbnails();
m_Outputs[i]->m_pDevice = m_pDevice;
}
// delete the old replay
old->Shutdown();
// reset pipeline states to default
m_D3D11PipelineState = D3D11Pipe::State();
m_D3D12PipelineState = D3D12Pipe::State();
m_GLPipelineState = GLPipe::State();
m_VulkanPipelineState = VKPipe::State();
return true;
}
return false;
}
rdcarray<ShaderEncoding> ReplayController::GetCustomShaderEncodings()
{
CHECK_REPLAY_THREAD();
@@ -1912,6 +1996,7 @@ rdcpair<ResourceId, rdcstr> ReplayController::BuildTargetShader(
}
m_pDevice->BuildTargetShader(sourceEncoding, source, entry, compileFlags, type, id, errs);
FatalErrorCheck();
if(id != ResourceId())
m_TargetResources.insert(id);
@@ -1952,6 +2037,7 @@ rdcpair<ResourceId, rdcstr> ReplayController::BuildCustomShader(
RDCLOG("Building custom shader");
m_pDevice->BuildCustomShader(sourceEncoding, source, entry, compileFlags, type, id, errs);
FatalErrorCheck();
if(id != ResourceId())
{
@@ -1987,6 +2073,7 @@ void ReplayController::ReplaceResource(ResourceId from, ResourceId to)
CHECK_REPLAY_THREAD();
m_pDevice->ReplaceResource(from, to);
FatalErrorCheck();
SetFrameEvent(m_EventID, true);
@@ -2000,6 +2087,7 @@ void ReplayController::RemoveReplacement(ResourceId id)
CHECK_REPLAY_THREAD();
m_pDevice->RemoveReplacement(id);
FatalErrorCheck();
SetFrameEvent(m_EventID, true);
@@ -2054,6 +2142,7 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd
GCNISA::CacheSupport(m_APIProps.pipelineType);
ReplayStatus status = m_pDevice->ReadLogInitialisation(rdc, false);
FatalErrorCheck();
m_pDevice->SetPipelineStates(&m_D3D11PipelineState, &m_D3D12PipelineState, &m_GLPipelineState,
&m_VulkanPipelineState);
@@ -2101,6 +2190,7 @@ void ReplayController::FetchPipelineState(uint32_t eventId)
RENDERDOC_PROFILEFUNCTION();
m_pDevice->SavePipelineState(eventId);
FatalErrorCheck();
if(m_APIProps.pipelineType == GraphicsAPI::D3D11)
m_PipeState.SetState(&m_D3D11PipelineState);
+8 -2
View File
@@ -77,7 +77,7 @@ private:
uint64_t m_ThreadID;
ReplayController *m_pRenderer;
ReplayController *m_pController;
bool m_OverlayDirty;
bool m_ForceOverlayRefresh;
@@ -173,7 +173,7 @@ public:
const rdcarray<BufferDescription> &GetBuffers();
const rdcarray<ResourceDescription> &GetResources();
rdcarray<DebugMessage> GetDebugMessages();
ReplayStatus GetFatalErrorStatus() { return m_FatalError; }
rdcarray<ShaderEntryPoint> GetShaderEntryPoints(ResourceId shader);
const ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, ShaderEntryPoint entry);
@@ -218,6 +218,8 @@ public:
void ShutdownOutput(IReplayOutput *output);
void Shutdown();
bool FatalErrorCheck();
private:
virtual ~ReplayController();
ReplayStatus PostCreateInit(IReplayDriver *device, RDCFile *rdc);
@@ -240,6 +242,8 @@ private:
int32_t m_ReplayLoopCancel = 0;
int32_t m_ReplayLoopFinished = 0;
ReplayStatus m_FatalError = ReplayStatus::Succeeded;
uint32_t m_EventID;
std::map<uint32_t, uint32_t> m_EventRemap;
@@ -258,6 +262,8 @@ private:
IReplayDriver *m_pDevice;
rdcarray<ShaderDebugger *> m_Debuggers;
std::set<ResourceId> m_TargetResources;
std::set<ResourceId> m_CustomShaders;
+3
View File
@@ -212,6 +212,7 @@ public:
virtual bool IsRenderOutput(ResourceId id) = 0;
virtual void FileChanged() = 0;
virtual ReplayStatus FatalErrorCheck() = 0;
virtual bool NeedRemapForFetch(const ResourceFormat &format) = 0;
@@ -225,6 +226,8 @@ class IReplayDriver : public IRemoteDriver
public:
virtual bool IsRemoteProxy() = 0;
virtual IReplayDriver *MakeDummyDriver() = 0;
virtual rdcarray<WindowingSystem> GetSupportedWindowSystems() = 0;
virtual AMDRGPControl *GetRGPControl() = 0;
+71 -13
View File
@@ -88,7 +88,7 @@ ReplayOutput::ReplayOutput(ReplayController *parent, WindowingData window, Repla
{
m_ThreadID = Threading::GetCurrentID();
m_pRenderer = parent;
m_pController = parent;
m_MainOutput.dirty = true;
@@ -118,6 +118,8 @@ ReplayOutput::ReplayOutput(ReplayController *parent, WindowingData window, Repla
m_MainOutput.outputID = 0;
m_MainOutput.texture = ResourceId();
m_pController->FatalErrorCheck();
m_pDevice->GetOutputWindowDimensions(m_MainOutput.outputID, m_Width, m_Height);
m_CustomShaderResourceId = ResourceId();
@@ -130,11 +132,11 @@ ReplayOutput::~ReplayOutput()
{
CHECK_REPLAY_THREAD();
m_CustomShaderResourceId = ResourceId();
m_pDevice->DestroyOutputWindow(m_MainOutput.outputID);
m_pDevice->DestroyOutputWindow(m_PixelContext.outputID);
m_CustomShaderResourceId = ResourceId();
ClearThumbnails();
}
@@ -142,7 +144,7 @@ void ReplayOutput::Shutdown()
{
CHECK_REPLAY_THREAD();
m_pRenderer->ShutdownOutput(this);
m_pController->ShutdownOutput(this);
}
void ReplayOutput::SetDimensions(int32_t width, int32_t height)
@@ -152,6 +154,7 @@ void ReplayOutput::SetDimensions(int32_t width, int32_t height)
m_pDevice->SetOutputWindowDimensions(m_MainOutput.outputID, width > 0 ? width : 1,
height > 0 ? height : 1);
m_pDevice->GetOutputWindowDimensions(m_MainOutput.outputID, m_Width, m_Height);
m_pController->FatalErrorCheck();
}
bytebuf ReplayOutput::ReadbackOutputTexture()
@@ -160,6 +163,7 @@ bytebuf ReplayOutput::ReadbackOutputTexture()
bytebuf data;
m_pDevice->GetOutputWindowData(m_MainOutput.outputID, data);
m_pController->FatalErrorCheck();
return data;
}
@@ -226,7 +230,7 @@ void ReplayOutput::RefreshOverlay()
{
CHECK_REPLAY_THREAD();
ActionDescription *action = m_pRenderer->GetActionByEID(m_EventID);
ActionDescription *action = m_pController->GetActionByEID(m_EventID);
passEvents = m_pDevice->GetPassEvents(m_EventID);
@@ -254,12 +258,15 @@ void ReplayOutput::RefreshOverlay()
if(action != NULL && (action->flags & ActionFlags::Drawcall))
{
m_pDevice->InitPostVSBuffers(action->eventId);
m_pController->FatalErrorCheck();
if(postVSWholePass && !passEvents.empty())
{
m_pDevice->InitPostVSBuffers(passEvents);
m_pController->FatalErrorCheck();
m_pDevice->ReplayLog(m_EventID, eReplay_WithoutDraw);
m_pController->FatalErrorCheck();
}
}
}
@@ -274,6 +281,7 @@ void ReplayOutput::RefreshOverlay()
m_OverlayResourceId =
m_pDevice->RenderOverlay(id, f, m_RenderData.texDisplay.overlay, m_EventID, passEvents);
m_pController->FatalErrorCheck();
m_OverlayDirty = false;
}
else
@@ -301,8 +309,10 @@ ResourceId ReplayOutput::GetDebugOverlayTexID()
if(m_OverlayDirty)
{
m_pDevice->ReplayLog(m_EventID, eReplay_WithoutDraw);
m_pController->FatalErrorCheck();
RefreshOverlay();
m_pDevice->ReplayLog(m_EventID, eReplay_OnlyDraw);
m_pController->FatalErrorCheck();
}
return m_OverlayResourceId;
@@ -326,6 +336,8 @@ bool ReplayOutput::SetPixelContext(WindowingData window)
m_PixelContext.texture = ResourceId();
m_PixelContext.depthMode = false;
m_pController->FatalErrorCheck();
RDCASSERT(m_PixelContext.outputID > 0);
return m_PixelContext.outputID != 0;
@@ -342,12 +354,12 @@ bool ReplayOutput::AddThumbnail(WindowingData window, ResourceId texID, const Su
bool depthMode = false;
for(size_t t = 0; t < m_pRenderer->m_Textures.size(); t++)
for(size_t t = 0; t < m_pController->m_Textures.size(); t++)
{
if(m_pRenderer->m_Textures[t].resourceId == texID)
if(m_pController->m_Textures[t].resourceId == texID)
{
depthMode = (m_pRenderer->m_Textures[t].creationFlags & TextureCategory::DepthTarget) ||
(m_pRenderer->m_Textures[t].format.compType == CompType::Depth);
depthMode = (m_pController->m_Textures[t].creationFlags & TextureCategory::DepthTarget) ||
(m_pController->m_Textures[t].format.compType == CompType::Depth);
break;
}
}
@@ -374,6 +386,8 @@ bool ReplayOutput::AddThumbnail(WindowingData window, ResourceId texID, const Su
p.typeCast = typeCast;
p.dirty = true;
m_pController->FatalErrorCheck();
RDCASSERT(p.outputID > 0);
m_Thumbnails.push_back(p);
@@ -387,7 +401,7 @@ rdcpair<uint32_t, uint32_t> ReplayOutput::PickVertex(uint32_t x, uint32_t y)
RENDERDOC_PROFILEFUNCTION();
ActionDescription *action = m_pRenderer->GetActionByEID(m_EventID);
ActionDescription *action = m_pController->GetActionByEID(m_EventID);
const rdcpair<uint32_t, uint32_t> errorReturn = {~0U, ~0U};
@@ -432,6 +446,9 @@ rdcpair<uint32_t, uint32_t> ReplayOutput::PickVertex(uint32_t x, uint32_t y)
MeshFormat fmt =
m_pDevice->GetPostVSBuffers(action->eventId, m_RenderData.meshDisplay.curInstance,
m_RenderData.meshDisplay.curView, m_RenderData.meshDisplay.type);
m_pController->FatalErrorCheck();
uint64_t elemOffset = cfg.position.vertexByteOffset - fmt.vertexByteOffset;
for(uint32_t inst = firstInst; inst < maxInst; inst++)
@@ -439,10 +456,14 @@ rdcpair<uint32_t, uint32_t> ReplayOutput::PickVertex(uint32_t x, uint32_t y)
// find the start of this buffer, and apply the element offset, then pick in that instance
fmt = m_pDevice->GetPostVSBuffers(action->eventId, inst, m_RenderData.meshDisplay.curView,
m_RenderData.meshDisplay.type);
m_pController->FatalErrorCheck();
if(fmt.vertexResourceId != ResourceId())
cfg.position.vertexByteOffset = fmt.vertexByteOffset + elemOffset;
uint32_t vert = m_pDevice->PickVertex(m_EventID, m_Width, m_Height, cfg, x, y);
m_pController->FatalErrorCheck();
if(vert != ~0U)
{
return make_rdcpair(vert, inst);
@@ -454,6 +475,7 @@ rdcpair<uint32_t, uint32_t> ReplayOutput::PickVertex(uint32_t x, uint32_t y)
else
{
uint32_t vert = m_pDevice->PickVertex(m_EventID, m_Width, m_Height, cfg, x, y);
m_pController->FatalErrorCheck();
if(vert != ~0U)
return make_rdcpair(vert, m_RenderData.meshDisplay.curInstance);
@@ -492,6 +514,8 @@ void ReplayOutput::ClearBackground(uint64_t outputID, const FloatVector &backgro
RenderDoc::Inst().LightCheckerboardColor());
else
m_pDevice->ClearOutputWindowColor(outputID, ConvertSRGBToLinear(backgroundColor));
m_pController->FatalErrorCheck();
}
void ReplayOutput::DisplayContext()
@@ -500,13 +524,17 @@ void ReplayOutput::DisplayContext()
if(m_PixelContext.outputID == 0)
return;
m_pDevice->BindOutputWindow(m_PixelContext.outputID, false);
m_pController->FatalErrorCheck();
ClearBackground(m_PixelContext.outputID, m_RenderData.texDisplay.backgroundColor);
if((m_Type != ReplayOutputType::Texture) || (m_ContextX < 0.0f && m_ContextY < 0.0f) ||
(m_RenderData.texDisplay.resourceId == ResourceId()))
{
m_pDevice->FlipOutputWindow(m_PixelContext.outputID);
m_pController->FatalErrorCheck();
return;
}
@@ -552,10 +580,13 @@ void ReplayOutput::DisplayContext()
disp.resourceId = m_pDevice->GetLiveID(disp.resourceId);
m_pDevice->RenderTexture(disp);
m_pController->FatalErrorCheck();
m_pDevice->RenderHighlightBox(w, h, contextZoom);
m_pController->FatalErrorCheck();
m_pDevice->FlipOutputWindow(m_PixelContext.outputID);
m_pController->FatalErrorCheck();
}
void ReplayOutput::Display()
@@ -589,6 +620,7 @@ void ReplayOutput::Display()
}
m_pDevice->FlipOutputWindow(m_MainOutput.outputID);
m_pController->FatalErrorCheck();
DisplayContext();
}
@@ -596,8 +628,10 @@ void ReplayOutput::Display()
{
m_pDevice->BindOutputWindow(m_MainOutput.outputID, false);
m_pDevice->FlipOutputWindow(m_MainOutput.outputID);
m_pController->FatalErrorCheck();
m_pDevice->BindOutputWindow(m_PixelContext.outputID, false);
m_pDevice->FlipOutputWindow(m_PixelContext.outputID);
m_pController->FatalErrorCheck();
}
for(size_t i = 0; i < m_Thumbnails.size(); i++)
@@ -632,8 +666,10 @@ void ReplayOutput::Display()
light2.z = light.z;
light2.w = 1.0f;
m_pDevice->RenderCheckerboard(dark2, light2);
m_pController->FatalErrorCheck();
m_pDevice->FlipOutputWindow(m_Thumbnails[i].outputID);
m_pController->FatalErrorCheck();
continue;
}
@@ -667,8 +703,10 @@ void ReplayOutput::Display()
disp.green = disp.blue = false;
m_pDevice->RenderTexture(disp);
m_pController->FatalErrorCheck();
m_pDevice->FlipOutputWindow(m_Thumbnails[i].outputID);
m_pController->FatalErrorCheck();
m_Thumbnails[i].dirty = false;
}
@@ -680,7 +718,7 @@ void ReplayOutput::DisplayTex()
RENDERDOC_PROFILEFUNCTION();
ActionDescription *action = m_pRenderer->GetActionByEID(m_EventID);
ActionDescription *action = m_pController->GetActionByEID(m_EventID);
if(m_MainOutput.outputID == 0)
return;
@@ -701,14 +739,17 @@ void ReplayOutput::DisplayTex()
if(m_OverlayDirty)
{
m_pDevice->ReplayLog(m_EventID, eReplay_WithoutDraw);
m_pController->FatalErrorCheck();
RefreshOverlay();
m_pDevice->ReplayLog(m_EventID, eReplay_OnlyDraw);
m_pController->FatalErrorCheck();
}
}
else if(m_ForceOverlayRefresh)
{
m_ForceOverlayRefresh = false;
m_pDevice->ReplayLog(m_EventID, eReplay_Full);
m_pController->FatalErrorCheck();
}
if(m_RenderData.texDisplay.customShaderId != ResourceId())
@@ -716,6 +757,7 @@ void ReplayOutput::DisplayTex()
m_CustomShaderResourceId =
m_pDevice->ApplyCustomShader(m_RenderData.texDisplay.customShaderId, texDisplay.resourceId,
texDisplay.subresource, texDisplay.typeCast);
m_pController->FatalErrorCheck();
texDisplay.resourceId = m_pDevice->GetLiveID(m_CustomShaderResourceId);
texDisplay.typeCast = CompType::Typeless;
@@ -739,6 +781,7 @@ void ReplayOutput::DisplayTex()
}
m_pDevice->RenderTexture(texDisplay);
m_pController->FatalErrorCheck();
ResourceId id = m_pDevice->GetLiveID(m_RenderData.texDisplay.resourceId);
@@ -760,6 +803,7 @@ void ReplayOutput::DisplayTex()
texDisplay.typeCast = CompType::Typeless;
m_pDevice->RenderTexture(texDisplay);
m_pController->FatalErrorCheck();
}
}
@@ -769,7 +813,7 @@ void ReplayOutput::DisplayMesh()
RENDERDOC_PROFILEFUNCTION();
ActionDescription *action = m_pRenderer->GetActionByEID(m_EventID);
ActionDescription *action = m_pController->GetActionByEID(m_EventID);
if(action == NULL || m_MainOutput.outputID == 0 || m_Width <= 0 || m_Height <= 0 ||
(m_RenderData.meshDisplay.type == MeshDataStage::Unknown) ||
@@ -781,6 +825,7 @@ void ReplayOutput::DisplayMesh()
m_pDevice->ClearOutputWindowDepth(m_MainOutput.outputID, 1.0f, 0);
m_pDevice->RenderCheckerboard(RenderDoc::Inst().DarkCheckerboardColor(),
RenderDoc::Inst().LightCheckerboardColor());
m_pController->FatalErrorCheck();
return;
}
@@ -788,8 +833,10 @@ void ReplayOutput::DisplayMesh()
if(m_OverlayDirty)
{
m_pDevice->ReplayLog(m_EventID, eReplay_WithoutDraw);
m_pController->FatalErrorCheck();
RefreshOverlay();
m_pDevice->ReplayLog(m_EventID, eReplay_OnlyDraw);
m_pController->FatalErrorCheck();
}
m_pDevice->BindOutputWindow(m_MainOutput.outputID, true);
@@ -797,8 +844,10 @@ void ReplayOutput::DisplayMesh()
m_pDevice->RenderCheckerboard(RenderDoc::Inst().DarkCheckerboardColor(),
RenderDoc::Inst().LightCheckerboardColor());
m_pController->FatalErrorCheck();
m_pDevice->ClearOutputWindowDepth(m_MainOutput.outputID, 1.0f, 0);
m_pController->FatalErrorCheck();
MeshDisplay mesh = m_RenderData.meshDisplay;
mesh.position.vertexResourceId = m_pDevice->GetLiveID(mesh.position.vertexResourceId);
@@ -833,7 +882,7 @@ void ReplayOutput::DisplayMesh()
{
for(size_t i = 0; m_RenderData.meshDisplay.showWholePass && i < passEvents.size(); i++)
{
ActionDescription *d = m_pRenderer->GetActionByEID(passEvents[i]);
ActionDescription *d = m_pController->GetActionByEID(passEvents[i]);
if(d)
{
@@ -842,9 +891,13 @@ void ReplayOutput::DisplayMesh()
// get the 'most final' stage
MeshFormat fmt = m_pDevice->GetPostVSBuffers(
passEvents[i], inst, m_RenderData.meshDisplay.curView, MeshDataStage::GSOut);
m_pController->FatalErrorCheck();
if(fmt.vertexResourceId == ResourceId())
{
fmt = m_pDevice->GetPostVSBuffers(passEvents[i], inst, m_RenderData.meshDisplay.curView,
MeshDataStage::VSOut);
m_pController->FatalErrorCheck();
}
fmt.meshColor = passDraws;
@@ -869,9 +922,13 @@ void ReplayOutput::DisplayMesh()
// get the 'most final' stage
MeshFormat fmt = m_pDevice->GetPostVSBuffers(
action->eventId, inst, m_RenderData.meshDisplay.curView, MeshDataStage::GSOut);
m_pController->FatalErrorCheck();
if(fmt.vertexResourceId == ResourceId())
{
fmt = m_pDevice->GetPostVSBuffers(action->eventId, inst, m_RenderData.meshDisplay.curView,
MeshDataStage::VSOut);
m_pController->FatalErrorCheck();
}
fmt.meshColor = otherInstances;
@@ -885,4 +942,5 @@ void ReplayOutput::DisplayMesh()
mesh.position.meshColor = drawItself;
m_pDevice->RenderMesh(m_EventID, secondaryDraws, mesh);
m_pController->FatalErrorCheck();
}