You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 07:45:37