SharpDX中的Buffer更新

最近在用C#写一个基于图形API的UI库,底层用的是Managed的DX库SharpDX。由于UI绘制会在每一个UI事件重绘,更新Buffer变得十分的频繁。在调试的过程中时不时会遇到一个C#的System.AccessViolationException。这个异常是用了Intptr指针在非托管层调用时越界导致的,搜索了很久最后还是自己思考下想通了。

调用的接口是DeviceContext.UpdateSubresources,在SharpDX中的代码如下。

1
2
3
4
5
6
7
8
var rawbuffer = buffer.GetRawObject() as Buffer;
if (rawbuffer == null) throw new Exception();
var pinnedptr = GCHandle.Alloc(data, GCHandleType.Pinned);
m_obj.UpdateSubresourceSafe(new DataBox()
{
DataPointer = pinnedptr.AddrOfPinnedObject()
},rawbuffer,subresources);
pinnedptr.Free();

其中data是一个数组,rawbuffer是一个vertexBuffer。我们先使用GCHandler把数组的内存位置固定住,然后将指针传递给DX,从指针位置拷贝数据到VertexBuffer中,最后释放Pin住的数组。之前在写代码的时候并没有考虑太多,忽略了这里的更新Buffer并没有传递拷贝的地址长度的参数。也就是说拷贝数据的时候是按照DX中Buffer的大小来进行复制的,而不是这个data对象的大小。这样由于data在托管内存中,当Buffer的大小大于data的大小时,拷贝的指针就会越界,从而抛出异常。

回头来看DirectX 11中的Buffer在创建Buffer的时候,有一个参数是Usage,这里我们只讨论D3D11_USAGE_DEFAULTD3D11_USAGE_DYNAMIC。在Default的Buffer可使用UpdateSubresources的接口来更新Buffer数据,同时这个更新只能是一次性更新整个Buffer,就是上面说到的情况,不能只更新部分。而对于需要频繁动态更新的Buffer设置为D3D11_USAGE_DYNAMIC,图形API会进行性能优化,同时可以使用MapUnMap来更新Buffer数据。