Variable GLSL_ELEMENT_ID_SPLITConst
GLSL_ELEMENT_ID_SPLIT: "\n// Element index split into two 16-bit halves, low in .x and high in .y.\n// The pick pass carries the index through an RGBA32F buffer, and float32\n// has a 24-bit mantissa — so a single float channel cannot represent\n// consecutive indices past 16,777,216, while a node's capacity reaches\n// 2^25 on a 32768-texel device. Both halves are <= 65535, hence exact,\n// and the pick decoder recombines them (see picking-system/pick-render.ts).\n// Kept in INT space: doing the split on a float would already have lost\n// the bit it is meant to preserve.\nvec2 luxarElementIdSplit(uint i) {\n return vec2(float(i & 0xFFFFu), float(i >> 16u));\n}\n" = ...
The pick buffer's 16-bit element-id split, as a standalone function of an arbitrary index.
Separate from GLSL_SORTED_INDEX because mesh needs the split WITHOUT the ordering attributes: its pick id is
gl_VertexID(mesh IS depth sorted, but its ordering permutesgeometry.indexitself, so there is noaSortedIndexindirection to read — spec §6.5), so injecting the whole sorted-index block would declare two attributes the geometry does not carry. Declaring an unbound attribute is not merely wasteful on WebGPU — the vertex-buffer layout is cached from the attribute set at first draw.The split itself must NOT be written twice. Both halves have to agree with
voteWinner'shigh * 65536 + lowrecombination exactly (picking-system/pick-render.ts), and a second copy is a place for the shift or the mask to drift where the only symptom is picks resolving to the wrong element past 65,536 — silent, and only on large nodes.