Practical Rendering and Computation with Direct3D 11学习笔记(2.2.3)常量缓冲区

2.2.3常量缓冲区

2.2.3.1常量缓冲区概述(constant buffer)

     常量缓冲区是第一个我们接触的能进入可编程着色器阶段并且随后被HLSL代码使用的资源类型,一个常量缓冲区被用来向一个正在管线中执行的可编程着色器 应用程序提供常量信息。术语常量(constant)指的是在这个缓冲区内保存的常量通过绘画、调度调用等不会发生变化的事实。只改变管线调用之间的任何信息,例如世界变换矩阵或者对象颜色,在一个常量缓冲区被提供给着色器程序。这个机制是数据从主程序变换到可编程着色器阶段的主要思想。包含在一个常量缓冲区里的信息类型和数量可能从一个缓冲区到另一个缓冲区改变。这完全依靠每一个特定所需着色器程序的数据,并且在一个着色器程序被结构声明所定义。缓冲区能被定制在一定程度上任何 基本HLSL类型组合,以及创建这些基本类型组合。HLSL基本类型包括标量、向量、矩阵,这些类型数组、实例类和这些结构类的组合。一个这些类型的组合如图2.2.3.1描述。 
    
   迄今为止常量缓冲区与其他我们讨论过的缓冲区有些不同,顶点缓冲区和索引缓冲区定义了一个基本数据元素然后在一个类似数组形式多次重复。常量缓冲区定义了一个基本元素后,不会提供元素的多个实例,缓冲区会创建一个足够大的能容纳预期的信息,但是不会更大,这暗示常量缓冲区创建了一个结构而不是数组。

2.2.3.2 使用常量缓冲

   每一个可编程管线阶段可以接受一到多个常量缓冲区,然后把缓冲区的信息转化以便着色器程序使用。数据存取访问就如在着色器程序中的结构内容被全局声明,这就是说在相对全局作用域每个结构元素必须有个独一无二的名字。这种能力提供了一个巨大的自由去增加任何为特定渲染算法着色器程序的变化。需要重点关注的是在一个管线中一个缓冲区被创建绑定作为一个常量缓冲区那么他不能再绑定到其他类型的连接点。在实际中,这不是一个问题,因为在任何情况下一个缓冲区的内容对所有的可编程管线阶段是使用的。在管线中常量缓冲区可用绑定位置如图2.2.3.2
   
   使用相对大的常量缓冲区不是意味着你下意识的为着色器程序中所有的变量创建一个足够大的缓冲区,因为常量缓冲区直线的更新使用的是CPU,有任何改变后他们的内容必须被上传到GPU。让我们假设一种情况,假设一个着色器程序需要10个参数,但是每次渲染对象只在两个之间变动,每次Draw调用之间我们写入10个参数到缓冲区,而更新两个变化的参数,因为整个缓冲区一直在更新。根据需要渲染对象的数量,那些增加的参数更新能计算出一个无须浪费的带宽。如果我们创建两个缓冲区呢,其中一个包含8个静态参数而第二个包含两个动态参数,我们只更新改变的参数,这样就大幅坚守了每帧所需跟新的数据量。但是我们应该注意确保缓冲区在它们需要更新的时候被更新。在更新常量缓冲区中另一个潜在的减负来自于只支持读访问的着色器程序的事实。因为只读的常量缓冲区能同时被绑定到多个管线位置,而没有造成内存冲突的可能性。

2.2.3.3创建常量缓冲区

为了得到最好的显示效果常量缓冲区也提供了一些不同的资源配置,依据一个常量缓冲区怎样被CPU更新,一到两个典型的配置被选择,如果常量缓冲区在程序生命期被更新许多次,一个动态缓冲区资源被多数场景采用。当然,如果一个常量缓冲区包含一些数据在程序期作为一个静态缓冲区创建更有效率,那么使用Immutable 使用标志。下面将展示一个一个常量缓冲区怎样被创建: 
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_BUFFERj 
// Select the appropriate usage and CPU access flags based on the passed 
// in flags 
if ( dynamic && CPUupdates ) 

desc.Usage = D3D11_USAGE_DYNAMIC; 
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 

else if ( dynamic && !CPUupdates ) 

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更新,例如使用ID3D11DeviceContext: :CopyStructureCount()方法 复制Append/Consume缓冲区到常量缓冲区的元素数量,在这种情况下,我们应该要求一个Default 使用标志,但是我们不应设置任何CPU访问标志。这允许运行时选择仅在GPU上使用而创建资源。 
这有几个有趣的术语在上面的代码中,第一件是应用程序定义了一个C++结构去映射被期望的常量缓冲区内容在HLSL中。这允许程序更新这个结构的一个系统内存实例,这随后能直接复制到缓冲区。第二点是一个常量缓冲区字宽必须被创建为16 字的倍数。这个需求允许有效处理缓冲区使用4-tuple注册类型GPU,而且仅显示作为常量缓冲区需求。这必须在C++/C中被解释为结构声明。第三件事,缓冲区类型不需要去包含任何绑定标志除了D3D11_BIND_CONSTANT_BUFFER.另外,在实践中,这不是一个限制,因为这没有位置所期望去绑定一个常量缓冲区到其他位置。 
尽管常量缓冲区区被绑定到可编程着色器阶段,而且进入HLSL,它们内容并不解读为任何资源视图。它们指定为合适的内容为HLSL,所以不需要资源视图。

HLSL常量缓冲区资源对象

        这是我们遇到的第一个在可编程着色器阶段能直接访问HLSL代码内部的缓冲区类型,在HLSL中,常量缓冲区是被声明为cbuff关键字的资源对象,一个简单的声明如下:下面是三个从HLSL里摘录的声明。
       cbuffer Transforms
                  {
                         matrix WorldMatrixj
                         matrix ViewProjMatrix;
                         matrix SkinMatrices[26];
                   } ;
       cbuffer LightParameters
                   {
                         float3 LightPositionWS;
                         float4 LightColor;
                   } ;
        cbuffer ParticlelnsertParameters
                   {
                         float4 EmitterLocation;
                         float4 RandomVector;
                    };

     每一个被声明在这个cbuffer结构里的变量能直接被HLSL着色器程序使用,犹如变量被声明为一个全局变量。常量缓冲区名字(如上面的Transforms、 LightParameters、ParticlelnsertParameters)被主程序使用根据名字去人缓冲区,然后去加载适当的内容给他。然而,缓冲区名字在HLSL没被使用。正如常量缓冲区的名字,常量缓冲区的单独元素的名字和类型也能访问着色器反射API.这一系列方法能被用来确定每个子参数的名字和类型,这允许程序知道那个信息在运行时去插入缓冲区。














你可能感兴趣的:(渲染,管线)