更新顶点缓冲区导致调试层错误
Updating a vertex buffer causes a debug layer error
我将 Windows 10 更新到版本 1607,其中包括额外的 D3D 调试层检查。我的顶点缓冲区更新代码在更新前没有 warnings/errors 的情况下工作,但现在我在调用 CopyBufferRegion
:
时遇到错误
Resource state (0xAC3: D3D12_RESOURCE_STATE_GENERIC_READ) of resource
(0x000001DB301B9750:'') (subresource: 0) is
invalid for use as a destination buffer. Expected State Bits: 0x400:
D3D12_RESOURCE_STATE_COPY_DEST, Actual State: 0xAC3:
D3D12_RESOURCE_STATE_GENERIC_READ
如果我将 vb
转换为所需状态 (D3D12_RESOURCE_STATE_COPY_DEST
),我反而会收到如下错误:
D3D12 ERROR: ID3D12CommandList::ResourceBarrier: Certain heaps are
restricted to certain D3D12_RESOURCE_STATES states, and cannot be
changed. D3D12_HEAP_TYPE_UPLOAD requires
D3D12_RESOURCE_STATE_GENERIC_READ. D3D12_HEAP_TYPE_READBACK requires
D3D12_RESOURCE_STATE_COPY_DEST. [ RESOURCE_MANIPULATION ERROR #741:
RESOURCE_BARRIER_INVALID_HEAP]
这是我的代码。它创建一个顶点缓冲区并上传顶点数据,必要时调整缓冲区大小:
void ae3d::VertexBuffer::UploadVB( void* faces, void* vertices, unsigned ibSize )
{
D3D12_HEAP_PROPERTIES uploadProp = {};
uploadProp.Type = D3D12_HEAP_TYPE_UPLOAD;
uploadProp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
uploadProp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
uploadProp.CreationNodeMask = 1;
uploadProp.VisibleNodeMask = 1;
D3D12_RESOURCE_DESC bufferProp = {};
bufferProp.Alignment = 0;
bufferProp.DepthOrArraySize = 1;
bufferProp.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
bufferProp.Flags = D3D12_RESOURCE_FLAG_NONE;
bufferProp.Format = DXGI_FORMAT_UNKNOWN;
bufferProp.Height = 1;
bufferProp.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
bufferProp.MipLevels = 1;
bufferProp.SampleDesc.Count = 1;
bufferProp.SampleDesc.Quality = 0;
bufferProp.Width = ibOffset + ibSize;
// this branch resizes the buffer and causes validation errors.
if (vb != nullptr && bufferProp.Width <= sizeBytes)
{
ID3D12Resource* stagingBuffer = nullptr;
HRESULT hr = GfxDeviceGlobal::device->CreateCommittedResource( &uploadProp, D3D12_HEAP_FLAG_NONE, &bufferProp,
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS( &stagingBuffer ) );
AE3D_CHECK_D3D( hr, "Failed to create vertex staging resource" );
char* vbUploadPtr = nullptr;
hr = stagingBuffer->Map( 0, nullptr, reinterpret_cast<void**>(&vbUploadPtr) );
if (FAILED( hr ))
{
ae3d::System::Assert( false, "Unable to map vertex staging buffer!\n" );
return;
}
memcpy_s( vbUploadPtr, ibOffset, vertices, ibOffset );
memcpy_s( vbUploadPtr + ibOffset, ibSize, faces, ibSize );
stagingBuffer->Unmap( 0, nullptr );
// vb state is invalid at this point, needs D3D12_RESOURCE_STATE_COPY_DEST
GfxDeviceGlobal::graphicsCommandList->CopyBufferRegion( vb, 0, stagingBuffer, 0, ibOffset + ibSize );
Global::frameVBUploads.push_back( stagingBuffer );
sizeBytes = ibOffset + ibSize;
return;
}
sizeBytes = ibOffset + ibSize;
HRESULT hr = GfxDeviceGlobal::device->CreateCommittedResource(
&uploadProp,
D3D12_HEAP_FLAG_NONE,
&bufferProp,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS( &vb ) );
if (FAILED( hr ))
{
ae3d::System::Assert( false, "Unable to create vertex buffer!\n" );
return;
}
Global::vbs.push_back( vb );
char* vbUploadPtr = nullptr;
hr = vb->Map( 0, nullptr, reinterpret_cast<void**>(&vbUploadPtr) );
if (FAILED( hr ))
{
ae3d::System::Assert( false, "Unable to map vertex buffer!\n" );
return;
}
memcpy_s( vbUploadPtr, ibOffset, vertices, ibOffset );
memcpy_s( vbUploadPtr + ibOffset, ibSize, faces, ibSize );
vb->Unmap( 0, nullptr );
}
如何修复这些调试层错误?
上传缓冲区只能处于D3D12_RESOURCE_STATE_GENERIC_READ
状态(link). You need to create a default buffer with D3D12_RESOURCE_STATE_COPY_DEST
state. Then after mapping of upload heap on cpu you can copy contents of upload buffer to default buffer on gpu with ID3D12GraphicsCommandList::CopyBufferRegion()方法。不要忘记将默认缓冲区转换为可用状态,并确保上传堆在gpu时刻仍然存在执行了复制。
我将 Windows 10 更新到版本 1607,其中包括额外的 D3D 调试层检查。我的顶点缓冲区更新代码在更新前没有 warnings/errors 的情况下工作,但现在我在调用 CopyBufferRegion
:
Resource state (0xAC3: D3D12_RESOURCE_STATE_GENERIC_READ) of resource (0x000001DB301B9750:'') (subresource: 0) is invalid for use as a destination buffer. Expected State Bits: 0x400: D3D12_RESOURCE_STATE_COPY_DEST, Actual State: 0xAC3: D3D12_RESOURCE_STATE_GENERIC_READ
如果我将 vb
转换为所需状态 (D3D12_RESOURCE_STATE_COPY_DEST
),我反而会收到如下错误:
D3D12 ERROR: ID3D12CommandList::ResourceBarrier: Certain heaps are restricted to certain D3D12_RESOURCE_STATES states, and cannot be changed. D3D12_HEAP_TYPE_UPLOAD requires D3D12_RESOURCE_STATE_GENERIC_READ. D3D12_HEAP_TYPE_READBACK requires D3D12_RESOURCE_STATE_COPY_DEST. [ RESOURCE_MANIPULATION ERROR #741: RESOURCE_BARRIER_INVALID_HEAP]
这是我的代码。它创建一个顶点缓冲区并上传顶点数据,必要时调整缓冲区大小:
void ae3d::VertexBuffer::UploadVB( void* faces, void* vertices, unsigned ibSize )
{
D3D12_HEAP_PROPERTIES uploadProp = {};
uploadProp.Type = D3D12_HEAP_TYPE_UPLOAD;
uploadProp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
uploadProp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
uploadProp.CreationNodeMask = 1;
uploadProp.VisibleNodeMask = 1;
D3D12_RESOURCE_DESC bufferProp = {};
bufferProp.Alignment = 0;
bufferProp.DepthOrArraySize = 1;
bufferProp.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
bufferProp.Flags = D3D12_RESOURCE_FLAG_NONE;
bufferProp.Format = DXGI_FORMAT_UNKNOWN;
bufferProp.Height = 1;
bufferProp.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
bufferProp.MipLevels = 1;
bufferProp.SampleDesc.Count = 1;
bufferProp.SampleDesc.Quality = 0;
bufferProp.Width = ibOffset + ibSize;
// this branch resizes the buffer and causes validation errors.
if (vb != nullptr && bufferProp.Width <= sizeBytes)
{
ID3D12Resource* stagingBuffer = nullptr;
HRESULT hr = GfxDeviceGlobal::device->CreateCommittedResource( &uploadProp, D3D12_HEAP_FLAG_NONE, &bufferProp,
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS( &stagingBuffer ) );
AE3D_CHECK_D3D( hr, "Failed to create vertex staging resource" );
char* vbUploadPtr = nullptr;
hr = stagingBuffer->Map( 0, nullptr, reinterpret_cast<void**>(&vbUploadPtr) );
if (FAILED( hr ))
{
ae3d::System::Assert( false, "Unable to map vertex staging buffer!\n" );
return;
}
memcpy_s( vbUploadPtr, ibOffset, vertices, ibOffset );
memcpy_s( vbUploadPtr + ibOffset, ibSize, faces, ibSize );
stagingBuffer->Unmap( 0, nullptr );
// vb state is invalid at this point, needs D3D12_RESOURCE_STATE_COPY_DEST
GfxDeviceGlobal::graphicsCommandList->CopyBufferRegion( vb, 0, stagingBuffer, 0, ibOffset + ibSize );
Global::frameVBUploads.push_back( stagingBuffer );
sizeBytes = ibOffset + ibSize;
return;
}
sizeBytes = ibOffset + ibSize;
HRESULT hr = GfxDeviceGlobal::device->CreateCommittedResource(
&uploadProp,
D3D12_HEAP_FLAG_NONE,
&bufferProp,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS( &vb ) );
if (FAILED( hr ))
{
ae3d::System::Assert( false, "Unable to create vertex buffer!\n" );
return;
}
Global::vbs.push_back( vb );
char* vbUploadPtr = nullptr;
hr = vb->Map( 0, nullptr, reinterpret_cast<void**>(&vbUploadPtr) );
if (FAILED( hr ))
{
ae3d::System::Assert( false, "Unable to map vertex buffer!\n" );
return;
}
memcpy_s( vbUploadPtr, ibOffset, vertices, ibOffset );
memcpy_s( vbUploadPtr + ibOffset, ibSize, faces, ibSize );
vb->Unmap( 0, nullptr );
}
如何修复这些调试层错误?
上传缓冲区只能处于D3D12_RESOURCE_STATE_GENERIC_READ
状态(link). You need to create a default buffer with D3D12_RESOURCE_STATE_COPY_DEST
state. Then after mapping of upload heap on cpu you can copy contents of upload buffer to default buffer on gpu with ID3D12GraphicsCommandList::CopyBufferRegion()方法。不要忘记将默认缓冲区转换为可用状态,并确保上传堆在gpu时刻仍然存在执行了复制。