From f4bd51fc219aef41ab0cd040ef9b2b18a915bdb5 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 7 Feb 2017 14:52:45 +0000 Subject: [PATCH] Revamp progress dialogs to abstract away most of the handling --- qrenderdoc/Code/CaptureContext.cpp | 146 ++++++++++------------------- qrenderdoc/Code/CaptureContext.h | 7 +- qrenderdoc/Code/QRDUtils.cpp | 105 +++++++++++++++++++++ qrenderdoc/Code/QRDUtils.h | 8 ++ qrenderdoc/Windows/MainWindow.cpp | 44 ++------- 5 files changed, 174 insertions(+), 136 deletions(-) diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 5f6690499..b3af25e7a 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -123,74 +123,65 @@ QString CaptureContext::TempLogFilename(QString appname) void CaptureContext::LoadLogfile(const QString &logFile, const QString &origFilename, bool temporary, bool local) { - 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); + m_LoadInProgress = true; 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(); + + ShowProgressDialog( + m_MainWindow, QApplication::translate("CaptureContext", "Loading Log: %1").arg(origFilename), + [this]() { return !m_LoadInProgress; }, [this]() { return UpdateLoadProgress(); }); + + m_MainWindow->setProgress(-1.0f); + + if(m_LogLoaded) + { + QVector logviewers(m_LogViewers); + + // make sure we're on a consistent event before invoking log viewer forms + const FetchDrawcall *draw = &m_Drawcalls.back(); + while(!draw->children.empty()) + draw = &draw->children.back(); + + SetEventID(logviewers, draw->eventID, true); + + GUIInvoke::blockcall([&logviewers]() { + // notify all the registers log viewers that a log has been loaded + for(ILogViewerForm *logviewer : logviewers) + { + if(logviewer) + logviewer->OnLogfileLoaded(); + } + }); + } +} + +float CaptureContext::UpdateLoadProgress() +{ + float val = 0.8f * m_LoadProgress + 0.19f * m_PostloadProgress + 0.01f; + + m_MainWindow->setProgress(val); + + return val; } void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString &origFilename, bool temporary, bool local) { - QFileInfo fi(ConfigFile("UI.config")); - m_LogFile = origFilename; m_LogLocal = local; - m_LoadInProgress = true; + Config.Serialize(); - if(fi.exists()) - Config.Serialize(fi.absoluteFilePath()); - - 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(); + m_LoadProgress = 0.0f; + m_PostloadProgress = 0.0f; // this function call will block until the log is either loaded, or there's some failure - m_Renderer.OpenCapture(logFile, &loadProgress); + m_Renderer.OpenCapture(logFile, &m_LoadProgress); // if the renderer isn't running, we hit a failure case so display an error message if(!m_Renderer.IsRunning()) @@ -198,23 +189,13 @@ void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString & QString errmsg = "Unknown error message"; errmsg = ToQStr(m_Renderer.GetCreateStatus()); - progressThread.acquire(); - progressTickerThread.wait(); - 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)); - GUIInvoke::call([this]() { - m_Progress->setValue(1000); - m_MainWindow->setProgress(-1.0f); - m_Progress->hide(); - }); - m_LoadInProgress = false; - return; } @@ -222,23 +203,22 @@ void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString & { PersistantConfig::AddRecentFile(Config.RecentLogFiles, origFilename, 10); - if(fi.exists()) - Config.Serialize(fi.absoluteFilePath()); + Config.Serialize(); } m_EventID = 0; // fetch initial data like drawcalls, textures and buffers - m_Renderer.BlockInvoke([this, &postloadProgress](IReplayRenderer *r) { + m_Renderer.BlockInvoke([this](IReplayRenderer *r) { r->GetFrameInfo(&m_FrameInfo); m_APIProps = r->GetAPIProperties(); - postloadProgress = 0.2f; + m_PostloadProgress = 0.2f; r->GetDrawcalls(&m_Drawcalls); - postloadProgress = 0.4f; + m_PostloadProgress = 0.4f; r->GetSupportedWindowSystems(&m_WinSystems); @@ -267,13 +247,13 @@ void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString & for(FetchBuffer &b : m_BufferList) m_Buffers[b.ID] = &b; - postloadProgress = 0.8f; + m_PostloadProgress = 0.8f; r->GetTextures(&m_TextureList); for(FetchTexture &t : m_TextureList) m_Textures[t.ID] = &t; - postloadProgress = 0.9f; + m_PostloadProgress = 0.9f; r->GetD3D11PipelineState(&CurD3D11PipelineState); r->GetD3D12PipelineState(&CurD3D12PipelineState); @@ -285,7 +265,7 @@ void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString & UnreadMessageCount = 0; AddMessages(m_FrameInfo.debugMessages); - postloadProgress = 1.0f; + m_PostloadProgress = 1.0f; }); QThread::msleep(20); @@ -307,36 +287,8 @@ void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString & .arg(origFilename)); } - m_LogLoaded = true; - - progressThread.acquire(); - progressTickerThread.wait(); - - QVector logviewers(m_LogViewers); - - // make sure we're on a consistent event before invoking log viewer forms - const FetchDrawcall *draw = &m_Drawcalls.back(); - while(!draw->children.empty()) - draw = &draw->children.back(); - - SetEventID(logviewers, draw->eventID, true); - - GUIInvoke::blockcall([&logviewers]() { - // notify all the registers log viewers that a log has been loaded - for(ILogViewerForm *logviewer : logviewers) - { - if(logviewer) - logviewer->OnLogfileLoaded(); - } - }); - m_LoadInProgress = false; - - GUIInvoke::call([this]() { - m_Progress->setValue(1000); - m_MainWindow->setProgress(1.0f); - m_Progress->hide(); - }); + m_LogLoaded = true; } void CaptureContext::CloseLogfile() diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index fefe83222..1df17ac93 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -64,7 +64,6 @@ class TextureViewer; class CaptureDialog; class DebugMessageView; class StatisticsViewer; -class QProgressDialog; class CaptureContext { @@ -83,6 +82,7 @@ public: // Control functions void LoadLogfile(const QString &logFile, const QString &origFilename, bool temporary, bool local); + void CloseLogfile(); void SetEventID(const QVector &exclude, uint32_t selectedEventID, @@ -180,6 +180,10 @@ private: bool m_LogLoaded, m_LoadInProgress, m_LogLocal; QString m_LogFile; + float m_LoadProgress = 0.0f; + float m_PostloadProgress = 0.0f; + float UpdateLoadProgress(); + void LoadLogfileThreaded(const QString &logFile, const QString &origFilename, bool temporary, bool local); @@ -224,7 +228,6 @@ private: QIcon *m_Icon; // Windows - QProgressDialog *m_Progress; MainWindow *m_MainWindow = NULL; EventBrowser *m_EventBrowser = NULL; APIInspector *m_APIInspector = NULL; diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index a24c9eec0..e23319bbc 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -23,12 +23,16 @@ ******************************************************************************/ #include "QRDUtils.h" +#include #include #include #include #include +#include +#include #include #include +#include #include #include @@ -592,3 +596,104 @@ QString Formatter::Format(double f, bool) return ret; } + +class RDProgressDialog : public QProgressDialog +{ +public: + RDProgressDialog(const QString &labelText, QWidget *parent) + // we add 1 so that the progress value never hits maximum until we are actually finished + : QProgressDialog(labelText, QString(), 0, maxProgress + 1, parent), + m_Label(this) + { + setWindowTitle(tr("Please Wait")); + setWindowFlags(Qt::CustomizeWindowHint | Qt::Dialog | Qt::WindowTitleHint); + setWindowIcon(QIcon()); + setMinimumSize(QSize(250, 0)); + setMaximumSize(QSize(250, 10000)); + setCancelButton(NULL); + setMinimumDuration(0); + setWindowModality(Qt::ApplicationModal); + setValue(0); + + m_Label.setText(labelText); + m_Label.setAlignment(Qt::AlignCenter); + m_Label.setWordWrap(true); + + setLabel(&m_Label); + } + + void setPercentage(float percent) { setValue(int(maxProgress * percent)); } + void setInfinite(bool infinite) + { + if(infinite) + { + setMinimum(0); + setMaximum(0); + setValue(0); + } + else + { + setMinimum(0); + setMaximum(maxProgress + 1); + setValue(0); + } + } + + void closeAndReset() + { + setValue(maxProgress); + hide(); + reset(); + } + +protected: + void keyPressEvent(QKeyEvent *e) override + { + if(e->key() == Qt::Key_Escape) + return; + + QProgressDialog::keyPressEvent(e); + } + + QLabel m_Label; + + static const int maxProgress = 1000; +}; + +void ShowProgressDialog(QWidget *window, const QString &labelText, ProgressFinishedMethod finished, + ProgressUpdateMethod update) +{ + RDProgressDialog dialog(labelText, window); + + // if we don't have an update function, set the progress display to be 'infinite spinner' + dialog.setInfinite(!update); + + QSemaphore tickerSemaphore(1); + + // start a lambda thread to tick our functions and close the progress dialog when we're done. + LambdaThread progressTickerThread([finished, update, &dialog, &tickerSemaphore]() { + while(tickerSemaphore.available()) + { + QThread::msleep(30); + + if(update) + GUIInvoke::call([update, &dialog]() { dialog.setPercentage(update()); }); + + GUIInvoke::call([finished, &tickerSemaphore]() { + if(finished()) + tickerSemaphore.tryAcquire(); + }); + } + + GUIInvoke::call([&dialog]() { dialog.closeAndReset(); }); + }); + progressTickerThread.start(); + + // show the dialog + RDDialog::show(&dialog); + + // signal the thread to exit if somehow we got here without it finishing, then wait for it thread + // to clean itself up + tickerSemaphore.tryAcquire(); + progressTickerThread.wait(); +} diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 87eb17732..d3b0c0bd4 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -669,3 +669,11 @@ class QTreeWidgetItem; QTreeWidgetItem *makeTreeNode(const std::initializer_list &values); QTreeWidgetItem *makeTreeNode(const QVariantList &values); + +class QProgressDialog; + +typedef std::function ProgressUpdateMethod; +typedef std::function ProgressFinishedMethod; + +void ShowProgressDialog(QWidget *window, const QString &labelText, ProgressFinishedMethod finished, + ProgressUpdateMethod update = ProgressUpdateMethod()); diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index c369200a0..ae4dbf31d 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -1080,45 +1080,15 @@ void MainWindow::on_action_Resolve_Symbols_triggered() { m_Ctx->Renderer()->AsyncInvoke([this](IReplayRenderer *r) { r->InitResolver(); }); - QProgressDialog *m_Progress = - new QProgressDialog(tr("Please Wait - Resolving Symbols"), QString(), 0, 0, this); - 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(tr("Please Wait - Resolving Symbols")); - label->setAlignment(Qt::AlignCenter); - label->setWordWrap(true); - - m_Progress->setLabel(label); - - LambdaThread *thread = new LambdaThread([this, m_Progress]() { + ShowProgressDialog(this, tr("Please Wait - Resolving Symbols"), [this]() { bool running = true; - - while(running) - { - // just bail if we managed to get here without a resolver. - m_Ctx->Renderer()->BlockInvoke( - [&running](IReplayRenderer *r) { running = r->HasCallstacks() && !r->InitResolver(); }); - } - - GUIInvoke::call([this, m_Progress]() { - m_Progress->hide(); - delete m_Progress; - if(m_Ctx->hasAPIInspector()) - m_Ctx->apiInspector()->on_apiEvents_itemSelectionChanged(); - }); + m_Ctx->Renderer()->BlockInvoke( + [&running](IReplayRenderer *r) { running = r->HasCallstacks() && !r->InitResolver(); }); + return !running; }); - thread->selfDelete(true); - thread->start(); + + if(m_Ctx->hasAPIInspector()) + m_Ctx->apiInspector()->on_apiEvents_itemSelectionChanged(); } void MainWindow::on_action_Settings_triggered()