From 806876c5402f4ef42ef359046afc9dead2adc447 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 27 Jul 2017 15:18:54 +0100 Subject: [PATCH] Add a menu item to do nothing but replay the capture in a tight loop. --- qrenderdoc/Code/Interface/QRDInterface.h | 3 + qrenderdoc/Code/ReplayManager.cpp | 19 +++--- qrenderdoc/Code/ReplayManager.h | 4 ++ qrenderdoc/Windows/MainWindow.cpp | 65 +++++++++++++++++++++ qrenderdoc/Windows/MainWindow.h | 1 + qrenderdoc/Windows/MainWindow.ui | 9 ++- renderdoc/api/replay/renderdoc_replay.h | 16 +++++ renderdoc/replay/replay_controller.cpp | 64 ++++++++++++++++++++ renderdoc/replay/replay_controller.h | 6 ++ renderdocui/Code/RenderManager.cs | 20 +++++-- renderdocui/Interop/ReplayRenderer.cs | 19 ++++++ renderdocui/Windows/MainWindow.Designer.cs | 40 ++++++++----- renderdocui/Windows/MainWindow.cs | 68 ++++++++++++++++++++++ 13 files changed, 306 insertions(+), 28 deletions(-) diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index 5e8682c8e..e1860826f 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -606,6 +606,9 @@ struct IReplayManager DOCUMENT("Ping the remote server to ensure the connection is still alive."); virtual void PingRemote() = 0; + DOCUMENT("Cancels the active replay loop. See :meth:`~renderdoc.ReplayController.ReplayLoop`."); + virtual void CancelReplayLoop() = 0; + DOCUMENT(R"(Retrieves the host that the manager is currently connected to. :return: The host connected to, or ``None`` if no connection is active. diff --git a/qrenderdoc/Code/ReplayManager.cpp b/qrenderdoc/Code/ReplayManager.cpp index ead8990af..fde750c74 100644 --- a/qrenderdoc/Code/ReplayManager.cpp +++ b/qrenderdoc/Code/ReplayManager.cpp @@ -274,6 +274,11 @@ void ReplayManager::BlockInvoke(ReplayManager::InvokeCallback m) delete cmd; } +void ReplayManager::CancelReplayLoop() +{ + m_Renderer->CancelReplayLoop(); +} + void ReplayManager::CloseThread() { m_Running = false; @@ -393,11 +398,11 @@ void ReplayManager::PushInvoke(ReplayManager::InvokeHandle *cmd) void ReplayManager::run() { - IReplayController *renderer = NULL; + m_Renderer = NULL; if(m_Remote) { - std::tie(m_CreateStatus, renderer) = + std::tie(m_CreateStatus, m_Renderer) = m_Remote->OpenCapture(~0U, m_Logfile.toUtf8().data(), m_Progress); } else @@ -407,12 +412,12 @@ void ReplayManager::run() m_CreateStatus = file->OpenStatus(); if(m_CreateStatus == ReplayStatus::Succeeded) - std::tie(m_CreateStatus, renderer) = file->OpenCapture(m_Progress); + std::tie(m_CreateStatus, m_Renderer) = file->OpenCapture(m_Progress); file->Shutdown(); } - if(renderer == NULL) + if(m_Renderer == NULL) return; qInfo() << "QRenderDoc - renderer created for" << m_Logfile; @@ -439,7 +444,7 @@ void ReplayManager::run() continue; if(cmd->method != NULL) - cmd->method(renderer); + cmd->method(m_Renderer); // if it's a throwaway command, delete it if(cmd->selfdelete) @@ -471,7 +476,7 @@ void ReplayManager::run() // close the core renderer if(m_Remote) - m_Remote->CloseCapture(renderer); + m_Remote->CloseCapture(m_Renderer); else - renderer->Shutdown(); + m_Renderer->Shutdown(); } diff --git a/qrenderdoc/Code/ReplayManager.h b/qrenderdoc/Code/ReplayManager.h index d0132ef21..52034c533 100644 --- a/qrenderdoc/Code/ReplayManager.h +++ b/qrenderdoc/Code/ReplayManager.h @@ -67,6 +67,8 @@ public: void AsyncInvoke(InvokeCallback m); void BlockInvoke(InvokeCallback m); + void CancelReplayLoop(); + void CloseThread(); ReplayStatus ConnectToRemoteServer(RemoteHost *host); @@ -107,6 +109,8 @@ private: QQueue m_RenderQueue; QWaitCondition m_RenderCondition; + IReplayController *m_Renderer = NULL; + void PushInvoke(InvokeHandle *cmd); int m_ProxyRenderer; diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index 56aaf4839..6f02a393a 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -165,6 +165,7 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai return m_Ctx.CreateBuiltinWindow(objectName); }); + ui->action_Start_Replay_Loop->setEnabled(false); ui->action_Resolve_Symbols->setEnabled(false); ui->action_Resolve_Symbols->setText(tr("Resolve Symbols")); @@ -1246,6 +1247,8 @@ void MainWindow::OnLogfileLoaded() statusProgress->setVisible(false); + ui->action_Start_Replay_Loop->setEnabled(true); + setLogHasErrors(!m_Ctx.DebugMessages().empty()); m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) { @@ -1272,6 +1275,8 @@ void MainWindow::OnLogfileClosed() ui->action_Save_Log->setEnabled(false); ui->action_Close_Log->setEnabled(false); + ui->action_Start_Replay_Loop->setEnabled(false); + contextChooser->setEnabled(true); statusText->setText(QString()); @@ -1523,6 +1528,66 @@ void MainWindow::on_action_Resolve_Symbols_triggered() m_Ctx.GetAPIInspector()->Refresh(); } +void MainWindow::on_action_Start_Replay_Loop_triggered() +{ + if(!m_Ctx.LogLoaded()) + return; + + QDialog popup; + popup.setWindowFlags(popup.windowFlags() & ~Qt::WindowContextHelpButtonHint); + popup.setWindowIcon(windowIcon()); + + const TextureDescription *displayTex = NULL; + + const DrawcallDescription *lastDraw = m_Ctx.GetLastDrawcall(); + + displayTex = m_Ctx.GetTexture(lastDraw->copyDestination); + if(!displayTex) + displayTex = m_Ctx.GetTexture(lastDraw->outputs[0]); + + if(!displayTex) + { + // if no texture was bound, then use the first colour swapbuffer + for(const TextureDescription &tex : m_Ctx.GetTextures()) + { + if((tex.creationFlags & TextureCategory::SwapBuffer) && + tex.format.compType != CompType::Depth && tex.format.specialFormat != SpecialFormat::D16S8 && + tex.format.specialFormat != SpecialFormat::D24S8 && + tex.format.specialFormat != SpecialFormat::D32S8) + { + displayTex = &tex; + break; + } + } + } + + ResourceId id; + + if(displayTex) + { + id = displayTex->ID; + popup.resize((int)displayTex->width, (int)displayTex->height); + popup.setWindowTitle( + tr("Looping replay of %1 Displaying %2").arg(m_Ctx.LogFilename()).arg(ToQStr(displayTex->name))); + } + else + { + popup.resize(100, 100); + popup.setWindowTitle( + tr("Looping replay of %1 Displaying %2").arg(m_Ctx.LogFilename()).arg(tr("nothing"))); + } + + WindowingSystem winSys = m_Ctx.CurWindowingSystem(); + void *winData = m_Ctx.FillWindowingData(popup.winId()); + + m_Ctx.Replay().AsyncInvoke( + [winSys, winData, id](IReplayController *r) { r->ReplayLoop(winSys, winData, id); }); + + RDDialog::show(&popup); + + m_Ctx.Replay().CancelReplayLoop(); +} + void MainWindow::on_action_Attach_to_Running_Instance_triggered() { on_action_Manage_Remote_Servers_triggered(); diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index 91757575b..00bfdb77c 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -113,6 +113,7 @@ private slots: void on_action_Python_Shell_triggered(); void on_action_Inject_into_Process_triggered(); void on_action_Resolve_Symbols_triggered(); + void on_action_Start_Replay_Loop_triggered(); void on_action_Attach_to_Running_Instance_triggered(); void on_action_Manage_Remote_Servers_triggered(); void on_action_Settings_triggered(); diff --git a/qrenderdoc/Windows/MainWindow.ui b/qrenderdoc/Windows/MainWindow.ui index 527003872..20b564707 100644 --- a/qrenderdoc/Windows/MainWindow.ui +++ b/qrenderdoc/Windows/MainWindow.ui @@ -42,7 +42,7 @@ 0 0 1200 - 26 + 18 @@ -50,9 +50,11 @@ &Tools + + @@ -398,6 +400,11 @@ &Launch Application + + + Start Replay &Loop + + diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index b3d5e1abd..f94f3c724 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -444,6 +444,22 @@ struct IReplayController )"); virtual void ShutdownOutput(IReplayOutput *output) = 0; + DOCUMENT(R"(Goes into a blocking loop, repeatedly replaying the open capture as fast as possible, +displaying the selected texture in a default unscaled manner to the given output window. + +The function won't return until :meth:`CancelLoop` is called. Since this function is blocking, that +function must be called from another thread. + +:param WindowingSystem system: The type of native window handle data being provided +:param data: The native window data, in a format defined by the system +:type data: opaque void * pointer +:param ResourceId texid: The id of the texture to display. +)"); + virtual void ReplayLoop(WindowingSystem system, void *data, ResourceId texid) = 0; + + DOCUMENT("Cancels a replay loop begun in :meth:`ReplayLoop`. Does nothing if no loop is active."); + virtual void CancelReplayLoop() = 0; + DOCUMENT("Notify the interface that the file it has open has been changed on disk."); virtual void FileChanged() = 0; diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 9a2c9cae9..949e36408 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -1398,6 +1398,58 @@ rdctype::array ReplayController::GetSupportedWindowSystems() return m_pDevice->GetSupportedWindowSystems(); } +void ReplayController::ReplayLoop(WindowingSystem system, void *data, ResourceId texid) +{ + ReplayOutput *output = CreateOutput(system, data, ReplayOutputType::Texture); + + TextureDisplay d; + d.texid = texid; + d.mip = 0; + d.sampleIdx = ~0U; + d.overlay = DebugOverlay::NoOverlay; + d.typeHint = CompType::Typeless; + d.HDRMul = -1.0f; + d.linearDisplayAsGamma = true; + d.FlipY = false; + d.rangemin = 0.0f; + d.rangemax = 1.0f; + d.scale = 1.0f; + d.offx = 0.0f; + d.offy = 0.0f; + d.sliceFace = 0; + d.rawoutput = false; + d.Red = d.Green = d.Blue = true; + d.Alpha = false; + output->SetTextureDisplay(d); + + m_ReplayLoopCancel = 0; + m_ReplayLoopFinished = 0; + + while(Atomic::CmpExch32(&m_ReplayLoopCancel, 0, 0) == 0) + { + m_pDevice->ReplayLog(10000000, eReplay_Full); + + output->Display(); + } + + // restore back to where we were + m_pDevice->ReplayLog(m_EventID, eReplay_Full); + + ShutdownOutput(output); + + // mark that the loop is finished + Atomic::Inc32(&m_ReplayLoopFinished); +} + +void ReplayController::CancelReplayLoop() +{ + Atomic::Inc32(&m_ReplayLoopCancel); + + // wait for it to actually finish before returning + while(Atomic::CmpExch32(&m_ReplayLoopFinished, 0, 0) == 0) + Threading::Sleep(1); +} + ReplayOutput *ReplayController::CreateOutput(WindowingSystem system, void *data, ReplayOutputType type) { ReplayOutput *out = new ReplayOutput(this, system, data, type); @@ -1672,6 +1724,18 @@ extern "C" RENDERDOC_API void RENDERDOC_CC ReplayRenderer_ShutdownOutput(IReplay rend->ShutdownOutput(output); } +extern "C" RENDERDOC_API void RENDERDOC_CC ReplayRenderer_ReplayLoop(IReplayController *rend, + WindowingSystem system, + void *data, ResourceId texid) +{ + rend->ReplayLoop(system, data, texid); +} + +extern "C" RENDERDOC_API void RENDERDOC_CC ReplayRenderer_CancelReplayLoop(IReplayController *rend) +{ + rend->CancelReplayLoop(); +} + extern "C" RENDERDOC_API void RENDERDOC_CC ReplayRenderer_FileChanged(IReplayController *rend) { rend->FileChanged(); diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index ba8960cca..b77bf592a 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -191,6 +191,9 @@ public: rdctype::array GetSupportedWindowSystems(); + void ReplayLoop(WindowingSystem system, void *data, ResourceId texid); + void CancelReplayLoop(); + ReplayOutput *CreateOutput(WindowingSystem, void *data, ReplayOutputType type); void ShutdownOutput(IReplayOutput *output); @@ -205,6 +208,9 @@ private: FrameRecord m_FrameRecord; vector m_Drawcalls; + volatile int32_t m_ReplayLoopCancel = 0; + volatile int32_t m_ReplayLoopFinished = 0; + uint32_t m_EventID; D3D11Pipe::State m_D3D11PipelineState; diff --git a/renderdocui/Code/RenderManager.cs b/renderdocui/Code/RenderManager.cs index c180d06b9..812e02a94 100644 --- a/renderdocui/Code/RenderManager.cs +++ b/renderdocui/Code/RenderManager.cs @@ -410,6 +410,14 @@ namespace renderdocui.Code } } + public void CancelReplayLoop() + { + if (m_Thread == null || !Running) + return; + + m_Renderer.CancelReplayLoop(); + } + public ReplayCreateException InitException = null; public void CloseThreadSync() @@ -608,12 +616,14 @@ namespace renderdocui.Code renderer.Shutdown(); } + private ReplayRenderer m_Renderer; + private void RunThread() { try { - ReplayRenderer renderer = CreateReplayRenderer(); - if(renderer != null) + m_Renderer = CreateReplayRenderer(); + if(m_Renderer != null) { System.Diagnostics.Debug.WriteLine("Renderer created"); @@ -644,7 +654,7 @@ namespace renderdocui.Code { try { - m_current.method(renderer); + m_current.method(m_Renderer); } catch (Exception ex) { @@ -653,7 +663,7 @@ namespace renderdocui.Code } else { - m_current.method(renderer); + m_current.method(m_Renderer); } } @@ -669,7 +679,7 @@ namespace renderdocui.Code m_renderQueue.Clear(); } - DestroyReplayRenderer(renderer); + DestroyReplayRenderer(m_Renderer); } } catch (ReplayCreateException ex) diff --git a/renderdocui/Interop/ReplayRenderer.cs b/renderdocui/Interop/ReplayRenderer.cs index 5ca9b96e5..f29266faf 100644 --- a/renderdocui/Interop/ReplayRenderer.cs +++ b/renderdocui/Interop/ReplayRenderer.cs @@ -285,6 +285,11 @@ namespace renderdoc [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern void ReplayRenderer_ShutdownOutput(IntPtr real, IntPtr replayOutput); + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ReplayRenderer_ReplayLoop(IntPtr real, UInt32 windowSystem, IntPtr WindowHandle, ResourceId texid); + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ReplayRenderer_CancelReplayLoop(IntPtr real); + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern void ReplayRenderer_FileChanged(IntPtr real); @@ -396,6 +401,20 @@ namespace renderdoc return ret; } + public void ReplayLoop(IntPtr WindowHandle, ResourceId texid) + { + if (WindowHandle == IntPtr.Zero) + return; + + // 1 == eWindowingSystem_Win32 + ReplayRenderer_ReplayLoop(m_Real, 1u, WindowHandle, texid); + } + + public void CancelReplayLoop() + { + ReplayRenderer_CancelReplayLoop(m_Real); + } + public ReplayOutput CreateOutput(IntPtr WindowHandle, OutputType type) { // 0 == eWindowingSystem_Unknown diff --git a/renderdocui/Windows/MainWindow.Designer.cs b/renderdocui/Windows/MainWindow.Designer.cs index b90c40d77..cd3ac008e 100644 --- a/renderdocui/Windows/MainWindow.Designer.cs +++ b/renderdocui/Windows/MainWindow.Designer.cs @@ -95,6 +95,7 @@ this.toolStripSeparator11 = new System.Windows.Forms.ToolStripSeparator(); this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.manageRemote = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator13 = new System.Windows.Forms.ToolStripSeparator(); this.startAndroidRemoteServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.viewDocsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -119,7 +120,7 @@ this.statusProgress = new System.Windows.Forms.ToolStripProgressBar(); this.dockPanel = new WeifenLuo.WinFormsUI.Docking.DockPanel(); this.saveDialog = new System.Windows.Forms.SaveFileDialog(); - this.toolStripSeparator13 = new System.Windows.Forms.ToolStripSeparator(); + this.startReplayLoopToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menuStrip1.SuspendLayout(); this.toolStripContainer1.BottomToolStripPanel.SuspendLayout(); this.toolStripContainer1.ContentPanel.SuspendLayout(); @@ -516,6 +517,7 @@ // this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.resolveSymbolsToolStripMenuItem, + this.startReplayLoopToolStripMenuItem, this.toolStripSeparator11, this.optionsToolStripMenuItem, this.manageRemote, @@ -528,29 +530,41 @@ // resolveSymbolsToolStripMenuItem // this.resolveSymbolsToolStripMenuItem.Name = "resolveSymbolsToolStripMenuItem"; - this.resolveSymbolsToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.resolveSymbolsToolStripMenuItem.Size = new System.Drawing.Size(213, 22); this.resolveSymbolsToolStripMenuItem.Text = "&Resolve Symbols"; this.resolveSymbolsToolStripMenuItem.Click += new System.EventHandler(this.resolveSymbolsToolStripMenuItem_Click); // // toolStripSeparator11 // this.toolStripSeparator11.Name = "toolStripSeparator11"; - this.toolStripSeparator11.Size = new System.Drawing.Size(189, 6); + this.toolStripSeparator11.Size = new System.Drawing.Size(210, 6); // // optionsToolStripMenuItem // this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; - this.optionsToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.optionsToolStripMenuItem.Size = new System.Drawing.Size(213, 22); this.optionsToolStripMenuItem.Text = "&Options"; this.optionsToolStripMenuItem.Click += new System.EventHandler(this.optionsToolStripMenuItem_Click); // // manageRemote // this.manageRemote.Name = "manageRemote"; - this.manageRemote.Size = new System.Drawing.Size(192, 22); + this.manageRemote.Size = new System.Drawing.Size(213, 22); this.manageRemote.Text = "&Manage Remote Servers"; this.manageRemote.Click += new System.EventHandler(this.manageRemote_Click); // + // toolStripSeparator13 + // + this.toolStripSeparator13.Name = "toolStripSeparator13"; + this.toolStripSeparator13.Size = new System.Drawing.Size(210, 6); + // + // startAndroidRemoteServerToolStripMenuItem + // + this.startAndroidRemoteServerToolStripMenuItem.Name = "startAndroidRemoteServerToolStripMenuItem"; + this.startAndroidRemoteServerToolStripMenuItem.Size = new System.Drawing.Size(213, 22); + this.startAndroidRemoteServerToolStripMenuItem.Text = "Start Android Remote Server"; + this.startAndroidRemoteServerToolStripMenuItem.Click += new System.EventHandler(this.startAndroidRemoteServerToolStripMenuItem_Click); + // // helpToolStripMenuItem // this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -808,17 +822,12 @@ this.saveDialog.Filter = "Log Files (*.rdc)|*.rdc"; this.saveDialog.Title = "Save Log As"; // - // toolStripSeparator13 + // startReplayLoopToolStripMenuItem // - this.toolStripSeparator13.Name = "toolStripSeparator13"; - this.toolStripSeparator13.Size = new System.Drawing.Size(271, 6); - // - // startAndroidRemoteServerToolStripMenuItem - // - this.startAndroidRemoteServerToolStripMenuItem.Name = "startAndroidRemoteServerToolStripMenuItem"; - this.startAndroidRemoteServerToolStripMenuItem.Size = new System.Drawing.Size(274, 26); - this.startAndroidRemoteServerToolStripMenuItem.Text = "Start Android Remote Server"; - this.startAndroidRemoteServerToolStripMenuItem.Click += new System.EventHandler(this.startAndroidRemoteServerToolStripMenuItem_Click); + this.startReplayLoopToolStripMenuItem.Name = "startReplayLoopToolStripMenuItem"; + this.startReplayLoopToolStripMenuItem.Size = new System.Drawing.Size(213, 22); + this.startReplayLoopToolStripMenuItem.Text = "Start Replay Loop"; + this.startReplayLoopToolStripMenuItem.Click += new System.EventHandler(this.startReplayLoopToolStripMenuItem_Click); // // MainWindow // @@ -931,5 +940,6 @@ private System.Windows.Forms.ToolStripDropDownButton contextChooser; private System.Windows.Forms.ToolStripMenuItem localContext; private System.Windows.Forms.ToolStripSeparator toolStripSeparator13; + private System.Windows.Forms.ToolStripMenuItem startReplayLoopToolStripMenuItem; } } \ No newline at end of file diff --git a/renderdocui/Windows/MainWindow.cs b/renderdocui/Windows/MainWindow.cs index 3e1e8a574..39d624aab 100644 --- a/renderdocui/Windows/MainWindow.cs +++ b/renderdocui/Windows/MainWindow.cs @@ -148,6 +148,7 @@ namespace renderdocui.Windows m_InitRemoteIdent = remoteIdent; OwnTemporaryLog = temp; + startReplayLoopToolStripMenuItem.Enabled = false; resolveSymbolsToolStripMenuItem.Enabled = false; resolveSymbolsToolStripMenuItem.Text = "Resolve Symbols"; @@ -277,6 +278,7 @@ namespace renderdocui.Windows statusIcon.Image = null; statusProgress.Visible = false; + startReplayLoopToolStripMenuItem.Enabled = false; resolveSymbolsToolStripMenuItem.Enabled = false; resolveSymbolsToolStripMenuItem.Text = "Resolve Symbols"; @@ -439,6 +441,8 @@ namespace renderdocui.Windows statusProgress.Visible = false; + startReplayLoopToolStripMenuItem.Enabled = true; + m_Core.Renderer.BeginInvoke((ReplayRenderer r) => { bool hasResolver = r.HasCallstacks(); @@ -2033,5 +2037,69 @@ namespace renderdocui.Windows device = m_SelectedHost.Hostname; StaticExports.StartAndroidRemoteServer(device); } + + private void startReplayLoopToolStripMenuItem_Click(object sender, EventArgs e) + { + if (!m_Core.LogLoaded) + return; + + Form popup = new Form(); + popup.Icon = this.Icon; + popup.StartPosition = FormStartPosition.CenterScreen; + popup.FormBorderStyle = FormBorderStyle.SizableToolWindow; + popup.SizeGripStyle = SizeGripStyle.Hide; + + FetchTexture displayTex = null; + + // display the texture bound at the last drawcall - typically the present + var lastDraw = m_Core.CurDrawcalls[m_Core.CurDrawcalls.Length - 1]; + while (lastDraw.children != null && lastDraw.children.Length > 0) + lastDraw = lastDraw.children[lastDraw.children.Length - 1]; + + displayTex = m_Core.GetTexture(lastDraw.copyDestination); + if (displayTex == null) + displayTex = m_Core.GetTexture(lastDraw.outputs[0]); + + if (displayTex == null) + { + // if no texture was bound, then use the first colour swapbuffer + foreach (FetchTexture tex in m_Core.CurTextures) + { + if (tex.creationFlags.HasFlag(TextureCreationFlags.SwapBuffer) && + tex.format.compType != FormatComponentType.Depth && + tex.format.specialFormat != SpecialFormat.D16S8 && + tex.format.specialFormat != SpecialFormat.D24S8 && + tex.format.specialFormat != SpecialFormat.D32S8) + { + displayTex = tex; + break; + } + } + } + + ResourceId id = ResourceId.Null; + + if (displayTex != null) + { + id = displayTex.ID; + popup.ClientSize = new Size((int)displayTex.width, (int)displayTex.height); + popup.Text = "Looping replay of " + m_Core.LogFileName + " Displaying " + displayTex.name; + } + else + { + popup.ClientSize = new Size(100, 100); + popup.Text = "Looping replay of " + m_Core.LogFileName + " Displaying nothing"; + } + + popup.CreateControl(); + + IntPtr handle = popup.Handle; + + m_Core.Renderer.BeginInvoke((ReplayRenderer r) => { r.ReplayLoop(handle, id); }); + + popup.ShowDialog(); + + m_Core.Renderer.CancelReplayLoop(); + } } }