Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 一个常量缓冲区可以用于多个对象吗?_C++_Directx_Directx 11 - Fatal编程技术网

C++ 一个常量缓冲区可以用于多个对象吗?

C++ 一个常量缓冲区可以用于多个对象吗?,c++,directx,directx-11,C++,Directx,Directx 11,我是Direct3D 11的新手,在理解如何基于每个对象更新常量(和其他缓冲区)时遇到一些困难。我有一些简单的代码,我试图让两个四边形画在屏幕上,但位置不同。这是我用来画它们的代码 // ------------------------------------------------------------------------------------------------------------------------------ void QuadShape::UpdateBuffers

我是Direct3D 11的新手,在理解如何基于每个对象更新常量(和其他缓冲区)时遇到一些困难。我有一些简单的代码,我试图让两个四边形画在屏幕上,但位置不同。这是我用来画它们的代码

// ------------------------------------------------------------------------------------------------------------------------------
void QuadShape::UpdateBuffers(ID3D11DeviceContext* pContext)
{
  // We need to know about our verts + set the constant buffers...
  // NOTE: We only really need to do this when the buffer data actually changes...
  XMMATRIX translate = XMMatrixTranspose(XMMatrixTranslation(X, Y, 0));
  XMStoreFloat4x4(&ConstData.World, translate);

  D3D11_MAPPED_SUBRESOURCE mappedResource;
    ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));

  pContext->Map(ConstBuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
  memcpy(mappedResource.pData, &ConstData, sizeof(ObjectConstBuffer));
  pContext->Unmap(ConstBuf, 0);

}

// ------------------------------------------------------------------------------------------------------------------------------
void QuadShape::Draw(ID3D11DeviceContext* pContext)
{
  UpdateBuffers(pContext);
  pContext->DrawIndexed(_VertCount, _StartVert, 0);
}
您可以看到,我正在根据对象的当前X/Y位置计算平移矩阵,并将其映射到对象的常量缓冲区,表示为“ConstBuf”。我遇到的问题来自这样一个事实,即所有四边形最终都在同一位置绘制,尽管我已经验证了为每个四边形计算的矩阵确实不同,并且缓冲区确实是动态的


我猜想正在发生的是,映射的资源只是被最后一个矩阵所覆盖,但我认为MAP\u WRITE\u DISCARD应该可以避免这种情况。我很困惑,我如何才能为每个对象使用不同的常量缓冲区,并使它们显示在不同的位置?

您需要更新着色器中的常量缓冲区。在
UpdateBuffers
方法的末尾添加:

pContext->VSSetConstantBuffers(0, 1, ConstBuf);
还要确保缓冲区是16字节对齐的:

性能损失可能没有您想象的那么大,特别是与您在每帧基础上执行的其他昂贵操作相比(出于好奇,我将实际测试它并让您知道结果),这是非常标准的操作方式。如果你不想相信我的话,请看一看Frank Luna的这篇文章,这篇文章很好地解释了常量缓冲区,并提供了处理它们的常用策略:

如果您真的担心在每个对象的基础上调用
*SetConstantBuffers
(例如,您有许多独立移动的对象),那么您可以每帧绑定一次,然后为每个对象使用指向绑定缓冲区的指针

一种方法是使用
着色器
类来封装所有着色器相关资源以及绘制对象,因此在我的主要更新方法中,我会调用
着色器::bindBuffers()
一次,然后为每个对象及其参数(包括矩阵)调用
着色器::Render(Object*)
。在
Shader::Render()
内部,我使用之前绑定的缓冲区,并使用map/unmap等更新它们

另一种方法是让对象像您一样绘制自己,但将一个指向常量缓冲区(存储为调用类的成员)的指针传递给render方法(在您的例子中是so
UpdateBuffers
)。您可以在Microsoft提供的此示例中看到类似的方法:


然而,这些都是优化,您通常不应该过早地担心它们。

您应该根据更新频率对常量缓冲区进行分组,因此如果您有每个对象更改的一些数据,请将其放在一个常量缓冲区中。如果有其他数据(如投影矩阵)只有在调整窗口大小时才会更改,请将其放入另一个常量缓冲区中

例如,定义两个这样的缓冲区:

struct CBPerObject
{
    XMMATRIX mWorld;    // world matrix
};

struct CBChangeOnResize
{
    XMMATRIX mProjection;     // projection matrix
};
然后创建常量缓冲区,并在成员变量中保留对它们的引用:

CComPtr<ID3D11Buffer> m_pCBPerObject;        // dx11 constant buffer (per object)
CComPtr<ID3D11Buffer> m_pCBChangeOnResize;   // dx11 constant buffer (change on resize)
现在,您可以在初始化期间仅绑定它们一次(假设布局不会更改):

您可能也需要绑定到像素着色器,但请改用
PSSetConstantBuffers

现在,您只需要在需要时更新常量缓冲区。例如,调整窗口大小时:

void CMyClass::OnSize()
{
    // update projection matrix
    CBChangeOnResize cbBuffer;

    // calculate the projection matrix - for example:
    XMMATRIX mProjection = XMMatrixOrthographicOffCenterLH(fLeft, fRight, fBottom, 
                                                           fTop, 0.1f, 1000.0f);
    cbBuffer.mProjection = XMMatrixTranspose(mProjection);

    // update the constant buffer
    pContext->UpdateSubresource(m_pCBChangeOnResize, 0, nullptr, &cbBuffer, 0, 0);
}
类似地,绘制时,更新每个对象的世界矩阵:

void CMyClass::DrawScene()
{
    // draw the complete scene
    CBPerObject cbBuffer;

    // ... clear render target etc. 

    // for each object ... 
    {
        cbBuffer.mWorld = XMLoadFloat4x4(pObject->GetWorld());

        // update cb and draw object
        pContext->UpdateSubresource(m_pCBPerObject, 0, nullptr, &cbBuffer, 0, 0);
        pContext->DrawIndexed(6, 0, 0);
    }

    // ... etc.
}
因此,除非布局发生更改,否则无需重新绑定常量缓冲区,并且您可以使用
UpdateSubresource
而不是
Map
/
Unmap
,如我上面所示

在顶点(像素)着色器中,根据绑定常量缓冲区的插槽定义常量缓冲区:

cbuffer cbPerObject : register (b0)
{
    matrix mWorld;
};

cbuffer cbChangeOnResize : register (b1)
{
    matrix mProjection;
};
在语法中:
register(b0)
register(b1)
在这种情况下,
b0
b1
引用提供给
VSSetConstantBuffers
的第一个参数,您必须为每个对象“更新绑定绘图”。伪代码:

foreach(object in objects)
{
   constantBuffer.update(object.position);
   device.bind(constantBuffer);
   device.draw();
}
您的代码缺少绑定调用

这样,对于N个对象,您得到了N个缓冲区更新、N个绑定和N个绘制调用,如果您有很多对象(您在驱动程序中花费了很多时间,有CPU-GPU同步点,内存带宽被冗余数据传输占用),则效率会非常低。不过,不必要的绑定调用可以通过适当的驱动程序实现来消除

更新
存在几种不同的方法,它们将内存和代码复杂性与运行时性能进行权衡。值得注意的是,有“批处理”和一系列技术,称为“几何体实例”:

一,批处理可以描述为在顶点缓冲区中合并所有对象的预变换顶点,而不是在着色器中变换它们。可以大大节省顶点着色器的时间,但如果对象的任何属性(例如位置)发生更改,则需要在CPU(!)上重新计算对象的所有顶点,并部分更新顶点缓冲区。因此,它最适用于静态对象(例如,不经常/根本不移动)和小对象(没有多少顶点需要更新)。通常用于绘制2D内容:精灵、GUI和文本。

二,硬件几何体实例。您需要额外的顶点缓冲区,它将存储实例属性而不是顶点属性(我们称之为实例缓冲区)。必须更改输入布局,添加每个实例属性。您可以在对象更改其属性时更新实例缓冲区,就像使用任何顶点缓冲区一样。您可以使用
DrawInstanced*()
调用进行绘制。这是一种流行的技术,可以优化许多非常相似的动态对象(草、树叶、陨石、道路交通、足球场上的球迷等)的绘图。有几个高级技巧允许在eve应用它
cbuffer cbPerObject : register (b0)
{
    matrix mWorld;
};

cbuffer cbChangeOnResize : register (b1)
{
    matrix mProjection;
};
foreach(object in objects)
{
   constantBuffer.update(object.position);
   device.bind(constantBuffer);
   device.draw();
}