/****************************************************************************** * 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_manager.h" #include #include "driver/dx/official/d3dcompiler.h" #include "driver/dxgi/dxgi_common.h" #include "d3d12_command_list.h" #include "d3d12_command_queue.h" #include "d3d12_device.h" #include "d3d12_resources.h" #include "d3d12_shader_cache.h" void D3D12Descriptor::Init(const D3D12_SAMPLER_DESC2 *pDesc) { if(pDesc) data.samp.desc.Init(*pDesc); else RDCEraseEl(data.samp.desc); } void D3D12Descriptor::Init(const D3D12_SAMPLER_DESC *pDesc) { if(!pDesc) { RDCEraseEl(data.samp.desc); return; } D3D12_SAMPLER_DESC2 desc; desc.Filter = pDesc->Filter; desc.Filter = pDesc->Filter; desc.AddressU = pDesc->AddressU; desc.AddressV = pDesc->AddressV; desc.AddressW = pDesc->AddressW; desc.ComparisonFunc = pDesc->ComparisonFunc; desc.MipLODBias = pDesc->MipLODBias; desc.MaxAnisotropy = pDesc->MaxAnisotropy; memcpy(desc.UintBorderColor, pDesc->BorderColor, sizeof(desc.UintBorderColor)); desc.MinLOD = pDesc->MinLOD; desc.MaxLOD = pDesc->MaxLOD; desc.Flags = D3D12_SAMPLER_FLAG_NONE; data.samp.desc.Init(desc); } void D3D12Descriptor::Init(const D3D12_CONSTANT_BUFFER_VIEW_DESC *pDesc) { data.nonsamp.type = D3D12DescriptorType::CBV; data.nonsamp.resource = ResourceId(); if(pDesc) data.nonsamp.cbv = *pDesc; else RDCEraseEl(data.nonsamp.cbv); } void D3D12Descriptor::Init(ID3D12Resource *pResource, const D3D12_SHADER_RESOURCE_VIEW_DESC *pDesc) { data.nonsamp.type = D3D12DescriptorType::SRV; data.nonsamp.resource = GetResID(pResource); if(pDesc) data.nonsamp.srv.Init(*pDesc); else RDCEraseEl(data.nonsamp.srv); } void D3D12Descriptor::Init(ID3D12Resource *pResource, ID3D12Resource *pCounterResource, const D3D12_UNORDERED_ACCESS_VIEW_DESC *pDesc) { data.nonsamp.type = D3D12DescriptorType::UAV; data.nonsamp.resource = GetResID(pResource); data.nonsamp.counterResource = GetResID(pCounterResource); if(pDesc) data.nonsamp.uav.Init(*pDesc); else RDCEraseEl(data.nonsamp.uav); } void D3D12Descriptor::Init(ID3D12Resource *pResource, const D3D12_RENDER_TARGET_VIEW_DESC *pDesc) { data.nonsamp.type = D3D12DescriptorType::RTV; data.nonsamp.resource = GetResID(pResource); if(pDesc) data.nonsamp.rtv = *pDesc; else RDCEraseEl(data.nonsamp.rtv); } void D3D12Descriptor::Init(ID3D12Resource *pResource, const D3D12_DEPTH_STENCIL_VIEW_DESC *pDesc) { data.nonsamp.type = D3D12DescriptorType::DSV; data.nonsamp.resource = GetResID(pResource); if(pDesc) data.nonsamp.dsv = *pDesc; else RDCEraseEl(data.nonsamp.dsv); } // these are used to create NULL descriptors where necessary static D3D12_SHADER_RESOURCE_VIEW_DESC *defaultSRV() { static D3D12_SHADER_RESOURCE_VIEW_DESC ret = {}; ret.Format = DXGI_FORMAT_R8_UNORM; ret.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; ret.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; ret.Texture2D.MipLevels = 1; return &ret; } static D3D12_RENDER_TARGET_VIEW_DESC *defaultRTV() { static D3D12_RENDER_TARGET_VIEW_DESC ret = {}; ret.Format = DXGI_FORMAT_R8_UNORM; ret.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; return &ret; } static D3D12_DEPTH_STENCIL_VIEW_DESC *defaultDSV() { static D3D12_DEPTH_STENCIL_VIEW_DESC ret = {}; ret.Format = DXGI_FORMAT_D16_UNORM; ret.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; return &ret; } static D3D12_UNORDERED_ACCESS_VIEW_DESC *defaultUAV() { static D3D12_UNORDERED_ACCESS_VIEW_DESC ret = {}; ret.Format = DXGI_FORMAT_R8_UNORM; ret.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; return &ret; } void D3D12Descriptor::Create(D3D12_DESCRIPTOR_HEAP_TYPE heapType, WrappedID3D12Device *dev, D3D12_CPU_DESCRIPTOR_HANDLE handle) { D3D12DescriptorType type = GetType(); ID3D12Resource *res = NULL; ID3D12Resource *countRes = NULL; if(type != D3D12DescriptorType::Sampler && type != D3D12DescriptorType::CBV) res = dev->GetResourceManager()->GetCurrentAs(data.nonsamp.resource); // don't create a UAV with a counter resource but no main resource. This is fine because // if the main resource wasn't present in the capture, this UAV isn't present - the counter // must have been included for some other reference. if(type == D3D12DescriptorType::UAV && res) countRes = dev->GetResourceManager()->GetCurrentAs(data.nonsamp.counterResource); switch(type) { case D3D12DescriptorType::Sampler: { D3D12_SAMPLER_DESC2 desc = data.samp.desc.AsDesc(); if(desc.Flags == D3D12_SAMPLER_FLAG_NONE) { D3D12_SAMPLER_DESC desc1; memcpy(&desc1, &desc, sizeof(desc1)); dev->CreateSampler(&desc1, handle); } else { dev->CreateSampler2(&desc, handle); } break; } case D3D12DescriptorType::CBV: { if(data.nonsamp.cbv.BufferLocation != 0) dev->CreateConstantBufferView(&data.nonsamp.cbv, handle); else dev->CreateShaderResourceView(NULL, defaultSRV(), handle); break; } case D3D12DescriptorType::SRV: { D3D12_SHADER_RESOURCE_VIEW_DESC srvdesc = data.nonsamp.srv.AsDesc(); D3D12_SHADER_RESOURCE_VIEW_DESC *desc = &srvdesc; if(desc->ViewDimension == D3D12_SRV_DIMENSION_UNKNOWN) { desc = res ? NULL : defaultSRV(); const std::map &bbs = dev->GetBackbufferFormats(); auto it = bbs.find(GetResID(res)); // fixup for backbuffers if(it != bbs.end()) { D3D12_SHADER_RESOURCE_VIEW_DESC bbDesc = {}; bbDesc.Format = it->second; bbDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; bbDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; bbDesc.Texture2D.MipLevels = 1; dev->CreateShaderResourceView(res, &bbDesc, handle); return; } } else if(!res) { // if we don't have a resource (which is possible if the descriptor is unused or invalidated // by referring to a resource that was deleted), use a default descriptor desc = defaultSRV(); } else if(desc->Format == DXGI_FORMAT_UNKNOWN) { const std::map &bbs = dev->GetBackbufferFormats(); auto it = bbs.find(GetResID(res)); // fixup for backbuffers if(it != bbs.end()) { D3D12_SHADER_RESOURCE_VIEW_DESC bbDesc = *desc; bbDesc.Format = it->second; dev->CreateShaderResourceView(res, &bbDesc, handle); return; } } D3D12_SHADER_RESOURCE_VIEW_DESC planeDesc; // ensure that multi-plane formats have a valid plane slice specified. This shouldn't be // possible as it should be the application's responsibility to be valid too, but we fix it up // here anyway. if(res && desc) { D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = {}; formatInfo.Format = desc->Format; dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &formatInfo, sizeof(formatInfo)); // if this format is multi-plane if(formatInfo.PlaneCount > 1) { planeDesc = *desc; desc = &planeDesc; // detect formats that only read plane 1 and set the planeslice to 1 if(desc->Format == DXGI_FORMAT_X24_TYPELESS_G8_UINT || desc->Format == DXGI_FORMAT_X32_TYPELESS_G8X24_UINT) { switch(planeDesc.ViewDimension) { case D3D12_SRV_DIMENSION_TEXTURE2D: planeDesc.Texture2D.PlaneSlice = 1; break; case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: planeDesc.Texture2DArray.PlaneSlice = 1; break; default: break; } } else { // otherwise set it to 0 switch(planeDesc.ViewDimension) { case D3D12_SRV_DIMENSION_TEXTURE2D: planeDesc.Texture2D.PlaneSlice = 0; break; case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: planeDesc.Texture2DArray.PlaneSlice = 0; break; default: break; } } } } dev->CreateShaderResourceView(res, desc, handle); break; } case D3D12DescriptorType::RTV: { D3D12_RENDER_TARGET_VIEW_DESC *desc = &data.nonsamp.rtv; if(desc->ViewDimension == D3D12_RTV_DIMENSION_UNKNOWN) { desc = res ? NULL : defaultRTV(); const std::map &bbs = dev->GetBackbufferFormats(); auto it = bbs.find(GetResID(res)); // fixup for backbuffers if(it != bbs.end()) { D3D12_RENDER_TARGET_VIEW_DESC bbDesc = {}; bbDesc.Format = it->second; bbDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; dev->CreateRenderTargetView(res, &bbDesc, handle); return; } } else if(!res) { // if we don't have a resource (which is possible if the descriptor is unused or invalidated // by referring to a resource that was deleted), use a default descriptor desc = defaultRTV(); } else if(desc->Format == DXGI_FORMAT_UNKNOWN) { const std::map &bbs = dev->GetBackbufferFormats(); auto it = bbs.find(GetResID(res)); // fixup for backbuffers if(it != bbs.end()) { D3D12_RENDER_TARGET_VIEW_DESC bbDesc = *desc; bbDesc.Format = it->second; dev->CreateRenderTargetView(res, &bbDesc, handle); return; } } D3D12_RENDER_TARGET_VIEW_DESC planeDesc; // ensure that multi-plane formats have a valid plane slice specified. This shouldn't be // possible as it should be the application's responsibility to be valid too, but we fix it up // here anyway. if(res && desc) { D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = {}; formatInfo.Format = desc->Format; dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &formatInfo, sizeof(formatInfo)); // if this format is multi-plane if(formatInfo.PlaneCount > 1) { planeDesc = *desc; desc = &planeDesc; // detect formats that only read plane 1 and set the planeslice to 1 if(desc->Format == DXGI_FORMAT_X24_TYPELESS_G8_UINT || desc->Format == DXGI_FORMAT_X32_TYPELESS_G8X24_UINT) { switch(planeDesc.ViewDimension) { case D3D12_RTV_DIMENSION_TEXTURE2D: planeDesc.Texture2D.PlaneSlice = 1; break; case D3D12_RTV_DIMENSION_TEXTURE2DARRAY: planeDesc.Texture2DArray.PlaneSlice = 1; break; default: break; } } else { // otherwise set it to 0 switch(planeDesc.ViewDimension) { case D3D12_RTV_DIMENSION_TEXTURE2D: planeDesc.Texture2D.PlaneSlice = 0; break; case D3D12_RTV_DIMENSION_TEXTURE2DARRAY: planeDesc.Texture2DArray.PlaneSlice = 0; break; default: break; } } } } dev->CreateRenderTargetView(res, desc, handle); break; } case D3D12DescriptorType::DSV: { D3D12_DEPTH_STENCIL_VIEW_DESC *desc = &data.nonsamp.dsv; if(desc->ViewDimension == D3D12_DSV_DIMENSION_UNKNOWN) { desc = res ? NULL : defaultDSV(); } else if(!res) { // if we don't have a resource (which is possible if the descriptor is unused or invalidated // by referring to a resource that was deleted), use a default descriptor desc = defaultDSV(); } dev->CreateDepthStencilView(res, desc, handle); break; } case D3D12DescriptorType::UAV: { D3D12_UNORDERED_ACCESS_VIEW_DESC uavdesc = data.nonsamp.uav.AsDesc(); D3D12_UNORDERED_ACCESS_VIEW_DESC *desc = &uavdesc; if(uavdesc.ViewDimension == D3D12_UAV_DIMENSION_UNKNOWN) { desc = res ? NULL : defaultUAV(); const std::map &bbs = dev->GetBackbufferFormats(); auto it = bbs.find(GetResID(res)); // fixup for backbuffers if(it != bbs.end()) { D3D12_UNORDERED_ACCESS_VIEW_DESC bbDesc = {}; bbDesc.Format = it->second; bbDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; dev->CreateUnorderedAccessView(res, NULL, &bbDesc, handle); return; } } else if(!res) { // if we don't have a resource (which is possible if the descriptor is unused), use a // default descriptor desc = defaultUAV(); } else if(desc->Format == DXGI_FORMAT_UNKNOWN) { const std::map &bbs = dev->GetBackbufferFormats(); auto it = bbs.find(GetResID(res)); // fixup for backbuffers if(it != bbs.end()) { D3D12_UNORDERED_ACCESS_VIEW_DESC bbDesc = *desc; bbDesc.Format = it->second; dev->CreateUnorderedAccessView(res, NULL, &bbDesc, handle); return; } } if(countRes == NULL && desc && desc->ViewDimension == D3D12_UAV_DIMENSION_BUFFER) desc->Buffer.CounterOffsetInBytes = 0; D3D12_UNORDERED_ACCESS_VIEW_DESC planeDesc; // ensure that multi-plane formats have a valid plane slice specified. This shouldn't be // possible as it should be the application's responsibility to be valid too, but we fix it up // here anyway. if(res && desc) { D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = {}; formatInfo.Format = desc->Format; dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &formatInfo, sizeof(formatInfo)); // if this format is multi-plane if(formatInfo.PlaneCount > 1) { planeDesc = *desc; desc = &planeDesc; // detect formats that only read plane 1 and set the planeslice to 1 if(desc->Format == DXGI_FORMAT_X24_TYPELESS_G8_UINT || desc->Format == DXGI_FORMAT_X32_TYPELESS_G8X24_UINT) { switch(planeDesc.ViewDimension) { case D3D12_UAV_DIMENSION_TEXTURE2D: planeDesc.Texture2D.PlaneSlice = 1; break; case D3D12_UAV_DIMENSION_TEXTURE2DARRAY: planeDesc.Texture2DArray.PlaneSlice = 1; break; default: break; } } else { // otherwise set it to 0 switch(planeDesc.ViewDimension) { case D3D12_UAV_DIMENSION_TEXTURE2D: planeDesc.Texture2D.PlaneSlice = 0; break; case D3D12_UAV_DIMENSION_TEXTURE2DARRAY: planeDesc.Texture2DArray.PlaneSlice = 0; break; default: break; } } } } dev->CreateUnorderedAccessView(res, countRes, desc, handle); break; } case D3D12DescriptorType::Undefined: { // initially descriptors are undefined. This way we just init with // a null descriptor so it's valid to copy around etc but is no // less undefined for the application to use if(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) dev->CreateShaderResourceView(NULL, defaultSRV(), handle); else if(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_DSV) dev->CreateDepthStencilView(NULL, defaultDSV(), handle); else if(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_RTV) dev->CreateRenderTargetView(NULL, defaultRTV(), handle); break; } } } void D3D12Descriptor::CopyFrom(const D3D12Descriptor &src) { // save these so we can do a straight copy then restore them WrappedID3D12DescriptorHeap *heap = data.samp.heap; uint32_t index = data.samp.idx; *this = src; data.samp.heap = heap; data.samp.idx = index; } void D3D12Descriptor::GetRefIDs(ResourceId &id, ResourceId &id2, FrameRefType &ref) { id = ResourceId(); id2 = ResourceId(); ref = eFrameRef_Read; switch(GetType()) { case D3D12DescriptorType::Undefined: case D3D12DescriptorType::Sampler: // nothing to do - no resource here break; case D3D12DescriptorType::CBV: id = WrappedID3D12Resource::GetResIDFromAddr(data.nonsamp.cbv.BufferLocation); break; case D3D12DescriptorType::SRV: id = data.nonsamp.resource; break; case D3D12DescriptorType::UAV: id2 = data.nonsamp.counterResource; DELIBERATE_FALLTHROUGH(); case D3D12DescriptorType::RTV: case D3D12DescriptorType::DSV: ref = eFrameRef_PartialWrite; id = data.nonsamp.resource; break; } } D3D12_CPU_DESCRIPTOR_HANDLE D3D12Descriptor::GetCPU() const { return data.samp.heap->GetCPU(data.samp.idx); } D3D12_GPU_DESCRIPTOR_HANDLE D3D12Descriptor::GetGPU() const { return data.samp.heap->GetGPU(data.samp.idx); } PortableHandle D3D12Descriptor::GetPortableHandle() const { return PortableHandle(GetResID(data.samp.heap), data.samp.idx); } ResourceId D3D12Descriptor::GetHeapResourceId() const { return GetResID(data.samp.heap); } ResourceId D3D12Descriptor::GetResResourceId() const { return data.nonsamp.resource; } ResourceId D3D12Descriptor::GetCounterResourceId() const { return data.nonsamp.counterResource; } D3D12_CPU_DESCRIPTOR_HANDLE UnwrapCPU(D3D12Descriptor *handle) { D3D12_CPU_DESCRIPTOR_HANDLE ret = {}; if(handle == NULL) return ret; return handle->GetCPU(); } D3D12_GPU_DESCRIPTOR_HANDLE UnwrapGPU(D3D12Descriptor *handle) { D3D12_GPU_DESCRIPTOR_HANDLE ret = {}; if(handle == NULL) return ret; return handle->GetGPU(); } D3D12_CPU_DESCRIPTOR_HANDLE Unwrap(D3D12_CPU_DESCRIPTOR_HANDLE handle) { if(handle.ptr == 0) return handle; return UnwrapCPU(GetWrapped(handle)); } D3D12_GPU_DESCRIPTOR_HANDLE Unwrap(D3D12_GPU_DESCRIPTOR_HANDLE handle) { if(handle.ptr == 0) return handle; return UnwrapGPU(GetWrapped(handle)); } PortableHandle ToPortableHandle(D3D12Descriptor *desc) { if(desc == NULL) return PortableHandle(0); return desc->GetPortableHandle(); } PortableHandle ToPortableHandle(D3D12_CPU_DESCRIPTOR_HANDLE handle) { if(handle.ptr == 0) return PortableHandle(0); return ToPortableHandle(GetWrapped(handle)); } PortableHandle ToPortableHandle(D3D12_GPU_DESCRIPTOR_HANDLE handle) { if(handle.ptr == 0) return PortableHandle(0); return ToPortableHandle(GetWrapped(handle)); } D3D12_CPU_DESCRIPTOR_HANDLE CPUHandleFromPortableHandle(D3D12ResourceManager *manager, PortableHandle handle) { if(handle.heap == ResourceId()) return D3D12_CPU_DESCRIPTOR_HANDLE(); WrappedID3D12DescriptorHeap *heap = manager->GetLiveAs(handle.heap); if(heap) return heap->GetCPU(handle.index); return D3D12_CPU_DESCRIPTOR_HANDLE(); } D3D12_GPU_DESCRIPTOR_HANDLE GPUHandleFromPortableHandle(D3D12ResourceManager *manager, PortableHandle handle) { if(handle.heap == ResourceId()) return D3D12_GPU_DESCRIPTOR_HANDLE(); WrappedID3D12DescriptorHeap *heap = manager->GetLiveAs(handle.heap); if(heap) return heap->GetGPU(handle.index); return D3D12_GPU_DESCRIPTOR_HANDLE(); } D3D12Descriptor *DescriptorFromPortableHandle(D3D12ResourceManager *manager, PortableHandle handle) { if(handle.heap == ResourceId()) return NULL; WrappedID3D12DescriptorHeap *heap = manager->GetLiveAs(handle.heap, true); if(heap) return heap->GetDescriptors() + handle.index; return NULL; } // debugging logging for barriers #if 0 #define BARRIER_DBG RDCLOG #define BARRIER_ASSERT RDCASSERTMSG #else #define BARRIER_DBG(...) #define BARRIER_ASSERT(...) #endif D3D12RaytracingResourceAndUtilHandler::D3D12RaytracingResourceAndUtilHandler(WrappedID3D12Device *device) : m_wrappedDevice(device), m_cmdList(NULL), m_cmdAlloc(NULL), m_cmdQueue(NULL), m_gpuFence(NULL), m_gpuSyncHandle(NULL), m_gpuSyncCounter(0u) { if(m_wrappedDevice) { ID3D12Device *realDevice = m_wrappedDevice->GetReal(); if(realDevice) { HRESULT result = realDevice->CreateCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void **)&m_cmdAlloc); if(!SUCCEEDED(result)) RDCERR("D3D12 Command allocator creation failed with error %s", ToStr(result).c_str()); if(m_cmdAlloc != NULL) { ID3D12GraphicsCommandList *cmd = NULL; result = realDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_cmdAlloc, NULL, __uuidof(ID3D12GraphicsCommandList), (void **)(&cmd)); if(!SUCCEEDED(result)) RDCERR("D3D12 Command list creation failed with error %s", ToStr(result).c_str()); m_cmdList = (ID3D12GraphicsCommandListX *)cmd; m_cmdList->Close(); } D3D12_COMMAND_QUEUE_DESC cmdQueueDesc; cmdQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; cmdQueueDesc.NodeMask = 0; cmdQueueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; cmdQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; result = realDevice->CreateCommandQueue(&cmdQueueDesc, __uuidof(ID3D12CommandQueue), (void **)&m_cmdQueue); if(!SUCCEEDED(result)) RDCERR("D3D12 Command queue creation failed with error %s", ToStr(result).c_str()); result = realDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), (void **)&m_gpuFence); if(!SUCCEEDED(result)) RDCERR("D3D12 fence creation failed with error %s", ToStr(result).c_str()); m_gpuSyncHandle = ::CreateEvent(NULL, FALSE, FALSE, NULL); } D3D12GpuBufferAllocator::Inst()->Alloc(D3D12GpuBufferHeapType::CustomHeapWithUavCpuAccess, D3D12GpuBufferHeapMemoryFlag::Default, 16, 256, ASQueryBuffer); } } void D3D12RaytracingResourceAndUtilHandler::SyncGpuForRtWork() { m_gpuSyncCounter++; HRESULT hr = m_cmdQueue->Signal(m_gpuFence, m_gpuSyncCounter); if(!SUCCEEDED(hr)) RDCERR("Command queue fence signaling failed with error %s ", ToStr(hr).c_str()); hr = m_gpuFence->SetEventOnCompletion(m_gpuSyncCounter, m_gpuSyncHandle); if(!SUCCEEDED(hr)) RDCERR("Fence completion event signaling failed with error %s ", ToStr(hr).c_str()); WaitForSingleObject(m_gpuSyncHandle, 10000); } void D3D12RaytracingResourceAndUtilHandler::InitInternalResources() { if(IsReplayMode(m_wrappedDevice->GetState())) { InitReplayBlasPatchingResources(); } } void D3D12RaytracingResourceAndUtilHandler::ResizeSerialisationBuffer(UINT64 size) { if(size > ASSerialiseBuffer.Size()) { ASSerialiseBuffer.Release(); D3D12GpuBufferAllocator::Inst()->Alloc(D3D12GpuBufferHeapType::DefaultHeapWithUav, D3D12GpuBufferHeapMemoryFlag::Default, size, 256, ASSerialiseBuffer); } } void D3D12RaytracingResourceAndUtilHandler::InitReplayBlasPatchingResources() { // Root Signature rdcarray rootParameters; rootParameters.reserve((uint16_t)D3D12PatchAccStructRootParamIndices::Count); { D3D12_ROOT_PARAMETER1 rootParam; rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; rootParam.Constants.ShaderRegister = 0; rootParam.Constants.RegisterSpace = 0; rootParam.Constants.Num32BitValues = 1; rootParameters.push_back(rootParam); } { D3D12_ROOT_PARAMETER1 rootParam; rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; rootParam.Descriptor.ShaderRegister = 0; rootParam.Descriptor.RegisterSpace = 0; rootParam.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE; rootParameters.push_back(rootParam); } { D3D12_ROOT_PARAMETER1 rootParam; rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; rootParam.Descriptor.ShaderRegister = 0; rootParam.Descriptor.RegisterSpace = 0; rootParam.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE; rootParameters.push_back(rootParam); } D3D12ShaderCache *shaderCache = m_wrappedDevice->GetShaderCache(); if(shaderCache != NULL) { ID3DBlob *rootSig = shaderCache->MakeRootSig(rootParameters, D3D12_ROOT_SIGNATURE_FLAG_NONE); if(rootSig) { HRESULT result = m_wrappedDevice->GetReal()->CreateRootSignature( 0, rootSig->GetBufferPointer(), rootSig->GetBufferSize(), __uuidof(ID3D12RootSignature), (void **)&m_accStructPatchInfo.m_rootSignature); if(!SUCCEEDED(result)) RDCERR("Unable to create root signature for patching the BLAS"); // PipelineState ID3DBlob *shader = NULL; rdcstr hlsl = GetEmbeddedResource(raytracing_hlsl); shaderCache->GetShaderBlob(hlsl.c_str(), "RENDERDOC_PatchAccStructAddressCS", D3DCOMPILE_WARNINGS_ARE_ERRORS, {}, "cs_6_0", &shader); if(shader) { D3D12_COMPUTE_PIPELINE_STATE_DESC pipeline; pipeline.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; pipeline.NodeMask = 0; pipeline.CS = {(void *)shader->GetBufferPointer(), shader->GetBufferSize()}; pipeline.CachedPSO = {NULL, 0}; pipeline.pRootSignature = m_accStructPatchInfo.m_rootSignature; result = m_wrappedDevice->GetReal()->CreateComputePipelineState( &pipeline, __uuidof(ID3D12PipelineState), (void **)&m_accStructPatchInfo.m_pipeline); if(!SUCCEEDED(result)) RDCERR("Unable to create pipeline for patching the BLAS"); } SAFE_RELEASE(rootSig); } } else { RDCERR("Shadercache not available"); } } D3D12GpuBufferAllocator *D3D12GpuBufferAllocator::m_bufferAllocator = NULL; bool D3D12GpuBufferAllocator::CopyBufferRegion(WrappedID3D12GraphicsCommandList *wrappedCmd, const D3D12GpuBuffer &destBuffer, D3D12_GPU_VIRTUAL_ADDRESS srcAddress, uint64_t dataSize) { if(D3D12GpuBuffer() != destBuffer && dataSize > 0) { ResourceId srcResourceId; D3D12BufferOffset srcResourceOffset; rdcarray resBarriers; rdcarray finalBarriers; { D3D12_RESOURCE_BARRIER resBarrier; resBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; resBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; resBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; resBarrier.Transition.pResource = destBuffer.Resource(); resBarriers.push_back(resBarrier); resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON; finalBarriers.push_back(resBarrier); } WrappedID3D12Resource::GetResIDFromAddr(srcAddress, srcResourceId, srcResourceOffset); if(srcResourceId != ResourceId()) { D3D12_RESOURCE_STATES srResourceState = wrappedCmd->GetWrappedDevice()->GetSubresourceStates(srcResourceId)[0].ToStates(); ID3D12Resource *srcResource = NULL; srcResource = wrappedCmd->GetWrappedDevice() ->GetResourceManager() ->GetCurrentAs(srcResourceId) ->GetReal(); if(!(srResourceState & D3D12_RESOURCE_STATE_COPY_SOURCE)) { D3D12_RESOURCE_BARRIER resBarrier; resBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; resBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resBarrier.Transition.StateBefore = srResourceState; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; resBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; resBarrier.Transition.pResource = srcResource; resBarriers.push_back(resBarrier); resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; resBarrier.Transition.StateAfter = srResourceState; finalBarriers.push_back(resBarrier); } wrappedCmd->GetReal()->ResourceBarrier((UINT)resBarriers.size(), resBarriers.data()); wrappedCmd->GetReal()->CopyBufferRegion(destBuffer.Resource(), destBuffer.Offset(), srcResource, srcResourceOffset, dataSize); wrappedCmd->GetReal()->ResourceBarrier((UINT)finalBarriers.size(), finalBarriers.data()); return true; } } return false; } bool D3D12GpuBufferAllocator::CopyBufferRegion(WrappedID3D12GraphicsCommandList *wrappedCmd, const D3D12GpuBuffer &destBuffer, const D3D12GpuBuffer &sourceBuffer, uint64_t dataSize) { // This will only handle if both are on default heap if(destBuffer.GetD3D12HeapType() != D3D12_HEAP_TYPE_DEFAULT || sourceBuffer.GetD3D12HeapType() != D3D12_HEAP_TYPE_DEFAULT) { return false; } rdcarray initBarriers; rdcarray finalBarriers; { D3D12_RESOURCE_BARRIER resBarrier; resBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; resBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; resBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; resBarrier.Transition.pResource = sourceBuffer.Resource(); initBarriers.push_back(resBarrier); resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON; finalBarriers.push_back(resBarrier); } { D3D12_RESOURCE_BARRIER resBarrier; resBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; resBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; resBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; resBarrier.Transition.pResource = destBuffer.Resource(); initBarriers.push_back(resBarrier); resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON; finalBarriers.push_back(resBarrier); } wrappedCmd->GetReal()->ResourceBarrier((UINT)initBarriers.size(), initBarriers.data()); wrappedCmd->GetReal()->CopyBufferRegion(destBuffer.Resource(), destBuffer.Offset(), sourceBuffer.Resource(), sourceBuffer.Offset(), dataSize); wrappedCmd->GetReal()->ResourceBarrier((UINT)finalBarriers.size(), finalBarriers.data()); return true; } bool D3D12GpuBufferAllocator::D3D12GpuBufferResource::CreateCommittedResourceBuffer( ID3D12Device *device, const D3D12_HEAP_PROPERTIES &heapProperty, D3D12_RESOURCE_STATES initState, uint64_t size, bool allowUav, D3D12GpuBufferResource **bufferResource) { if(device && bufferResource) { ID3D12Resource *newBufferResource = NULL; D3D12_RESOURCE_DESC bufferResDesc; bufferResDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; bufferResDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; bufferResDesc.DepthOrArraySize = 1u; bufferResDesc.MipLevels = 1u; bufferResDesc.Height = 1u; bufferResDesc.Flags = allowUav ? D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS : D3D12_RESOURCE_FLAG_NONE; bufferResDesc.Format = DXGI_FORMAT_UNKNOWN; bufferResDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; bufferResDesc.SampleDesc = {1, 0}; bufferResDesc.Width = size; D3D12GpuBufferResource *retBufferRes = NULL; // Create committed resource HRESULT opResult = device->CreateCommittedResource( &heapProperty, D3D12_HEAP_FLAG_NONE, &bufferResDesc, initState, NULL, __uuidof(ID3D12Resource), (void **)&newBufferResource); if(SUCCEEDED(opResult) && newBufferResource != NULL) { retBufferRes = new D3D12GpuBufferResource(newBufferResource, heapProperty.Type); } else { RDCERR("Allocation failed with result code %s", ToStr(opResult).c_str()); } if(retBufferRes) { *bufferResource = retBufferRes; return true; } } return false; } bool D3D12GpuBufferAllocator::D3D12GpuBufferResource::ReleaseGpuBufferResource( D3D12GpuBufferResource *bufferResource) { delete bufferResource; bufferResource = NULL; return true; } D3D12GpuBufferAllocator::D3D12GpuBufferResource::D3D12GpuBufferResource(ID3D12Resource *resource, D3D12_HEAP_TYPE heapType) : m_resource(resource), m_heapType(heapType) { if(m_resource) { m_resDesc = m_resource->GetDesc(); m_resourceGpuAddressRange.start = resource->GetGPUVirtualAddress(); m_resourceGpuAddressRange.realEnd = m_resourceGpuAddressRange.start + m_resDesc.Width; } } bool D3D12GpuBufferAllocator::D3D12GpuBufferPool::Alloc(WrappedID3D12Device *wrappedDevice, D3D12GpuBufferHeapMemoryFlag heapMem, uint64_t size, uint64_t alignment, D3D12GpuBuffer &gpuBuffer) { if(heapMem == D3D12GpuBufferHeapMemoryFlag::Default) { if(size > m_bufferInitSize) { m_bufferInitSize = size; } D3D12_GPU_VIRTUAL_ADDRESS gpuAddress = 0; for(D3D12GpuBufferResource *bufferRes : m_bufferResourceList) { if(bufferRes->SubAlloc(size, alignment, gpuAddress)) { gpuBuffer = D3D12GpuBuffer(m_bufferPoolHeapType, D3D12GpuBufferHeapMemoryFlag::Default, size, alignment, gpuAddress, bufferRes->Resource()); return true; } } D3D12GpuBufferResource *newBufferResource = NULL; if(D3D12GpuBufferAllocator::CreateBufferResource(wrappedDevice, m_bufferPoolHeapType, m_bufferInitSize, &newBufferResource)) { m_bufferResourceList.push_back(newBufferResource); if(newBufferResource->SubAlloc(size, alignment, gpuAddress)) { gpuBuffer = D3D12GpuBuffer(m_bufferPoolHeapType, D3D12GpuBufferHeapMemoryFlag::Default, size, alignment, gpuAddress, newBufferResource->Resource()); return true; } } } else { D3D12GpuBufferResource *newBufferResource = NULL; if(CreateBufferResource(m_bufferAllocator->m_wrappedDevice, m_bufferPoolHeapType, size, &newBufferResource)) { m_bufferResourceList.push_back(newBufferResource); gpuBuffer = D3D12GpuBuffer(m_bufferPoolHeapType, D3D12GpuBufferHeapMemoryFlag::Dedicated, size, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, newBufferResource->Resource()->GetGPUVirtualAddress(), newBufferResource->Resource()); return true; } } RDCERR("Unable to allocate GPU memory"); return false; } bool D3D12GpuBufferAllocator::D3D12GpuBufferPool::Free(const D3D12GpuBuffer &gpuBuffer) { if(gpuBuffer != D3D12GpuBuffer()) { for(D3D12GpuBufferResource *bufferRes : m_bufferResourceList) { if(bufferRes->Resource() == gpuBuffer.Resource()) { D3D12GpuBufferHeapMemoryFlag heapMem = gpuBuffer.HeapMemory(); if(heapMem == D3D12GpuBufferHeapMemoryFlag::Default) { if(bufferRes->SubAllocationInRange(gpuBuffer.Address())) { return bufferRes->Free(gpuBuffer.Address()); } } else if(heapMem == D3D12GpuBufferHeapMemoryFlag::Dedicated) { if(D3D12GpuBufferResource::ReleaseGpuBufferResource(bufferRes)) { m_bufferResourceList.removeOne(bufferRes); return true; } } } } } return false; } bool D3D12GpuBufferAllocator::Alloc(D3D12GpuBufferHeapType heapType, D3D12GpuBufferHeapMemoryFlag heapMem, uint64_t size, uint64_t alignment, D3D12GpuBuffer &gpuBuffer) { SCOPED_LOCK(m_bufferAllocLock); bool success = false; if(heapType < D3D12GpuBufferHeapType::Count && heapType != D3D12GpuBufferHeapType::UnInitialized) { size_t heap = (size_t)heapType; if(m_bufferPoolList[heap] == NULL) { uint64_t bufferPoolInitSize = D3D12GpuBufferPool::kDefaultWithUavSizeBufferInitSize; if(heapType == D3D12GpuBufferHeapType::AccStructDefaultHeap) { bufferPoolInitSize = D3D12GpuBufferPool::kAccStructBufferPoolInitSize; } m_bufferPoolList[heap] = new D3D12GpuBufferPool(heapType, bufferPoolInitSize); } if(m_bufferPoolList[heap] != NULL) { success = m_bufferPoolList[heap]->Alloc(m_wrappedDevice, heapMem, size, alignment, gpuBuffer); } } if(success) { m_totalAllocatedMemoryInUse += size; } return success; } bool D3D12GpuBufferAllocator::Release(const D3D12GpuBuffer &gpuBuffer) { SCOPED_LOCK(m_bufferAllocLock); size_t heap = (size_t)gpuBuffer.HeapType(); if(gpuBuffer.HeapType() < D3D12GpuBufferHeapType::Count && m_bufferPoolList[heap] != NULL) { return m_bufferPoolList[heap]->Free(gpuBuffer); } return false; } bool D3D12GpuBufferAllocator::CreateBufferResource(WrappedID3D12Device *wrappedDevice, D3D12GpuBufferHeapType heapType, uint64_t size, D3D12GpuBufferResource **bufferResource) { D3D12_HEAP_PROPERTIES heapProperty; heapProperty.CreationNodeMask = 0; heapProperty.VisibleNodeMask = 0; heapProperty.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProperty.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; D3D12_RESOURCE_STATES initState = D3D12_RESOURCE_STATE_COMMON; bool allowUav = false; switch(heapType) { case D3D12GpuBufferHeapType::AccStructDefaultHeap: case D3D12GpuBufferHeapType::DefaultHeap: case D3D12GpuBufferHeapType::DefaultHeapWithUav: { heapProperty.Type = D3D12_HEAP_TYPE_DEFAULT; if(heapType == D3D12GpuBufferHeapType::AccStructDefaultHeap) { initState = D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE; allowUav = true; } if(heapType == D3D12GpuBufferHeapType::DefaultHeapWithUav) { allowUav = true; } break; } case D3D12GpuBufferHeapType::ReadBackHeap: { heapProperty.Type = D3D12_HEAP_TYPE_READBACK; initState = D3D12_RESOURCE_STATE_COPY_DEST; break; } case D3D12GpuBufferHeapType::UploadHeap: { heapProperty.Type = D3D12_HEAP_TYPE_UPLOAD; initState = D3D12_RESOURCE_STATE_GENERIC_READ; break; } case D3D12GpuBufferHeapType::CustomHeapWithUavCpuAccess: { heapProperty.Type = D3D12_HEAP_TYPE_CUSTOM; heapProperty.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK; heapProperty.MemoryPoolPreference = D3D12_MEMORY_POOL_L0; allowUav = true; break; } default: RDCLOG("Unhandled buffer pool"); } D3D12GpuBufferResource *newBufferResource = NULL; if(D3D12GpuBufferResource::CreateCommittedResourceBuffer( wrappedDevice->GetReal(), heapProperty, initState, size, allowUav, &newBufferResource)) { *bufferResource = newBufferResource; return true; } return false; } void D3D12ResourceManager::ApplyBarriers(BarrierSet &barriers, std::map &states) { for(size_t b = 0; b < barriers.barriers.size(); b++) { const D3D12_RESOURCE_TRANSITION_BARRIER &trans = barriers.barriers[b].Transition; ResourceId id = GetResID(trans.pResource); auto it = states.find(id); if(it == states.end()) continue; SubresourceStateVector &st = it->second; // skip non-transitions, or begin-halves of transitions if(barriers.barriers[b].Type != D3D12_RESOURCE_BARRIER_TYPE_TRANSITION || (barriers.barriers[b].Flags & D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY)) continue; size_t first = trans.Subresource; if(trans.Subresource == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES) first = 0; for(size_t i = first; i < st.size(); i++) { // layout must either match StateBefore or else be in the common layout BARRIER_ASSERT("Mismatching before state", (st[i].IsStates() && st[i].ToStates() == trans.StateBefore) || (st[i].IsLayout() && st[i].ToLayout() == D3D12_BARRIER_LAYOUT_COMMON), st[i], trans.StateBefore, i); st[i] = D3D12ResourceLayout::FromStates(trans.StateAfter); if(trans.Subresource != D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES) break; } } for(size_t b = 0; b < barriers.newBarriers.size(); b++) { const D3D12_TEXTURE_BARRIER &trans = barriers.newBarriers[b]; ResourceId id = GetResID(trans.pResource); auto it = states.find(id); if(it == states.end()) continue; SubresourceStateVector &st = it->second; // skip begin-halves of split transitions if(trans.SyncBefore == D3D12_BARRIER_SYNC_SPLIT) continue; // skip non-layout barriers (including UNDEFINED-UNDEFINED) if(trans.LayoutBefore == trans.LayoutAfter) continue; if(trans.Subresources.NumMipLevels == 0) { size_t first = 0; if(trans.Subresources.IndexOrFirstMipLevel == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES) first = 0; for(size_t sub = first; sub < st.size(); sub++) { // layout must either match StateBefore, be undefined, or else be in the common state BARRIER_ASSERT("Mismatching before state", (st[sub].IsLayout() && st[sub].ToLayout() == trans.LayoutBefore) || trans.LayoutBefore == D3D12_BARRIER_LAYOUT_UNDEFINED || (st[sub].IsStates() && st[sub].ToStates() == D3D12_RESOURCE_STATE_COMMON), st[sub], trans.LayoutBefore, sub); st[sub] = D3D12ResourceLayout::FromLayout(trans.LayoutAfter); if(trans.Subresources.IndexOrFirstMipLevel != D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES) break; } } D3D12_RESOURCE_DESC desc = trans.pResource->GetDesc(); UINT arrays = RDCMAX((UINT16)1, desc.DepthOrArraySize); if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) arrays = 1; UINT mips = RDCMAX((UINT16)1, desc.MipLevels); for(UINT p = trans.Subresources.FirstPlane; p < trans.Subresources.NumPlanes; p++) { for(UINT a = trans.Subresources.FirstArraySlice; a < trans.Subresources.NumArraySlices; a++) { for(UINT m = trans.Subresources.IndexOrFirstMipLevel; m < trans.Subresources.NumMipLevels; m++) { UINT sub = ((p * arrays) + a) * mips + m; // layout must either match StateBefore, be undefined, or else be in the common state BARRIER_ASSERT( "Mismatching before state", (st[sub].IsLayout() && st[sub].ToLayout() == trans.LayoutBefore) || trans.LayoutBefore == D3D12_BARRIER_LAYOUT_UNDEFINED || (st[sub].IsStates() && st[sub].ToStates() == D3D12_RESOURCE_STATE_COMMON), st[sub], trans.LayoutBefore, sub); st[sub] = D3D12ResourceLayout::FromLayout(trans.LayoutAfter); } } } } } void AddStateResetBarrier(D3D12ResourceLayout srcState, D3D12ResourceLayout dstState, ID3D12Resource *res, UINT subresource, BarrierSet &barriers) { if(srcState.IsStates() && dstState.IsStates()) { D3D12_RESOURCE_BARRIER b; b.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; b.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; b.Transition.pResource = res; b.Transition.Subresource = (UINT)subresource; b.Transition.StateBefore = srcState.ToStates(); b.Transition.StateAfter = dstState.ToStates(); barriers.barriers.push_back(b); } else if(srcState.IsLayout() && dstState.IsLayout()) { D3D12_TEXTURE_BARRIER b = {}; b.LayoutBefore = srcState.ToLayout(); b.AccessBefore = D3D12_BARRIER_ACCESS_COMMON; b.SyncBefore = D3D12_BARRIER_SYNC_ALL; b.AccessAfter = D3D12_BARRIER_ACCESS_COMMON; b.SyncAfter = D3D12_BARRIER_SYNC_ALL; b.LayoutAfter = dstState.ToLayout(); if(b.LayoutBefore == D3D12_BARRIER_LAYOUT_UNDEFINED) b.AccessBefore = D3D12_BARRIER_ACCESS_NO_ACCESS; if(b.LayoutAfter == D3D12_BARRIER_LAYOUT_UNDEFINED) b.AccessAfter = D3D12_BARRIER_ACCESS_NO_ACCESS; b.Subresources.IndexOrFirstMipLevel = (UINT)subresource; b.pResource = res; barriers.newBarriers.push_back(b); } else { // difficult case, moving between barrier types and need to go to common in between if(srcState.IsStates()) { if(srcState.ToStates() != D3D12_RESOURCE_STATE_COMMON) { D3D12_RESOURCE_BARRIER b; b.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; b.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; b.Transition.pResource = res; b.Transition.Subresource = (UINT)subresource; b.Transition.StateBefore = srcState.ToStates(); b.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON; barriers.barriers.push_back(b); } { D3D12_TEXTURE_BARRIER b = {}; b.LayoutBefore = D3D12_BARRIER_LAYOUT_COMMON; b.AccessBefore = D3D12_BARRIER_ACCESS_COMMON; b.SyncBefore = D3D12_BARRIER_SYNC_ALL; b.AccessAfter = D3D12_BARRIER_ACCESS_COMMON; b.SyncAfter = D3D12_BARRIER_SYNC_ALL; b.LayoutAfter = dstState.ToLayout(); if(b.LayoutBefore == D3D12_BARRIER_LAYOUT_UNDEFINED) b.AccessBefore = D3D12_BARRIER_ACCESS_NO_ACCESS; if(b.LayoutAfter == D3D12_BARRIER_LAYOUT_UNDEFINED) b.AccessAfter = D3D12_BARRIER_ACCESS_NO_ACCESS; b.Subresources.IndexOrFirstMipLevel = (UINT)subresource; b.pResource = res; barriers.newBarriers.push_back(b); } } else { { D3D12_TEXTURE_BARRIER b = {}; b.LayoutBefore = srcState.ToLayout(); b.AccessBefore = D3D12_BARRIER_ACCESS_COMMON; b.SyncBefore = D3D12_BARRIER_SYNC_ALL; b.AccessAfter = D3D12_BARRIER_ACCESS_COMMON; b.SyncAfter = D3D12_BARRIER_SYNC_ALL; b.LayoutAfter = D3D12_BARRIER_LAYOUT_COMMON; b.Subresources.IndexOrFirstMipLevel = (UINT)subresource; b.pResource = res; barriers.newBarriers.push_back(b); } if(dstState.ToStates() != D3D12_RESOURCE_STATE_COMMON) { D3D12_RESOURCE_BARRIER b; b.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; b.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; b.Transition.pResource = res; b.Transition.Subresource = (UINT)subresource; b.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON; b.Transition.StateAfter = dstState.ToStates(); barriers.newToOldBarriers.push_back(b); } } } } template void D3D12ResourceManager::SerialiseResourceStates( SerialiserType &ser, BarrierSet &barriers, std::map &states, const std::map &initialStates) { SERIALISE_ELEMENT_LOCAL(NumMems, (uint32_t)states.size()); auto srcit = states.begin(); std::unordered_set processed; for(uint32_t i = 0; i < NumMems; i++) { SERIALISE_ELEMENT_LOCAL(Resource, srcit->first).TypedAs("ID3D12Resource *"_lit); SERIALISE_ELEMENT_LOCAL(States, srcit->second); ResourceId liveid; if(IsReplayingAndReading() && HasLiveResource(Resource)) liveid = GetLiveID(Resource); if(IsReplayingAndReading() && liveid != ResourceId()) { processed.insert(liveid); for(size_t m = 0; m < States.size(); m++) { D3D12ResourceLayout srcState = states[liveid][m]; D3D12ResourceLayout dstState = States[m]; // because of some extreme ugliness on the D3D12 side, resources can be created in new // layouts without the new barriers actually being supported. If that's the case, we just // pretend they're in old COMMON to avoid doing the new barrier if(!m_Device->GetOpts12().EnhancedBarriersSupported && srcState.IsLayout()) { RDCASSERT(srcState.ToLayout() == D3D12_BARRIER_LAYOUT_COMMON, srcState.ToLayout()); srcState = D3D12ResourceLayout::FromStates(D3D12_RESOURCE_STATE_COMMON); } if(!m_Device->GetOpts12().EnhancedBarriersSupported && dstState.IsLayout()) { RDCASSERT(dstState.ToLayout() == D3D12_BARRIER_LAYOUT_COMMON, dstState.ToLayout()); dstState = D3D12ResourceLayout::FromStates(D3D12_RESOURCE_STATE_COMMON); } if(srcState != dstState) { AddStateResetBarrier(srcState, dstState, (ID3D12Resource *)GetCurrentResource(liveid), (UINT)m, barriers); } } } if(ser.IsWriting()) srcit++; } // for any resources that didn't have a recorded state, use the initialStates we're given and // restore them if needed if(IsReplayingAndReading()) { for(auto it = initialStates.begin(); it != initialStates.end(); ++it) { // ignore internal resources, we only care about restoring states for captured resources if(GetOriginalID(it->first) == it->first) continue; if(processed.find(it->first) == processed.end()) { for(size_t m = 0; m < it->second.size(); m++) { const D3D12ResourceLayout srcState = states[it->first][m]; const D3D12ResourceLayout dstState = it->second[m]; if(srcState != dstState) AddStateResetBarrier(srcState, dstState, (ID3D12Resource *)GetCurrentResource(it->first), (UINT)m, barriers); } } } } ApplyBarriers(barriers, states); } template void D3D12ResourceManager::SerialiseResourceStates( ReadSerialiser &ser, BarrierSet &barriers, std::map &states, const std::map &initialStates); template void D3D12ResourceManager::SerialiseResourceStates( WriteSerialiser &ser, BarrierSet &barriers, std::map &states, const std::map &initialStates); void D3D12ResourceManager::SetInternalResource(ID3D12DeviceChild *res) { if(!RenderDoc::Inst().IsReplayApp() && res) { D3D12ResourceRecord *record = GetResourceRecord(GetResID(res)); if(record) record->InternalResource = true; } } ResourceId D3D12ResourceManager::GetID(ID3D12DeviceChild *res) { return GetResID(res); } bool D3D12ResourceManager::ResourceTypeRelease(ID3D12DeviceChild *res) { if(res) res->Release(); return true; } void GPUAddressRangeTracker::AddTo(const GPUAddressRange &range) { SCOPED_WRITELOCK(addressLock); auto it = std::lower_bound(addresses.begin(), addresses.end(), range.start); addresses.insert(it - addresses.begin(), range); } void GPUAddressRangeTracker::RemoveFrom(const GPUAddressRange &range) { { SCOPED_WRITELOCK(addressLock); size_t i = std::lower_bound(addresses.begin(), addresses.end(), range.start) - addresses.begin(); // there might be multiple buffers with the same range start, find the exact range for this // buffer while(i < addresses.size() && addresses[i].start == range.start) { if(addresses[i].id == range.id) { addresses.erase(i); return; } ++i; } } RDCERR("Couldn't find matching range to remove for %s", ToStr(range.id).c_str()); } void GPUAddressRangeTracker::GetResIDFromAddr(D3D12_GPU_VIRTUAL_ADDRESS addr, ResourceId &id, UINT64 &offs) { id = ResourceId(); offs = 0; if(addr == 0) return; GPUAddressRange range; // this should really be a read-write lock { SCOPED_READLOCK(addressLock); auto it = std::lower_bound(addresses.begin(), addresses.end(), addr); if(it == addresses.end()) return; range = *it; } if(addr < range.start || addr >= range.realEnd) return; id = range.id; offs = addr - range.start; } void GPUAddressRangeTracker::GetResIDFromAddrAllowOutOfBounds(D3D12_GPU_VIRTUAL_ADDRESS addr, ResourceId &id, UINT64 &offs) { id = ResourceId(); offs = 0; if(addr == 0) return; GPUAddressRange range; // this should really be a read-write lock { SCOPED_READLOCK(addressLock); auto it = std::lower_bound(addresses.begin(), addresses.end(), addr); if(it == addresses.end()) return; range = *it; } if(addr < range.start) return; // still enforce the OOB end on ranges - which is the remaining range in the backing store. // Otherwise we could end up passing through invalid addresses stored in stale descriptors if(addr >= range.oobEnd) return; id = range.id; offs = addr - range.start; } bool D3D12GpuBuffer::Release() { bool success = D3D12GpuBufferAllocator::Inst()->Release(*this); if(success) { *this = {}; } return success; }