在C#中如何将引用类型内存移至固定连续缓冲区?Unity性能优化
Unity缓存友好的Mesh/Material绘制优化方案
Unity的Mesh、Material属于托管堆对象,无法直接以值类型形式存储在连续内存缓冲区中,但可以通过以下几种方式实现内存连续化,减少CPU缓存失效问题,适配Graphics.DrawMeshInstancedIndirect的性能需求:
1. 用NativeArray存储托管对象引用
Unity.Collections.NativeArray<T>的内存布局是连续的,相比普通数组,它能让托管对象的引用在内存中集中排列,遍历时大幅提升CPU缓存命中率。
代码实现:
using Unity.Collections; NativeArray<Mesh> _nativeMeshes; NativeArray<Material> _nativeMaterials; void InitializeBuffers() { Mesh[] sourceMeshes = GetTargetMeshes(); // 替换为你的Mesh获取逻辑 Material[] sourceMaterials = GetTargetMaterials(); // 替换为你的Material获取逻辑 _nativeMeshes = new NativeArray<Mesh>(sourceMeshes.Length, Allocator.Persistent); _nativeMaterials = new NativeArray<Material>(sourceMaterials.Length, Allocator.Persistent); for (int i = 0; i < sourceMeshes.Length; i++) { _nativeMeshes[i] = sourceMeshes[i]; _nativeMaterials[i] = sourceMaterials[i]; } } void PerformDrawCalls() { for (int i = 0; i < _nativeMeshes.Length; i++) { Graphics.DrawMeshInstancedIndirect( _nativeMeshes[i], 0, _nativeMaterials[i], // 补充你的其他参数:Bounds、argsBuffer等 ); } } void OnDestroy() { if (_nativeMeshes.IsCreated) _nativeMeshes.Dispose(); if (_nativeMaterials.IsCreated) _nativeMaterials.Dispose(); }
注意:NativeArray存储的仍是托管对象引用,但数组本身内存连续,能避免普通数组引用分散导致的缓存失效。必须在对象销毁时手动释放NativeArray,避免内存泄漏。
2. 存储原生句柄并转换使用
Unity的Mesh和Material对应底层C++对象,可通过GetNativeHandle()获取原生句柄(数值类型),将句柄存储在连续的结构体数组中,绘制时再转换回托管对象。这种方式完全使用值类型存储,缓存效率最高。
代码实现:
using Unity.Collections; struct MeshMaterialPair { public ulong MeshHandle; public ulong MaterialHandle; } NativeArray<MeshMaterialPair> _nativeHandlePairs; void InitializeHandleBuffers() { Mesh[] sourceMeshes = GetTargetMeshes(); Material[] sourceMaterials = GetTargetMaterials(); _nativeHandlePairs = new NativeArray<MeshMaterialPair>(sourceMeshes.Length, Allocator.Persistent); for (int i = 0; i < sourceMeshes.Length; i++) { _nativeHandlePairs[i] = new MeshMaterialPair { MeshHandle = sourceMeshes[i].GetNativeHandle(), MaterialHandle = sourceMaterials[i].GetNativeHandle() }; } } void PerformDrawCallsWithHandles() { for (int i = 0; i < _nativeHandlePairs.Length; i++) { var pair = _nativeHandlePairs[i]; Mesh mesh = Mesh.FromNativeHandle(pair.MeshHandle); Material material = Material.FromNativeHandle(pair.MaterialHandle); Graphics.DrawMeshInstancedIndirect( mesh, 0, material, // 补充你的其他参数:Bounds、argsBuffer等 ); } } void OnDestroy() { if (_nativeHandlePairs.IsCreated) _nativeHandlePairs.Dispose(); }
注意:原生句柄仅在对应的Mesh/Material未被销毁时有效,需额外维护对象生命周期,避免访问无效句柄导致崩溃。
3. 按Material分组批量绘制
如果你的Mesh存在按Material分组的规律,可将同一Material的Mesh集中存储,绘制时批量处理同一Material的所有Mesh。这种方式能减少Material切换带来的开销,同时进一步优化缓存利用。
内容的提问来源于stack exchange,提问作者Tree3708
相关产品推荐
相关产品推荐

