【发布时间】:2017-03-02 10:00:33
【问题描述】:
我尝试使用 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);
}
}
}
}
最后,间接执行
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,只有两个错误:
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());
尽管做了这些修改,它仍然不能正常工作。
【问题讨论】:
-
您是否尝试过调试层和基于 gpu 的验证。我没有看到您的代码中的状态转换为间接参数,这是 GCN 硬件上某些缓存刷新所必需的。
-
这是 DirectX 12 提供的低级别暴露级别所带来的成本:您需要能够跨硬件套件对特定应用程序进行故障排除和验证。 Direct3D 调试层始终是一个很好的起点,就像尝试 WARP12 一样,但实际上您需要有许多不同的机器来尝试诊断问题是驱动程序还是您的应用程序,然后联系硬件供应商试用测试版驱动程序。见Anatomy of Direct3D 12 Create Device。
-
另外,请确保您拥有混合的硬件功能层,因为这也会产生很大的不同。我遇到了DirectX Tool Kit for DirectX 12 的许多问题,这些问题仅在第 1 层资源绑定系统上重现,但在第 2 层或更好的情况下工作正常。这就是为什么在您真正突破 DirectX 11 实施的限制之前使用 DirectX 12 可能无法有效地利用项目时间的原因。当然,如果这是一个学习练习,那么只要你有背景,你现在正在学习一些很好的课程。
-
@galop1n 我更新了。请你看一下好吗?谢谢。
-
@ChuckWalbourn 谢谢你的建议。我想我以后只会初始化支持功能 12_0 和 12_1 的设备。是的,我现在只是在练习,对此非常感兴趣。
标签: directx directx-12