diff --git a/renderdoc/api/app/renderdoc_app.h b/renderdoc/api/app/renderdoc_app.h index c1cc770cb..7f012d66a 100644 --- a/renderdoc/api/app/renderdoc_app.h +++ b/renderdoc/api/app/renderdoc_app.h @@ -67,7 +67,7 @@ struct CaptureOptions CaptureCallstacks(false), CaptureCallstacksOnlyDraws(false), DelayForDebugger(0), - CacheStateObjects(false), + VerifyMapWrites(true), HookIntoChildren(false), RefAllResources(false), SaveAllInitials(false), @@ -101,8 +101,9 @@ struct CaptureOptions // creating or injecting into a process, before continuing to allow it to run. uint32_t DelayForDebugger; - // Deprecated, ignored. - bool32 CacheStateObjects; + // Verify any writes to mapped buffers, to check that they don't overwrite the + // bounds of the pointer returned. + bool32 VerifyMapWrites; // Hooks any system API events that create child processes, and injects // renderdoc into them recursively with the same options. @@ -141,6 +142,7 @@ struct CaptureOptions >> CaptureCallstacks >> CaptureCallstacksOnlyDraws >> DelayForDebugger + >> VerifyMapWrites >> HookIntoChildren >> RefAllResources >> SaveAllInitials @@ -157,6 +159,7 @@ struct CaptureOptions << CaptureCallstacks << " " << CaptureCallstacksOnlyDraws << " " << DelayForDebugger << " " + << VerifyMapWrites << " " << HookIntoChildren << " " << RefAllResources << " " << SaveAllInitials << " " diff --git a/renderdoc/driver/d3d11/d3d11_context.cpp b/renderdoc/driver/d3d11/d3d11_context.cpp index 138309ea7..0c9285497 100644 --- a/renderdoc/driver/d3d11/d3d11_context.cpp +++ b/renderdoc/driver/d3d11/d3d11_context.cpp @@ -913,8 +913,11 @@ void WrappedID3D11DeviceContext::RefreshDrawcallIDs(DrawcallTreeNode &node) // assign new drawcall IDs for(size_t i=0; i < node.children.size(); i++) { + m_CurEventID++; + + node.children[i].draw.eventID = m_CurEventID; node.children[i].draw.drawcallID = m_CurDrawcallID; - + // markers don't increment drawcall ID if((node.children[i].draw.flags & (eDraw_SetMarker|eDraw_PushMarker)) == 0) m_CurDrawcallID++; diff --git a/renderdoc/driver/d3d11/d3d11_context.h b/renderdoc/driver/d3d11/d3d11_context.h index 27bceb904..e0d85b5d6 100644 --- a/renderdoc/driver/d3d11/d3d11_context.h +++ b/renderdoc/driver/d3d11/d3d11_context.h @@ -64,6 +64,8 @@ struct MapIntercept D3D11_MAP MapType; UINT MapFlags; + bool verifyWrite; + void CopyFromD3D(); void CopyToD3D(size_t RangeStart = 0, size_t RangeEnd = 0); }; diff --git a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp index c67baadbb..1bb235dad 100644 --- a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp @@ -6188,6 +6188,7 @@ bool WrappedID3D11DeviceContext::Serialise_Map(ID3D11Resource *pResource, UINT S } intercept = MapIntercept(); + intercept.verifyWrite = (RenderDoc::Inst().GetCaptureOptions().VerifyMapWrites != 0); intercept.SetD3D(mappedResource); intercept.InitWrappedResource(resMap, Subresource, appMem); intercept.MapType = MapType; @@ -6205,11 +6206,44 @@ bool WrappedID3D11DeviceContext::Serialise_Map(ID3D11Resource *pResource, UINT S mapLength = record->Length; intercept = MapIntercept(); + intercept.verifyWrite = (RenderDoc::Inst().GetCaptureOptions().VerifyMapWrites != 0); intercept.SetD3D(mappedResource); - intercept.InitWrappedResource(pResource, Subresource, record->GetDataPtr()); intercept.MapType = MapType; intercept.MapFlags = MapFlags; + if(intercept.verifyWrite) + { + int ctxMapID = 0; + + ResourceId Resource = GetIDForResource(pResource); + + if(GetType() == D3D11_DEVICE_CONTEXT_DEFERRED) + { + if(m_MapResourceRecordAllocs[Resource] == 0) + m_MapResourceRecordAllocs[Resource] = record->GetContextID(); + + ctxMapID = m_MapResourceRecordAllocs[Resource]; + + RDCASSERT(ctxMapID != 0); + } + + byte *appMem = record->GetShadowPtr(ctxMapID, 0); + + if(appMem == NULL) + { + record->AllocShadowStorage(ctxMapID, mapLength); + appMem = record->GetShadowPtr(ctxMapID, 0); + } + + memcpy(appMem, record->GetDataPtr(), mapLength); + + intercept.InitWrappedResource(pResource, Subresource, appMem); + } + else + { + intercept.InitWrappedResource(pResource, Subresource, record->GetDataPtr()); + } + *pMappedResource = intercept.app; m_OpenMaps[MappedResource(GetIDForResource(pResource), Subresource)] = intercept; @@ -6310,9 +6344,10 @@ HRESULT WrappedID3D11DeviceContext::Map(ID3D11Resource *pResource, UINT Subresou if(record->NumSubResources > (int)Subresource) record = (D3D11ResourceRecord *)record->SubResources[Subresource]; - record->UpdateCount++; + if(RenderDoc::Inst().GetCaptureOptions().VerifyMapWrites == 0) + record->UpdateCount++; - if(record->UpdateCount > 60) + if(record->UpdateCount > 60 && RenderDoc::Inst().GetCaptureOptions().VerifyMapWrites == 0) { m_HighTrafficResources.insert(pResource); m_pDevice->GetResourceManager()->MarkDirtyResource(Id); @@ -6356,6 +6391,8 @@ bool WrappedID3D11DeviceContext::Serialise_Unmap(ID3D11Resource *pResource, UINT MapIntercept intercept; + int ctxMapID = 0; + if(m_State >= WRITING) { auto it = m_OpenMaps.find(mapIdx); @@ -6365,6 +6402,27 @@ bool WrappedID3D11DeviceContext::Serialise_Unmap(ID3D11Resource *pResource, UINT intercept = it->second; m_OpenMaps.erase(it); + + if(GetType() == D3D11_DEVICE_CONTEXT_DEFERRED && (m_State == WRITING_CAPFRAME || intercept.verifyWrite)) + { + ctxMapID = m_MapResourceRecordAllocs[mapIdx.resource]; + + RDCASSERT(ctxMapID != 0); + } + + if(intercept.verifyWrite && record) + { + if(!record->VerifyShadowStorage(ctxMapID)) + { + int res = MessageBoxA(NULL, + "Breakpoint now to see callstack,\nor click 'Yes' to debugbreak.", + "Map() overwrite detected!", MB_YESNO|MB_ICONERROR); + if(res == IDYES) + { + OS_DEBUG_BREAK(); + } + } + } } if(m_State < WRITING || m_State == WRITING_CAPFRAME) @@ -6378,15 +6436,6 @@ bool WrappedID3D11DeviceContext::Serialise_Unmap(ID3D11Resource *pResource, UINT size_t diffStart = 0; size_t diffEnd = len; - int ctxMapID = 0; - - if(GetType() == D3D11_DEVICE_CONTEXT_DEFERRED && m_State == WRITING_CAPFRAME) - { - ctxMapID = m_MapResourceRecordAllocs[mapIdx.resource]; - - RDCASSERT(ctxMapID != 0); - } - if(m_State == WRITING_CAPFRAME && len > 512 && intercept.MapType != D3D11_MAP_WRITE_DISCARD) { bool found = FindDiffRange(appWritePtr, record->GetShadowPtr(ctxMapID, 1), len, diffStart, diffEnd); @@ -6526,6 +6575,10 @@ bool WrappedID3D11DeviceContext::Serialise_Unmap(ID3D11Resource *pResource, UINT if(m_State < WRITING) SAFE_DELETE_ARRAY(buf); } + else if(intercept.verifyWrite) + { + memcpy(record->GetDataPtr(), intercept.app.pData, len); + } } return true; diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index d7c7e4ffe..c653d4d58 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -2379,6 +2379,8 @@ void WrappedID3D11Device::StartFrameCapture(void *wnd) GetResourceManager()->MarkResourceFrameReferenced(m_ResourceID, eFrameRef_Write); + m_pImmediateContext->FreeCaptureData(); + m_pImmediateContext->AttemptCapture(); m_pImmediateContext->BeginCaptureFrame(); diff --git a/renderdoc/driver/d3d11/d3d11_manager.cpp b/renderdoc/driver/d3d11/d3d11_manager.cpp index 6ca57573d..abe0d8b9b 100644 --- a/renderdoc/driver/d3d11/d3d11_manager.cpp +++ b/renderdoc/driver/d3d11/d3d11_manager.cpp @@ -27,6 +27,13 @@ #include "driver/d3d11/d3d11_context.h" #include "driver/d3d11/d3d11_resources.h" +byte D3D11ResourceRecord::markerValue[32] = { + 0xaa, 0xbb, 0xcc, 0xdd, + 0x88, 0x77, 0x66, 0x55, + 0x01, 0x23, 0x45, 0x67, + 0x98, 0x76, 0x54, 0x32, +}; + ID3D11DeviceChild *D3D11ResourceManager::UnwrapResource(ID3D11DeviceChild *res) { if(res == NULL) return res; diff --git a/renderdoc/driver/d3d11/d3d11_manager.h b/renderdoc/driver/d3d11/d3d11_manager.h index 1b232d5cd..d3946e7b2 100644 --- a/renderdoc/driver/d3d11/d3d11_manager.h +++ b/renderdoc/driver/d3d11/d3d11_manager.h @@ -43,6 +43,8 @@ struct D3D11ResourceRecord : public ResourceRecord { enum { NullResource = NULL }; + static byte markerValue[32]; + D3D11ResourceRecord(ResourceId id) : ResourceRecord(id, true) { @@ -60,11 +62,27 @@ struct D3D11ResourceRecord : public ResourceRecord { if(ShadowPtr[ctx][0] == NULL) { - ShadowPtr[ctx][0] = Serialiser::AllocAlignedBuffer(size); - ShadowPtr[ctx][1] = Serialiser::AllocAlignedBuffer(size); + ShadowPtr[ctx][0] = Serialiser::AllocAlignedBuffer(size + sizeof(markerValue)); + ShadowPtr[ctx][1] = Serialiser::AllocAlignedBuffer(size + sizeof(markerValue)); + + memcpy(ShadowPtr[ctx][0] + size, markerValue, sizeof(markerValue)); + memcpy(ShadowPtr[ctx][1] + size, markerValue, sizeof(markerValue)); + + ShadowSize[ctx] = size; } } + bool VerifyShadowStorage(int ctx) + { + if(ShadowPtr[ctx][0] && memcmp(ShadowPtr[ctx][0] + ShadowSize[ctx], markerValue, sizeof(markerValue))) + return false; + + if(ShadowPtr[ctx][1] && memcmp(ShadowPtr[ctx][1] + ShadowSize[ctx], markerValue, sizeof(markerValue))) + return false; + + return true; + } + void FreeShadowStorage() { for(int i=0; i < 32; i++) @@ -109,6 +127,7 @@ struct D3D11ResourceRecord : public ResourceRecord private: byte *ShadowPtr[32][2]; + size_t ShadowSize[32]; bool contexts[32]; }; diff --git a/renderdocui/Interop/CaptureOptions.cs b/renderdocui/Interop/CaptureOptions.cs index 5efc886cb..605e7516a 100644 --- a/renderdocui/Interop/CaptureOptions.cs +++ b/renderdocui/Interop/CaptureOptions.cs @@ -73,7 +73,7 @@ namespace renderdoc public bool CaptureCallstacks; public bool CaptureCallstacksOnlyDraws; public UInt32 DelayForDebugger; - public bool CacheStateObjects; + public bool VerifyMapWrites; public bool HookIntoChildren; public bool RefAllResources; public bool SaveAllInitials; @@ -90,7 +90,7 @@ namespace renderdoc defs.CaptureCallstacks = false; defs.CaptureCallstacksOnlyDraws = false; defs.DelayForDebugger = 0; - defs.CacheStateObjects = false; + defs.VerifyMapWrites = true; defs.HookIntoChildren = false; defs.RefAllResources = false; defs.SaveAllInitials = false; diff --git a/renderdocui/Windows/Dialogs/CaptureDialog.Designer.cs b/renderdocui/Windows/Dialogs/CaptureDialog.Designer.cs index 5fab8a5bf..19face360 100644 --- a/renderdocui/Windows/Dialogs/CaptureDialog.Designer.cs +++ b/renderdocui/Windows/Dialogs/CaptureDialog.Designer.cs @@ -56,6 +56,7 @@ this.SaveAllInitials = new System.Windows.Forms.CheckBox(); this.RefAllResources = new System.Windows.Forms.CheckBox(); this.CaptureAllCmdLists = new System.Windows.Forms.CheckBox(); + this.VerifyMapWrites = new System.Windows.Forms.CheckBox(); this.AutoStart = new System.Windows.Forms.CheckBox(); this.panel2 = new System.Windows.Forms.Panel(); this.load = new System.Windows.Forms.Button(); @@ -181,7 +182,7 @@ this.queueFrameCap.Location = new System.Drawing.Point(3, 3); this.queueFrameCap.Name = "queueFrameCap"; this.queueFrameCap.Size = new System.Drawing.Size(151, 24); - this.queueFrameCap.TabIndex = 19; + this.queueFrameCap.TabIndex = 20; this.queueFrameCap.Text = "Queue Capture of Frame"; this.queueFrameCap.UseVisualStyleBackColor = true; // @@ -201,7 +202,7 @@ 0}); this.queuedCapFrame.Name = "queuedCapFrame"; this.queuedCapFrame.Size = new System.Drawing.Size(120, 20); - this.queuedCapFrame.TabIndex = 20; + this.queuedCapFrame.TabIndex = 21; this.queuedCapFrame.ThousandsSeparator = true; this.queuedCapFrame.Value = new decimal(new int[] { 2, @@ -278,6 +279,7 @@ this.capOptsFlow.Controls.Add(this.SaveAllInitials); this.capOptsFlow.Controls.Add(this.RefAllResources); this.capOptsFlow.Controls.Add(this.CaptureAllCmdLists); + this.capOptsFlow.Controls.Add(this.VerifyMapWrites); this.capOptsFlow.Controls.Add(this.AutoStart); this.capOptsFlow.Location = new System.Drawing.Point(3, 16); this.capOptsFlow.MaximumSize = new System.Drawing.Size(640, 0); @@ -412,12 +414,22 @@ "as an overhead but ensures if you hold onto a list it will be captured."); this.CaptureAllCmdLists.UseVisualStyleBackColor = true; // + // VerifyMapWrites + // + this.VerifyMapWrites.Location = new System.Drawing.Point(275, 55); + this.VerifyMapWrites.Name = "VerifyMapWrites"; + this.VerifyMapWrites.Size = new System.Drawing.Size(130, 20); + this.VerifyMapWrites.TabIndex = 18; + this.VerifyMapWrites.Text = "Verify Map() Writes"; + this.toolTip.SetToolTip(this.AutoStart, "When enabled, causes Map() pointers to be validated to check for buffer overruns."); + this.VerifyMapWrites.UseVisualStyleBackColor = true; + // // AutoStart // - this.AutoStart.Location = new System.Drawing.Point(275, 55); + this.AutoStart.Location = new System.Drawing.Point(411, 55); this.AutoStart.Name = "AutoStart"; this.AutoStart.Size = new System.Drawing.Size(130, 20); - this.AutoStart.TabIndex = 18; + this.AutoStart.TabIndex = 19; this.AutoStart.Text = "Auto start"; this.toolTip.SetToolTip(this.AutoStart, "If these capture settings are saved & run, auto start the capture instantly on lo" + "ad"); @@ -733,6 +745,7 @@ private System.Windows.Forms.CheckBox SaveAllInitials; private System.Windows.Forms.CheckBox RefAllResources; private System.Windows.Forms.CheckBox CaptureCallstacksOnlyDraws; + private System.Windows.Forms.CheckBox VerifyMapWrites; private System.Windows.Forms.CheckBox AutoStart; private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.FlowLayoutPanel capOptsFlow; diff --git a/renderdocui/Windows/Dialogs/CaptureDialog.cs b/renderdocui/Windows/Dialogs/CaptureDialog.cs index 9a6b9a210..3af0a5c56 100644 --- a/renderdocui/Windows/Dialogs/CaptureDialog.cs +++ b/renderdocui/Windows/Dialogs/CaptureDialog.cs @@ -79,6 +79,7 @@ namespace renderdocui.Windows.Dialogs RefAllResources.Checked = settings.Options.RefAllResources; SaveAllInitials.Checked = settings.Options.SaveAllInitials; DelayForDebugger.Value = settings.Options.DelayForDebugger; + VerifyMapWrites.Checked = settings.Options.VerifyMapWrites; AutoStart.Checked = settings.AutoStart; if (settings.AutoStart) @@ -117,6 +118,7 @@ namespace renderdocui.Windows.Dialogs ret.Options.SaveAllInitials = SaveAllInitials.Checked; ret.Options.CaptureAllCmdLists = CaptureAllCmdLists.Checked; ret.Options.DelayForDebugger = (uint)DelayForDebugger.Value; + ret.Options.VerifyMapWrites = VerifyMapWrites.Checked; return ret; }