1

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?

deadpin
  • 13
  • 4

1 Answers1

0

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.

Derag
  • 596
  • 3
  • 13
  • 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.

    – deadpin May 11 '18 at 19:14