diff --git a/qrenderdoc/Windows/APIInspector.cpp b/qrenderdoc/Windows/APIInspector.cpp index 1855774b9..d4723402c 100644 --- a/qrenderdoc/Windows/APIInspector.cpp +++ b/qrenderdoc/Windows/APIInspector.cpp @@ -34,6 +34,7 @@ APIInspector::APIInspector(ICaptureContext &ctx, QWidget *parent) ui->setupUi(this); ui->apiEvents->setColumns({lit("EID"), tr("Event")}); + ui->apiEvents->header()->resizeSection(0, 200); ui->splitter->setCollapsible(1, true); ui->splitter->setSizes({1, 0}); @@ -134,43 +135,128 @@ void APIInspector::fillAPIView() QRegularExpression rgxopen(lit("^\\s*{")); QRegularExpression rgxclose(lit("^\\s*}")); - const DrawcallDescription *draw = m_Ctx.CurSelectedDrawcall(); + m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) { + const SDFile &file = r->GetStructuredFile(); - if(draw != NULL && !draw->events.isEmpty()) - { - for(const APIEvent &ev : draw->events) - { - QStringList lines = QString(ev.eventDesc).split(lit("\n"), QString::SkipEmptyParts); + GUIInvoke::call([this, &file]() { + const DrawcallDescription *draw = m_Ctx.CurSelectedDrawcall(); - RDTreeWidgetItem *root = new RDTreeWidgetItem({QString::number(ev.eventID), lines[0]}); - - int i = 1; - - if(i < lines.count() && lines[i].trimmed() == lit("{")) - i++; - - QList nodestack; - nodestack.push_back(root); - - for(; i < lines.count(); i++) + if(draw != NULL && !draw->events.isEmpty()) { - if(rgxopen.match(lines[i]).hasMatch()) - nodestack.push_back(nodestack.back()->child(nodestack.back()->childCount() - 1)); - else if(rgxclose.match(lines[i]).hasMatch()) - nodestack.pop_back(); - else if(!nodestack.empty()) - nodestack.back()->addChild(new RDTreeWidgetItem({QString(), lines[i].trimmed()})); + for(const APIEvent &ev : draw->events) + { + RDTreeWidgetItem *root = new RDTreeWidgetItem({QString::number(ev.eventID), QString()}); + + if(ev.chunkIndex < file.chunks.size()) + { + SDChunk *chunk = file.chunks[ev.chunkIndex]; + + root->setText(1, chunk->name); + + addObjects(root, chunk->data.children, false); + } + else + { + root->setText(1, tr("Invalid chunk index %1").arg(ev.chunkIndex)); + } + + if(ev.eventID == draw->eventID) + root->setBold(true); + + root->setTag(QVariant::fromValue(ev)); + + ui->apiEvents->addTopLevelItem(root); + + ui->apiEvents->setSelectedItem(root); + } } - - if(ev.eventID == draw->eventID) - root->setBold(true); - - root->setTag(QVariant::fromValue(ev)); - - ui->apiEvents->addTopLevelItem(root); - - ui->apiEvents->setSelectedItem(root); - } - } - ui->apiEvents->setUpdatesEnabled(true); + ui->apiEvents->setUpdatesEnabled(true); + }); + }); +} + +void APIInspector::addObjects(RDTreeWidgetItem *parent, const StructuredObjectList &objs, + bool parentIsArray) +{ + for(const SDObject *obj : objs) + { + if(obj->type.flags & SDTypeFlags::Hidden) + continue; + + QString param; + + if(parentIsArray) + param = QFormatStr("[%1]").arg(parent->childCount()); + else + param = obj->name; + + RDTreeWidgetItem *item = new RDTreeWidgetItem({param, QString()}); + + param = QString(); + + // we don't identify via the type name as many types could be serialised as a ResourceId - + // e.g. ID3D11Resource* or ID3D11Buffer* which would be the actual typename. We want to preserve + // that for the best raw structured data representation instead of flattening those out to just + // "ResourceId", and we also don't want to store two types ('fake' and 'real'), so instead we + // check the custom string. + if((obj->type.flags & SDTypeFlags::HasCustomString) && + !strncmp(obj->data.str.c_str(), "ResourceId", 10)) + { + ResourceId id; + static_assert(sizeof(id) == sizeof(obj->data.basic.u), "ResourceId is no longer uint64_t!"); + memcpy(&id, &obj->data.basic.u, sizeof(id)); + // for resource IDs, try to locate the resource. + TextureDescription *tex = m_Ctx.GetTexture(id); + BufferDescription *buf = m_Ctx.GetBuffer(id); + + if(tex) + { + param += tex->name; + } + else if(buf) + { + param += buf->name; + } + else + { + param += lit("%1 %2").arg(obj->type.name).arg(obj->data.basic.u); + } + } + else if(obj->type.flags & SDTypeFlags::NullString) + { + param += lit("NULL"); + } + else if(obj->type.flags & SDTypeFlags::HasCustomString) + { + param += obj->data.str; + } + else + { + switch(obj->type.basetype) + { + case SDBasic::Chunk: + case SDBasic::Struct: + param += QFormatStr("%1()").arg(obj->type.name); + addObjects(item, obj->data.children, false); + break; + case SDBasic::Array: + param += QFormatStr("%1[]").arg(obj->type.name); + addObjects(item, obj->data.children, true); + break; + case SDBasic::Null: param += lit("NULL"); break; + case SDBasic::Buffer: param += lit("(%1 byte buffer)").arg(obj->type.byteSize); break; + case SDBasic::String: param += obj->data.str; break; + case SDBasic::Enum: + case SDBasic::UnsignedInteger: param += Formatter::Format(obj->data.basic.u); break; + case SDBasic::SignedInteger: param += Formatter::Format(obj->data.basic.i); break; + case SDBasic::Float: param += Formatter::Format(obj->data.basic.d); break; + case SDBasic::Boolean: param += (obj->data.basic.b ? tr("True") : tr("False")); break; + case SDBasic::Character: param += QLatin1Char(obj->data.basic.c); break; + } + } + + item->setText(1, param); + + parent->addChild(item); + } } diff --git a/qrenderdoc/Windows/APIInspector.h b/qrenderdoc/Windows/APIInspector.h index 98217c48c..567535c86 100644 --- a/qrenderdoc/Windows/APIInspector.h +++ b/qrenderdoc/Windows/APIInspector.h @@ -32,6 +32,8 @@ namespace Ui class APIInspector; } +class RDTreeWidgetItem; + class APIInspector : public QFrame, public IAPIInspector, public ILogViewer { Q_OBJECT @@ -57,4 +59,5 @@ private: void addCallstack(rdcarray calls); void fillAPIView(); + void addObjects(RDTreeWidgetItem *parent, const StructuredObjectList &objs, bool parentIsArray); }; diff --git a/renderdoc/api/replay/data_types.h b/renderdoc/api/replay/data_types.h index 9a575fe8f..834577fcd 100644 --- a/renderdoc/api/replay/data_types.h +++ b/renderdoc/api/replay/data_types.h @@ -248,8 +248,8 @@ results part way through the multi draw. DOCUMENT("A list of addresses in the CPU callstack where this function was called."); rdcarray callstack; - DOCUMENT("A raw debug string with the serialised form of the function call parameters."); - rdcstr eventDesc; + DOCUMENT("The chunk index for this function call in the structured file."); + uint32_t chunkIndex; DOCUMENT(R"(A byte offset in the data stream where this event happens. diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 2cffdd68c..2beeb3afb 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -760,6 +760,13 @@ See :meth:`BuildTargetShader`. )"); virtual FrameDescription GetFrameInfo() = 0; + DOCUMENT(R"(Fetch the structured data representation of the capture loaded. + +:return: The structured file. +:rtype: SDFile +)"); + virtual const SDFile &GetStructuredFile() = 0; + DOCUMENT(R"(Retrieve the list of root-level drawcalls in the capture. :return: The list of root-level drawcalls in the capture. diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index fa342080a..d503b6f52 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -149,6 +149,7 @@ public: const D3D11Pipe::State &GetD3D11PipelineState() { return m_PipelineState; } // other operations are dropped/ignored, to avoid confusion void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) {} + const SDFile &GetStructuredFile() { return m_File; } void RenderMesh(uint32_t eventID, const vector &secondaryDraws, const MeshDisplay &cfg) { } @@ -276,6 +277,7 @@ private: IReplayDriver *m_Proxy; string m_Filename; ResourceId m_TextureID; + SDFile m_File; TextureDescription m_TexDetails; }; diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 8e363509f..2aadb8310 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -1255,6 +1255,64 @@ void ReplayProxy::ReplayLog(uint32_t endEventID, ReplayLogType replayType) PROXY_FUNCTION(ReplayLog, endEventID, replayType); } +template +void ReplayProxy::Proxied_FetchStructuredFile(ParamSerialiser ¶mser, ReturnSerialiser &retser) +{ + const ReplayProxyPacket packet = eReplayProxy_FetchStructuredFile; + + { + BEGIN_PARAMS(); + END_PARAMS(); + } + + SDFile *file = &m_StructuredFile; + + if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) + file = (SDFile *)&m_Remote->GetStructuredFile(); + + { + ReturnSerialiser &ser = retser; + PACKET_HEADER(packet); + + uint64_t chunkCount = file->chunks.size(); + SERIALISE_ELEMENT(chunkCount); + + if(retser.IsReading()) + file->chunks.resize((size_t)chunkCount); + + for(size_t c = 0; c < (size_t)chunkCount; c++) + { + if(retser.IsReading()) + file->chunks[c] = new SDChunk(""); + + ser.Serialise("chunk", *file->chunks[c]); + } + + uint64_t bufferCount = file->buffers.size(); + SERIALISE_ELEMENT(bufferCount); + + if(retser.IsReading()) + file->buffers.resize((size_t)bufferCount); + + for(size_t b = 0; b < (size_t)bufferCount; b++) + { + if(retser.IsReading()) + file->buffers[b] = new bytebuf; + + bytebuf *buf = file->buffers[b]; + + ser.Serialise("buffer", *buf); + } + + ser.EndChunk(); + } +} + +void ReplayProxy::FetchStructuredFile() +{ + PROXY_FUNCTION(FetchStructuredFile); +} + #pragma endregion Proxied Functions // If a remap is required, modify the params that are used when getting the proxy texture data @@ -1400,6 +1458,7 @@ bool ReplayProxy::Tick(int type) switch(type) { case eReplayProxy_ReplayLog: ReplayLog(0, (ReplayLogType)0); break; + case eReplayProxy_FetchStructuredFile: FetchStructuredFile(); break; case eReplayProxy_GetAPIProperties: GetAPIProperties(); break; case eReplayProxy_GetPassEvents: GetPassEvents(0); break; case eReplayProxy_GetTextures: GetTextures(); break; diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index a54bb66dc..6f6fdd97e 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -38,6 +38,8 @@ enum ReplayProxyPacket eReplayProxy_ReplayLog = eReplayProxy_First, eReplayProxy_GetAPIProperties, + eReplayProxy_FetchStructuredFile, + eReplayProxy_GetPassEvents, eReplayProxy_GetTextures, @@ -103,6 +105,7 @@ public: : m_Reader(reader), m_Writer(writer), m_Proxy(proxy), m_Remote(NULL), m_RemoteServer(false) { GetAPIProperties(); + FetchStructuredFile(); } ReplayProxy(ReadSerialiser &reader, WriteSerialiser &writer, IRemoteDriver *remote) @@ -385,6 +388,9 @@ public: const D3D12Pipe::State &GetD3D12PipelineState() { return m_D3D12PipelineState; } const GLPipe::State &GetGLPipelineState() { return m_GLPipelineState; } const VKPipe::State &GetVulkanPipelineState() { return m_VulkanPipelineState; } + const SDFile &GetStructuredFile() { return m_StructuredFile; } + IMPLEMENT_FUNCTION_PROXIED(void, FetchStructuredFile); + IMPLEMENT_FUNCTION_PROXIED(std::vector, GetBuffers); IMPLEMENT_FUNCTION_PROXIED(BufferDescription, GetBuffer, ResourceId id); @@ -551,6 +557,8 @@ private: APIProperties m_APIProps; + SDFile m_StructuredFile; + D3D11Pipe::State m_D3D11PipelineState; D3D12Pipe::State m_D3D12PipelineState; GLPipe::State m_GLPipelineState; diff --git a/renderdoc/driver/d3d11/d3d11_context.cpp b/renderdoc/driver/d3d11/d3d11_context.cpp index e1735ac7f..59caf5efa 100644 --- a/renderdoc/driver/d3d11/d3d11_context.cpp +++ b/renderdoc/driver/d3d11/d3d11_context.cpp @@ -1008,8 +1008,7 @@ void WrappedID3D11DeviceContext::AddEvent() apievent.fileOffset = m_CurChunkOffset; apievent.eventID = m_CurEventID; - // TODO structured data? - apievent.eventDesc = "TODO"; + apievent.chunkIndex = uint32_t(m_StructuredFile->chunks.size() - 1); apievent.callstack = m_ChunkMetadata.callstack; @@ -1058,6 +1057,12 @@ void WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32_t start ser.ConfigureStructuredExport(&GetChunkName, IsStructuredExporting(m_State)); ser.GetStructuredFile().swap(m_pDevice->GetStructuredFile()); + + m_StructuredFile = &ser.GetStructuredFile(); + } + else + { + m_StructuredFile = &m_pDevice->GetStructuredFile(); } m_DoStateVerify = true; @@ -1208,6 +1213,8 @@ void WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32_t start if(IsLoading(m_State) || IsStructuredExporting(m_State)) ser.GetStructuredFile().swap(m_pDevice->GetStructuredFile()); + m_StructuredFile = NULL; + m_pDevice->GetResourceManager()->MarkInFrame(false); m_DoStateVerify = false; diff --git a/renderdoc/driver/d3d11/d3d11_context.h b/renderdoc/driver/d3d11/d3d11_context.h index 868c33797..233d7274d 100644 --- a/renderdoc/driver/d3d11/d3d11_context.h +++ b/renderdoc/driver/d3d11/d3d11_context.h @@ -194,6 +194,8 @@ private: vector m_AnnotationQueue; Threading::CriticalSection m_AnnotLock; + SDFile *m_StructuredFile = NULL; + uint64_t m_CurChunkOffset; SDChunkMetaData m_ChunkMetadata; uint32_t m_CurEventID, m_CurDrawcallID; diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index e90968d6b..ea5bb1286 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -1300,6 +1300,11 @@ void D3D11Replay::ReplayLog(uint32_t endEventID, ReplayLogType replayType) m_pDevice->ReplayLog(0, endEventID, replayType); } +const SDFile &D3D11Replay::GetStructuredFile() +{ + return m_pDevice->GetStructuredFile(); +} + vector D3D11Replay::GetPassEvents(uint32_t eventID) { vector passEvents; diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index b59dae3a9..b1f5f45ab 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -75,6 +75,7 @@ public: void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); + const SDFile &GetStructuredFile(); vector GetPassEvents(uint32_t eventID); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 0e0c69cc4..a4995fd9f 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -2790,8 +2790,7 @@ void WrappedVulkan::AddEvent() ? m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID : m_RootEventID; - // TODO structured data? - apievent.eventDesc = "TODO"; + apievent.chunkIndex = uint32_t(m_StructuredFile->chunks.size() - 1); apievent.callstack = m_ChunkMetadata.callstack; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 60dd8ca5b..551b26538 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -669,6 +669,11 @@ void VulkanReplay::ReplayLog(uint32_t endEventID, ReplayLogType replayType) m_pDriver->ReplayLog(0, endEventID, replayType); } +const SDFile &VulkanReplay::GetStructuredFile() +{ + return m_pDriver->GetStructuredFile(); +} + vector VulkanReplay::GetPassEvents(uint32_t eventID) { vector passEvents; diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 3bade8979..15a7b1e30 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -161,6 +161,7 @@ public: void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); + const SDFile &GetStructuredFile(); vector GetPassEvents(uint32_t eventID); diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 26e6bab0f..25b2306ad 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -386,10 +386,10 @@ void DoSerialise(SerialiserType &ser, APIEvent &el) { SERIALISE_MEMBER(eventID); SERIALISE_MEMBER(callstack); - SERIALISE_MEMBER(eventDesc); + SERIALISE_MEMBER(chunkIndex); SERIALISE_MEMBER(fileOffset); - SIZE_CHECK(48); + SIZE_CHECK(40); } template diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 27b8b36be..3b80c0744 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -258,6 +258,11 @@ FrameDescription ReplayController::GetFrameInfo() return m_FrameRecord.frameInfo; } +const SDFile &ReplayController::GetStructuredFile() +{ + return m_pDevice->GetStructuredFile(); +} + DrawcallDescription *ReplayController::GetDrawcallByEID(uint32_t eventID) { if(eventID >= m_Drawcalls.size()) diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 6173c8b72..4c2b99362 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -156,6 +156,7 @@ public: void FreeTargetResource(ResourceId id); FrameDescription GetFrameInfo(); + const SDFile &GetStructuredFile(); rdcarray GetDrawcalls(); rdcarray FetchCounters(const rdcarray &counters); rdcarray EnumerateCounters(); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index c28b11ed0..7d8a2e0bf 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -116,6 +116,7 @@ public: virtual void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) = 0; virtual void ReplayLog(uint32_t endEventID, ReplayLogType replayType) = 0; + virtual const SDFile &GetStructuredFile() = 0; virtual vector GetPassEvents(uint32_t eventID) = 0;