From 1e65762e937ffc3fc2ac27844002eadb22e2f669 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 17 Feb 2017 13:43:19 +0000 Subject: [PATCH] Process render commands 1 by 1 to allow pre-empting high frequency cmds --- qrenderdoc/Code/RenderManager.cpp | 60 +++++++++++++++++++--------- qrenderdoc/Code/RenderManager.h | 11 ++++- qrenderdoc/Windows/BufferViewer.cpp | 2 +- qrenderdoc/Windows/TextureViewer.cpp | 6 ++- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/qrenderdoc/Code/RenderManager.cpp b/qrenderdoc/Code/RenderManager.cpp index 7c54f1dfa..62832449b 100644 --- a/qrenderdoc/Code/RenderManager.cpp +++ b/qrenderdoc/Code/RenderManager.cpp @@ -231,6 +231,31 @@ bool RenderManager::IsRunning() return m_Thread && m_Thread->isRunning() && m_Running; } +void RenderManager::AsyncInvoke(const QString &tag, RenderManager::InvokeMethod m) +{ + { + QMutexLocker autolock(&m_RenderLock); + for(int i = 0; i < m_RenderQueue.count();) + { + if(m_RenderQueue[i]->tag == tag) + { + InvokeHandle *cmd = m_RenderQueue.takeAt(i); + if(cmd->selfdelete) + delete cmd; + } + else + { + i++; + } + } + } + + InvokeHandle *cmd = new InvokeHandle(m, tag); + cmd->selfdelete = true; + + PushInvoke(cmd); +} + void RenderManager::AsyncInvoke(RenderManager::InvokeMethod m) { InvokeHandle *cmd = new InvokeHandle(m); @@ -373,7 +398,7 @@ void RenderManager::PushInvoke(RenderManager::InvokeHandle *cmd) } QMutexLocker autolock(&m_RenderLock); - m_RenderQueue.push_back(cmd); + m_RenderQueue.enqueue(cmd); m_RenderCondition.wakeAll(); } @@ -396,31 +421,30 @@ void RenderManager::run() // main render command loop while(m_Running) { - QQueue queue; + InvokeHandle *cmd = NULL; - // wait for the condition to be woken, grab current queue, + // wait for the condition to be woken, grab top of current queue, // unlock again. { QMutexLocker autolock(&m_RenderLock); - m_RenderCondition.wait(&m_RenderLock, 10); - m_RenderQueue.swap(queue); + if(m_RenderQueue.isEmpty()) + m_RenderCondition.wait(&m_RenderLock, 10); + + if(!m_RenderQueue.isEmpty()) + cmd = m_RenderQueue.dequeue(); } - // process all the commands - for(InvokeHandle *cmd : queue) - { - if(cmd == NULL) - continue; + if(cmd == NULL) + continue; - if(cmd->method != NULL) - cmd->method(renderer); + if(cmd->method != NULL) + cmd->method(renderer); - // if it's a throwaway command, delete it - if(cmd->selfdelete) - delete cmd; - else - cmd->processed.release(); - } + // if it's a throwaway command, delete it + if(cmd->selfdelete) + delete cmd; + else + cmd->processed.release(); } // clean up anything left in the queue diff --git a/qrenderdoc/Code/RenderManager.h b/qrenderdoc/Code/RenderManager.h index 4d7b579c7..969a8992c 100644 --- a/qrenderdoc/Code/RenderManager.h +++ b/qrenderdoc/Code/RenderManager.h @@ -131,6 +131,13 @@ public: bool IsRunning(); ReplayCreateStatus GetCreateStatus() { return m_CreateStatus; } + // 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 + void AsyncInvoke(const QString &tag, InvokeMethod m); void AsyncInvoke(InvokeMethod m); void BlockInvoke(InvokeMethod m); @@ -155,12 +162,14 @@ public: private: struct InvokeHandle { - InvokeHandle(InvokeMethod m) + InvokeHandle(InvokeMethod m, const QString &t = QString()) { + tag = t; method = m; selfdelete = false; } + QString tag; InvokeMethod method; QSemaphore processed; bool selfdelete; diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index f560bf6f4..0216e7ca5 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -1889,7 +1889,7 @@ void BufferViewer::render_clicked(QMouseEvent *e) if((e->buttons() & Qt::RightButton) && m_Output) { - m_Ctx.Renderer().AsyncInvoke([this, curpos](IReplayRenderer *r) { + m_Ctx.Renderer().AsyncInvoke("PickVertex", [this, curpos](IReplayRenderer *r) { uint32_t instanceSelected = 0; uint32_t vertSelected = m_Output->PickVertex(m_Ctx.CurEvent(), (uint32_t)curpos.x(), (uint32_t)curpos.y(), &instanceSelected); diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index 5b0772275..469f267e6 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -2185,11 +2185,13 @@ void TextureViewer::render_mouseMove(QMouseEvent *e) m_PickedPoint.setX(qBound(0, m_PickedPoint.x(), (int)texptr->width - 1)); m_PickedPoint.setY(qBound(0, m_PickedPoint.y(), (int)texptr->height - 1)); - INVOKE_MEMFN(RT_PickPixelsAndUpdate); + m_Ctx.Renderer().AsyncInvoke("PickPixelClick", + [this](IReplayRenderer *r) { RT_PickPixelsAndUpdate(r); }); } else if(e->buttons() == Qt::NoButton) { - INVOKE_MEMFN(RT_PickHoverAndUpdate); + m_Ctx.Renderer().AsyncInvoke("PickPixelHover", + [this](IReplayRenderer *r) { RT_PickHoverAndUpdate(r); }); } } }