从10FPS到满帧, 大型3D 场景优化

给大佬顶一个

我理解是包体吧, 通常情况下 PNG8(TinyPNG) 压缩后是比 ASTC小; 如果目标是减少包体 对于 UI、图标、带大量透明区域的素材,优先用 PNG/PNG8的 atlas;对场景贴图、法线贴图、位图照片类用 ASTC。

niuBi

大佬六百六十六

真的是应用AI去落地解决游戏问题了,而不是虚有其表的去讲AI

老板看到了会怎么想

鸿蒙的DevEco 可以直接ai测试ccc吗还是说要生成指定包来测试

用AI也是需要技术的

666​:+1::+1::+1:

只是测试下发热和耗电

插眼 66666

niubi :+1: :+1: :+1:

666​:+1::+1::+1:

666666 :heart_eyes:

邪修之为什么大量node.active 性能差, 比如数千个node 再update里 active = true/false

我们先看下node内部发生了什么

大量调用 node.active 会触发节点及其子孙的生命周期回调(preload/onEnable/onDisable)、组件启用、变换和渲染数据重建,导致大量 CPU 开销和可能的内存分配/GC。一次性激活或频繁切换很多节点还会打乱渲染批次

    public activateNode (node: Node, active: boolean): void {
        if (active) {
            const task = activateTasksPool.get();
            if (task) {
                this._activatingStack.push(task);

                this._activateNodeRecursively(node, task.preload, task.onLoad, task.onEnable);
                task.preload.invoke();
                task.onLoad.invoke();
                task.onEnable.invoke();

                this._activatingStack.pop();
                activateTasksPool.put(task);
            }
        } else {
            this._deactivateNodeRecursively(node);

            // remove children of this node from previous activating tasks to debounce
            // (this is an inefficient operation but it ensures general case could be implemented in a efficient way)
            const stack = this._activatingStack;
            for (const lastTask of stack) {
                lastTask.preload.cancelInactive(IsPreloadStarted);
                lastTask.onLoad.cancelInactive(IsOnLoadStarted);
                lastTask.onEnable.cancelInactive(IsOnEnableCalled);
            }
        }
        node.emit(NodeEventType.ACTIVE_IN_HIERARCHY_CHANGED, node);
    }

怎么优化?

这里有2个思路

普通版

  • 手动管理Node的Visible, 不渲染或者超出屏幕边界时候 我们设置成null, 同时使用init 作为激活函数名, 不实用默认的onEnable, 这个方案对于3D是比较友好了
    function enqueueRenderObject (model: Model): void {
        // filter model by view visibility
        if (model.enabled) {
            if (scene.isCulledByLod(camera, model)) {
                return;
            }

            if (model.castShadow) {
                castShadowObjects.push(getRenderObject(model, camera));
                csmLayerObjects.push(getRenderObject(model, camera));
            }

            if (model.node && ((visibility & model.node.layer) === model.node.layer)
                 || (visibility & model.visFlags)) {
                // frustum culling
                if (model.worldBounds && !geometry.intersect.aabbFrustum(model.worldBounds, camera.frustum)) {
                    return;
                }

                renderObjects.push(getRenderObject(model, camera));
            }
        }
    }

    for (let i = 0; i < models.length; i++) {
        enqueueRenderObject(models[i]);
    }

邪修版

但对于2D 3D 大量伤害文字和特效, 设置成null, 会影响batcher2D 的排序;

2D文字/3D特效/拖尾混排

222

优化重构assembler, 上万文字,脱尾,特效, 2个assembler 搞定, 同时保证三角面的有序culling
我们在assembler里面也做了3个优化

  • 预创建 预先创建好极限大小的buffer, 如果超过, 会把有限时间的移除
  • 位置分配 针对光污染特效, 单独一个assembler不去做特定排序(不过先来后到 数量大的时候, 不会很明显) 通过slot 预先分配, 先不用删除, 把能用的slot 给后续节点, 比如有1000个再跑的特效, 有500个要删除, 先添加了300个, 这时候我们通过 vbF32.fill/iBuffer.fill 更新到指定长度就可以了, 前面的数据
    private _createBuffers(maxVertCount: number, maxIndexCount: number): EffectBuffer {
        const device = director.root!.device;
        const vbByteLength = maxVertCount * MultiEffect.VERTEX_SIZE;
        const ibByteLength = maxIndexCount * 2;
        const vbF32 = new Float32Array(maxVertCount * MultiEffect.FLOATS_PER_VERT);
        const iBuffer = new Uint16Array(maxIndexCount);
        const vertexBuffer = device.createBuffer(new gfx.BufferInfo(
            gfx.BufferUsageBit.VERTEX | gfx.BufferUsageBit.TRANSFER_DST,
            gfx.MemoryUsageBit.HOST | gfx.MemoryUsageBit.DEVICE,
            vbByteLength,
            MultiEffect.VERTEX_SIZE
        ));
        const indexBuffer = device.createBuffer(new gfx.BufferInfo(
            gfx.BufferUsageBit.INDEX | gfx.BufferUsageBit.TRANSFER_DST,
            gfx.MemoryUsageBit.HOST | gfx.MemoryUsageBit.DEVICE,
            ibByteLength,
            2
        ));
        return { vertexBuffer, indexBuffer, vbF32, iBuffer };
    }

    private _processRemovals(config: MatConfig) {
        if (!config.hasRemovals) return;
        const effs = config.effList!;
        let i = 0;
        while (i < effs.length) {
            const eff = effs[i];
            if (eff.remove) {
                config.freeSlots!.push(eff.slotIndex);
                this._clearEffectBuffer(eff);
                // 使用 splice 保持数组连续性
                effs.splice(i, 1);
            } else {
                i++;
            }
        }
        config.hasRemovals = false;
    }
    private _clearEffectBuffer(eff: EffectConfig) {
        const buffers = eff.matConfig!.buffers!;
        const mat = eff.matConfig!;
        const length = mat.maxLength!;
        const shapeCount = 2;
        const vertexCount = length * shapeCount * MultiEffect.FLOATS_PER_VERT;
        const vbStart = eff.offset;
        const indexCount = (length - 1) * (eff.shapeCount - 1) * 2 * 3;
        const ibStart = eff.indexOffset;
        buffers.vbF32.fill(0, vbStart, vbStart + vertexCount);
        buffers.iBuffer.fill(0, ibStart, ibStart + indexCount);
        EffectPool.put(eff);
    }
3赞

自定义性能真好,满天都是小星星.

UI渲染合批 3.x

牛逼呀大佬

鬼才啊 二喵酱

有这种小型优化的demo就好了,开箱即用,放store里卖也好

特定领域ai大显神通