OGRE 硬件缓冲区(硬件缓存)

·定义

实际上这个缓冲区就是一块malloc出来的存储区域,不过它不如malloc是在内存中申请的区域,而这个缓冲区是在gpu/agp中,它的写读速度更快。通常硬件缓冲区作用有拿来做顶点缓冲区,索引缓冲区,和象素缓冲区。

·使用

硬件缓冲区的管理是交由一个硬件缓存管理器负责的HardwareBufferManager,他负责缓冲区的创建和释放,它是几何体创建工厂,单键在Root初始化时就会被创建,所以,当我们需要一块内存的时候,一定不要直接New或malloc操作,而应当是这样

VerBuf = HardwareBufferManager::GetSingleton().CreateVertexBuffer()

·类型

       我们在分配一块硬件缓冲区时,需要传一个参数,来指明这块缓冲区的类型,是否需要频繁读写?这样对底层的硬件缓存区域分配管理提供很大的便利。我们来看一下硬件缓冲区的类型有哪些,我们分配它的时候应该做何选择。(HBU是HarewareBufferUsage简写)

       HBU_STATIC 静态硬件缓冲区,它意味着我们很少写入更新缓冲区,偶尔会从中进行数据读取。

       HBU_STATIC_WRITE_ONLY 只写静态硬件缓冲区。它意味着我们很少更新缓冲区,并且绝对不从该缓冲区进行数据读取。但是,当我们创建了一个备份缓冲的话,我们依旧可以对其读取。

       HBU_DYNAMIC 动态硬件缓冲区。它意味着我们会经常性的更新缓冲区中的数据,并且也希望能从其中读取数据,这一个效率最低的缓冲区使用方法。

       HBU­_DYNAMIC_WRITE_ONLY 只写动态硬件缓冲区,这个是个只许写入的硬件缓冲区,但当我们创建了一个备份缓冲的话,还是允许读取的。

       HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE 这个参数指明这个硬件缓冲区是一个需要频繁更新的缓冲区,大多数是每桢更新的数据存放在这里。但是需要注意的是,向该数据缓冲写入数据时记得加缓存锁。

建议:多使用WRITE_ONLY为后缀的缓冲区类型。即使必须进行读取,也建议使用备份缓冲,而非时刻可写的缓冲。

·备份缓冲

当我们创建一个WRITE_ONLY的硬件缓冲区后,我们有些时候假若非要从中读取数据,我们可以在创建缓冲区时传参,这样我们在内存中就会创建一个备份的缓冲区。每当我们向显存中写入一份数据的时候,Ogre会自动的先将这份数据拷贝到内存缓冲区后,再将其更新到显存中的硬件缓冲区。当然,这个技术会带来更多的开销,所以,非必要时不要用它。

·缓存锁

当我们更新写入缓冲或者在读取缓冲的时候,都应该先“锁”住它,以免它被修改。当然,之后记得解锁。pBuffer->Lock(begin , length , lockType)一般来说,锁的范围越小越便捷快速。但是锁的类型lockType也可以对读取的效率产生影响。

锁的类型包括:

·HBL_NORMAL 这种锁支持从缓冲区读取数据,但是效率很低下,因为它允许我们从硬件缓冲区进行数据读取。但是,当我们使用备份缓冲的话,这种影响会得到一些改善。

·HBL_READ_ONLY 这种锁意味着我们只能从缓冲区中进行内容的读取,禁止写入。此时建议我们使用备份缓冲,会提高我们效率。而且,此时我们实际上读取到的并非硬件缓冲区,而是内存中的数据。

·HBL_DISCARD 这种锁意味着我们每次操作都会将硬件缓冲区中的所有内容丢弃,一般这种操作也是会在每桢都处理的环境下才会使用,它是禁止读出的。但是一旦我们如此声明,也就基本上是向引擎宣称我们不会对硬件缓冲区的内容感兴趣,那么就不应当创建备份缓冲区。在我们没有使用备份缓冲区时尽量使用这种锁,它的效率很高,但若有了内存备份缓冲区的话,它就没有必要了。

·HBL_NO_OVERWRITE 当我们有些时候需要更新部分缓冲区而非全部缓冲区时,使用HBL_DISCARD就显的不适合了。此时我们就需要使用这种锁了,它的效率依旧很高,但是也仅是没有备份缓冲的时候才有作用。

·硬件缓冲区和缓冲锁使用经验

       1:因为最快最优秀的缓冲自然是通过 HBU_STATIC_WRITE_ONLY类型创建,不创建备份缓存,并且仅进行一次HBL_DISCARD的锁操作永不再额外处理的缓冲。

       2:当我们需要频繁更新的缓冲,可以用HBU_DYNAMIC_WRITE_ONLY来创建,不创建备份缓存,之后使用HBL_DISCARD加锁,若不想全部更新,则使用HBL_NO_OVERWRITE进行锁操作。

       3:若我们必须从缓冲区中读取数据的话,那么我们可以创建一个备份缓冲,用HBL_READ_ONLY将其锁住。可能的话,尽量声明缓冲区为静态的。

       4:在我们对顶点的不同元素需要使用不同模式的时候,我们不要通过指针大量更新缓冲区的全部顶点结构,应该分块更新。例如,我们假设只需要经常更新纹理坐标信息,那么我们应当将纹理坐标信息保存在一个单独的缓冲区区域,而其他的不经常更新的元素拆分保存在HBU_STATIC_WRITE_ONLY缓冲区中。

·顶点缓冲区

       VertexData中有几个重要成员:

       ·VertexStart                顶点起始位置信息

       ·VertexCount              顶点个数

       ·VertexDeclaration       一个指向顶点数据个数的指针

       ·VertexBufferBinding   一个指向顶点缓冲区绑定的指针

       其中顶点类型描述中我们需要强调一个顺序问题,为了支持DX9以前的版本,我们有必要按如下顺序声明和数据保存:

       1:顶点位置信息

       2:顶点绑定权重

       3:顶点法线信息

       4:顶点环境光颜色信息

       5:顶点镜面光颜色信息

       6:顶点纹理坐标信息

       除了上面的顺序需要注意以外,我们还需要注意的是顶点缓冲区中,是绝对不允许有空隙存在的。

       我们创建了一个顶点缓冲区后,我们还需要将起和指定的资源进行绑定。

       格式如下 verterBufferBinding->setBinding(0, vertexBuffer);

       之后,我们在运行时将顶点缓冲区绑定起来,循环的将其信息更新填充进入,Ogre提供了临接点与点之间的间隔长度和起始点信息,以便我们进行数据更新。

·索引缓冲区

       与顶点缓冲区基本都是一致的。创建后更新。唯一的区别就是创建时有些属性不同而已。

·象素缓冲区

       这里是保存纹理象素信息的。但是和顶点缓冲和索引缓冲不同的是,我们不能手动创建象素缓冲区,只有在我们创建一个纹理的时候,象素缓冲会自动被创建出来。

       象素缓冲区中支持的纹理类型:

              TEX_TYPE_1D 一维的纹理,通过1D纹理坐标来索引

              TEX_TYPE_2D 二维的纹理,通过2D文理坐标来索引

TEX_TYPE_3D 三维的纹理,通过3D文理坐标来索引

TEX_TYPE_CUBE_MAP 一个立方体的六个表面纹理,通过3D纹理进行索引。

象素缓冲区的内存分配格式

       Ogre中的图象数据的信息都被封装在一个个的PixelBox对象之中,我们需要注意的是PixelBox本身是保存在GPU中,但是真正的纹理都是保存在内存中,并非读到GPU中。GPU中的 PixelBox保存着内存中象素的格式位置内容信息的描述,但是PixelBox并没有内存管理的功能,它只能通过保存的内存指针来操作数据。象素盒中提供了通过深度,高度,宽度来索引象素的方法,若一维纹理没有高度和深度时,就将其参数补1。如下:(width, 1, 1),二维的纹理(width, height, 1)

象素缓冲区的更新

       Ogre提供了两种更新象素缓冲区的方法。

1:手动建立一个纹理并将一个图片放入这个纹理中。我们可以如下代码Image img; img.load(“xxx.jpg”, “General”); Texture pTex = Texture::getSingleton().createManual( …. ); pTex->GetBuffer(0,0)->blitFromMemory( );

2:对一个象素缓冲区加锁之后对其进行读取和写入。

Buffer->Lock(HarewareBuffer::HBL_DISCARD);

const PixelBox &pb = buffer->getCurrentLock();              // 锁好了之后进行处理

for (int I = 0; I < pb.GetWindth(); ++I)

{

       For( int j = 0; j < GetHeight(); ++j)

       {

       Static_cast<unit32*>(pb.data) // 数据获得,随便处理

}

}

Buffer->unlock(); //最后记得解锁。


你可能感兴趣的:(缓存)