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

Unity游戏多线程调用ID3D11ImmediateContext::Map触发E_OUTOFMEMORY问题

解决多线程调用ID3D11ImmediateContext::Map时的无规律失败与E_OUTOFMEMORY错误

首先得敲黑板:ID3D11ImmediateContext绝对不是线程安全的!你遇到的无规律失败和内存错误,本质是多个线程同时抢占Immediate Context的资源访问权,导致D3D11内部状态混乱——看起来是内存不足,实际是线程竞争触发的异常。下面给你几个针对性的解决方案:

1. 改用Deferred Context做线程安全的资源修改

这是D3D11为多线程渲染/资源更新设计的标准方案。每个工作线程可以创建自己的ID3D11DeviceContextDeferred,在这个延迟上下文里独立执行Map、修改缓冲区/纹理的操作,最后把生成的命令列表提交给Immediate Context统一执行。

示例代码大概是这样:

// 每个线程创建自己的Deferred Context
ID3D11DeviceContextDeferred* deferredCtx = nullptr;
sU_m_D_device->CreateDeferredContext(0, &deferredCtx);

// 在Deferred Context上安全地Map资源
ID3D11Buffer* d3dbuf = static_cast<ID3D11Buffer*>(bufferHandle);
D3D11_MAPPED_SUBRESOURCE mappedRes;
HRESULT hr = deferredCtx->Map(d3dbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedRes);
if (SUCCEEDED(hr)) {
    // 修改顶点数据...
    deferredCtx->Unmap(d3dbuf, 0);
}

// 生成命令列表并提交给Immediate Context执行
ID3D11CommandList* cmdList = nullptr;
deferredCtx->FinishCommandList(FALSE, &cmdList);
sU_m_D_context->ExecuteCommandList(cmdList, FALSE);

// 别忘了释放资源
cmdList->Release();
deferredCtx->Release();

2. 给Immediate Context的访问加线程锁

如果暂时不想重构为Deferred Context,最直接的办法是用同步原语(比如临界区)保证同一时间只有一个线程调用Immediate Context的方法:

// 全局或类级别的临界区
CRITICAL_SECTION ctxLock;
InitializeCriticalSection(&ctxLock);

void* BeginModifyingVBO(void* bufferHandle) {
    ID3D11Buffer* d3dbuf = static_cast<ID3D11Buffer*>(bufferHandle);
    D3D11_MAPPED_SUBRESOURCE mappedRes;

    // 进入临界区,独占Immediate Context
    EnterCriticalSection(&ctxLock);
    HRESULT hr = sU_m_D_context->Map(d3dbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedRes);
    if (FAILED(hr)) {
        LeaveCriticalSection(&ctxLock);
        return nullptr;
    }

    // 注意:Unmap的时候也要在临界区里!
    // 可以把Unmap封装到EndModifyingVBO函数里,同样加锁
    return mappedRes.pData;
}

void EndModifyingVBO(void* bufferHandle) {
    ID3D11Buffer* d3dbuf = static_cast<ID3D11Buffer*>(bufferHandle);
    EnterCriticalSection(&ctxLock);
    sU_m_D_context->Unmap(d3dbuf, 0);
    LeaveCriticalSection(&ctxLock);
}

3. 优化资源的创建与Map参数

有时候E_OUTOFMEMORY是假警报——比如资源正在被GPU使用,无法被Map。你需要确保:

  • 创建顶点缓冲区时使用D3D11_USAGE_DYNAMIC,绑定D3D11_BIND_VERTEX_BUFFER,这是为频繁更新的动态资源设计的。
  • Map时优先用D3D11_MAP_WRITE_DISCARD(适合完全重写的情况)或D3D11_MAP_WRITE_NO_OVERWRITE(适合追加数据的情况),这两种模式不需要等待GPU释放资源,能减少冲突概率。

4. 启用D3D11调试层排查真实错误

打开D3D11的调试层,它会输出更详细的错误日志——比如到底是真的内存不足,还是线程访问冲突,或者资源状态不兼容导致的Map失败。在创建D3D11设备时加上D3D11_CREATE_DEVICE_DEBUG标志即可。


内容的提问来源于stack exchange,提问作者MrDyrektor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:11:58