vector<ResourceId> D3D12RenderState::GetRTVIDs() const { vector<ResourceId> ret; if(rtSingle) { if(!rts.empty()) { const D3D12Descriptor *descs = DescriptorFromPortableHandle(GetResourceManager(), rts[0]); for(UINT i = 0; i < rts.size(); i++) { RDCASSERT(descs[i].GetType() == D3D12Descriptor::TypeRTV); ret.push_back(GetResID(descs[i].nonsamp.resource)); } } } else { for(UINT i = 0; i < rts.size(); i++) { WrappedID3D12DescriptorHeap *heap = GetResourceManager()->GetLiveAs<WrappedID3D12DescriptorHeap>(rts[0].heap); const D3D12Descriptor &desc = heap->GetDescriptors()[rts[i].index]; RDCASSERT(desc.GetType() == D3D12Descriptor::TypeRTV); ret.push_back(GetResID(desc.nonsamp.resource)); } } return ret; }
D3D12_GPU_DESCRIPTOR_HANDLE GPUHandleFromPortableHandle(D3D12ResourceManager *manager, PortableHandle handle) { if(handle.heap == ResourceId()) return D3D12_GPU_DESCRIPTOR_HANDLE(); WrappedID3D12DescriptorHeap *heap = manager->GetLiveAs<WrappedID3D12DescriptorHeap>(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; if(!manager->HasLiveResource(handle.heap)) return NULL; WrappedID3D12DescriptorHeap *heap = manager->GetLiveAs<WrappedID3D12DescriptorHeap>(handle.heap); if(heap) return heap->GetDescriptors() + handle.index; return NULL; }
HRESULT WrappedID3D12Device::CreateDescriptorHeap(const D3D12_DESCRIPTOR_HEAP_DESC *pDescriptorHeapDesc, REFIID riid, void **ppvHeap) { if(ppvHeap == NULL) return m_pDevice->CreateDescriptorHeap(pDescriptorHeapDesc, riid, NULL); if(riid != __uuidof(ID3D12DescriptorHeap)) return E_NOINTERFACE; ID3D12DescriptorHeap *real = NULL; HRESULT ret = m_pDevice->CreateDescriptorHeap(pDescriptorHeapDesc, riid, (void **)&real); if(SUCCEEDED(ret)) { SCOPED_LOCK(m_D3DLock); WrappedID3D12DescriptorHeap *wrapped = new WrappedID3D12DescriptorHeap(real, this, *pDescriptorHeapDesc); if(m_State >= WRITING) { SCOPED_SERIALISE_CONTEXT(CREATE_DESCRIPTOR_HEAP); Serialise_CreateDescriptorHeap(pDescriptorHeapDesc, riid, (void **)&wrapped); D3D12ResourceRecord *record = GetResourceManager()->AddResourceRecord(wrapped->GetResourceID()); record->type = Resource_DescriptorHeap; record->Length = 0; wrapped->SetResourceRecord(record); record->AddChunk(scope.Get()); { SCOPED_LOCK(m_CapTransitionLock); if(m_State != WRITING_CAPFRAME) GetResourceManager()->MarkDirtyResource(wrapped->GetResourceID()); else GetResourceManager()->MarkPendingDirty(wrapped->GetResourceID()); } } else { GetResourceManager()->AddLiveResource(wrapped->GetResourceID(), wrapped); } *ppvHeap = (ID3D12DescriptorHeap *)wrapped; } return ret; }
bool D3D12ResourceManager::Serialise_InitialState(ResourceId resid, ID3D12DeviceChild *liveRes) { D3D12ResourceRecord *record = NULL; if(m_State >= WRITING) record = GetResourceRecord(resid); SERIALISE_ELEMENT(ResourceId, id, resid); SERIALISE_ELEMENT(D3D12ResourceType, type, record->type); if(m_State >= WRITING) { D3D12ResourceManager::InitialContentData initContents = GetInitialContents(id); if(type == Resource_DescriptorHeap) { D3D12Descriptor *descs = (D3D12Descriptor *)initContents.blob; uint32_t numElems = initContents.num; m_pSerialiser->SerialiseComplexArray("Descriptors", descs, numElems); } else if(type == Resource_Resource) { m_Device->ExecuteLists(); m_Device->FlushLists(); ID3D12Resource *copiedBuffer = (ID3D12Resource *)initContents.resource; if(initContents.num == 1) { copiedBuffer = (ID3D12Resource *)liveRes; } if(initContents.num == 2) { D3D12NOTIMP("Multisampled initial contents"); return true; } byte dummy[4] = {}; byte *ptr = NULL; uint64_t size = 0; HRESULT hr = E_NOINTERFACE; if(copiedBuffer) { hr = copiedBuffer->Map(0, NULL, (void **)&ptr); size = (uint64_t)copiedBuffer->GetDesc().Width; } if(FAILED(hr) || ptr == NULL) { size = 4; ptr = dummy; RDCERR("Failed to map buffer for readback! 0x%08x", hr); } m_pSerialiser->Serialise("NumBytes", size); size_t sz = (size_t)size; m_pSerialiser->SerialiseBuffer("BufferData", ptr, sz); if(SUCCEEDED(hr) && ptr) copiedBuffer->Unmap(0, NULL); return true; } else { RDCERR("Unexpected type needing an initial state serialised out: %d", type); return false; } } else { ID3D12DeviceChild *res = GetLiveResource(id); RDCASSERT(res != NULL); ResourceId liveid = GetLiveID(id); if(type == Resource_DescriptorHeap) { WrappedID3D12DescriptorHeap *heap = (WrappedID3D12DescriptorHeap *)res; uint32_t numElems = 0; D3D12Descriptor *descs = NULL; m_pSerialiser->SerialiseComplexArray("Descriptors", descs, numElems); D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc(); // this heap doesn't have to be shader visible, we just use it to copy from desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; ID3D12DescriptorHeap *copyheap = NULL; HRESULT hr = m_Device->GetReal()->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), (void **)©heap); if(FAILED(hr)) { RDCERR("Failed to create CPU descriptor heap for initial state: 0x%08x", hr); return false; } copyheap = new WrappedID3D12DescriptorHeap(copyheap, m_Device, desc); D3D12_CPU_DESCRIPTOR_HANDLE handle = copyheap->GetCPUDescriptorHandleForHeapStart(); UINT increment = m_Device->GetDescriptorHandleIncrementSize(desc.Type); for(uint32_t i = 0; i < numElems; i++) { descs[i].Create(desc.Type, m_Device, handle); handle.ptr += increment; } SAFE_DELETE_ARRAY(descs); SetInitialContents(id, D3D12ResourceManager::InitialContentData(copyheap, 0, NULL)); } else if(type == Resource_Resource) { D3D12_RESOURCE_DESC resDesc = ((ID3D12Resource *)res)->GetDesc(); if(resDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D && resDesc.SampleDesc.Count > 1) { D3D12NOTIMP("Multisampled initial contents"); return true; } uint64_t size = 0; m_pSerialiser->Serialise("NumBytes", size); D3D12_HEAP_PROPERTIES heapProps; heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProps.CreationNodeMask = 1; heapProps.VisibleNodeMask = 1; D3D12_RESOURCE_DESC desc; desc.Alignment = 0; desc.DepthOrArraySize = 1; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Flags = D3D12_RESOURCE_FLAG_NONE; desc.Format = DXGI_FORMAT_UNKNOWN; desc.Height = 1; desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; desc.MipLevels = 1; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Width = size; ID3D12Resource *copySrc = NULL; HRESULT hr = m_Device->GetReal()->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, __uuidof(ID3D12Resource), (void **)©Src); if(FAILED(hr)) { RDCERR("Couldn't create upload buffer: 0x%08x", hr); return false; } byte *ptr = NULL; hr = copySrc->Map(0, NULL, (void **)&ptr); if(FAILED(hr)) { RDCERR("Couldn't map upload buffer: 0x%08x", hr); ptr = NULL; } size_t sz = (size_t)size; m_pSerialiser->SerialiseBuffer("BufferData", ptr, sz); if(SUCCEEDED(hr)) copySrc->Unmap(0, NULL); else SAFE_DELETE_ARRAY(ptr); SetInitialContents(id, D3D12ResourceManager::InitialContentData(copySrc, 1, NULL)); return true; } else { RDCERR("Unexpected type needing an initial state serialised in: %d", type); return false; } } return true; }
bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res) { ResourceId id = GetResID(res); D3D12ResourceType type = IdentifyTypeByPtr(res); if(type == Resource_DescriptorHeap) { WrappedID3D12DescriptorHeap *heap = (WrappedID3D12DescriptorHeap *)res; UINT numElems = heap->GetDesc().NumDescriptors; D3D12Descriptor *descs = (D3D12Descriptor *)Serialiser::AllocAlignedBuffer(sizeof(D3D12Descriptor) * numElems); memcpy(descs, heap->GetDescriptors(), sizeof(D3D12Descriptor) * numElems); SetInitialContents(heap->GetResourceID(), D3D12ResourceManager::InitialContentData(NULL, numElems, (byte *)descs)); return true; } else if(type == Resource_Resource) { WrappedID3D12Resource *r = (WrappedID3D12Resource *)res; ID3D12Pageable *pageable = r; bool nonresident = false; if(!r->Resident()) nonresident = true; D3D12_RESOURCE_DESC desc = r->GetDesc(); if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D && desc.SampleDesc.Count > 1) { D3D12NOTIMP("Multisampled initial contents"); SetInitialContents(GetResID(r), D3D12ResourceManager::InitialContentData(NULL, 2, NULL)); return true; } else if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) { D3D12_HEAP_PROPERTIES heapProps; r->GetHeapProperties(&heapProps, NULL); if(heapProps.Type == D3D12_HEAP_TYPE_READBACK) { // already on readback heap, just mark that we can map it directly and continue SetInitialContents(GetResID(r), D3D12ResourceManager::InitialContentData(NULL, 1, NULL)); return true; } heapProps.Type = D3D12_HEAP_TYPE_READBACK; heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProps.CreationNodeMask = 1; heapProps.VisibleNodeMask = 1; desc.Flags = D3D12_RESOURCE_FLAG_NONE; ID3D12Resource *copyDst = NULL; HRESULT hr = m_Device->GetReal()->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, __uuidof(ID3D12Resource), (void **)©Dst); if(nonresident) m_Device->MakeResident(1, &pageable); if(SUCCEEDED(hr)) { ID3D12GraphicsCommandList *list = Unwrap(m_Device->GetInitialStateList()); list->CopyResource(copyDst, r->GetReal()); } else { RDCERR("Couldn't create readback buffer: 0x%08x", hr); } if(nonresident) { m_Device->CloseInitialStateList(); m_Device->ExecuteLists(); m_Device->FlushLists(); m_Device->Evict(1, &pageable); } SetInitialContents(GetResID(r), D3D12ResourceManager::InitialContentData(copyDst, 0, NULL)); return true; } else { D3D12_HEAP_PROPERTIES heapProps; heapProps.Type = D3D12_HEAP_TYPE_READBACK; heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProps.CreationNodeMask = 1; heapProps.VisibleNodeMask = 1; D3D12_RESOURCE_DESC bufDesc; bufDesc.Alignment = 0; bufDesc.DepthOrArraySize = 1; bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; bufDesc.Flags = D3D12_RESOURCE_FLAG_NONE; bufDesc.Format = DXGI_FORMAT_UNKNOWN; bufDesc.Height = 1; bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; bufDesc.MipLevels = 1; bufDesc.SampleDesc.Count = 1; bufDesc.SampleDesc.Quality = 0; bufDesc.Width = 1; UINT numSubresources = desc.MipLevels; if(desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D) numSubresources *= desc.DepthOrArraySize; D3D12_PLACED_SUBRESOURCE_FOOTPRINT *layouts = new D3D12_PLACED_SUBRESOURCE_FOOTPRINT[numSubresources]; m_Device->GetCopyableFootprints(&desc, 0, numSubresources, 0, layouts, NULL, NULL, &bufDesc.Width); ID3D12Resource *copyDst = NULL; HRESULT hr = m_Device->GetReal()->CreateCommittedResource( &heapProps, D3D12_HEAP_FLAG_NONE, &bufDesc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, __uuidof(ID3D12Resource), (void **)©Dst); if(nonresident) m_Device->MakeResident(1, &pageable); if(SUCCEEDED(hr)) { ID3D12GraphicsCommandList *list = Unwrap(m_Device->GetInitialStateList()); vector<D3D12_RESOURCE_BARRIER> barriers; const vector<D3D12_RESOURCE_STATES> &states = m_Device->GetSubresourceStates(GetResID(r)); barriers.reserve(states.size()); for(size_t i = 0; i < states.size(); i++) { if(states[i] & D3D12_RESOURCE_STATE_COPY_SOURCE) continue; D3D12_RESOURCE_BARRIER barrier; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; barrier.Transition.pResource = r->GetReal(); barrier.Transition.Subresource = (UINT)i; barrier.Transition.StateBefore = states[i]; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; barriers.push_back(barrier); } // transition to copy dest if(!barriers.empty()) list->ResourceBarrier((UINT)barriers.size(), &barriers[0]); for(UINT i = 0; i < numSubresources; i++) { D3D12_TEXTURE_COPY_LOCATION dst, src; src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; src.pResource = r->GetReal(); src.SubresourceIndex = i; dst.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dst.pResource = copyDst; dst.PlacedFootprint = layouts[i]; list->CopyTextureRegion(&dst, 0, 0, 0, &src, NULL); } // transition back for(size_t i = 0; i < barriers.size(); i++) std::swap(barriers[i].Transition.StateBefore, barriers[i].Transition.StateAfter); if(!barriers.empty()) list->ResourceBarrier((UINT)barriers.size(), &barriers[0]); } else { RDCERR("Couldn't create readback buffer: 0x%08x", hr); } if(nonresident) { m_Device->CloseInitialStateList(); m_Device->ExecuteLists(); m_Device->FlushLists(); m_Device->Evict(1, &pageable); } SAFE_DELETE_ARRAY(layouts); SetInitialContents(GetResID(r), D3D12ResourceManager::InitialContentData(copyDst, 0, NULL)); return true; } } else { RDCERR("Unexpected type needing an initial state prepared: %d", type); } return false; }
void PreDraw(uint32_t eid, ID3D12GraphicsCommandList *cmd) { if(std::find(m_Events.begin(), m_Events.end(), eid) == m_Events.end()) return; // we customise the pipeline to disable framebuffer writes, but perform normal testing // and substitute our quad calculation fragment shader that writes to a storage image // that is bound in a new root signature element. D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState; m_PrevState = rs; // check cache first CachedPipeline cache = m_PipelineCache[rs.pipe]; // if we don't get a hit, create a modified pipeline if(cache.pipe == NULL) { HRESULT hr = S_OK; WrappedID3D12RootSignature *sig = m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12RootSignature>( rs.graphics.rootsig); // need to be able to add a descriptor table with our UAV without hitting the 64 DWORD limit RDCASSERT(sig->sig.dwordLength < 64); D3D12RootSignature modsig = sig->sig; // make sure no other UAV tables overlap. We can't remove elements entirely because then the // root signature indices wouldn't match up as expected. // Instead move them into an unused space. for(size_t i = 0; i < modsig.params.size(); i++) { if(modsig.params[i].ShaderVisibility == D3D12_SHADER_VISIBILITY_PIXEL) { if(modsig.params[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_UAV) { modsig.params[i].Descriptor.RegisterSpace = modsig.numSpaces; } else if(modsig.params[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) { for(size_t r = 0; r < modsig.params[i].ranges.size(); r++) { modsig.params[i].ranges[r].RegisterSpace = modsig.numSpaces; } } } } D3D12_DESCRIPTOR_RANGE1 range; range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; range.NumDescriptors = 1; range.BaseShaderRegister = 0; range.RegisterSpace = 0; range.Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE; range.OffsetInDescriptorsFromTableStart = 0; modsig.params.push_back(D3D12RootSignatureParameter()); D3D12RootSignatureParameter ¶m = modsig.params.back(); param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; param.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; param.DescriptorTable.NumDescriptorRanges = 1; param.DescriptorTable.pDescriptorRanges = ⦥ cache.sigElem = uint32_t(modsig.params.size() - 1); std::vector<D3D12_ROOT_PARAMETER1> params; params.resize(modsig.params.size()); for(size_t i = 0; i < params.size(); i++) params[i] = modsig.params[i]; ID3DBlob *root = m_pDevice->GetShaderCache()->MakeRootSig(modsig); hr = m_pDevice->CreateRootSignature(0, root->GetBufferPointer(), root->GetBufferSize(), __uuidof(ID3D12RootSignature), (void **)&cache.sig); RDCASSERTEQUAL(hr, S_OK); SAFE_RELEASE(root); WrappedID3D12PipelineState *origPSO = m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12PipelineState>(rs.pipe); RDCASSERT(origPSO->IsGraphics()); D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeDesc = origPSO->GetGraphicsDesc(); for(size_t i = 0; i < ARRAY_COUNT(pipeDesc.BlendState.RenderTarget); i++) pipeDesc.BlendState.RenderTarget[i].RenderTargetWriteMask = 0; // disable depth/stencil writes pipeDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO; pipeDesc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; pipeDesc.DepthStencilState.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; pipeDesc.DepthStencilState.StencilWriteMask = 0; pipeDesc.PS = m_QuadWritePS; pipeDesc.pRootSignature = cache.sig; hr = m_pDevice->CreateGraphicsPipelineState(&pipeDesc, __uuidof(ID3D12PipelineState), (void **)&cache.pipe); RDCASSERTEQUAL(hr, S_OK); m_PipelineCache[rs.pipe] = cache; } // modify state for first draw call rs.pipe = GetResID(cache.pipe); rs.graphics.rootsig = GetResID(cache.sig); if(rs.graphics.sigelems.size() <= cache.sigElem) rs.graphics.sigelems.resize(cache.sigElem + 1); PortableHandle uav = m_UAV; // if a CBV_SRV_UAV heap is already set, we need to copy our descriptor in // if we haven't already. Otherwise we can set our own heap. for(size_t i = 0; i < rs.heaps.size(); i++) { WrappedID3D12DescriptorHeap *h = m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12DescriptorHeap>(rs.heaps[i]); if(h->GetDesc().Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) { // use the last descriptor D3D12_CPU_DESCRIPTOR_HANDLE dst = h->GetCPUDescriptorHandleForHeapStart(); dst.ptr += (h->GetDesc().NumDescriptors - 1) * sizeof(D3D12Descriptor); if(m_CopiedHeaps.find(rs.heaps[i]) == m_CopiedHeaps.end()) { WrappedID3D12DescriptorHeap *h2 = m_pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12DescriptorHeap>(m_UAV.heap); D3D12_CPU_DESCRIPTOR_HANDLE src = h2->GetCPUDescriptorHandleForHeapStart(); src.ptr += m_UAV.index * sizeof(D3D12Descriptor); // can't do a copy because the src heap is CPU write-only (shader visible). So instead, // create directly D3D12Descriptor *srcDesc = (D3D12Descriptor *)src.ptr; srcDesc->Create(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_pDevice, dst); m_CopiedHeaps.insert(rs.heaps[i]); } uav = ToPortableHandle(dst); break; } } if(uav.heap == m_UAV.heap) rs.heaps.push_back(m_UAV.heap); rs.graphics.sigelems[cache.sigElem] = D3D12RenderState::SignatureElement(eRootTable, uav.heap, uav.index); // as we're changing the root signature, we need to reapply all elements, // so just apply all state if(cmd) rs.ApplyState(cmd); }