From 666aee093b3da6e2f3533f4ed3eb16c03ef467f9 Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 3 Apr 2017 18:31:59 +0100 Subject: [PATCH] Backport 'tagged' render invokes from qrenderdoc * This is a possible fix for a case where render work triggered by mouse movements (such as pixel and vertex picking) can happen faster than it executes, leading to a backlog of render commands and a noticeable lag which only gets worse the more you move the mouse until everything seems to be unresponsive or laggy (especially if you then trigger a blocking command like event change, which will block the whole UI until the queued picks happen). * Since a new pick coming in will override and make redundant the previous pick, we allow the render commands to do just that. If a new command comes in, we remove any previous commands with the same tag and put the command in the first match (this prevents a tagged invoke always being pushed to the back of the queue). --- renderdocui/Code/RenderManager.cs | 94 +++++++++++++++++++++++-- renderdocui/Controls/ResourcePreview.cs | 2 +- renderdocui/Windows/BufferViewer.cs | 4 +- renderdocui/Windows/TextureViewer.cs | 10 +-- 4 files changed, 98 insertions(+), 12 deletions(-) diff --git a/renderdocui/Code/RenderManager.cs b/renderdocui/Code/RenderManager.cs index 2b77065fc..c180d06b9 100644 --- a/renderdocui/Code/RenderManager.cs +++ b/renderdocui/Code/RenderManager.cs @@ -45,12 +45,21 @@ namespace renderdocui.Code { private class InvokeHandle { - public InvokeHandle(InvokeMethod m) + public InvokeHandle(string t, InvokeMethod m) { + tag = t; method = m; processed = false; } + public InvokeHandle(InvokeMethod m) + { + tag = ""; + method = m; + processed = false; + } + + public string tag; public InvokeMethod method; public bool paintInvoke = false; volatile public bool processed; @@ -423,6 +432,55 @@ namespace renderdocui.Code CatchExceptions = catching; } + // this tagged version is for cases when we might send a request - e.g. to pick a vertex or pixel + // - and want to pre-empt it with a new request before the first has returned. Either because some + // other work is taking a while or because we're sending requests faster than they can be + // processed. + // the manager processes only the request on the top of the queue, so when a new tagged invoke + // comes in, we remove any other requests in the queue before it that have the same tag + public void BeginInvoke(string tag, InvokeMethod m) + { + InvokeHandle cmd = new InvokeHandle(tag, m); + + if (tag != "") + { + lock (m_renderQueue) + { + bool added = false; + + for (int i = 0; i < m_renderQueue.Count;) + { + if (m_renderQueue[i].tag == tag) + { + m_renderQueue[i].processed = true; + if (!added) + { + m_renderQueue[i] = cmd; + added = true; + } + else + { + m_renderQueue.RemoveAt(i); + } + } + else + { + i++; + } + } + + if (!added) + m_renderQueue.Add(cmd); + } + + m_WakeupEvent.Set(); + } + else + { + PushInvoke(cmd); + } + } + public void BeginInvoke(InvokeMethod m) { InvokeHandle cmd = new InvokeHandle(m); @@ -442,7 +500,7 @@ namespace renderdocui.Code throw cmd.ex; } - public void InvokeForPaint(InvokeMethod m) + public void InvokeForPaint(string tag, InvokeMethod m) { if (m_Thread == null || !Running) return; @@ -458,7 +516,7 @@ namespace renderdocui.Code bool waitable = true; - InvokeHandle cmd = new InvokeHandle(m); + InvokeHandle cmd = new InvokeHandle(tag, m); cmd.paintInvoke = true; lock (m_renderQueue) @@ -473,7 +531,35 @@ namespace renderdocui.Code if (!m_renderQueue[i].paintInvoke) waitable = false; - m_renderQueue.Add(cmd); + // remove any duplicated paints if we have a tag + bool added = false; + + if (tag != "") + { + for (int i = 0; i < m_renderQueue.Count;) + { + if (m_renderQueue[i].tag == tag) + { + m_renderQueue[i].processed = true; + if (!added) + { + m_renderQueue[i] = cmd; + added = true; + } + else + { + m_renderQueue.RemoveAt(i); + } + } + else + { + i++; + } + } + } + + if (!added) + m_renderQueue.Add(cmd); } m_WakeupEvent.Set(); diff --git a/renderdocui/Controls/ResourcePreview.cs b/renderdocui/Controls/ResourcePreview.cs index 84bdd056d..8cf25fd77 100644 --- a/renderdocui/Controls/ResourcePreview.cs +++ b/renderdocui/Controls/ResourcePreview.cs @@ -155,7 +155,7 @@ namespace renderdocui.Controls } if (m_Output != null) - m_Core.Renderer.InvokeForPaint((ReplayRenderer r) => { m_Output.Display(); }); + m_Core.Renderer.InvokeForPaint("thumbpaint", (ReplayRenderer r) => { m_Output.Display(); }); } public void SetSize(Size s) diff --git a/renderdocui/Windows/BufferViewer.cs b/renderdocui/Windows/BufferViewer.cs index 8ae2f3c3e..f090b9b5f 100644 --- a/renderdocui/Windows/BufferViewer.cs +++ b/renderdocui/Windows/BufferViewer.cs @@ -2391,7 +2391,7 @@ namespace renderdocui.Windows if (!m_Core.LogLoaded) return; - m_Core.Renderer.BeginInvoke((ReplayRenderer r) => + m_Core.Renderer.BeginInvoke("PickVertex", (ReplayRenderer r) => { UInt32 instanceSelected = 0; UInt32 vertSelected = m_Output.PickVertex(m_Core.CurEvent, (UInt32)p.X, (UInt32)p.Y, out instanceSelected); @@ -2598,7 +2598,7 @@ namespace renderdocui.Windows return; } - m_Core.Renderer.InvokeForPaint((ReplayRenderer r) => { RT_UpdateRenderOutput(r); if (m_Output != null) m_Output.Display(); }); + m_Core.Renderer.InvokeForPaint("bufferpaint", (ReplayRenderer r) => { RT_UpdateRenderOutput(r); if (m_Output != null) m_Output.Display(); }); } private void BufferViewer_Load(object sender, EventArgs e) diff --git a/renderdocui/Windows/TextureViewer.cs b/renderdocui/Windows/TextureViewer.cs index aa93269a0..219bd4117 100644 --- a/renderdocui/Windows/TextureViewer.cs +++ b/renderdocui/Windows/TextureViewer.cs @@ -2452,7 +2452,7 @@ namespace renderdocui.Windows return; } - m_Core.Renderer.InvokeForPaint((ReplayRenderer r) => { if (m_Output != null) m_Output.Display(); }); + m_Core.Renderer.InvokeForPaint("contextpaint", (ReplayRenderer r) => { if (m_Output != null) m_Output.Display(); }); } private void render_Paint(object sender, PaintEventArgs e) @@ -2470,7 +2470,7 @@ namespace renderdocui.Windows foreach (var prev in roPanel.Thumbnails) if (prev.Unbound) prev.Clear(); - m_Core.Renderer.InvokeForPaint((ReplayRenderer r) => { if (m_Output != null) m_Output.Display(); }); + m_Core.Renderer.InvokeForPaint("texpaint", (ReplayRenderer r) => { if (m_Output != null) m_Output.Display(); }); } #endregion @@ -2910,7 +2910,7 @@ namespace renderdocui.Windows m_PickedPoint.X = Helpers.Clamp(m_PickedPoint.X, 0, (int)tex.width - 1); m_PickedPoint.Y = Helpers.Clamp(m_PickedPoint.Y, 0, (int)tex.height - 1); - m_Core.Renderer.BeginInvoke((ReplayRenderer r) => + m_Core.Renderer.BeginInvoke("PickPixelClick", (ReplayRenderer r) => { if (m_Output != null) RT_PickPixelsAndUpdate(m_PickedPoint.X, m_PickedPoint.Y, true); @@ -2926,7 +2926,7 @@ namespace renderdocui.Windows if (tex != null) { - m_Core.Renderer.BeginInvoke((ReplayRenderer r) => + m_Core.Renderer.BeginInvoke("PickPixelHover", (ReplayRenderer r) => { if (m_Output != null) { @@ -3289,7 +3289,7 @@ namespace renderdocui.Windows rangePaintThread = Helpers.NewThread(new ThreadStart(() => { - m_Core.Renderer.InvokeForPaint((ReplayRenderer r) => { RT_UpdateAndDisplay(r); if (m_Output != null) m_Output.Display(); }); + m_Core.Renderer.InvokeForPaint("", (ReplayRenderer r) => { RT_UpdateAndDisplay(r); if (m_Output != null) m_Output.Display(); }); Thread.Sleep(8); })); rangePaintThread.Start();