Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GLTFLoader: Update MeshoptDecoder support to support WebWorkers #24460

Merged
merged 1 commit into from
Aug 12, 2022
Merged

GLTFLoader: Update MeshoptDecoder support to support WebWorkers #24460

merged 1 commit into from
Aug 12, 2022

Conversation

zeux
Copy link
Contributor

@zeux zeux commented Aug 7, 2022

MeshoptDecoder can decode at ~1 GB/sec (with filters) on modern desktop
CPUs, which mostly means we don't need to offload the processing to
other threads... except when we're dealing with scenes with ~800 MB of
geometry, at which point we might be stalling the main thread for
700-900ms.

To solve this, meshoptimizer is implementing support for WebWorkers:
zeux/meshoptimizer#454

The decoder gains a new function, decodeGltfBufferAsync, which works
regardless of whether WebWorkers are enabled; when decoder.useWorkers is
called with the desired worker count, it switches to asynchronous
decoding which almost entirely eliminates main thread overhead, with the
exception of copying the input buffer.

This change is structured to still work with the old versions of the
library, the library will be updated separately. Note that when
decodeGltfBufferAsync is used, ready promise doesn't need to be used.

@zeux
Copy link
Contributor Author

zeux commented Aug 7, 2022

It's been a little while since I've looked at three.js so I'm not sure what the jsm/js situation is; this change for now just updates the jsm version. Feedback on this change or the linked PR is welcome!

MeshoptDecoder can decode at ~1 GB/sec (with filters) on modern desktop
CPUs, which mostly means we don't need to offload the processing to
other threads... except when we're dealing with scenes with ~800 MB of
geometry, at which point we might be stalling the main thread for
700-900ms.

To solve this, meshoptimizer is implementing support for WebWorkers:
zeux/meshoptimizer#454

The decoder gains a new function, decodeGltfBufferAsync, which works
regardless of whether WebWorkers are enabled; when decoder.useWorkers is
called with the desired worker count, it switches to asynchronous
decoding which almost entirely eliminates main thread overhead, with the
exception of copying the input buffer.

This change is structured to still work with the old versions of the
library, the library will be updated separately. Note that when
decodeGltfBufferAsync is used, `ready` promise doesn't need to be used.
@gkjohnson gkjohnson requested a review from donmccurdy August 8, 2022 01:26
@donmccurdy
Copy link
Collaborator

Thanks @zeux! It's a huge credit to your work on the meshopt decoder that Web Workers haven't been necessary for most use cases so far. E.g. when a 5 MB geometry decodes in only 5ms, it can fit within many frame budgets. Agreed that Web Workers make sense for these very large geometry files though, or very framerate-sensitive applications like those using WebXR.

The only downside I can see is that the round-trip to/from Web Workers does add a bit to wall time. I'm not sure if this is current anymore, but it used to take ~40ms to initialize a Web Worker. Since the user can opt in/out with useWorkers(count), this doesn't seem like a problem.

No need to update the js/ copy, it'll be autogenerated.

@zeux
Copy link
Contributor Author

zeux commented Aug 10, 2022

Right - the startup latency is less of a problem here since the workers will be started eagerly when useWorkers is called, so it will likely overlap with the latency of fetching the actual assets, but for geometry-light cases it might be the case that workers aren't a good idea because of the extra latency of decoding as you'd need to pass a few buffers back and forth. Also there's an additional copy involved to be able to transfer the data.

Either way extremely geometry heavy cases are rare enough that web workers make sense here more as an opt-in, by default this change won't change the mechanics of decoding.

@zeux zeux marked this pull request as ready for review August 10, 2022 03:16
@Mugen87 Mugen87 added this to the r144 milestone Aug 10, 2022
@mrdoob mrdoob merged commit 223c572 into mrdoob:dev Aug 12, 2022
@mrdoob
Copy link
Owner

mrdoob commented Aug 12, 2022

Thanks!

abernier pushed a commit to abernier/three.js that referenced this pull request Sep 16, 2022
…ob#24460)

MeshoptDecoder can decode at ~1 GB/sec (with filters) on modern desktop
CPUs, which mostly means we don't need to offload the processing to
other threads... except when we're dealing with scenes with ~800 MB of
geometry, at which point we might be stalling the main thread for
700-900ms.

To solve this, meshoptimizer is implementing support for WebWorkers:
zeux/meshoptimizer#454

The decoder gains a new function, decodeGltfBufferAsync, which works
regardless of whether WebWorkers are enabled; when decoder.useWorkers is
called with the desired worker count, it switches to asynchronous
decoding which almost entirely eliminates main thread overhead, with the
exception of copying the input buffer.

This change is structured to still work with the old versions of the
library, the library will be updated separately. Note that when
decodeGltfBufferAsync is used, `ready` promise doesn't need to be used.
snagy pushed a commit to snagy/three.js-1 that referenced this pull request Sep 21, 2022
…ob#24460)

MeshoptDecoder can decode at ~1 GB/sec (with filters) on modern desktop
CPUs, which mostly means we don't need to offload the processing to
other threads... except when we're dealing with scenes with ~800 MB of
geometry, at which point we might be stalling the main thread for
700-900ms.

To solve this, meshoptimizer is implementing support for WebWorkers:
zeux/meshoptimizer#454

The decoder gains a new function, decodeGltfBufferAsync, which works
regardless of whether WebWorkers are enabled; when decoder.useWorkers is
called with the desired worker count, it switches to asynchronous
decoding which almost entirely eliminates main thread overhead, with the
exception of copying the input buffer.

This change is structured to still work with the old versions of the
library, the library will be updated separately. Note that when
decodeGltfBufferAsync is used, `ready` promise doesn't need to be used.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants