Practical Rendering and Computation with Direct3D 11
+ -

2.2.1缓冲区资源-常量缓冲区

2024-06-14 19 0

常量缓冲区是我们遇到的第一种资源类型,可从可编程着色器阶段访问,随后在HLSL代码中使用。

常量缓冲器用于向在流水线中执行的可编程着色器程序提供常数信息

术语常量是指在执行draw或dispatch调用的整个过程中,该缓冲区内的数据保持不变。仅在管道调用之间更改的任何信息(如世界变换矩阵或对象颜色)都会在常量缓冲区中提供给着色器程序。此机制是从主机应用程序向每个可编程着色器阶段传输数据的主要方式。常量缓冲区中包含的信息的类型和数量可能因缓冲区而异。这完全取决于每个特定着色器程序所需的数据,并由着色器程序中的结构声明定义。缓冲区可以或多或少地适应基本HLSL类型的任何组合,以及由这些基本类型组成的结构。我们将在第6章“高级着色语言”中更详细地介绍HLSL数据类型,但现在我们将提到这些类型包括标量、向量、矩阵、这些类型的数组、类实例以及结构中每种类型的组合。这些类型的单一组合如图2.12所示。
163731733447

常量缓冲区与我们迄今为止讨论的其他缓冲区有些不同。顶点缓冲区和索引缓冲区都定义了一个基本数据元素,然后以类似数组的方式多次重复该元素。常量缓冲区定义了一个基本元素,但它不提供多个元素实例:缓冲区创建得足够大,可以容纳所需的信息,但不能更大。这意味着常量缓冲区实现的是一个结构,而不是数组。

使用常量缓冲区

每个可编程流水线级可以接受一个或多个常量缓冲区。然后,它使缓冲区中的信息可用于着色器程序。访问数据时,就好像在着色器程序中全局声明了结构内容一样。这意味着在这个伪全局范围内,每个结构的元素都必须具有唯一的名称。此功能为向特定渲染算法所需的任何着色器程序添加变化提供了很大的灵活性。需要注意的是,为绑定创建的缓冲区作为常量缓冲区可能不会绑定到管道上的任何其他类型的连接点。在实践中,这不是问题,因为常量缓冲器的内容无论如何都已经可用于所有可编程流水线级。在管道上可用于绑定常量缓冲区的位置如图2.13所示。

164122424674

使用相对较大的常量缓冲区的能力并不意味着应该为着色器程序中所需的所有变量自动创建一个较大的缓冲区。由于常量缓冲区在CPU使用之间进行更新,因此在进行任何更改后,必须将其内容上载到GPU。

让我们以一个假设的情况为例。假设着色器程序需要十个参数,但每次渲染对象时只有两个参数发生变化。在这种情况下,我们将在每次绘制调用之间将所有十个参数写入缓冲区,只是为了更新两个不同的参数,因为整个缓冲区总是完全更新的。根据要渲染的对象数量,这些额外的参数更新可能会导致大量不必要的带宽浪费。如果我们改为创建两个常量缓冲区,其中一个缓冲区保存八个静态参数,第二个缓冲区保留两个动态参数,我们可以只更新不断变化的参数,并显著减少每帧所需的数据更新量。然而,我们需要注意确保缓冲区只有在真正需要的时候才会更新!

更新常量缓冲区的另一个潜在减少源于这些缓冲区仅支持着色器程序的读取访问。由于它是只读的,单个常量缓冲区可以同时绑定到管道中的多个位置,而不可能导致内存访问冲突。

创建常量缓冲区

常量缓冲区还提供了许多不同的资源配置,以获得尽可能好的性能。根据CPU更新常量缓冲区的频率,选择两种典型配置之一。如果在应用程序的整个生命周期中多次更新常量缓冲区,那么动态缓冲区资源最有意义。当然,如果常量缓冲区将包含一些在整个应用程序中不会更改的数据(例如固定的后台缓冲区大小),则将其创建为具有不可变immutable使用标志的完全静态缓冲区会更有效。清单2.8演示了通常如何创建常量缓冲区。

ID3DllBuffer* CreateConstantBuffer( UINT size,bool dynamic,bool CPUupdates,D3D11_SUBRES0URCE_DATA* pData )
{
    D3D11_BUFFER_DESC desc;
    desc.ByteWidth = size;
    desc.HiscFlags = 0;
    desc.StructureByteStride = 0;
    desc.BindFlags = D3Dll_BIND_C0NSTANT_BUFFER;//常量缓冲区
    // Select the appropriate usage and CPU access flags based on the passed
    // in flags
    if ( dynamic && CPUupdates )//着色器更改,CPU可更改?
    {
        desc.Usage = D3D11_USAGE_DYNAMIC;
        desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    }
    else if ( dynamic && !CPUupdates )//着色器更改,CPU不可更改?
    {
        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.CPUAccessFlags = 0;
    }
    else  //永远不变
    {
        desc.Usage = D3D11_USAGE_IMMUTABLE;
        desc.CPUAccessFlags = 0;
    }
    // Create the buffer with the specified configuration
    ID3D11Buffer* pBuffer = 0;
    HRESULT hr = g_pDevice->CreateBuffer( &desc, pData, &pBuffer );
    if ( FAILED( hr ) )
    {
    // Handle the error here...
    return( 0 );
    }
    return( pBuffer );
}

与顶点和索引缓冲区一样,动态常量缓冲区的创建使用D3D11_USAGE_dynamic使用标志与D3D11_CPU_ACCESS_WRITE标志相结合,以允许CPU在运行时更新资源。对于静态内容,使用D3D11_USAGE_IMMUTABLE用法时没有任何CPU访问标志。一种额外的可能性是拥有一个仅由GPU在运行时更新的缓冲区,例如使用ID3D11 DeviceContext::CopyStructureCount()方法将Append/Consume缓冲区中的元素数复制到常量缓冲区时。在这种情况下,我们需要一个默认的使用标志,但我们不会设置任何CPU访问标志。这允许运行时优化创建的资源,以便仅在GPU上使用。

有几个感兴趣的项目没有显示在清单2.8中。首先要注意的是,应用程序通常定义一个C++结构,该结构反映HLSL中常量缓冲区的所需内容。这允许将应用程序更新应用于此结构的系统内存实例,然后可以将其直接复制到缓冲区中。我们将在本章后面探讨更新资源内容的方法。第二个关注点是,必须创建一个字节宽度为16字节倍数的常量缓冲区。这一要求允许使用GPU的4元组寄存器类型有效地处理缓冲区,并且它仅表现为对恒定缓冲区的要求。这必须在C/C++中的结构声明中说明。第三个有趣的点是,缓冲区描述不允许包含D3D11_bind_C0NSTAI\IT_buffer以外的任何其他绑定标志。同样,在实践中,这并不是一个很大的限制,因为通常不存在希望将常量缓冲区绑定到管道中的另一个位置的情况。

资源视图要求

尽管常量缓冲区绑定到可编程着色器阶段,并且可以通过HLSL访问,但它们的内容不会以任何方式通过资源视图进行解释。它们是按照在HLSL中应该可用的方式指定的,因此不需要使用资源视图。

0 篇笔记 写笔记

2.2.1缓冲区资源-常量缓冲区
常量缓冲区是我们遇到的第一种资源类型,可从可编程着色器阶段访问,随后在HLSL代码中使用。常量缓冲器用于向在流水线中执行的可编程着色器程序提供常数信息。术语常量是指在执行draw或dispatch调用的整个过程中,该缓冲区内的数据保持不变。仅在管道调用之间更改的任何信息(如世界变换矩阵或对象颜色......
6.4.1默认常量缓冲区
任何在没有static常量修饰符的情况下声明的全局变量都将被编译器视为名为$Globals的默认常量缓冲区内的常量。类似地,标记为统一的着色器入口点函数的参数将被放置在另一个名为$Params的默认常量缓冲区中.......
作者信息
站长漫谈
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!