fix(ui): skip re-batching of canvas-internal leaders in main pipeline

UICanvas pre-batches its children into leaders that carry a self-contained
draw range. When two canvases sharing the same atlas push their leaders
into the transparent queue, the main-pipeline batcher previously fed those
leaders back into `_canBatch`/`_batch` as if they were single sub-elements.
That corrupted `subMesh.count` and re-appended already-written indices,
dropping draws or overlapping ranges.

The batch boundary is the canvas — matches Unity uGUI's behavior — so
detect already-batched leaders at the batcher entry and pass them through.
This commit is contained in:
chenmo.gl
2026-05-13 20:30:45 +08:00
parent 04ac8b3b7a
commit 9606bf3de8

View File

@@ -56,30 +56,34 @@ export class BatcherManager {
let preConstructor: Function;
for (let i = 0, n = input.length; i < n; ++i) {
const curElement = input[i];
// Already-batched leaders (e.g. produced by UICanvas pre-batching) are terminal —
// each carries an opaque, self-contained draw range that must not be merged again.
// Flush any pending pre and pass the leader straight through
if (curElement._isBatched) {
preElement && (BatcherManager._flush(output, preElement), (preElement = null));
output.push(curElement);
continue;
}
const renderer = curElement.component;
const constructor = renderer.constructor;
if (preElement) {
if (preConstructor === constructor && preRenderer._canBatch(preElement, curElement)) {
preRenderer._batch(preElement, curElement);
} else {
preElement._isBatched = true;
output.push(preElement);
preElement = curElement;
preRenderer = renderer;
preConstructor = constructor;
renderer._batch(null, curElement);
}
if (preElement && preConstructor === constructor && preRenderer._canBatch(preElement, curElement)) {
preRenderer._batch(preElement, curElement);
} else {
preElement && BatcherManager._flush(output, preElement);
preElement = curElement;
preRenderer = renderer;
preConstructor = constructor;
renderer._batch(null, curElement);
}
}
if (preElement) {
preElement._isBatched = true;
output.push(preElement);
}
preElement && BatcherManager._flush(output, preElement);
}
private static _flush(output: RenderElement[], element: RenderElement): void {
element._isBatched = true;
output.push(element);
}
uploadBuffer() {