Don't launch blocking execute call on UI thread, pop up progress ticker

* This most commonly happens launching an Android program that takes a
  while to launch, or if you're launching a program with the delay for
  debugger option set.
* Instead of the whole UI hanging, you'll get a progress dialog to
  appear while it's waiting.
This commit is contained in:
baldurk
2017-02-24 17:34:02 +00:00
parent 0ff1ff0d03
commit f76a4cc339
7 changed files with 172 additions and 92 deletions
+6 -3
View File
@@ -685,11 +685,14 @@ CaptureDialog *CaptureContext::captureDialog()
m_CaptureDialog = new CaptureDialog(
*this,
[this](const QString &exe, const QString &workingDir, const QString &cmdLine,
const QList<EnvironmentModification> &env, CaptureOptions opts) {
return m_MainWindow->OnCaptureTrigger(exe, workingDir, cmdLine, env, opts);
const QList<EnvironmentModification> &env, CaptureOptions opts,
std::function<void(LiveCapture *)> callback) {
return m_MainWindow->OnCaptureTrigger(exe, workingDir, cmdLine, env, opts, callback);
},
[this](uint32_t PID, const QList<EnvironmentModification> &env, const QString &name,
CaptureOptions opts) { return m_MainWindow->OnInjectTrigger(PID, env, name, opts); },
CaptureOptions opts, std::function<void(LiveCapture *)> callback) {
return m_MainWindow->OnInjectTrigger(PID, env, name, opts, callback);
},
m_MainWindow);
m_CaptureDialog->setObjectName("capDialog");
m_CaptureDialog->setWindowIcon(*m_Icon);
+10 -9
View File
@@ -745,10 +745,11 @@ void CaptureDialog::triggerCapture()
QString name = m_ProcessModel->data(m_ProcessModel->index(item.row(), 0)).toString();
uint32_t PID = m_ProcessModel->data(m_ProcessModel->index(item.row(), 1)).toUInt();
LiveCapture *live = m_InjectCallback(PID, settings().Environment, name, settings().Options);
if(ui->queueFrameCap->isChecked() && live != NULL)
live->QueueCapture((int)ui->queuedFrame->value());
m_InjectCallback(PID, settings().Environment, name, settings().Options,
[this](LiveCapture *live) {
if(ui->queueFrameCap->isChecked())
live->QueueCapture((int)ui->queuedFrame->value());
});
}
}
else
@@ -781,10 +782,10 @@ void CaptureDialog::triggerCapture()
QString cmdLine = ui->cmdline->text();
LiveCapture *live =
m_CaptureCallback(exe, workingDir, cmdLine, settings().Environment, settings().Options);
if(ui->queueFrameCap->isChecked() && live != NULL)
live->QueueCapture((int)ui->queuedFrame->value());
m_CaptureCallback(exe, workingDir, cmdLine, settings().Environment, settings().Options,
[this](LiveCapture *live) {
if(ui->queueFrameCap->isChecked())
live->QueueCapture((int)ui->queuedFrame->value());
});
}
}
+5 -4
View File
@@ -57,11 +57,12 @@ class CaptureDialog : public QFrame
Q_OBJECT
public:
typedef std::function<LiveCapture *(const QString &exe, const QString &workingDir, const QString &cmdLine,
const QList<EnvironmentModification> &env, CaptureOptions opts)>
typedef std::function<void(const QString &exe, const QString &workingDir, const QString &cmdLine,
const QList<EnvironmentModification> &env, CaptureOptions opts,
std::function<void(LiveCapture *)> callback)>
OnCaptureMethod;
typedef std::function<LiveCapture *(uint32_t PID, const QList<EnvironmentModification> &env,
const QString &name, CaptureOptions opts)>
typedef std::function<void(uint32_t PID, const QList<EnvironmentModification> &env, const QString &name,
CaptureOptions opts, std::function<void(LiveCapture *)> callback)>
OnInjectMethod;
explicit CaptureDialog(CaptureContext &ctx, OnCaptureMethod captureCallback,
+66 -38
View File
@@ -324,63 +324,91 @@ void MainWindow::LoadFromFilename(const QString &filename)
}
}
LiveCapture *MainWindow::OnCaptureTrigger(const QString &exe, const QString &workingDir,
const QString &cmdLine,
const QList<EnvironmentModification> &env,
CaptureOptions opts)
void MainWindow::OnCaptureTrigger(const QString &exe, const QString &workingDir,
const QString &cmdLine, const QList<EnvironmentModification> &env,
CaptureOptions opts, std::function<void(LiveCapture *)> callback)
{
if(!PromptCloseLog())
return NULL;
return;
QString logfile = m_Ctx.TempLogFilename(QFileInfo(exe).baseName());
LambdaThread *th = new LambdaThread([this, exe, workingDir, cmdLine, env, opts, callback]() {
QString logfile = m_Ctx.TempLogFilename(QFileInfo(exe).baseName());
uint32_t ret = m_Ctx.Renderer().ExecuteAndInject(exe, workingDir, cmdLine, env, logfile, opts);
uint32_t ret = m_Ctx.Renderer().ExecuteAndInject(exe, workingDir, cmdLine, env, logfile, opts);
if(ret == 0)
GUIInvoke::call([this, exe, ret, callback]() {
if(ret == 0)
{
RDDialog::critical(this, tr("Error kicking capture"),
tr("Error launching %1 for capture.\n\nCheck diagnostic log in Help "
"menu for more details.")
.arg(exe));
return;
}
LiveCapture *live = new LiveCapture(
m_Ctx, m_Ctx.Renderer().remote() ? m_Ctx.Renderer().remote()->Hostname : "", ret, this,
this);
ShowLiveCapture(live);
callback(live);
});
});
th->start();
// wait a few ms before popping up a progress bar
th->wait(500);
if(th->isRunning())
{
RDDialog::critical(
this, tr("Error kicking capture"),
tr("Error launching %1 for capture.\n\nCheck diagnostic log in Help menu for more details.")
.arg(exe));
return NULL;
ShowProgressDialog(this, tr("Launching %1, please wait...").arg(exe),
[th]() { return !th->isRunning(); });
}
LiveCapture *live = new LiveCapture(
m_Ctx, m_Ctx.Renderer().remote() ? m_Ctx.Renderer().remote()->Hostname : "", ret, this, this);
ShowLiveCapture(live);
return live;
th->deleteLater();
}
LiveCapture *MainWindow::OnInjectTrigger(uint32_t PID, const QList<EnvironmentModification> &env,
const QString &name, CaptureOptions opts)
void MainWindow::OnInjectTrigger(uint32_t PID, const QList<EnvironmentModification> &env,
const QString &name, CaptureOptions opts,
std::function<void(LiveCapture *)> callback)
{
if(!PromptCloseLog())
return NULL;
return;
QString logfile = m_Ctx.TempLogFilename(name);
LambdaThread *th = new LambdaThread([this, PID, env, name, opts, callback]() {
QString logfile = m_Ctx.TempLogFilename(name);
void *envList = RENDERDOC_MakeEnvironmentModificationList(env.size());
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);
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_InjectIntoProcess(PID, envList, logfile.toUtf8().data(), &opts, false);
uint32_t ret = RENDERDOC_InjectIntoProcess(PID, envList, logfile.toUtf8().data(), &opts, false);
RENDERDOC_FreeEnvironmentModificationList(envList);
RENDERDOC_FreeEnvironmentModificationList(envList);
if(ret == 0)
GUIInvoke::call([this, PID, ret, callback]() {
if(ret == 0)
{
RDDialog::critical(
this, tr("Error kicking capture"),
tr("Error injecting into process %1 for capture.\n\nCheck diagnostic log in "
"Help menu for more details.")
.arg(PID));
return;
}
LiveCapture *live = new LiveCapture(m_Ctx, "", ret, this, this);
ShowLiveCapture(live);
});
});
th->start();
// wait a few ms before popping up a progress bar
th->wait(500);
if(th->isRunning())
{
RDDialog::critical(this, tr("Error kicking capture"),
tr("Error injecting into process %1 for capture.\n\nCheck diagnostic log in "
"Help menu for more details.")
.arg(PID));
return NULL;
ShowProgressDialog(this, tr("Injecting into %1, please wait...").arg(PID),
[th]() { return !th->isRunning(); });
}
LiveCapture *live = new LiveCapture(m_Ctx, "", ret, this, this);
ShowLiveCapture(live);
return live;
th->deleteLater();
}
void MainWindow::LoadLogfile(const QString &filename, bool temporary, bool local)
+5 -4
View File
@@ -63,10 +63,11 @@ public:
void CloseLogfile();
QString GetSavePath();
LiveCapture *OnCaptureTrigger(const QString &exe, const QString &workingDir, const QString &cmdLine,
const QList<EnvironmentModification> &env, CaptureOptions opts);
LiveCapture *OnInjectTrigger(uint32_t PID, const QList<EnvironmentModification> &env,
const QString &name, CaptureOptions opts);
void OnCaptureTrigger(const QString &exe, const QString &workingDir, const QString &cmdLine,
const QList<EnvironmentModification> &env, CaptureOptions opts,
std::function<void(LiveCapture *)> callback);
void OnInjectTrigger(uint32_t PID, const QList<EnvironmentModification> &env, const QString &name,
CaptureOptions opts, std::function<void(LiveCapture *)> callback);
void PopulateRecentFiles();
+14 -10
View File
@@ -259,8 +259,10 @@ namespace renderdocui.Windows.Dialogs
#region Callbacks
public delegate LiveCapture OnCaptureMethod(string exe, string workingDir, string cmdLine, EnvironmentModification[] env, CaptureOptions opts);
public delegate LiveCapture OnInjectMethod(UInt32 PID, EnvironmentModification[] env, string name, CaptureOptions opts);
public delegate void OnConnectionEstablishedMethod(LiveCapture live);
public delegate void OnCaptureMethod(string exe, string workingDir, string cmdLine, EnvironmentModification[] env, CaptureOptions opts, OnConnectionEstablishedMethod cb);
public delegate void OnInjectMethod(UInt32 PID, EnvironmentModification[] env, string name, CaptureOptions opts, OnConnectionEstablishedMethod cb);
private OnCaptureMethod m_CaptureCallback = null;
private OnInjectMethod m_InjectCallback = null;
@@ -430,10 +432,11 @@ namespace renderdocui.Windows.Dialogs
string cmdLine = cmdline.Text;
var live = m_CaptureCallback(exe, workingDir, cmdLine, GetSettings().Environment, GetSettings().Options);
if (queueFrameCap.Checked && live != null)
live.QueueCapture((int)queuedCapFrame.Value);
m_CaptureCallback(exe, workingDir, cmdLine, GetSettings().Environment, GetSettings().Options, (LiveCapture live) =>
{
if (queueFrameCap.Checked)
live.QueueCapture((int)queuedCapFrame.Value);
});
}
private void OnInject()
@@ -445,10 +448,11 @@ namespace renderdocui.Windows.Dialogs
string name = item.SubItems[1].Text;
UInt32 PID = (UInt32)item.Tag;
var live = m_InjectCallback(PID, GetSettings().Environment, name, GetSettings().Options);
if (queueFrameCap.Checked && live != null)
live.QueueCapture((int)queuedCapFrame.Value);
m_InjectCallback(PID, GetSettings().Environment, name, GetSettings().Options, (LiveCapture live) =>
{
if (queueFrameCap.Checked)
live.QueueCapture((int)queuedCapFrame.Value);
});
}
}
+66 -24
View File
@@ -1019,48 +1019,90 @@ namespace renderdocui.Windows
return "";
}
private LiveCapture OnCaptureTrigger(string exe, string workingDir, string cmdLine, EnvironmentModification[] env, CaptureOptions opts)
private void OnCaptureTrigger(string exe, string workingDir, string cmdLine, EnvironmentModification[] env, CaptureOptions opts, Dialogs.CaptureDialog.OnConnectionEstablishedMethod callback)
{
if (!PromptCloseLog())
return null;
return;
string logfile = m_Core.TempLogFilename(Path.GetFileNameWithoutExtension(exe));
StaticExports.SetConfigSetting("MaxConnectTimeout", m_Core.Config.MaxConnectTimeout.ToString());
UInt32 ret = m_Core.Renderer.ExecuteAndInject(exe, workingDir, cmdLine, env, logfile, opts);
if (ret == 0)
Thread th = Helpers.NewThread(new ThreadStart(() =>
{
MessageBox.Show(string.Format("Error launching {0} for capture.\n\nCheck diagnostic log in Help menu for more details.", exe),
"Error kicking capture", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
UInt32 ret = m_Core.Renderer.ExecuteAndInject(exe, workingDir, cmdLine, env, logfile, opts);
var live = new LiveCapture(m_Core, m_Core.Renderer.Remote == null ? "" : m_Core.Renderer.Remote.Hostname, ret, this);
ShowLiveCapture(live);
return live;
this.BeginInvoke(new Action(() =>
{
if (ret == 0)
{
MessageBox.Show(string.Format("Error launching {0} for capture.\n\nCheck diagnostic log in Help menu for more details.", exe),
"Error kicking capture", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var live = new LiveCapture(m_Core, m_Core.Renderer.Remote == null ? "" : m_Core.Renderer.Remote.Hostname, ret, this);
ShowLiveCapture(live);
callback(live);
}));
}));
th.Start();
// wait a few ms before popping up a progress bar
th.Join(500);
if (th.IsAlive)
{
ProgressPopup modal = new ProgressPopup((ModalCloseCallback)delegate
{
return !th.IsAlive;
}, false);
modal.SetModalText(String.Format("Launching {0}, please wait...", exe));
modal.ShowDialog();
}
}
private LiveCapture OnInjectTrigger(UInt32 PID, EnvironmentModification[] env, string name, CaptureOptions opts)
private void OnInjectTrigger(UInt32 PID, EnvironmentModification[] env, string name, CaptureOptions opts, Dialogs.CaptureDialog.OnConnectionEstablishedMethod callback)
{
if (!PromptCloseLog())
return null;
return;
string logfile = m_Core.TempLogFilename(name);
UInt32 ret = StaticExports.InjectIntoProcess(PID, env, logfile, opts);
if (ret == 0)
Thread th = Helpers.NewThread(new ThreadStart(() =>
{
MessageBox.Show(string.Format("Error injecting into process {0} for capture.\n\nCheck diagnostic log in Help menu for more details.", PID),
"Error kicking capture", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
UInt32 ret = StaticExports.InjectIntoProcess(PID, env, logfile, opts);
var live = new LiveCapture(m_Core, "", ret, this);
ShowLiveCapture(live);
return live;
this.BeginInvoke(new Action(() =>
{
if (ret == 0)
{
MessageBox.Show(string.Format("Error injecting into process {0} for capture.\n\nCheck diagnostic log in Help menu for more details.", PID),
"Error kicking capture", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var live = new LiveCapture(m_Core, m_Core.Renderer.Remote == null ? "" : m_Core.Renderer.Remote.Hostname, ret, this);
ShowLiveCapture(live);
callback(live);
}));
}));
th.Start();
// wait a few ms before popping up a progress bar
th.Join(500);
if (th.IsAlive)
{
ProgressPopup modal = new ProgressPopup((ModalCloseCallback)delegate
{
return !th.IsAlive;
}, false);
modal.SetModalText(String.Format("Injecting into {0}, please wait...", PID));
modal.ShowDialog();
}
}
private void captureLogToolStripMenuItem_Click(object sender, EventArgs e)