跳至正文

Creator3.x引擎切场景时阴影内存泄漏的非官方补丁

一 现象

CocosCreator3.x引擎的场景中开启阴影后,例如阴影贴图使用的是2048*2048,则每一次切换场景都会引起36M(贴图16M+深度20M)的内存泄漏,而且是必现。

这个BUG在3.x引擎一直存在,从3.6开始才没有这个问题,但没有找到专门针对这个BUG的fix说明。因为项目用的是3.4.2引擎,不想升级引擎,只能自己动手修改引擎源码来解决了。

二 原因分析

在ShadowFlow中有个名为shadowFrameBufferMap的Map对象,Map对象存放着以light为key,值为frameBuffer的对象,frameBuffer创建了阴影和深度共两张纹理。每次切换场景会从 shadowFrameBufferMap 中检索light是否已存在,如果不存在则新创建一个frameBuffer。

现在不知问题在哪,切换场景后light对象属性被修改,导致检索不到,则每次都创建一个新的frameBuffer,也即创建两张新纹理,所以shadowFrameBufferMap越来越大,并且原来的纹理没有释放。

三 临时修复方案

因为每次切换场景时,都会调用 _detachFromScene 来移除light,所以就在这里根据旧light,从 shadowFrameBufferMap 中找到 frameBuffer 来释放,反复测试后没再出现内存泄漏问题。

修改Creator3.4.2版本的引擎源码如下。

在 core/pipeline/shadow/shadow-flow.ts 中增加一个方法:

public destroyFrameBufferByLight(light: Light) {
    if (!light || !this._pipeline || !this._pipeline.pipelineSceneData) {
        return;
    }

    const shadowFrameBufferMap = this._pipeline.pipelineSceneData.shadowFrameBufferMap;
    const frameBuffer = shadowFrameBufferMap.get(light);
    if (!frameBuffer) { 
        return; 
    }

    const renderTargets = frameBuffer.colorTextures;
    for (let i = 0; i < renderTargets.length; i++) {
        const renderTarget = renderTargets[i];
        if (renderTarget) {
            renderTarget.destroy();
        }
    }
    renderTargets.length = 0;

    const depth = frameBuffer.depthStencilTexture;
    if (depth) {
        depth.destroy();
    }

    frameBuffer.destroy();
    shadowFrameBufferMap.delete(light);
}

在 render-scene.ts 文件开头,增加:

import { ShadowFlow } from '../../pipeline/shadow/shadow-flow';

在 removeDirectionalLight 函数开头处,增加:

public removeDirectionalLight (dl: DirectionalLight) {
    if (this._root && this._root.pipeline && this._root.pipeline.flows) {
        let shadowFlow = this._root.pipeline.flows[0];
        if (shadowFlow && shadowFlow instanceof ShadowFlow) {
            shadowFlow.destroyFrameBufferByLight(dl);
        }
    }
    // ...
}

说明:场景只用了 DirectionalLight ,所以只处理了 DirectionalLight,其它类型的灯光仅供参考。

标签:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注