Take the desktop picker id through a flat varying, not gl_PrimitiveID - #6774
Take the desktop picker id through a flat varying, not gl_PrimitiveID#6774Fedr wants to merge 2 commits into
Conversation
The Ubuntu 26.04 UI test picked face -1090332440 out of a 512-face mesh. On desktop RenderMeshObject::renderPicker uses MeshDesktopPicker, whose fragment shader took the id from gl_PrimitiveID, and that is the one channel of the pick pixel that came back wrong - uniGeomId, a uniform in the neighbouring channel, was right. llvmpipe 21.1.8 / Mesa 26.0.8 is the only stack where it happens; 22.04 and 24.04 pass. The vertex shader already computes the id as uint(gl_VertexID) / primBucketSize for the corner-mode path, so pass that same value down a flat integer varying and read it instead. Emscripten keeps gl_PrimitiveID untouched behind #ifndef, and the varying is declared only in the mode that reads it so the lines pickers keep linking.
|
Closing: the flat id is only valid in corner mode, and the desktop picker is usually not in corner mode.
So in vertex mode That also weakens the evidence I posted above: on the ubuntu26 run the guard stopped warning and the crash disappeared, but a vertex-mode id lands in range — a valid face id, just the wrong face. Absence of an out-of-range value is not proof of a correct pick, and the feature scenarios compare final meshes with a tolerance, so a plausible-but-wrong surface point can pass them. I presented that as confirmation; it was not. Where that leaves the llvmpipe bug (mesa#15660, fixed on mesa
MeshInspectorCode#7749 tracks the user-visible crash and needs its "fixes in flight" section corrected. |
What was wrong
The Ubuntu 26.04 UI test picked face -1090332440 out of a 512-face mesh, which an instrumented run reports as:
On desktop,
RenderMeshObject::renderPickerusesMeshDesktopPicker(MRRenderMeshObject.cpp:157), whose fragment shader took the primitive id fromuint(gl_PrimitiveID). That is the channel that came back wrong. The decisive detail is what came back right:uniGeomId, a plain uniform written into the neighbouring channel of the very same pick pixel, resolved to a real mesh object — otherwise the guard would never have reached the mesh branch. One channel of one fragment correct, the other garbage, points atgl_PrimitiveIDitself rather than at the FBO, the read-back or the decode.The read-back is not at fault:
GL_RGBA32UItexture,glClearBufferuivto0xffffffff,glReadPixels(..., GL_RGBA_INTEGER, GL_UNSIGNED_INT, ...)— internally consistent. Nor is the id arithmetic: every renderer that draws for picking setsprimBucketSize(mesh 3 and 1, points 1, label 1; features delegate throughRenderObjectCombinator), so there is no division by zero.Upstream: this is a known llvmpipe regression
mesa#15660 (
llvmpipe,regression, filed 2026-06-15, closed 2026-07-22), reported against the same renderer string we run on,llvmpipe (LLVM 21.1.8). Its fix, mesa!42967 "llvmpipe: emit FS input vertex attributes in driver location order", states the cause:That is exactly this shader:
world_pos,primitiveIdf0andprimitiveIdf1are user varyings,gl_PrimitiveIDis read alongside them, and MeshLib has no geometry shader anywhere. It also accounts for the shape of the garbage --1090332440is bit-identical to the float-0.5111, i.e. a user varying's value read out of the wrong attrib slot, most likely aworld_poscomponent of the picked point.The fix commit
77251a488e91is on Mesamainonly - no stable tag, no26.0branch, no backport label - and Ubuntu 26.04 ships Mesa 26.0.8, so this is not something waiting on a distro update.Note the mesh draw shader reads
gl_PrimitiveIDthe same way on desktop (MRMeshShader.cpp, for flat shading and the selected-faces bitset). Being a varying-layout mismatch rather than a broken system value, the bug depends on each program's input set, and that one evidently lays out compatibly - theselectUI scenarios pass. Left alone deliberately: no reason to churn the rendering path that works.What this does
The picker vertex shader already computes
uint primId = uint(gl_VertexID) / primBucketSizefor the corner-mode path. This passes that same value down a flat integer varying and reads it in place ofgl_PrimitiveID:flat out uint primitiveIdFlat;/primitiveIdFlat = primId;in the picker vertex shader, desktop only.primitiveIdFlat.Emscripten keeps
gl_PrimitiveIDuntouched behind#ifndef __EMSCRIPTEN__, and the varying is declared only in the mode that reads it, soLinesPicker/LinesJointPickerkeep linking against vertex shaders that do not provide it.There is precedent for the platform split a few files over:
getMeshFragmentShaderColoringBlock()already picksgl_PrimitiveIDon desktop and the interpolated float pair on Emscripten.Evidence it works
Same MeshInspector UI suite, same ubuntu26 image, with this commit in the MeshLib gitlink:
face=-1090332440toTriPoint, then a second infindVisibleFaces(The invalid-pick log line and the absence of the first crash come from #6772, which guards that conversion independently; this PR removes the cause rather than the symptom, and the two do not depend on each other.)
| scenarios | died on the first pick of
create_feature_sphere| features and select suites run to completion, 5 of 6 suites pass |The remaining failure is unrelated: the
textscenario misses its mesh-similarity threshold by 0.0009 (0.9941 vs 0.995), which looks like a freetype/font difference on Resolute and is a baseline question, not a pick one.MRViewer.vcxprojbuilds clean locally andMeshViewer -hidden -noEventLoop -unloadPluginsAtEndexits 0 on Windows — though that only exercises the C++, since the picker shader is compiled lazily on the first pick.Note on scope
No
disable-build-*labels: this changes shader generation shared by every desktop GL platform, so Windows, macOS and the vcpkg legs all want to run, and the Emscripten leg is what proves the untouched ES branch still compiles.