Files
renderdoc/renderdoc/driver/d3d12/d3d12_resources.cpp
T
baldurk 3c61c01ba7 Don't allow buffers to be destroyed during D3D12 capture
* We need to make sure we can still look up GPU addresses for buffers that were
  destroyed during the frame, otherwise we might get the wrong contents for
  descriptors.
2020-02-03 18:18:17 +00:00

523 lines
15 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2020 Baldur Karlsson
*
* 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 "d3d12_resources.h"
#include "3rdparty/lz4/lz4.h"
#include "driver/shaders/dxbc/dxbc_reflect.h"
#include "d3d12_command_list.h"
#include "d3d12_command_queue.h"
GPUAddressRangeTracker WrappedID3D12Resource1::m_Addresses;
std::map<WrappedID3D12PipelineState::DXBCKey, WrappedID3D12Shader *> WrappedID3D12Shader::m_Shaders;
bool WrappedID3D12Shader::m_InternalResources = false;
const GUID RENDERDOC_ID3D12ShaderGUID_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
void WrappedID3D12Shader::TryReplaceOriginalByteCode()
{
if(!DXBC::DXBCContainer::CheckForDebugInfo((const void *)&m_Bytecode[0], m_Bytecode.size()))
{
rdcstr originalPath = m_DebugInfoPath;
if(originalPath.empty())
originalPath =
DXBC::DXBCContainer::GetDebugBinaryPath((const void *)&m_Bytecode[0], m_Bytecode.size());
if(!originalPath.empty())
{
bool lz4 = false;
if(!strncmp(originalPath.c_str(), "lz4#", 4))
{
originalPath = originalPath.substr(4);
lz4 = true;
}
// could support more if we're willing to compile in the decompressor
FILE *originalShaderFile = NULL;
size_t numSearchPaths = m_DebugInfoSearchPaths ? m_DebugInfoSearchPaths->size() : 0;
rdcstr foundPath;
// while we haven't found a file, keep trying through the search paths. For i==0
// check the path on its own, in case it's an absolute path.
for(size_t i = 0; originalShaderFile == NULL && i <= numSearchPaths; i++)
{
if(i == 0)
{
originalShaderFile = FileIO::fopen(originalPath.c_str(), "rb");
foundPath = originalPath;
continue;
}
else
{
const rdcstr &searchPath = (*m_DebugInfoSearchPaths)[i - 1];
foundPath = searchPath + "/" + originalPath;
originalShaderFile = FileIO::fopen(foundPath.c_str(), "rb");
}
}
if(originalShaderFile == NULL)
return;
FileIO::fseek64(originalShaderFile, 0L, SEEK_END);
uint64_t originalShaderSize = FileIO::ftell64(originalShaderFile);
FileIO::fseek64(originalShaderFile, 0, SEEK_SET);
if(lz4 || originalShaderSize >= m_Bytecode.size())
{
rdcarray<byte> originalBytecode;
originalBytecode.resize((size_t)originalShaderSize);
FileIO::fread(&originalBytecode[0], sizeof(byte), (size_t)originalShaderSize,
originalShaderFile);
if(lz4)
{
rdcarray<byte> decompressed;
// first try decompressing to 1MB flat
decompressed.resize(100 * 1024);
int ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
(int)originalBytecode.size(), (int)decompressed.size());
if(ret < 0)
{
// if it failed, either source is corrupt or we didn't allocate enough space.
// Just allocate 255x compressed size since it can't need any more than that.
decompressed.resize(255 * originalBytecode.size());
ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
(int)originalBytecode.size(), (int)decompressed.size());
if(ret < 0)
{
RDCERR("Failed to decompress LZ4 data from %s", foundPath.c_str());
return;
}
}
RDCASSERT(ret > 0, ret);
// we resize and memcpy instead of just doing .swap() because that would
// transfer over the over-large pessimistic capacity needed for decompression
originalBytecode.resize(ret);
memcpy(&originalBytecode[0], &decompressed[0], originalBytecode.size());
}
if(DXBC::DXBCContainer::CheckForDebugInfo((const void *)&originalBytecode[0],
originalBytecode.size()))
{
m_Bytecode.swap(originalBytecode);
}
}
FileIO::fclose(originalShaderFile);
}
}
}
#undef D3D12_TYPE_MACRO
#define D3D12_TYPE_MACRO(iface) WRAPPED_POOL_INST(CONCAT(Wrapped, iface));
ALL_D3D12_TYPES;
WRAPPED_POOL_INST(WrappedID3D12Shader);
D3D12ResourceType IdentifyTypeByPtr(ID3D12Object *ptr)
{
if(ptr == NULL)
return Resource_Unknown;
#undef D3D12_TYPE_MACRO
#define D3D12_TYPE_MACRO(iface) \
if(UnwrapHelper<iface>::IsAlloc(ptr)) \
return UnwrapHelper<iface>::GetTypeEnum();
ALL_D3D12_TYPES;
if(WrappedID3D12GraphicsCommandList::IsAlloc(ptr))
return Resource_GraphicsCommandList;
if(WrappedID3D12CommandQueue::IsAlloc(ptr))
return Resource_CommandQueue;
RDCERR("Unknown type for ptr 0x%p", ptr);
return Resource_Unknown;
}
TrackedResource12 *GetTracked(ID3D12Object *ptr)
{
if(ptr == NULL)
return NULL;
#undef D3D12_TYPE_MACRO
#define D3D12_TYPE_MACRO(iface) \
if(UnwrapHelper<iface>::IsAlloc(ptr)) \
return (TrackedResource12 *)GetWrapped((iface *)ptr);
ALL_D3D12_TYPES;
if(WrappedID3D12Shader::IsAlloc(ptr))
return (TrackedResource12 *)(WrappedID3D12Shader *)ptr;
return NULL;
}
template <>
ID3D12Object *Unwrap(ID3D12Object *ptr)
{
if(ptr == NULL)
return NULL;
#undef D3D12_TYPE_MACRO
#define D3D12_TYPE_MACRO(iface) \
if(UnwrapHelper<iface>::IsAlloc(ptr)) \
return (ID3D12Object *)GetWrapped((iface *)ptr)->GetReal();
ALL_D3D12_TYPES;
if(WrappedID3D12GraphicsCommandList::IsAlloc(ptr))
return (ID3D12Object *)(((WrappedID3D12GraphicsCommandList *)ptr)->GetReal());
if(WrappedID3D12CommandQueue::IsAlloc(ptr))
return (ID3D12Object *)(((WrappedID3D12CommandQueue *)ptr)->GetReal());
RDCERR("Unknown type of ptr 0x%p", ptr);
return NULL;
}
template <>
ResourceId GetResID(ID3D12Object *ptr)
{
if(ptr == NULL)
return ResourceId();
TrackedResource12 *res = GetTracked(ptr);
if(res == NULL)
{
if(WrappedID3D12GraphicsCommandList::IsAlloc(ptr))
return ((WrappedID3D12GraphicsCommandList *)ptr)->GetResourceID();
if(WrappedID3D12CommandQueue::IsAlloc(ptr))
return ((WrappedID3D12CommandQueue *)ptr)->GetResourceID();
RDCERR("Unknown type of ptr 0x%p", ptr);
return ResourceId();
}
return res->GetResourceID();
}
template <>
D3D12ResourceRecord *GetRecord(ID3D12Object *ptr)
{
if(ptr == NULL)
return NULL;
TrackedResource12 *res = GetTracked(ptr);
if(res == NULL)
{
if(WrappedID3D12GraphicsCommandList::IsAlloc(ptr))
return ((WrappedID3D12GraphicsCommandList *)ptr)->GetResourceRecord();
if(WrappedID3D12CommandQueue::IsAlloc(ptr))
return ((WrappedID3D12CommandQueue *)ptr)->GetResourceRecord();
RDCERR("Unknown type of ptr 0x%p", ptr);
return NULL;
}
return res->GetResourceRecord();
}
template <>
ResourceId GetResID(ID3D12DeviceChild *ptr)
{
return GetResID((ID3D12Object *)ptr);
}
template <>
ResourceId GetResID(ID3D12Pageable *ptr)
{
return GetResID((ID3D12Object *)ptr);
}
template <>
D3D12ResourceRecord *GetRecord(ID3D12DeviceChild *ptr)
{
return GetRecord((ID3D12Object *)ptr);
}
template <>
ID3D12DeviceChild *Unwrap(ID3D12DeviceChild *ptr)
{
return (ID3D12DeviceChild *)Unwrap((ID3D12Object *)ptr);
}
WrappedID3D12Resource1::~WrappedID3D12Resource1()
{
SAFE_RELEASE(m_pReal1);
// perform an implicit unmap on release
if(GetResourceRecord())
{
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
size_t mapcount = GetResourceRecord()->m_MapsCount;
// may not have a map if e.g. no pointer was requested
for(size_t i = 0; i < mapcount; i++)
{
if(map[i].refcount > 0)
{
m_pDevice->Unmap(this, (UINT)i, map[i].realPtr, NULL);
FreeAlignedBuffer(map[i].shadowPtr);
map[i].realPtr = NULL;
map[i].shadowPtr = NULL;
}
}
}
if(IsReplayMode(m_pDevice->GetState()))
m_pDevice->GetResourceList().erase(GetResourceID());
// assuming only valid for buffers
if(m_pReal->GetDesc().Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
GPUAddressRange range;
range.start = m_pReal->GetGPUVirtualAddress();
range.end = m_pReal->GetGPUVirtualAddress() + m_pReal->GetDesc().Width;
range.id = GetResourceID();
m_Addresses.RemoveFrom(range);
}
Shutdown();
m_ID = ResourceId();
}
byte *WrappedID3D12Resource1::GetMap(UINT Subresource)
{
SCOPED_LOCK(GetResourceRecord()->m_MapLock);
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
size_t mapcount = GetResourceRecord()->m_MapsCount;
if(Subresource < mapcount)
return map[Subresource].realPtr;
return NULL;
}
byte *WrappedID3D12Resource1::GetShadow(UINT Subresource)
{
SCOPED_LOCK(GetResourceRecord()->m_MapLock);
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
return map[Subresource].shadowPtr;
}
void WrappedID3D12Resource1::AllocShadow(UINT Subresource, size_t size)
{
SCOPED_LOCK(GetResourceRecord()->m_MapLock);
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
if(map[Subresource].shadowPtr == NULL)
map[Subresource].shadowPtr = AllocAlignedBuffer(size);
}
void WrappedID3D12Resource1::FreeShadow()
{
SCOPED_LOCK(GetResourceRecord()->m_MapLock);
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
size_t mapcount = GetResourceRecord()->m_MapsCount;
for(size_t i = 0; i < mapcount; i++)
{
FreeAlignedBuffer(map[i].shadowPtr);
map[i].shadowPtr = NULL;
}
}
WriteSerialiser &WrappedID3D12Resource1::GetThreadSerialiser()
{
return m_pDevice->GetThreadSerialiser();
}
HRESULT STDMETHODCALLTYPE WrappedID3D12Resource1::Map(UINT Subresource,
const D3D12_RANGE *pReadRange, void **ppData)
{
// don't care about maps without returned pointers - we'll just intercept the WriteToSubresource
// calls
if(ppData == NULL)
return m_pReal->Map(Subresource, pReadRange, ppData);
void *mapPtr = NULL;
// pass a NULL range as we might want to read from the whole range
HRESULT hr = m_pReal->Map(Subresource, NULL, &mapPtr);
*ppData = mapPtr;
if(SUCCEEDED(hr) && GetResourceRecord())
{
SCOPED_LOCK(GetResourceRecord()->m_MapLock);
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
map[Subresource].realPtr = (byte *)mapPtr;
map[Subresource].refcount++;
// on the first map, register this so we can flush any updates in case it's left persistant
if(map[Subresource].refcount == 1)
m_pDevice->Map(this, Subresource);
}
return hr;
}
void STDMETHODCALLTYPE WrappedID3D12Resource1::Unmap(UINT Subresource,
const D3D12_RANGE *pWrittenRange)
{
if(GetResourceRecord())
{
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
{
SCOPED_LOCK(GetResourceRecord()->m_MapLock);
// may not have a ref at all if e.g. no pointer was requested
if(map[Subresource].refcount >= 1)
{
map[Subresource].refcount--;
if(map[Subresource].refcount == 0)
{
m_pDevice->Unmap(this, Subresource, map[Subresource].realPtr, pWrittenRange);
FreeAlignedBuffer(map[Subresource].shadowPtr);
map[Subresource].realPtr = NULL;
map[Subresource].shadowPtr = NULL;
}
}
}
}
return m_pReal->Unmap(Subresource, pWrittenRange);
}
HRESULT STDMETHODCALLTYPE WrappedID3D12Resource1::WriteToSubresource(UINT DstSubresource,
const D3D12_BOX *pDstBox,
const void *pSrcData,
UINT SrcRowPitch,
UINT SrcDepthPitch)
{
HRESULT ret;
SERIALISE_TIME_CALL(ret = m_pReal->WriteToSubresource(DstSubresource, pDstBox, pSrcData,
SrcRowPitch, SrcDepthPitch));
if(GetResourceRecord())
{
m_pDevice->WriteToSubresource(this, DstSubresource, pDstBox, pSrcData, SrcRowPitch,
SrcDepthPitch);
}
return ret;
}
void WrappedID3D12Resource1::RefBuffers(D3D12ResourceManager *rm)
{
// only buffers go into m_Addresses
SCOPED_READLOCK(m_Addresses.addressLock);
for(size_t i = 0; i < m_Addresses.addresses.size(); i++)
rm->MarkResourceFrameReferenced(m_Addresses.addresses[i].id, eFrameRef_Read);
}
void WrappedID3D12Resource1::AddRefBuffersBeforeCapture(D3D12ResourceManager *rm)
{
SCOPED_READLOCK(m_Addresses.addressLock);
for(size_t i = 0; i < m_Addresses.addresses.size(); i++)
rm->GetCurrentResource(m_Addresses.addresses[i].id)->AddRef();
}
void WrappedID3D12Resource1::ReleaseBuffersAfterCapture(D3D12ResourceManager *rm)
{
// make a copy because we might release the last reference on a buffer which will need to modify
// the actual m_Addresses
rdcarray<GPUAddressRange> addresses;
{
SCOPED_READLOCK(m_Addresses.addressLock);
addresses = m_Addresses.addresses;
}
for(size_t i = 0; i < addresses.size(); i++)
rm->GetCurrentResource(addresses[i].id)->Release();
}
WrappedID3D12DescriptorHeap::WrappedID3D12DescriptorHeap(ID3D12DescriptorHeap *real,
WrappedID3D12Device *device,
const D3D12_DESCRIPTOR_HEAP_DESC &desc)
: WrappedDeviceChild12(real, device)
{
realCPUBase = real->GetCPUDescriptorHandleForHeapStart();
realGPUBase = real->GetGPUDescriptorHandleForHeapStart();
SetResident(true);
increment = device->GetUnwrappedDescriptorIncrement(desc.Type);
numDescriptors = desc.NumDescriptors;
descriptors = new D3D12Descriptor[numDescriptors];
RDCEraseMem(descriptors, sizeof(D3D12Descriptor) * numDescriptors);
for(UINT i = 0; i < numDescriptors; i++)
descriptors[i].Setup(this, i);
}
WrappedID3D12DescriptorHeap::~WrappedID3D12DescriptorHeap()
{
Shutdown();
SAFE_DELETE_ARRAY(descriptors);
}
void WrappedID3D12PipelineState::ShaderEntry::BuildReflection()
{
RDCCOMPILE_ASSERT(
D3Dx_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT == D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT,
"Mismatched vertex input count");
MakeShaderReflection(m_DXBCFile, &m_Details, &m_Mapping);
m_Details.resourceId = GetResourceID();
}