Add KHR_mesh_primitive_restart#2569
Conversation
|
|
|
I've updated the PR description including simple test assets for all four topology types (line strip, line loop, triangle strip, triangle fan). Will address open comments on the PR next - thanks all! |
Co-authored-by: Adam Morris <adam@cesium.com>
- Prefer 'restart index' to 'restart value' - Clarify that maximum possible index values are still restricted for list topologies - Minor cleanup
| > `indices` accessor **MUST NOT** contain the maximum possible value for the component type used (i.e., 255 for unsigned bytes, 65535 for unsigned shorts, 4294967295 for unsigned ints). | ||
|
|
||
| This extension removes the restriction above, allowing `indices` accessors to contain the maximum possible value for the component type in select primitive draw modes, and specifying that these values indicate primitive restart commands. | ||
| This extension modifies the restriction above, allowing `indices` accessors to contain the maximum possible value for the component type in select primitive draw modes, and specifying that these values indicate primitive restart commands. |
There was a problem hiding this comment.
This is a potentially a nitpick, but it is better to use "relaxes" instead of "modifies" here. Modifies is a neutral term, whereas relaxes is a more technically precise term. Stating, "relaxes the restriction," helps to clearly establish the change in constraints adding to overall clarity.
There was a problem hiding this comment.
That's OK with me, so long as we think it's clear that "relaxes" does not imply removing the restriction entirely (list topologies are still subject to it). Personally I find "relaxes" to be clear in context. I'd also be fine with @lexaknyazev's earlier suggestion of "adjusts", but that's a similarly neutral term, so I'll let you both decide. :)
(For now, I've pushed a change to "relaxes")
There was a problem hiding this comment.
@weegeekps : Copyright date should be the earliest date that the software is first publicly published (including distribution). It is allowed to include a range of years to indicate the most recent significant update. So something like: (c) 2025-2026 Bentley Systems, Incorporated. (or whatever is the correct years & owner). If there are multiple owners, then multiples lines are used.
|
In addition to the new samples linked above, I've pushed changes based on the suggestions and feedback. No intended changes in meaning or implementation, all changes are clarifications. I believe all outstanding feedback has been addressed, thanks @lexaknyazev and @weegeekps! |
Motivation
glTF 2.0 includes seven primitive modes:
0 POINTS1 LINES2 LINE_LOOP3 LINE_STRIP4 TRIANGLES5 TRIANGLE_STRIP6 TRIANGLE_FANFour of these (
LINE_LOOP,LINE_STRIP,TRIANGLE_STRIP,TRIANGLE_FAN) define topologies that may contain any number of vertices. Each glTF mesh primitive defines a single contiguous topology for these types, because primitive restart values — which would instruct the graphics API to begin a new topological primitive, without a new draw call — are disallowed by the core specification for maximum compatibility (see: WebGL 1).This PR proposes a
KHR_mesh_primitive_restartextension, which can be added to any glTF asset, removing the restriction against primitive restart values. The mechanism and implementation are intentionally very similar toKHR_mesh_quantization, which removes restrictions on particular vertex attribute component types.Primitive restart values allow a scene to contain arbitrary numbers of line loops, line strips, triangle strips, and triangle fans, without requiring arbitrary numbers of mesh primitives, along with costly processing and draw calls. This also greatly reduces the amount of JSON (primitive and accessor) definitions required to represent such scenes.
Background
The existing
EXT_mesh_primitive_restartallows primitive restart values, but does so by encoding each primitive topology as a separate glTF mesh primitive, defined in JSON and having a distinct indices accessor. The extension adds indices with restart values as metadata, allowing runtimes to stitch these primitives together as a single draw call, but the runtime must still download and parse data for the original N primitives. Unfortunately, the performance implications of this approach are not ideal. See CesiumGS#100.EXT vs. KHR
I'm open to modifying the existing
EXT_mesh_primitive_restartextension, but modifying an existing EXT extension would be an unusual step. While I believe it has only very limited implementations today, initial feedback has been on the side of avoiding changes to an existing EXT extension.For that reason, and hoping the approach here might be broadly useful for anyone using these primitive topologies in glTF 2.0, I'm proposing a simplified/optimized approach as a new extension,
KHR_mesh_primitive_restart.Fallback
If a runtime doesn't support primitive restart values, it would need to pre-process the index list. But this optimizes the extension for present-day and future use, where primitive restart support is standard, and imposes the cost of fallback only on the small (and shrinking) cases where primitive restart is not supported in hardware. I believe that's an important improvement for adoption and production use of the extension.
Fallback may not necessarily require separate draw calls for each topological primitive. Multi-draw Indirect (MDI) would be an alternative (if there exists a graphic API that supports MDI but not primitive restart values). I'm not familiar with how a path-tracing renderer would want to handle primitive restart indices.
Samples
PrimitiveRestartLineStrip.glb.zip
In some viewers (including https://gltf-viewer.donmccurdy.com/) the model will display correctly without any modifications to support
KHR_mesh_primitive_restart, because primitive restart is already enabled by default by the WebGL 2 API.Additional, simpler samples for line strip, line loop, triangle strip, and triangle fan topologies:
The line strip and line loop examples currently work in three.js, but not the triangle strip and triangle fan samples. All four work in PlayCanvas, shown below:
Implementations