mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-06 06:41:05 +00:00
* 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.
523 lines
15 KiB
C++
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();
|
|
}
|