mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 17:10:47 +00:00
Set up proper log loading/closing
* This includes the recent log menu list, progress bars and popup.
This commit is contained in:
@@ -25,9 +25,11 @@
|
||||
#include "CaptureContext.h"
|
||||
#include <QApplication>
|
||||
#include <QFileInfo>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QMetaObject>
|
||||
#include <QProgressDialog>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
#include "Windows/MainWindow.h"
|
||||
@@ -52,10 +54,7 @@ CaptureContext::CaptureContext(QString paramFilename, QString remoteHost, uint32
|
||||
{
|
||||
QFileInfo fi(paramFilename);
|
||||
|
||||
if(fi.suffix() == "rdc")
|
||||
{
|
||||
LoadLogfile(paramFilename, temp);
|
||||
}
|
||||
m_MainWindow->LoadFromFilename(paramFilename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,23 +80,77 @@ QString CaptureContext::ConfigFile(const QString &filename)
|
||||
return QDir::cleanPath(dir.absoluteFilePath(filename));
|
||||
}
|
||||
|
||||
void CaptureContext::LoadLogfile(QString logFile, bool temporary)
|
||||
void CaptureContext::LoadLogfile(const QString &logFile, const QString &origFilename,
|
||||
bool temporary, bool local)
|
||||
{
|
||||
LoadLogfile(-1, "", logFile, temporary);
|
||||
m_Progress = new QProgressDialog(QString("Loading Log"), QString(), 0, 1000, m_MainWindow);
|
||||
m_Progress->setWindowTitle("Please Wait");
|
||||
m_Progress->setWindowFlags(Qt::CustomizeWindowHint | Qt::Dialog | Qt::WindowTitleHint);
|
||||
m_Progress->setWindowIcon(QIcon());
|
||||
m_Progress->setMinimumSize(QSize(250, 0));
|
||||
m_Progress->setMaximumSize(QSize(250, 10000));
|
||||
m_Progress->setCancelButton(NULL);
|
||||
m_Progress->setMinimumDuration(0);
|
||||
m_Progress->setWindowModality(Qt::ApplicationModal);
|
||||
m_Progress->setValue(0);
|
||||
|
||||
QLabel *label = new QLabel(m_Progress);
|
||||
|
||||
label->setText(QString("Loading Log: %1").arg(origFilename));
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
label->setWordWrap(true);
|
||||
|
||||
m_Progress->setLabel(label);
|
||||
|
||||
LambdaThread *thread = new LambdaThread([this, logFile, origFilename, temporary, local]() {
|
||||
LoadLogfileThreaded(logFile, origFilename, temporary, local);
|
||||
|
||||
GUIInvoke::call([this, origFilename]() {
|
||||
delete m_Progress;
|
||||
m_Progress = NULL;
|
||||
});
|
||||
});
|
||||
thread->selfDelete(true);
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void CaptureContext::LoadLogfile(int proxyRenderer, QString replayHost, QString logFile,
|
||||
bool temporary)
|
||||
void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString &origFilename,
|
||||
bool temporary, bool local)
|
||||
{
|
||||
m_LogFile = logFile;
|
||||
QFileInfo fi(ConfigFile("UI.config"));
|
||||
|
||||
m_LogFile = origFilename;
|
||||
|
||||
m_LogLocal = local;
|
||||
|
||||
m_LoadInProgress = true;
|
||||
|
||||
if(fi.exists())
|
||||
Config.Serialize(fi.absolutePath());
|
||||
|
||||
float loadProgress = 0.0f;
|
||||
float postloadProgress = 0.0f;
|
||||
|
||||
QSemaphore progressThread(1);
|
||||
|
||||
LambdaThread progressTickerThread([this, &progressThread, &loadProgress, &postloadProgress]() {
|
||||
while(progressThread.available())
|
||||
{
|
||||
QThread::msleep(30);
|
||||
|
||||
float val = 0.8f * loadProgress + 0.19f * postloadProgress + 0.01f;
|
||||
|
||||
GUIInvoke::call([this, val]() {
|
||||
m_Progress->setValue(val * 1000);
|
||||
m_MainWindow->setProgress(val);
|
||||
});
|
||||
}
|
||||
GUIInvoke::call([this]() { m_Progress->setValue(1000); });
|
||||
});
|
||||
progressTickerThread.start();
|
||||
|
||||
// this function call will block until the log is either loaded, or there's some failure
|
||||
m_Renderer.Init(proxyRenderer, replayHost, logFile, &loadProgress);
|
||||
m_Renderer.OpenCapture(logFile, &loadProgress);
|
||||
|
||||
// if the renderer isn't running, we hit a failure case so display an error message
|
||||
if(!m_Renderer.IsRunning())
|
||||
@@ -106,22 +159,34 @@ void CaptureContext::LoadLogfile(int proxyRenderer, QString replayHost, QString
|
||||
ReplayCreateStatus status = m_Renderer.GetCreateStatus();
|
||||
errmsg = status;
|
||||
|
||||
if(proxyRenderer >= 0)
|
||||
RDDialog::critical(NULL, "Error opening log",
|
||||
QString("%1\nFailed to transfer and replay on remote host %2: %3.\n\n"
|
||||
"Check diagnostic log in Help menu for more details.")
|
||||
.arg(logFile, replayHost, errmsg));
|
||||
else
|
||||
RDDialog::critical(NULL, "Error opening log",
|
||||
QString("%1\nFailed to open logfile for replay: %1.\n\n"
|
||||
"Check diagnostic log in Help menu for more details.")
|
||||
.arg(logFile, errmsg));
|
||||
RDDialog::critical(NULL, "Error opening log",
|
||||
QString("%1\nFailed to open logfile for replay: %2.\n\n"
|
||||
"Check diagnostic log in Help menu for more details.")
|
||||
.arg(logFile)
|
||||
.arg(errmsg));
|
||||
|
||||
progressThread.acquire();
|
||||
progressTickerThread.wait();
|
||||
|
||||
GUIInvoke::call([this]() {
|
||||
m_Progress->setValue(1000);
|
||||
m_MainWindow->setProgress(-1.0f);
|
||||
m_Progress->hide();
|
||||
});
|
||||
|
||||
m_LoadInProgress = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!temporary)
|
||||
{
|
||||
PersistantConfig::AddRecentFile(Config.RecentLogFiles, origFilename, 10);
|
||||
|
||||
if(fi.exists())
|
||||
Config.Serialize(fi.absolutePath());
|
||||
}
|
||||
|
||||
m_EventID = 0;
|
||||
|
||||
// fetch initial data like drawcalls, textures and buffers
|
||||
@@ -186,8 +251,28 @@ void CaptureContext::LoadLogfile(int proxyRenderer, QString replayHost, QString
|
||||
|
||||
QThread::msleep(20);
|
||||
|
||||
QDateTime today = QDateTime::currentDateTimeUtc();
|
||||
QDateTime compare = today.addDays(-21);
|
||||
|
||||
if(compare > Config.DegradedLog_LastUpdate && m_APIProps.degraded)
|
||||
{
|
||||
Config.DegradedLog_LastUpdate = today;
|
||||
|
||||
RDDialog::critical(
|
||||
NULL, "Degraded support of log",
|
||||
QString(
|
||||
"%1\nThis log opened with degraded support - "
|
||||
"this could mean missing hardware support caused a fallback to software rendering.\n\n"
|
||||
"This warning will not appear every time this happens, "
|
||||
"check debug errors/warnings window for more details.")
|
||||
.arg(origFilename));
|
||||
}
|
||||
|
||||
m_LogLoaded = true;
|
||||
|
||||
progressThread.acquire();
|
||||
progressTickerThread.wait();
|
||||
|
||||
QVector<ILogViewerForm *> logviewers(m_LogViewers);
|
||||
|
||||
GUIInvoke::blockcall([&logviewers]() {
|
||||
@@ -200,6 +285,48 @@ void CaptureContext::LoadLogfile(int proxyRenderer, QString replayHost, QString
|
||||
});
|
||||
|
||||
m_LoadInProgress = false;
|
||||
|
||||
GUIInvoke::call([this]() {
|
||||
m_Progress->setValue(1000);
|
||||
m_MainWindow->setProgress(1.0f);
|
||||
m_Progress->hide();
|
||||
});
|
||||
}
|
||||
|
||||
void CaptureContext::CloseLogfile()
|
||||
{
|
||||
if(!m_LogLoaded)
|
||||
return;
|
||||
|
||||
m_LogFile = "";
|
||||
|
||||
m_Renderer.CloseThread();
|
||||
|
||||
memset(&m_APIProps, 0, sizeof(m_APIProps));
|
||||
memset(&m_FrameInfo, 0, sizeof(m_FrameInfo));
|
||||
m_Buffers.clear();
|
||||
m_BufferList.clear();
|
||||
m_Textures.clear();
|
||||
m_TextureList.clear();
|
||||
|
||||
CurD3D11PipelineState = D3D11PipelineState();
|
||||
CurD3D12PipelineState = D3D12PipelineState();
|
||||
CurGLPipelineState = GLPipelineState();
|
||||
CurVulkanPipelineState = VulkanPipelineState();
|
||||
CurPipelineState.SetStates(m_APIProps, NULL, NULL, NULL, NULL);
|
||||
|
||||
DebugMessages.clear();
|
||||
UnreadMessageCount = 0;
|
||||
|
||||
m_LogLoaded = false;
|
||||
|
||||
QVector<ILogViewerForm *> logviewers(m_LogViewers);
|
||||
|
||||
for(ILogViewerForm *logviewer : logviewers)
|
||||
{
|
||||
if(logviewer)
|
||||
logviewer->OnLogfileClosed();
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureContext::SetEventID(ILogViewerForm *exclude, uint32_t eventID, bool force)
|
||||
|
||||
@@ -46,13 +46,8 @@ struct ILogViewerForm
|
||||
virtual void OnEventSelected(uint32_t eventID) = 0;
|
||||
};
|
||||
|
||||
struct ILogLoadProgressListener
|
||||
{
|
||||
virtual void LogfileProgressBegin() = 0;
|
||||
virtual void LogfileProgress(float progress) = 0;
|
||||
};
|
||||
|
||||
class MainWindow;
|
||||
class QProgressDialog;
|
||||
|
||||
struct Formatter
|
||||
{
|
||||
@@ -79,19 +74,11 @@ public:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Control functions
|
||||
|
||||
// loading a local log, no remote replay
|
||||
void LoadLogfile(QString logFile, bool temporary);
|
||||
|
||||
// when loading a log while replaying remotely, provide the proxy renderer that will be used
|
||||
// as well as the hostname to replay on.
|
||||
void LoadLogfile(int proxyRenderer, QString replayHost, QString logFile, bool temporary);
|
||||
|
||||
void LoadLogfile(const QString &logFile, const QString &origFilename, bool temporary, bool local);
|
||||
void CloseLogfile();
|
||||
|
||||
void SetEventID(ILogViewerForm *exclude, uint32_t eventID, bool force = false);
|
||||
void RefreshStatus() { SetEventID(NULL, m_EventID, true); }
|
||||
void AddLogProgressListener(ILogLoadProgressListener *p);
|
||||
|
||||
void AddLogViewer(ILogViewerForm *f)
|
||||
{
|
||||
m_LogViewers.push_back(f);
|
||||
@@ -122,6 +109,7 @@ public:
|
||||
|
||||
RenderManager *Renderer() { return &m_Renderer; }
|
||||
bool LogLoaded() { return m_LogLoaded; }
|
||||
bool IsLogLocal() { return m_LogLocal; }
|
||||
bool LogLoading() { return m_LoadInProgress; }
|
||||
QString LogFilename() { return m_LogFile; }
|
||||
const FetchFrameInfo &FrameInfo() { return m_FrameInfo; }
|
||||
@@ -158,11 +146,13 @@ private:
|
||||
RenderManager m_Renderer;
|
||||
|
||||
QVector<ILogViewerForm *> m_LogViewers;
|
||||
QVector<ILogLoadProgressListener *> m_ProgressListeners;
|
||||
|
||||
bool m_LogLoaded, m_LoadInProgress;
|
||||
bool m_LogLoaded, m_LoadInProgress, m_LogLocal;
|
||||
QString m_LogFile;
|
||||
|
||||
void LoadLogfileThreaded(const QString &logFile, const QString &origFilename, bool temporary,
|
||||
bool local);
|
||||
|
||||
uint32_t m_EventID;
|
||||
|
||||
const FetchDrawcall *GetDrawcall(const rdctype::array<FetchDrawcall> &draws, uint32_t eventID)
|
||||
@@ -201,6 +191,7 @@ private:
|
||||
#endif
|
||||
|
||||
// Windows
|
||||
QProgressDialog *m_Progress;
|
||||
MainWindow *m_MainWindow;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,13 +36,13 @@ RenderManager::~RenderManager()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderManager::Init(int proxyRenderer, QString replayHost, QString logfile, float *progress)
|
||||
void RenderManager::OpenCapture(const QString &logfile, float *progress)
|
||||
{
|
||||
if(m_Running)
|
||||
return;
|
||||
|
||||
m_ProxyRenderer = proxyRenderer;
|
||||
m_ReplayHost = replayHost;
|
||||
m_ProxyRenderer = -1;
|
||||
m_ReplayHost = "";
|
||||
m_Logfile = logfile;
|
||||
m_Progress = progress;
|
||||
|
||||
@@ -56,6 +56,25 @@ void RenderManager::Init(int proxyRenderer, QString replayHost, QString logfile,
|
||||
}
|
||||
}
|
||||
|
||||
void RenderManager::DeleteCapture(const QString &logfile, bool local)
|
||||
{
|
||||
if(IsRunning())
|
||||
{
|
||||
AsyncInvoke([this, logfile, local](IReplayRenderer *) { DeleteCapture(logfile, local); });
|
||||
return;
|
||||
}
|
||||
|
||||
if(local)
|
||||
{
|
||||
QFile::remove(logfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
// m_Remote.TakeOwnershipCapture(logfile);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderManager::IsRunning()
|
||||
{
|
||||
return m_Thread->isRunning() && m_Running;
|
||||
|
||||
@@ -48,7 +48,8 @@ public:
|
||||
RenderManager();
|
||||
~RenderManager();
|
||||
|
||||
void Init(int proxyRenderer, QString replayHost, QString logfile, float *progress);
|
||||
void OpenCapture(const QString &logfile, float *progress);
|
||||
void DeleteCapture(const QString &logfile, bool local);
|
||||
|
||||
bool IsRunning();
|
||||
ReplayCreateStatus GetCreateStatus() { return m_CreateStatus; }
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonDocument>
|
||||
#include <QProgressBar>
|
||||
#include "Code/CaptureContext.h"
|
||||
#include "Windows/Dialogs/AboutDialog.h"
|
||||
#include "EventBrowser.h"
|
||||
@@ -66,6 +67,29 @@ MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::Main
|
||||
QObject::connect(ui->action_Save_Layout_6, &QAction::triggered, this,
|
||||
&MainWindow::saveLayout_triggered);
|
||||
|
||||
statusIcon = new QLabel(this);
|
||||
ui->statusBar->addWidget(statusIcon);
|
||||
|
||||
statusText = new QLabel(this);
|
||||
ui->statusBar->addWidget(statusText);
|
||||
|
||||
statusProgress = new QProgressBar(this);
|
||||
ui->statusBar->addWidget(statusProgress);
|
||||
|
||||
statusProgress->setVisible(false);
|
||||
statusProgress->setMinimumSize(QSize(200, 0));
|
||||
statusProgress->setMinimum(0);
|
||||
statusProgress->setMaximum(1000);
|
||||
|
||||
statusIcon->setText("");
|
||||
statusIcon->setPixmap(QPixmap());
|
||||
statusText->setText("");
|
||||
|
||||
SetTitle();
|
||||
|
||||
PopulateRecentFiles();
|
||||
PopulateRecentCaptures();
|
||||
|
||||
ui->toolWindowManager->setRubberBandLineWidth(50);
|
||||
ui->toolWindowManager->setToolWindowCreateCallback([this](const QString &objectName) -> QWidget * {
|
||||
if(objectName == "textureViewer")
|
||||
@@ -126,18 +150,537 @@ void MainWindow::on_action_Exit_triggered()
|
||||
|
||||
void MainWindow::on_action_Open_Log_triggered()
|
||||
{
|
||||
if(!PromptCloseLog())
|
||||
return;
|
||||
|
||||
QString filename =
|
||||
RDDialog::getOpenFileName(this, "Select Logfile to open", "",
|
||||
RDDialog::getOpenFileName(this, "Select Logfile to open", m_Ctx->Config.LastLogPath,
|
||||
"Log Files (*.rdc);;Image Files (*.dds *.hdr *.exr *.bmp *.jpg "
|
||||
"*.jpeg *.png *.tga *.gif *.psd;;All Files (*.*)");
|
||||
|
||||
QFileInfo checkFile(filename);
|
||||
if(filename != "" && checkFile.exists() && checkFile.isFile())
|
||||
if(filename != "")
|
||||
LoadFromFilename(filename);
|
||||
}
|
||||
|
||||
void MainWindow::LoadFromFilename(const QString &filename)
|
||||
{
|
||||
QFileInfo path(filename);
|
||||
QString ext = path.suffix().toLower();
|
||||
|
||||
if(ext == ".rdc")
|
||||
{
|
||||
LambdaThread *thread =
|
||||
new LambdaThread([filename, this]() { m_Ctx->LoadLogfile(filename, false); });
|
||||
thread->start();
|
||||
LoadLogfile(filename, false, true);
|
||||
}
|
||||
else if(ext == ".cap")
|
||||
{
|
||||
// OpenCaptureConfigFile(filename, false);
|
||||
}
|
||||
else if(ext == ".exe")
|
||||
{
|
||||
// OpenCaptureConfigFile(filename, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a recognised filetype, see if we can load it anyway
|
||||
LoadLogfile(filename, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local)
|
||||
{
|
||||
if(PromptCloseLog())
|
||||
{
|
||||
if(m_Ctx->LogLoading())
|
||||
return;
|
||||
|
||||
rdctype::str driver;
|
||||
rdctype::str machineIdent;
|
||||
ReplaySupport support = eReplaySupport_Unsupported;
|
||||
|
||||
bool remoteReplay =
|
||||
!local /*|| (m_Core.Renderer.Remote != null && m_Core.Renderer.Remote.Connected)*/;
|
||||
|
||||
if(local)
|
||||
{
|
||||
support = RENDERDOC_SupportLocalReplay(filename.toUtf8().data(), &driver, &machineIdent);
|
||||
|
||||
// if the return value suggests remote replay, and it's not already selected, AND the user
|
||||
// hasn't
|
||||
// previously chosen to always replay locally without being prompted, ask if they'd prefer to
|
||||
// switch to a remote context for replaying.
|
||||
if(support == eReplaySupport_SuggestRemote && !remoteReplay &&
|
||||
!m_Ctx->Config.AlwaysReplayLocally)
|
||||
{
|
||||
RDDialog::information(NULL, tr("Not Implemented"),
|
||||
tr("Can't suggest a remote host, replaying locally"));
|
||||
/*
|
||||
var dialog = new Dialogs.SuggestRemoteDialog(driver, machineIdent);
|
||||
|
||||
FillRemotesToolStrip(dialog.RemoteItems, false);
|
||||
|
||||
dialog.ShowDialog();
|
||||
|
||||
if(dialog.Result == Dialogs.SuggestRemoteDialog.SuggestRemoteResult.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if(dialog.Result == Dialogs.SuggestRemoteDialog.SuggestRemoteResult.Remote)
|
||||
{
|
||||
// we only get back here from the dialog once the context switch has begun,
|
||||
// so contextChooser will have been disabled.
|
||||
// Check once to see if it's enabled before even popping up the dialog in case
|
||||
// it has finished already. Otherwise pop up a waiting dialog until it completes
|
||||
// one way or another, then process the result.
|
||||
|
||||
if(!contextChooser.Enabled)
|
||||
{
|
||||
ProgressPopup modal = new ProgressPopup((ModalCloseCallback)delegate
|
||||
{
|
||||
return contextChooser.Enabled;
|
||||
}, false);
|
||||
modal.SetModalText("Please Wait - Checking remote connection...");
|
||||
|
||||
modal.ShowDialog();
|
||||
}
|
||||
|
||||
remoteReplay = (m_Core.Renderer.Remote != null && m_Core.Renderer.Remote.Connected);
|
||||
|
||||
if(!remoteReplay)
|
||||
{
|
||||
string remoteMessage = "Failed to make a connection to the remote server.\n\n";
|
||||
|
||||
remoteMessage += "More information may be available in the status bar.";
|
||||
|
||||
MessageBox.Show(remoteMessage, "Couldn't connect to remote server",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to do - we just continue replaying locally
|
||||
// however we need to check if the user selected 'always replay locally' and
|
||||
// set that bit as sticky in the config
|
||||
if(dialog.AlwaysReplayLocally)
|
||||
{
|
||||
m_Core.Config.AlwaysReplayLocally = true;
|
||||
|
||||
m_Core.Config.Serialize(Core.ConfigFilename);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if(remoteReplay)
|
||||
{
|
||||
support = eReplaySupport_Unsupported;
|
||||
|
||||
QVector<QString> remoteDrivers = {}; // m_Ctx->Renderer.GetRemoteSupport();
|
||||
|
||||
for(const QString &d : remoteDrivers)
|
||||
{
|
||||
if(driver == d)
|
||||
support = eReplaySupport_Supported;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString origFilename = filename;
|
||||
|
||||
// if driver is empty something went wrong loading the log, let it be handled as usual
|
||||
// below. Otherwise indicate that support is missing.
|
||||
if(driver.count > 0 && support == eReplaySupport_Unsupported)
|
||||
{
|
||||
if(remoteReplay)
|
||||
{
|
||||
QString remoteMessage =
|
||||
tr("This log was captured with %1 and cannot be replayed on %2.\n\n")
|
||||
.arg(driver.c_str())
|
||||
.arg("localhost" /*m_Ctx->Renderer.Remote.Hostname*/);
|
||||
|
||||
remoteMessage += "Try selecting a different remote context in the status bar.";
|
||||
|
||||
RDDialog::critical(NULL, tr("Unsupported logfile type"), remoteMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString remoteMessage =
|
||||
tr("This log was captured with %1 and cannot be replayed locally.\n\n").arg(driver.c_str());
|
||||
|
||||
remoteMessage += "Try selecting a remote context in the status bar.";
|
||||
|
||||
RDDialog::critical(NULL, tr("Unsupported logfile type"), remoteMessage);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(remoteReplay && local)
|
||||
{
|
||||
RDDialog::critical(NULL, tr("Not Implemented"), tr("Not Implemented"));
|
||||
return;
|
||||
/*
|
||||
try
|
||||
{
|
||||
filename = m_Ctx->Renderer.CopyCaptureToRemote(filename, this);
|
||||
|
||||
// deliberately leave local as true so that we keep referring to the locally saved log
|
||||
|
||||
// some error
|
||||
if(filename == "")
|
||||
throw new ApplicationException();
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
MessageBox.Show("Couldn't copy " + filename + " to remote host for replaying", "Error
|
||||
copying to remote",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
m_Ctx->LoadLogfile(filename, origFilename, temporary, local);
|
||||
}
|
||||
|
||||
if(!remoteReplay)
|
||||
{
|
||||
m_Ctx->Config.LastLogPath = QFileInfo(filename).dir().absolutePath();
|
||||
}
|
||||
|
||||
statusText->setText(tr("Loading ") + origFilename + "...");
|
||||
}
|
||||
}
|
||||
|
||||
QString MainWindow::GetSavePath()
|
||||
{
|
||||
QString dir;
|
||||
|
||||
if(m_Ctx->Config.DefaultCaptureSaveDirectory != "")
|
||||
{
|
||||
if(m_LastSaveCapturePath == "")
|
||||
dir = m_Ctx->Config.DefaultCaptureSaveDirectory;
|
||||
else
|
||||
dir = m_LastSaveCapturePath;
|
||||
}
|
||||
|
||||
QString filename =
|
||||
RDDialog::getSaveFileName(this, tr("Save Capture As"), dir, "Log Files (*.rdc)");
|
||||
|
||||
if(filename != "")
|
||||
{
|
||||
QDir dirinfo = QFileInfo(filename).dir();
|
||||
if(dirinfo.exists())
|
||||
m_LastSaveCapturePath = dirinfo.absolutePath();
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool MainWindow::PromptSaveLog()
|
||||
{
|
||||
QString saveFilename = GetSavePath();
|
||||
|
||||
if(saveFilename != "")
|
||||
{
|
||||
if(m_Ctx->IsLogLocal() && !QFileInfo(m_Ctx->LogFilename()).exists())
|
||||
{
|
||||
RDDialog::critical(NULL, tr("File not found"),
|
||||
tr("Logfile %1 couldn't be found, cannot save.").arg(m_Ctx->LogFilename()));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
if(m_Ctx->IsLogLocal())
|
||||
{
|
||||
// we copy the (possibly) temp log to the desired path, but the log item remains referring to
|
||||
// the original path.
|
||||
// This ensures that if the user deletes the saved path we can still open or re-save it.
|
||||
success = QFile::copy(m_Ctx->LogFilename(), saveFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
// m_Core.Renderer.CopyCaptureFromRemote(m_Core.LogFileName, saveFilename, this);
|
||||
}
|
||||
|
||||
if(!success)
|
||||
{
|
||||
RDDialog::critical(NULL, tr("File not found"), tr("Couldn't save to %1").arg(saveFilename));
|
||||
return false;
|
||||
}
|
||||
|
||||
PersistantConfig::AddRecentFile(m_Ctx->Config.RecentLogFiles, saveFilename, 10);
|
||||
PopulateRecentFiles();
|
||||
SetTitle(saveFilename);
|
||||
|
||||
// we don't prompt to save on closing - if the user deleted the log that we just saved, then
|
||||
// that is up to them.
|
||||
m_SavedTempLog = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MainWindow::PromptCloseLog()
|
||||
{
|
||||
if(!m_Ctx->LogLoaded())
|
||||
return true;
|
||||
|
||||
QString deletepath = "";
|
||||
bool loglocal = false;
|
||||
|
||||
if(m_OwnTempLog)
|
||||
{
|
||||
QString temppath = m_Ctx->LogFilename();
|
||||
loglocal = m_Ctx->IsLogLocal();
|
||||
|
||||
QMessageBox::StandardButton res = QMessageBox::No;
|
||||
|
||||
// unless we've saved the log, prompt to save
|
||||
if(!m_SavedTempLog)
|
||||
res = RDDialog::question(NULL, tr("Unsaved log"), tr("Save this logfile?"),
|
||||
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
||||
|
||||
if(res == QMessageBox::Cancel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(res == QMessageBox::Yes)
|
||||
{
|
||||
bool success = PromptSaveLog();
|
||||
|
||||
if(!success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(temppath != m_Ctx->LogFilename() || res == QMessageBox::No)
|
||||
deletepath = temppath;
|
||||
m_OwnTempLog = false;
|
||||
m_SavedTempLog = false;
|
||||
}
|
||||
|
||||
CloseLogfile();
|
||||
|
||||
if(!deletepath.isEmpty())
|
||||
m_Ctx->Renderer()->DeleteCapture(deletepath, loglocal);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWindow::CloseLogfile()
|
||||
{
|
||||
m_Ctx->CloseLogfile();
|
||||
|
||||
ui->action_Save_Log->setEnabled(false);
|
||||
}
|
||||
|
||||
void MainWindow::SetTitle(const QString &filename)
|
||||
{
|
||||
QString prefix = "";
|
||||
|
||||
if(m_Ctx != NULL && m_Ctx->LogLoaded())
|
||||
{
|
||||
prefix = QFileInfo(filename).fileName();
|
||||
if(m_Ctx->APIProps().degraded)
|
||||
prefix += " !DEGRADED PERFORMANCE!";
|
||||
prefix += " - ";
|
||||
}
|
||||
|
||||
// TODO
|
||||
// if(m_Ctx != NULL && m_Ctx->Renderer.Remote != null)
|
||||
// prefix += String.Format("Remote: {0} - ", m_Core.Renderer.Remote.Hostname);
|
||||
|
||||
QString text = prefix + "RenderDoc ";
|
||||
|
||||
// TODO
|
||||
/*
|
||||
if(OfficialVersion)
|
||||
text += VersionString;
|
||||
else if(BetaVersion)
|
||||
text += String.Format("{0}-beta - {1}", VersionString, GitCommitHash);
|
||||
else */
|
||||
text +=
|
||||
tr("Unofficial release (%1 - %2)").arg(RENDERDOC_GetVersionString()).arg(RENDERDOC_GetCommitHash());
|
||||
|
||||
// TODO
|
||||
// if(IsVersionMismatched())
|
||||
// text += " - !! VERSION MISMATCH DETECTED !!";
|
||||
|
||||
setWindowTitle(text);
|
||||
}
|
||||
|
||||
void MainWindow::SetTitle()
|
||||
{
|
||||
SetTitle(m_Ctx != NULL ? m_Ctx->LogFilename() : "");
|
||||
}
|
||||
|
||||
void MainWindow::PopulateRecentFiles()
|
||||
{
|
||||
ui->menu_Recent_Logs->clear();
|
||||
|
||||
ui->menu_Recent_Logs->setEnabled(false);
|
||||
|
||||
int idx = 1;
|
||||
for(const QString &filename : m_Ctx->Config.RecentLogFiles)
|
||||
{
|
||||
ui->menu_Recent_Logs->addAction("&" + QString::number(idx) + " " + filename,
|
||||
[this, filename] { recentLog(filename); });
|
||||
idx++;
|
||||
|
||||
ui->menu_Recent_Logs->setEnabled(true);
|
||||
}
|
||||
|
||||
ui->menu_Recent_Logs->addSeparator();
|
||||
ui->menu_Recent_Logs->addAction(ui->action_Clear_Log_History);
|
||||
}
|
||||
|
||||
void MainWindow::PopulateRecentCaptures()
|
||||
{
|
||||
ui->menu_Recent_Capture_Settings->clear();
|
||||
|
||||
ui->menu_Recent_Capture_Settings->setEnabled(false);
|
||||
|
||||
int idx = 1;
|
||||
for(const QString &filename : m_Ctx->Config.RecentCaptureSettings)
|
||||
{
|
||||
ui->menu_Recent_Capture_Settings->addAction("&" + QString::number(idx) + " " + filename,
|
||||
[this, filename] { recentCapture(filename); });
|
||||
idx++;
|
||||
|
||||
ui->menu_Recent_Capture_Settings->setEnabled(true);
|
||||
}
|
||||
|
||||
ui->menu_Recent_Capture_Settings->addSeparator();
|
||||
ui->menu_Recent_Capture_Settings->addAction(ui->action_Clear_Log_History);
|
||||
}
|
||||
|
||||
void MainWindow::recentLog(const QString &filename)
|
||||
{
|
||||
if(QFileInfo::exists(filename))
|
||||
{
|
||||
LoadLogfile(filename, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::StandardButton res =
|
||||
RDDialog::question(NULL, tr("File not found"),
|
||||
tr("File %1 couldn't be found.\nRemove from recent list?").arg(filename));
|
||||
|
||||
if(res == QMessageBox::Yes)
|
||||
{
|
||||
m_Ctx->Config.RecentLogFiles.removeOne(filename);
|
||||
|
||||
PopulateRecentFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::recentCapture(const QString &filename)
|
||||
{
|
||||
if(QFileInfo::exists(filename))
|
||||
{
|
||||
// OpenCaptureConfigFile(filename, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::StandardButton res =
|
||||
RDDialog::question(NULL, tr("File not found"),
|
||||
tr("File %1 couldn't be found.\nRemove from recent list?").arg(filename));
|
||||
|
||||
if(res == QMessageBox::Yes)
|
||||
{
|
||||
m_Ctx->Config.RecentLogFiles.removeOne(filename);
|
||||
|
||||
PopulateRecentFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setProgress(float val)
|
||||
{
|
||||
if(val < 0.0f || val >= 1.0f)
|
||||
{
|
||||
statusProgress->setVisible(false);
|
||||
statusText->setText("");
|
||||
}
|
||||
else
|
||||
{
|
||||
statusProgress->setVisible(true);
|
||||
statusProgress->setValue(1000 * val);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnLogfileLoaded()
|
||||
{
|
||||
// TODO
|
||||
// don't allow changing context while log is open
|
||||
// contextChooser.Enabled = false;
|
||||
|
||||
// LogHasErrors = (m_Core.DebugMessages.Count > 0);
|
||||
|
||||
statusProgress->setVisible(false);
|
||||
|
||||
m_Ctx->Renderer()->AsyncInvoke([this](IReplayRenderer *r) {
|
||||
bool hasResolver = r->HasCallstacks();
|
||||
|
||||
GUIInvoke::call([this, hasResolver]() {
|
||||
ui->action_Resolve_Symbols->setEnabled(hasResolver);
|
||||
ui->action_Resolve_Symbols->setText(hasResolver ? tr("Resolve Symbols")
|
||||
: tr("Resolve Symbols - None in log"));
|
||||
});
|
||||
});
|
||||
|
||||
ui->action_Save_Log->setEnabled(true);
|
||||
|
||||
SetTitle();
|
||||
|
||||
PopulateRecentFiles();
|
||||
|
||||
// m_Core.GetEventBrowser().Focus();
|
||||
}
|
||||
|
||||
void MainWindow::OnLogfileClosed()
|
||||
{
|
||||
// contextChooser.Enabled = true;
|
||||
|
||||
statusText->setText("");
|
||||
statusIcon->setPixmap(QPixmap());
|
||||
statusProgress->setVisible(false);
|
||||
|
||||
ui->action_Resolve_Symbols->setEnabled(false);
|
||||
ui->action_Resolve_Symbols->setText(tr("Resolve Symbols"));
|
||||
|
||||
SetTitle();
|
||||
|
||||
// if the remote sever disconnected during log replay, resort back to a 'disconnected' state
|
||||
// TODO
|
||||
/*
|
||||
if(m_Core.Renderer.Remote != null && !m_Core.Renderer.Remote.ServerRunning)
|
||||
{
|
||||
statusText.Text = "Remote server disconnected. To attempt to reconnect please select it again.";
|
||||
contextChooser.Text = "Replay Context: Local";
|
||||
m_Core.Renderer.DisconnectFromRemoteServer();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void MainWindow::OnEventSelected(uint32_t eventID)
|
||||
{
|
||||
}
|
||||
|
||||
void MainWindow::on_action_Close_Log_triggered()
|
||||
{
|
||||
PromptCloseLog();
|
||||
}
|
||||
|
||||
void MainWindow::on_action_About_triggered()
|
||||
|
||||
@@ -26,15 +26,17 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <QMainWindow>
|
||||
#include "Code/CaptureContext.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class CaptureContext;
|
||||
class QLabel;
|
||||
class QProgressBar;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
class MainWindow : public QMainWindow, public ILogViewerForm
|
||||
{
|
||||
private:
|
||||
Q_OBJECT
|
||||
@@ -43,11 +45,20 @@ public:
|
||||
explicit MainWindow(CaptureContext *ctx);
|
||||
~MainWindow();
|
||||
|
||||
void OnLogfileLoaded();
|
||||
void OnLogfileClosed();
|
||||
void OnEventSelected(uint32_t eventID);
|
||||
|
||||
void setProgress(float val);
|
||||
bool ownTemporaryLog() { return m_OwnTempLog; }
|
||||
void LoadFromFilename(const QString &filename);
|
||||
|
||||
private slots:
|
||||
// automatic slots
|
||||
void on_action_Exit_triggered();
|
||||
void on_action_About_triggered();
|
||||
void on_action_Open_Log_triggered();
|
||||
void on_action_Close_Log_triggered();
|
||||
void on_action_Mesh_Output_triggered();
|
||||
void on_action_Event_Viewer_triggered();
|
||||
void on_action_Texture_Viewer_triggered();
|
||||
@@ -62,6 +73,30 @@ private:
|
||||
Ui::MainWindow *ui;
|
||||
CaptureContext *m_Ctx;
|
||||
|
||||
QLabel *statusIcon;
|
||||
QLabel *statusText;
|
||||
QProgressBar *statusProgress;
|
||||
|
||||
bool m_OwnTempLog = false;
|
||||
bool m_SavedTempLog = false;
|
||||
|
||||
QString m_LastSaveCapturePath = "";
|
||||
|
||||
void SetTitle(const QString &filename);
|
||||
void SetTitle();
|
||||
|
||||
void PopulateRecentFiles();
|
||||
void PopulateRecentCaptures();
|
||||
|
||||
void recentLog(const QString &filename);
|
||||
void recentCapture(const QString &filename);
|
||||
|
||||
bool PromptCloseLog();
|
||||
bool PromptSaveLog();
|
||||
void LoadLogfile(const QString &filename, bool temporary, bool local);
|
||||
QString GetSavePath();
|
||||
void CloseLogfile();
|
||||
|
||||
QVariantMap saveState();
|
||||
bool restoreState(QVariantMap &state);
|
||||
|
||||
|
||||
@@ -59,16 +59,30 @@
|
||||
<property name="title">
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_Recent_Logs">
|
||||
<property name="title">
|
||||
<string>&Recent Logs</string>
|
||||
</property>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Clear_Log_History"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Recent_Capture_Settings">
|
||||
<property name="title">
|
||||
<string>R&ecent Capture Settings</string>
|
||||
</property>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Clear_Capture_History"/>
|
||||
</widget>
|
||||
<addaction name="action_Capture_Log"/>
|
||||
<addaction name="action_Attach_to_Running_Instance"/>
|
||||
<addaction name="action_Inject_into_Process"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Open_Log"/>
|
||||
<addaction name="action_Save_Log"/>
|
||||
<addaction name="actionC_lose_Log"/>
|
||||
<addaction name="action_Close_Log"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Recent_Logs"/>
|
||||
<addaction name="actionR_ecent_Capture_Settings"/>
|
||||
<addaction name="menu_Recent_Logs"/>
|
||||
<addaction name="menu_Recent_Capture_Settings"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Exit"/>
|
||||
</widget>
|
||||
@@ -168,7 +182,7 @@
|
||||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionC_lose_Log">
|
||||
<action name="action_Close_Log">
|
||||
<property name="text">
|
||||
<string>C&lose Log</string>
|
||||
</property>
|
||||
@@ -176,16 +190,6 @@
|
||||
<string>Ctrl+W</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Recent_Logs">
|
||||
<property name="text">
|
||||
<string>&Recent Logs</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionR_ecent_Capture_Settings">
|
||||
<property name="text">
|
||||
<string>R&ecent Capture Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Exit">
|
||||
<property name="text">
|
||||
<string>&Exit</string>
|
||||
@@ -369,6 +373,16 @@
|
||||
<string>&About</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Clear_Log_History">
|
||||
<property name="text">
|
||||
<string>Clear History</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Clear_Capture_History">
|
||||
<property name="text">
|
||||
<string>Clear History</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
|
||||
Reference in New Issue
Block a user