Add support for remote context capture and replay, including Android

* Still missing is remote file/folder browsing, and managing remote
  hosts adding/removing etc.
This commit is contained in:
baldurk
2017-02-09 19:28:21 +00:00
parent 267ad262ee
commit 4ec3b4dce3
9 changed files with 680 additions and 211 deletions
+10 -4
View File
@@ -378,12 +378,14 @@ QMessageBox::StandardButton RDDialog::messageBox(QMessageBox::Icon icon, QWidget
}
QString RDDialog::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir,
QFileDialog::Options options)
QFileDialog::Options options, QAbstractProxyModel *proxy)
{
QFileDialog fd(parent, caption, dir, QString());
fd.setAcceptMode(QFileDialog::AcceptOpen);
fd.setFileMode(QFileDialog::DirectoryOnly);
fd.setOptions(options);
if(proxy)
fd.setProxyModel(proxy);
show(&fd);
if(fd.result() == QFileDialog::Accepted)
@@ -420,7 +422,7 @@ QString RDDialog::getOpenFileName(QWidget *parent, const QString &caption, const
}
QString RDDialog::getExecutableFileName(QWidget *parent, const QString &caption, const QString &dir,
QFileDialog::Options options)
QFileDialog::Options options, QAbstractProxyModel *proxy)
{
QString filter;
@@ -433,8 +435,12 @@ QString RDDialog::getExecutableFileName(QWidget *parent, const QString &caption,
fd.setOptions(options);
fd.setAcceptMode(QFileDialog::AcceptOpen);
fd.setFileMode(QFileDialog::ExistingFile);
QFileFilterModel *proxy = new QFileFilterModel(parent);
proxy->setRequirePermissions(QDir::Executable);
if(!proxy)
{
QFileFilterModel *fileProxy = new QFileFilterModel(parent);
fileProxy->setRequirePermissions(QDir::Executable);
proxy = fileProxy;
}
fd.setProxyModel(proxy);
show(&fd);
+4 -2
View File
@@ -628,7 +628,8 @@ struct RDDialog
static QString getExistingDirectory(QWidget *parent = NULL, const QString &caption = QString(),
const QString &dir = QString(),
QFileDialog::Options options = QFileDialog::ShowDirsOnly);
QFileDialog::Options options = QFileDialog::ShowDirsOnly,
QAbstractProxyModel *proxy = NULL);
static QString getOpenFileName(QWidget *parent = NULL, const QString &caption = QString(),
const QString &dir = QString(), const QString &filter = QString(),
@@ -637,7 +638,8 @@ struct RDDialog
static QString getExecutableFileName(QWidget *parent = NULL, const QString &caption = QString(),
const QString &dir = QString(),
QFileDialog::Options options = QFileDialog::Options());
QFileDialog::Options options = QFileDialog::Options(),
QAbstractProxyModel *proxy = NULL);
static QString getSaveFileName(QWidget *parent = NULL, const QString &caption = QString(),
const QString &dir = QString(), const QString &filter = QString(),
+231 -28
View File
@@ -23,7 +23,9 @@
******************************************************************************/
#include "RenderManager.h"
#include <QApplication>
#include <QMutexLocker>
#include <QProgressDialog>
#include "CaptureContext.h"
#include "QRDUtils.h"
@@ -71,11 +73,150 @@ void RenderManager::DeleteCapture(const QString &logfile, bool local)
}
else
{
// TODO Remote
// m_Remote.TakeOwnershipCapture(logfile);
// this will be cleaned up automatically when the remote connection
// is closed.
if(m_Remote)
{
QMutexLocker autolock(&m_RemoteLock);
m_Remote->TakeOwnershipCapture(logfile.toUtf8().data());
}
}
}
QStringList RenderManager::GetRemoteSupport()
{
QStringList ret;
if(m_Remote && !IsRunning())
{
QMutexLocker autolock(&m_RemoteLock);
rdctype::array<rdctype::str> supported;
m_Remote->RemoteSupportedReplays(&supported);
for(rdctype::str &s : supported)
ret << ToQStr(s);
}
return ret;
}
void RenderManager::GetHomeFolder(DirectoryBrowseMethod cb)
{
if(!m_Remote)
return;
if(IsRunning() && m_Thread->isCurrentThread())
{
AsyncInvoke([cb, this](IReplayRenderer *r) {
cb(m_Remote->GetHomeFolder().c_str(), rdctype::array<DirectoryFile>());
});
return;
}
rdctype::str home;
{
QMutexLocker autolock(&m_RemoteLock);
home = m_Remote->GetHomeFolder();
}
cb(home.c_str(), rdctype::array<DirectoryFile>());
}
bool RenderManager::ListFolder(QString path, DirectoryBrowseMethod cb)
{
if(!m_Remote)
return false;
if(IsRunning() && m_Thread->isCurrentThread())
{
AsyncInvoke([cb, path, this](IReplayRenderer *r) {
const char *pathstr = path.toUtf8().data();
cb(pathstr, m_Remote->ListFolder(pathstr));
});
return true;
}
rdctype::array<DirectoryFile> contents;
const char *pathstr = path.toUtf8().data();
// prevent pings while fetching remote FS data
{
QMutexLocker autolock(&m_RemoteLock);
contents = m_Remote->ListFolder(pathstr);
}
cb(pathstr, contents);
return true;
}
QString RenderManager::CopyCaptureToRemote(const QString &localpath, QWidget *window)
{
if(!m_Remote)
return "";
QString remotepath = "";
bool copied = false;
float progress = 0.0f;
auto lambda = [this, localpath, &remotepath, &progress, &copied](IReplayRenderer *r) {
QMutexLocker autolock(&m_RemoteLock);
remotepath = m_Remote->CopyCaptureToRemote(localpath.toUtf8().data(), &progress);
copied = true;
};
// we should never have the thread running at this point, but let's be safe.
if(IsRunning())
{
AsyncInvoke(lambda);
}
else
{
LambdaThread *thread = new LambdaThread([&lambda]() { lambda(NULL); });
thread->selfDelete(true);
thread->start();
}
ShowProgressDialog(window, QApplication::translate("RenderManager", "Transferring..."),
[&copied]() { return !copied; }, [&progress]() { return progress; });
return remotepath;
}
void RenderManager::CopyCaptureFromRemote(const QString &remotepath, const QString &localpath,
QWidget *window)
{
if(!m_Remote)
return;
bool copied = false;
float progress = 0.0f;
auto lambda = [this, localpath, remotepath, &progress, &copied](IReplayRenderer *r) {
QMutexLocker autolock(&m_RemoteLock);
m_Remote->CopyCaptureFromRemote(remotepath.toUtf8().data(), localpath.toUtf8().data(), &progress);
copied = true;
};
// we should never have the thread running at this point, but let's be safe.
if(IsRunning())
{
AsyncInvoke(lambda);
}
else
{
LambdaThread *thread = new LambdaThread([&lambda]() { lambda(NULL); });
thread->selfDelete(true);
thread->start();
}
ShowProgressDialog(window, QApplication::translate("RenderManager", "Transferring..."),
[&copied]() { return !copied; }, [&progress]() { return progress; });
}
bool RenderManager::IsRunning()
{
return m_Thread && m_Thread->isRunning() && m_Running;
@@ -122,33 +263,90 @@ void RenderManager::CloseThread()
m_Thread = NULL;
}
ReplayCreateStatus RenderManager::ConnectToRemoteServer(RemoteHost *host)
{
ReplayCreateStatus status =
RENDERDOC_CreateRemoteServerConnection(host->Hostname.toUtf8().data(), 0, &m_Remote);
m_RemoteHost = host;
if(status == eReplayCreate_Success)
{
m_RemoteHost->Connected = true;
return status;
}
return status;
}
void RenderManager::DisconnectFromRemoteServer()
{
if(m_Remote)
{
QMutexLocker autolock(&m_RemoteLock);
m_Remote->ShutdownConnection();
}
m_RemoteHost = NULL;
m_Remote = NULL;
}
void RenderManager::ShutdownServer()
{
if(m_Remote)
{
QMutexLocker autolock(&m_RemoteLock);
m_Remote->ShutdownServerAndConnection();
}
m_Remote = NULL;
}
void RenderManager::PingRemote()
{
if(!m_Remote)
return;
if(m_RemoteLock.tryLock(0))
{
if(!IsRunning() || m_Thread->isCurrentThread())
{
if(!m_Remote->Ping())
m_RemoteHost->ServerRunning = false;
}
m_RemoteLock.unlock();
}
}
uint32_t RenderManager::ExecuteAndInject(const QString &exe, const QString &workingDir,
const QString &cmdLine,
const QList<EnvironmentModification> &env,
const QString &logfile, CaptureOptions opts)
{
// if (m_Remote == null)
void *envList = RENDERDOC_MakeEnvironmentModificationList(env.size());
for(int i = 0; i < env.size(); i++)
RENDERDOC_SetEnvironmentModification(envList, i, env[i].variable.toUtf8().data(),
env[i].value.toUtf8().data(), env[i].type, env[i].separator);
uint32_t ret = 0;
if(m_Remote)
{
void *envList = RENDERDOC_MakeEnvironmentModificationList(env.size());
for(int i = 0; i < env.size(); i++)
RENDERDOC_SetEnvironmentModification(envList, i, env[i].variable.toUtf8().data(),
env[i].value.toUtf8().data(), env[i].type,
env[i].separator);
uint32_t ret = RENDERDOC_ExecuteAndInject(exe.toUtf8().data(), workingDir.toUtf8().data(),
cmdLine.toUtf8().data(), envList,
logfile.toUtf8().data(), &opts, false);
RENDERDOC_FreeEnvironmentModificationList(envList);
return ret;
QMutexLocker autolock(&m_RemoteLock);
ret = m_Remote->ExecuteAndInject(exe.toUtf8().data(), workingDir.toUtf8().data(),
cmdLine.toUtf8().data(), envList, &opts);
}
/*
else
{
ret = RENDERDOC_ExecuteAndInject(exe.toUtf8().data(), workingDir.toUtf8().data(),
cmdLine.toUtf8().data(), envList, logfile.toUtf8().data(),
&opts, false);
}
*/
RENDERDOC_FreeEnvironmentModificationList(envList);
return ret;
}
void RenderManager::PushInvoke(RenderManager::InvokeHandle *cmd)
@@ -162,17 +360,19 @@ void RenderManager::PushInvoke(RenderManager::InvokeHandle *cmd)
return;
}
m_RenderLock.lock();
QMutexLocker autolock(&m_RenderLock);
m_RenderQueue.push_back(cmd);
m_RenderCondition.wakeAll();
m_RenderLock.unlock();
}
void RenderManager::run()
{
IReplayRenderer *renderer = NULL;
m_CreateStatus = RENDERDOC_CreateReplayRenderer(m_Logfile.toUtf8(), m_Progress, &renderer);
if(m_Remote)
m_CreateStatus = m_Remote->OpenCapture(~0U, m_Logfile.toUtf8().data(), m_Progress, &renderer);
else
m_CreateStatus = RENDERDOC_CreateReplayRenderer(m_Logfile.toUtf8().data(), m_Progress, &renderer);
if(renderer == NULL)
return;
@@ -189,10 +389,9 @@ void RenderManager::run()
// wait for the condition to be woken, grab current queue,
// unlock again.
{
m_RenderLock.lock();
QMutexLocker autolock(&m_RenderLock);
m_RenderCondition.wait(&m_RenderLock, 10);
m_RenderQueue.swap(queue);
m_RenderLock.unlock();
}
// process all the commands
@@ -216,9 +415,10 @@ void RenderManager::run()
{
QQueue<InvokeHandle *> queue;
m_RenderLock.lock();
m_RenderQueue.swap(queue);
m_RenderLock.unlock();
{
QMutexLocker autolock(&m_RenderLock);
m_RenderQueue.swap(queue);
}
for(InvokeHandle *cmd : queue)
{
@@ -233,5 +433,8 @@ void RenderManager::run()
}
// close the core renderer
renderer->Shutdown();
if(m_Remote)
m_Remote->CloseCapture(renderer);
else
renderer->Shutdown();
}
+19
View File
@@ -33,10 +33,12 @@
#include <QWaitCondition>
#include <functional>
#include "QRDUtils.h"
#include "RemoteHost.h"
#include "renderdoc_replay.h"
struct IReplayRenderer;
class LambdaThread;
class RemoteHost;
// simple helper for the common case of 'we just need to run this on the render thread
#define INVOKE_MEMFN(function) \
@@ -119,6 +121,7 @@ class RenderManager
{
public:
typedef std::function<void(IReplayRenderer *)> InvokeMethod;
typedef std::function<void(const char *, const rdctype::array<DirectoryFile> &)> DirectoryBrowseMethod;
RenderManager();
~RenderManager();
@@ -133,10 +136,22 @@ public:
void CloseThread();
ReplayCreateStatus ConnectToRemoteServer(RemoteHost *host);
void DisconnectFromRemoteServer();
void ShutdownServer();
void PingRemote();
const RemoteHost *remote() { return m_RemoteHost; }
uint32_t ExecuteAndInject(const QString &exe, const QString &workingDir, const QString &cmdLine,
const QList<EnvironmentModification> &env, const QString &logfile,
CaptureOptions opts);
QStringList GetRemoteSupport();
void GetHomeFolder(DirectoryBrowseMethod cb);
bool ListFolder(QString path, DirectoryBrowseMethod cb);
QString CopyCaptureToRemote(const QString &localpath, QWidget *window);
void CopyCaptureFromRemote(const QString &remotepath, const QString &localpath, QWidget *window);
private:
struct InvokeHandle
{
@@ -164,6 +179,10 @@ private:
QString m_Logfile;
float *m_Progress;
QMutex m_RemoteLock;
RemoteHost *m_RemoteHost = NULL;
IRemoteServer *m_Remote = NULL;
volatile bool m_Running;
LambdaThread *m_Thread;
ReplayCreateStatus m_CreateStatus;
+1 -1
View File
@@ -371,7 +371,7 @@ void DebugMessageView::messages_contextMenu(const QPoint &pos)
m_ContextMessage = msg;
m_ContextMenu->exec(ui->messages->viewport()->mapToGlobal(pos));
RDDialog::show(m_ContextMenu, ui->messages->viewport()->mapToGlobal(pos));
}
}
+38 -43
View File
@@ -244,9 +244,21 @@ void CaptureDialog::on_exePath_textChanged(const QString &exe)
bool valid = dir.makeAbsolute();
if(valid && f.isAbsolute())
ui->workDirPath->setPlaceholderText(QDir::toNativeSeparators(dir.absolutePath()));
{
QString path = QDir::toNativeSeparators(dir.absolutePath());
// match the path separators from the path
if(exe.count(QChar('/')) > exe.count(QChar('\\')))
path = path.replace('\\', '/');
else
path = path.replace('/', '\\');
ui->workDirPath->setPlaceholderText(path);
}
else if(exe == "")
{
ui->workDirPath->setPlaceholderText("");
}
updateGlobalHook();
}
@@ -279,24 +291,20 @@ void CaptureDialog::on_exePathBrowse_clicked()
file = m_Ctx->Config.LastCaptureExe;
}
// TODO Remote
QAbstractProxyModel *proxy = NULL;
QFileDialog::Options options;
// if(m_Core.Renderer.Remote == null)
if(m_Ctx->Renderer()->remote())
{
QString filename = RDDialog::getExecutableFileName(this, tr("Choose executable"), initDir);
if(filename != "")
setExecutableFilename(filename);
// proxy = new RemoteFileProxy(m_Ctx->Renderer());
options = QFileDialog::DontUseNativeDialog;
}
/*
else
{
VirtualOpenFileDialog remoteBrowser = new VirtualOpenFileDialog(m_Core.Renderer);
remoteBrowser.Opened += new EventHandler<FileOpenedEventArgs>(this.virtualExeBrowser_Opened);
remoteBrowser.ShowDialog(this);
}
*/
QString filename =
RDDialog::getExecutableFileName(this, tr("Choose executable"), initDir, options, proxy);
if(filename != "")
setExecutableFilename(filename);
}
void CaptureDialog::on_workDirBrowse_clicked()
@@ -316,27 +324,20 @@ void CaptureDialog::on_workDirBrowse_clicked()
initDir = m_Ctx->Config.LastCapturePath;
}
// TODO Remote
QAbstractProxyModel *proxy = NULL;
QFileDialog::Options options = QFileDialog::ShowDirsOnly;
// if(m_Core.Renderer.Remote == null)
if(m_Ctx->Renderer()->remote())
{
QString dir = RDDialog::getExistingDirectory(this, "Choose working directory", initDir);
if(dir != "")
ui->workDirPath->setText(dir);
// proxy = new RemoteFileProxy(m_Ctx->Renderer());
options |= QFileDialog::DontUseNativeDialog;
}
/*
else
{
VirtualOpenFileDialog remoteBrowser = new VirtualOpenFileDialog(m_Core.Renderer);
remoteBrowser.Opened += new
EventHandler<FileOpenedEventArgs>(this.virtualWorkDirBrowser_Opened);
remoteBrowser.DirectoryBrowse = true;
QString dir =
RDDialog::getExistingDirectory(this, "Choose working directory", initDir, options, proxy);
remoteBrowser.ShowDialog(this);
}
*/
if(dir != "")
ui->workDirPath->setText(dir);
}
void CaptureDialog::on_envVarEdit_clicked()
@@ -584,10 +585,8 @@ void CaptureDialog::triggerCapture()
{
QString exe = ui->exePath->text();
// TODO Remote
// for non-remote captures, check the executable locally
// if(m_Core.Renderer.Remote == null)
if(!m_Ctx->Renderer()->remote())
{
if(!QFileInfo::exists(exe))
{
@@ -599,20 +598,16 @@ void CaptureDialog::triggerCapture()
QString workingDir = "";
// TODO Remote
// for non-remote captures, check the directory locally
// if(m_Core.Renderer.Remote == null)
if(m_Ctx->Renderer()->remote())
{
workingDir = ui->workDirPath->text();
}
else
{
if(QDir(ui->workDirPath->text()).exists())
workingDir = ui->workDirPath->text();
}
/*
else
{
workingDir = RealWorkDir;
}
*/
QString cmdLine = ui->cmdline->text();
+46 -52
View File
@@ -560,38 +560,35 @@ bool LiveCapture::checkAllowClose()
return false;
}
// TODO Remote
// we either have to save or delete the log. Make sure that if it's remote that we are able
// to by having an active connection or replay context on that host.
/*
if(suppressRemoteWarning == false && (m_Connection == NULL || !m_Connection->Connected()) &&
!log->local &&
(m_Core.Renderer.Remote == null ||
m_Core.Renderer.Remote.Hostname != m_Host ||
!m_Core.Renderer.Remote.Connected)
)
!log->local &&
(m_Ctx->Renderer()->remote() == NULL || m_Ctx->Renderer()->remote()->Hostname != m_Hostname ||
!m_Ctx->Renderer()->remote()->Connected))
{
DialogResult res2 = MessageBox.Show(
String.Format("This capture is on remote host {0} and there is no active replay context on
that host.\n", m_Host) +
"Without an active replay context the capture cannot be " + (res == DialogResult.Yes ?
"saved.\n\n" : "deleted.\n\n") +
"Would you like to continue and discard this capture and any others, to be left in the
temporary folder on the remote machine?",
"No active replay context", MessageBoxButtons.YesNoCancel);
QMessageBox::StandardButton res2 = RDDialog::question(
this, tr("No active replay context"),
tr("This capture is on remote host %1 and there is no active replay context on that "
"host.\n")
.arg(m_Hostname) +
tr("Without an active replay context the capture cannot be %1.\n\n")
.arg(tr(res == QMessageBox::Yes ? "saved" : "deleted")) +
tr("Would you like to continue and discard this capture and any others, to be left "
"in the temporary folder on the remote machine?"),
RDDialog::YesNoCancel);
if(res2 == DialogResult.Yes)
if(res2 == QMessageBox::Yes)
{
suppressRemoteWarning = true;
res = DialogResult.No;
res = QMessageBox::No;
}
else
{
m_IgnoreThreadClosed = false;
return false;
}
}*/
}
if(res == QMessageBox::Yes)
{
@@ -613,22 +610,18 @@ void LiveCapture::openCapture(CaptureLog *log)
{
log->opened = true;
// TODO Remote
/*
if(!log->local &&
(m_Core.Renderer.Remote == null ||
m_Core.Renderer.Remote.Hostname != m_Host ||
!m_Core.Renderer.Remote.Connected)
)
(m_Ctx->Renderer()->remote() == NULL || m_Ctx->Renderer()->remote()->Hostname != m_Hostname ||
!m_Ctx->Renderer()->remote()->Connected))
{
RDDialog::critical(this,
tr("No active replay context"),
tr("This capture is on remote host %1 and there is no active replay context on that host.\n" \
"You can either save the log locally, or switch to a replay context on
%1.").arg(m_Hostname));
RDDialog::critical(
this, tr("No active replay context"),
tr("This capture is on remote host %1 and there is no active replay context on that "
"host.\n") +
tr("You can either save the log locally, or switch to a replay context on %1.")
.arg(m_Hostname));
return;
}
*/
m_Main->LoadLogfile(log->path, !log->saved, log->local);
}
@@ -672,24 +665,28 @@ bool LiveCapture::saveCapture(CaptureLog *log)
}
else
{
// TODO Remote
/*
if(m_Core.Renderer.Remote == null ||
m_Core.Renderer.Remote.Hostname != m_Host ||
!m_Core.Renderer.Remote.Connected)
if(m_Ctx->Renderer()->remote() == NULL || m_Ctx->Renderer()->remote()->Hostname != m_Hostname ||
!m_Ctx->Renderer()->remote()->Connected)
{
MessageBox.Show(
String.Format("This capture is on remote host {0} and there is no active replay context on
that host.\n" +
"Without an active replay context the capture cannot be saved, try switching to a replay
context on {0}.\n\n", m_Host),
"No active replay context", MessageBoxButtons.OK);
RDDialog::critical(this, tr("No active replay context"),
tr("This capture is on remote host %1 and there is no active replay "
"context on that host.\n") +
tr("Without an active replay context the capture cannot be saved, "
"try switching to a replay context on %1.")
.arg(m_Hostname));
return false;
}
m_Core.Renderer.CopyCaptureFromRemote(log.path, path, this);
m_Core.Renderer.DeleteCapture(log.path, false);
*/
m_Ctx->Renderer()->CopyCaptureFromRemote(log->path, path, this);
if(!QFile::exists(path))
{
RDDialog::critical(this, tr("Cannot save"),
tr("File couldn't be transferred from remote host"));
return false;
}
m_Ctx->Renderer()->DeleteCapture(log->path, false);
}
log->saved = true;
@@ -874,18 +871,15 @@ void LiveCapture::connectionClosed()
{
CaptureLog *log = GetLog(ui->captures->item(0));
// TODO Remote
// only auto-open a non-local log if we are successfully connected
// to this machine as a remote context
/*
if(!log->local)
{
if(m_Core.Renderer.Remote == null ||
m_Host != m_Core.Renderer.Remote.Hostname ||
!m_Core.Renderer.Remote.Connected)
if(m_Ctx->Renderer()->remote() == NULL ||
m_Ctx->Renderer()->remote()->Hostname != m_Hostname ||
!m_Ctx->Renderer()->remote()->Connected)
return;
}*/
}
if(log->opened)
return;
+321 -81
View File
@@ -28,6 +28,7 @@
#include <QMimeData>
#include <QProgressBar>
#include <QProgressDialog>
#include <QToolButton>
#include "Code/CaptureContext.h"
#include "Code/QRDUtils.h"
#include "PipelineState/PipelineStateViewer.h"
@@ -37,6 +38,7 @@
#include "Windows/Dialogs/CaptureDialog.h"
#include "Windows/Dialogs/LiveCapture.h"
#include "Windows/Dialogs/SettingsDialog.h"
#include "Windows/Dialogs/SuggestRemoteDialog.h"
#include "APIInspector.h"
#include "BufferViewer.h"
#include "ConstantBufferPreviewer.h"
@@ -100,6 +102,24 @@ MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::Main
QObject::connect(ui->action_Save_Layout_6, &QAction::triggered, this,
&MainWindow::saveLayout_triggered);
contextChooserMenu = new QMenu(this);
FillRemotesMenu(contextChooserMenu, true);
contextChooser = new QToolButton(this);
contextChooser->setText(tr("Replay Context: %1").arg("Local"));
contextChooser->setIcon(QIcon(QPixmap(QString::fromUtf8(":/Resources/house.png"))));
contextChooser->setAutoRaise(true);
contextChooser->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
contextChooser->setPopupMode(QToolButton::InstantPopup);
contextChooser->setMenu(contextChooserMenu);
contextChooser->setStyleSheet("QToolButton::menu-indicator { image: none; }");
contextChooser->setContextMenuPolicy(Qt::DefaultContextMenu);
QObject::connect(contextChooserMenu, &QMenu::aboutToShow, this,
&MainWindow::contextChooser_menuShowing);
ui->statusBar->addWidget(contextChooser);
statusIcon = new RDLabel(this);
ui->statusBar->addWidget(statusIcon);
@@ -126,6 +146,11 @@ MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::Main
m_MessageTick.setInterval(500);
m_MessageTick.start();
QObject::connect(&m_RemoteProbe, &QTimer::timeout, this, &MainWindow::remoteProbe);
m_RemoteProbe.setSingleShot(false);
m_RemoteProbe.setInterval(7500);
m_RemoteProbe.start();
ui->statusBar->setStyleSheet("QStatusBar::item { border: 0px }");
SetTitle();
@@ -143,6 +168,14 @@ MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::Main
bool loaded = LoadLayout(0);
LambdaThread *th = new LambdaThread([this]() {
m_Ctx->Config.AddAndroidHosts();
for(RemoteHost *host : m_Ctx->Config.RemoteHosts)
host->CheckStatus();
});
th->selfDelete(true);
th->start();
// create default layout if layout failed to load
if(!loaded)
{
@@ -265,9 +298,9 @@ LiveCapture *MainWindow::OnCaptureTrigger(const QString &exe, const QString &wor
return NULL;
}
// TODO Remote
// m_Core.Renderer.Remote == NULL ? "" : m_Core.Renderer.Remote.Hostname
LiveCapture *live = new LiveCapture(m_Ctx, "", ret, this, this);
LiveCapture *live = new LiveCapture(
m_Ctx, m_Ctx->Renderer()->remote() ? m_Ctx->Renderer()->remote()->Hostname : "", ret, this,
this);
ShowLiveCapture(live);
return live;
}
@@ -315,36 +348,32 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local
rdctype::str machineIdent;
ReplaySupport support = eReplaySupport_Unsupported;
// TODO Remote
bool remoteReplay =
!local /*|| (m_Core.Renderer.Remote != null && m_Core.Renderer.Remote.Connected)*/;
!local || (m_Ctx->Renderer()->remote() && m_Ctx->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.
// 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);
SuggestRemoteDialog dialog(ToQStr(driver), ToQStr(machineIdent), this);
FillRemotesToolStrip(dialog.RemoteItems, false);
FillRemotesMenu(dialog.remotesMenu(), false);
dialog.ShowDialog();
dialog.remotesAdded();
if(dialog.Result == Dialogs.SuggestRemoteDialog.SuggestRemoteResult.Cancel)
RDDialog::show(&dialog);
if(dialog.choice() == SuggestRemoteDialog::Cancel)
{
return;
}
else if(dialog.Result == Dialogs.SuggestRemoteDialog.SuggestRemoteResult.Remote)
else if(dialog.choice() == SuggestRemoteDialog::Remote)
{
// we only get back here from the dialog once the context switch has begun,
// so contextChooser will have been disabled.
@@ -352,27 +381,21 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local
// it has finished already. Otherwise pop up a waiting dialog until it completes
// one way or another, then process the result.
if(!contextChooser.Enabled)
if(!contextChooser->isEnabled())
{
ProgressPopup modal = new ProgressPopup((ModalCloseCallback)delegate
{
return contextChooser.Enabled;
}, false);
modal.SetModalText("Please Wait - Checking remote connection...");
modal.ShowDialog();
ShowProgressDialog(this, tr("Please Wait - Checking remote connection..."),
[this]() { return contextChooser->isEnabled(); });
}
remoteReplay = (m_Core.Renderer.Remote != null && m_Core.Renderer.Remote.Connected);
remoteReplay = (m_Ctx->Renderer()->remote() && m_Ctx->Renderer()->remote()->Connected);
if(!remoteReplay)
{
string remoteMessage = "Failed to make a connection to the remote server.\n\n";
QString remoteMessage = tr("Failed to make a connection to the remote server.\n\n");
remoteMessage += "More information may be available in the status bar.";
remoteMessage += tr("More information may be available in the status bar.");
MessageBox.Show(remoteMessage, "Couldn't connect to remote server",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
RDDialog::information(this, tr("Couldn't connect to remote server"), remoteMessage);
return;
}
}
@@ -381,14 +404,13 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local
// 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)
if(dialog.alwaysReplayLocally())
{
m_Core.Config.AlwaysReplayLocally = true;
m_Ctx->Config.AlwaysReplayLocally = true;
m_Ctx->Config.Save();
}
}
*/
}
if(remoteReplay)
@@ -436,34 +458,24 @@ void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local
}
else
{
QString fileToLoad = filename;
if(remoteReplay && local)
{
RDDialog::critical(NULL, tr("Not Implemented"), tr("Not Implemented"));
return;
fileToLoad = m_Ctx->Renderer()->CopyCaptureToRemote(filename, this);
// TODO Remote
/*
try
// deliberately leave local as true so that we keep referring to the locally saved log
// some error
if(fileToLoad == "")
{
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);
RDDialog::critical(NULL, tr("Error copying to remote"),
tr("Couldn't copy %1 to remote host for replaying").arg(filename));
return;
}
*/
}
m_Ctx->LoadLogfile(filename, origFilename, temporary, local);
m_Ctx->LoadLogfile(fileToLoad, origFilename, temporary, local);
}
if(!remoteReplay)
@@ -529,6 +541,7 @@ bool MainWindow::PromptSaveLog()
}
bool success = false;
QString error;
if(m_Ctx->IsLogLocal())
{
@@ -536,16 +549,20 @@ bool MainWindow::PromptSaveLog()
// 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);
error = tr("Couldn't save to %1").arg(saveFilename);
}
else
{
// TODO Remote
// m_Core.Renderer.CopyCaptureFromRemote(m_Core.LogFileName, saveFilename, this);
m_Ctx->Renderer()->CopyCaptureFromRemote(m_Ctx->LogFilename(), saveFilename, this);
success = QFile::exists(saveFilename);
error = tr("File couldn't be transferred from remote host");
}
if(!success)
{
RDDialog::critical(NULL, tr("File not found"), tr("Couldn't save to %1").arg(saveFilename));
RDDialog::critical(NULL, tr("Error Saving"), error);
return false;
}
@@ -623,7 +640,7 @@ void MainWindow::SetTitle(const QString &filename)
{
QString prefix = "";
if(m_Ctx != NULL && m_Ctx->LogLoaded())
if(m_Ctx && m_Ctx->LogLoaded())
{
prefix = QFileInfo(filename).fileName();
if(m_Ctx->APIProps().degraded)
@@ -631,9 +648,8 @@ void MainWindow::SetTitle(const QString &filename)
prefix += " - ";
}
// TODO Remote
// if(m_Ctx != NULL && m_Ctx->Renderer.Remote != null)
// prefix += String.Format("Remote: {0} - ", m_Core.Renderer.Remote.Hostname);
if(m_Ctx && m_Ctx->Renderer()->remote())
prefix += tr("Remote: %1 - ").arg(m_Ctx->Renderer()->remote()->Hostname);
QString text = prefix + "RenderDoc ";
@@ -835,6 +851,21 @@ void MainWindow::setLogHasErrors(bool errors)
}
}
void MainWindow::remoteProbe()
{
if(!m_Ctx->LogLoaded() && !m_Ctx->LogLoading())
{
for(RemoteHost *host : m_Ctx->Config.RemoteHosts)
{
// don't mess with a host we're connected to - this is handled anyway
if(host->Connected)
continue;
host->CheckStatus();
}
}
}
void MainWindow::messageCheck()
{
if(m_Ctx->LogLoaded())
@@ -845,17 +876,15 @@ void MainWindow::messageCheck()
bool disconnected = false;
// TODO Remote
/*
if(me.m_Core.Renderer.Remote != null)
if(m_Ctx->Renderer()->remote())
{
bool prev = me.m_Core.Renderer.Remote.ServerRunning;
bool prev = m_Ctx->Renderer()->remote()->ServerRunning;
me.m_Core.Renderer.PingRemote();
m_Ctx->Renderer()->PingRemote();
if(prev != me.m_Core.Renderer.Remote.ServerRunning)
if(prev != m_Ctx->Renderer()->remote()->ServerRunning)
disconnected = true;
}*/
}
GUIInvoke::call([this, disconnected, msgs] {
// if we just got disconnected while replaying a log, alert the user.
@@ -868,9 +897,8 @@ void MainWindow::messageCheck()
"RenderDoc to reconnect and load the capture again"));
}
// TODO Remote
// if(me.m_Core.Renderer.Remote != null && !me.m_Core.Renderer.Remote.ServerRunning)
// me.contextChooser.Image = global::renderdocui.Properties.Resources.cross;
if(m_Ctx->Renderer()->remote() && !m_Ctx->Renderer()->remote()->ServerRunning)
contextChooser->setIcon(QIcon(QPixmap(QString::fromUtf8(":/Resources/cross.png"))));
if(!msgs.empty())
{
@@ -887,6 +915,217 @@ void MainWindow::messageCheck()
});
});
}
else if(!m_Ctx->LogLoaded() && !m_Ctx->LogLoading())
{
if(m_Ctx->Renderer()->remote())
m_Ctx->Renderer()->PingRemote();
GUIInvoke::call([this]() {
if(m_Ctx->Renderer()->remote() && !m_Ctx->Renderer()->remote()->ServerRunning)
{
contextChooser->setIcon(QIcon(QPixmap(QString::fromUtf8(":/Resources/cross.png"))));
contextChooser->setText(tr("Replay Context: %1").arg("Local"));
statusText->setText(
tr("Remote server disconnected. To attempt to reconnect please select it again."));
m_Ctx->Renderer()->DisconnectFromRemoteServer();
}
});
}
}
void MainWindow::FillRemotesMenu(QMenu *menu, bool includeLocalhost)
{
menu->clear();
QIcon tick(QPixmap(QString::fromUtf8(":/Resources/tick.png")));
QIcon cross(QPixmap(QString::fromUtf8(":/Resources/cross.png")));
for(int i = 0; i < m_Ctx->Config.RemoteHosts.count(); i++)
{
RemoteHost *host = m_Ctx->Config.RemoteHosts[i];
// add localhost at the end
if(host->Hostname == "localhost")
continue;
QAction *action = new QAction(menu);
action->setIcon(host->ServerRunning && !host->VersionMismatch ? tick : cross);
if(host->Connected)
action->setText(tr("%1 (Connected)").arg(host->Hostname));
else if(host->ServerRunning && host->VersionMismatch)
action->setText(tr("%1 (Bad Version)").arg(host->Hostname));
else if(host->ServerRunning && host->Busy)
action->setText(tr("%1 (Busy)").arg(host->Hostname));
else if(host->ServerRunning)
action->setText(tr("%1 (Online)").arg(host->Hostname));
else
action->setText(tr("%1 (Offline)").arg(host->Hostname));
QObject::connect(action, &QAction::triggered, this, &MainWindow::switchContext);
action->setData(i);
// don't allow switching to the connected host
if(host->Connected)
action->setEnabled(false);
menu->addAction(action);
}
if(includeLocalhost)
{
QAction *localContext = new QAction(menu);
localContext->setText("Local");
localContext->setIcon(QIcon(QPixmap(QString::fromUtf8(":/Resources/house.png"))));
QObject::connect(localContext, &QAction::triggered, this, &MainWindow::switchContext);
localContext->setData(-1);
menu->addAction(localContext);
}
}
void MainWindow::switchContext()
{
QAction *item = qobject_cast<QAction *>(QObject::sender());
if(!item)
return;
bool ok = false;
int hostIdx = item->data().toInt(&ok);
if(!ok)
return;
RemoteHost *host = NULL;
if(hostIdx >= 0 && hostIdx < m_Ctx->Config.RemoteHosts.count())
{
host = m_Ctx->Config.RemoteHosts[hostIdx];
}
for(LiveCapture *live : m_LiveCaptures)
{
// allow live captures to this host to stay open, that way
// we can connect to a live capture, then switch into that
// context
if(host && live->hostname() == host->Hostname)
continue;
if(!live->checkAllowClose())
return;
}
if(!PromptCloseLog())
return;
for(LiveCapture *live : m_LiveCaptures)
{
// allow live captures to this host to stay open, that way
// we can connect to a live capture, then switch into that
// context
if(host && live->hostname() == host->Hostname)
continue;
live->cleanItems();
live->close();
}
m_Ctx->Renderer()->DisconnectFromRemoteServer();
if(!host)
{
contextChooser->setIcon(QIcon(QPixmap(QString::fromUtf8(":/Resources/house.png"))));
contextChooser->setText(tr("Replay Context: %1").arg("Local"));
ui->action_Inject_into_Process->setEnabled(true);
statusText->setText("");
SetTitle();
}
else
{
contextChooser->setText(tr("Replay Context: %1").arg(host->Hostname));
contextChooser->setIcon(host->ServerRunning
? QIcon(QPixmap(QString::fromUtf8(":/Resources/connect.png")))
: QIcon(QPixmap(QString::fromUtf8(":/Resources/disconnect.png"))));
// disable until checking is done
contextChooser->setEnabled(false);
ui->action_Inject_into_Process->setEnabled(false);
SetTitle();
statusText->setText(tr("Checking remote server status..."));
LambdaThread *th = new LambdaThread([this, host]() {
// see if the server is up
host->CheckStatus();
if(!host->ServerRunning && host->RunCommand != "")
{
GUIInvoke::call([this]() { statusText->setText(tr("Running remote server command...")); });
host->Launch();
// check if it's running now
host->CheckStatus();
}
ReplayCreateStatus status = eReplayCreate_Success;
if(host->ServerRunning && !host->Busy)
{
status = m_Ctx->Renderer()->ConnectToRemoteServer(host);
}
GUIInvoke::call([this, host, status]() {
contextChooser->setIcon(
host->ServerRunning && !host->Busy
? QIcon(QPixmap(QString::fromUtf8(":/Resources/connect.png")))
: QIcon(QPixmap(QString::fromUtf8(":/Resources/disconnect.png"))));
if(status != eReplayCreate_Success)
{
contextChooser->setIcon(QIcon(QPixmap(QString::fromUtf8(":/Resources/cross.png"))));
contextChooser->setText(tr("Replay Context: %1").arg("Local"));
statusText->setText(tr("Connection failed: %1").arg(ToQStr(status)));
}
else if(host->VersionMismatch)
{
statusText->setText(tr("Remote server is not running RenderDoc %1").arg(Version::string()));
}
else if(host->Busy)
{
statusText->setText(tr("Remote server in use elsewhere"));
}
else if(host->ServerRunning)
{
statusText->setText(tr("Remote server ready"));
}
else
{
if(host->RunCommand != "")
statusText->setText(tr("Remote server not running or failed to start"));
else
statusText->setText(tr("Remote server not running - no start command configured"));
}
contextChooser->setEnabled(true);
});
});
th->selfDelete(true);
th->start();
}
}
void MainWindow::contextChooser_menuShowing()
{
FillRemotesMenu(contextChooserMenu, true);
}
void MainWindow::statusDoubleClicked()
@@ -896,9 +1135,8 @@ void MainWindow::statusDoubleClicked()
void MainWindow::OnLogfileLoaded()
{
// TODO Remote
// don't allow changing context while log is open
// contextChooser.Enabled = false;
contextChooser->setEnabled(false);
statusProgress->setVisible(false);
@@ -925,8 +1163,7 @@ void MainWindow::OnLogfileLoaded()
void MainWindow::OnLogfileClosed()
{
// TODO Remote
// contextChooser.Enabled = true;
contextChooser->setEnabled(true);
statusText->setText("");
statusIcon->setPixmap(QPixmap());
@@ -938,15 +1175,13 @@ void MainWindow::OnLogfileClosed()
SetTitle();
// if the remote sever disconnected during log replay, resort back to a 'disconnected' state
// TODO Remote
/*
if(m_Core.Renderer.Remote != null && !m_Core.Renderer.Remote.ServerRunning)
if(m_Ctx->Renderer()->remote() && !m_Ctx->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();
statusText->setText(
tr("Remote server disconnected. To attempt to reconnect please select it again."));
contextChooser->setText(tr("Replay Context: %1").arg("Local"));
m_Ctx->Renderer()->DisconnectFromRemoteServer();
}
*/
}
void MainWindow::OnEventChanged(uint32_t eventID)
@@ -1091,6 +1326,11 @@ void MainWindow::on_action_Resolve_Symbols_triggered()
m_Ctx->apiInspector()->on_apiEvents_itemSelectionChanged();
}
void MainWindow::on_action_Start_Android_Remote_Server_triggered()
{
RENDERDOC_StartAndroidRemoteServer();
}
void MainWindow::on_action_Settings_triggered()
{
SettingsDialog about(m_Ctx, this);
+10
View File
@@ -38,6 +38,7 @@ class MainWindow;
class RDLabel;
class QMimeData;
class QProgressBar;
class QToolButton;
class CaptureDialog;
class LiveCapture;
@@ -97,13 +98,17 @@ private slots:
void on_action_Statistics_Viewer_triggered();
void on_action_Inject_into_Process_triggered();
void on_action_Resolve_Symbols_triggered();
void on_action_Start_Android_Remote_Server_triggered();
void on_action_Settings_triggered();
// manual slots
void saveLayout_triggered();
void loadLayout_triggered();
void messageCheck();
void remoteProbe();
void statusDoubleClicked();
void switchContext();
void contextChooser_menuShowing();
private:
void closeEvent(QCloseEvent *event) override;
@@ -124,8 +129,11 @@ private:
RDLabel *statusIcon;
RDLabel *statusText;
QProgressBar *statusProgress;
QMenu *contextChooserMenu;
QToolButton *contextChooser;
QTimer m_MessageTick;
QTimer m_RemoteProbe;
bool m_messageAlternate = false;
@@ -155,4 +163,6 @@ private:
void LoadSaveLayout(QAction *action, bool save);
bool LoadLayout(int layout);
bool SaveLayout(int layout);
void FillRemotesMenu(QMenu *menu, bool includeLocalhost);
};