对象池对象重用导致的 drawcall数飙升的问题【含Demo】

开发需求:
1、大地图,需要大量的草地格子进行平铺

问题描述:
1、创建3000个node(sprite)在同一层,使用同一个纹理,这时候是会渲染合批的
2、把这3000个node(sprite)移除舞台放入对象池
3、从对象池取出这3000个node(sprite)在放入舞台(还是同一层,但是3000个node的顺序和第1步可能不一样了),这时候渲染已经不合批了。

关联因素:
1、BATCHER2D_MEM_INCREMENT 是144,调大似乎可以解决(不确定是否有隐患)
2、渲染顺序在 sprite组件创建时就确定了(不太确定),所以node对象重用顺序打乱后相邻2的对象渲染顺序不连续导致不能合批。

问题:
1、在使用对象池node(sprite)的情况下,如果不调大BATCHER2D_MEM_INCREMENT,是否有方法实现合批渲染?
2、如果调大 BATCHER2D_MEM_INCREMENT,设成多少合适?

demo:https://cdn-applet.flash8f.com/pandaHomeLand/app/web_cn/testhepi.zip
demo说明:点击【重置】按钮后 draw call立马飙升

1赞

解决方案就是靠调大BATCHER2D_MEM_INCREMENT ,这个是合批组的大小,你超过这个大小就分批了。调大副作用就是增大内存。你项目如果内存不是瓶颈不用太在意,优化放到最后进行把,游戏不一定需要优化。

内存肯定要省着用哦,
且 BATCHER2D_MEM_INCREMENT 和对象池重用导致的未合批似乎是2个不同因素,BATCHER2D_MEM_INCREMENT即使调大,对象重用后理论上依旧存在跨合批组的问题吧?

请问下这里使用的还是相同的纹理吗?如果是相同的纹理,理论上还是可以合批的吧?

是相同的纹理,刚new出来的时候是会合批的,对象池重用打乱顺序后再添加到舞台就大部分不会合批了

你这个说得很不合理,建议出个demo让大家看看

一般来说,相同的纹理,相同的材质,都是可以自动合批的,跟节点顺序没啥关系。

假设A使用了相同的纹理和材质,那么A1、A2、A3 的任意排序应该都是可以合批的。只有当其中混入了一个使用其他纹理或材质的B才会打断合批,例如 A1、B、A2、A3,会分成三个渲染批次提交 [A1]、[B]、[A2、A3]。

可以检查下是否在对象池复用的过程中是否修改了材质、纹理等属性。或者从对象池中取出来之后,挂载在原节点下时,是否穿插了使用其他纹理或材质的节点。

demo加上了

我也觉得不合理,但是似乎符合官方的说明,
官方文档里面有写 sprite创建时就已经确定了渲染顺序,所以对象重用后渲染顺序和children顺序不一致就不会合批了(不知道我理解对不对)

https://docs.cocos.com/creator/3.8/manual/zh/ui-system/components/engine/ui-batch.html)

你说的纹理B的情况不存在的,请看demo

必须在同一个大batch里才能合批,这个踩过坑,一种方案可以考虑全部回收再使用,极大概率重新合批。

这个时候是会这样,是官方没明说的一个坑点

明白,没有完美的解决办法么? 【极大概率重新合批】感觉有隐患 :joy: :joy:

1赞

BATCHER2D_MEM_INCREMENT,开最大吧,

设计如此,可以减少数据的计算和更换,就看chunck回收和分配机制,

够不够智能,尽量少跨buffer,就不会打断 draw call

他这个是顶点数据不在同一个vbo里面了

那就属于bug了吧,材质一致又在同一层级不把vb合并,这不就是合批的目的吗

多谢!原来 github上早有开发者提了,官方给出解决方案了。

看来以后要多关注github,仅仅看论坛不够 :joy:

他这个肯定也是为了避免频繁的去调整vbo影响性能,他会给所有2d渲染组件先分配一块固定大小的buffer,创建一个2d渲染组件的时候会给他在这个buffer里面找一块空闲内存,你只要不销毁,即使不在场景里面,这块内存也一直是属于这个组件的。再创建新的只能去找其他空闲的内存,满了就创建一个新buffer。这就导致多个渲染组件无法在同一个buffer里面了。