mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-23 15:06:32 +00:00
Loads of changes, fix thread & invoke helpers, skeleton of Core working
* GUIInvoke helper changed to use QMetaObject::invokeMethod which works on threads better. * LambdaThread helper class now has a thread member, it doesn't derive from thread (this seems to be recommended practice).
This commit is contained in:
+155
-6
@@ -1,19 +1,168 @@
|
||||
#include "Core.h"
|
||||
#include "Windows/MainWindow.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
#include <QMetaObject>
|
||||
|
||||
Core::Core()
|
||||
Core::Core(QString paramFilename, QString remoteHost, uint32_t remoteIdent, bool temp)
|
||||
{
|
||||
|
||||
m_MainWindow = new MainWindow(this, paramFilename, remoteHost, remoteIdent, temp);
|
||||
m_MainWindow->show();
|
||||
}
|
||||
|
||||
Core::~Core()
|
||||
{
|
||||
|
||||
delete m_MainWindow;
|
||||
}
|
||||
|
||||
|
||||
void QInvoke::call(const std::function<void()> &f)
|
||||
void Core::LoadLogfile(QString logFile, bool temporary)
|
||||
{
|
||||
QTimer::singleShot(0, new QInvoke(f), SLOT(doInvoke()));
|
||||
LoadLogfile(-1, "", logFile, temporary);
|
||||
}
|
||||
|
||||
void Core::LoadLogfile(int proxyRenderer, QString replayHost, QString logFile, bool temporary)
|
||||
{
|
||||
m_LogFile = logFile;
|
||||
|
||||
m_LoadInProgress = true;
|
||||
|
||||
float loadProgress = 0.0f;
|
||||
float postloadProgress = 0.0f;
|
||||
|
||||
// this function call will block until the log is either loaded, or there's some failure
|
||||
m_Renderer.Init(proxyRenderer, replayHost, logFile, &loadProgress);
|
||||
|
||||
// if the renderer isn't running, we hit a failure case so display an error message
|
||||
if (!m_Renderer.IsRunning())
|
||||
{
|
||||
QString errmsg = "Unknown error message";
|
||||
ReplayCreateStatus status = m_Renderer.GetCreateStatus();
|
||||
errmsg = status;
|
||||
|
||||
if(proxyRenderer >= 0)
|
||||
QMessageBox::critical(NULL, "Error opening log",
|
||||
QString("%1\nFailed to transfer and replay on remote host %2: %3.\n\n" \
|
||||
"Check diagnostic log in Help menu for more details.").arg(logFile, replayHost, errmsg));
|
||||
else
|
||||
QMessageBox::critical(NULL, "Error opening log",
|
||||
QString("%1\nFailed to open logfile for replay: %1.\n\n" \
|
||||
"Check diagnostic log in Help menu for more details.").arg(logFile, errmsg));
|
||||
|
||||
m_LoadInProgress = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_FrameID = 0;
|
||||
m_EventID = 0;
|
||||
|
||||
// fetch initial data like drawcalls, textures and buffers
|
||||
m_Renderer.BlockInvoke([this,&postloadProgress](IReplayRenderer *r) {
|
||||
r->GetFrameInfo(&m_FrameInfo);
|
||||
|
||||
m_APIProps = r->GetAPIProperties();
|
||||
|
||||
postloadProgress = 0.2f;
|
||||
|
||||
m_Drawcalls = new rdctype::array<FetchDrawcall>[m_FrameInfo.count];
|
||||
|
||||
postloadProgress = 0.4f;
|
||||
|
||||
for (int i = 0; i < m_FrameInfo.count; i++)
|
||||
r->GetDrawcalls((uint32_t)i, &m_Drawcalls[i]);
|
||||
|
||||
postloadProgress = 0.7f;
|
||||
|
||||
r->GetBuffers(&m_BufferList);
|
||||
for(int i=0; i < m_BufferList.count; i++)
|
||||
m_Buffers[ m_BufferList[i].ID ] = &m_BufferList[i];
|
||||
|
||||
postloadProgress = 0.8f;
|
||||
|
||||
r->GetTextures(&m_TextureList);
|
||||
for(int i=0; i < m_TextureList.count; i++)
|
||||
m_Textures[ m_TextureList[i].ID ] = &m_TextureList[i];
|
||||
|
||||
postloadProgress = 0.9f;
|
||||
|
||||
r->GetD3D11PipelineState(&CurD3D11PipelineState);
|
||||
r->GetGLPipelineState(&CurGLPipelineState);
|
||||
//CurPipelineState.SetStates(m_APIProps, CurD3D11PipelineState, CurGLPipelineState);
|
||||
|
||||
UnreadMessageCount = 0;
|
||||
AddMessages(m_FrameInfo[0].debugMessages);
|
||||
|
||||
postloadProgress = 1.0f;
|
||||
});
|
||||
|
||||
QThread::msleep(20);
|
||||
|
||||
m_LogLoaded = true;
|
||||
|
||||
QList<ILogViewerForm*> logviewers(m_LogViewers);
|
||||
|
||||
GUIInvoke::blockcall([&logviewers]() {
|
||||
// notify all the registers log viewers that a log has been loaded
|
||||
for(ILogViewerForm *logviewer : logviewers)
|
||||
{
|
||||
if(logviewer) logviewer->OnLogfileLoaded();
|
||||
}
|
||||
});
|
||||
|
||||
m_LoadInProgress = false;
|
||||
}
|
||||
|
||||
void Core::SetEventID(ILogViewerForm *exclude, uint32_t frameID, uint32_t eventID)
|
||||
{
|
||||
m_FrameID = frameID;
|
||||
m_EventID = eventID;
|
||||
|
||||
m_Renderer.BlockInvoke([frameID,eventID,this](IReplayRenderer *r) {
|
||||
r->SetFrameEvent(frameID, eventID);
|
||||
r->GetD3D11PipelineState(&CurD3D11PipelineState);
|
||||
r->GetGLPipelineState(&CurGLPipelineState);
|
||||
//CurPipelineState.SetStates(m_APIProps, CurD3D11PipelineState, CurGLPipelineState);
|
||||
});
|
||||
|
||||
for(ILogViewerForm *logviewer : m_LogViewers)
|
||||
{
|
||||
if(logviewer == exclude)
|
||||
continue;
|
||||
|
||||
logviewer->OnEventSelected(frameID, eventID);
|
||||
}
|
||||
}
|
||||
|
||||
void GUIInvoke::call(const std::function<void()> &f)
|
||||
{
|
||||
if(qApp->thread() == QThread::currentThread())
|
||||
{
|
||||
f();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: could maybe do away with string compare here via caching
|
||||
// invoke->metaObject()->indexOfMethod("doInvoke"); ?
|
||||
|
||||
GUIInvoke *invoke = new GUIInvoke(f);
|
||||
invoke->moveToThread(qApp->thread());
|
||||
QMetaObject::invokeMethod(invoke, "doInvoke", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void GUIInvoke::blockcall(const std::function<void()> &f)
|
||||
{
|
||||
if(qApp->thread() == QThread::currentThread())
|
||||
{
|
||||
f();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: could maybe do away with string compare here via caching
|
||||
// invoke->metaObject()->indexOfMethod("doInvoke"); ?
|
||||
|
||||
GUIInvoke *invoke = new GUIInvoke(f);
|
||||
invoke->moveToThread(qApp->thread());
|
||||
QMetaObject::invokeMethod(invoke, "doInvoke", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
|
||||
+187
-7
@@ -3,16 +3,164 @@
|
||||
|
||||
#include "RenderManager.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
|
||||
struct ILogViewerForm
|
||||
{
|
||||
virtual void OnLogfileLoaded() = 0;
|
||||
virtual void OnLogfileClosed() = 0;
|
||||
virtual void OnEventSelected(uint32_t frameID, uint32_t eventID) = 0;
|
||||
};
|
||||
|
||||
struct ILogLoadProgressListener
|
||||
{
|
||||
virtual void LogfileProgressBegin() = 0;
|
||||
virtual void LogfileProgress(float progress) = 0;
|
||||
};
|
||||
|
||||
class MainWindow;
|
||||
|
||||
class Core
|
||||
{
|
||||
public:
|
||||
Core();
|
||||
Core(QString paramFilename, QString remoteHost, uint32_t remoteIdent, bool temp);
|
||||
~Core();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Control functions
|
||||
|
||||
// loading a local log, no remote replay
|
||||
void LoadLogfile(QString logFile, bool temporary);
|
||||
|
||||
// when loading a log while replaying remotely, provide the proxy renderer that will be used
|
||||
// as well as the hostname to replay on.
|
||||
void LoadLogfile(int proxyRenderer, QString replayHost, QString logFile, bool temporary);
|
||||
|
||||
void CloseLogfile();
|
||||
|
||||
QString TempLogFilename(QString appname);
|
||||
|
||||
void SetEventID(ILogViewerForm *exclude, uint32_t frameID, uint32_t eventID);
|
||||
|
||||
void AddLogProgressListener(ILogLoadProgressListener *p);
|
||||
|
||||
void AddLogViewer(ILogViewerForm *f)
|
||||
{
|
||||
m_LogViewers.push_back(f);
|
||||
|
||||
if(LogLoaded())
|
||||
{
|
||||
f->OnLogfileLoaded();
|
||||
f->OnEventSelected(CurFrame(), CurEvent());
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveLogViewer(ILogViewerForm *f)
|
||||
{
|
||||
m_LogViewers.removeAll(f);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Singleton windows
|
||||
|
||||
/*
|
||||
private MainWindow m_MainWindow = null;
|
||||
private EventBrowser m_EventBrowser = null;
|
||||
private APIInspector m_APIInspector = null;
|
||||
private DebugMessages m_DebugMessages = null;
|
||||
private TimelineBar m_TimelineBar = null;
|
||||
private TextureViewer m_TextureViewer = null;
|
||||
private PipelineStateViewer m_PipelineStateViewer = null;
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Accessors
|
||||
|
||||
RenderManager *Renderer() { return &m_Renderer; }
|
||||
|
||||
bool LogLoaded() { return m_LogLoaded; }
|
||||
bool LogLoading() { return m_LoadInProgress; }
|
||||
QString LogFilename() { return m_LogFile; }
|
||||
|
||||
const rdctype::array<FetchFrameInfo> &FrameInfo() { return m_FrameInfo; }
|
||||
const APIProperties &APIProps() { return m_APIProps; }
|
||||
|
||||
// TODO: support multiple frames
|
||||
uint32_t CurFrame() { return m_FrameID; }
|
||||
uint32_t CurEvent() { return m_EventID; }
|
||||
|
||||
const FetchDrawcall *CurDrawcall() { return GetDrawcall(CurFrame(), CurEvent()); }
|
||||
const rdctype::array<FetchDrawcall> &CurDrawcalls(uint32_t frame) { return m_Drawcalls[frame]; }
|
||||
|
||||
FetchTexture *GetTexture(ResourceId id) { return m_Textures[id]; }
|
||||
const rdctype::array<FetchTexture> &GetTextures() { return m_TextureList; }
|
||||
|
||||
FetchBuffer *GetBuffer(ResourceId id) { return m_Buffers[id]; }
|
||||
const rdctype::array<FetchBuffer> &GetBuffers() { return m_BufferList; }
|
||||
|
||||
QList<DebugMessage> DebugMessages;
|
||||
int UnreadMessageCount;
|
||||
void AddMessages(rdctype::array<DebugMessage> &msgs)
|
||||
{
|
||||
UnreadMessageCount += msgs.count;
|
||||
for(int i=0; i < msgs.count; i++)
|
||||
DebugMessages.push_back(msgs[i]);
|
||||
}
|
||||
|
||||
D3D11PipelineState CurD3D11PipelineState;
|
||||
GLPipelineState CurGLPipelineState;
|
||||
//CommonPipelineState CurPipelineState;
|
||||
|
||||
private:
|
||||
RenderManager m_Renderer;
|
||||
|
||||
QList<ILogViewerForm*> m_LogViewers;
|
||||
QList<ILogLoadProgressListener*> m_ProgressListeners;
|
||||
|
||||
bool m_LogLoaded, m_LoadInProgress;
|
||||
QString m_LogFile;
|
||||
|
||||
uint32_t m_FrameID, m_EventID;
|
||||
|
||||
const FetchDrawcall *GetDrawcall(const rdctype::array<FetchDrawcall> &draws, uint32_t eventID)
|
||||
{
|
||||
for(int i=0; i < draws.count; i++)
|
||||
{
|
||||
if (draws[i].children.count > 0)
|
||||
{
|
||||
const FetchDrawcall *draw = GetDrawcall(draws[i].children, eventID);
|
||||
if (draw != NULL) return draw;
|
||||
}
|
||||
|
||||
if (draws[i].eventID == eventID)
|
||||
return &draws[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const FetchDrawcall *GetDrawcall(uint32_t frameID, uint32_t eventID)
|
||||
{
|
||||
if (m_Drawcalls == NULL || frameID >= (uint32_t)m_FrameInfo.count)
|
||||
return NULL;
|
||||
|
||||
return GetDrawcall(m_Drawcalls[frameID], eventID);
|
||||
}
|
||||
|
||||
rdctype::array<FetchDrawcall> *m_Drawcalls;
|
||||
|
||||
APIProperties m_APIProps;
|
||||
rdctype::array<FetchFrameInfo> m_FrameInfo;
|
||||
|
||||
QMap<ResourceId, FetchTexture*> m_Textures;
|
||||
rdctype::array<FetchTexture> m_TextureList;
|
||||
QMap<ResourceId, FetchBuffer*> m_Buffers;
|
||||
rdctype::array<FetchBuffer> m_BufferList;
|
||||
|
||||
// Windows
|
||||
MainWindow *m_MainWindow;
|
||||
};
|
||||
|
||||
// Utility class for invoking a lambda on the GUI thread.
|
||||
@@ -20,21 +168,53 @@ class Core
|
||||
// wise not to require a higher version that necessary.
|
||||
#include <functional>
|
||||
|
||||
class QInvoke : public QObject
|
||||
class GUIInvoke : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QInvoke(const std::function<void()> &f) : func(f) {}
|
||||
GUIInvoke(const std::function<void()> &f) : func(f) {}
|
||||
std::function<void()> func;
|
||||
public:
|
||||
public:
|
||||
static void call(const std::function<void()> &f);
|
||||
static void blockcall(const std::function<void()> &f);
|
||||
|
||||
protected slots:
|
||||
protected slots:
|
||||
void doInvoke()
|
||||
{
|
||||
func();
|
||||
deleteLater();
|
||||
func();
|
||||
deleteLater();
|
||||
}
|
||||
};
|
||||
|
||||
// Utility class for calling a lambda on a new thread.
|
||||
#include <QThread>
|
||||
|
||||
class LambdaThread : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
std::function<void()> m_func;
|
||||
QThread *m_Thread;
|
||||
|
||||
public slots:
|
||||
void process()
|
||||
{
|
||||
m_func();
|
||||
m_Thread->quit();
|
||||
deleteLater();
|
||||
m_Thread->deleteLater();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit LambdaThread(std::function<void()> f)
|
||||
{
|
||||
m_Thread = new QThread();
|
||||
m_func = f;
|
||||
moveToThread(m_Thread);
|
||||
connect(m_Thread, SIGNAL(started()), this, SLOT(process()));
|
||||
}
|
||||
|
||||
void start(QThread::Priority prio = QThread::InheritPriority) { m_Thread->start(prio); }
|
||||
bool isRunning() { return m_Thread->isRunning(); }
|
||||
};
|
||||
|
||||
#endif // CORE_H
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include "RenderManager.h"
|
||||
|
||||
#include "Core.h"
|
||||
|
||||
#include <QMutexLocker>
|
||||
|
||||
RenderManager::RenderManager()
|
||||
{
|
||||
m_Running = false;
|
||||
m_Thread = NULL;
|
||||
}
|
||||
|
||||
RenderManager::~RenderManager()
|
||||
@@ -24,9 +27,15 @@ void RenderManager::Init(int proxyRenderer, QString replayHost, QString logfile,
|
||||
|
||||
*progress = 0.0f;
|
||||
|
||||
start(HighestPriority);
|
||||
m_Thread = new LambdaThread([this]() { run(); });
|
||||
m_Thread->start(QThread::HighestPriority);
|
||||
|
||||
while (!isRunning()) {}
|
||||
while (m_Thread->isRunning() && !m_Running) {}
|
||||
}
|
||||
|
||||
bool RenderManager::IsRunning()
|
||||
{
|
||||
return m_Thread->isRunning() && m_Running;
|
||||
}
|
||||
|
||||
void RenderManager::AsyncInvoke(RenderManager::InvokeMethod m)
|
||||
@@ -53,12 +62,14 @@ void RenderManager::CloseThread()
|
||||
m_RenderCondition.wakeAll();
|
||||
|
||||
// wait for the thread to close and clean up
|
||||
while(isRunning()) {}
|
||||
while(m_Thread->isRunning()) {}
|
||||
|
||||
m_Thread->deleteLater();
|
||||
}
|
||||
|
||||
void RenderManager::PushInvoke(RenderManager::InvokeHandle *cmd)
|
||||
{
|
||||
if(!isRunning() || !m_Running)
|
||||
if(m_Thread == NULL || !m_Thread->isRunning() || !m_Running)
|
||||
{
|
||||
cmd->processed = true;
|
||||
if(cmd->selfdelete) delete cmd;
|
||||
|
||||
@@ -12,12 +12,10 @@
|
||||
#include <QWaitCondition>
|
||||
|
||||
struct IReplayRenderer;
|
||||
class LambdaThread;
|
||||
|
||||
class RenderManager : public QThread
|
||||
class RenderManager
|
||||
{
|
||||
Q_OBJECT
|
||||
void run();
|
||||
|
||||
public:
|
||||
typedef std::function<void(IReplayRenderer*)> InvokeMethod;
|
||||
|
||||
@@ -26,7 +24,7 @@ class RenderManager : public QThread
|
||||
|
||||
void Init(int proxyRenderer, QString replayHost, QString logfile, float *progress);
|
||||
|
||||
bool IsRunning() { return isRunning() && m_Running; }
|
||||
bool IsRunning();
|
||||
ReplayCreateStatus GetCreateStatus() { return m_CreateStatus; }
|
||||
|
||||
void AsyncInvoke(InvokeMethod m);
|
||||
@@ -49,6 +47,8 @@ class RenderManager : public QThread
|
||||
bool selfdelete;
|
||||
};
|
||||
|
||||
void run();
|
||||
|
||||
QMutex m_RenderLock;
|
||||
QQueue<InvokeHandle *> m_RenderQueue;
|
||||
QWaitCondition m_RenderCondition;
|
||||
@@ -61,6 +61,7 @@ class RenderManager : public QThread
|
||||
float *m_Progress;
|
||||
|
||||
volatile bool m_Running;
|
||||
LambdaThread *m_Thread;
|
||||
ReplayCreateStatus m_CreateStatus;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +1,65 @@
|
||||
#include "Windows/MainWindow.h"
|
||||
#include <QApplication>
|
||||
#include "Code/Core.h"
|
||||
|
||||
#include "renderdoc_replay.h"
|
||||
#include <QApplication>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QFileInfo>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
RENDERDOC_LogText("QRenderDoc initialising.");
|
||||
|
||||
QString filename = "";
|
||||
bool temp = false;
|
||||
|
||||
for(int i=0; i < argc; i++)
|
||||
{
|
||||
if(!QString::compare(argv[i], "--tempfile", Qt::CaseInsensitive))
|
||||
temp = true;
|
||||
}
|
||||
|
||||
QString remoteHost = "";
|
||||
uint remoteIdent = 0;
|
||||
|
||||
for (int i = 0; i+1 < argc; i++)
|
||||
{
|
||||
if (!QString::compare(argv[i], "--REMOTEACCESS", Qt::CaseInsensitive))
|
||||
{
|
||||
QRegularExpression regexp("^([a-zA-Z0-9_-]+:)?([0-9]+)$");
|
||||
|
||||
QRegularExpressionMatch match = regexp.match(argv[i+1]);
|
||||
|
||||
if (match.hasMatch())
|
||||
{
|
||||
QString host = match.captured(1);
|
||||
|
||||
if (host.length() > 0 && host[host.length() - 1] == ':')
|
||||
host.chop(1);
|
||||
|
||||
bool ok = false;
|
||||
uint32_t ident = match.captured(2).toUInt(&ok);
|
||||
|
||||
if(ok)
|
||||
{
|
||||
remoteHost = host;
|
||||
remoteIdent = ident;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
filename = argv[argc - 1];
|
||||
QFileInfo checkFile(filename);
|
||||
if(!checkFile.exists() || !checkFile.isFile())
|
||||
filename = "";
|
||||
}
|
||||
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
Core core(filename, remoteHost, remoteIdent, temp);
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user