0

インスタンスの描画に 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 を使用してシーンをロードします。ゲームオブジェクトがレンダリング オブジェクトの場合。私のシステムは AppendIndirectCommandBuffer() を呼び出し、データを間接コマンド バッファーにコピーします。

シーンの初期化後にゲームオブジェクトのクローンを作成しない場合、ExecuteIndirect() はうまく機能します。

そして、実行時に(Update()で)ゲームオブジェクトのクローンを作成しようとします。

私のシステムは AppendIndirectCommandBuffer() を再度呼び出して間接コマンド バッファーのサイズを変更し、新しいデータをバッファーにコピーします。

ゲームオブジェクトのクローンを数秒間作成した後、システムに不具合が生じ始め、R9 380 で TDR が発生します。

ただし、WARP と Intel GPU の両方で適切に動作します。

理由はわかりません:(。

この問題を解決するには?どうしても間接画を上手く使いこなしたい。

ありがとう!

アップデート

アップロード ヒープ リソースは、D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT に遷移できません。

デフォルト タイプのヒープを作成し、アップロード ヒープを使用してデータをそこにコピーします。

デバッグ層を試しましたが、そこからエラーは返されません。GBV を試してみましたが、エラーは 2 つしかありませんでした。

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]

次の関数を呼び出した後、これら 2 つのエラーはなくなりました。

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

これらの変更を行っても、まだ正しく動作しません。

4

1 に答える 1

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;

そのため、初期化時に固定サイズのインスタンス バッファを作成するか (アドレスは変更されません)、ゲームオブジェクトのクローンを作成した後に以前のすべての間接コマンド データを更新する必要があります (パフォーマンスが低下します)。

ばかげた質問をしてすみません:$。

于 2017-03-03T05:32:19.727 に答える