/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2015-2019 Baldur Karlsson * Copyright (c) 2014 Crytek * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #include "d3d11_device.h" #include "core/core.h" #include "driver/dxgi/dxgi_wrapped.h" #include "jpeg-compressor/jpge.h" #include "maths/formatpacking.h" #include "serialise/rdcfile.h" #include "strings/string_utils.h" #include "d3d11_context.h" #include "d3d11_debug.h" #include "d3d11_renderstate.h" #include "d3d11_rendertext.h" #include "d3d11_resources.h" #include "d3d11_shader_cache.h" WRAPPED_POOL_INST(WrappedID3D11Device); WrappedID3D11Device *WrappedID3D11Device::m_pCurrentWrappedDevice = NULL; void WrappedID3D11Device::NewSwapchainBuffer(IUnknown *backbuffer) { WrappedID3D11Texture2D1 *wrapped = (WrappedID3D11Texture2D1 *)backbuffer; if(wrapped) { // keep ref as a 'view' (invisible to user) wrapped->ViewAddRef(); wrapped->Release(); } } WrappedID3D11Device::WrappedID3D11Device(ID3D11Device *realDevice, D3D11InitParams params) : m_RefCounter(realDevice, false), m_SoftRefCounter(NULL, false), m_pDevice(realDevice), m_ScratchSerialiser(new StreamWriter(1024), Ownership::Stream) { if(RenderDoc::Inst().GetCrashHandler()) RenderDoc::Inst().GetCrashHandler()->RegisterMemoryRegion(this, sizeof(WrappedID3D11Device)); m_SectionVersion = D3D11InitParams::CurrentVersion; uint32_t flags = WriteSerialiser::ChunkDuration | WriteSerialiser::ChunkTimestamp | WriteSerialiser::ChunkThreadID; if(RenderDoc::Inst().GetCaptureOptions().captureCallstacks) flags |= WriteSerialiser::ChunkCallstack; m_ScratchSerialiser.SetChunkMetadataRecording(flags); m_ScratchSerialiser.SetVersion(D3D11InitParams::CurrentVersion); m_StructuredFile = &m_StoredStructuredData; m_pDevice1 = NULL; m_pDevice2 = NULL; m_pDevice3 = NULL; m_pDevice4 = NULL; m_pDevice5 = NULL; if(m_pDevice) { m_pDevice->QueryInterface(__uuidof(ID3D11Device1), (void **)&m_pDevice1); m_pDevice->QueryInterface(__uuidof(ID3D11Device2), (void **)&m_pDevice2); m_pDevice->QueryInterface(__uuidof(ID3D11Device3), (void **)&m_pDevice3); m_pDevice->QueryInterface(__uuidof(ID3D11Device4), (void **)&m_pDevice4); m_pDevice->QueryInterface(__uuidof(ID3D11Device5), (void **)&m_pDevice5); } // refcounters implicitly construct with one reference, but we don't start with any soft // references. m_SoftRefCounter.Release(); m_InternalRefcount = 0; m_Alive = true; m_DummyInfoQueue.m_pDevice = this; m_DummyDebug.m_pDevice = this; m_WrappedDebug.m_pDevice = this; m_WrappedMultithread.m_pDevice = this; m_WrappedVideo.m_pDevice = this; m_FailedReason = CaptureSucceeded; m_ChunkAtomic = 0; if(RenderDoc::Inst().IsReplayApp()) { m_State = CaptureState::LoadingReplaying; D3D11MarkerRegion::device = this; std::string shaderSearchPathString = RenderDoc::Inst().GetConfigSetting("shader.debug.searchPaths"); split(shaderSearchPathString, m_ShaderSearchPaths, ';'); ResourceIDGen::SetReplayResourceIDs(); } else { m_State = CaptureState::BackgroundCapturing; } m_ResourceManager = new D3D11ResourceManager(m_State, this); m_ShaderCache = new D3D11ShaderCache(this); m_ScratchSerialiser.SetUserData(GetResourceManager()); // create a temporary and grab its resource ID m_ResourceID = ResourceIDGen::GetNewUniqueID(); m_DeviceRecord = NULL; m_InitParams = params; if(!RenderDoc::Inst().IsReplayApp()) { m_DeviceRecord = GetResourceManager()->AddResourceRecord(m_ResourceID); m_DeviceRecord->ResType = Resource_Unknown; m_DeviceRecord->DataInSerialiser = false; m_DeviceRecord->InternalResource = true; m_DeviceRecord->Length = 0; m_DeviceRecord->NumSubResources = 0; m_DeviceRecord->SubResources = NULL; RenderDoc::Inst().AddDeviceFrameCapturer((ID3D11Device *)this, this); { IDXGIDevice *pDXGIDevice = NULL; HRESULT hr = m_pDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice); if(FAILED(hr)) { RDCERR("Couldn't get DXGI device from D3D device"); } else { IDXGIAdapter *pDXGIAdapter = NULL; hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&pDXGIAdapter); if(SUCCEEDED(hr)) { DXGI_ADAPTER_DESC desc = {}; pDXGIAdapter->GetDesc(&desc); m_InitParams.AdapterDesc = desc; GPUVendor vendor = GPUVendorFromPCIVendor(desc.VendorId); std::string descString = GetDriverVersion(desc); RDCLOG("New D3D11 device created: %s / %s", ToStr(vendor).c_str(), descString.c_str()); SAFE_RELEASE(pDXGIAdapter); } } SAFE_RELEASE(pDXGIDevice); } } ID3D11DeviceContext *context = NULL; if(realDevice) realDevice->GetImmediateContext(&context); m_pImmediateContext = new WrappedID3D11DeviceContext(this, context); m_pImmediateContext->GetScratchSerialiser().SetChunkMetadataRecording( m_ScratchSerialiser.GetChunkMetadataRecording()); m_pInfoQueue = NULL; if(realDevice) { realDevice->QueryInterface(__uuidof(ID3D11InfoQueue), (void **)&m_pInfoQueue); realDevice->QueryInterface(__uuidof(ID3D11Debug), (void **)&m_WrappedDebug.m_pDebug); realDevice->QueryInterface(__uuidof(ID3D11Multithread), (void **)&m_WrappedMultithread.m_pReal); realDevice->QueryInterface(__uuidof(ID3D11VideoDevice), (void **)&m_WrappedVideo.m_pReal); realDevice->QueryInterface(__uuidof(ID3D11VideoDevice1), (void **)&m_WrappedVideo.m_pReal1); realDevice->QueryInterface(__uuidof(ID3D11VideoDevice2), (void **)&m_WrappedVideo.m_pReal2); } // useful for marking regions during replay for self-captures m_RealAnnotations = NULL; if(context) context->QueryInterface(__uuidof(ID3DUserDefinedAnnotation), (void **)&m_RealAnnotations); if(m_pInfoQueue) { if(RenderDoc::Inst().GetCaptureOptions().debugOutputMute) m_pInfoQueue->SetMuteDebugOutput(true); UINT size = m_pInfoQueue->GetStorageFilterStackSize(); while(size > 1) { m_pInfoQueue->ClearStorageFilter(); size = m_pInfoQueue->GetStorageFilterStackSize(); } size = m_pInfoQueue->GetRetrievalFilterStackSize(); while(size > 1) { m_pInfoQueue->ClearRetrievalFilter(); size = m_pInfoQueue->GetRetrievalFilterStackSize(); } m_pInfoQueue->ClearStoredMessages(); if(RenderDoc::Inst().IsReplayApp()) m_pInfoQueue->SetMuteDebugOutput(false); } else if(m_pDevice) { RDCDEBUG("Couldn't get ID3D11InfoQueue."); } m_Replay.SetDevice(this); if(realDevice) m_DebugManager = new D3D11DebugManager(this); // ATI workaround - these dlls can get unloaded and cause a crash. if(GetModuleHandleA("aticfx32.dll")) LoadLibraryA("aticfx32.dll"); if(GetModuleHandleA("atiuxpag.dll")) LoadLibraryA("atiuxpag.dll"); if(GetModuleHandleA("atidxx32.dll")) LoadLibraryA("atidxx32.dll"); if(GetModuleHandleA("aticfx64.dll")) LoadLibraryA("aticfx64.dll"); if(GetModuleHandleA("atiuxp64.dll")) LoadLibraryA("atiuxp64.dll"); if(GetModuleHandleA("atidxx64.dll")) LoadLibraryA("atidxx64.dll"); // NVIDIA workaround - same as above! if(GetModuleHandleA("nvwgf2umx.dll")) LoadLibraryA("nvwgf2umx.dll"); } WrappedID3D11Device::~WrappedID3D11Device() { if(m_pCurrentWrappedDevice == this) m_pCurrentWrappedDevice = NULL; D3D11MarkerRegion::device = NULL; RenderDoc::Inst().RemoveDeviceFrameCapturer((ID3D11Device *)this); for(auto it = m_CachedStateObjects.begin(); it != m_CachedStateObjects.end(); ++it) if(*it) (*it)->Release(); m_CachedStateObjects.clear(); GetResourceManager()->ClearReferencedResources(); SAFE_RELEASE(m_pDevice1); SAFE_RELEASE(m_pDevice2); SAFE_RELEASE(m_pDevice3); SAFE_RELEASE(m_pDevice4); SAFE_RELEASE(m_pDevice5); SAFE_RELEASE(m_RealAnnotations); SAFE_RELEASE(m_pImmediateContext); for(auto it = m_SwapChains.begin(); it != m_SwapChains.end(); ++it) SAFE_RELEASE(it->second); m_Replay.DestroyResources(); SAFE_DELETE(m_DebugManager); SAFE_DELETE(m_TextRenderer); SAFE_DELETE(m_ShaderCache); if(m_DeviceRecord) { RDCASSERT(m_DeviceRecord->GetRefCount() == 1); m_DeviceRecord->Delete(GetResourceManager()); } for(auto it = m_LayoutShaders.begin(); it != m_LayoutShaders.end(); ++it) SAFE_DELETE(it->second); m_LayoutShaders.clear(); m_LayoutDescs.clear(); m_ResourceManager->Shutdown(); SAFE_DELETE(m_ResourceManager); SAFE_RELEASE(m_pInfoQueue); SAFE_RELEASE(m_WrappedMultithread.m_pReal); SAFE_RELEASE(m_WrappedVideo.m_pReal); SAFE_RELEASE(m_WrappedVideo.m_pReal1); SAFE_RELEASE(m_WrappedVideo.m_pReal2); SAFE_RELEASE(m_WrappedDebug.m_pDebug); SAFE_RELEASE(m_pDevice); if(!IsStructuredExporting(m_State)) { RDCASSERT(WrappedID3D11Buffer::m_BufferList.empty()); RDCASSERT(WrappedID3D11Texture1D::m_TextureList.empty()); RDCASSERT(WrappedID3D11Texture2D1::m_TextureList.empty()); RDCASSERT(WrappedID3D11Texture3D1::m_TextureList.empty()); } if(RenderDoc::Inst().GetCrashHandler()) RenderDoc::Inst().GetCrashHandler()->UnregisterMemoryRegion(this); } void WrappedID3D11Device::CheckForDeath() { if(!m_Alive) return; if(m_RefCounter.GetRefCount() == 0) { RDCASSERT(m_SoftRefCounter.GetRefCount() >= m_InternalRefcount); // MEGA HACK if(m_SoftRefCounter.GetRefCount() <= m_InternalRefcount || IsReplayMode(m_State)) { m_Alive = false; delete this; } } } ULONG STDMETHODCALLTYPE DummyID3D11InfoQueue::AddRef() { m_pDevice->AddRef(); return 1; } ULONG STDMETHODCALLTYPE DummyID3D11InfoQueue::Release() { m_pDevice->Release(); return 1; } HRESULT STDMETHODCALLTYPE DummyID3D11Debug::QueryInterface(REFIID riid, void **ppvObject) { return m_pDevice->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE DummyID3D11Debug::AddRef() { m_pDevice->AddRef(); return 1; } ULONG STDMETHODCALLTYPE DummyID3D11Debug::Release() { m_pDevice->Release(); return 1; } HRESULT STDMETHODCALLTYPE WrappedD3D11Multithread::QueryInterface(REFIID riid, void **ppvObject) { if(riid == __uuidof(IUnknown)) { *ppvObject = (IUnknown *)this; AddRef(); return S_OK; } if(riid == __uuidof(ID3D11Multithread)) { *ppvObject = (ID3D11Multithread *)this; AddRef(); return S_OK; } return E_NOINTERFACE; } ULONG STDMETHODCALLTYPE WrappedD3D11Multithread::AddRef() { m_pDevice->AddRef(); return 1; } ULONG STDMETHODCALLTYPE WrappedD3D11Multithread::Release() { m_pDevice->Release(); return 1; } void STDMETHODCALLTYPE WrappedD3D11Multithread::Enter() { m_pDevice->D3DLock().Lock(); if(m_pReal) m_pReal->Enter(); } void STDMETHODCALLTYPE WrappedD3D11Multithread::Leave() { if(m_pReal) m_pReal->Leave(); m_pDevice->D3DLock().Unlock(); } BOOL STDMETHODCALLTYPE WrappedD3D11Multithread::SetMultithreadProtected(BOOL bMTProtect) { bool old = m_pDevice->D3DThreadSafe(); m_pDevice->SetD3DThreadSafe(bMTProtect == TRUE); if(m_pReal) m_pReal->SetMultithreadProtected(bMTProtect); // TODO - unclear if m_MultithreadProtected just enables Enter/Leave to work, or if it enables // auto-thread safety on all D3D interfaces return old ? TRUE : FALSE; } BOOL STDMETHODCALLTYPE WrappedD3D11Multithread::GetMultithreadProtected() { return m_pDevice->D3DThreadSafe() ? TRUE : FALSE; } HRESULT STDMETHODCALLTYPE WrappedID3D11Debug::QueryInterface(REFIID riid, void **ppvObject) { if(riid == __uuidof(ID3D11InfoQueue) || riid == __uuidof(ID3D11Debug) || riid == __uuidof(ID3D11Device) || riid == __uuidof(ID3D11Device1) || riid == __uuidof(ID3D11Device2) || riid == __uuidof(ID3D11Device3) || riid == __uuidof(ID3D11Device4)) return m_pDevice->QueryInterface(riid, ppvObject); if(riid == __uuidof(IUnknown)) { *ppvObject = (IUnknown *)(ID3D11Debug *)this; AddRef(); return S_OK; } WarnUnknownGUID("ID3D11Debug", riid); return m_pDebug->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE WrappedID3D11Debug::AddRef() { m_pDevice->AddRef(); return 1; } ULONG STDMETHODCALLTYPE WrappedID3D11Debug::Release() { m_pDevice->Release(); return 1; } HRESULT WrappedID3D11Device::QueryInterface(REFIID riid, void **ppvObject) { // DEFINE_GUID(IID_IDirect3DDevice9, 0xd0223b96, 0xbf7a, 0x43fd, 0x92, 0xbd, 0xa4, 0x3b, 0xd, // 0x82, 0xb9, 0xeb); static const GUID IDirect3DDevice9_uuid = { 0xd0223b96, 0xbf7a, 0x43fd, {0x92, 0xbd, 0xa4, 0x3b, 0xd, 0x82, 0xb9, 0xeb}}; // ID3D10Device UUID {9B7E4C0F-342C-4106-A19F-4F2704F689F0} static const GUID ID3D10Device_uuid = { 0x9b7e4c0f, 0x342c, 0x4106, {0xa1, 0x9f, 0x4f, 0x27, 0x04, 0xf6, 0x89, 0xf0}}; // ID3D10DeviceChild UUID {9B7E4C00-342C-4106-A19F-4F2704F689F0} static const GUID ID3D10DeviceChild_uuid = { 0x9b7e4c00, 0x342c, 0x4106, {0xa1, 0x9f, 0x4f, 0x27, 0x04, 0xf6, 0x89, 0xf0}}; // ID3D12Device UUID {189819f1-1db6-4b57-be54-1821339b85f7} static const GUID ID3D12Device_uuid = { 0x189819f1, 0x1db6, 0x4b57, {0xbe, 0x54, 0x18, 0x21, 0x33, 0x9b, 0x85, 0xf7}}; // ID3D11ShaderTraceFactory UUID {1fbad429-66ab-41cc-9617-667ac10e4459} static const GUID ID3D11ShaderTraceFactory_uuid = { 0x1fbad429, 0x66ab, 0x41cc, {0x96, 0x17, 0x66, 0x7a, 0xc1, 0x0e, 0x44, 0x59}}; // ID3D11On12Device UUID {85611e73-70a9-490e-9614-a9e302777904} static const GUID ID3D11On12Device_uuid = { 0x85611e73, 0x70a9, 0x490e, {0x96, 0x14, 0xa9, 0xe3, 0x02, 0x77, 0x79, 0x04}}; // RenderDoc UUID {A7AA6116-9C8D-4BBA-9083-B4D816B71B78} static const GUID IRenderDoc_uuid = { 0xa7aa6116, 0x9c8d, 0x4bba, {0x90, 0x83, 0xb4, 0xd8, 0x16, 0xb7, 0x1b, 0x78}}; // UUID for returning unwrapped ID3D11InfoQueue {3FC4E618-3F70-452A-8B8F-A73ACCB58E3D} static const GUID unwrappedID3D11InfoQueue__uuid = { 0x3fc4e618, 0x3f70, 0x452a, {0x8b, 0x8f, 0xa7, 0x3a, 0xcc, 0xb5, 0x8e, 0x3d}}; HRESULT hr = S_OK; if(riid == __uuidof(IUnknown)) { *ppvObject = (IUnknown *)(ID3D11Device4 *)this; AddRef(); return S_OK; } else if(riid == __uuidof(IDXGIDevice)) { hr = m_pDevice->QueryInterface(riid, ppvObject); if(SUCCEEDED(hr)) { IDXGIDevice *real = (IDXGIDevice *)(*ppvObject); *ppvObject = (IDXGIDevice *)(new WrappedIDXGIDevice4(real, this)); return S_OK; } else { *ppvObject = NULL; return hr; } } else if(riid == __uuidof(IDXGIDevice1)) { hr = m_pDevice->QueryInterface(riid, ppvObject); if(SUCCEEDED(hr)) { IDXGIDevice1 *real = (IDXGIDevice1 *)(*ppvObject); *ppvObject = (IDXGIDevice1 *)(new WrappedIDXGIDevice4(real, this)); return S_OK; } else { *ppvObject = NULL; return hr; } } else if(riid == __uuidof(IDXGIDevice2)) { hr = m_pDevice->QueryInterface(riid, ppvObject); if(SUCCEEDED(hr)) { IDXGIDevice2 *real = (IDXGIDevice2 *)(*ppvObject); *ppvObject = (IDXGIDevice2 *)(new WrappedIDXGIDevice4(real, this)); return S_OK; } else { *ppvObject = NULL; return hr; } } else if(riid == __uuidof(IDXGIDevice3)) { hr = m_pDevice->QueryInterface(riid, ppvObject); if(SUCCEEDED(hr)) { IDXGIDevice3 *real = (IDXGIDevice3 *)(*ppvObject); *ppvObject = (IDXGIDevice3 *)(new WrappedIDXGIDevice4(real, this)); return S_OK; } else { *ppvObject = NULL; return hr; } } else if(riid == __uuidof(IDXGIDevice4)) { hr = m_pDevice->QueryInterface(riid, ppvObject); if(SUCCEEDED(hr)) { IDXGIDevice4 *real = (IDXGIDevice4 *)(*ppvObject); *ppvObject = (IDXGIDevice4 *)(new WrappedIDXGIDevice4(real, this)); return S_OK; } else { *ppvObject = NULL; return hr; } } else if(riid == __uuidof(ID3D11Device)) { AddRef(); *ppvObject = (ID3D11Device *)this; return S_OK; } else if(riid == ID3D10Device_uuid) { RDCWARN("Trying to get ID3D10Device - not supported."); *ppvObject = NULL; return E_NOINTERFACE; } else if(riid == ID3D10DeviceChild_uuid) { RDCWARN("Trying to get ID3D10DeviceChild - not supported."); *ppvObject = NULL; return E_NOINTERFACE; } else if(riid == ID3D12Device_uuid) { RDCWARN("Trying to get ID3D12Device - not supported."); *ppvObject = NULL; return E_NOINTERFACE; } else if(riid == IDirect3DDevice9_uuid) { RDCWARN("Trying to get IDirect3DDevice9 - not supported."); *ppvObject = NULL; return E_NOINTERFACE; } else if(riid == __uuidof(ID3D11Device1)) { if(m_pDevice1) { AddRef(); *ppvObject = (ID3D11Device1 *)this; return S_OK; } else { return E_NOINTERFACE; } } else if(riid == __uuidof(ID3D11Device2)) { if(m_pDevice2) { AddRef(); *ppvObject = (ID3D11Device2 *)this; RDCWARN( "Trying to get ID3D11Device2. DX11.2 tiled resources are not supported at this time."); return S_OK; } else { return E_NOINTERFACE; } } else if(riid == __uuidof(ID3D11Device3)) { if(m_pDevice3) { AddRef(); *ppvObject = (ID3D11Device3 *)this; return S_OK; } else { return E_NOINTERFACE; } } else if(riid == __uuidof(ID3D11Device4)) { if(m_pDevice4) { AddRef(); *ppvObject = (ID3D11Device4 *)this; return S_OK; } else { return E_NOINTERFACE; } } else if(riid == __uuidof(ID3D11Device5)) { if(m_pDevice5) { AddRef(); *ppvObject = (ID3D11Device5 *)this; return S_OK; } else { return E_NOINTERFACE; } } else if(riid == __uuidof(ID3D11Multithread)) { AddRef(); *ppvObject = (ID3D11Multithread *)&m_WrappedMultithread; return S_OK; } else if(riid == ID3D11ShaderTraceFactory_uuid) { RDCWARN("Trying to get ID3D11ShaderTraceFactory. Not supported at this time."); *ppvObject = NULL; return E_NOINTERFACE; } else if(riid == ID3D11On12Device_uuid) { RDCWARN("Trying to get ID3D11On12Device. Not supported at this time."); *ppvObject = NULL; return E_NOINTERFACE; } else if(riid == __uuidof(ID3D11InfoQueue)) { RDCWARN( "Returning a dummy ID3D11InfoQueue that does nothing. RenderDoc takes control of the debug " "layer."); RDCWARN( "If you want direct access, enable API validation and query for %s. This will return the " "real ID3D11InfoQueue - be careful as it is unwrapped so you should not call " "QueryInterface on it.", ToStr(unwrappedID3D11InfoQueue__uuid).c_str()); *ppvObject = (ID3D11InfoQueue *)&m_DummyInfoQueue; m_DummyInfoQueue.AddRef(); return S_OK; } else if(riid == unwrappedID3D11InfoQueue__uuid) { if(m_pInfoQueue) { *ppvObject = m_pInfoQueue; m_pInfoQueue->AddRef(); return S_OK; } else { if(!RenderDoc::Inst().GetCaptureOptions().apiValidation) { RDCWARN("API Validation is not enabled, RenderDoc disabled the debug layer."); RDCWARN( "Enable this either in the capture options, or using the RenderDoc API before device " "creation."); } return E_NOINTERFACE; } } else if(riid == __uuidof(ID3D11Debug)) { // we queryinterface for this at startup, so if it's present we can // return our wrapper if(m_WrappedDebug.m_pDebug) { AddRef(); *ppvObject = (ID3D11Debug *)&m_WrappedDebug; return S_OK; } else { RDCWARN("Returning a dummy ID3D11Debug that does nothing. This ID3D11Debug will not work!"); *ppvObject = (ID3D11Debug *)&m_DummyDebug; m_DummyDebug.AddRef(); return S_OK; } } else if(riid == IRenderDoc_uuid) { AddRef(); *ppvObject = (IUnknown *)this; return S_OK; } else if(riid == __uuidof(ID3D11VideoDevice) || riid == __uuidof(ID3D11VideoDevice1) || riid == __uuidof(ID3D11VideoDevice2)) { return m_WrappedVideo.QueryInterface(riid, ppvObject); } WarnUnknownGUID("ID3D11Device", riid); return m_RefCounter.QueryInterface(riid, ppvObject); } std::string WrappedID3D11Device::GetChunkName(uint32_t idx) { if((SystemChunk)idx < SystemChunk::FirstDriverChunk) return ToStr((SystemChunk)idx); return ToStr((D3D11Chunk)idx); } void WrappedID3D11Device::AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, std::string d) { // Only add runtime warnings while executing. // While reading, add the messages from the log, and while writing add messages // we add (on top of the API debug messages) if(!IsActiveReplaying(m_State) || src == MessageSource::RuntimeWarning) { DebugMessage msg; msg.eventId = IsCaptureMode(m_State) ? 0 : m_pImmediateContext->GetEventID(); msg.messageID = 0; msg.source = src; msg.category = c; msg.severity = sv; msg.description = d; m_DebugMessages.push_back(msg); } } void WrappedID3D11Device::AddDebugMessage(DebugMessage msg) { if(!IsActiveReplaying(m_State) || msg.source == MessageSource::RuntimeWarning) m_DebugMessages.push_back(msg); } std::vector WrappedID3D11Device::GetDebugMessages() { std::vector ret; if(IsActiveReplaying(m_State)) { // once we're active replaying, m_DebugMessages will contain all the messages from loading // (either from the captured serialised messages, or from ourselves during replay). ret.swap(m_DebugMessages); return ret; } if(IsCaptureMode(m_State)) { // add any manually-added messages before fetching those from the API ret.swap(m_DebugMessages); } // during loading only try and fetch messages if we're doing that deliberately during replay if(IsLoading(m_State) && !m_ReplayOptions.apiValidation) return ret; // during capture, and during loading if the option is enabled, we fetch messages from the API if(!m_pInfoQueue) return ret; UINT64 numMessages = m_pInfoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); for(UINT64 i = 0; i < m_pInfoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); i++) { SIZE_T len = 0; m_pInfoQueue->GetMessage(i, NULL, &len); char *msgbuf = new char[len]; D3D11_MESSAGE *message = (D3D11_MESSAGE *)msgbuf; m_pInfoQueue->GetMessage(i, message, &len); DebugMessage msg; msg.eventId = 0; msg.source = MessageSource::API; msg.category = MessageCategory::Miscellaneous; msg.severity = MessageSeverity::Medium; switch(message->Category) { case D3D11_MESSAGE_CATEGORY_APPLICATION_DEFINED: msg.category = MessageCategory::Application_Defined; break; case D3D11_MESSAGE_CATEGORY_MISCELLANEOUS: msg.category = MessageCategory::Miscellaneous; break; case D3D11_MESSAGE_CATEGORY_INITIALIZATION: msg.category = MessageCategory::Initialization; break; case D3D11_MESSAGE_CATEGORY_CLEANUP: msg.category = MessageCategory::Cleanup; break; case D3D11_MESSAGE_CATEGORY_COMPILATION: msg.category = MessageCategory::Compilation; break; case D3D11_MESSAGE_CATEGORY_STATE_CREATION: msg.category = MessageCategory::State_Creation; break; case D3D11_MESSAGE_CATEGORY_STATE_SETTING: msg.category = MessageCategory::State_Setting; break; case D3D11_MESSAGE_CATEGORY_STATE_GETTING: msg.category = MessageCategory::State_Getting; break; case D3D11_MESSAGE_CATEGORY_RESOURCE_MANIPULATION: msg.category = MessageCategory::Resource_Manipulation; break; case D3D11_MESSAGE_CATEGORY_EXECUTION: msg.category = MessageCategory::Execution; break; case D3D11_MESSAGE_CATEGORY_SHADER: msg.category = MessageCategory::Shaders; break; default: RDCWARN("Unexpected message category: %d", message->Category); break; } switch(message->Severity) { case D3D11_MESSAGE_SEVERITY_CORRUPTION: msg.severity = MessageSeverity::High; break; case D3D11_MESSAGE_SEVERITY_ERROR: msg.severity = MessageSeverity::High; break; case D3D11_MESSAGE_SEVERITY_WARNING: msg.severity = MessageSeverity::Medium; break; case D3D11_MESSAGE_SEVERITY_INFO: msg.severity = MessageSeverity::Low; break; case D3D11_MESSAGE_SEVERITY_MESSAGE: msg.severity = MessageSeverity::Info; break; default: RDCWARN("Unexpected message severity: %d", message->Severity); break; } msg.messageID = (uint32_t)message->ID; msg.description = std::string(message->pDescription); ret.push_back(msg); SAFE_DELETE_ARRAY(msgbuf); } // Docs are fuzzy on the thread safety of the info queue, but I'm going to assume it should only // ever be accessed on one thread since it's tied to the device & immediate context. // There doesn't seem to be a way to lock it for access and without that there's no way to know // that a new message won't be added between the time you retrieve the last one and clearing the // queue. There is also no way to pop a message that I can see, which would presumably be the // best way if its member functions are thread safe themselves (if the queue is protected // internally). RDCASSERT(numMessages == m_pInfoQueue->GetNumStoredMessagesAllowedByRetrievalFilter()); m_pInfoQueue->ClearStoredMessages(); return ret; } bool WrappedID3D11Device::ProcessChunk(ReadSerialiser &ser, D3D11Chunk context) { switch(context) { case D3D11Chunk::DeviceInitialisation: { SERIALISE_ELEMENT_LOCAL(ImmediateContext, ResourceId()).TypedAs("ID3D11DeviceContext *"_lit); SERIALISE_CHECK_READ_ERRORS(); // add a reference for the resource manager - normally it takes ownership of the resource on // creation and releases it // to destruction, but we want to control our immediate context ourselves. if(IsReplayingAndReading()) { m_pImmediateContext->AddRef(); m_ResourceManager->AddLiveResource(ImmediateContext, m_pImmediateContext); AddResource(ImmediateContext, ResourceType::Queue, ""); ResourceDescription &desc = GetReplay()->GetResourceDesc(ImmediateContext); desc.autogeneratedName = false; desc.name = "Immediate Context"; desc.initialisationChunks.clear(); } return true; } case D3D11Chunk::SetResourceName: return Serialise_SetResourceName(ser, 0x0, ""); case D3D11Chunk::CreateSwapBuffer: return Serialise_WrapSwapchainBuffer(ser, 0x0, DXGI_FORMAT_UNKNOWN, 0, 0x0); case D3D11Chunk::CreateTexture1D: return Serialise_CreateTexture1D(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateTexture2D: return Serialise_CreateTexture2D(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateTexture2D1: return Serialise_CreateTexture2D1(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateTexture3D: return Serialise_CreateTexture3D(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateTexture3D1: return Serialise_CreateTexture3D1(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateBuffer: return Serialise_CreateBuffer(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateVertexShader: return Serialise_CreateVertexShader(ser, 0x0, 0, 0x0, 0x0); case D3D11Chunk::CreateHullShader: return Serialise_CreateHullShader(ser, 0x0, 0, 0x0, 0x0); case D3D11Chunk::CreateDomainShader: return Serialise_CreateDomainShader(ser, 0x0, 0, 0x0, 0x0); case D3D11Chunk::CreateGeometryShader: return Serialise_CreateGeometryShader(ser, 0x0, 0, 0x0, 0x0); case D3D11Chunk::CreateGeometryShaderWithStreamOutput: return Serialise_CreateGeometryShaderWithStreamOutput(ser, 0x0, 0, 0x0, 0, 0x0, 0, 0, 0x0, 0x0); case D3D11Chunk::CreatePixelShader: return Serialise_CreatePixelShader(ser, 0x0, 0, 0x0, 0x0); case D3D11Chunk::CreateComputeShader: return Serialise_CreateComputeShader(ser, 0x0, 0, 0x0, 0x0); case D3D11Chunk::GetClassInstance: return Serialise_GetClassInstance(ser, 0x0, 0, 0x0, 0x0); case D3D11Chunk::CreateClassInstance: return Serialise_CreateClassInstance(ser, 0x0, 0, 0, 0, 0, 0x0, 0x0); case D3D11Chunk::CreateClassLinkage: return Serialise_CreateClassLinkage(ser, 0x0); case D3D11Chunk::CreateShaderResourceView: return Serialise_CreateShaderResourceView(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateShaderResourceView1: return Serialise_CreateShaderResourceView1(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateRenderTargetView: return Serialise_CreateRenderTargetView(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateRenderTargetView1: return Serialise_CreateRenderTargetView1(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateDepthStencilView: return Serialise_CreateDepthStencilView(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateUnorderedAccessView: return Serialise_CreateUnorderedAccessView(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateUnorderedAccessView1: return Serialise_CreateUnorderedAccessView1(ser, 0x0, 0x0, 0x0); case D3D11Chunk::CreateInputLayout: return Serialise_CreateInputLayout(ser, 0x0, 0, 0x0, 0, 0x0); case D3D11Chunk::CreateBlendState: return Serialise_CreateBlendState(ser, 0x0, 0x0); case D3D11Chunk::CreateBlendState1: return Serialise_CreateBlendState1(ser, 0x0, 0x0); case D3D11Chunk::CreateDepthStencilState: return Serialise_CreateDepthStencilState(ser, 0x0, 0x0); case D3D11Chunk::CreateRasterizerState: return Serialise_CreateRasterizerState(ser, 0x0, 0x0); case D3D11Chunk::CreateRasterizerState1: return Serialise_CreateRasterizerState1(ser, 0x0, 0x0); case D3D11Chunk::CreateRasterizerState2: return Serialise_CreateRasterizerState2(ser, 0x0, 0x0); case D3D11Chunk::CreateSamplerState: return Serialise_CreateSamplerState(ser, 0x0, 0x0); case D3D11Chunk::CreateQuery: return Serialise_CreateQuery(ser, 0x0, 0x0); case D3D11Chunk::CreateQuery1: return Serialise_CreateQuery1(ser, 0x0, 0x0); case D3D11Chunk::CreatePredicate: return Serialise_CreatePredicate(ser, 0x0, 0x0); case D3D11Chunk::CreateCounter: return Serialise_CreateCounter(ser, 0x0, 0x0); case D3D11Chunk::CreateDeferredContext: return Serialise_CreateDeferredContext(ser, 0, 0x0); case D3D11Chunk::SetExceptionMode: return Serialise_SetExceptionMode(ser, 0); case D3D11Chunk::ExternalDXGIResource: case D3D11Chunk::OpenSharedResource: case D3D11Chunk::OpenSharedResource1: case D3D11Chunk::OpenSharedResourceByName: { IID nul; return Serialise_OpenSharedResource(ser, 0, nul, NULL); } case D3D11Chunk::SetShaderDebugPath: return Serialise_SetShaderDebugPath(ser, NULL, NULL); default: { SystemChunk system = (SystemChunk)context; if(system == SystemChunk::DriverInit) { D3D11InitParams InitParams; SERIALISE_ELEMENT(InitParams); SERIALISE_CHECK_READ_ERRORS(); } else if(system == SystemChunk::InitialContentsList) { GetResourceManager()->CreateInitialContents(ser); SERIALISE_CHECK_READ_ERRORS(); } else if(system == SystemChunk::InitialContents) { return Serialise_InitialState(ser, ResourceId(), NULL, NULL); } else if(system == SystemChunk::CaptureScope) { return Serialise_CaptureScope(ser); } else if(system < SystemChunk::FirstDriverChunk) { RDCERR("Unexpected system chunk in capture data: %u", system); ser.SkipCurrentChunk(); SERIALISE_CHECK_READ_ERRORS(); } else { return m_pImmediateContext->ProcessChunk(ser, context); } break; } } return true; } template bool WrappedID3D11Device::Serialise_CaptureScope(SerialiserType &ser) { SERIALISE_ELEMENT_LOCAL(frameNumber, m_CapturedFrames.back().frameNumber); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayMode(m_State)) { m_FrameRecord.frameInfo.frameNumber = frameNumber; FrameStatistics &stats = m_FrameRecord.frameInfo.stats; RDCEraseEl(stats); // #mivance GL/Vulkan don't set this so don't get stats in window stats.recorded = true; for(uint32_t stage = uint32_t(ShaderStage::First); stage < uint32_t(ShaderStage::Count); stage++) { stats.constants[stage].bindslots.resize(D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT + 1); stats.constants[stage].sizes.resize(ConstantBindStats::BucketCount); stats.samplers[stage].bindslots.resize(D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT + 1); stats.resources[stage].types.resize(uint32_t(TextureType::Count)); stats.resources[stage].bindslots.resize(D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT + 1); } stats.updates.types.resize(uint32_t(TextureType::Count)); stats.updates.sizes.resize(ResourceUpdateStats::BucketCount); stats.draws.counts.resize(DrawcallStats::BucketCount); stats.vertices.bindslots.resize(D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT + 1); stats.rasters.viewports.resize(D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX + 2); stats.rasters.rects.resize(D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX + 2); stats.outputs.bindslots.resize(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT + D3D11_1_UAV_SLOT_COUNT + 1); } return true; } ReplayStatus WrappedID3D11Device::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { int sectionIdx = rdc->SectionIndex(SectionType::FrameCapture); if(sectionIdx < 0) return ReplayStatus::FileCorrupted; StreamReader *reader = rdc->ReadSection(sectionIdx); if(reader->IsErrored()) { delete reader; return ReplayStatus::FileIOFailed; } ReadSerialiser ser(reader, Ownership::Stream); ser.SetStringDatabase(&m_StringDB); ser.SetUserData(GetResourceManager()); ser.ConfigureStructuredExport(&GetChunkName, storeStructuredBuffers); m_StructuredFile = &ser.GetStructuredFile(); m_StoredStructuredData.version = m_StructuredFile->version = m_SectionVersion; ser.SetVersion(m_SectionVersion); int chunkIdx = 0; struct chunkinfo { chunkinfo() : count(0), totalsize(0), total(0.0) {} int count; uint64_t totalsize; double total; }; std::map chunkInfos; SCOPED_TIMER("chunk initialisation"); uint64_t frameDataSize = 0; for(;;) { PerformanceTimer timer; uint64_t offsetStart = reader->GetOffset(); D3D11Chunk context = ser.ReadChunk(); chunkIdx++; if(reader->IsErrored()) return ReplayStatus::APIDataCorrupted; bool success = ProcessChunk(ser, context); ser.EndChunk(); if(reader->IsErrored()) return ReplayStatus::APIDataCorrupted; // if there wasn't a serialisation error, but the chunk didn't succeed, then it's an API replay // failure. if(!success) return m_FailedReplayStatus; uint64_t offsetEnd = reader->GetOffset(); RenderDoc::Inst().SetProgress(LoadProgress::FileInitialRead, float(offsetEnd) / float(reader->GetSize())); if((SystemChunk)context == SystemChunk::CaptureScope) { m_FrameRecord.frameInfo.fileOffset = offsetStart; // read the remaining data into memory and pass to immediate context frameDataSize = reader->GetSize() - reader->GetOffset(); m_pImmediateContext->SetFrameReader(new StreamReader(reader, frameDataSize)); if(!IsStructuredExporting(m_State)) { std::vector savedDebugMessages; // save any debug messages we built up savedDebugMessages.swap(m_DebugMessages); GetResourceManager()->ApplyInitialContents(); // restore saved messages - which implicitly discards any generated while applying initial // contents savedDebugMessages.swap(m_DebugMessages); } ReplayStatus status = m_pImmediateContext->ReplayLog(m_State, 0, 0, false); if(status != ReplayStatus::Succeeded) return status; } chunkInfos[context].total += timer.GetMilliseconds(); chunkInfos[context].totalsize += offsetEnd - offsetStart; chunkInfos[context].count++; if((SystemChunk)context == SystemChunk::CaptureScope || reader->IsErrored() || reader->AtEnd()) break; } // steal the structured data for ourselves m_StructuredFile->Swap(m_StoredStructuredData); // and in future use this file. m_StructuredFile = &m_StoredStructuredData; if(!IsStructuredExporting(m_State)) { SetupDrawcallPointers(m_Drawcalls, GetFrameRecord().drawcallList); } #if ENABLED(RDOC_DEVEL) for(auto it = chunkInfos.begin(); it != chunkInfos.end(); ++it) { double dcount = double(it->second.count); RDCDEBUG( "% 5d chunks - Time: %9.3fms total/%9.3fms avg - Size: %8.3fMB total/%7.3fMB avg - %s (%u)", it->second.count, it->second.total, it->second.total / dcount, double(it->second.totalsize) / (1024.0 * 1024.0), double(it->second.totalsize) / (dcount * 1024.0 * 1024.0), GetChunkName((uint32_t)it->first).c_str(), uint32_t(it->first)); } #endif m_FrameRecord.frameInfo.uncompressedFileSize = rdc->GetSectionProperties(sectionIdx).uncompressedSize; m_FrameRecord.frameInfo.compressedFileSize = rdc->GetSectionProperties(sectionIdx).compressedSize; m_FrameRecord.frameInfo.persistentSize = frameDataSize; m_FrameRecord.frameInfo.initDataSize = chunkInfos[(D3D11Chunk)SystemChunk::InitialContents].totalsize; 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, ReplayLogType replayType) { bool partial = true; if(startEventID == 0 && (replayType == eReplay_WithoutDraw || replayType == eReplay_Full)) { startEventID = 1; partial = false; } if(!partial) { D3D11MarkerRegion apply("!!!!RenderDoc Internal: ApplyInitialContents"); GetResourceManager()->ApplyInitialContents(); } m_State = CaptureState::ActiveReplaying; D3D11MarkerRegion::Set(StringFormat::Fmt("!!!!RenderDoc Internal: Replay %d (%d): %u->%u", (int)replayType, (int)partial, startEventID, endEventID)); m_ReplayEventCount = 0; ReplayStatus status = ReplayStatus::Succeeded; if(replayType == eReplay_Full) status = m_pImmediateContext->ReplayLog(m_State, startEventID, endEventID, partial); else if(replayType == eReplay_WithoutDraw) status = m_pImmediateContext->ReplayLog(m_State, startEventID, RDCMAX(1U, endEventID) - 1, partial); else if(replayType == eReplay_OnlyDraw) status = m_pImmediateContext->ReplayLog(m_State, endEventID, endEventID, partial); else RDCFATAL("Unexpected replay type"); RDCASSERTEQUAL(status, ReplayStatus::Succeeded); // make sure to end any unbalanced replay events if we stopped in the middle of a frame for(int i = 0; i < m_ReplayEventCount; i++) D3D11MarkerRegion::End(); D3D11MarkerRegion::Set("!!!!RenderDoc Internal: Done replay"); } void WrappedID3D11Device::ReleaseSwapchainResources(IDXGISwapper *swapper, UINT QueueCount, IUnknown *const *ppPresentQueue, IUnknown **unwrappedQueues) { RDCASSERT(ppPresentQueue == NULL); if(ppPresentQueue) { RDCERR("D3D11 doesn't support present queues - passing through unmodified"); for(UINT i = 0; i < QueueCount; i++) unwrappedQueues[i] = ppPresentQueue[i]; } for(int i = 0; i < swapper->GetNumBackbuffers(); i++) { WrappedID3D11Texture2D1 *wrapped11 = (WrappedID3D11Texture2D1 *)swapper->GetBackbuffers()[i]; if(wrapped11) { ResourceRange range(wrapped11); GetImmediateContext()->GetCurrentPipelineState()->UnbindRangeForWrite(range); GetImmediateContext()->GetCurrentPipelineState()->UnbindRangeForRead(range); { SCOPED_LOCK(WrappedID3DDeviceContextState::m_Lock); for(size_t s = 0; s < WrappedID3DDeviceContextState::m_List.size(); s++) { WrappedID3DDeviceContextState::m_List[s]->state->UnbindRangeForWrite(range); WrappedID3DDeviceContextState::m_List[s]->state->UnbindRangeForRead(range); } } wrapped11->ViewRelease(); } wrapped11 = NULL; } HWND wnd = swapper->GetHWND(); if(wnd) { Keyboard::RemoveInputWindow(WindowingSystem::Win32, wnd); RenderDoc::Inst().RemoveFrameCapturer((ID3D11Device *)this, wnd); } auto it = m_SwapChains.find(swapper); if(it != m_SwapChains.end()) { SAFE_RELEASE(it->second); m_SwapChains.erase(it); } } template bool WrappedID3D11Device::Serialise_WrapSwapchainBuffer(SerialiserType &ser, IDXGISwapper *swapper, DXGI_FORMAT bufferFormat, UINT Buffer, IUnknown *realSurface) { WrappedID3D11Texture2D1 *pTex = (WrappedID3D11Texture2D1 *)realSurface; SERIALISE_ELEMENT(Buffer); SERIALISE_ELEMENT_LOCAL(SwapbufferID, pTex->GetResourceID()).TypedAs("IDXGISwapChain *"_lit); m_BBID = SwapbufferID; D3D11_TEXTURE2D_DESC BackbufferDescriptor; if(ser.IsWriting() && pTex) pTex->GetDesc(&BackbufferDescriptor); SERIALISE_ELEMENT(BackbufferDescriptor); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading()) { ID3D11Texture2D *fakeBB; D3D11_TEXTURE2D_DESC realDescriptor = BackbufferDescriptor; // DXGI swap chain back buffers can be freely cast as a special-case. // translate the format to a typeless format to allow for this. // the original type will be stored in the texture below BackbufferDescriptor.Format = GetTypelessFormat(BackbufferDescriptor.Format); HRESULT hr = m_pDevice->CreateTexture2D(&BackbufferDescriptor, NULL, &fakeBB); AddResource(SwapbufferID, ResourceType::SwapchainImage, "Swapchain Image"); if(FAILED(hr)) { RDCERR("Failed to create fake back buffer, HRESULT: %s", ToStr(hr).c_str()); } else { WrappedID3D11Texture2D1 *wrapped = new WrappedID3D11Texture2D1(fakeBB, this, TEXDISPLAY_INDIRECT_VIEW); fakeBB = wrapped; wrapped->m_RealDescriptor = new D3D11_TEXTURE2D_DESC(realDescriptor); SetDebugName(fakeBB, "Serialised Swap Chain Buffer"); GetResourceManager()->AddLiveResource(SwapbufferID, fakeBB); } } return true; } IUnknown *WrappedID3D11Device::WrapSwapchainBuffer(IDXGISwapper *swapper, DXGI_FORMAT bufferFormat, UINT buffer, IUnknown *realSurface) { if(GetResourceManager()->HasWrapper((ID3D11DeviceChild *)realSurface)) { ID3D11Texture2D *tex = (ID3D11Texture2D *)GetResourceManager()->GetWrapper((ID3D11DeviceChild *)realSurface); tex->AddRef(); realSurface->Release(); return tex; } WrappedID3D11Texture2D1 *pTex = new WrappedID3D11Texture2D1((ID3D11Texture2D *)realSurface, this, TEXDISPLAY_UNKNOWN); SetDebugName(pTex, "Swap Chain Backbuffer"); D3D11_TEXTURE2D_DESC desc; pTex->GetDesc(&desc); ResourceId id = pTex->GetResourceID(); // init the text renderer if(m_TextRenderer == NULL) m_TextRenderer = new D3D11TextRenderer(this); // there shouldn't be a resource record for this texture as it wasn't created via // CreateTexture2D RDCASSERT(id != ResourceId() && !GetResourceManager()->HasResourceRecord(id)); if(IsCaptureMode(m_State)) { D3D11ResourceRecord *record = GetResourceManager()->AddResourceRecord(id); record->ResType = Resource_Texture2D; record->DataInSerialiser = false; record->Length = 0; record->NumSubResources = 0; record->SubResources = NULL; SCOPED_LOCK(m_D3DLock); WriteSerialiser &ser = m_ScratchSerialiser; SCOPED_SERIALISE_CHUNK(D3D11Chunk::CreateSwapBuffer); Serialise_WrapSwapchainBuffer(ser, swapper, bufferFormat, buffer, pTex); record->AddChunk(scope.Get()); } if(buffer == 0 && IsCaptureMode(m_State)) { ID3D11RenderTargetView *rtv = NULL; HRESULT hr = m_pDevice->CreateRenderTargetView(UNWRAP(WrappedID3D11Texture2D1, pTex), NULL, &rtv); if(FAILED(hr)) RDCERR("Couldn't create RTV for swapchain tex HRESULT: %s", ToStr(hr).c_str()); m_SwapChains[swapper] = rtv; } HWND wnd = swapper->GetHWND(); if(wnd) { Keyboard::AddInputWindow(WindowingSystem::Win32, wnd); RenderDoc::Inst().AddFrameCapturer((ID3D11Device *)this, wnd, this); } return pTex; } IDXGIResource *WrappedID3D11Device::WrapExternalDXGIResource(IDXGIResource *res) { ID3D11Resource *d3d11res; res->QueryInterface(__uuidof(ID3D11Resource), (void **)&d3d11res); if(GetResourceManager()->HasWrapper(d3d11res)) { ID3D11DeviceChild *wrapper = GetResourceManager()->GetWrapper(d3d11res); IDXGIResource *ret = NULL; wrapper->QueryInterface(__uuidof(IDXGIResource), (void **)&ret); res->Release(); return ret; } void *voidRes; // duration will be meaningless but at least we can get timestamp. SERIALISE_TIME_CALL(voidRes = (void *)res); OpenSharedResourceInternal(D3D11Chunk::ExternalDXGIResource, __uuidof(IDXGIResource), &voidRes); return (IDXGIResource *)voidRes; } void WrappedID3D11Device::SetMarker(uint32_t col, const wchar_t *name) { if(m_pCurrentWrappedDevice == NULL) return; m_pCurrentWrappedDevice->m_pImmediateContext->ThreadSafe_SetMarker(col, name); } int WrappedID3D11Device::BeginEvent(uint32_t col, const wchar_t *name) { if(m_pCurrentWrappedDevice == NULL) return 0; return m_pCurrentWrappedDevice->m_pImmediateContext->ThreadSafe_BeginEvent(col, name); } int WrappedID3D11Device::EndEvent() { if(m_pCurrentWrappedDevice == NULL) return 0; return m_pCurrentWrappedDevice->m_pImmediateContext->ThreadSafe_EndEvent(); } void WrappedID3D11Device::StartFrameCapture(void *dev, void *wnd) { SCOPED_LOCK(m_D3DLock); if(!IsBackgroundCapturing(m_State)) return; m_State = CaptureState::ActiveCapturing; m_AppControlledCapture = true; m_Failures = 0; m_FailedFrame = 0; m_FailedReason = CaptureSucceeded; FrameDescription frame; // for app-controlled captures the frame number is meaningless, so set it here here first. We'll // update it with the actual frame counter when we un-set m_AppControlledCapture. frame.frameNumber = ~0U; frame.captureTime = Timing::GetUnixTimestamp(); m_CapturedFrames.push_back(frame); m_DebugMessages.clear(); GetResourceManager()->ClearReferencedResources(); GetResourceManager()->MarkResourceFrameReferenced(m_ResourceID, eFrameRef_PartialWrite); GetResourceManager()->FreeCaptureData(); m_pImmediateContext->AttemptCapture(); m_pImmediateContext->BeginCaptureFrame(); for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) { context->AttemptCapture(); } else { RDCERR("NULL deferred context in resource record!"); } } GetResourceManager()->PrepareInitialContents(); if(m_pInfoQueue) m_pInfoQueue->ClearStoredMessages(); RDCLOG("Starting capture, frame %u", m_CapturedFrames.back().frameNumber); } bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd) { SCOPED_LOCK(m_D3DLock); if(!IsActiveCapturing(m_State)) return true; CaptureFailReason reason; IDXGISwapper *swapper = NULL; if(wnd) { for(auto it = m_SwapChains.begin(); it != m_SwapChains.end(); ++it) { if(it->first->GetHWND() == wnd) { swapper = it->first; break; } } if(swapper == NULL) { RDCERR("Output window %p provided for frame capture corresponds with no known swap chain", wnd); return false; } } if(m_pImmediateContext->HasSuccessfulCapture(reason)) { RDCLOG("Finished capture, Frame %u", m_CapturedFrames.back().frameNumber); if(swapper == NULL) swapper = m_LastSwap; m_Failures = 0; m_FailedFrame = 0; m_FailedReason = CaptureSucceeded; m_pImmediateContext->EndCaptureFrame(); m_pImmediateContext->FinishCapture(); for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) { context->FinishCapture(); } else { RDCERR("NULL deferred context in resource record!"); } } const uint32_t maxSize = 2048; RenderDoc::FramePixels fp; if(swapper != NULL) { ID3D11RenderTargetView *rtv = m_SwapChains[swapper]; ID3D11Resource *res = NULL; rtv->GetResource(&res); res->Release(); ID3D11Texture2D *tex = (ID3D11Texture2D *)res; D3D11_TEXTURE2D_DESC desc; tex->GetDesc(&desc); desc.BindFlags = 0; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.MiscFlags = 0; desc.Usage = D3D11_USAGE_STAGING; bool msaa = (desc.SampleDesc.Count > 1) || (desc.SampleDesc.Quality > 0); desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; ID3D11Texture2D *stagingTex = NULL; HRESULT hr = S_OK; hr = m_pDevice->CreateTexture2D(&desc, NULL, &stagingTex); if(FAILED(hr)) { RDCERR("Couldn't create staging texture to create thumbnail. HRESULT: %s", ToStr(hr).c_str()); } else { if(msaa) { desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = 0; desc.Usage = D3D11_USAGE_DEFAULT; ID3D11Texture2D *resolveTex = NULL; hr = m_pDevice->CreateTexture2D(&desc, NULL, &resolveTex); if(FAILED(hr)) { RDCERR("Couldn't create resolve texture to create thumbnail. HRESULT: %s", ToStr(hr).c_str()); tex = NULL; } else { m_pImmediateContext->GetReal()->ResolveSubresource(resolveTex, 0, tex, 0, desc.Format); m_pImmediateContext->GetReal()->CopyResource(stagingTex, resolveTex); resolveTex->Release(); } } else { m_pImmediateContext->GetReal()->CopyResource(stagingTex, tex); } if(tex) { ResourceFormat fmt = MakeResourceFormat(desc.Format); D3D11_MAPPED_SUBRESOURCE mapped; hr = m_pImmediateContext->GetReal()->Map(stagingTex, 0, D3D11_MAP_READ, 0, &mapped); if(FAILED(hr)) { RDCERR("Couldn't map staging texture to create thumbnail. HRESULT: %s", ToStr(hr).c_str()); } else { fp.len = (uint32_t)mapped.RowPitch * desc.Height; fp.data = new uint8_t[fp.len]; memcpy(fp.data, mapped.pData, fp.len); m_pImmediateContext->GetReal()->Unmap(stagingTex, 0); fp.width = (uint32_t)desc.Width; fp.height = (uint32_t)desc.Height; fp.pitch = mapped.RowPitch; fp.stride = fmt.compByteWidth * fmt.compCount; fp.bpc = fmt.compByteWidth; fp.bgra = fmt.BGRAOrder(); fp.max_width = maxSize; fp.pitch_requirement = 8; switch(fmt.type) { case ResourceFormatType::R10G10B10A2: fp.stride = 4; fp.buf1010102 = true; break; case ResourceFormatType::R5G6B5: fp.stride = 2; fp.buf565 = true; break; case ResourceFormatType::R5G5B5A1: fp.stride = 2; fp.buf5551 = true; break; default: break; } } } stagingTex->Release(); } } RDCFile *rdc = RenderDoc::Inst().CreateRDC(RDCDriver::D3D11, m_CapturedFrames.back().frameNumber, fp); StreamWriter *captureWriter = NULL; if(rdc) { SectionProperties props; // Compress with LZ4 so that it's fast props.flags = SectionFlags::LZ4Compressed; props.version = m_SectionVersion; props.type = SectionType::FrameCapture; captureWriter = rdc->WriteSection(props); } else { captureWriter = new StreamWriter(StreamWriter::InvalidStream); } { WriteSerialiser ser(captureWriter, Ownership::Stream); ser.SetChunkMetadataRecording(m_ScratchSerialiser.GetChunkMetadataRecording()); ser.SetUserData(GetResourceManager()); { // remember to update this estimated chunk length if you add more parameters SCOPED_SERIALISE_CHUNK(SystemChunk::DriverInit, sizeof(D3D11InitParams) + 16); SERIALISE_ELEMENT(m_InitParams); } { // remember to update this estimated chunk length if you add more parameters SCOPED_SERIALISE_CHUNK(D3D11Chunk::DeviceInitialisation, 16); SERIALISE_ELEMENT_LOCAL(ImmediateContext, m_pImmediateContext->GetResourceID()) .TypedAs("ID3D11DeviceContext *"_lit); } RDCDEBUG("Inserting Resource Serialisers"); LockForChunkFlushing(); GetResourceManager()->ApplyInitialContentsNonChunks(ser); GetResourceManager()->InsertReferencedChunks(ser); GetResourceManager()->InsertInitialContentsChunks(ser); RDCDEBUG("Creating Capture Scope"); GetResourceManager()->Serialise_InitialContentsNeeded(ser); { // remember to update this estimated chunk length if you add more parameters SCOPED_SERIALISE_CHUNK(SystemChunk::CaptureScope, 16); Serialise_CaptureScope(ser); } { RDCDEBUG("Getting Resource Record"); D3D11ResourceRecord *record = m_pImmediateContext->GetResourceRecord(); RDCDEBUG("Accumulating context resource list"); std::map recordlist; record->Insert(recordlist); RDCDEBUG("Flushing %u records to file serialiser", (uint32_t)recordlist.size()); float num = float(recordlist.size()); float idx = 0.0f; for(auto it = recordlist.begin(); it != recordlist.end(); ++it) { RenderDoc::Inst().SetProgress(CaptureProgress::SerialiseFrameContents, idx / num); idx += 1.0f; it->second->Write(ser); } RDCDEBUG("Done"); } UnlockForChunkFlushing(); } RenderDoc::Inst().FinishCaptureWriting(rdc, m_CapturedFrames.back().frameNumber); m_State = CaptureState::BackgroundCapturing; m_pImmediateContext->CleanupCapture(); GetResourceManager()->FreeCaptureData(); for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) context->CleanupCapture(); else RDCERR("NULL deferred context in resource record!"); } GetResourceManager()->MarkUnwrittenResources(); GetResourceManager()->ClearReferencedResources(); GetResourceManager()->FreeInitialContents(); return true; } else { const char *reasonString = "Unknown reason"; switch(reason) { case CaptureFailed_UncappedCmdlist: reasonString = "Uncapped command list"; break; case CaptureFailed_UncappedUnmap: reasonString = "Uncapped Map()/Unmap()"; break; default: break; } RDCLOG("Failed to capture, frame %u: %s", m_CapturedFrames.back().frameNumber, reasonString); m_Failures++; if((RenderDoc::Inst().GetOverlayBits() & eRENDERDOC_Overlay_Enabled) && swapper) { D3D11RenderState old = *m_pImmediateContext->GetCurrentPipelineState(); ID3D11RenderTargetView *rtv = m_SwapChains[swapper]; if(rtv) { m_pImmediateContext->GetReal()->OMSetRenderTargets(1, &rtv, NULL); m_TextRenderer->SetOutputDimensions(swapper->GetWidth(), swapper->GetHeight()); m_TextRenderer->SetOutputWindow(swapper->GetHWND()); m_TextRenderer->RenderText(0.0f, 0.0f, "Failed to capture frame %u: %s", m_CapturedFrames.back().frameNumber, reasonString); } old.ApplyState(m_pImmediateContext); } uint32_t failedFrame = m_CapturedFrames.back().frameNumber; m_CapturedFrames.back().frameNumber = m_AppControlledCapture ? ~0U : m_FrameCounter; m_pImmediateContext->CleanupCapture(); for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) context->CleanupCapture(); else RDCERR("NULL deferred context in resource record!"); } GetResourceManager()->ClearReferencedResources(); GetResourceManager()->FreeInitialContents(); // if it's a capture triggered from application code, immediately // give up as it's not reasonable to expect applications to detect and retry. // otherwise we can retry in case the next frame works. if(m_Failures > 5 || m_AppControlledCapture) { m_pImmediateContext->FinishCapture(); m_CapturedFrames.pop_back(); for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) { context->FinishCapture(); } else { RDCERR("NULL deferred context in resource record!"); } } GetResourceManager()->FreeCaptureData(); m_FailedFrame = failedFrame; m_FailedReason = reason; m_State = CaptureState::BackgroundCapturing; for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) context->CleanupCapture(); else RDCERR("NULL deferred context in resource record!"); } GetResourceManager()->MarkUnwrittenResources(); } else { GetResourceManager()->MarkResourceFrameReferenced(m_ResourceID, eFrameRef_PartialWrite); GetResourceManager()->PrepareInitialContents(); m_pImmediateContext->AttemptCapture(); m_pImmediateContext->BeginCaptureFrame(); for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) { context->AttemptCapture(); } else { RDCERR("NULL deferred context in resource record!"); } } } if(m_pInfoQueue) m_pInfoQueue->ClearStoredMessages(); return false; } } bool WrappedID3D11Device::DiscardFrameCapture(void *dev, void *wnd) { SCOPED_LOCK(m_D3DLock); if(!IsActiveCapturing(m_State)) return true; RenderDoc::Inst().FinishCaptureWriting(NULL, m_CapturedFrames.back().frameNumber); m_pImmediateContext->CleanupCapture(); for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) context->CleanupCapture(); else RDCERR("NULL deferred context in resource record!"); } GetResourceManager()->ClearReferencedResources(); GetResourceManager()->FreeInitialContents(); m_pImmediateContext->FinishCapture(); m_CapturedFrames.pop_back(); for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) { context->FinishCapture(); } else { RDCERR("NULL deferred context in resource record!"); } } GetResourceManager()->FreeCaptureData(); m_State = CaptureState::BackgroundCapturing; for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) { WrappedID3D11DeviceContext *context = *it; if(context) context->CleanupCapture(); else RDCERR("NULL deferred context in resource record!"); } GetResourceManager()->MarkUnwrittenResources(); if(m_pInfoQueue) m_pInfoQueue->ClearStoredMessages(); return true; } void WrappedID3D11Device::LockForChunkFlushing() { // wait for the value to be 0 (no-one messing with chunks right now) and set to -1 // to indicate that we're writing chunks and so no-one should try messing. for(;;) { int32_t val = Atomic::CmpExch32(&m_ChunkAtomic, 0, -1); // val was 0, so we replaced it, so we can stop if(val == 0) break; // we don't support recursive locking, so negative value is invalid if(val < 0) { RDCERR("Something went wrong! m_ChunkAtomic was %d before!", val); // try and recover by just setting to -1 anyway and hope for the best val = -1; break; } // spin while val is positive } } void WrappedID3D11Device::UnlockForChunkFlushing() { // set value back to 0 int32_t val = Atomic::CmpExch32(&m_ChunkAtomic, -1, 0); // should only come in here if we successfully grabbed the lock before. We don't // support multiple flushing locks. if(val != -1) { RDCERR("Something went wrong! m_ChunkAtomic was %d before, expected -1", val); // try and recover by just setting to 0 anyway and hope for the best val = 0; } } void WrappedID3D11Device::LockForChunkRemoval() { // wait for value to be non-negative (indicating that we're not using the chunks) // and then increment it. Spin until we have incremented it. for(;;) { int32_t prev = m_ChunkAtomic; // spin while val is negative if(prev < 0) continue; // try to increment the value int32_t val = Atomic::CmpExch32(&m_ChunkAtomic, prev, prev + 1); // val was prev. That means we incremented it so we can stop if(val == prev) break; } } void WrappedID3D11Device::UnlockForChunkRemoval() { // spin until we've decremented the value for(;;) { int32_t prev = m_ChunkAtomic; // val should always be positive because we locked it. Bail out if not if(prev <= 0) { RDCERR("Something went wrong! m_ChunkAtomic was %d before, expected positive", prev); // do nothing, hope it all goes OK break; } // try to decrement the value int32_t val = Atomic::CmpExch32(&m_ChunkAtomic, prev, prev - 1); // val was prev. That means we decremented it so we can stop if(val == prev) break; } } void WrappedID3D11Device::FirstFrame(IDXGISwapper *swapper) { // if we have to capture the first frame, begin capturing immediately if(IsBackgroundCapturing(m_State) && RenderDoc::Inst().ShouldTriggerCapture(0)) { RenderDoc::Inst().StartFrameCapture((ID3D11Device *)this, swapper->GetHWND()); m_AppControlledCapture = false; m_CapturedFrames.back().frameNumber = 0; } } HRESULT WrappedID3D11Device::Present(IDXGISwapper *swapper, UINT SyncInterval, UINT Flags) { if((Flags & DXGI_PRESENT_TEST) != 0) return S_OK; m_pCurrentWrappedDevice = this; if(IsBackgroundCapturing(m_State)) RenderDoc::Inst().Tick(); m_pImmediateContext->EndFrame(); m_FrameCounter++; // first present becomes frame #1, this function is at the end of the frame m_pImmediateContext->BeginFrame(); bool activeWindow = RenderDoc::Inst().IsActiveWindow((ID3D11Device *)this, swapper->GetHWND()); m_LastSwap = swapper; if(IsBackgroundCapturing(m_State)) { D3D11RenderState old = *m_pImmediateContext->GetCurrentPipelineState(); uint32_t overlay = RenderDoc::Inst().GetOverlayBits(); if(overlay & eRENDERDOC_Overlay_Enabled) { ID3D11RenderTargetView *rtv = m_SwapChains[swapper]; m_pImmediateContext->GetReal()->OMSetRenderTargets(1, &rtv, NULL); m_TextRenderer->SetOutputDimensions(swapper->GetWidth(), swapper->GetHeight()); m_TextRenderer->SetOutputWindow(swapper->GetHWND()); int flags = activeWindow ? RenderDoc::eOverlay_ActiveWindow : 0; std::string overlayText = RenderDoc::Inst().GetOverlayText(RDCDriver::D3D11, m_FrameCounter, flags); if(activeWindow && m_FailedFrame > 0) { const char *reasonString = "Unknown reason"; switch(m_FailedReason) { case CaptureFailed_UncappedCmdlist: reasonString = "Uncapped command list"; break; case CaptureFailed_UncappedUnmap: reasonString = "Uncapped Map()/Unmap()"; break; default: break; } overlayText += StringFormat::Fmt("Failed capture at frame %d:\n", m_FailedFrame); overlayText += StringFormat::Fmt(" %s\n", reasonString); } if(!overlayText.empty()) m_TextRenderer->RenderText(0.0f, 0.0f, overlayText.c_str()); old.ApplyState(m_pImmediateContext); } } RenderDoc::Inst().AddActiveDriver(RDCDriver::D3D11, true); // serialise the present call, even for inactive windows if(IsActiveCapturing(m_State)) m_pImmediateContext->Present(SyncInterval, Flags); if(!activeWindow) return S_OK; // kill any current capture that isn't application defined if(IsActiveCapturing(m_State) && !m_AppControlledCapture) { RenderDoc::Inst().EndFrameCapture((ID3D11Device *)this, swapper->GetHWND()); } if(IsBackgroundCapturing(m_State) && RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter)) { RenderDoc::Inst().StartFrameCapture((ID3D11Device *)this, swapper->GetHWND()); m_AppControlledCapture = false; m_CapturedFrames.back().frameNumber = m_FrameCounter; } return S_OK; } void WrappedID3D11Device::CachedObjectsGarbageCollect() { // 4000 is a fairly arbitrary number, chosen to make sure this garbage // collection kicks in as rarely as possible (4000 is a *lot* of unique // state objects to have), while still meaning that we'll never // accidentally cause a state object to fail to create because the app // expects only N to be alive but we're caching M more causing M+N>4096 if(m_CachedStateObjects.size() < 4000) return; // Now release all purely cached objects that have no external refcounts. // This will thrash if we have e.g. 2000 rasterizer state objects, all // referenced, and 2000 sampler state objects, all referenced. for(auto it = m_CachedStateObjects.begin(); it != m_CachedStateObjects.end();) { ID3D11DeviceChild *o = *it; o->AddRef(); if(o->Release() == 1) { auto eraseit = it; ++it; o->Release(); InternalRelease(); m_CachedStateObjects.erase(eraseit); } else { ++it; } } } void WrappedID3D11Device::AddDeferredContext(WrappedID3D11DeviceContext *defctx) { RDCASSERT(m_DeferredContexts.find(defctx) == m_DeferredContexts.end()); m_DeferredContexts.insert(defctx); } void WrappedID3D11Device::RemoveDeferredContext(WrappedID3D11DeviceContext *defctx) { RDCASSERT(m_DeferredContexts.find(defctx) != m_DeferredContexts.end()); m_DeferredContexts.erase(defctx); } void WrappedID3D11Device::AddResource(ResourceId id, ResourceType type, const char *defaultNamePrefix) { ResourceDescription &descr = GetReplay()->GetResourceDesc(id); uint64_t num; memcpy(&num, &id, sizeof(uint64_t)); descr.name = defaultNamePrefix + (" " + ToStr(num)); descr.autogeneratedName = true; descr.type = type; AddResourceCurChunk(descr); } void WrappedID3D11Device::AddResourceCurChunk(ResourceDescription &descr) { descr.initialisationChunks.push_back((uint32_t)m_StructuredFile->chunks.size() - 1); } void WrappedID3D11Device::AddResourceCurChunk(ResourceId id) { if(GetResourceManager()->HasLiveResource(id)) AddResourceCurChunk(GetReplay()->GetResourceDesc(id)); } void WrappedID3D11Device::DerivedResource(ID3D11DeviceChild *parent, ResourceId child) { ResourceId parentId = GetResourceManager()->GetOriginalID(GetIDForResource(parent)); GetReplay()->GetResourceDesc(parentId).derivedResources.push_back(child); GetReplay()->GetResourceDesc(child).parentResources.push_back(parentId); } template bool WrappedID3D11Device::Serialise_SetShaderDebugPath(SerialiserType &ser, ID3D11DeviceChild *pResource, const char *Path) { SERIALISE_ELEMENT(pResource); SERIALISE_ELEMENT(Path); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading() && pResource) { ResourceId resId = GetResourceManager()->GetOriginalID(GetIDForResource(pResource)); AddResourceCurChunk(resId); auto it = WrappedShader::m_ShaderList.find(GetIDForResource(pResource)); if(it != WrappedShader::m_ShaderList.end()) it->second->SetDebugInfoPath(Path); } return true; } HRESULT WrappedID3D11Device::SetShaderDebugPath(ID3D11DeviceChild *pResource, const char *Path) { if(IsCaptureMode(m_State)) { ResourceId idx = GetIDForResource(pResource); D3D11ResourceRecord *record = GetResourceManager()->GetResourceRecord(idx); if(record == NULL) { RDCERR("Setting shader debug path on object %p of type %d that has no resource record.", pResource, IdentifyTypeByPtr(pResource)); return E_INVALIDARG; } RDCASSERT(idx != ResourceId()); { WriteSerialiser &ser = m_ScratchSerialiser; SCOPED_SERIALISE_CHUNK(D3D11Chunk::SetShaderDebugPath); Serialise_SetShaderDebugPath(ser, pResource, Path); record->AddChunk(scope.Get()); } return S_OK; } return S_OK; } template bool WrappedID3D11Device::Serialise_SetResourceName(SerialiserType &ser, ID3D11DeviceChild *pResource, const char *Name) { SERIALISE_ELEMENT(pResource); SERIALISE_ELEMENT(Name); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading() && pResource) { ResourceDescription &descr = GetReplay()->GetResourceDesc( GetResourceManager()->GetOriginalID(GetIDForResource(pResource))); descr.SetCustomName(Name ? Name : ""); AddResourceCurChunk(descr); SetDebugName(pResource, Name); } return true; } void WrappedID3D11Device::SetResourceName(ID3D11DeviceChild *pResource, const char *Name) { // don't allow naming device contexts or command lists so we know this chunk // is always on a pre-capture chunk. if(IsCaptureMode(m_State) && !WrappedID3D11DeviceContext::IsAlloc(pResource) && !WrappedID3D11CommandList::IsAlloc(pResource)) { ResourceId idx = GetIDForResource(pResource); D3D11ResourceRecord *record = GetResourceManager()->GetResourceRecord(idx); if(record == NULL) record = m_DeviceRecord; RDCASSERT(idx != ResourceId()); SCOPED_LOCK(m_D3DLock); { WriteSerialiser &ser = m_ScratchSerialiser; SCOPED_SERIALISE_CHUNK(D3D11Chunk::SetResourceName); Serialise_SetResourceName(ser, pResource, Name); LockForChunkRemoval(); // don't serialise many SetResourceName chunks to the // object record, but we can't afford to drop any. record->LockChunks(); while(record->HasChunks()) { Chunk *end = record->GetLastChunk(); if(end->GetChunkType() == D3D11Chunk::SetResourceName) { SAFE_DELETE(end); record->PopChunk(); continue; } break; } record->UnlockChunks(); UnlockForChunkRemoval(); record->AddChunk(scope.Get()); } } } void WrappedID3D11Device::ReleaseResource(ID3D11DeviceChild *res) { ResourceId idx = GetIDForResource(res); // wrapped resources get released all the time, we don't want to // try and slerp in a resource release. Just the explicit ones if(!IsCaptureMode(m_State)) { if(GetResourceManager()->HasLiveResource(idx)) GetResourceManager()->EraseLiveResource(idx); return; } SCOPED_LOCK(m_D3DLock); if(WrappedID3D11DeviceContext::IsAlloc(res)) RemoveDeferredContext((WrappedID3D11DeviceContext *)res); D3D11ResourceRecord *record = GetResourceManager()->GetResourceRecord(idx); if(record) record->Delete(GetResourceManager()); } WrappedID3D11DeviceContext *WrappedID3D11Device::GetDeferredContext(size_t idx) { auto it = m_DeferredContexts.begin(); for(size_t i = 0; i < idx; i++) { ++it; if(it == m_DeferredContexts.end()) return NULL; } return *it; } const DrawcallDescription *WrappedID3D11Device::GetDrawcall(uint32_t eventId) { if(eventId >= m_Drawcalls.size()) return NULL; return m_Drawcalls[eventId]; }