如何:使用动态资源

重要的 API

当应用需要更改这些资源中的数据时,可以创建并使用动态资源。 可以创建纹理和缓冲区以供动态使用。

需要了解的事项

技术

先决条件

我们假定你熟悉 C++。 你还需要具有图形编程概念方面的基本经验。

Instructions

步骤 1:指定动态使用情况

如果希望应用能够更改资源,则必须在创建这些资源时将这些资源指定为动态且可写。

指定动态使用情况

  1. 将资源使用情况指定为动态。 例如,在顶点或常量缓冲区的 D3D11_BUFFER_DESCUsage 成员中指定D3D11_USAGE_DYNAMIC值,并在 2D 纹理的 D3D11_TEXTURE2D_DESC 的 Usage 成员中指定D3D11_USAGE_DYNAMIC值。
  2. 将资源指定为可写资源。 例如,在 D3D11_BUFFER_DESC D3D11_TEXTURE2D_DESCCPUAccessFlags 成员中指定D3D11_CPU_ACCESS_WRITE值。
  3. 将资源说明传递给创建函数。 例如,将 D3D11_BUFFER_DESC 的地址传递给 ID3D11Device::CreateBuffer ,并将 D3D11_TEXTURE2D_DESC 的地址传递给 ID3D11Device::CreateTexture2D

下面是创建动态顶点缓冲区的示例:

    // Create a vertex buffer for a triangle.

    float2 triangleVertices[] =
    {
        float2(-0.5f, -0.5f),
        float2(0.0f, 0.5f),
        float2(0.5f, -0.5f),
    };

    D3D11_BUFFER_DESC vertexBufferDesc = { 0 };
    vertexBufferDesc.ByteWidth = sizeof(float2)* ARRAYSIZE(triangleVertices);
    vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;

    D3D11_SUBRESOURCE_DATA vertexBufferData;
    vertexBufferData.pSysMem = triangleVertices;
    vertexBufferData.SysMemPitch = 0;
    vertexBufferData.SysMemSlicePitch = 0;

    DX::ThrowIfFailed(
        m_d3dDevice->CreateBuffer(
        &vertexBufferDesc,
        &vertexBufferData,
        &vertexBuffer2
        )
        );

步骤 2:更改动态资源

如果在创建资源时将资源指定为动态且可写,则可以稍后对该资源进行更改。

更改动态资源中的数据

  1. 为资源设置新数据。 声明 D3D11_MAPPED_SUBRESOURCE 类型变量并将其初始化为零。 你将使用此变量来更改资源。
  2. 禁用图形处理单元 (GPU) 访问要更改的数据,并获取指向包含数据的内存的指针。 若要获取此指针,请在调用 ID3D11DeviceContext::Map 时将D3D11_MAP_WRITE_DISCARD传递给 MapType 参数。 将此指针设置为上一步中 D3D11_MAPPED_SUBRESOURCE 变量的地址。
  3. 将新数据写入内存。
  4. 编写完新数据后,调用 ID3D11DeviceContext::Unmap 以重新启用 GPU 对数据的访问权限。

下面是更改动态顶点缓冲区的示例:

// This might get called as the result of a click event to double the size of the triangle.
void TriangleRenderer::MapDoubleVertices()
{
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));
    float2 vertices[] =
    {
        float2(-1.0f, -1.0f),
        float2(0.0f, 1.0f),
        float2(1.0f, -1.0f),
    };
    //  Disable GPU access to the vertex buffer data.
    auto m_d3dContext = m_deviceResources->GetD3DDeviceContext();
    m_d3dContext->Map(vertexBuffer2.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
    //  Update the vertex buffer here.
    memcpy(mappedResource.pData, vertices, sizeof(vertices));
    //  Reenable GPU access to the vertex buffer data.
    m_d3dContext->Unmap(vertexBuffer2.Get(), 0);
}

备注

使用动态纹理

建议仅为每个格式创建一个动态纹理,并且可能为每个大小创建一个动态纹理。 不建议创建动态 mipmap、多维数据集和卷,因为映射每个级别会产生额外的开销。 对于 mipmap,只能在顶层指定 D3D11_MAP_WRITE_DISCARD 。 仅映射顶级会丢弃所有级别。 此行为对于卷和多维数据集是相同的。 对于多维数据集,将映射顶级和面 0。

使用动态缓冲区

当 GPU 使用缓冲区时,在静态顶点缓冲区上调用 Map 时,会显著降低性能。 在这种情况下, Map 必须等到 GPU 完成从缓冲区读取顶点或索引数据后 ,Map 才能返回到调用应用,这会导致明显的延迟。 调用 Map ,然后在每帧中多次从静态缓冲区进行呈现也会阻止 GPU 缓冲呈现命令,因为它必须在返回映射指针之前完成命令。 如果没有缓冲命令,GPU 将保持空闲状态,直到应用填充完顶点缓冲区或索引缓冲区并发出呈现命令。

理想情况下,顶点或索引数据永远不会更改,但这并非始终可行。 在许多情况下,应用需要每帧更改顶点或索引数据,甚至可能每帧更改多次。 对于这些情况,建议使用 D3D11_USAGE_DYNAMIC创建顶点或索引缓冲区。 此用法标志会导致运行时针对频繁的映射操作进行优化。 D3D11_USAGE_DYNAMIC 仅在频繁映射缓冲区时才有用;如果数据保持不变,请将该数据置于静态顶点或索引缓冲区中。

若要在使用动态顶点缓冲区时获得性能改进,应用必须使用适当的D3D11_MAP值调用 MapD3D11_MAP_WRITE_DISCARD 指示应用不需要在缓冲区中保留旧的顶点或索引数据。 如果使用 D3D11_MAP_WRITE_DISCARD 调用 Map 时 GPU 仍在使用缓冲区,则运行时将返回指向新内存区域的指针,而不是旧缓冲区数据。 因此,当应用在新缓冲区中放置数据时,GPU 可继续使用旧数据。 在应用中无需进行额外的内存管理;当 GPU 用完旧缓冲区时,系统会自动重复使用或销毁旧缓冲区。

注意

使用 D3D11_MAP_WRITE_DISCARD 映射缓冲区时,运行时始终放弃整个缓冲区。 无法通过指定非零偏移量或有限大小字段在缓冲区的未映射区域中保留信息。

 

在某些情况下,应用需要为每个地图存储的数据量很小,例如添加四个顶点来呈现子画面。 D3D11_MAP_WRITE_NO_OVERWRITE 指示应用不会覆盖动态缓冲区中已使用的数据。 Map 调用将返回指向旧数据的指针,这将允许应用在顶点或索引缓冲区的未使用区域中添加新数据。 应用不得修改绘制操作中使用的顶点或索引,因为它们可能仍在由 GPU 使用。 建议应用在动态缓冲区已满后使用 D3D11_MAP_WRITE_DISCARD 来接收新的内存区域,这会在 GPU 完成后放弃旧的顶点或索引数据。

异步查询机制可用于确定 GPU 是否仍在使用顶点。 在使用顶点的最后一次绘图调用后发出 D3D11_QUERY_EVENT 类型的查询。 ID3D11DeviceContext::GetData 返回S_OK时,不再使用顶点。 使用 D3D11_MAP_WRITE_DISCARD 或不映射值映射缓冲区时,始终可以保证顶点与 GPU 正确同步。 但是,在没有映射值的情况下映射时,将产生前面所述的性能损失。

注意

Direct3D 11.1 运行时(从 Windows 8 开始提供)支持映射动态常量缓冲区和着色器资源视图, (SRV) 具有D3D11_MAP_WRITE_NO_OVERWRITE的动态缓冲区。 Direct3D 11 及更早版本的运行时限制到顶点或索引缓冲区的不覆盖部分更新映射。 若要确定 Direct3D 设备是否支持这些功能,请使用 D3D11_FEATURE_D3D11_OPTIONS 调用 ID3D11Device::CheckFeatureSupportCheckFeatureSupport 使用设备的功能填充 D3D11_FEATURE_DATA_D3D11_OPTIONS 结构的成员。 此处的相关成员是 MapNoOverwriteOnDynamicConstantBufferMapNoOverwriteOnDynamicBufferSRV

 

如何使用 Direct3D 11