问题
你需要频繁更新顶点数据,如果使用VertexBuffer的SetData方法会拖慢程序。
解决方案
如果你计划频繁更新顶点数据,应该使用DynamicVertexBuffer而不是VertexBuffer。
这会让数据不是存储在最快的显存中,而是更容易处理的某些内存中。所以,这样做会让性能有一点降低,但在VertexBuffer中频繁地改变数据会让性能得到极大地提高。
对于VertexBuffer,只要显卡被要求切换任务(例如,使用Alt+Tab切换程序),DynamicVertexBuffer中的内容必须被重载,你可以通过订阅到DynamicVertexBuffer的ContentLost事件上实现这个功能。
工作原理
DynamicVertexBuffer的工作原理与VertexBuffer很像。首先需要一个顶点数组,如下所示:
private void InitVertices() { myVertexDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements); vertices = new VertexPositionTexture[6]; int i = 0; vertices[i++] = new VertexPositionTexture(new Vector3(-5.0f, -3, -1), new Vector2(-0.5f, 1.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(-2.5f, 5, -1), new Vector2(0.5f, -1.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(0, -3, -1), new Vector2(1.5f, 1.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(0, -3, -1), new Vector2(-0.5f, 1.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(2.5f, 5, -1), new Vector2(0.5f, -1.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(5.0f, -3, -1), new Vector2(1.5f, 1.5f)); dynVertBuffer = new DynamicVertexBuffer(device, VertexPositionTexture.SizeInBytes * vertices.Length, BufferUsage.WriteOnly); dynVertBuffer.SetData(vertices, 0, vertices.Length, SetDataOptions.NoOverwrite); dynVertBuffer.ContentLost +=new EventHandler(dynVertBuffer_ContentLost); }
创建DynamicVertexBuffer接受和创建VertexBuffer一样的参数,但SetData方法接受一个新的加强的参数,我会在下面讲到。最后一行代码是新的,如果显卡被要求处理另一个程序,设备会丢失,DynamicVertexBuffer的内容也会丢失。所以你要在DynamicVertexBuffer 的ContentLost事件上订阅一个方法。只要DynamicVertexBuffer丢失了内容就会引发ContentLost 事件,对应的方法就会被调用,这个方法会重载DynamicVertexBuffer中的内容。本例中,你将dynVertBuffer_ContentLost方法订阅到事件。
当然你还要定义这个方法,在这个方法中重载缓冲的内容:
private void dynVertBuffer_ContentLost(object sender, EventArgs e) { dynVertBuffer.SetData(vertices, 0, vertices.Length, SetDataOptions.NoOverwrite); }
注意:这需要本地处理顶点数据。因为只有当顶点数据变化地很频繁时才使用DynamixVertexBuffer,所以首先要保证数据没有问题,记住你永远不会从(Dynamic)VertexBuffer中读取数据。
当进行到绘制这步时,DynamicVertexBuffer的工作方式与VertexBuffer是相同的。
性能考虑:DynamicVertexBuffer.SetData方法
因为当你想频繁更新数据时才会使用DynamicVertexBuffer,所以你会经常使用SetData 方法。因此DynamicVertexBuffer的SetData方法还接受一个额外的参数SetDataOptions。
这个参数让你可以指定一些选项可以提高程序的速度。默认情况下,当你想覆盖显存中的内容时,因为不支持同时读写,显卡无法从显存中读取数据。当你把大量数据写至内存时会导致显卡的绘制过程中止,这是因为显卡要等待你的复制过程结束。但是,有两个方法可以让你确保显卡不会等待复制操作结束。你可以使用SetDataOptions 参数,下面是可选项:
- SetDataOptions. None:这个选项可以完全控制覆盖VertexBuffer 哪一部分。但是,如前面解释的那样,这会导致性能降低。如果显卡绘制的数据是从VertexBuffer 之前内容提取的,那么显卡必须停止绘制直到较慢的复制过程结束。
- SetDataOptions. Discard:使用这个选项表示你不再需要VertexBuffer的之前的内容。这时数据存储在显存的一个新的位置。在写入过程发生时,显卡还能继续使用老的数据。一旦写入过程完成,显卡可以使用新的数据绘制而抛弃老数据。简而言之,显卡无需等待,但你必须重写所有数据。(在Xbox平台上无法使用这个选项,你只能调用DrawUserIndexedPrimitive绘制频繁更新的顶点。)
- SetDataOptions . NoOverwrite:这个选项很强大但比较危险。你必须保证正在覆盖的VertexBuffer部分没有被渲染过程使用。这样,你可以覆盖VertexBuffer 的特定部位,此时显卡无需等待复制过程结束,因为你正在覆盖的部分不参与绘制过程。这比使用Discard选项快,因为你无需保留在内存中保留一块新的部分。