diff --git a/qrenderdoc/Code/ReplayManager.cpp b/qrenderdoc/Code/ReplayManager.cpp index 67bdd3bd1..a105e3848 100644 --- a/qrenderdoc/Code/ReplayManager.cpp +++ b/qrenderdoc/Code/ReplayManager.cpp @@ -423,7 +423,13 @@ void ReplayManager::run(int proxyRenderer, const QString &capturefile, float *pr } if(m_Renderer == NULL) + { + if(m_CaptureFile) + m_CaptureFile->Shutdown(); + m_CaptureFile = NULL; + return; + } qInfo() << "QRenderDoc - renderer created for" << capturefile; diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index dc9686509..8ba80e9f6 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -423,10 +423,18 @@ void MainWindow::LoadCapture(const QString &filename, bool temporary, bool local { ICaptureFile *file = RENDERDOC_OpenCaptureFile(); - if(file->OpenFile(filename.toUtf8().data(), "rdc") != ReplayStatus::Succeeded) + ReplayStatus status = file->OpenFile(filename.toUtf8().data(), "rdc"); + + if(status != ReplayStatus::Succeeded) { - RDDialog::critical(NULL, tr("Error opening capture"), - tr("Couldn't open file '%1'").arg(filename)); + QString text = tr("Couldn't open file '%1'\n").arg(filename); + QString message = file->ErrorString(); + if(message.isEmpty()) + text += tr("%1").arg(ToQStr(status)); + else + text += tr("%1: %2").arg(ToQStr(status)).arg(message); + + RDDialog::critical(NULL, tr("Error opening capture"), text); file->Shutdown(); return; @@ -559,6 +567,8 @@ void MainWindow::LoadCapture(const QString &filename, bool temporary, bool local } } + statusText->setText(tr("Loading %1...").arg(origFilename)); + m_Ctx.LoadCapture(fileToLoad, origFilename, temporary, local); } @@ -566,8 +576,6 @@ void MainWindow::LoadCapture(const QString &filename, bool temporary, bool local { m_Ctx.Config().LastCaptureFilePath = QFileInfo(filename).absolutePath(); } - - statusText->setText(tr("Loading %1...").arg(origFilename)); } } @@ -976,6 +984,7 @@ void MainWindow::setProgress(float val) if(val < 0.0f || val >= 1.0f) { statusProgress->setVisible(false); + statusText->setText(QString()); } else { diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index ae96461fa..3a1ca5f25 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -1406,6 +1406,16 @@ representation back to native RDC. )"); virtual ReplayStatus Convert(const char *filename, const char *filetype, float *progress) = 0; + DOCUMENT(R"(Returns the human-readable error string for the last error received. + +The error string is not reset by calling this function so it's safe to call multiple times. However +any other function call may reset the error string to empty. + +:return: The error string, if one exists, or an empty string. +:rtype: str +)"); + virtual rdcstr ErrorString() = 0; + DOCUMENT(R"(Returns the list of capture file formats. :return: The list of capture file formats available. diff --git a/renderdoc/api/replay/renderdoc_tostr.inl b/renderdoc/api/replay/renderdoc_tostr.inl index 2d8415aeb..5bf51f0a0 100644 --- a/renderdoc/api/replay/renderdoc_tostr.inl +++ b/renderdoc/api/replay/renderdoc_tostr.inl @@ -39,10 +39,16 @@ std::string DoStringise(const ReplayStatus &el) STRINGISE_ENUM_CLASS_NAMED(FileIOFailed, "File I/O failed"); STRINGISE_ENUM_CLASS_NAMED(FileIncompatibleVersion, "File of incompatible version"); STRINGISE_ENUM_CLASS_NAMED(FileCorrupted, "File corrupted"); - STRINGISE_ENUM_CLASS_NAMED(APIUnsupported, "API unsupported"); + STRINGISE_ENUM_CLASS_NAMED(APIUnsupported, "API is not unsupported"); STRINGISE_ENUM_CLASS_NAMED(APIInitFailed, "API initialisation failed"); - STRINGISE_ENUM_CLASS_NAMED(APIIncompatibleVersion, "API incompatible version"); - STRINGISE_ENUM_CLASS_NAMED(APIHardwareUnsupported, "API hardware unsupported"); + STRINGISE_ENUM_CLASS_NAMED(APIIncompatibleVersion, + "Captured API data has an incompatible version"); + STRINGISE_ENUM_CLASS_NAMED( + APIHardwareUnsupported, + "Current hardware unsupported or incompatible with captured hardware"); + STRINGISE_ENUM_CLASS_NAMED(APIDataCorrupted, + "Replaying the capture encountered invalid/corrupted data"); + STRINGISE_ENUM_CLASS_NAMED(APIReplayFailed, "Replaying the capture failed at the API level"); } END_ENUM_STRINGISE(); } diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 2377e30da..26640ebd3 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -2833,6 +2833,15 @@ a remote server. .. data:: APIHardwareUnsupported The API is not supported on the currently available hardware. + +.. data:: APIDataCorrupted + + While loading the capture for replay, the driver encountered corrupted or invalid serialised data. + +.. data:: APIReplayFailed + + The API failed to replay the capture, with some runtime error that couldn't be determined until + the replay began. )"); enum class ReplayStatus : uint32_t { @@ -2853,6 +2862,8 @@ enum class ReplayStatus : uint32_t APIInitFailed, APIIncompatibleVersion, APIHardwareUnsupported, + APIDataCorrupted, + APIReplayFailed, }; DECLARE_REFLECTION_ENUM(ReplayStatus); diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index 822035d1d..6b381c0b3 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -151,7 +151,10 @@ public: FrameRecord GetFrameRecord() { return m_FrameRecord; } const D3D11Pipe::State &GetD3D11PipelineState() { return m_PipelineState; } // other operations are dropped/ignored, to avoid confusion - void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) {} + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) + { + return ReplayStatus::Succeeded; + } const SDFile &GetStructuredFile() { return m_File; } void RenderMesh(uint32_t eventID, const vector &secondaryDraws, const MeshDisplay &cfg) { diff --git a/renderdoc/core/remote_server.cpp b/renderdoc/core/remote_server.cpp index a113dddc9..85c7a8bc4 100644 --- a/renderdoc/core/remote_server.cpp +++ b/renderdoc/core/remote_server.cpp @@ -205,6 +205,7 @@ static void ActiveRemoteClientThread(ClientThread *threadData) ReadSerialiser reader(new StreamReader(client, Ownership::Nothing), Ownership::Stream); writer.SetStreamingMode(true); + reader.SetStreamingMode(true); while(client) { @@ -429,15 +430,25 @@ static void ActiveRemoteClientThread(ClientThread *threadData) } else { - driver->ReadLogInitialisation(rdc, false); + status = driver->ReadLogInitialisation(rdc, false); - RenderDoc::Inst().SetProgressPtr(NULL); + if(status != ReplayStatus::Succeeded) + { + RDCERR("Failed to initialise remote driver."); - kill = true; - Threading::JoinThread(ticker); - Threading::CloseThread(ticker); + driver->Shutdown(); + driver = NULL; + } + else + { + RenderDoc::Inst().SetProgressPtr(NULL); - proxy = new ReplayProxy(reader, writer, driver); + kill = true; + Threading::JoinThread(ticker); + Threading::CloseThread(ticker); + + proxy = new ReplayProxy(reader, writer, driver); + } } } else @@ -966,6 +977,7 @@ public: writer(new StreamWriter(sock, Ownership::Nothing), Ownership::Stream) { writer.SetStreamingMode(true); + reader.SetStreamingMode(true); std::map m = RenderDoc::Inst().GetReplayDrivers(); diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index 35c053182..24ea69383 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -120,7 +120,10 @@ public: bool IsRemoteProxy() { return !m_RemoteServer; } void Shutdown() { delete this; } - void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) {} + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) + { + return ReplayStatus::Succeeded; + } vector GetSupportedWindowSystems() { if(m_Proxy) diff --git a/renderdoc/core/target_control.cpp b/renderdoc/core/target_control.cpp index 25fe81872..3c0006183 100644 --- a/renderdoc/core/target_control.cpp +++ b/renderdoc/core/target_control.cpp @@ -76,6 +76,7 @@ void RenderDoc::TargetControlClientThread(Network::Socket *client) ReadSerialiser reader(new StreamReader(client, Ownership::Nothing), Ownership::Stream); writer.SetStreamingMode(true); + reader.SetStreamingMode(true); std::string api = ""; RDCDriver driver; @@ -404,6 +405,7 @@ public: std::vector payload; writer.SetStreamingMode(true); + reader.SetStreamingMode(true); m_PID = 0; diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index c663d5399..01d78e2a4 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -912,17 +912,17 @@ void WrappedID3D11Device::Serialise_CaptureScope(SerialiserType &ser) } } -void WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return; + return ReplayStatus::FileCorrupted; StreamReader *reader = rdc->ReadSection(sectionIdx); if(reader->IsErrored()) - return; + return ReplayStatus::FileIOFailed; ReadSerialiser ser(reader, Ownership::Stream); @@ -961,10 +961,16 @@ void WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructur chunkIdx++; + if(reader->IsErrored()) + return ReplayStatus::APIDataCorrupted; + ProcessChunk(ser, context); ser.EndChunk(); + if(reader->IsErrored()) + return ReplayStatus::APIDataCorrupted; + uint64_t offsetEnd = reader->GetOffset(); RenderDoc::Inst().SetProgress(FileInitialRead, float(offsetEnd) / float(reader->GetSize())); @@ -1027,6 +1033,8 @@ void WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructur RDCDEBUG("Allocating %llu persistant bytes of memory for the log.", m_FrameRecord.frameInfo.persistentSize); + + return ReplayStatus::Succeeded; } void WrappedID3D11Device::ReplayLog(uint32_t startEventID, uint32_t endEventID, diff --git a/renderdoc/driver/d3d11/d3d11_device.h b/renderdoc/driver/d3d11/d3d11_device.h index 63d20c304..7375b0910 100644 --- a/renderdoc/driver/d3d11/d3d11_device.h +++ b/renderdoc/driver/d3d11/d3d11_device.h @@ -514,7 +514,7 @@ public: m_SectionVersion = sectionVersion; m_State = CaptureState::StructuredExport; } - void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ProcessChunk(ReadSerialiser &ser, D3D11Chunk context); void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType); diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index f0559eaa8..2e1577a10 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -1218,9 +1218,9 @@ void D3D11Replay::SavePipelineState() } } -void D3D11Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +ReplayStatus D3D11Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { - m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers); + return m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers); } void D3D11Replay::ReplayLog(uint32_t endEventID, ReplayLogType replayType) @@ -1896,7 +1896,7 @@ ReplayStatus D3D11_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) if(!D3D11InitParams::IsSupportedVersion(ver)) { RDCERR("Incompatible D3D11 serialise version %llu", ver); - return ReplayStatus::APIUnsupported; + return ReplayStatus::APIIncompatibleVersion; } StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -2051,9 +2051,10 @@ void D3D11_ProcessStructured(RDCFile *rdc, SDFile &output) return; device.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version); - device.ReadLogInitialisation(rdc, true); + ReplayStatus status = device.ReadLogInitialisation(rdc, true); - device.GetStructuredFile().swap(output); + if(status == ReplayStatus::Succeeded) + device.GetStructuredFile().swap(output); } static StructuredProcessRegistration D3D11ProcessRegistration(RDC_D3D11, &D3D11_ProcessStructured); \ No newline at end of file diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index c70fb7684..27da86e48 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -77,7 +77,7 @@ public: void FreeTargetResource(ResourceId id); void FreeCustomShader(ResourceId id); - void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); const SDFile &GetStructuredFile(); diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index d8bfabbcf..d6b1c9402 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -2367,17 +2367,17 @@ void WrappedID3D12Device::AddResourceCurChunk(ResourceId id) AddResourceCurChunk(GetReplay()->GetResourceDesc(id)); } -void WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +ReplayStatus WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return; + return ReplayStatus::FileCorrupted; StreamReader *reader = rdc->ReadSection(sectionIdx); if(reader->IsErrored()) - return; + return ReplayStatus::FileIOFailed; ReadSerialiser ser(reader, Ownership::Stream); @@ -2416,10 +2416,16 @@ void WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructur chunkIdx++; + if(reader->IsErrored()) + return ReplayStatus::APIDataCorrupted; + ProcessChunk(ser, context); ser.EndChunk(); + if(reader->IsErrored()) + return ReplayStatus::APIDataCorrupted; + uint64_t offsetEnd = reader->GetOffset(); RenderDoc::Inst().SetProgress(FileInitialRead, float(offsetEnd) / float(reader->GetSize())); @@ -2498,6 +2504,8 @@ void WrappedID3D12Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructur RDCDEBUG("Allocating %llu persistant bytes of memory for the log.", m_FrameRecord.frameInfo.persistentSize); + + return ReplayStatus::Succeeded; } void WrappedID3D12Device::ReplayLog(uint32_t startEventID, uint32_t endEventID, diff --git a/renderdoc/driver/d3d12/d3d12_device.h b/renderdoc/driver/d3d12/d3d12_device.h index 5b3dbf73c..0e422a328 100644 --- a/renderdoc/driver/d3d12/d3d12_device.h +++ b/renderdoc/driver/d3d12/d3d12_device.h @@ -474,7 +474,7 @@ public: bool Serialise_DynamicDescriptorCopies(SerialiserType &ser, const std::vector &DescriptorCopies); - void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType); void SetStructuredExport(uint64_t sectionVersion) diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 21ae33122..875a97d1f 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -59,9 +59,9 @@ void D3D12Replay::Shutdown() D3D12Replay::PostDeviceShutdownCounters(); } -void D3D12Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +ReplayStatus D3D12Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { - m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers); + return m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers); } APIProperties D3D12Replay::GetAPIProperties() @@ -1752,7 +1752,7 @@ ReplayStatus D3D12_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) if(!D3D12InitParams::IsSupportedVersion(ver)) { RDCERR("Incompatible D3D11 serialise version %llu", ver); - return ReplayStatus::APIUnsupported; + return ReplayStatus::APIIncompatibleVersion; } StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -1816,9 +1816,10 @@ void D3D12_ProcessStructured(RDCFile *rdc, SDFile &output) return; device.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version); - device.ReadLogInitialisation(rdc, true); + ReplayStatus status = device.ReadLogInitialisation(rdc, true); - device.GetStructuredFile().swap(output); + if(status == ReplayStatus::Succeeded) + device.GetStructuredFile().swap(output); } static StructuredProcessRegistration D3D12ProcessRegistration(RDC_D3D12, &D3D12_ProcessStructured); diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index bc7afd342..7336aaa37 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -75,7 +75,7 @@ public: void FreeTargetResource(ResourceId id); void FreeCustomShader(ResourceId id); - void ReadLogInitialisation(RDCFile *rdc, bool readStructuredBuffers); + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool readStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); const SDFile &GetStructuredFile(); diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index a9d856e56..28914a62d 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -3054,17 +3054,17 @@ void WrappedOpenGL::AddResourceInitChunk(GLResource res) } } -void WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +ReplayStatus WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return; + return ReplayStatus::FileCorrupted; StreamReader *reader = rdc->ReadSection(sectionIdx); if(reader->IsErrored()) - return; + return ReplayStatus::FileIOFailed; ReadSerialiser ser(reader, Ownership::Stream); @@ -3103,10 +3103,16 @@ void WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuff chunkIdx++; + if(reader->IsErrored()) + return ReplayStatus::APIDataCorrupted; + ProcessChunk(ser, context); ser.EndChunk(); + if(reader->IsErrored()) + return ReplayStatus::APIDataCorrupted; + uint64_t offsetEnd = reader->GetOffset(); RenderDoc::Inst().SetProgress(FileInitialRead, float(offsetEnd) / float(reader->GetSize())); @@ -3161,6 +3167,8 @@ void WrappedOpenGL::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuff RDCDEBUG("Allocating %llu persistant bytes of memory for the log.", m_FrameRecord.frameInfo.persistentSize); + + return ReplayStatus::Succeeded; } void WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index f9dc91a30..f95a8bfc4 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -559,7 +559,7 @@ public: // replay interface void Initialise(GLInitParams ¶ms, uint64_t sectionVersion); void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType); - void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); GLuint GetFakeBBFBO() { return m_FakeBB_FBO; } GLuint GetFakeVAO() { return m_FakeVAO; } diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index f7f11b7e4..6cc35981a 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -73,10 +73,10 @@ void GLReplay::Shutdown() GLReplay::PostContextShutdownCounters(); } -void GLReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +ReplayStatus GLReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { MakeCurrentReplayContext(&m_ReplayCtx); - m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers); + return m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers); } void GLReplay::ReplayLog(uint32_t endEventID, ReplayLogType replayType) @@ -3341,9 +3341,10 @@ void GL_ProcessStructured(RDCFile *rdc, SDFile &output) return; device.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version); - device.ReadLogInitialisation(rdc, true); + ReplayStatus status = device.ReadLogInitialisation(rdc, true); - device.GetStructuredFile().swap(output); + if(status == ReplayStatus::Succeeded) + device.GetStructuredFile().swap(output); } static StructuredProcessRegistration GLProcessRegistration(RDC_OpenGL, &GL_ProcessStructured); diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index bbf2d1962..95c9c90bc 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -121,7 +121,7 @@ public: const VKPipe::State &GetVulkanPipelineState() { return m_VKState; } void FreeTargetResource(ResourceId id); - void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); const SDFile &GetStructuredFile(); diff --git a/renderdoc/driver/gl/gl_replay_egl.cpp b/renderdoc/driver/gl/gl_replay_egl.cpp index e9b948163..7b2567f4d 100644 --- a/renderdoc/driver/gl/gl_replay_egl.cpp +++ b/renderdoc/driver/gl/gl_replay_egl.cpp @@ -73,7 +73,7 @@ ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) if(!GLInitParams::IsSupportedVersion(ver)) { RDCERR("Incompatible D3D11 serialise version %llu", ver); - return ReplayStatus::APIUnsupported; + return ReplayStatus::APIIncompatibleVersion; } StreamReader *reader = rdc->ReadSection(sectionIdx); diff --git a/renderdoc/driver/gl/gl_replay_linux.cpp b/renderdoc/driver/gl/gl_replay_linux.cpp index f534bc07a..3eaa54741 100644 --- a/renderdoc/driver/gl/gl_replay_linux.cpp +++ b/renderdoc/driver/gl/gl_replay_linux.cpp @@ -108,7 +108,7 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) if(!GLInitParams::IsSupportedVersion(ver)) { RDCERR("Incompatible D3D11 serialise version %llu", ver); - return ReplayStatus::APIUnsupported; + return ReplayStatus::APIIncompatibleVersion; } StreamReader *reader = rdc->ReadSection(sectionIdx); diff --git a/renderdoc/driver/gl/gl_replay_win32.cpp b/renderdoc/driver/gl/gl_replay_win32.cpp index fab7ca6bd..a67a31644 100644 --- a/renderdoc/driver/gl/gl_replay_win32.cpp +++ b/renderdoc/driver/gl/gl_replay_win32.cpp @@ -70,7 +70,7 @@ ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) if(!GLInitParams::IsSupportedVersion(ver)) { RDCERR("Incompatible D3D11 serialise version %llu", ver); - return ReplayStatus::APIUnsupported; + return ReplayStatus::APIIncompatibleVersion; } StreamReader *reader = rdc->ReadSection(sectionIdx); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 378a1830c..cf33c747e 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -1420,17 +1420,17 @@ void WrappedVulkan::AddResourceCurChunk(ResourceId id) AddResourceCurChunk(GetReplay()->GetResourceDesc(id)); } -void WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +ReplayStatus WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) - return; + return ReplayStatus::FileCorrupted; StreamReader *reader = rdc->ReadSection(sectionIdx); if(reader->IsErrored()) - return; + return ReplayStatus::FileIOFailed; ReadSerialiser ser(reader, Ownership::Stream); @@ -1469,10 +1469,16 @@ void WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuff chunkIdx++; + if(reader->IsErrored()) + return ReplayStatus::APIDataCorrupted; + ProcessChunk(ser, context); ser.EndChunk(); + if(reader->IsErrored()) + return ReplayStatus::APIDataCorrupted; + uint64_t offsetEnd = reader->GetOffset(); RenderDoc::Inst().SetProgress(FileInitialRead, float(offsetEnd) / float(reader->GetSize())); @@ -1533,6 +1539,8 @@ void WrappedVulkan::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuff RDCASSERT(m_Device != VK_NULL_HANDLE && m_Queue != VK_NULL_HANDLE && m_InternalCmds.cmdpool != VK_NULL_HANDLE); } + + return ReplayStatus::Succeeded; } void WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t startEventID, diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index a473a84a3..8bd2bc845 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -763,7 +763,7 @@ public: } void Shutdown(); void ReplayLog(uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType); - void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); SDFile &GetStructuredFile() { return *m_StructuredFile; } FrameRecord &GetFrameRecord() { return m_FrameRecord; } diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index b8e42ec02..49f898d8f 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -659,9 +659,9 @@ APIProperties VulkanReplay::GetAPIProperties() return ret; } -void VulkanReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) +ReplayStatus VulkanReplay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { - m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers); + return m_pDriver->ReadLogInitialisation(rdc, storeStructuredBuffers); } void VulkanReplay::ReplayLog(uint32_t endEventID, ReplayLogType replayType) @@ -5360,7 +5360,7 @@ ReplayStatus Vulkan_CreateReplayDevice(RDCFile *rdc, IReplayDriver **driver) if(!VkInitParams::IsSupportedVersion(ver)) { RDCERR("Incompatible Vulkan serialise version %llu", ver); - return ReplayStatus::APIUnsupported; + return ReplayStatus::APIIncompatibleVersion; } StreamReader *reader = rdc->ReadSection(sectionIdx); @@ -5428,9 +5428,10 @@ void Vulkan_ProcessStructured(RDCFile *rdc, SDFile &output) return; vulkan.SetStructuredExport(rdc->GetSectionProperties(sectionIdx).version); - vulkan.ReadLogInitialisation(rdc, true); + ReplayStatus status = vulkan.ReadLogInitialisation(rdc, true); - vulkan.GetStructuredFile().swap(output); + if(status == ReplayStatus::Succeeded) + vulkan.GetStructuredFile().swap(output); } static StructuredProcessRegistration VulkanProcessRegistration(RDC_Vulkan, &Vulkan_ProcessStructured); \ No newline at end of file diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index e40ca341a..1e694015d 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -163,7 +163,7 @@ public: const VKPipe::State &GetVulkanPipelineState() { return m_VulkanPipelineState; } void FreeTargetResource(ResourceId id); - void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); + ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); void ReplayLog(uint32_t endEventID, ReplayLogType replayType); const SDFile &GetStructuredFile(); diff --git a/renderdoc/replay/capture_file.cpp b/renderdoc/replay/capture_file.cpp index 52392dbdb..76860ac28 100644 --- a/renderdoc/replay/capture_file.cpp +++ b/renderdoc/replay/capture_file.cpp @@ -113,7 +113,7 @@ public: ReplayStatus OpenFile(const char *filename, const char *filetype); ReplayStatus OpenBuffer(const bytebuf &buffer, const char *filetype); bool CopyFileTo(const char *filename); - + rdcstr ErrorString() { return m_ErrorString; } void Shutdown() { delete this; } ReplaySupport LocalReplaySupport() { return m_Support; } const char *DriverName() { return m_DriverName.c_str(); } @@ -183,7 +183,7 @@ private: SDFile m_StructuredData; - std::string m_DriverName, m_Ident; + std::string m_DriverName, m_Ident, m_ErrorString; ReplaySupport m_Support = ReplaySupport::Unsupported; }; @@ -214,6 +214,7 @@ ReplayStatus CaptureFile::OpenFile(const char *filename, const char *filetype) if(ret != ReplayStatus::Succeeded) { + m_ErrorString = StringFormat::Fmt("Importer '%s' failed to import file.", filetype); delete m_RDC; return ret; } @@ -249,6 +250,7 @@ ReplayStatus CaptureFile::OpenBuffer(const bytebuf &buffer, const char *filetype if(ret != ReplayStatus::Succeeded) { + m_ErrorString = StringFormat::Fmt("Importer '%s' failed to import file.", filetype); delete m_RDC; return ret; } @@ -278,6 +280,8 @@ ReplayStatus CaptureFile::Init() if(!m_RDC) return ReplayStatus::InternalError; + m_ErrorString = m_RDC->ErrorString(); + switch(m_RDC->ErrorCode()) { case ContainerError::FileNotFound: return ReplayStatus::FileNotFound; break; diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 6c94dfeee..5f25af31e 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -1586,7 +1586,10 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd { m_pDevice = device; - m_pDevice->ReadLogInitialisation(rdc, false); + ReplayStatus status = m_pDevice->ReadLogInitialisation(rdc, false); + + if(status != ReplayStatus::Succeeded) + return status; FetchPipelineState(); @@ -1612,6 +1615,9 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd m_FrameRecord = m_pDevice->GetFrameRecord(); + if(m_FrameRecord.drawcallList.empty()) + return ReplayStatus::APIReplayFailed; + DrawcallDescription *previous = NULL; SetupDrawcallPointers(&m_Drawcalls, m_FrameRecord.drawcallList, NULL, previous); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 8300a6b73..81fc7ec91 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -117,7 +117,7 @@ public: virtual FrameRecord GetFrameRecord() = 0; - virtual void ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) = 0; + virtual ReplayStatus ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) = 0; virtual void ReplayLog(uint32_t endEventID, ReplayLogType replayType) = 0; virtual const SDFile &GetStructuredFile() = 0; diff --git a/renderdoc/serialise/rdcfile.cpp b/renderdoc/serialise/rdcfile.cpp index e2e89c20c..c663ea583 100644 --- a/renderdoc/serialise/rdcfile.cpp +++ b/renderdoc/serialise/rdcfile.cpp @@ -223,11 +223,17 @@ struct BinarySectionHeader }; }; -#define RETURNCORRUPT(...) \ - { \ - RDCERR(__VA_ARGS__); \ - m_Error = ContainerError::Corrupt; \ - return; \ +#define SETERROR(error, ...) \ + { \ + m_ErrorString = StringFormat::Fmt(__VA_ARGS__); \ + RDCERR("%s", m_ErrorString.c_str()); \ + m_Error = error; \ + } + +#define RETURNERROR(error, ...) \ + { \ + SETERROR(error, __VA_ARGS__); \ + return; \ } RDCFile::~RDCFile() @@ -245,8 +251,7 @@ void RDCFile::Open(const char *path) // empty path. if(path == NULL || path[0] == 0) { - m_Error = ContainerError::FileNotFound; - return; + RETURNERROR(ContainerError::FileNotFound, "Invalid file path specified"); } RDCLOG("Opening RDCFile %s", path); @@ -260,9 +265,8 @@ void RDCFile::Open(const char *path) if(!m_File) { - RDCERR("Can't open capture file '%s' for read - errno %d", path, errno); - m_Error = ContainerError::FileNotFound; - return; + RETURNERROR(ContainerError::FileNotFound, "Can't open capture file '%s' for read - errno %d", + path, errno); } // try to identify if this is an image @@ -318,31 +322,30 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RDCERR("I/O error reading magic number"); - m_Error = ContainerError::FileIO; - return; + RETURNERROR(ContainerError::FileIO, "I/O error reading magic number"); } if(header.magic != MAGIC_HEADER) { - RDCWARN("Invalid capture file. Expected magic %08x, got %08x.", MAGIC_HEADER, - (uint32_t)header.magic); - - m_Error = ContainerError::Corrupt; - return; + RETURNERROR(ContainerError::Corrupt, "Invalid capture file. Expected magic %08x, got %08x.", + MAGIC_HEADER, (uint32_t)header.magic); } m_SerVer = header.version; if(m_SerVer != SERIALISE_VERSION) { - RDCERR( - "Capture file from wrong version. This program (v%s) is logfile version %llu, file is " - "logfile version %llu capture on %s.", - MAJOR_MINOR_VERSION_STRING, SERIALISE_VERSION, header.version, header.progVersion); + if(header.version < V1_0_VERSION) + { + RDCEraseEl(header.progVersion); + memcpy(header.progVersion, "v0.x", sizeof("v0.x")); + } - m_Error = ContainerError::UnsupportedVersion; - return; + RETURNERROR( + ContainerError::UnsupportedVersion, + "Capture file from wrong version. This program (v%s) uses logfile version %u, this file is " + "logfile version %u captured on %s.", + MAJOR_MINOR_VERSION_STRING, SERIALISE_VERSION, header.version, header.progVersion); } BinaryThumbnail thumb; @@ -350,15 +353,13 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RDCERR("I/O error reading thumbnail header"); - m_Error = ContainerError::FileIO; - return; + RETURNERROR(ContainerError::FileIO, "I/O error reading thumbnail header"); } // check the thumbnail size is sensible if(thumb.length > 10 * 1024 * 1024) { - RETURNCORRUPT("Thumbnail byte length invalid: %u", thumb.length); + RETURNERROR(ContainerError::Corrupt, "Thumbnail byte length invalid: %u", thumb.length); } byte *thumbData = new byte[thumb.length]; @@ -366,10 +367,8 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RDCERR("I/O error reading thumbnail data"); delete[] thumbData; - m_Error = ContainerError::FileIO; - return; + RETURNERROR(ContainerError::FileIO, "I/O error reading thumbnail data"); } CaptureMetaData meta; @@ -377,16 +376,15 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RDCERR("I/O error reading capture metadata"); delete[] thumbData; - m_Error = ContainerError::FileIO; - return; + RETURNERROR(ContainerError::FileIO, "I/O error reading capture metadata"); } if(meta.driverNameLength == 0) { delete[] thumbData; - RETURNCORRUPT("Driver name length is invalid, must be at least 1 to contain NULL terminator"); + RETURNERROR(ContainerError::Corrupt, + "Driver name length is invalid, must be at least 1 to contain NULL terminator"); } char *driverName = new char[meta.driverNameLength]; @@ -394,11 +392,9 @@ void RDCFile::Init(StreamReader &reader) if(reader.IsErrored()) { - RDCERR("I/O error reading driver name"); delete[] thumbData; delete[] driverName; - m_Error = ContainerError::FileIO; - return; + RETURNERROR(ContainerError::FileIO, "I/O error reading driver name"); } driverName[meta.driverNameLength - 1] = '\0'; @@ -421,9 +417,7 @@ void RDCFile::Init(StreamReader &reader) if(reader.GetOffset() > header.headerLength) { - RDCERR("I/O error seeking to end of header"); - m_Error = ContainerError::FileIO; - return; + RETURNERROR(ContainerError::FileIO, "I/O error seeking to end of header"); } reader.SkipBytes(header.headerLength - (uint32_t)reader.GetOffset()); @@ -447,10 +441,10 @@ void RDCFile::Init(StreamReader &reader) char c = 0; reader.Read(c); if(reader.IsErrored()) - RETURNCORRUPT("Invalid ASCII data section '%hhx'", c); + RETURNERROR(ContainerError::Corrupt, "Invalid ASCII data section '%hhx'", c); if(reader.AtEnd()) - RETURNCORRUPT("Invalid truncated ASCII data section"); + RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); uint64_t length = 0; @@ -468,7 +462,7 @@ void RDCFile::Init(StreamReader &reader) } if(reader.IsErrored() || reader.AtEnd()) - RETURNCORRUPT("Invalid truncated ASCII data section"); + RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); uint32_t type = 0; @@ -486,7 +480,7 @@ void RDCFile::Init(StreamReader &reader) } if(reader.IsErrored() || reader.AtEnd()) - RETURNCORRUPT("Invalid truncated ASCII data section"); + RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); uint64_t version = 0; @@ -504,7 +498,7 @@ void RDCFile::Init(StreamReader &reader) } if(reader.IsErrored() || reader.AtEnd()) - RETURNCORRUPT("Invalid truncated ASCII data section"); + RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); std::string name; @@ -521,7 +515,7 @@ void RDCFile::Init(StreamReader &reader) } if(reader.IsErrored() || reader.AtEnd()) - RETURNCORRUPT("Invalid truncated ASCII data section"); + RETURNERROR(ContainerError::Corrupt, "Invalid truncated ASCII data section"); SectionProperties props; props.flags = SectionFlags::ASCIIStored; @@ -539,7 +533,8 @@ void RDCFile::Init(StreamReader &reader) reader.SkipBytes(loc.diskLength); if(reader.IsErrored()) - RETURNCORRUPT("Error seeking past ASCII section '%s' data", name.c_str()); + RETURNERROR(ContainerError::Corrupt, "Error seeking past ASCII section '%s' data", + name.c_str()); m_Sections.push_back(props); m_SectionLocations.push_back(loc); @@ -550,7 +545,7 @@ void RDCFile::Init(StreamReader &reader) reader.Read(reading, offsetof(BinarySectionHeader, name) - 1); if(reader.IsErrored()) - RETURNCORRUPT("Error reading binary section header"); + RETURNERROR(ContainerError::Corrupt, "Error reading binary section header"); SectionProperties props; props.flags = sectionHeader.sectionFlags; @@ -561,7 +556,8 @@ void RDCFile::Init(StreamReader &reader) if(sectionHeader.sectionNameLength == 0 || sectionHeader.sectionNameLength > 2 * 1024) { - RETURNCORRUPT("Invalid section name length %u", sectionHeader.sectionNameLength); + RETURNERROR(ContainerError::Corrupt, "Invalid section name length %u", + sectionHeader.sectionNameLength); } props.name.resize(sectionHeader.sectionNameLength - 1); @@ -569,12 +565,12 @@ void RDCFile::Init(StreamReader &reader) reader.Read(&props.name[0], sectionHeader.sectionNameLength - 1); if(reader.IsErrored()) - RETURNCORRUPT("Error reading binary section header"); + RETURNERROR(ContainerError::Corrupt, "Error reading binary section header"); reader.SkipBytes(1); if(reader.IsErrored()) - RETURNCORRUPT("Error reading binary section header"); + RETURNERROR(ContainerError::Corrupt, "Error reading binary section header"); SectionLocation loc; loc.headerOffset = headerOffset; @@ -587,17 +583,18 @@ void RDCFile::Init(StreamReader &reader) reader.SkipBytes(loc.diskLength); if(reader.IsErrored()) - RETURNCORRUPT("Error seeking past binary section '%s' data", props.name.c_str()); + RETURNERROR(ContainerError::Corrupt, "Error seeking past binary section '%s' data", + props.name.c_str()); } else { - RETURNCORRUPT("Unrecognised section type '%hhx'", sectionHeader.isASCII); + RETURNERROR(ContainerError::Corrupt, "Unrecognised section type '%hhx'", sectionHeader.isASCII); } } if(SectionIndex(SectionType::FrameCapture) == -1) { - RETURNCORRUPT("Capture file doesn't have a frame capture"); + RETURNERROR(ContainerError::Corrupt, "Capture file doesn't have a frame capture"); } } @@ -649,9 +646,8 @@ void RDCFile::Create(const char *filename) if(!m_File) { - RDCERR("Can't open capture file '%s' for write, errno %d", filename, errno); - m_Error = ContainerError::FileIO; - return; + RETURNERROR(ContainerError::FileIO, "Can't open capture file '%s' for write, errno %d", + filename, errno); } RDCDEBUG("Opened capture file for write"); @@ -686,9 +682,7 @@ void RDCFile::Create(const char *filename) if(writer.IsErrored()) { - RDCERR("Error writing file header"); - m_Error = ContainerError::FileIO; - return; + RETURNERROR(ContainerError::FileIO, "Error writing file header"); } } @@ -1033,8 +1027,7 @@ StreamWriter *RDCFile::WriteSection(const SectionProperties &props) if(numWritten != offsetof(BinarySectionHeader, name) + name.size() + 1) { - RDCERR("Error seeking to end of file, errno %d", errno); - m_Error = ContainerError::FileIO; + SETERROR(ContainerError::FileIO, "Error seeking to end of file, errno %d", errno); return new StreamWriter(StreamWriter::InvalidStream); } @@ -1097,9 +1090,7 @@ StreamWriter *RDCFile::WriteSection(const SectionProperties &props) if(bytesWritten != 2 * sizeof(uint64_t)) { - RDCERR("Error applying fixup to section header, errno %d", errno); - m_Error = ContainerError::FileIO; - return; + RETURNERROR(ContainerError::FileIO, "Error applying fixup to section header, errno %d", errno); } }); diff --git a/renderdoc/serialise/rdcfile.h b/renderdoc/serialise/rdcfile.h index c66bb927d..b74f2b0e5 100644 --- a/renderdoc/serialise/rdcfile.h +++ b/renderdoc/serialise/rdcfile.h @@ -54,6 +54,10 @@ public: // API that interprets the stream that can be bumped. static const uint32_t SERIALISE_VERSION = 0x00000100; + // this must never be changed - files before this were in the v0.x series and didn't have embedded + // version numbers + static const uint32_t V1_0_VERSION = 0x00000100; + ~RDCFile(); // opens an existing file for read and/or modification. Error if file doesn't exist @@ -70,6 +74,7 @@ public: void Create(const char *filename); ContainerError ErrorCode() const { return m_Error; } + std::string ErrorString() const { return m_ErrorString; } RDCDriver GetDriver() const { return m_Driver; } const std::string &GetDriverName() const { return m_DriverName; } uint64_t GetMachineIdent() const { return m_MachineIdent; } @@ -102,6 +107,7 @@ private: RDCThumb m_Thumb; ContainerError m_Error = ContainerError::NoError; + std::string m_ErrorString; struct SectionLocation { diff --git a/renderdoc/serialise/serialiser.cpp b/renderdoc/serialise/serialiser.cpp index 59063c7a1..947aacad0 100644 --- a/renderdoc/serialise/serialiser.cpp +++ b/renderdoc/serialise/serialiser.cpp @@ -198,7 +198,7 @@ void Serialiser::EndChunk() // only skip remaining bytes if we have a valid length - if we have a length of 0 we wrote this // chunk in 'streaming mode' (see SetStreamingMode and the Writing EndChunk() impl) so there's // nothing to skip. - if(m_ChunkMetadata.length > 0) + if(m_ChunkMetadata.length > 0 && !m_Read->IsErrored()) { // this will be a no-op if the last chunk length was accurate. If it was a // conservative estimate of the length then we'll skip some padding bytes diff --git a/renderdoc/serialise/serialiser.h b/renderdoc/serialise/serialiser.h index f8c3fcd2d..123f3df60 100644 --- a/renderdoc/serialise/serialiser.h +++ b/renderdoc/serialise/serialiser.h @@ -146,6 +146,14 @@ public: uint32_t BeginChunk(uint32_t chunkID, uint32_t byteLength); void EndChunk(); + std::string GetCurChunkName() + { + if(m_ChunkLookup) + return m_ChunkLookup(m_ChunkMetadata.chunkID); + + return StringFormat::Fmt("", m_ChunkMetadata.chunkID); + } + ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -213,7 +221,10 @@ public: } if(IsReading()) + { + VerifyArraySize(count); byteSize = count; + } if(ExportStructure()) { @@ -343,6 +354,11 @@ public: m_InternalElement = false; } + if(IsReading()) + { + VerifyArraySize(count); + } + if(ExportStructure()) { if(m_StructureStack.empty()) @@ -411,6 +427,11 @@ public: m_InternalElement = false; } + if(IsReading()) + { + VerifyArraySize(count); + } + if(ExportStructure()) { if(m_StructureStack.empty()) @@ -651,7 +672,10 @@ public: } if(IsReading()) + { + VerifyArraySize(count); arrayCount = count; + } if(ExportStructure()) { @@ -750,6 +774,11 @@ public: m_InternalElement = false; } + if(IsReading()) + { + VerifyArraySize(size); + } + if(ExportStructure()) { if(m_StructureStack.empty()) @@ -815,6 +844,11 @@ public: m_InternalElement = false; } + if(IsReading()) + { + VerifyArraySize(size); + } + if(ExportStructure()) { if(m_StructureStack.empty()) @@ -948,6 +982,11 @@ public: m_InternalElement = false; } + if(IsReading()) + { + VerifyArraySize(size); + } + if(ExportStructure()) { if(m_StructureStack.empty()) @@ -1516,6 +1555,32 @@ private: } }; + void VerifyArraySize(uint64_t &count) + { + uint64_t size = m_Read->GetSize(); + + // for streaming, just take 4GB as a 'semi reasonable' upper limit for array sizes + if(m_DataStreaming) + size = 0xFFFFFFFFU; + + if(count > size) + { + RDCERR("Reading invalid array or byte buffer - %llu larger than total stream size %llu.", + count, size); + + // if we owned the previous stream, delete it + if(m_Ownership == Ownership::Stream) + delete m_Read; + + // replace our stream with an invalid one so all subsequent reads fail + m_Read = new StreamReader(StreamReader::InvalidStream); + m_Ownership = Ownership::Stream; + + // set the count to 0 + count = 0; + } + } + void *m_pUserData = NULL; StreamWriter *m_Write = NULL; @@ -1907,3 +1972,12 @@ struct ScopedDeserialiseArray // a member that is a pointer and could be NULL, so needs a hidden 'present' // flag serialised out #define SERIALISE_MEMBER_OPT(obj) ser.SerialiseNullable(#obj, el.obj) + +// simple utility function for inside serialise functions, to check if the serialiser has hit an +// error and then bail, without trying to replay anything. +#define SERIALISE_CHECK_READ_ERRORS() \ + if(ser.IsReading() && ser.IsErrored()) \ + { \ + RDCERR("Serialisation failed in '%s'.", ser.GetCurChunkName().c_str()); \ + return false; \ + } diff --git a/renderdoc/serialise/streamio.cpp b/renderdoc/serialise/streamio.cpp index 7d4db6e1b..b95132647 100644 --- a/renderdoc/serialise/streamio.cpp +++ b/renderdoc/serialise/streamio.cpp @@ -68,6 +68,8 @@ StreamReader::StreamReader(StreamInvalidType) m_BufferHead = m_BufferBase = NULL; m_Ownership = Ownership::Nothing; + + m_HasError = true; } StreamReader::StreamReader(Network::Socket *sock, Ownership own) @@ -336,6 +338,8 @@ StreamWriter::StreamWriter(StreamInvalidType) m_Ownership = Ownership::Nothing; m_InMemory = false; + + m_HasError = true; } StreamWriter::StreamWriter(Network::Socket *sock, Ownership own)