我用的3.8.7版本,参考此pr让ai写了个构建脚本,修改构建后的cc.js,至少不会报错了,但是似乎治标不治本?rtMat不存在,或者rtMat.passes.length为0是正常情况吗?
protected _updateBuiltinMaterial (): Material {
let mat = super._updateBuiltinMaterial();
if (this.spriteFrame && this.spriteFrame.texture instanceof RenderTexture) {
const rtMatName = `rt-${mat.name}`;
let rtMat = builtinResMgr.get(rtMatName) as Material | null;
if (!rtMat || !rtMat.passes.length) {
rtMat = new Material(rtMatName);
rtMat.copy(mat, { defines: { SAMPLE_FROM_RT: true } });
builtinResMgr.addAsset(rtMatName, rtMat);
}
mat = rtMat;
}
return mat;
}
有重新赋值的。
这边发现回滚这次提交就正常了
用GLM5.2给出的分析:
根本原因分析
该 Bug 出在 Cocos 的 JSB(原生)实现中,具体是关于缓存的 rt- 材质及其 _passes JS 缓存的工作方式:
-
_updateBuiltinMaterial在sprite.ts:565通过builtinResMgr.addAsset()缓存了一个名为rt-ui-sprite-material的材质。该材质具有SAMPLE_FROM_RT: true。 -
在原生平台上 (
material.jsb.ts:283),passes属性是一个 缓存的 JS 数组 ,仅通过_onPassesUpdated回调进行更新。该回调由原生的emit<PassesUpdated>()触发,它只在Material::update()(创建 passes)和Material::doDestroy()(清除 passes)中触发。 -
在 Web 平台上 (
material.ts),_passes是一个普通的数组,总是从_effectAsset中重新派生,因此不存在缓存陈旧的问题。 -
关键问题 :当第一个截图精灵被销毁时,rt- 材质的 JS 包装器可能会失去其唯一的 JS 引用(
_materials[0]被置空)。原生对象通过builtinResMgr的原生IntrusivePtr存活,但_passes的 JS 缓存可能变陈旧或失效。在第二次调用时,builtinResMgr.get()回落到原生的getAsset()(因为它只通过原生的addAsset添加,而不是 JS 的_resources),返回一个可能带有陈旧/空_passes缓存的包装器。当_updateBlendFunc(ui-renderer.ts:603) 访问passes[0]时,数组为空 → 导致崩溃。
感谢反馈,这边会再跟进。