EXT_mesh_primitive_restart: Performance optimizations and schema updates#100
EXT_mesh_primitive_restart: Performance optimizations and schema updates#100donmccurdy wants to merge 1 commit into
Conversation
|
|
||
| Because the extension does not provide a way to specify fallback indices without restart values, files that use the extension must specify it in `extensionsRequired` array - the extension is not optional. | ||
|
|
||
| > **Implementation Note:** Implementations on graphics APIs without primitive restart may still support the extension, by rewriting primitive indices. Compared to processing many more primitives and accessors, the extension may still provide performance advantages even for these implementations, in assets with many primitives. |
There was a problem hiding this comment.
These changes led me to doing a little research into the landscape of primitive restart and where the GPUs and APIs currently land on it. The short and sweet of it is that the last device that doesn't support it was produced over a decade ago. With the exception of OpenGL SC (Safety Critical), OpenGL ES 2.0 and WebGL 1.0 everything supports primitive restart and implements it effectively the same way.
Of the 3 that don't support primitive restart, OpenGL SC is the only one that concerns me. I know some automotive manufacturers use glTF but I'm unsure if they use it with OpenGL SC. I would assume they're using some safety critical API because of ASIL, but whether that is Vulkan SC or OpenGL SC is unclear to me.
There's nothing to change here but I just wanted to add some context to what the landscape currently looks like.
There was a problem hiding this comment.
That's great to know, thanks — I hadn't looked beyond WebGL and WebGPU yet.
| This extension permits the above prohibition to be selectively relaxed, while providing a trivial fallback for implementations that don't support primitive restart. | ||
| 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. | ||
|
|
||
| Because the extension does not provide a way to specify fallback indices without restart values, files that use the extension must specify it in `extensionsRequired` array - the extension is not optional. |
There was a problem hiding this comment.
Full disclosure: I am on the fence about the thoughts I've written below. I've been in standards long enough to know to avoid devil's advocacy, but I am still commenting here as I think we need to further discuss this. It may be overkill given the situation.
We should still allow (and encourage) supporting for fallback, despite what I discovered about the landscape of primitive restart. The complexity of glTF has continued to rise as 3DF evolves the standard.
This change would be the special case: the additional accessor metadata goes beyond the point where it begins to harm the ability to stream the glTF efficiently, so the glTF author makes it required and the additional metadata is no longer required.
Ultimately, we would like to eventually consider petitioning for ratification of this extension. A lack of a fallback may be a strategic issue from the 3DF perspective.
There was a problem hiding this comment.
Understood that you're on the fence! A few comments for the record, just in case. Here, the fallback creates a "medicine worse than the disease" situation. Rewriting the indices as a runtime fallback is fairly cheap, and I think preferable to receiving separate primitives to begin with.
Certainly a different case than BasisU, Draco, or Meshopt compression, where an explicit fallback is necessary for any fallback to be available. And even in those cases (while I support making the fallback possible!), I would caution that I don't think these fallbacks are widely implemented today.
There was a problem hiding this comment.
Here, the fallback creates a "medicine worse than the disease" situation. Rewriting the indices as a runtime fallback is fairly cheap, and I think preferable to receiving separate primitives to begin with.
Yes, definitely. It may be worthwhile to add a non-normative section that goes through how to rewrite the indices at a high-level. While it may seem clear to you and me, it may not be clear to the dev who ends up implementing support for this extension in some obscure rendering engine.
| [0, 1] [2, 3, 4] | ||
| ``` | ||
|
|
||
| This permits two equivalent representations of the geometry without duplicating any binary data. In glTF, the accessors look like this: |
There was a problem hiding this comment.
Would it make sense to include an updated JSON example?
There was a problem hiding this comment.
So, there's not really any JSON defined by the extension now — the glTF asset lists the extension under extensionsUsed and extensionsRequired, and the (binary) mesh primitive indices buffers are then allowed to contain primitive restart values (0xFF, 0xFFFF, or 0xFFFFFFFF). But the JSON defining the mesh primitives and their accessors is unchanged from the core spec.
|
Example dataset: The ZIP contains segments sampled from U.S. Highways in GeoJSON format, and three representations of the same file in glTF, with 125,000 LINE_STRIP primitives:
All three glTF models display correctly on https://gltf-viewer.donmccurdy.com/. three.js does not explicitly implement EXT_mesh_primitive_restart, but requires WebGL 2 and primitive restart is therefore on by default. The example is meant to demonstrate that the JSON required to represent each primitive separately is costly. I didn't attempt to include EXT_mesh_primitive_restart in any of these samples, and just wrote the mesh primitive data as the extensions would have required. The files using primitive restart are therefore technically invalid. If there's a path forward for the extension, I'll create more sample files using EXT_mesh_primitive_restart properly. |
|
To be continued: |
Motivation
In its current form, the
EXT_mesh_primitive_restartextension encodes each primitive (line strip, line loop, triangle strip, triangle fan) as a separate glTF mesh primitive, defined in JSON and having a distinct indices accessor. The extension adds metadata allowing runtimes to stitch these primitives together as a single draw call using the primitive restart values, but the runtime must still download and parse data for the original N primitives.Unfortunately... I think this approach is increasing file size a lot. For a U.S. Highways dataset (125K small polylines) GLB size increases from 8 MB to 38 MB after splitting each polyline into a separate primitive as the extension currently requires. I did reuse one POSITION attribute across all primitives. Draco and Meshopt compression don't help here, because much of the extra data is JSON.
I would ideally like to propose that
EXT_mesh_primitive_restartshould instead do whatEXT_mesh_quantizationdoes, and simply say "mesh primitives are allowed to use primitive restart values" now, without needing further metadata. If the 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.Draft samples
TODO
Draft implementations