From 18898b7f09e9890d9c81be6c1d750e603ccf31cb Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 12 Oct 2016 16:59:25 +0200 Subject: [PATCH] Set up proper log loading/closing * This includes the recent log menu list, progress bars and popup. --- qrenderdoc/Code/CaptureContext.cpp | 167 +++++++-- qrenderdoc/Code/CaptureContext.h | 25 +- qrenderdoc/Code/RenderManager.cpp | 25 +- qrenderdoc/Code/RenderManager.h | 3 +- qrenderdoc/Windows/MainWindow.cpp | 555 ++++++++++++++++++++++++++++- qrenderdoc/Windows/MainWindow.h | 39 +- qrenderdoc/Windows/MainWindow.ui | 42 ++- 7 files changed, 793 insertions(+), 63 deletions(-) diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 7a403ea13..0bfc826b3 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -25,9 +25,11 @@ #include "CaptureContext.h" #include #include +#include #include #include #include +#include #include #include #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 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 logviewers(m_LogViewers); + + for(ILogViewerForm *logviewer : logviewers) + { + if(logviewer) + logviewer->OnLogfileClosed(); + } } void CaptureContext::SetEventID(ILogViewerForm *exclude, uint32_t eventID, bool force) diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index 189f339ed..1773a472c 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -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 m_LogViewers; - QVector 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 &draws, uint32_t eventID) @@ -201,6 +191,7 @@ private: #endif // Windows + QProgressDialog *m_Progress; MainWindow *m_MainWindow; }; diff --git a/qrenderdoc/Code/RenderManager.cpp b/qrenderdoc/Code/RenderManager.cpp index 45d7f62b0..567da3bc4 100644 --- a/qrenderdoc/Code/RenderManager.cpp +++ b/qrenderdoc/Code/RenderManager.cpp @@ -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; diff --git a/qrenderdoc/Code/RenderManager.h b/qrenderdoc/Code/RenderManager.h index 7aa3a0ad9..3bfcdbeeb 100644 --- a/qrenderdoc/Code/RenderManager.h +++ b/qrenderdoc/Code/RenderManager.h @@ -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; } diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index fe4d41fc3..d73ab6b6f 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #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 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() diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index 8e88ed282..41f76f4fd 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -26,15 +26,17 @@ #include #include +#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); diff --git a/qrenderdoc/Windows/MainWindow.ui b/qrenderdoc/Windows/MainWindow.ui index 7556fa991..09d46b952 100644 --- a/qrenderdoc/Windows/MainWindow.ui +++ b/qrenderdoc/Windows/MainWindow.ui @@ -59,16 +59,30 @@ &File + + + &Recent Logs + + + + + + + R&ecent Capture Settings + + + + - + - - + + @@ -168,7 +182,7 @@ Ctrl+S - + C&lose Log @@ -176,16 +190,6 @@ Ctrl+W - - - &Recent Logs - - - - - R&ecent Capture Settings - - &Exit @@ -369,6 +373,16 @@ &About + + + Clear History + + + + + Clear History + +