Skip to content

Backend-neutral vertex formats and topologies (GLenum → WebGPU-style strings, with GLenum back-compat) #1551

Description

@obiot

Summary

Replace the raw GLenum values in the public batcher API with the WebGPU vocabulary — vertex formats ("float32x3", "unorm8x4") and topologies ("triangle-list", "line-list") — while continuing to accept the existing GLenum form indefinitely.

Groundwork for the future WebGPU backend, and a natural companion to #1492 (backend-neutral vertex layout descriptor).

Motivation

After #1509 each Batcher owns an immutable vertex state built from attributes + stride. That record is already shaped like a GPUVertexBufferLayout — except the type field holds a GLenum (gl.FLOAT, gl.UNSIGNED_BYTE), and draw modes hold gl.TRIANGLES and friends. Those are the last GL-specific values in an otherwise portable layout description, so a WebGPU backend would have to translate them on every path instead of consuming the record directly.

Scope — 6 public entry points, two vocabularies

Vertex formats (data type):

  • Batcher constructor, settings.attributes[].typebatchers/batcher.js
  • Batcher.addAttribute(name, size, type, normalized, offset)batchers/batcher.js

Topologies (draw mode):

  • PrimitiveBatcher.drawVertices(mode, verts, vertexCount)batchers/primitive_batcher.js
  • Batcher.flush(mode) and QuadBatcher.flush(mode)
  • the Batcher.mode property

Mapping

The WebGPU format grammar <baseType><bits>[x<count>] maps totally onto vertexAttribPointer: countsize, (baseType, bits) → the GLenum, unorm/snormnormalized, and bits × count / 8 → the byte width (which replaces the switch (type) stride block in addAttribute).

format size GL type normalized bytes
float32 1 FLOAT false 4
float32x2 2 FLOAT false 8
float32x3 3 FLOAT false 12
float32x4 4 FLOAT false 16
unorm8x4 4 UNSIGNED_BYTE true 4

Those five cover 100% of current engine usage (all five built-in batchers use only FLOAT sizes 1–4 and UNSIGNED_BYTE×4 normalized); the rest of the WebGPU set is a few extra table rows.

Backward compatibility

The GLenum form must keep working — custom batchers and custom shaders in the wild pass gl.FLOAT / gl.TRIANGLES today, and Batcher, QuadBatcher and PrimitiveBatcher are all public exports.

  • Detect the new form with typeof arg === "string"; otherwise take the legacy path.
  • The legacy path should normalize into the new representation (reverse lookup: (FLOAT, 3, false)"float32x3"), so there is exactly one internal record regardless of which entry point was used. This is the point of the exercise — the WebGPU backend reads that record directly.
  • Keep size / type / normalized populated on the attribute record as derived fields, so existing code reading attributes[i].type is unaffected.
  • This makes the change non-breaking, so it does not need a major version and the GLenum form can stay supported indefinitely.

Key design decision

Store the format string on the attribute record and resolve it to a GLenum inside Batcher.createVertexState() — the GL-specific consumer — not eagerly at addAttribute() time. Converting eagerly buys only the signature change and leaves the record GL-flavoured; resolving late makes attributes + stride literally a GPUVertexBufferLayout + arrayStride, so #1492 becomes largely a rename.

Bonus: topology normalization falls out for free

WebGPU has no triangle-fan or line-loop. Once drawVertices speaks the WebGPU vocabulary those modes cannot be named, so normalization at the entry point (fan → triangle-list, loop → line-strip with the first vertex re-appended — the latter already exists in the chunked path) stops being a separate decision.

No engine code emits them today: every renderer call site passes gl.TRIANGLES or gl.LINES, and generateTriangleFan() already expands to a triangle list. They are reachable-but-unused support in the public method. Normalizing therefore also makes the per-topology chunk-overlap rules in #drawVerticesChunked (fan re-anchoring, loop closing) deletable.

Notes

Part of the #1184 (WebGPU renderer) groundwork. Follow-up to #1509. Related: #1492 (backend-neutral vertex layout), #1410 (renderer-agnostic TextureCache/Batcher).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions