一 现象
如这个帖子所说https://forum.cocos.org/t/topic/113191,Creator3.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);
}
}
// ...
}
说明:
1、因为我的场景只用到了 DirectionalLight ,如果是其它类型的灯光,请自行测试。
2、项目上线在即,不知道这种修改是否有其它问题,请各大佬指示;
