我在尝试实现 DirectX 12 顶点缓冲区输出流以获取世界变换的顶点位置时遇到了麻烦。我几乎让它工作了——它做所有事情都没有错误,但它返回计数器 0 和输出流数据零。我从简单的代码开始,我可以分享这些代码以希望解决它。我用谷歌搜索,聊天 gpted,并使用 dxcpl.exe 强制打开调试消息。 我从 BzTut 04 开始: https://www.braynzasoft.net/viewtutorial/q16390-04-direct3d-12-drawing 最终结果是: https://github.com/ryan-de-boer/DirectX12SampleCode
这是一些代码,其中 counterValue 为零,streamOutputData 全部为零。
顶点着色器
struct VertexOutput // also in pixel shader as input
{
float3 position : POSITION; // Stream out 3*4bytes
float3 worldPos: WORLDPOS; // Stream out world pos position 3*4bytes
float4 sv_position : SV_POSITION; // Stream out position 4*4bytes, total 40bytes
};
struct VertexInput
{
float3 position : POSITION; // Stream out 3*4bytes
float3 worldPos: WORLDPOS; // Stream out world pos position 3*4bytes
float4 sv_position : SV_POSITION; // Stream out position 4*4bytes, total 40bytes
};
VertexOutput main(VertexInput input)
{
VertexOutput vo;
vo.worldPos = float3(1.1f, 1.2f, 1.3f); // I know this should be localPosition * world transform, but I want something simple working first.
vo.position = input.position;
vo.sv_position = float4(input.position, 1.0f);
return vo;
}
主要:
D3D12_STREAM_OUTPUT_BUFFER_VIEW streamOutputBufferView = {};
streamOutputBufferView.BufferFilledSizeLocation = streamOutputBuffer->GetGPUVirtualAddress();
streamOutputBufferView.BufferLocation = streamOutputBufferView.BufferFilledSizeLocation + sizeof(UINT64);
streamOutputBufferView.SizeInBytes = bufferSize;/* Size of the buffer */;
// Bind the output buffer to the stream-output stage
commandList->SOSetTargets(0/* Start slot */, 1/* Number of views */, &streamOutputBufferView);
commandList->DrawInstanced(3, 1, 0, 0); // finally draw 3 vertices (draw the triangle)
// Transition the state of the source buffer to D3D12_RESOURCE_STATE_COPY_SOURCE
CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(
streamOutputBuffer, // Resource
D3D12_RESOURCE_STATE_STREAM_OUT, // From state
D3D12_RESOURCE_STATE_COPY_SOURCE // To state
);
// Insert the barrier into the command list
commandList->ResourceBarrier(1, &barrier);
// Define the heap properties
D3D12_HEAP_PROPERTIES heapProperties = {};
heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; // Use UPLOAD type for CPU-accessible heap
heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; // Specify CPU visibility
heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; // Use default memory pool
// Define the buffer description
CD3DX12_RESOURCE_DESC bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(totalBufferSize);
// Create the destination buffer in the CPU-accessible heap
ID3D12Resource* destinationBuffer;
device->CreateCommittedResource(
&heapProperties, // Heap properties
D3D12_HEAP_FLAG_NONE, // Flags
&bufferDesc, // Resource description
D3D12_RESOURCE_STATE_GENERIC_READ, // Initial resource state
nullptr, // Clear value (unused for buffers)
IID_PPV_ARGS(&destinationBuffer) // Destination pointer
);
// Calculate the size of the counter (assuming UINT64)
const UINT counterSize = sizeof(UINT64);
// Calculate the total size of the data to copy
const UINT totalDataSize = totalBufferSize;
// Perform the copy
commandList->CopyBufferRegion(
destinationBuffer, // Destination buffer
0, // Destination offset
streamOutputBuffer, // Source buffer
0, // Source offset
totalDataSize // Size to copy
);
// Map the destination buffer to CPU-accessible memory
UINT8* mappedData = nullptr;
CD3DX12_RANGE readRange(0, 0); // We'll read the entire buffer
hr = destinationBuffer->Map(0, &readRange, reinterpret_cast<void**>(&mappedData));
if (FAILED(hr))
{
OutputDebugString(L"MAP FAILED");
}
// Copy the data from the mapped memory
// Assuming the first sizeof(UINT64) bytes contain the counter and the rest contain the stream output data
UINT64 counterValue = *reinterpret_cast<UINT64*>(mappedData);
BYTE* streamOutputData = mappedData + sizeof(UINT64);
// Now you can use counterValue and streamOutputData as needed
// Unmap the buffer
destinationBuffer->Unmap(0, nullptr);
commandList->SOSetTargets(0/* Start slot */, 0/* Number of views */, nullptr);
其余代码请参阅我的 github 链接,您可以使用 VS2022 进行编译。我还添加了在示例代码上运行的 PIX 的屏幕截图,您可以看到它知道如何在 VS 输出缓冲区中获取世界变换位置。
我修复了目标缓冲区充满零的问题。关键的解决方案是在从 CopyResource 读取缓冲区之前执行命令列表。
以下是我最近对 github 示例所做的更改:
在我的 github 页面上查看 HxD 查看二进制数据的屏幕截图。