How to read vertices from vertex buffer in Direct3

2019-04-11 11:28发布

I have a question regarding vertex buffers. How does one read the vertices from the vertex buffer in D3D11? I want to get a particular vertex's position for calculations, if this approach is wrong, how would one do it? The following code does not (obviously) work.

VERTEX* vert;
D3D11_MAPPED_SUBRESOURCE ms;
devcon->Map(pVBufferSphere, NULL, D3D11_MAP_READ, NULL, &ms);
vert = (VERTEX*) ms.pData;
devcon->Unmap(pVBufferSphere, NULL);  

Thanks.

3条回答
Viruses.
2楼-- · 2019-04-11 11:56

Where your code is wrong:

  • You asking GPU to give you an address to its memory(Map()),
  • Storing this adress (operator=()),
  • Then saying: "Thanks, I don't need it anymore" (Unmap()).

After unmap, you can't really say where your pointer now points. It can point to memory location where already allocated another stuff or at memory of your girlfriend's laptop (just kidding =) ). You must copy data (all or it's part), not pointer in between Map() Unmap(): use memcopy, for loop, anything. Put it in array, std::vector, BST, everything.

Typical mistakes that newcomers can made here:

  1. Not to check HRESULT return value from ID3D11DeviceContext::Map method. If map fails it can return whatever pointer it likes. Dereferencing such pointer leads to undefined behavior. So, better check any DirectX function return value.
  2. Not to check D3D11 debug output. It can clearly say what's wrong and what to do in plain good English language (clearly better than my English =) ). So, you can fix bug almost instantly.
  3. You can only read from ID3D11Buffer if it was created with D3D11_CPU_ACCESS_READ CPU access flag which means that you must also set D3D11_USAGE_STAGING usage fag.

How do we usualy read from buffer:

  • We don't use staging buffers for rendering/calculations: it's slow.
  • Instead we copy from main buffer (non-staging and non-readable by CPU) to staging one (ID3D11DeviceContext::CopyResource() or ID3D11DeviceContext::CopySubresourceRegion()), and then copying data to system memory (memcopy()).
  • We don't do this too much in release builds, it will harm performance.
  • There are two main real-life usages of staging buffers: debugging (see if buffer contains wrong data and fix some bug in algorithm) and reading final non-pixel data (for example if you calculating scientific data in Compute shader).
  • In most cases you can avoid staging buffers at all by well-designing your code. Think as if CPU<->GPU was connected only one way: CPU->GPU.
查看更多
够拽才男人
3楼-- · 2019-04-11 12:02

Drop's answer was helpful. I figured that the reason why I wasn't able to read the buffer was because I didn't have the CPU_ACCESS_FLAG set to D3D11_CPU_ACCESS_READ before. Here

D3D11_BUFFER_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(bufferDesc));
bufferDesc.ByteWidth = iNumElements * sizeof(T);
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
bufferDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE ;
bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
bufferDesc.StructureByteStride = sizeof(T);

And then to read data I did

    const ID3D11Device& device = *DXUTGetD3D11Device();
    ID3D11DeviceContext& deviceContext = *DXUTGetD3D11DeviceContext();
    D3D11_MAPPED_SUBRESOURCE ms;
    HRESULT hr = deviceContext.Map(g_pParticles, 0, D3D11_MAP_READ, 0, &ms);    

    Particle* p = (Particle*)malloc(sizeof(Particle*) * g_iNumParticles);
    ZeroMemory(p, sizeof(Particle*) * g_iNumParticles);
    memccpy(p, ms.pData, 0, sizeof(ms.pData));
    deviceContext.Unmap(g_pParticles, 0);

    delete[] p;

I agree it's a performance decline, I wanted to do this, just to be able to debug the values!

Thanks anyway! =)

查看更多
Animai°情兽
4楼-- · 2019-04-11 12:08

The following code only get the address of the mapped resource, you didn't read anything before Unmap.

vert = (VERTEX*) ms.pData;

If you want to read data from the mapped resource, first allocate enough memory, then use memcpy to copy the data, I don't know your VERTEX structure, so I suppose vert is void*, you can convert it yourself

vert = new BYTE[ms.DepthPitch];
memcpy(vert, ms.pData, ms.DepthPitch];
查看更多
登录 后发表回答