The Beauty of DirectX 11 (4) -- Append/Consume,Byte Address and Indirect Argument Buffer

The Beauty of DirectX 11 (4) -- Append/Consume,Byte Address and Indirect Argument Buffer

作者:clayman

仅供个人学习使用,请勿转载,勿用于任何商业用途。

 

Append/Consume Buffers

         Appendcomsume buffer都是SBV的变体。本质上,他们都是需要UAV绑定的资源,但他们在HLSL中实现了一种类似堆栈的访问行为:使用append()函数把元素pushbuffer中,或者用consume()函数拉出元素。对于这两种buffer来说,添加元素的顺序并不重要,但所添加元素的数量很重要。UAV内部会记录添向buffer中添加或者删除的元素数量,因此当不同的GPU线程同时操作这样的buffer时,并不需要同步,大大提高了效率。

 

         我们通过一个简单的例子来介绍这种buffer的用途,一个基于GPU的粒子系统:系统使用2buffer保存粒子信息,一块保存粒子当前的状态,另一块保存更新过的粒子。运行时,computer shader中的每个一个GPU线程都使用consume()方法读取当前粒子信息,进行更新计算,然后,用append()方法把结果写到另一块buffer中。由于每个粒子的状态都是独立的,所以buffer中粒子的顺序无关紧要,只要保证数量正确即可。

 

         创建A/C类型的buffer约束比较严格,因为buffer需要可同时读写,需要使用D3D11_USAGE_DEFAULT标志,此外绑定标识必须包含3D311_BIND_UNORDERED_ACCESS标识,一般还需要加上D3D11_BIND_SHADER_RESOURCE。创建相应的RV时,数据格式总是DXGI_FORMAT_R32_UNKNOWN,同时无论是append还是comsume buffer都必须用D3D11_BUFFER_UAV_FLAG_APPEND参数。上一部分介绍B/S buffer时曾经说个可以把B/S buffer划分为多个subresource,分别使用不同的RV,但是,对于允许读写操作UAV来说却不行,这样的资源只能作为一个整体。

         下面是在HLSL中声明A/C buffer的代码

struc Particle
{

         float3 position;

         float3 velocity;

         float time;

}

AppendStructuredBuffer<Particle> newSimulationState : register(u0);

ConsumeStrucuredBuffer<Particle> currentSimulationState : register (u1);

 

Byte Address Buffers

         BABHLSL提供了一种相对低级的方式来访问显存块。与通过索引访问元素的方法不同,BAB使用字节地址访问其中的元素: 字节地址n代表的值为从资源开头偏移n个字节后的432位无符号整数的值,注意,n必须为4的倍数,指向的432位无符号整数也可以通过转型转义为其他类型的值。这种类型的bufferHLSL提供了强大的数据管理操作能力,几乎可以用它来实现任意类型的数据结构。

 

         举例来说,一个储存32位颜色值的链表,每个链表节点由一个颜色值和指向下一个元素的标记组成。当添加第一个元素时,颜色值被写到偏移值为0的位置,指向下一个元素的索引为-1 添加第二个元素时,程序首先读取偏移值为0处的232位值,然后通过索引,依次找到链表末端,最后完成添加。使用BAB,以前很多只能在CPU上计算的传统算法,都可以能GPU上实现了! 当然,由于GPU天生的平行性,实际情况要稍微复杂一些,以后会仔细介绍。

 

         BAB的创建方式与前面介绍的几种资源基本一致,唯一的区别是需要把MiscFlags指定为D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS。只读的BAB可以通过shader resource view绑定到任意shader阶段;作为UAV使用的可读写BAB则只能绑定到pixel shadercompute shader阶段。两种情况下,RV都必须是DXGI_FORMAT_R32_TYPELESS格式,UAV还必须加上D3D11_BUFFER_UAV_FLAG_RAW标识。

 

         HLSL中,用以下代码声明BAB资源:

ByteAddressBuffer rawBuffer0;

RWByteAddressBuffer rawBUffer1;

 

Indirect Argument Buffers

         前面介绍的几种buffer主要用于把数据从CPU端传递到GPU端,而IAB的设计理念是允许GPU自己填充数据,并且在后面的计算中使用。IAB主要在以下三个函数中使用:

DrawInstancedIndirect(ID3D11Buffer* pBufferForArgs, UINT AlignedByteOffsetForArgs);

DrawIndexedInstancedIndirect(ID3D11Buffer *pBufferForArgs,UINT AlignedByteOffsetForArgs);

void DispatchIndirect(ID3D11Buffer *pBufferForArgs,UINT AlignedOffsetForArgs);

 

         三个函数都以indirect后缀结尾,与没有indirect后缀的方法相比,虽然参数类型不同,本质却相同,比如DrawInstanced()接受4个参数:vertexCountPerInstance,instanceCount,startVertexLocationstartInstanceLocationIndirect版本同样需要这4个参数,只不过把他们放到了第一个参数pBufferForArgs中,并且通过第二个参数定位他们在buffer中的位置。因此,必须保证pBufferForArgs在指定偏移位置有4个有效的uint值,并且偏移值必须以4byte对齐的,pBufferForArgs可以在不同位置,包含多组不同的参数值,通过offset选择希望使用的参数。这样的好处在于 buffer数据可由GPU填充,所以CPU完全不需要知道要绘制多少图元!图形管线输出的数据和CPU计算的结果都可以用来更新IAB数据。 比如,把compute shader的计算结果保存到AppendStructuredBuffer中,使用ID3D11DeviceContext:CopyStructureCountAppendStructuredBuffer中的数据复制到IAB中,最后使用DrawInstancedIndirect()CS计算的数据直接渲染出来,整个过程只需要很少的CPU参与,CPU完全不用知道渲染了多少图形。

 

         创建IAB的方法和前面几种buffer没有太多区别,一般来说,如果希望用GPU填充数据,那么一定要使用default usage,此外,MiscFlags必须是D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS,最后,byteWidth必须是4byten倍。

         当由GPU填充IAB数据时,比如通过stream outrender target或者UAV方式,必须和配合相应的resource view一起使用,当由CPU填充数据时,则主要通过UpdateSubresource方法和CopyStructureCount

 

      至此,我们介绍了DirectX 11中的所有buffer resource,下一部分将讨论texture resource。

 

------------------------------to be continue----------------------------------------------

 

再次提醒,已经发了的四篇文章对DX11的介绍都比较抽象,如果觉得看不太懂,那么应该先找一些有demo的DX11的入门教程来看。这系列的文章偏重对DX11 API原理的介绍和梳理,适合作为参考资料,而不是第一本入门教程。未来的内容在介绍完texture resource之后,还主要有以下几个部分:

Rendering Pipeline 介绍流水线的每一个阶段

Tessellation Pipeline

Computation Pipeline   着重介绍DX11中两个最重要的功能

HLSL 5.0

Multithreaded Rendering

 

 

 

 

你可能感兴趣的:(The Beauty of DirectX 11 (4) -- Append/Consume,Byte Address and Indirect Argument Buffer)