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); }