3.8.8 BUG RenderTexture在安卓端报错 'blendState'

解决了吗,我也遇到了

解决了么 我也遇到了

1赞

我也遇到一样的问题,有人解决吗

我这边也遇到了,其实就是创建了一个spriteFrame,并把对应的RenderTexture赋值给摄像机的targetTexture,跟帖子一样,首次进入是没问题的,第二次再次创建调用的时候,渲染出现材质损坏的问题,只有在原生端出现,有什么解决方案吗??

并且在3.8.2是没问题的,升级3.8.7才有的问题

能否提供可复现的demo ?

我用极简代码也复现了

麻烦参考这个 PR 验证,谢谢。

这个PR会导致崩溃,以下是崩溃关键日志

05-29 15:46:05.629 4971 5007 E Cocos : 15:46:05 [ERROR]: [SE_ERROR] (C:/ProgramData/cocos/editors/Creator/3.8.8/resources/resources/3d/engine/native/cocos/bindings/jswrapper/v8/ScriptEngine.cpp, 1020): _onPassesUpdated is not a function: undefined

你好,可以提供下复现demo?

import { _decorator, Component, Node, Button, view, RenderTexture, Camera, director, Director, Sprite, SpriteFrame } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('test')
export class test extends Component {
    btnCaputre!: Button
    btnClose!: Button

    start() {
        this.btnCaputre = this.node.getChildByName("capture").getComponent(Button)
        this.btnClose = this.node.getChildByName("close").getComponent(Button)

        this.btnCaputre.node.on(Button.EventType.CLICK, () => {
            this.capture();
        })
        this.btnClose.node.on(Button.EventType.CLICK, () => {
            this.close();
        })
    }


    async capture() { 
        const node = new Node("_capture");
        const sprite = node.addComponent(Sprite);
        node.setParent(this.node);

        const renderTexture = new RenderTexture();
        renderTexture.initialize({
            width: Math.floor(view.getVisibleSize().width),
            height: Math.floor(view.getVisibleSize().height)
        });
        const spriteFrame = new SpriteFrame();
        spriteFrame.packable = false;
        spriteFrame.texture = renderTexture;

        const cameras = director.getScene().getComponentsInChildren(Camera).filter(camera => camera.node.activeInHierarchy);
        for (const cam of cameras) {
            cam.targetTexture = renderTexture;
        }
        await new Promise<void>((r, j) => director.once(Director.EVENT_AFTER_RENDER, r));
        for (const cam of cameras) {
            cam.targetTexture = null;
        }
        sprite.spriteFrame = spriteFrame;
    }

    close() {
        const _capture = this.node.getChildByName("_capture")
        if(!_capture || !_capture.isValid)
            return;
        const sprite = _capture.getComponent(Sprite)
        if(!sprite || !sprite.isValid)
            return;
        const spriteFrame = sprite.spriteFrame;
        sprite.spriteFrame = null;
        const renderTexture = spriteFrame.texture;
        spriteFrame.destroy();
        renderTexture.destroy();

        _capture.removeFromParent();
        _capture.destroy();
    }
}

弄两个按钮,然后来回点击,大概率复现。

这边实测,点击一次截图,再点击销毁,挂个1分钟,再点击截图就崩溃

你好,有下文吗?

是用388原始代码? android用什么机型?
我这边用388原始代码,在windows, android 都没复现。

是原始代码,打包到mumu模拟器12运行的,怀疑是v8那边的对象被GC了

我用的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 缓存的工作方式:

  1. _updateBuiltinMaterialsprite.ts:565 通过 builtinResMgr.addAsset() 缓存了一个名为 rt-ui-sprite-material 的材质。该材质具有 SAMPLE_FROM_RT: true
  2. 在原生平台上 ( material.jsb.ts:283 ), passes 属性是一个 缓存的 JS 数组 ,仅通过 _onPassesUpdated 回调进行更新。该回调由原生的 emit<PassesUpdated>() 触发,它只在 Material::update() (创建 passes)和 Material::doDestroy() (清除 passes)中触发。
  3. 在 Web 平台上 ( material.ts ), _passes 是一个普通的数组,总是从 _effectAsset 中重新派生,因此不存在缓存陈旧的问题。
  4. 关键问题 :当第一个截图精灵被销毁时,rt- 材质的 JS 包装器可能会失去其唯一的 JS 引用( _materials[0] 被置空)。原生对象通过 builtinResMgr 的原生 IntrusivePtr 存活,但 _passes 的 JS 缓存可能变陈旧或失效。在第二次调用时, builtinResMgr.get() 回落到原生的 getAsset() (因为它只通过原生的 addAsset 添加,而不是 JS 的 _resources ),返回一个可能带有陈旧/空 _passes 缓存的包装器。当 _updateBlendFunc ( ui-renderer.ts:603 ) 访问 passes[0] 时,数组为空 → 导致崩溃。

感谢反馈,这边会再跟进。

请参考这个PR的修改