zoukankan      html  css  js  c++  java
  • The Beauty of DirectX 11 (3) constant buffer, buffered/structure buffer

    The Beauty of DirectX 11 (3) --- constant buffer, buffered/structure buffer

    作者:clayman

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

     

    Constant Buffer

             Constant buffer(cb)DX10中引入的概念,它取代了DX9时代GPU常量寄存器的概念,允许通过一块大小可变的buffershader提供常量数据,而不是之前数量非常受限的n个寄存器,这也是我们遇到的第一种可在shader着色阶段由HLSL访问的资源。 管线的每个可编程阶段都能同时访问一个或者几个cb,对于shader代码来说,cb中的数据都是全局常量,作为cb而创建的资源不能绑定到其他*类型*的管线位置,但同一个cb可以同时绑定到管线的多个不同阶段。

     

             虽然cbDXshader constant的用法几乎一样,但要特别注意,cb中的数据总是作为一个整体被提交给GPU,这意味着即使cb中只有一个变量改变了,也必须重新提交整个cb。因此,所有关于DX10/11的文章都会强调不要把所有变量都放到一个cb中,而是按照变量改变的频率来组织变量,以尽量减少带宽消耗。比如,把viewMatrix,viewProjMatrix,eyePosition,sunVectorper-frame数据放到cb0中,只要每帧渲染前更新一次即可;把worldMatrixlocalLightobjectColor等作为per-object参数放到另外一块cb中,每次渲染物体时更新。游戏总是以每秒30fps以上的速度运行,并且每帧需要渲染大量物体,因此,合理组织cb非常重要!

     

             创建cb的代码和上一次创建vertex buffer的代码类似,大部分参数的用途也一致,选择合适的枚举,以获得最好性能。有三点需要注意的地方,1. cb中的数据结构应该直接对应着HLSL中的数据结构,这样更新cb时,只需要直接复制数据即可;2. cb的大小必须是16byten(nfloat4大小)3. cbbind flag只能是D3D11_BIND_CONSTANT_BUFFER,不能与其他位标记组合使用。使用cb时,不需要resource view,可以直接绑定到管线。

     

             因为cbshader直接可访问的资源,在HLSL中,用以下语法声明cb

    cbuffer Tramsform
    {
        matrix worldMatrix;
        matrix worldViewProjMatrix;
        matrix bones[26];
    }

    cbuffer View
    {
       float3 eyePosition;
       matrix viewMatrix;
    }

     

     

             cbuffer关键字声明cbcb的名称一般对HLSL没太多用处,而是让C++应用程序端来识别特定cb。可以通过shader reflection API获得每个cb中的数据结构信息,在介绍HLSL的部分会详细讨论。

    Buffer/Structured Buffer Resource

             接下来的一种资源,根据所保存的数据类型不同,又分为standard(buffered) buffer resouce(扭曲的名字)structured buffer resource。两者都是类似数组的结构,区别在于BBR的元素是普通内置类型,比如float4,而SBR的元素则是自定义的结构。之所以要做区分,原因是底层在处理数据映射到HLSL时的方式稍有不同。这种类型的buffer特别适合于shader需要访问大量数据的情况。也是目前为止第一种需要通过resource view绑定到管线的资源。所有可编程shader阶段都可访问b/s buffer,在pixel shadercomputer shader中还允许写入操作。通过不同的resource view来控制b/s buffer的可访问性,比如shader resource view, unordered access view或者两者一起使用,任何shade都可以使用shader resource view标志,而unordered access view则只能对pscs使用。当b/s buffer为只读时,可以同时绑定到管线的多个阶段;允许写入时(使用了uav标志)则只能绑定到一个位置。

     

             B/S buffer有三种典型的用途,第一种,用来保存静态数据,比如预计算的辐射传播(PRT)数据;第二种,运行时,由CPU计算出的数据,比如很多涉及到GPGPU的算法;最后一种则是由GPU计算填充的数据,比如由GPU计算的物理模拟结果。创建b/s buffer时,通常根据这三种用途,选择合适的访问参数。对于structured buffer来说,除了指定buffer大小之外,还需要提供每个结构的大小。以下是创建structured buffer的代码:

     

    ID3D11Buffer* CreateStructuredBuffer(uint count,uint structSize,bool CPUWritable,bool GPUWritable,D3D11_SUBRESOURCE_DATA* pData)
    {
             D3D11_BUFFER_DESC desc;
             desc.ByteWidth = count * structSize;
             desc.MiscFlags = D3D11_RESOURCE_MISC_MUFFER_STRUCTURED;
             desc.StructureByteStride = structSize;

             if(!CPUWritable && !GPUWriteable)
             {
                 desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
                 desc.Usage = D3D11_USAGE_IMMUTABLE;
                 desc.CPUAccessFlags = 0;
             }
             else if( CPUWritable && !GPUWritable)
             {
                 desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
                 desc.Usage = D3D11_USAGE_DYNAMIC;
                 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
             }
             else if( !CPUWritable && GPUWritable )
             {
                 desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
                 desc.Usage = D3D11_USAGE_DEFAULT;
                 desc.CPUAccessFlags = 0;
             }
             else if ( CPUWritable && GPUWritable)
             {
                //error handling
                
    //resource acan’t be write by both CPU and GPU simultaneously
             }

             ID3D11BUFFER* pBuffe = 0;
             HRESULT hr = pDevice->CreateBuffer( &desc,pData,&pBuffer);
             if(FAILED(hr))
             {
                return 0;
             }
             return pBuffer;
    }

     

             B/S buffer需要通过RV才能绑定到管线上,并且只能是shader resource view或者unordered access view。需要在创建RV时提供buffer的数据格式,BBR的格式必须是DXGI中定义的格式之一,SBR的格式总是DXGI_FORMAT_UNKNOWN。创建RV时,可以指定开始元素(ElementOffset)的位置和元素个数(ElementWidth),可以选择把整个或者一部分buffer数据包含在view中。对于HLSL来说,绑定到管线的buffer则总是一个下表标从在[0,ElementWidth]之间的数组。

     

    ID3D11ShaderResourceVIew* CreateBufferSRV ( ID3D11Rsource* pResource)
    {
             D3D11_SHADER_RESOURCE_VIEW_DESC desc;
             desc.Format = DXGI_FORMAT_R32G32B32_FLOAT;
             desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
             desc.Buffer.ElementOffset = 0;
             desc.Buffer.ElementWidth = 100;

             ID3D11ShaderResourceView* view = 0;
             HRESULT hr = pDevice->CreateShaderResourceView(pResource, &desc, &pView);

             return pView;
    }

     

             使用ElementOffsetElementWidth参数,可以把同一个B/S buffer分成多个子buffer,每个子buffer都可以有独立的RV。创建UAV的方法和SRV类似,但需要额外加一个属性:

    desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_COUNTER;

     

             为了在shader中访问B/S buffer数据, HLSL中也要用类似一种模板或泛型的语法声明相应的变量,下面是SBR的声明:

    struct GridPoint
    {
        float height;
        float4 flow;
    }

    RWStructuredBuffer<GridPoint> newWaterState  :register(u0);
    StructuredBuffer<GridPoint> currentWaterState  :register(t0);

     

             可以通过两种方法声明结构化bufferRWStructuredBuffer<>StructuredBuffer< >。区别在于前者对应着可读可写的bufferUAV),后者则是只读的数据(SRV),注意,不同buffer对应的寄存器标识也不一样。声明了HLSL变量以后,就能用普通数组的语法,通过索引访问buffer中的数据了。HLSL中提供GetDimensions()函数,可以返回buffer大小,或者是SBR中的数组元素个数。对于使用UAV绑定的SRB来说,由于指定了D3D11_BUFFER_UAV_FLAG_COUNTER标记,还可以使用IncrementCounter()DecrementCounter函数管理数据。

     

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

     

  • 相关阅读:
    7.1 深搜子集和问题 (枚举子集+剪枝)
    javascript的运算符
    javascript中的数据类型Null
    其他进制数字转换
    javascript数据类型转换number
    javascript强制数据类型转换String
    自增自减
    一元运算符
    javascript中的数据类型boolean
    自增自减的练习
  • 原文地址:https://www.cnblogs.com/clayman/p/2216889.html
Copyright © 2011-2022 走看看