Files
renderdoc/renderdoc/driver/d3d12/d3d12_resources.cpp
T
2024-06-06 14:55:24 +01:00

1510 lines
50 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2024 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 "driver/shaders/dxbc/dxbc_reflect.h"
#include "d3d12_command_list.h"
#include "d3d12_command_queue.h"
GPUAddressRangeTracker WrappedID3D12Resource::m_Addresses;
std::map<WrappedID3D12PipelineState::DXBCKey, WrappedID3D12Shader *> WrappedID3D12Shader::m_Shaders;
bool WrappedID3D12Shader::m_InternalResources = false;
int32_t WrappedID3D12CommandAllocator::m_ResetEnabled = 1;
const GUID RENDERDOC_ID3D12ShaderGUID_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
#undef D3D12_TYPE_MACRO
#define D3D12_TYPE_MACRO(iface) WRAPPED_POOL_INST(CONCAT(Wrapped, iface));
ALL_D3D12_TYPES;
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;
if(D3D12AccelerationStructure::IsAlloc(ptr))
return Resource_AccelerationStructure;
RDCERR("Unknown type for ptr 0x%p", ptr);
return Resource_Unknown;
}
TrackedResource12 *GetTracked(ID3D12Object *ptr)
{
if(ptr == NULL)
return NULL;
return (TrackedResource12 *)(WrappedDeviceChild12<ID3D12DeviceChild> *)ptr;
}
template <>
ID3D12Object *Unwrap(ID3D12Object *ptr)
{
if(ptr == NULL)
return NULL;
if(WrappedID3D12GraphicsCommandList::IsAlloc(ptr))
return (ID3D12Object *)(((WrappedID3D12GraphicsCommandList *)ptr)->GetReal());
if(WrappedID3D12CommandQueue::IsAlloc(ptr))
return (ID3D12Object *)(((WrappedID3D12CommandQueue *)ptr)->GetReal());
return ((WrappedDeviceChild12<ID3D12DeviceChild> *)ptr)->GetReal();
}
template <>
ResourceId GetResID(ID3D12Object *ptr)
{
if(ptr == NULL)
return ResourceId();
if(WrappedID3D12GraphicsCommandList::IsAlloc(ptr))
return ((WrappedID3D12GraphicsCommandList *)ptr)->GetResourceID();
if(WrappedID3D12CommandQueue::IsAlloc(ptr))
return ((WrappedID3D12CommandQueue *)ptr)->GetResourceID();
return GetTracked(ptr)->GetResourceID();
}
template <>
D3D12ResourceRecord *GetRecord(ID3D12Object *ptr)
{
if(ptr == NULL)
return NULL;
if(WrappedID3D12GraphicsCommandList::IsAlloc(ptr))
return ((WrappedID3D12GraphicsCommandList *)ptr)->GetResourceRecord();
if(WrappedID3D12CommandQueue::IsAlloc(ptr))
return ((WrappedID3D12CommandQueue *)ptr)->GetResourceRecord();
return GetTracked(ptr)->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);
}
WRAPPED_POOL_INST(D3D12AccelerationStructure);
D3D12AccelerationStructure::D3D12AccelerationStructure(WrappedID3D12Device *wrappedDevice,
WrappedID3D12Resource *bufferRes,
D3D12BufferOffset bufferOffset,
UINT64 byteSize)
: WrappedDeviceChild12(NULL, wrappedDevice),
m_asbWrappedResource(bufferRes),
m_asbWrappedResourceBufferOffset(bufferOffset),
byteSize(byteSize)
{
}
D3D12AccelerationStructure::~D3D12AccelerationStructure()
{
Shutdown();
}
bool WrappedID3D12Resource::CreateAccStruct(D3D12BufferOffset bufferOffset, UINT64 byteSize,
D3D12AccelerationStructure **accStruct)
{
SCOPED_LOCK(m_accStructResourcesCS);
if(m_accelerationStructMap.find(bufferOffset) == m_accelerationStructMap.end())
{
m_accelerationStructMap[bufferOffset] =
new D3D12AccelerationStructure(m_pDevice, this, bufferOffset, byteSize);
if(accStruct)
{
*accStruct = m_accelerationStructMap[bufferOffset];
if(IsCaptureMode(m_pDevice->GetState()))
{
DeleteOverlappingAccStructsInRangeAtOffset(bufferOffset);
}
}
return true;
}
return false;
}
WrappedID3D12Resource::~WrappedID3D12Resource()
{
SAFE_RELEASE(m_Heap);
// 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;
}
}
}
// release all ASs during capture. During replay these will be destroyed themselves
if(IsCaptureMode(m_pDevice->GetState()))
{
for(auto it = m_accelerationStructMap.begin(); it != m_accelerationStructMap.end(); ++it)
SAFE_RELEASE(it->second);
}
if(IsReplayMode(m_pDevice->GetState()))
m_pDevice->RemoveReplayResource(GetResourceID());
// assuming only valid for buffers
if(m_pReal->GetDesc().Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
GPUAddressRange range;
range.start = m_pReal->GetGPUVirtualAddress();
// realEnd and oobEnd are not used for removing, just start + id
range.id = GetResourceID();
m_Addresses.RemoveFrom(range);
}
Shutdown();
m_ID = ResourceId();
}
byte *WrappedID3D12Resource::GetMap(UINT Subresource)
{
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
size_t mapcount = GetResourceRecord()->m_MapsCount;
if(Subresource < mapcount)
return map[Subresource].realPtr;
return NULL;
}
byte *WrappedID3D12Resource::GetShadow(UINT Subresource)
{
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
return map[Subresource].shadowPtr;
}
void WrappedID3D12Resource::AllocShadow(UINT Subresource, size_t size)
{
D3D12ResourceRecord::MapData *map = GetResourceRecord()->m_Maps;
if(map[Subresource].shadowPtr == NULL)
map[Subresource].shadowPtr = AllocAlignedBuffer(size);
}
void WrappedID3D12Resource::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;
}
}
void WrappedID3D12Resource::LockMaps()
{
GetResourceRecord()->m_MapLock.Lock();
}
void WrappedID3D12Resource::UnlockMaps()
{
GetResourceRecord()->m_MapLock.Unlock();
}
WriteSerialiser &WrappedID3D12Resource::GetThreadSerialiser()
{
return m_pDevice->GetThreadSerialiser();
}
size_t WrappedID3D12Resource::DeleteOverlappingAccStructsInRangeAtOffset(D3D12BufferOffset bufferOffset)
{
SCOPED_LOCK(m_accStructResourcesCS);
if(!m_accelerationStructMap.empty())
{
rdcarray<D3D12BufferOffset> toBeDeleted;
uint64_t accStructAtOffsetSize = m_accelerationStructMap[bufferOffset]->Size();
for(const rdcpair<D3D12BufferOffset, D3D12AccelerationStructure *> &accStructAtOffset :
m_accelerationStructMap)
{
if(accStructAtOffset.first == bufferOffset)
{
continue;
}
if(accStructAtOffset.first < bufferOffset &&
(accStructAtOffset.first + accStructAtOffset.second->Size()) > bufferOffset)
{
toBeDeleted.push_back(accStructAtOffset.first);
}
if(accStructAtOffset.first > bufferOffset &&
(bufferOffset + accStructAtOffsetSize) > accStructAtOffset.first)
{
toBeDeleted.push_back(accStructAtOffset.first);
}
}
for(D3D12BufferOffset deleting : toBeDeleted)
{
DeleteAccStructAtOffset(deleting);
}
return toBeDeleted.size();
}
return 0;
}
HRESULT STDMETHODCALLTYPE WrappedID3D12Resource::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 WrappedID3D12Resource::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 WrappedID3D12Resource::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;
}
bool WrappedID3D12Resource::GetAccStructIfExist(D3D12BufferOffset bufferOffset,
D3D12AccelerationStructure **accStruct)
{
SCOPED_LOCK(m_accStructResourcesCS);
bool found = false;
if(m_accelerationStructMap.find(bufferOffset) != m_accelerationStructMap.end())
{
found = true;
if(accStruct)
{
*accStruct = m_accelerationStructMap[bufferOffset];
}
}
return found;
}
bool WrappedID3D12Resource::DeleteAccStructAtOffset(D3D12BufferOffset bufferOffset)
{
SCOPED_LOCK(m_accStructResourcesCS);
D3D12AccelerationStructure *accStruct = NULL;
if(GetAccStructIfExist(bufferOffset, &accStruct))
{
if(m_accelerationStructMap[bufferOffset]->Release() == 0)
{
m_accelerationStructMap.erase(bufferOffset);
}
return true;
}
return false;
}
void WrappedID3D12Resource::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 WrappedID3D12Resource::GetMappableIDs(D3D12ResourceManager *rm,
const std::unordered_set<ResourceId> &refdIDs,
std::unordered_set<ResourceId> &mappableIDs)
{
SCOPED_READLOCK(m_Addresses.addressLock);
for(size_t i = 0; i < m_Addresses.addresses.size(); i++)
{
if(refdIDs.find(m_Addresses.addresses[i].id) != refdIDs.end())
{
WrappedID3D12Resource *resource =
(WrappedID3D12Resource *)rm->GetCurrentResource(m_Addresses.addresses[i].id);
mappableIDs.insert(resource->GetMappableID());
}
}
}
rdcarray<ID3D12Resource *> WrappedID3D12Resource::AddRefBuffersBeforeCapture(D3D12ResourceManager *rm)
{
rdcarray<ID3D12Resource *> ret;
rdcarray<GPUAddressRange> addresses;
{
SCOPED_READLOCK(m_Addresses.addressLock);
addresses = m_Addresses.addresses;
}
for(size_t i = 0; i < addresses.size(); i++)
{
ID3D12Resource *resource = (ID3D12Resource *)rm->GetCurrentResource(addresses[i].id);
if(resource)
{
resource->AddRef();
ret.push_back(resource);
}
}
return ret;
}
void WrappedID3D12DescriptorHeap::MarkMutableIndex(uint32_t index)
{
if(!mutableDescriptorBitmask)
return;
mutableDescriptorBitmask[index / 64] |= (1ULL << (index % 64));
}
bool WrappedID3D12DescriptorHeap::HasValidDescriptorCache(uint32_t index)
{
if(!mutableDescriptorBitmask)
return false;
// don't cache mutable views. In theory we could but we'd need to know which ones were modified
// mid-frame, to mark the cache as stale when initial contents are re-applied. This optimisation
// is aimed at the assumption of a huge number of descriptors that don't change so we just don't
// cache ones that change mid-frame
if((mutableDescriptorBitmask[index / 64] & (1ULL << (index % 64))) != 0)
return false;
EnsureDescriptorCache();
// anything that's not mutable is valid once it's been set at least once. Since we
// zero-initialise, we use bind as a flag (it isn't retrieved from the cache since it depends on
// the binding)
return cachedDescriptors[index].type != DescriptorType::Unknown;
}
void WrappedID3D12DescriptorHeap::GetFromDescriptorCache(uint32_t index, Descriptor &view)
{
if(!mutableDescriptorBitmask)
return;
EnsureDescriptorCache();
view = cachedDescriptors[index];
}
void WrappedID3D12DescriptorHeap::EnsureDescriptorCache()
{
if(!cachedDescriptors)
{
D3D12_DESCRIPTOR_HEAP_DESC desc = GetDesc();
cachedDescriptors = new Descriptor[desc.NumDescriptors];
RDCEraseMem(cachedDescriptors, sizeof(Descriptor) * desc.NumDescriptors);
}
}
void WrappedID3D12DescriptorHeap::SetToDescriptorCache(uint32_t index, const Descriptor &view)
{
if(!mutableDescriptorBitmask)
return;
EnsureDescriptorCache();
cachedDescriptors[index] = view;
}
WrappedID3D12DescriptorHeap::WrappedID3D12DescriptorHeap(ID3D12DescriptorHeap *real,
WrappedID3D12Device *device,
const D3D12_DESCRIPTOR_HEAP_DESC &desc,
UINT UnpatchedNumDescriptors)
: WrappedDeviceChild12(real, device)
{
realCPUBase = real->GetCPUDescriptorHandleForHeapStart();
if(desc.Flags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
realGPUBase = real->GetGPUDescriptorHandleForHeapStart();
increment = device->GetUnwrappedDescriptorIncrement(desc.Type);
numDescriptors = UnpatchedNumDescriptors;
descriptors = new D3D12Descriptor[desc.NumDescriptors];
m_OriginalWrappedGPUBase = (uint64_t)descriptors;
RDCEraseMem(descriptors, sizeof(D3D12Descriptor) * desc.NumDescriptors);
for(UINT i = 0; i < desc.NumDescriptors; i++)
descriptors[i].Setup(this, i);
// only cache views for "large" descriptor heaps where we expect few will actually change
// mid-frame
if(IsReplayMode(device->GetState()) && desc.NumDescriptors > 1024)
{
size_t bitmaskSize = AlignUp(desc.NumDescriptors, 64U) / 64;
cachedDescriptors = NULL;
mutableDescriptorBitmask = new uint64_t[bitmaskSize];
RDCEraseMem(mutableDescriptorBitmask, sizeof(uint64_t) * bitmaskSize);
}
else
{
cachedDescriptors = NULL;
mutableDescriptorBitmask = NULL;
}
}
WrappedID3D12DescriptorHeap::~WrappedID3D12DescriptorHeap()
{
Shutdown();
SAFE_DELETE_ARRAY(descriptors);
SAFE_DELETE_ARRAY(cachedDescriptors);
SAFE_DELETE_ARRAY(mutableDescriptorBitmask);
}
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_Details->resourceId = GetResourceID();
}
rdcpair<uint32_t, uint32_t> FindMatchingRootParameter(const D3D12RootSignature *sig,
D3D12_SHADER_VISIBILITY visibility,
D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
uint32_t space, uint32_t bind)
{
// search the root signature to find the matching entry and figure out the offset from the root binding
for(uint32_t root = 0; root < sig->Parameters.size(); root++)
{
const D3D12RootSignatureParameter &param = sig->Parameters[root];
if(param.ShaderVisibility != visibility && param.ShaderVisibility != D3D12_SHADER_VISIBILITY_ALL)
continue;
// identify root parameters
if((
// root constants
(param.ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS &&
rangeType == D3D12_DESCRIPTOR_RANGE_TYPE_CBV) ||
// root CBV
(param.ParameterType == D3D12_ROOT_PARAMETER_TYPE_CBV &&
rangeType == D3D12_DESCRIPTOR_RANGE_TYPE_CBV) ||
// root SRV
(param.ParameterType == D3D12_ROOT_PARAMETER_TYPE_SRV &&
rangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SRV) ||
// root UAV
(param.ParameterType == D3D12_ROOT_PARAMETER_TYPE_UAV &&
rangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV)) &&
// and matching space/binding
param.Descriptor.RegisterSpace == space && param.Descriptor.ShaderRegister == bind)
{
// offset is unused since it's just the root parameter, so we indicate that with the offset
return {root, ~0U};
}
uint32_t descOffset = 0;
for(const D3D12_DESCRIPTOR_RANGE1 &range : param.ranges)
{
uint32_t rangeOffset = range.OffsetInDescriptorsFromTableStart;
if(range.OffsetInDescriptorsFromTableStart == D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
rangeOffset = descOffset;
if(range.RangeType == rangeType && range.RegisterSpace == space &&
range.BaseShaderRegister <= bind &&
(range.NumDescriptors == ~0U || bind < range.BaseShaderRegister + range.NumDescriptors))
{
return {root, rangeOffset + (bind - range.BaseShaderRegister)};
}
descOffset = rangeOffset + range.NumDescriptors;
}
}
// if not found above, and looking for samplers, look at static samplers next
if(rangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER)
{
// indicate that we're looking up static samplers
uint32_t numRoots = (uint32_t)sig->Parameters.size();
for(uint32_t samp = 0; samp < sig->StaticSamplers.size(); samp++)
{
if(sig->StaticSamplers[samp].RegisterSpace == space &&
sig->StaticSamplers[samp].ShaderRegister == bind)
{
return {numRoots, samp};
}
}
}
return {~0U, 0};
}
void WrappedID3D12PipelineState::ProcessDescriptorAccess()
{
if(m_AccessProcessed)
return;
m_AccessProcessed = true;
const D3D12RootSignature *sig = NULL;
if(graphics)
sig = &((WrappedID3D12RootSignature *)graphics->pRootSignature)->sig;
else if(compute)
sig = &((WrappedID3D12RootSignature *)compute->pRootSignature)->sig;
for(ShaderEntry *shad : {VS(), HS(), DS(), GS(), PS(), AS(), MS(), CS()})
{
if(!shad)
continue;
const ShaderReflection &refl = shad->GetDetails();
D3D12_SHADER_VISIBILITY visibility;
switch(refl.stage)
{
case ShaderStage::Vertex: visibility = D3D12_SHADER_VISIBILITY_VERTEX; break;
case ShaderStage::Hull: visibility = D3D12_SHADER_VISIBILITY_HULL; break;
case ShaderStage::Domain: visibility = D3D12_SHADER_VISIBILITY_DOMAIN; break;
case ShaderStage::Geometry: visibility = D3D12_SHADER_VISIBILITY_GEOMETRY; break;
case ShaderStage::Pixel: visibility = D3D12_SHADER_VISIBILITY_PIXEL; break;
case ShaderStage::Amplification: visibility = D3D12_SHADER_VISIBILITY_AMPLIFICATION; break;
case ShaderStage::Mesh: visibility = D3D12_SHADER_VISIBILITY_MESH; break;
default: visibility = D3D12_SHADER_VISIBILITY_ALL; break;
}
DescriptorAccess access;
access.stage = refl.stage;
// we will store the root signature element in byteSize to be decoded into descriptorStore later
access.byteSize = 0;
staticDescriptorAccess.reserve(staticDescriptorAccess.size() + refl.constantBlocks.size() +
refl.samplers.size() + refl.readOnlyResources.size() +
refl.readWriteResources.size());
RDCASSERT(refl.constantBlocks.size() < 0xffff, refl.constantBlocks.size());
for(uint16_t i = 0; i < refl.constantBlocks.size(); i++)
{
const ConstantBlock &bind = refl.constantBlocks[i];
// arrayed descriptors will be handled with bindless feedback
if(bind.bindArraySize > 1)
continue;
access.type = DescriptorType::ConstantBuffer;
access.index = i;
rdctie(access.byteSize, access.byteOffset) =
FindMatchingRootParameter(sig, visibility, D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
bind.fixedBindSetOrSpace, bind.fixedBindNumber);
if(access.byteSize != ~0U)
staticDescriptorAccess.push_back(access);
}
RDCASSERT(refl.samplers.size() < 0xffff, refl.samplers.size());
for(uint16_t i = 0; i < refl.samplers.size(); i++)
{
const ShaderSampler &bind = refl.samplers[i];
// arrayed descriptors will be handled with bindless feedback
if(bind.bindArraySize > 1)
continue;
access.type = DescriptorType::Sampler;
access.index = i;
rdctie(access.byteSize, access.byteOffset) =
FindMatchingRootParameter(sig, visibility, D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER,
bind.fixedBindSetOrSpace, bind.fixedBindNumber);
if(access.byteSize != ~0U)
staticDescriptorAccess.push_back(access);
}
RDCASSERT(refl.readOnlyResources.size() < 0xffff, refl.readOnlyResources.size());
for(uint16_t i = 0; i < refl.readOnlyResources.size(); i++)
{
const ShaderResource &bind = refl.readOnlyResources[i];
// arrayed descriptors will be handled with bindless feedback
if(bind.bindArraySize > 1)
continue;
access.type = refl.readOnlyResources[i].descriptorType;
access.index = i;
rdctie(access.byteSize, access.byteOffset) =
FindMatchingRootParameter(sig, visibility, D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
bind.fixedBindSetOrSpace, bind.fixedBindNumber);
if(access.byteSize != ~0U)
staticDescriptorAccess.push_back(access);
}
RDCASSERT(refl.readWriteResources.size() < 0xffff, refl.readWriteResources.size());
for(uint16_t i = 0; i < refl.readWriteResources.size(); i++)
{
const ShaderResource &bind = refl.readWriteResources[i];
// arrayed descriptors will be handled with bindless feedback
if(bind.bindArraySize > 1)
continue;
access.type = refl.readWriteResources[i].descriptorType;
access.index = i;
rdctie(access.byteSize, access.byteOffset) =
FindMatchingRootParameter(sig, visibility, D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
bind.fixedBindSetOrSpace, bind.fixedBindNumber);
if(access.byteSize != ~0U)
staticDescriptorAccess.push_back(access);
}
}
}
D3D12ShaderExportDatabase::D3D12ShaderExportDatabase(ResourceId id,
D3D12RaytracingResourceAndUtilHandler *rayManager,
ID3D12StateObjectProperties *obj)
: RefCounter12(NULL), objectOriginalId(id), m_RayManager(rayManager), m_StateObjectProps(obj)
{
m_RayManager->RegisterExportDatabase(this);
}
D3D12ShaderExportDatabase::~D3D12ShaderExportDatabase()
{
for(D3D12ShaderExportDatabase *parent : parents)
{
SAFE_RELEASE(parent);
}
m_RayManager->UnregisterExportDatabase(this);
}
void D3D12ShaderExportDatabase::PopulateDatabase(size_t NumSubobjects,
const D3D12_STATE_SUBOBJECT *subobjects)
{
// store the default local root signature - if we only find one in the whole state object then it becomes default
ID3D12RootSignature *defaultRoot = NULL;
bool unassocDefaultValid = false;
bool explicitDefault = false;
bool unassocDXILDefaultValid = false;
uint32_t dxilDefaultRoot = ~0U;
rdcarray<rdcpair<rdcstr, uint32_t>> explicitRootSigAssocs;
rdcarray<rdcstr> explicitDefaultDxilAssocs;
rdcarray<rdcpair<rdcstr, rdcstr>> explicitDxilAssocs;
rdcflatmap<rdcstr, uint32_t> dxilLocalRootSigs;
rdcarray<rdcpair<rdcstr, uint32_t>> inheritedRootSigAssocs;
rdcarray<rdcpair<rdcstr, rdcstr>> inheritedDXILRootSigAssocs;
rdcflatmap<rdcstr, uint32_t> inheritedDXILLocalRootSigs;
// fill shader exports list as well as local root signature lookups.
// shader exports that can be queried come from two sources:
// - hit groups
// - exports from a DXIL library
// - exports from a collection
for(size_t i = 0; i < NumSubobjects; i++)
{
if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP)
{
D3D12_HIT_GROUP_DESC *desc = (D3D12_HIT_GROUP_DESC *)subobjects[i].pDesc;
AddExport(StringFormat::Wide2UTF8(desc->HitGroupExport));
rdcarray<rdcstr> shaders;
if(desc->IntersectionShaderImport)
shaders.push_back(StringFormat::Wide2UTF8(desc->IntersectionShaderImport));
if(desc->AnyHitShaderImport)
shaders.push_back(StringFormat::Wide2UTF8(desc->AnyHitShaderImport));
if(desc->ClosestHitShaderImport)
shaders.push_back(StringFormat::Wide2UTF8(desc->ClosestHitShaderImport));
// register the hit group so that if we get associations with the individual shaders we
// can apply that up to the hit group
AddLastHitGroupShaders(std::move(shaders));
}
else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY)
{
D3D12_DXIL_LIBRARY_DESC *dxil = (D3D12_DXIL_LIBRARY_DESC *)subobjects[i].pDesc;
if(dxil->NumExports > 0)
{
for(UINT e = 0; e < dxil->NumExports; e++)
{
// Name is always the name used for exports - if renaming then the renamed-from name
// is only used to lookup in the dxil library and not for any associations-by-name
AddExport(StringFormat::Wide2UTF8(dxil->pExports[e].Name));
}
}
else
{
// hard part, we need to parse the DXIL to get the entry points
DXBC::DXBCContainer container(
bytebuf((byte *)dxil->DXILLibrary.pShaderBytecode, dxil->DXILLibrary.BytecodeLength),
rdcstr(), GraphicsAPI::D3D12, ~0U, ~0U);
rdcarray<ShaderEntryPoint> entries = container.GetEntryPoints();
for(const ShaderEntryPoint &e : entries)
AddExport(e.name);
}
// TODO: register local root signature subobjects into dxilLocalRootSigs. Override
// anything in there, unlike the import from a collection below.
}
else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_EXISTING_COLLECTION)
{
D3D12_EXISTING_COLLECTION_DESC *coll = (D3D12_EXISTING_COLLECTION_DESC *)subobjects[i].pDesc;
WrappedID3D12StateObject *stateObj = (WrappedID3D12StateObject *)coll->pExistingCollection;
if(coll->NumExports > 0)
{
for(UINT e = 0; e < coll->NumExports; e++)
InheritCollectionExport(stateObj->exports, StringFormat::Wide2UTF8(coll->pExports[e].Name),
StringFormat::Wide2UTF8(coll->pExports[e].ExportToRename
? coll->pExports[e].ExportToRename
: coll->pExports[e].Name));
}
else
{
InheritAllCollectionExports(stateObj->exports);
}
// inherit explicit associations from the collection as lowest priority
inheritedRootSigAssocs.append(stateObj->exports->danglingRootSigAssocs);
inheritedDXILRootSigAssocs.append(stateObj->exports->danglingDXILRootSigAssocs);
for(auto it = stateObj->exports->danglingDXILLocalRootSigs.begin();
it != stateObj->exports->danglingDXILLocalRootSigs.end(); ++it)
{
// don't override any local root signatures with the same name we already have. Not sure
// how this conflict should be resolved properly?
if(dxilLocalRootSigs.find(it->first) == dxilLocalRootSigs.end())
dxilLocalRootSigs[it->first] = it->second;
}
}
else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_LOCAL_ROOT_SIGNATURE)
{
// ignore these if an explicit default association has been made
if(!explicitDefault)
{
// if multiple root signatures are defined, then there can't be an unspecified default
unassocDefaultValid = defaultRoot == NULL;
defaultRoot = ((D3D12_LOCAL_ROOT_SIGNATURE *)subobjects[i].pDesc)->pLocalRootSignature;
}
}
else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_SUBOBJECT_TO_EXPORTS_ASSOCIATION)
{
D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION *assoc =
(D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION *)subobjects[i].pDesc;
const D3D12_STATE_SUBOBJECT *other = assoc->pSubobjectToAssociate;
// only care about associating local root signatures
if(other->Type == D3D12_STATE_SUBOBJECT_TYPE_LOCAL_ROOT_SIGNATURE)
{
ID3D12RootSignature *root = ((D3D12_LOCAL_ROOT_SIGNATURE *)other->pDesc)->pLocalRootSignature;
WrappedID3D12RootSignature *wrappedRoot = (WrappedID3D12RootSignature *)root;
// if there are no exports this is an explicit default association. We assume this
// matches and doesn't conflict
if(assoc->NumExports == NULL)
{
explicitDefault = true;
defaultRoot = root;
}
else
{
// otherwise record the explicit associations - these may refer to exports that
// haven't been seen yet so we record them locally
for(UINT e = 0; e < assoc->NumExports; e++)
explicitRootSigAssocs.push_back(
{StringFormat::Wide2UTF8(assoc->pExports[e]), wrappedRoot->localRootSigIdx});
}
}
}
else if(subobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION)
{
D3D12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION *assoc =
(D3D12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION *)subobjects[i].pDesc;
rdcstr other = StringFormat::Wide2UTF8(assoc->SubobjectToAssociate);
// we can't tell yet if this is a local root signature or not so we have to store it regardless
{
// if there are no exports this is an explicit default association, but we don't know if
// it's for a local root signature...
if(assoc->NumExports == NULL)
{
explicitDefaultDxilAssocs.push_back(other);
}
else
{
// otherwise record the explicit associations - these may refer to exports that
// haven't been seen yet so we record them locally
for(UINT e = 0; e < assoc->NumExports; e++)
explicitDxilAssocs.push_back({StringFormat::Wide2UTF8(assoc->pExports[e]), other});
}
}
}
}
// now that we have all exports registered, apply all associations we have in order of
// priority to get the right
for(size_t i = 0; i < explicitRootSigAssocs.size(); i++)
{
ApplyRoot(SubObjectPriority::CodeExplicitAssociation, explicitRootSigAssocs[i].first,
explicitRootSigAssocs[i].second);
}
if(explicitDefault)
{
WrappedID3D12RootSignature *wrappedRoot = (WrappedID3D12RootSignature *)defaultRoot;
ApplyDefaultRoot(SubObjectPriority::CodeExplicitDefault, wrappedRoot->localRootSigIdx);
}
// shouldn't be possible to have both explicit and implicit defaults?
else if(unassocDefaultValid)
{
WrappedID3D12RootSignature *wrappedRoot = (WrappedID3D12RootSignature *)defaultRoot;
ApplyDefaultRoot(SubObjectPriority::CodeImplicitDefault, wrappedRoot->localRootSigIdx);
}
for(size_t i = 0; i < explicitDxilAssocs.size(); i++)
{
auto it = dxilLocalRootSigs.find(explicitDxilAssocs[i].second);
if(it == dxilLocalRootSigs.end())
continue;
uint32_t localRootSigIdx = it->second;
ApplyRoot(SubObjectPriority::DXILExplicitAssociation, explicitDxilAssocs[i].first,
localRootSigIdx);
}
for(size_t i = 0; i < explicitDefaultDxilAssocs.size(); i++)
{
auto it = dxilLocalRootSigs.find(explicitDefaultDxilAssocs[i]);
if(it == dxilLocalRootSigs.end())
continue;
uint32_t localRootSigIdx = it->second;
ApplyDefaultRoot(SubObjectPriority::DXILExplicitDefault, localRootSigIdx);
// only expect one local root signature - the array is because we can't tell the type of the
// default subobject when we encounter it
break;
}
if(unassocDXILDefaultValid)
{
ApplyDefaultRoot(SubObjectPriority::DXILImplicitDefault, dxilDefaultRoot);
}
// we assume it's not possible to inherit two different explicit associations for a single export
for(size_t i = 0; i < inheritedRootSigAssocs.size(); i++)
{
ApplyRoot(SubObjectPriority::CollectionExplicitAssociation, inheritedRootSigAssocs[i].first,
inheritedRootSigAssocs[i].second);
}
for(size_t i = 0; i < inheritedDXILRootSigAssocs.size(); i++)
{
auto it = dxilLocalRootSigs.find(inheritedDXILRootSigAssocs[i].second);
if(it == dxilLocalRootSigs.end())
continue;
uint32_t localRootSigIdx = it->second;
ApplyRoot(SubObjectPriority::CollectionExplicitAssociation, inheritedDXILRootSigAssocs[i].first,
localRootSigIdx);
}
danglingRootSigAssocs.swap(inheritedRootSigAssocs);
danglingDXILRootSigAssocs.swap(inheritedDXILRootSigAssocs);
danglingDXILLocalRootSigs.swap(dxilLocalRootSigs);
UpdateHitGroupAssociations();
}
void D3D12ShaderExportDatabase::AddExport(const rdcstr &exportName)
{
bool mangled = false;
rdcstr unmangledName;
if(exportName.size() > 2 && exportName[0] == '\x1' && exportName[1] == '?')
{
int idx = exportName.indexOf('@');
if(idx > 2)
{
unmangledName = exportName.substr(2, idx - 2);
mangled = true;
}
}
void *identifier = NULL;
// shader identifiers seem to be only accessible via unmangled names
if(m_StateObjectProps)
identifier = m_StateObjectProps->GetShaderIdentifier(
StringFormat::UTF82Wide(mangled ? unmangledName : exportName).c_str());
const bool complete = identifier != NULL;
{
// store the wrapped identifier here in this database, ready to return to the application in
// this object or any child objects.
wrappedIdentifiers.push_back({objectOriginalId, (uint32_t)ownExports.size()});
// store the unwrapping information to go into the giant lookup table
ownExports.push_back({});
// if there's a real identifier then store it. But we track this regardless so that we can
// know the root signature for hitgroup-component shaders. If this export is inherited then it
// will be detected as incomplete and copied and patched in the child
if(identifier)
memcpy(ownExports.back().real, identifier, sizeof(ShaderIdentifier));
// a local root signature may never get specified, so default to none
ownExports.back().rootSigPrio = SubObjectPriority::NotYetDefined;
ownExports.back().localRootSigIndex = 0xffff;
}
exportLookups.emplace_back(exportName, unmangledName, complete);
}
void D3D12ShaderExportDatabase::InheritCollectionExport(D3D12ShaderExportDatabase *existing,
const rdcstr &nameToExport,
const rdcstr &nameInExisting)
{
if(!parents.contains(existing))
{
parents.push_back(existing);
existing->AddRef();
}
for(size_t i = 0; i < existing->exportLookups.size(); i++)
{
if(existing->exportLookups[i].name == nameInExisting ||
existing->exportLookups[i].altName == nameInExisting)
{
InheritExport(nameInExisting, existing, i);
// if we renamed, now that we found the right export in the existing collection use the
// desired name going forward. This may still find the existing identifier as that hasn't
// necessarily changed
if(nameToExport != nameInExisting)
{
exportLookups.back().name = nameToExport;
exportLookups.back().altName.clear();
if(exportLookups.back().hitgroup)
hitGroups.back().first = nameToExport;
}
}
}
}
void D3D12ShaderExportDatabase::InheritExport(const rdcstr &exportName,
D3D12ShaderExportDatabase *existing, size_t i)
{
void *identifier = NULL;
if(m_StateObjectProps)
identifier = m_StateObjectProps->GetShaderIdentifier(StringFormat::UTF82Wide(exportName).c_str());
wrappedIdentifiers.push_back(existing->wrappedIdentifiers[i]);
exportLookups.push_back(existing->exportLookups[i]);
// if this export wasn't previously complete, consider it exported in this object
// note that identifier may be NULL if this is a shader that can't be used on its own like any
// hit, but we want to keep it in our export list so we can track its root signature to update
// the hit group's root signature. Since there is only one level of collection => RT PSO this
// won't cause too much wasted exports
// we don't inherit non-complete identifiers when doing AddToStateObject so this doesn't apply
if(!exportLookups.back().complete)
{
ownExports.push_back({});
// we expect this identifier to have come from the object we're inheriting
RDCASSERTEQUAL(wrappedIdentifiers.back().id, existing->objectOriginalId);
// which means we can copy any root signature it had associated even if it wasn't complete
ownExports.back() = existing->ownExports[wrappedIdentifiers.back().index];
// now set the identifier, if we got one
if(identifier)
memcpy(ownExports.back().real, identifier, sizeof(ShaderIdentifier));
// and re-point this to point to ourselves when queried as we have the best data for it.
wrappedIdentifiers.back() = {objectOriginalId, (uint32_t)ownExports.size()};
// if this is an incomplete hitgroup, also grab the hitgroup component data
if(exportLookups.back().hitgroup)
{
for(size_t h = 0; h < existing->hitGroups.size(); h++)
{
if(existing->hitGroups[h].first == exportName)
{
hitGroups.push_back(existing->hitGroups[h]);
break;
}
}
}
}
}
void D3D12ShaderExportDatabase::ApplyRoot(SubObjectPriority priority, const rdcstr &exportName,
uint32_t localRootSigIndex)
{
for(size_t i = 0; i < exportLookups.size(); i++)
{
if(exportLookups[i].name == exportName || exportLookups[i].altName == exportName)
{
ApplyRoot(wrappedIdentifiers[i], priority, localRootSigIndex);
break;
}
}
}
void D3D12ShaderExportDatabase::ApplyRoot(const ShaderIdentifier &identifier,
SubObjectPriority priority, uint32_t localRootSigIndex)
{
if(identifier.id == objectOriginalId)
{
// set this anywhere we have a looser/lower priority association already (including the most
// common case presumably where one isn't set at all)
ExportedIdentifier &exported = ownExports[identifier.index];
if(exported.rootSigPrio < priority)
{
exported.rootSigPrio = priority;
exported.localRootSigIndex = (uint16_t)localRootSigIndex;
}
}
}
void D3D12ShaderExportDatabase::AddLastHitGroupShaders(rdcarray<rdcstr> &&shaders)
{
exportLookups.back().hitgroup = true;
hitGroups.emplace_back(exportLookups.back().name, shaders);
}
void D3D12ShaderExportDatabase::UpdateHitGroupAssociations()
{
// for each hit group
for(size_t h = 0; h < hitGroups.size(); h++)
{
// find it in the exports, as it could have been dangling before
for(size_t e = 0; e < exportLookups.size(); e++)
{
if(hitGroups[h].first == exportLookups[e].name)
{
// if the export is our own (ie. not complete and finished in a parent), we might need to
// update its root sig
if(wrappedIdentifiers[e].id == objectOriginalId)
{
// if the hit group got a code association already we assume it must match, but a DXIL
// association or a default association could be overridden since it's unclear if a
// hitgroup is a 'candidate' for default
if(ownExports[wrappedIdentifiers[e].index].rootSigPrio !=
SubObjectPriority::CodeExplicitAssociation)
{
// for each export, find it and try to update the root signature
for(const rdcstr &shaderExport : hitGroups[h].second)
{
for(size_t e2 = 0; e2 < exportLookups.size(); e2++)
{
if(shaderExport == exportLookups[e2].name || shaderExport == exportLookups[e2].altName)
{
RDCASSERTEQUAL(wrappedIdentifiers[e2].id, objectOriginalId);
uint32_t idx = wrappedIdentifiers[e2].index;
ApplyRoot(wrappedIdentifiers[e], ownExports[idx].rootSigPrio,
ownExports[idx].localRootSigIndex);
// don't keep looking at exports, we found this shader
break;
}
}
// if we've inherited an explicit code association from a component shader, that
// also must match so we can stop looking. Otherwise we keep looking to try and find
// a 'better' association that can't be overridden
if(ownExports[wrappedIdentifiers[e].index].rootSigPrio ==
SubObjectPriority::CodeExplicitAssociation)
break;
}
}
}
// found this hit group, don't keep looking
break;
}
}
}
}
void D3D12ShaderExportDatabase::InheritAllCollectionExports(D3D12ShaderExportDatabase *existing)
{
if(!parents.contains(existing))
{
parents.push_back(existing);
existing->AddRef();
}
wrappedIdentifiers.reserve(wrappedIdentifiers.size() + existing->wrappedIdentifiers.size());
exportLookups.reserve(exportLookups.size() + existing->exportLookups.size());
for(size_t i = 0; i < existing->exportLookups.size(); i++)
{
InheritExport(existing->exportLookups[i].name, existing, i);
}
}
void D3D12ShaderExportDatabase::ApplyDefaultRoot(SubObjectPriority priority,
uint32_t localRootSigIndex)
{
for(size_t i = 0; i < wrappedIdentifiers.size(); i++)
ApplyRoot(wrappedIdentifiers[i], priority, localRootSigIndex);
}
UINT GetPlaneForSubresource(ID3D12Resource *res, int Subresource)
{
D3D12_RESOURCE_DESC desc = res->GetDesc();
if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
return 0;
int mipLevels = desc.MipLevels;
if(mipLevels == 0)
mipLevels = CalcNumMips((int)desc.Width, 1, 1);
UINT arraySlices = desc.DepthOrArraySize;
if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
arraySlices = 1;
return Subresource / (mipLevels * arraySlices);
}
UINT GetMipForSubresource(ID3D12Resource *res, int Subresource)
{
D3D12_RESOURCE_DESC desc = res->GetDesc();
if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
return Subresource;
int mipLevels = desc.MipLevels;
if(mipLevels == 0)
mipLevels = CalcNumMips((int)desc.Width, 1, 1);
return (Subresource % mipLevels);
}
UINT GetSliceForSubresource(ID3D12Resource *res, int Subresource)
{
D3D12_RESOURCE_DESC desc = res->GetDesc();
if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
return Subresource;
int mipLevels = desc.MipLevels;
if(mipLevels == 0)
mipLevels = CalcNumMips((int)desc.Width, 1, 1);
return (Subresource / mipLevels) % desc.DepthOrArraySize;
}
UINT GetMipForDsv(const D3D12_DEPTH_STENCIL_VIEW_DESC &view)
{
switch(view.ViewDimension)
{
case D3D12_DSV_DIMENSION_TEXTURE1D: return view.Texture1D.MipSlice;
case D3D12_DSV_DIMENSION_TEXTURE1DARRAY: return view.Texture1DArray.MipSlice;
case D3D12_DSV_DIMENSION_TEXTURE2D: return view.Texture2D.MipSlice;
case D3D12_DSV_DIMENSION_TEXTURE2DARRAY: return view.Texture2DArray.MipSlice;
default: return 0;
}
}
UINT GetSliceForDsv(const D3D12_DEPTH_STENCIL_VIEW_DESC &view)
{
switch(view.ViewDimension)
{
case D3D12_DSV_DIMENSION_TEXTURE1DARRAY: return view.Texture1DArray.FirstArraySlice;
case D3D12_DSV_DIMENSION_TEXTURE2DARRAY: return view.Texture2DArray.FirstArraySlice;
case D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY: return view.Texture2DMSArray.FirstArraySlice;
default: return 0;
}
}
UINT GetMipForRtv(const D3D12_RENDER_TARGET_VIEW_DESC &view)
{
switch(view.ViewDimension)
{
case D3D12_RTV_DIMENSION_TEXTURE1D: return view.Texture1D.MipSlice;
case D3D12_RTV_DIMENSION_TEXTURE1DARRAY: return view.Texture1DArray.MipSlice;
case D3D12_RTV_DIMENSION_TEXTURE2D: return view.Texture2D.MipSlice;
case D3D12_RTV_DIMENSION_TEXTURE2DARRAY: return view.Texture2DArray.MipSlice;
case D3D12_RTV_DIMENSION_TEXTURE3D: return view.Texture3D.MipSlice;
default: return 0;
}
}
UINT GetSliceForRtv(const D3D12_RENDER_TARGET_VIEW_DESC &view)
{
switch(view.ViewDimension)
{
case D3D12_RTV_DIMENSION_TEXTURE1DARRAY: return view.Texture1DArray.FirstArraySlice;
case D3D12_RTV_DIMENSION_TEXTURE2DARRAY: return view.Texture2DArray.FirstArraySlice;
case D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY: return view.Texture2DMSArray.FirstArraySlice;
default: return 0;
}
}
D3D12_SHADER_RESOURCE_VIEW_DESC MakeSRVDesc(const D3D12_RESOURCE_DESC &desc)
{
D3D12_SHADER_RESOURCE_VIEW_DESC ret = {};
ret.Format = desc.Format;
ret.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
bool arrayed = desc.DepthOrArraySize > 1;
if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
// I don't think it's possible to create a SRV/SRV of a buffer with a NULL desc, but the docs
// and debug layer are quite hard to be sure. Put in something sensible.
ret.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
ret.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
ret.Buffer.StructureByteStride = 0;
ret.Buffer.FirstElement = 0;
ret.Buffer.NumElements = (UINT)desc.Width;
}
else if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D)
{
ret.ViewDimension = arrayed ? D3D12_SRV_DIMENSION_TEXTURE1DARRAY : D3D12_SRV_DIMENSION_TEXTURE1D;
// shared between arrayed and not
ret.Texture1D.MipLevels = desc.MipLevels;
if(arrayed)
ret.Texture1DArray.ArraySize = desc.DepthOrArraySize;
}
else if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D)
{
if(desc.SampleDesc.Count > 1)
{
ret.ViewDimension =
arrayed ? D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY : D3D12_SRV_DIMENSION_TEXTURE2DMS;
if(arrayed)
ret.Texture2DMSArray.ArraySize = desc.DepthOrArraySize;
}
else
{
ret.ViewDimension =
arrayed ? D3D12_SRV_DIMENSION_TEXTURE2DARRAY : D3D12_SRV_DIMENSION_TEXTURE2D;
// shared between arrayed and not
ret.Texture2D.MipLevels = desc.MipLevels;
if(arrayed)
ret.Texture2DArray.ArraySize = desc.DepthOrArraySize;
}
}
else if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
{
ret.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
ret.Texture3D.MipLevels = desc.MipLevels;
}
return ret;
}
D3D12_UNORDERED_ACCESS_VIEW_DESC MakeUAVDesc(const D3D12_RESOURCE_DESC &desc)
{
D3D12_UNORDERED_ACCESS_VIEW_DESC ret = {};
ret.Format = desc.Format;
bool arrayed = desc.DepthOrArraySize > 1;
if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
// I don't think it's possible to create a UAV/SRV of a buffer with a NULL desc, but the docs
// and debug layer are quite hard to be sure. Put in something sensible.
ret.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
ret.Buffer.NumElements = (UINT)desc.Width;
}
else if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D)
{
ret.ViewDimension = arrayed ? D3D12_UAV_DIMENSION_TEXTURE1DARRAY : D3D12_UAV_DIMENSION_TEXTURE1D;
if(arrayed)
ret.Texture1DArray.ArraySize = desc.DepthOrArraySize;
}
else if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D)
{
if(desc.SampleDesc.Count > 1)
{
ret.ViewDimension =
arrayed ? D3D12_UAV_DIMENSION_TEXTURE2DMSARRAY : D3D12_UAV_DIMENSION_TEXTURE2DMS;
if(arrayed)
ret.Texture2DMSArray.ArraySize = desc.DepthOrArraySize;
}
else
{
ret.ViewDimension =
arrayed ? D3D12_UAV_DIMENSION_TEXTURE2DARRAY : D3D12_UAV_DIMENSION_TEXTURE2D;
if(arrayed)
ret.Texture2DArray.ArraySize = desc.DepthOrArraySize;
}
}
else if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
{
ret.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
ret.Texture3D.WSize = desc.DepthOrArraySize;
}
return ret;
}