2017-03-02 78 views
0

我嘗試使用ExecuteIndirect繪製我的情況下,當上AMD GPU墜毀。ExecuteIndirect擴大間接命令緩衝區

這裏是我的代碼:

struct IndirectCommand 
{ 
    D3D12_GPU_VIRTUAL_ADDRESS materialBufferAddress; 
    D3D12_GPU_VIRTUAL_ADDRESS instanceBufferAddress; 
    D3D12_DRAW_INDEXED_ARGUMENTS drawArguments; 
}; // byte stride: 40 

// code for initializing command signature 
void InstanceManager::InitIndirectBuffer() 
{ 
    D3D12_INDIRECT_ARGUMENT_DESC indirectDescs[3] = {}; 
    indirectDescs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW; 
    indirectDescs[0].ConstantBufferView.RootParameterIndex = cMaterialPass; 
    indirectDescs[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_SHADER_RESOURCE_VIEW; 
    indirectDescs[1].ShaderResourceView.RootParameterIndex = cInstancePass; 
    indirectDescs[2].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED; 

    D3D12_COMMAND_SIGNATURE_DESC commandSignatureDesc = {}; 
    commandSignatureDesc.pArgumentDescs = indirectDescs; 
    commandSignatureDesc.NumArgumentDescs = _countof(indirectDescs); 
    commandSignatureDesc.ByteStride = sizeof(IndirectCommand); 

    ThrowIfFailed(Engine::GetApp()->GetDevice()->CreateCommandSignature(&commandSignatureDesc, Engine::GetApp()->GetRootSignature(), IID_PPV_ARGS(&mCommandSignature))); 
} 

追加間接命令緩衝區:

void AppendIndirectCommandBuffer() 
{ 
    // wait for gpu 
    Engine::GetApp()->ResetCommandList(); 
    Engine::GetApp()->ExecuteCommand(); 
    Engine::GetApp()->FlushCommandQueue(); 

    for (int i = 0; i < gNumFrameResources; i++) 
    { 
     // alloc upload heap 
     Engine::GetApp()->GetFrameManager().GetFrameResource(i)->AppendUploadBuffer<IndirectCommand>(mIndirectBufferUpload[i] 
      , mLastIndirectUploadCount[i] 
      , mIndirectCount 
      , false); 

     // alloc default heap 
     Engine::GetApp()->GetFrameManager().GetFrameResource(i)->AppendDefaultBuffer<IndirectCommand>(mIndirectBufferDefault[i] 
      , mLastIndirectDefaultCount[i] 
      , mIndirectCount++ 
      , false); 
    } 
    UpdateIndirectData(_obj); 
    // code for appending indirect command buffer 
} 

更新間接命令緩衝區的數據:

void UpdateIndirectData(GameObject _obj) 
{ 
    UINT matCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(MaterialData)); 
    UINT insCBByteSize = sizeof(InstanceData); 

    for (int i = 0; i < gNumFrameResources; i++) 
    { 
     if (Engine::GetApp()->GetFrameManager().GetFrameResource(i) != NULL) 
     { 
      string _matName = _obj->GetComponent<RenderObject>()->GetMaterialName(); 
      string _geoName = _obj->GetComponent<RenderObject>()->GetGeometryName(); 
      Material *mat = Engine::GetApp()->GetMaterialManager().GetMaterial(_matName); 

      if (mat != nullptr) 
      { 
       IndirectCommand data; 

       data.materialBufferAddress = Engine::GetApp()->GetMaterialManager().GetMaterialBuffer(i)->Resource()->GetGPUVirtualAddress() 
       + matCBByteSize*mat->GetMatBufferIndex(); 

       data.instanceBufferAddress = mInstanceBuffer[i]->Resource()->GetGPUVirtualAddress() + mInstanceIndex[_obj->GetID()] * insCBByteSize; 

       data.drawArguments.BaseVertexLocation = mDrawArgs[_geoName].BaseVertexLocation; 
       data.drawArguments.IndexCountPerInstance = mDrawArgs[_geoName].IndexCount; 
       data.drawArguments.StartIndexLocation = mDrawArgs[_geoName].StartIndexLocation; 
       data.drawArguments.StartInstanceLocation = 0; 
       data.drawArguments.InstanceCount = 1; 

       int indirectIndex = mIndirectIndex[_obj->GetID()]; 
       mIndirectCommand[indirectIndex] = data; // an array of indirect command, size is set to 1000 temporarily 

       // copy to default heap 
       D3D12_SUBRESOURCE_DATA commandData = {}; 
       commandData.pData = reinterpret_cast<UINT8*>(&mIndirectCommand[0]); 
       commandData.RowPitch = sizeof(IndirectCommand) * mIndirectCount; 
       commandData.SlicePitch = commandData.RowPitch; 
       UpdateSubresources<1U>(Engine::GetApp()->GetCommandList(), mIndirectBufferDefault[i]->Resource(), mIndirectBufferUpload[i]->Resource(), 0, 0, 1, &commandData); 
      } 
     } 
    } 
} 

末,executeindirect

void DrawIndirectInstance() 
{ 
    Engine::GetApp()->GetCommandList()->IASetVertexBuffers(0, 1, &mGeometry->VertexBufferView()); 
    Engine::GetApp()->GetCommandList()->IASetIndexBuffer(&mGeometry->IndexBufferView16()); 
    Engine::GetApp()->GetCommandList()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 

    // just set a pso for testing, I haven't group my instances by material yet 
    ID3D12PipelineState *pso = Engine::GetApp()->GetMaterialManager().GetMaterialPSO("test"); 
    Engine::GetApp()->GetCommandList()->SetPipelineState(pso); 

    int frameIndex = Engine::GetApp()->GetFrameManager().GetCurrFrameIndex(); 
    auto indirectBuffer = mIndirectBufferUpload[frameIndex].get(); 

    auto materialBuffer = Engine::GetApp()->GetMaterialManager().GetMaterialBuffer(frameIndex); 
    Engine::GetApp()->GetCommandList()->SetGraphicsRootConstantBufferView(cMaterialPass, materialBuffer->Resource()->GetGPUVirtualAddress()); 
    Engine::GetApp()->GetCommandList()->SetGraphicsRootShaderResourceView(cInstancePass, mInstanceBuffer[frameIndex]->Resource()->GetGPUVirtualAddress()); 

    Engine::GetApp()->GetCommandList()->ResourceBarrier(1 
    , &CD3DX12_RESOURCE_BARRIER::Transition(indirectBuffer->Resource(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT)); 

    Engine::GetApp()->GetCommandList()->ExecuteIndirect(mCommandSignature.Get() 
    , mIndirectCount 
    , indirectBuffer->Resource() 
    , 0 
    , nullptr 
    , 0); 

    Engine::GetApp()->GetCommandList()->ResourceBarrier(1 
    , &CD3DX12_RESOURCE_BARRIER::Transition(indirectBuffer->Resource(), D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT, D3D12_RESOURCE_STATE_COPY_DEST)); 
} 

我使用我的SceneManager加載場景。如果一個gameobject是一個渲染對象。我的系統將調用AppendIndirectCommandBuffer()並將數據複製到間接命令緩衝區中。

ExecuteIndirect()會是不錯的,如果我沒有我的場景的初始化之後克隆我的遊戲對象。

我嘗試在運行時克隆我的遊戲對象(在我的更新())。再次

我的系統調用AppendIndirectCommandBuffer()調整間接命令緩衝區和複製新的數據緩衝。

克隆遊戲物體幾秒鐘後,我的系統開始毛刺,並導致TDR我R9 380

但上正常工作經線和Intel GPU。

而且我不知道爲什麼:(。

如何解決這個問題呢?我真的想好好利用間接繪製的。

謝謝!

更新

上傳堆資源無法傳輸到D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT。

我創建一個默認的類型堆,並使用上傳堆將數據複製到它。

我試着調試層,沒有錯誤是從它返回。 我試圖肝炎病毒,只獲得了兩個錯誤:

D3D12 ERROR: GPU-BASED VALIDATION: Draw, Uninitialized root argument accessed. Shader Stage: VERTEX, Root Parameter Index: [2], Draw Index: [0], Shader Code: Forward.hlsl(21,2-42), Asm Instruction Range: [0x114-0x13b], Asm Operand Index: [3], Command List: 0x00000134BB3C1EE0:'Unnamed ID3D12GraphicsCommandList Object', SRV/UAV/CBV Descriptor Heap: 0x00000134AE6DD020:'Unnamed ID3D12DescriptorHeap Object', Sampler Descriptor Heap: <not set>, Pipeline State: 0x00000134BB5CB540:'Unnamed ID3D12PipelineState Object', [ EXECUTION ERROR #935: GPU_BASED_VALIDATION_ROOT_ARGUMENT_UNINITIALIZED] 
D3D12 ERROR: GPU-BASED VALIDATION: Draw, Uninitialized root argument accessed. Shader Stage: VERTEX, Root Parameter Index: [1], Draw Index: [0], Shader Code: Forward.hlsl(37,2-41), Asm Instruction Range: [0x8c0-0x8e3], Asm Operand Index: [2], Command List: 0x000001D0D7B70860:'Unnamed ID3D12GraphicsCommandList Object', SRV/UAV/CBV Descriptor Heap: 0x000001D0D7AC6C80:'Unnamed ID3D12DescriptorHeap Object', Sampler Descriptor Heap: <not set>, Pipeline State: 0x000001D0D7BBF450:'Unnamed ID3D12PipelineState Object', [ EXECUTION ERROR #935: GPU_BASED_VALIDATION_ROOT_ARGUMENT_UNINITIALIZED] 

併發出警告:

D3D12 WARNING: ID3D12CommandList::ExecuteIndirect: GPU-based validation is not supported for ExecuteIndirect that changes root bindings. All further GPU-based validation output may not be reliable. [ EXECUTION WARNING #1000: GPU_BASED_VALIDATION_UNSUPPORTED] 

後,我撥打以下功能,這兩種錯誤都沒有了。

Engine::GetApp()->GetCommandList()->SetGraphicsRootConstantBufferView(cMaterialPass, materialBuffer->Resource()->GetGPUVirtualAddress()); 
Engine::GetApp()->GetCommandList()->SetGraphicsRootShaderResourceView(cInstancePass, mInstanceBuffer[frameIndex]->Resource()->GetGPUVirtualAddress()); 

儘管進行了這些修改,仍然無法正常工作。

+0

你有沒有嘗試基於調試層和gpu的驗證。在您的代碼中,我沒有看到狀態轉換爲間接參數,這是GCN硬件上某些緩存刷新所必需的。 – galop1n

+0

這是DirectX 12提供的低級暴露級別的成本:您需要能夠在一套硬件中對您的特定應用程序進行故障排除和驗證。與嘗試使用WARP12一樣,Direct3D調試層始終是一個很好的起點,但實際上,您需要有大量不同的計算機來嘗試診斷問題是驅動程序還是應用程序,然後與硬件供應商聯繫試用一個測試版驅動程序。參見[Direct3D 12創建設備剖析](https://blogs.msdn.microsoft.com/chuckw/2016/08/16/anatomy-of-direct3d-12-create-device/)。 –

+0

也要確保你有硬件功能層的混合,因爲這也可以有很大的不同。我在[DirectX Tool Kit for DirectX 12](https://github.com/Microsoft/DirectXTK12)中遇到了很多問題,它們只在第1層資源綁定系統上重現,但在第2層或更好的情況下可以正常工作。這很大程度上是爲什麼在真正推動DirectX 11實施的限制之前使用DirectX 12可能不會有效利用項目的時間。當然,如果這是一個學習練習,那麼只要你有背景,你現在正在學習一些好的課程。 –

回答

1

最後,我找到原因!

錯誤是從我的實例數據緩衝。

Engine::GetApp()->GetFrameManager().GetFrameResource(i)->AppendUploadBuffer<InstanceData>(mInstanceBuffer[i] 
      , mLastInstanceCount[i] 
      , mInstanceCount 
      , false); 

調用此之後,內存地址改變。 如果我用下面的代碼設置間接命令緩衝區數據,GPU將會崩潰。

data.instanceBufferAddress = mInstanceBuffer[i]->Resource()->GetGPUVirtualAddress() + mInstanceIndex[_obj->GetID()] * insCBByteSize; 

所以我需要或者在初始化時創建一個固定大小的實例緩衝區(地址不會改變)或克隆我的遊戲物體(會殺了性能)後更新所有以前的間接命令數據。

對不起,問一個愚蠢的問題:$。

+0

錯誤發生,使用d3d12中的已刪除資源是其中之一,因爲缺乏安全措施。您可以選擇不重做間接緩衝區中的地址。如果使用保留資源,則可以創建足夠大的實例數據資源,以便在最壞情況下使用,並在添加實例時提交頁面。這樣,gpu虛擬地址將保持不變。 – galop1n