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

Posted

技术标签:

【中文标题】如何从 Direct3d11 中的顶点缓冲区读取顶点【英文标题】:How to read vertices from vertex buffer in Direct3d11 【发布时间】:2013-06-29 03:58:32 【问题描述】:

我有一个关于顶点缓冲区的问题。如何从 D3D11 中的顶点缓冲区中读取顶点?我想得到一个特定顶点的位置进行计算,如果这种方法是错误的,人们会怎么做?以下代码(显然)不起作用。

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

谢谢。

【问题讨论】:

【参考方案1】:

你的代码哪里错了:

你要求 GPU 给你一个内存地址(Map()), 存储此地址 (operator=()), 然后说:“谢谢,我不再需要它了”(Unmap())。

取消映射后,您无法真正说出指针现在指向的位置。它可以指向已经分配了另一个东西的内存位置或你女朋友笔记本电脑的内存(开玩笑=))。 您必须复制数据(全部或部分),而不是 Map() Unmap() 之间的指针:使用 memcopy,for 循环,任何东西。将其放入数组中,std::vector,BST,所有内容。

新手在这里可能犯的典型错误:

    不检查来自ID3D11DeviceContext::Map 方法的HRESULT 返回值。如果 map 失败,它可以返回它喜欢的任何指针。取消引用此类指针会导致未定义的行为。因此,最好检查任何 DirectX 函数的返回值。 不检查 D3D11 调试输出。它可以用通俗易懂的英语(显然比我的英语好=)清楚地说出什么是错的和做什么。因此,您几乎可以立即修复错误。 您只能从 ID3D11Buffer 读取 created 和 D3D11_CPU_ACCESS_READ CPU 访问标志,这意味着您还必须设置 D3D11_USAGE_STAGING 使用标志。

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

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

【讨论】:

感谢您的解释。作为directx in 的新手并没有意识到我可以将最终的变换矩阵与顶点相乘以获得它在c ++代码中的位置,即在着色器之外。这就是我想要的,所以使用管道中的输出流是一种严重的过度杀伤。【参考方案2】:

下面的代码只是获取映射资源的地址,在Unmap之前你什么都没读过。

vert = (VERTEX*) ms.pData;

如果要从映射资源中读取数据,先分配足够的内存,然后使用memcpy复制数据,我不知道你的VERTEX结构,所以我猜vert是void*,你可以自己转换

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

【讨论】:

感谢您的解释! 复制内存后,如何真正获得对顶点的访问权限?【参考方案3】:

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

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;

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

还是谢谢! =)

【讨论】:

以上是关于如何从 Direct3d11 中的顶点缓冲区读取顶点的主要内容,如果未能解决你的问题,请参考以下文章

Direct3D11:翻转 ID3D11Texture2D

使用 DirectX11 更新非 UI 线程中的顶点缓冲区

Direct3D11学习:Direct3D11初始化

Direct3D11学习:渲染管线

在 D3D11 中将纹理预加载到 GPU 内存

DirectX11--深入理解与使用缓冲区资源