SharpDX中的Buffer更新
最近在用C#写一个基于图形API的UI库,底层用的是Managed的DX库SharpDX。由于UI绘制会在每一个UI事件重绘,更新Buffer变得十分的频繁。在调试的过程中时不时会遇到一个C#的System.AccessViolationException
。这个异常是用了Intptr
指针在非托管层调用时越界导致的,搜索了很久最后还是自己思考下想通了。
调用的接口是DeviceContext.UpdateSubresources
,在SharpDX中的代码如下。
1 | var rawbuffer = buffer.GetRawObject() as Buffer; |
其中data
是一个数组,rawbuffer
是一个vertexBuffer。我们先使用GCHandler
把数组的内存位置固定住,然后将指针传递给DX,从指针位置拷贝数据到VertexBuffer中,最后释放Pin住的数组。之前在写代码的时候并没有考虑太多,忽略了这里的更新Buffer并没有传递拷贝的地址长度的参数。也就是说拷贝数据的时候是按照DX中Buffer的大小来进行复制的,而不是这个data对象的大小。这样由于data在托管内存中,当Buffer的大小大于data的大小时,拷贝的指针就会越界,从而抛出异常。
回头来看DirectX 11中的Buffer在创建Buffer的时候,有一个参数是Usage,这里我们只讨论D3D11_USAGE_DEFAULT
和D3D11_USAGE_DYNAMIC
。在Default的Buffer可使用UpdateSubresources
的接口来更新Buffer数据,同时这个更新只能是一次性更新整个Buffer,就是上面说到的情况,不能只更新部分。而对于需要频繁动态更新的Buffer设置为D3D11_USAGE_DYNAMIC
,图形API会进行性能优化,同时可以使用Map
和UnMap
来更新Buffer数据。