Found PolylineCollection bug, would appreciate maintainer opinions on solution implementation

Summary

When a PolylineCollection holds enough polylines that its vertex count reaches 65536, PolylineBucket.updateIndices writes index 65535 into the Uint16Array index buffer. In WebGL 2, PRIMITIVE_RESTART_FIXED_INDEX is always enabled (see here). Because Cesium uses unsigned shorts by default, this means 65535 is reserved and can’t be used as a vertex index. This results in a vertex that spikes to random screen positions every frame.

I’ve hit this a couple times over the last couple of years (see my only other post lol), so I finally went digging for the root cause. I’m fairly sure it’s also what people are seeing in this 2D thread, along with a couple others.

Sandcastle (because I am a model forum user !)

link

I tried to comment the code enough to make it clear what’s going on. The toolbar lets you see various config options I was going through while testing. You can pick counts that put the vertex buffer boundary either exactly on a polyline boundary (65 points per line) or mid-polyline (73 points per line). Behavior also changes based on shared vs. isolated material.

There’s also a USE_WEBGL1 flag I threw at the top so you can confirm what I said in the summary.

Things came across

With per-frame position updates off, nothing ever glitches

The spike only appears when that polyline shares a draw command with the polylines before it. Giving it its own material makes the spike disappear

Locally changing the split condition in updateIndices so the highest index emitted is below 65535 also eliminates the glitch in every configuration I tried

Code/nitty-gritty

In PolylineBucket.prototype.updateIndices, the check before each segment is

if (indicesCount + 4 > CesiumMath.SIXTY_FOUR_KILOBYTES) {

With indicesCount = 65532, 65536 > 65536 is false, so the segment 65532, 65534, 65533, 65533, 65534, 65535 is pushed and the split only happens on the next segment. That segment contains the reserved index.

EDIT: I explained this poorly, here’s some ms paint:

Why the reserved index makes a massive spike (rather than a dropped triangle), and only while the buffer is being updated, and only when the polyline is batched into a larger draw command? I have literally no idea! I hope never to know.

Possible fixes

I can think of two fixes, either adjusting the splitting (changing both indicesCount + 4 checks to use >=, fixing the between-polyline branch, maybe something else?) or switching to 32-bit indices on WebGL 2. It feels like switching to 32 bit would get rid of this whole class of bug (and gets rid of vertexBufferOffset, _locatorBuckets, whatever the 2d path is doing, etc.), but it would more involved to implement. Plus, you’d need to keep the splitting path around for WebGL 1 contexts and that obviously sucks.

Either way I’d love to throw together a pull request if this isn’t something you already know about.

Hey @Ben-W, thanks for the great writeup! With support for OES_element_index_uint currently at 99.92%, I think it’s probably a good time to eject the 65536-vertex bucketing and use 32-bit indices if the collection has >=65536 vertices. I don’t think a fallback is necessary any more. If you’d be open to making a PR along those lines, that’d be much appreciated. :slight_smile: