1
$\begingroup$

Consider a situation where there are 3 different buffers describing a particular mesh piece: vBuffer, nBuffer, iBuffer (vertices, normals, and indices) which equate to totalBufferSize in aggregate.

I want to save these 3 buffers to the same default heap (D3D12_HEAP_TYPE_DEFAULT). I know I still need to use an upload heap (D3D12_HEAP_TYPE_UPLOAD) as an intermediary but I can't figure out how to eliminate extra copies.

Currently my code (which works) does the following:

    const D3D12_HEAP_PROPERTIES defaultHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
    const D3D12_HEAP_PROPERTIES uploadHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);

...

    const CD3DX12_RESOURCE_DESC resourceDescV = CD3DX12_RESOURCE_DESC::Buffer(totalBufferSize);
    DX::ThrowIfFailed(device->CreateCommittedResource(
        &defaultHeapProperties,
        D3D12_HEAP_FLAG_NONE,
        &resourceDescV,
        D3D12_RESOURCE_STATE_COPY_DEST,
        nullptr,
        IID_PPV_ARGS(meshPart.m_mainBuffer.ReleaseAndGetAddressOf())));

    DX::ThrowIfFailed(device->CreateCommittedResource(
        &uploadHeapProperties,
        D3D12_HEAP_FLAG_NONE,
        &resourceDescV,
        D3D12_RESOURCE_STATE_GENERIC_READ,
        nullptr,
        IID_PPV_ARGS(meshPart.m_uploadBuffer.ReleaseAndGetAddressOf())));

    uint8_t * bufferStart{};
    const CD3DX12_RANGE readRange(0, 0);
    DX::ThrowIfFailed(meshPart.m_uploadBuffer->Map(0, &readRange, reinterpret_cast<void **>(&bufferStart)));

    // Can these be eliminated?
    uint32_t offset{};
    std::memcpy(bufferStart, vBuffer.data, vBuffer.totalSize);
    offset += vBuffer.totalSize;
    std::memcpy(bufferStart + offset, nBuffer.data, nBuffer.totalSize);
    offset += nBuffer.totalSize;
    std::memcpy(bufferStart + offset, iBuffer.data, iBuffer.totalSize);

    D3D12_SUBRESOURCE_DATA bufferData{};
    bufferData.pData = bufferStart;
    bufferData.RowPitch = totalBufferSize;
    bufferData.SlicePitch = totalBufferSize;

    UpdateSubresources<1>(commandList, meshPart.m_mainBuffer.Get(), meshPart.m_uploadBuffer.Get(), 0, 0, 1, &bufferData);
    CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(meshPart.m_mainBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
    commandList->ResourceBarrier(1, &barrier);

However, I'm trying to figure out if I can eliminate those std::memcpy calls. All examples of UpdateSubresources I've seen do not explicitly Map+memcpy into the upload buffer. They just set D3D12_SUBRESOURCE_DATA.pData directly.

I tried calling using UpdateSubresources<3> and passing in an array of D3D12_SUBRESOURCE_DATA, each pointing to the beginning of my mesh buffers, but the validation layer didn't like that and threw an exception.

Is it possible to upload all 3 buffers in one step here -- without the explicit Map and memcpy steps?

$\endgroup$

1 Answer 1

0
$\begingroup$

UpdateSubresources is a helper function. Inside it is another memcpy. I would recommend simply rewriting it - source is in d3dx12.h. Then you will be able to copy your data no more than necessary.

$\endgroup$
1
  • $\begingroup$ Hmm, yeah... the helper function essentially amounts to the following in my case: commandList->CopyBufferRegion(meshPart.m_mainBuffer.Get(), 0, meshPart.m_uploadBuffer.Get(), 0, totalBufferSize); I'll keep my Map+memcpy and just call CopyBufferRegion myself without going through the helper. Thanks for the reality check. $\endgroup$
    – deadpin
    Commented May 11, 2018 at 19:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.