Skip to content

Commit 91b0957

Browse files
committed
WebGL renderer: index buffer batching, increased batch size, and rename and deprecate Compositor
1. Index buffer batching — Quad rendering now uses gl.drawElements with a static index buffer instead of gl.drawArrays, pushing 4 vertices per quad instead of 6 (33% less vertex data). New IndexBuffer utility class under buffer/ with WebGL2 Uint32 / WebGL1 Uint16 support.                                                                                                                               2. Increased batch size — Vertex batch size increased from 256 to 4096 (1024 quads), matching industry standard. Defined as a   MAX_VERTICES constant in the batcher.                                                                                                  3. Fixed-size vertex buffer — VertexArrayBuffer no longer auto-resizes; it's allocated once at a fixed size. Removed dead resize(), length(), isEmpty() methods and simplified push().                                                                                     4. setCompositor fast path — Early-return when the compositor and shader are already active, avoiding unnecessary flushes and binds on every draw call.                                                                                                                      5. Naming cleanup — Renamed compositors/ → batchers/, Compositor → Batcher, QuadCompositor → QuadBatcher, PrimitiveCompositor →  PrimitiveBatcher, vertexByteSize → stride. Old names preserved as deprecated aliases in deprecated.js. 6. Tests — Expanded VertexArrayBuffer tests (22 tests) and added new IndexBuffer test suite (9 tests).
1 parent 621da47 commit 91b0957

File tree

13 files changed

+475
-157
lines changed

13 files changed

+475
-157
lines changed

packages/melonjs/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@
3636
- WebGLRenderer: `fillPolygon` now uses `Polygon.getIndices()` cached earcut results instead of rebuilding the path each frame
3737
- WebGLRenderer: skip redundant `gl.uniform1i` sampler call in `QuadCompositor.addQuad()` when consecutive quads share the same texture unit
3838
- WebGLRenderer: replace per-frame `clone()`/`push()`/`pop()` allocations in save/restore with zero-allocation `RenderState` stacks
39+
- WebGLRenderer: switch quad rendering from `gl.drawArrays` (6 vertices per quad) to `gl.drawElements` with a static index buffer (4 vertices + 6 indices per quad), reducing vertex data by 33%
40+
- WebGLRenderer: increase vertex batch size from 256 (64 quads) to 4096 (1024 quads), reducing draw calls for sprite-heavy and tile-heavy scenes
41+
42+
### Changed
43+
- WebGLRenderer: `Compositor`, `QuadCompositor`, and `PrimitiveCompositor` are now deprecated in favor of `Batcher`, `QuadBatcher`, and `PrimitiveBatcher`
3944

4045
## [18.0.0] (melonJS 2) - _2026-03-10_
4146

packages/melonjs/src/application/settings.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { RendererType } from "../const";
77
import Renderer from "../video/renderer";
8-
import Compositor from "../video/webgl/compositors/compositor";
8+
import { Batcher } from "../video/webgl/batchers/batcher";
99
import { ScaleMethod } from "./scaleMethods";
1010

1111
type BlendMode = "normal" | "multiply" | "lighter" | "additive" | "screen";
@@ -88,9 +88,9 @@ export type ApplicationSettings = {
8888
legacy: boolean;
8989

9090
/**
91-
* a custom compositor class (WebGL only)
91+
* a custom batcher class (WebGL only)
9292
*/
93-
compositor?: Compositor | undefined;
93+
compositor?: Batcher | undefined;
9494
} & (
9595
| {
9696
// the DOM parent element to hold the canvas in the HTML file

packages/melonjs/src/index.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ import Renderer from "./video/renderer.js";
5959
import RenderState from "./video/renderstate.js";
6060
import CanvasRenderTarget from "./video/rendertarget/canvasrendertarget.js";
6161
import { TextureAtlas } from "./video/texture/atlas.js";
62-
import Compositor from "./video/webgl/compositors/compositor.js";
63-
import PrimitiveCompositor from "./video/webgl/compositors/primitive_compositor.js";
64-
import QuadCompositor from "./video/webgl/compositors/quad_compositor.js";
62+
import { Batcher } from "./video/webgl/batchers/batcher.js";
63+
import PrimitiveBatcher from "./video/webgl/batchers/primitive_batcher.js";
64+
import QuadBatcher from "./video/webgl/batchers/quad_batcher.js";
6565
import GLShader from "./video/webgl/glshader.js";
6666
import WebGLRenderer from "./video/webgl/webgl_renderer.js";
6767

@@ -103,9 +103,9 @@ export {
103103
Tween,
104104
QuadTree,
105105
GLShader,
106-
Compositor,
107-
PrimitiveCompositor,
108-
QuadCompositor,
106+
Batcher,
107+
PrimitiveBatcher,
108+
QuadBatcher,
109109
Renderer,
110110
RenderState,
111111
WebGLRenderer,

packages/melonjs/src/lang/deprecated.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import CanvasRenderer from "../video/canvas/canvas_renderer.js";
22
import CanvasRenderTarget from "../video/rendertarget/canvasrendertarget.js";
3+
import { Batcher } from "../video/webgl/batchers/batcher.js";
4+
import PrimitiveBatcher from "../video/webgl/batchers/primitive_batcher.js";
5+
import QuadBatcher from "../video/webgl/batchers/quad_batcher.js";
36
import WebGLRenderer from "../video/webgl/webgl_renderer.js";
47
import { warning } from "./console.js";
58

@@ -53,6 +56,42 @@ WebGLRenderer.prototype.setLineWidth = function (width) {
5356
this.lineWidth = width;
5457
};
5558

59+
/**
60+
* @deprecated since 18.2.0
61+
* @see Batcher
62+
*/
63+
export class Compositor extends Batcher {
64+
/** @param {any[]} args */
65+
constructor(...args) {
66+
warning("Compositor", "Batcher", "18.2.0");
67+
super(...args);
68+
}
69+
}
70+
71+
/**
72+
* @deprecated since 18.2.0
73+
* @see PrimitiveBatcher
74+
*/
75+
export class PrimitiveCompositor extends PrimitiveBatcher {
76+
/** @param {any[]} args */
77+
constructor(...args) {
78+
warning("PrimitiveCompositor", "PrimitiveBatcher", "18.2.0");
79+
super(...args);
80+
}
81+
}
82+
83+
/**
84+
* @deprecated since 18.2.0
85+
* @see QuadBatcher
86+
*/
87+
export class QuadCompositor extends QuadBatcher {
88+
/** @param {any[]} args */
89+
constructor(...args) {
90+
warning("QuadCompositor", "QuadBatcher", "18.2.0");
91+
super(...args);
92+
}
93+
}
94+
5695
/**
5796
* @namespace Math
5897
* @deprecated since 18.0.0

packages/melonjs/src/video/webgl/compositors/compositor.js renamed to packages/melonjs/src/video/webgl/batchers/batcher.js

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@ import GLShader from "../glshader.js";
88
*/
99

1010
/**
11-
* A base Compositor object.
11+
* Maximum number of vertices per batch.
12+
* At 4096 vertices (1024 quads), the vertex buffer is ~80 KB (5 floats × 4 bytes × 4096),
13+
* which balances draw call reduction with safe buffer upload sizes on mobile tile-based GPUs.
14+
* Within the Uint16 index limit (65,535) required for WebGL1 compatibility.
15+
* @ignore
1216
*/
13-
export default class Compositor {
17+
const MAX_VERTICES = 4096;
18+
19+
/**
20+
* A base WebGL Batcher object that manages shader programs, vertex attribute
21+
* definitions, and vertex buffer batching for efficient GPU draw calls.
22+
*/
23+
export class Batcher {
1424
/**
1525
* @param {WebGLRenderer} renderer - the current WebGL renderer session
16-
* @param {object} settings - additional settings to initialize this compositors
26+
* @param {object} settings - additional settings to initialize this batcher
1727
* @param {object[]} settings.attribute - an array of attributes definition
1828
* @param {string} settings.attribute.name - name of the attribute in the vertex shader
1929
* @param {number} settings.attribute.size - number of components per vertex attribute. Must be 1, 2, 3, or 4.
@@ -29,7 +39,7 @@ export default class Compositor {
2939
}
3040

3141
/**
32-
* Initialize the compositor
42+
* Initialize the batcher
3343
* @ignore
3444
*/
3545
init(renderer, settings) {
@@ -43,13 +53,13 @@ export default class Compositor {
4353
this.viewMatrix = renderer.currentTransform;
4454

4555
/**
46-
* the default shader created by this compositor
56+
* the default shader created by this batcher
4757
* @type {GLShader}
4858
*/
4959
this.defaultShader = undefined;
5060

5161
/**
52-
* the shader currently used by this compositor
62+
* the shader currently used by this batcher
5363
* @type {GLShader}
5464
*/
5565
this.currentShader = undefined;
@@ -63,29 +73,29 @@ export default class Compositor {
6373

6474
/**
6575
* an array of vertex attribute properties
66-
* @see WebGLCompositor.addAttribute
76+
* @see Batcher.addAttribute
6777
* @type {Array.<Object>}
6878
*/
6979
this.attributes = [];
7080

7181
/**
72-
* the size of a single vertex in bytes
82+
* the stride of a single vertex in bytes
7383
* (will automatically be calculated as attributes definitions are added)
74-
* @see WebGLCompositor.addAttribute
84+
* @see Batcher.addAttribute
7585
* @type {number}
7686
*/
77-
this.vertexByteSize = 0;
87+
this.stride = 0;
7888

7989
/**
8090
* the size of a single vertex in floats
8191
* (will automatically be calculated as attributes definitions are added)
82-
* @see WebGLCompositor.addAttribute
92+
* @see Batcher.addAttribute
8393
* @type {number}
8494
*/
8595
this.vertexSize = 0;
8696

8797
/**
88-
* the vertex data buffer used by this compositor
98+
* the vertex data buffer used by this batcher
8999
* @type {VertexArrayBuffer}
90100
*/
91101
this.vertexData = null;
@@ -101,7 +111,7 @@ export default class Compositor {
101111
attr.offset,
102112
);
103113
});
104-
this.vertexData = new VertexArrayBuffer(this.vertexSize, 6);
114+
this.vertexData = new VertexArrayBuffer(this.vertexSize, MAX_VERTICES);
105115
} else {
106116
throw new Error("attributes definition missing");
107117
}
@@ -122,7 +132,7 @@ export default class Compositor {
122132
}
123133

124134
/**
125-
* Reset compositor internal state
135+
* Reset batcher internal state
126136
* @ignore
127137
*/
128138
reset() {
@@ -134,7 +144,7 @@ export default class Compositor {
134144
}
135145

136146
/**
137-
* called by the WebGL renderer when a compositor become the current one
147+
* called by the WebGL renderer when a batcher becomes the current one
138148
*/
139149
bind() {
140150
if (this.renderer.currentProgram !== this.defaultShader.program) {
@@ -152,15 +162,15 @@ export default class Compositor {
152162
this.flush();
153163
shader.bind();
154164
shader.setUniform("uProjectionMatrix", this.renderer.projectionMatrix);
155-
shader.setVertexAttributes(this.gl, this.attributes, this.vertexByteSize);
165+
shader.setVertexAttributes(this.gl, this.attributes, this.stride);
156166

157167
this.currentShader = shader;
158168
this.renderer.currentProgram = this.currentShader.program;
159169
}
160170
}
161171

162172
/**
163-
* add vertex attribute property definition to the compositor
173+
* add vertex attribute property definition to the batcher
164174
* @param {string} name - name of the attribute in the vertex shader
165175
* @param {number} size - number of components per vertex attribute. Must be 1, 2, 3, or 4.
166176
* @param {GLenum} type - data type of each component in the array
@@ -172,30 +182,30 @@ export default class Compositor {
172182

173183
switch (type) {
174184
case this.gl.BYTE:
175-
this.vertexByteSize += size * Int8Array.BYTES_PER_ELEMENT;
185+
this.stride += size * Int8Array.BYTES_PER_ELEMENT;
176186
break;
177187
case this.gl.UNSIGNED_BYTE:
178-
this.vertexByteSize += size * Uint8Array.BYTES_PER_ELEMENT;
188+
this.stride += size * Uint8Array.BYTES_PER_ELEMENT;
179189
break;
180190
case this.gl.SHORT:
181-
this.vertexByteSize += size * Int16Array.BYTES_PER_ELEMENT;
191+
this.stride += size * Int16Array.BYTES_PER_ELEMENT;
182192
break;
183193
case this.gl.UNSIGNED_SHORT:
184-
this.vertexByteSize += size * Uint16Array.BYTES_PER_ELEMENT;
194+
this.stride += size * Uint16Array.BYTES_PER_ELEMENT;
185195
break;
186196
case this.gl.INT:
187-
this.vertexByteSize += size * Int32Array.BYTES_PER_ELEMENT;
197+
this.stride += size * Int32Array.BYTES_PER_ELEMENT;
188198
break;
189199
case this.gl.UNSIGNED_INT:
190-
this.vertexByteSize += size * Uint32Array.BYTES_PER_ELEMENT;
200+
this.stride += size * Uint32Array.BYTES_PER_ELEMENT;
191201
break;
192202
case this.gl.FLOAT:
193-
this.vertexByteSize += size * Float32Array.BYTES_PER_ELEMENT;
203+
this.stride += size * Float32Array.BYTES_PER_ELEMENT;
194204
break;
195205
default:
196206
throw new Error("Invalid GL Attribute type");
197207
}
198-
this.vertexSize = this.vertexByteSize / Float32Array.BYTES_PER_ELEMENT;
208+
this.vertexSize = this.stride / Float32Array.BYTES_PER_ELEMENT;
199209
}
200210

201211
/**
@@ -242,3 +252,5 @@ export default class Compositor {
242252
}
243253
}
244254
}
255+
256+
export default Batcher;

packages/melonjs/src/video/webgl/compositors/primitive_compositor.js renamed to packages/melonjs/src/video/webgl/batchers/primitive_batcher.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import primitiveFragment from "./../shaders/primitive.frag";
22
import primitiveVertex from "./../shaders/primitive.vert";
3-
import Compositor from "./compositor.js";
3+
import { Batcher } from "./batcher.js";
44

55
/**
66
* additional import for TypeScript
@@ -11,7 +11,7 @@ import Compositor from "./compositor.js";
1111
* A WebGL Compositor object. This class handles all of the WebGL state<br>
1212
* Pushes texture regions or shape geometry into WebGL buffers, automatically flushes to GPU
1313
*/
14-
export default class PrimitiveCompositor extends Compositor {
14+
export default class PrimitiveBatcher extends Batcher {
1515
/**
1616
* Initialize the compositor
1717
* @ignore

0 commit comments

Comments
 (0)