From bb6452c3344f6f632af8c4065bbabae917af59c4 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 16 Nov 2017 14:35:48 +0000 Subject: [PATCH] Expand IStackResolver into ICaptureAccess to allow section read/write --- qrenderdoc/Code/Interface/QRDInterface.h | 2 +- qrenderdoc/Code/ReplayManager.h | 6 +- qrenderdoc/Windows/APIInspector.cpp | 4 +- qrenderdoc/Windows/MainWindow.cpp | 6 +- renderdoc/api/replay/renderdoc_replay.h | 60 ++++- renderdoc/core/remote_server.cpp | 281 ++++++++++++++++++++++- renderdoc/replay/capture_file.cpp | 65 ++++++ 7 files changed, 398 insertions(+), 26 deletions(-) diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index 34c656a38..f075ffb68 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -668,7 +668,7 @@ struct IReplayManager :return: The file handle active, or ``None`` if no capture is open. :rtype: StackResolver )"); - virtual IStackResolver *GetResolver() = 0; + virtual ICaptureAccess *GetCaptureAccess() = 0; DOCUMENT(R"(Launch an application and inject into it to allow capturing. diff --git a/qrenderdoc/Code/ReplayManager.h b/qrenderdoc/Code/ReplayManager.h index 3bf707487..d3c028c44 100644 --- a/qrenderdoc/Code/ReplayManager.h +++ b/qrenderdoc/Code/ReplayManager.h @@ -75,12 +75,12 @@ public: void ShutdownServer(); void PingRemote(); - IStackResolver *GetResolver() + ICaptureAccess *GetCaptureAccess() { - if(m_CaptureFile) - return m_CaptureFile; if(m_Remote) return m_Remote; + if(m_CaptureFile) + return m_CaptureFile; return NULL; } diff --git a/qrenderdoc/Windows/APIInspector.cpp b/qrenderdoc/Windows/APIInspector.cpp index fdfd258b4..a4ca83ee7 100644 --- a/qrenderdoc/Windows/APIInspector.cpp +++ b/qrenderdoc/Windows/APIInspector.cpp @@ -101,10 +101,10 @@ void APIInspector::on_apiEvents_itemSelectionChanged() if(!ev.callstack.isEmpty()) { - if(m_Ctx.Replay().GetResolver()) + if(m_Ctx.Replay().GetCaptureAccess()) { m_Ctx.Replay().AsyncInvoke([this, ev](IReplayController *) { - rdcarray stack = m_Ctx.Replay().GetResolver()->GetResolve(ev.callstack); + rdcarray stack = m_Ctx.Replay().GetCaptureAccess()->GetResolve(ev.callstack); GUIInvoke::call([this, stack]() { addCallstack(stack); }); }); diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index 35019bb23..441d16b16 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -1241,7 +1241,7 @@ void MainWindow::OnCaptureLoaded() ui->action_Resolve_Symbols->setEnabled(false); m_Ctx.Replay().AsyncInvoke([this](IReplayController *) { - bool hasResolver = m_Ctx.Replay().GetResolver()->HasCallstacks(); + bool hasResolver = m_Ctx.Replay().GetCaptureAccess()->HasCallstacks(); GUIInvoke::call([this, hasResolver]() { ui->action_Resolve_Symbols->setEnabled(hasResolver); @@ -1505,7 +1505,7 @@ void MainWindow::on_action_Python_Shell_triggered() void MainWindow::on_action_Resolve_Symbols_triggered() { - if(!m_Ctx.Replay().GetResolver()) + if(!m_Ctx.Replay().GetCaptureAccess()) { RDDialog::critical( this, tr("Not Available"), @@ -1517,7 +1517,7 @@ void MainWindow::on_action_Resolve_Symbols_triggered() bool finished = false; m_Ctx.Replay().AsyncInvoke([this, &progress, &finished](IReplayController *) { - bool success = m_Ctx.Replay().GetResolver()->InitResolver(&progress, NULL); + bool success = m_Ctx.Replay().GetCaptureAccess()->InitResolver(&progress, NULL); if(!success) { diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index b5b1470e7..f9a4a845c 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -1074,11 +1074,57 @@ protected: ~ITargetControl() = default; }; -DOCUMENT(R"(An interface for resolving callstacks. This is separate since it can be either -implemented locally by a file handle, or remotely to an open capture. +DOCUMENT(R"(An interface for accessing a capture, possibly over a network connection. This is a +subset of the functionality provided in :class:`CaptureFile` which only supports import/export +and construction of files. )"); -struct IStackResolver +struct ICaptureAccess { + DOCUMENT(R"(Locate the index of a section by its name. Returns ``-1`` if the section is not found. + +This index should not be cached, as writing sections could re-order the indices. + +:param str name: The name of the section to search for. +:return: The index of the section, or ``-1`` if not found. +:rtype: ``int``. +)"); + virtual int FindSectionByName(const char *name) = 0; + + DOCUMENT(R"(Locate the index of a section by its type. Returns ``-1`` if the section is not found. + +This index should not be cached, as writing sections could re-order the indices. + +:param SectionType type: The type of the section to search for. +:return: The index of the section, or ``-1`` if not found. +:rtype: ``int``. +)"); + virtual int FindSectionByType(SectionType type) = 0; + + DOCUMENT(R"(Get the describing properties of the specified section. + +:param int index: The index of the section. +:return: The properties of the section, if the index is valid. +:rtype: SectionProperties. +)"); + virtual SectionProperties GetSectionProperties(int index) = 0; + + DOCUMENT(R"(Get the raw byte contents of the specified section. + +:param int index: The index of the section. +:return: The raw contents of the section, if the index is valid. +:rtype: ``bytes``. +)"); + virtual bytebuf GetSectionContents(int index) = 0; + + DOCUMENT(R"(Writes a new section with specified properties and contents. If an existing section +already has the same type or name, it will be overwritten (two sections cannot share the same type +or name). + +:param SectionProperties props: The properties of the section to be written. +:param byte contents: The raw contents of the section. +)"); + virtual void WriteSection(const SectionProperties &props, const bytebuf &contents) = 0; + DOCUMENT(R"(Query if callstacks are available. :return: ``True`` if any callstacks are available, ``False`` otherwise. @@ -1112,8 +1158,8 @@ Must only be called after :meth:`InitResolver` has returned ``True``. virtual rdcarray GetResolve(const rdcarray &callstack) = 0; protected: - IStackResolver() = default; - ~IStackResolver() = default; + ICaptureAccess() = default; + ~ICaptureAccess() = default; }; DOCUMENT(R"(A connection to a running remote RenderDoc server on another machine. This allows the @@ -1125,7 +1171,7 @@ much work as possible happening on the local machine. No preference for a particular value, see :meth:`DebugPixel`. )"); -struct IRemoteServer : public IStackResolver +struct IRemoteServer : public ICaptureAccess { DOCUMENT("Closes the connection without affecting the running server."); virtual void ShutdownConnection() = 0; @@ -1276,7 +1322,7 @@ protected: DOCUMENT(R"(A handle to a capture file. Used for simple cheap processing and meta-data fetching without opening the capture for analysis. )") -struct ICaptureFile : public IStackResolver +struct ICaptureFile : public ICaptureAccess { DOCUMENT("Closes the file handle."); virtual void Shutdown() = 0; diff --git a/renderdoc/core/remote_server.cpp b/renderdoc/core/remote_server.cpp index 79e89cb9a..85f1921c4 100644 --- a/renderdoc/core/remote_server.cpp +++ b/renderdoc/core/remote_server.cpp @@ -61,6 +61,11 @@ enum RemoteServerPacket eRemoteServer_ListDir, eRemoteServer_ExecuteAndInject, eRemoteServer_ShutdownServer, + eRemoteServer_FindSectionByName, + eRemoteServer_FindSectionByType, + eRemoteServer_GetSectionProperties, + eRemoteServer_GetSectionContents, + eRemoteServer_WriteSection, eRemoteServer_RemoteServerCount, }; @@ -547,6 +552,121 @@ static void ActiveRemoteClientThread(ClientThread *threadData) SERIALISE_ELEMENT(StackFrames); } } + else if(type == eRemoteServer_FindSectionByName) + { + std::string name; + + { + READ_DATA_SCOPE(); + SERIALISE_ELEMENT(name); + } + + reader.EndChunk(); + + int index = rdc ? rdc->SectionIndex(name.c_str()) : -1; + + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_FindSectionByName); + SERIALISE_ELEMENT(index); + } + } + else if(type == eRemoteServer_FindSectionByType) + { + SectionType sectionType; + + { + READ_DATA_SCOPE(); + SERIALISE_ELEMENT(sectionType); + } + + reader.EndChunk(); + + int index = rdc ? rdc->SectionIndex(sectionType) : -1; + + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_FindSectionByType); + SERIALISE_ELEMENT(index); + } + } + else if(type == eRemoteServer_GetSectionProperties) + { + int index = -1; + + { + READ_DATA_SCOPE(); + SERIALISE_ELEMENT(index); + } + + reader.EndChunk(); + + SectionProperties props; + if(rdc && index >= 0 && index < rdc->NumSections()) + props = rdc->GetSectionProperties(index); + + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_GetSectionProperties); + SERIALISE_ELEMENT(props); + } + } + else if(type == eRemoteServer_GetSectionContents) + { + int index = -1; + + { + READ_DATA_SCOPE(); + SERIALISE_ELEMENT(index); + } + + reader.EndChunk(); + + bytebuf contents; + + if(rdc && index >= 0 && index < rdc->NumSections()) + { + StreamReader *sectionReader = rdc->ReadSection(index); + + contents.resize((size_t)sectionReader->GetSize()); + bool success = sectionReader->Read(contents.data(), sectionReader->GetSize()); + + if(!success) + contents.clear(); + + delete sectionReader; + } + + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_GetSectionContents); + SERIALISE_ELEMENT(contents); + } + } + else if(type == eRemoteServer_WriteSection) + { + SectionProperties props; + bytebuf contents; + + { + READ_DATA_SCOPE(); + SERIALISE_ELEMENT(props); + SERIALISE_ELEMENT(contents); + } + + reader.EndChunk(); + + if(rdc) + { + StreamWriter *sectionWriter = rdc->WriteSection(props); + + if(sectionWriter) + { + sectionWriter->Write(contents.data(), contents.size()); + delete sectionWriter; + } + } + } else if(type == eRemoteServer_CloseLog) { reader.EndChunk(); @@ -1268,6 +1388,157 @@ public: return ret; } + void CloseCapture(IReplayController *rend) + { + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_CloseLog); + } + + rend->Shutdown(); + } + + int FindSectionByName(const char *name) + { + if(!Connected()) + return -1; + + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_FindSectionByName); + SERIALISE_ELEMENT(name); + } + + int index = -1; + + { + READ_DATA_SCOPE(); + RemoteServerPacket type = ser.ReadChunk(); + + if(type == eRemoteServer_FindSectionByName) + { + SERIALISE_ELEMENT(index); + } + else + { + RDCERR("Unexpected response to FindSectionByName"); + } + + ser.EndChunk(); + } + + return index; + } + + int FindSectionByType(SectionType sectionType) + { + if(!Connected()) + return -1; + + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_FindSectionByType); + SERIALISE_ELEMENT(sectionType); + } + + int index = -1; + + { + READ_DATA_SCOPE(); + RemoteServerPacket type = ser.ReadChunk(); + + if(type == eRemoteServer_FindSectionByType) + { + SERIALISE_ELEMENT(index); + } + else + { + RDCERR("Unexpected response to FindSectionByType"); + } + + ser.EndChunk(); + } + + return index; + } + + SectionProperties GetSectionProperties(int index) + { + if(!Connected()) + return SectionProperties(); + + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_GetSectionProperties); + SERIALISE_ELEMENT(index); + } + + SectionProperties props; + + { + READ_DATA_SCOPE(); + RemoteServerPacket type = ser.ReadChunk(); + + if(type == eRemoteServer_GetSectionProperties) + { + SERIALISE_ELEMENT(props); + } + else + { + RDCERR("Unexpected response to GetSectionProperties"); + } + + ser.EndChunk(); + } + + return props; + } + + bytebuf GetSectionContents(int index) override + { + if(!Connected()) + return bytebuf(); + + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_GetSectionContents); + SERIALISE_ELEMENT(index); + } + + bytebuf contents; + + { + READ_DATA_SCOPE(); + RemoteServerPacket type = ser.ReadChunk(); + + if(type == eRemoteServer_GetSectionContents) + { + SERIALISE_ELEMENT(contents); + } + else + { + RDCERR("Unexpected response to GetSectionContents"); + } + + ser.EndChunk(); + } + + return contents; + } + + void WriteSection(const SectionProperties &props, const bytebuf &contents) + { + if(!Connected()) + return; + + { + WRITE_DATA_SCOPE(); + SCOPED_SERIALISE_CHUNK(eRemoteServer_WriteSection); + SERIALISE_ELEMENT(props); + SERIALISE_ELEMENT(contents); + } + } + bool HasCallstacks() { if(!Connected()) @@ -1377,16 +1648,6 @@ public: return StackFrames; } - void CloseCapture(IReplayController *rend) - { - { - WRITE_DATA_SCOPE(); - SCOPED_SERIALISE_CHUNK(eRemoteServer_CloseLog); - } - - rend->Shutdown(); - } - private: Network::Socket *m_Socket; WriteSerialiser writer; diff --git a/renderdoc/replay/capture_file.cpp b/renderdoc/replay/capture_file.cpp index 594637f98..afc887f9e 100644 --- a/renderdoc/replay/capture_file.cpp +++ b/renderdoc/replay/capture_file.cpp @@ -162,6 +162,15 @@ public: } bytebuf GetThumbnail(FileType type, uint32_t maxsize); + + // ICaptureAccess + + int FindSectionByName(const char *name); + int FindSectionByType(SectionType type); + SectionProperties GetSectionProperties(int index); + bytebuf GetSectionContents(int index); + void WriteSection(const SectionProperties &props, const bytebuf &contents); + bool HasCallstacks(); bool InitResolver(float *progress, volatile bool *killSignal); rdcarray GetResolve(const rdcarray &callstack); @@ -213,6 +222,7 @@ ReplayStatus CaptureFile::OpenFile(const char *filename, const char *filetype) if(filetype != NULL && strcmp(filetype, "") && strcmp(filetype, "rdc")) RDCWARN("Opening file with unrecognised filetype '%s' - treating as 'rdc'", filetype); + delete m_RDC; m_RDC = new RDCFile; m_RDC->Open(filename); } @@ -582,6 +592,61 @@ bytebuf CaptureFile::GetThumbnail(FileType type, uint32_t maxsize) return buf; } +int CaptureFile::FindSectionByName(const char *name) +{ + if(!m_RDC) + return -1; + + return m_RDC->SectionIndex(name); +} + +int CaptureFile::FindSectionByType(SectionType type) +{ + if(!m_RDC) + return -1; + + return m_RDC->SectionIndex(type); +} + +SectionProperties CaptureFile::GetSectionProperties(int index) +{ + if(!m_RDC || index < 0 || index >= m_RDC->NumSections()) + return SectionProperties(); + + return m_RDC->GetSectionProperties(index); +} + +bytebuf CaptureFile::GetSectionContents(int index) +{ + bytebuf ret; + + if(!m_RDC || index < 0 || index >= m_RDC->NumSections()) + return ret; + + StreamReader *reader = m_RDC->ReadSection(index); + + ret.resize((size_t)reader->GetSize()); + bool success = reader->Read(ret.data(), reader->GetSize()); + + delete reader; + + if(!success) + ret.clear(); + + return ret; +} + +void CaptureFile::WriteSection(const SectionProperties &props, const bytebuf &contents) +{ + StreamWriter *writer = m_RDC->WriteSection(props); + if(!writer) + return; + + writer->Write(contents.data(), contents.size()); + + delete writer; +} + bool CaptureFile::HasCallstacks() { return m_RDC && m_RDC->SectionIndex(SectionType::ResolveDatabase) >= 0;