如何从Direct3d11的顶点缓冲区中读取顶点

3

我有一个关于顶点缓冲的问题。在D3D11中如何从顶点缓冲中读取顶点?我想要获取特定顶点的位置进行计算,如果这种方法不正确,应该如何做?下面的代码显然没有起作用。

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

谢谢。
3个回答

8

你代码的问题所在:

  • 你请求GPU给你它的内存地址(Map()),
  • 将这个地址存储起来(operator=()),
  • 然后说:“谢谢,我不再需要它了”(Unmap())。

解除映射之后,你无法确定指针现在指向哪里。它可能指向已经分配了其他东西的内存位置,也可能指向你女朋友电脑的内存(开个玩笑 =))。你必须在Map()Unmap()之间复制数据(全部或部分),而不是指针:使用memcopy、for循环等方法,将它放入数组、std::vector、BST等中。

新手常犯的错误:

  1. 不要忽略从ID3D11DeviceContext::Map方法返回的HRESULT值。如果映射失败,它可以返回任何指针。引用这样的指针会导致未定义行为。因此,最好检查任何DirectX函数的返回值。
  2. 不要忽略D3D11调试输出。它可以清楚地说明问题和如何做(比我的英语更清楚)。因此,您几乎可以立即修复错误。
  3. 只有当ID3D11Buffer使用标志设置为D3D11_CPU_ACCESS_READ时,才能从中读取数据,这意味着您还必须设置D3D11_USAGE_STAGING使用标志。

我们通常如何从缓冲区读取:

  • 我们不使用暂存缓冲区进行渲染/计算:这样很慢。
  • 相反,我们从主缓冲区(非暂存且不可由CPU读取)复制到暂存缓冲区(ID3D11DeviceContext::CopyResource()ID3D11DeviceContext::CopySubresourceRegion()),然后将数据复制到系统内存(memcopy())。
  • 在发布版本中不要频繁执行此操作,否则会影响性能。
  • 暂存缓冲区有两个主要的实际用途:调试(查看缓冲区是否包含错误的数据并修复算法中的一些错误)和读取最终的非像素数据(例如,如果您在计算科学数据,则可以使用计算着色器)。
  • 在大多数情况下,您可以通过良好设计代码来完全避免使用暂存缓冲区。请考虑CPU<->GPU只连接一种方式的情况:CPU->GPU。

感谢您的解释。作为一个DirectX新手,我没有意识到我可以在C++代码中将最终变换矩阵与顶点相乘,以获得其位置,即在着色器之外。这就是我想要的,因此使用管道的输出流是严重过度的。 - Ma Sa

2
以下代码仅获取映射资源的地址,您在取消映射之前没有读取任何内容。
vert = (VERTEX*) ms.pData;

如果您想从映射的资源中读取数据,则首先分配足够的内存,然后使用memcpy复制数据。我不知道您的VERTEX结构,所以我假设vert是void*,您可以自行进行转换。

vert = new BYTE[ms.DepthPitch];
memcpy(vert, ms.pData, ms.DepthPitch];

复制内存后,您如何实际访问顶点? - junfanbl

2

Drop的回答很有帮助。我发现我无法读取缓冲区的原因是在此之前我没有设置D3D11_CPU_ACCESS_READ的CPU_ACCESS_FLAG。这里

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);

然后我执行了读取数据的操作

    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;

我同意这会导致性能下降,但我想这样做只是为了能够调试数值!

无论如何,还是谢谢!=)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接