Performance Optimizations (Direct3D 9)
General Performance Tips
- 尽量少用Clear : 尽量在原有的上面画。
- 尽量少变换状态。
- 尽量用小的纹理
- 尽量用front->back顺序画screen
- 尽量用triangle而不是lists或fanarrange strips to reuse triangle vertices sooner, rather than later.
- 尽量少用性能需求高的效果. 将其降级使用。
- 经常 test 你应用程序的性能。
- 尽量少交换vertex buffer
- 尽量多用static vertex buffers
- 尽量用一个大的static vertex buffer有不同的FVF为一个对象。而非多个。
- 尽量用32bytes的整数倍, 进入vertex buffer, 否则选择尽量小的格式。
- 尽量用定点索引。
- 尽量在同一时间清理depth buffer 和 stencil buffer
Databases and Culling
为对象建造一个可靠的数据库 是 D3D杰出表现的关键。 这比 优化 光栅化和硬件更重要。
你应该保持用尽量少的多边形。使用多边形时一定要保证不会牺牲以后的效率。记住最快的多边形是不去画。
Batching Primitives
将相同状态的Primitive打包,一起渲染。
举例: 一个对象 包含2种纹理。 把素有no.1纹理的点 batch and render. 再把no.2纹理的点batch and render.
Lighting Tips
光的使用,要涉及每个顶点计算。
- 尽量少的光源。使用整体级别增量,而非增加光源。例如使用环境光。
- 平行光,比点光,聚光灯,有更高的效率。因为平行光是固定的,不需要为每点计算。
- 聚光灯,比电光,有更高的效率。
- 镜面光,是双倍性能消耗。只有当必须的时候才用。默认是关闭D3DRS_SPECULARENABLE. 当设置了纹理时, 必须把镜面光设置为0, 只把材料的颜色设置为0,0,0是不成的。
Texture Size
纹理映射很依赖于内存速度。这里有一些最大化你cache速度的方法。
- 纹理要小。小纹理更可能被缓存于CPU的2级缓存中。
- 尽量用方纹理。256*256是最快的。
Using Dynamic Vertex and Index Buffers
当 图像处理器 在使用vertex buffer时, 锁住vertex buffer, 这显然有效率问题。
必须等到 图像处理 完所有定点, 才能返回到 应用程序, 这有明显的延迟。
--------------------------
动态定点缓存 可以带来性能提高。
应用程序必须调用Lock加适当的flags。
D3DLOCKDISCARD使应用程序不必保持old vertex或old index 的buffer.
如果graphic processor仍在使用这些buffer, 当lock被调用时(有人也要进入, 使用buffer.),一个指向新内存区域的指针被返回, 来替代老的buffer指针。
这允许graphics processor能继续使用旧的数据, 同时应用程序把数据放在新buffer中一份。
这不会带来额外的内存维护;当old buffer用完之后, 会被自动释放或重新利用。
注意: lock buffer with D3DLOCK_DISCARD总是丢失整个buffer, 如果你在冲掉缓存时(后来进入lock的行为), 设置了起始点和偏移量,这将使其他定点丢失。
---------------------------------
这里有一种情况, 应用程序 每次lock时, 需要存储的data是较小的,
例如, 增加4个点精灵去渲染。
D3DLOCK_NOOVERWRITE使得应用程序不会覆盖 正在使用的 动态缓存。
后者调用lock时, 会返回一个指向old data的指针, 允许应用程序 在没有使用的vertex or index区域 增加新的data。 应用程序 不应该 修改vertices or index, 当在执行draw操作graphics processor可能正在使用这些定点时。
应用程序 应该在新buffer完全接收完后, 并在graphics processor使用完后, 完全被丢掉。
----------------------------------
异步查询机制有助于 判断定点是否仍被graphics processor使用。
发行一个D3DQUERYTYPE_EVENT的查询, 在所有DrawPrimitive完成后。
当GetData返回S_OK时, 说明graphics processor没有再使用buffer.
lock buffer 带 D3DLOCK_DISCARD或没有flags的方式,总能保证使用的vertex buffer是最新的, 但是使用lock 不带 flags的方式 会产生性能问题。
其他API不保证 graphics processor 已经完成对vertices的使用BeginScene, EndScene, Present.
// USAGE STYLE 1
// Discard the entire vertex buffer and refill with thousands of vertices.
// Might contain multiple objects and/or require multiple DrawPrimitive
// calls separated by state changes, etc.
// Determine the size of data to be moved into the vertex buffer.
UINT nSizeOfData = nNumberOfVertices * m_nVertexStride;
// Discard and refill the used portion of the vertex buffer.
CONST DWORD dwLockFlags = D3DLOCK_DISCARD;
// Lock the vertex buffer.
BYTE* pBytes;
if( FAILED( m_pVertexBuffer->Lock( 0, 0, &pBytes, dwLockFlags ) ) )
return false;
// Copy the vertices into the vertex buffer.
memcpy( pBytes, pVertices, nSizeOfData ); //因为使用完后,
m_pVertexBuffer->Unlock();
// Render the primitives.
m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, nNumberOfVertices/3)