reduce-draw-calls
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCut draw calls, ranked by cost
减少绘制调用的方法(按成本排序)
Work the ladder in order and stop at the first rung that clears the budget. Each rung trades away
less flexibility than the next; skipping straight to a custom renderer or hand-written mesh merge
costs more engineering time than the draw calls it saves.
按顺序尝试这些方法,找到第一个能满足性能预算的方法即可停止。每一种方法的灵活性损失都比下一种小;直接跳过前面的步骤去编写自定义渲染器或手动合并网格,所花费的工程时间会超过其减少绘制调用带来的收益。
Measure first
先进行测量
Read (or the // breakdown) before changing
anything, or drop in for a live overlay. Every rung below is proved against this number,
not against intuition about what "looks expensive."
app.stats.drawCalls.totalforwarddepthshadowMiniStats在进行任何修改前,先查看(或//的细分数据),或者引入来显示实时数据面板。下面的每一种方法都需要基于这个数据来验证效果,而不是凭直觉判断哪些内容“看起来开销大”。
app.stats.drawCalls.totalforwarddepthshadowMiniStatsRung 1: stop drawing invisible things
方法1:停止绘制不可见元素
An element at opacity 0 still submits a draw call — its mesh instance exists and is still in a
layer, so the GPU processes it every frame for no visible result. Toggle on the entity to
actually skip it. Check every element that is ever fully transparent, not only the ones on screen
when you're profiling — this rung is easy to skip precisely because nothing looks wrong.
enabled透明度为0的元素仍然会提交绘制调用——它的网格实例依然存在且属于某个图层,因此GPU每帧都会处理它,但不会产生任何可见效果。通过切换实体的属性来真正跳过它的绘制。检查所有曾经完全透明的元素,而不仅仅是你分析时屏幕上的元素——正因为视觉上没有任何问题,这个方法很容易被忽略。
enabledRung 2: BatchManager
— merge without touching shaders
BatchManager方法2:使用BatchManager
——无需修改着色器即可合并
BatchManagerapp.batcher.addGroup(name, dynamic, maxAabbSize)batchGroupIddynamic: falseContract: the setter only inserts into the batcher while is true,
and that flag requires both the local enable state and hierarchy attachment — set
after the entity is parented into the live tree, not before, or the member silently drops out of the
group. runs once automatically on the app's first rendered frame; if group
membership changes afterward, call (or ) yourself.
An Engine-only app without the full bootstrap must register the class via
before it can batch anything.
batchGroupIdentity.enabledbatchGroupIdBatchManager.generate()app.batcher.markGroupDirty(id)generate([id])ApplicationAppOptions.batchManager调用,然后为每个成员的组件(渲染、精灵或UI元素)设置。对于不会移动的几何体,使用。
app.batcher.addGroup(name, dynamic, maxAabbSize)batchGroupIddynamic: false注意事项:只有当为true时,设置才会将元素加入到批处理管理器中,而该属性同时要求实体处于本地启用状态且已加入层级结构——要在实体被添加到活跃层级树之后再设置,否则该元素会被静默地排除在批处理组之外。会在应用首次渲染帧时自动运行一次;如果之后批处理组成员发生变化,需要手动调用(或)。对于未完整初始化的纯Engine应用,必须先通过注册该类,才能进行批处理操作。
entity.enabledbatchGroupIdbatchGroupIdBatchManager.generate()app.batcher.markGroupDirty(id)generate([id])ApplicationAppOptions.batchManagerRung 3: hardware instancing — merge without touching layout
方法3:硬件实例化——无需修改布局即可合并
Reach for this once distinct materials or per-frame transform updates would defeat .
Build a per-instance vertex buffer with (one mat4
per instance), call , and set . A custom vertex
chunk needs its own code path with an identity-matrix fallback for the non-instanced
case (cross-reference ).
BatchManagerVertexFormat.getDefaultInstancingFormat(device)meshInstance.setInstancing(vb)instancingCountINSTANCINGoverride-shader-chunksCulling trade-off: instanced meshes cull as one unit against a single bounding volume — an
off-screen instance inside an otherwise-visible group still draws unless you opt in. Pass
and set a spanning every instance's world
extent so the renderer has something correct to cull against instead of culling nothing or
culling the whole group by one instance's bounds.
setInstancing(vb, true)RenderComponent#customAabbAdapt the official recipes rather than deriving the buffer layout or vertex-shader wiring from
memory: for the format/buffer contract, for
the vertex-shader side. Locate both at the installed engine version via .
graphics/instancing-basicgraphics/instancing-customfind-examples当不同材质或每帧变换更新会导致失效时,可以使用此方法。通过创建每个实例的顶点缓冲区(每个实例对应一个mat4矩阵),调用并设置。自定义顶点块需要有自己的代码路径,并为非实例化情况提供单位矩阵作为回退(参考)。
BatchManagerVertexFormat.getDefaultInstancingFormat(device)meshInstance.setInstancing(vb)instancingCountINSTANCINGoverride-shader-chunks剔除权衡:实例化网格会作为一个整体,基于单个包围盒进行剔除——除非你主动配置,否则即使组内某个实例不在屏幕范围内,只要整个组可见,该实例仍会被绘制。传入并设置以覆盖所有实例的世界范围,这样渲染器就能基于正确的范围进行剔除,而不是不做剔除或仅根据单个实例的边界剔除整个组。
setInstancing(vb, true)RenderComponent#customAabb建议参考官方示例,而不是凭记忆推导缓冲区布局或顶点着色器连接:示例介绍了格式/缓冲区的规范,示例介绍了顶点着色器相关内容。可以通过找到对应引擎版本的这两个示例。
graphics/instancing-basicgraphics/instancing-customfind-examplesProve and report
验证并记录
Verify the rendered image is unchanged with before calling the change done —
merging or instancing must not move a single pixel. Re-measure afterward and
state the before/after counts in the change description; "fewer draw calls" without numbers is not
a result.
verify-pixelsapp.stats.drawCalls在确认修改完成前,使用验证渲染图像没有变化——合并或实例化操作不能改变任何一个像素的位置。修改后重新测量,并在变更说明中记录修改前后的数值;只说“减少了绘制调用”而没有具体数字不能算作有效结果。
verify-pixelsapp.stats.drawCalls