こんばんわ。
Pocolです。
WriteBufferImmediate()を使って,DREDもどきを実装するサンプルプログラムを書きました。
https://github.com/ProjectAsura/D3D12Samples/tree/master/D3D12_CustomDRED
実装としては,以前に説明したようにVirtualAlloc()でパンくず用のメモリを作り,これをID3D12Device3::OpenExistingHeapFromAddress()に渡して,ID3D12Heapを作ります。
作った,ヒープからCreatePlacedResource()でID3D12Resourceを作り,GetGPUVirtualAddress()して,WriteBufferImmediate()で書き込むためのベースアドレスを取得します。
bool Init(ID3D12Device3* pDevice) { if (pDevice == nullptr) { ELOG("Error : Invalid Argument"); return false; } m_BufferSize = sizeof(D3D12_AUTO_BREADCRUMB_OP) * UINT16_MAX; m_pAddress = VirtualAlloc(nullptr, m_BufferSize, MEM_COMMIT, PAGE_READWRITE); if (m_pAddress == nullptr) { ELOG("Error : Out of Memory"); return false; } auto hr = pDevice->OpenExistingHeapFromAddress(m_pAddress, IID_PPV_ARGS(&m_pHeap)); if (FAILED(hr)) { ELOG("Error : ID3D12Device3::OpenExistingHeapFromAddress() Failed. errcode = 0x%x", hr); return false; } D3D12_RESOURCE_DESC desc = {}; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Alignment = 0; desc.Width = m_BufferSize; desc.Height = 1; desc.DepthOrArraySize = 1; desc.MipLevels = 1; desc.Format = DXGI_FORMAT_UNKNOWN; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER; D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATE_COPY_DEST; hr = pDevice->CreatePlacedResource(m_pHeap, 0, &desc, state, nullptr, IID_PPV_ARGS(&m_pBuffer)); if (FAILED(hr)) { ELOG("Error : ID3D12Device::CreatePlacedResource() Failed. errcode = 0x%x", hr); return false; } m_GpuAddress = m_pBuffer->GetGPUVirtualAddress(); m_CurrIndex = 0; return true; }
あとは,コマンドの前後にWriteBufferImmediate()を呼び出して,コールされたかどうかをフラグを書き込みます。
void Push(ID3D12GraphicsCommandList2* pCmdList, D3D12_AUTO_BREADCRUMB_OP op) { Marker marker = {}; marker.Op = op; marker.In = 1; marker.Out = 0; D3D12_WRITEBUFFERIMMEDIATE_PARAMETER param = {}; param.Dest = m_GpuAddress + sizeof(D3D12_AUTO_BREADCRUMB_OP) * m_CurrIndex; param.Value = bit_cast<UINT>(marker); auto mode = D3D12_WRITEBUFFERIMMEDIATE_MODE_MARKER_IN; pCmdList->WriteBufferImmediate(1, ¶m, &mode); m_CurrentOp = op; } void Pop(ID3D12GraphicsCommandList2* pCmdList) { Marker marker = {}; marker.Op = m_CurrentOp; marker.In = 0; marker.Out = 1; D3D12_WRITEBUFFERIMMEDIATE_PARAMETER param = {}; param.Dest = m_GpuAddress + sizeof(D3D12_AUTO_BREADCRUMB_OP) * m_CurrIndex; param.Value = bit_cast<UINT>(marker); auto mode = D3D12_WRITEBUFFERIMMEDIATE_MODE_MARKER_OUT; pCmdList->WriteBufferImmediate(1, ¶m, &mode); m_CurrIndex++; }
Push()とPop()挟み方は次のような感じ。
void DrawInstanced( _In_ UINT VertexCountPerInstance, _In_ UINT InstanceCount, _In_ UINT StartVertexLocation, _In_ UINT StartInstanceLocation) override { Push(m_pCmdList, D3D12_AUTO_BREADCRUMB_OP_DRAWINSTANCED); m_pCmdList->DrawInstanced(VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); Pop(m_pCmdList); }
GPUクラッシュが発生したら,VirtualAllocで確保したメモリを見に行って,どこまで書き込みが終わっているかをチェックすれば,DREDもどきが出来ると思います。
void Print() { auto marker = reinterpret_cast<Marker*>(m_pAddress); if (marker == nullptr) return; ILOG("BreadcrumbCount : %u", m_LastIndex); for(auto i=0; i<m_LastIndex; ++i) { char state[3] = " "; if (marker[i].Out) { state[0] = 'O'; state[1] = 'K'; } else if (marker[i].In) { state[0] = 'N'; state[1] = 'G'; } uint32_t opIndex = marker[i].Op; ILOG("%04u : [%s] Op = %s", i, state, g_BreadcrumbTags[opIndex]); } }
というわけで,DREDもどきの実装でした。
PageFaultとかも頑張ろうかなと思ったのですが,MP切れなのと,BindlessResource(DynamicResource)でのアクセスが検知できないので,諦めました。
このあたりはNVIDIA AftermathとかAMD Radeon GPU Dectective使える環境ならそっちを使った方が良いです。