-
-
Notifications
You must be signed in to change notification settings - Fork 35.5k
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
ImageBitmapLoader: Support Firefox's partial createImageBitmap implementation. #12456
Conversation
@donmccurdy one thing I noticed when implementing pooled worker loaders is that there's actually a big benefit to loading the files in a separate queue, and then passing the binary data to the worker when it's ready to be processed. Fetching in the worker means that your workers are bound by network IO most of the time, so if you have a pool of 4 asset loaders, you'll end up blocking much longer overall as you work your way through the queue. If you have a separate download queue, your browser can also take better advantage of http pipelining. |
@jbaicoianu Thanks — I didn't do it here, but with a pool of 4 workers couldn't you keep sending requests for ImageBitmaps to the same workers, even before they've finished the last task? Or is there some particular advantage to all of the network IO happening on the main thread? Maybe I should be using |
@donmccurdy Need to look at your code in detail, but |
WIP: This is not fully working, yet, but almost all pieces are there. You can already get the idea: |
@kaisalmen thanks! Are the fixes to LoaderSupport changes that make sense from your perspective? Hopefully loading images isn't too much a one-off... Does LoaderSupport handle worker pooling this way? |
@donmccurdy I will likely revert the split of run into prepare and run as it not needed. I realized this morning. The |
…to Parser in compiled worker.
…to Parser in compiled worker.
@donmccurdy I would like to expand the example with a button to load n cubes (e.g. 256) with Let me know what you think? Edit: Do you know if a Blob is a transferable? Otherwise the postMessage is inefficient and an ArrayBuffer must be used. It can be returned by the fetch, but not feed to |
@spite looks good? |
I had a version that offloaded to a worker. I didn´t see a lot of difference, and I think the idea for most APIs dealing with downloads and image decoding the be moved outside of the main thread by the browser itself. So I didn't want to complicate the code with a Web Worker dependency, mostly because I usually ship an ES5 and an ES6 version of the code, which makes worker loading syntax a bit more verbose. But if it´s working fine, awesome! 🤘🏽 LGTM |
Oh! That would be great if the browser goes off-thread already... Will try this in Chrome and Firefox and report back... |
@donmccurdy Finally had time to create the Edit: Workers are created once and are then re-used. If the worker usage makes sense in this particular use case needs to be decided. Anyway a useful result of this experiment is that the overall concept of a common, instruction-based and repeatable worker usage can be applied to this class of problem. |
I agree completely with Juame - any XHR style request by default is already
largely non-blocking to the main thread - so before you all get bogged down
in the gritty of how to do it can we answer 'why' specifically?
Sure there's some stats but that's not really indicative of much..
- Michael
…On Mon, Oct 30, 2017 at 6:29 AM, Kai Salmen ***@***.***> wrote:
@donmccurdy <https://github.com/donmccurdy> Finally had time to create
the WorkerDirector proof of concept. The adjusted example requires 1.5GB
of spare graphics memory otherwise the browser will crash. Even with enough
graphics memory available Chrome crashes now and then. Workers are
destroyed properly, though. Let me know what you think?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#12456 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AE3lcX1oT82ytab2Z3mz1dkMOlu2qpgYks5sxPxOgaJpZM4QBc-q>
.
|
Also consider that a webworker cant even make XHR/fetch requests where a
response body is required as these will always be null so the main worker
is still going to be doing a lot more work as a transaction that is largely
simply offloaded to a fetch worker thread and then the handler called on
return becomes a much larger operation that will still largely all go
through the main thread.
The only time I can see this being advantageous is when there was an
extreme amount of immediate processing that is required on the resource
before it is usable by the main thread.
- Michael
On Mon, Oct 30, 2017 at 10:41 AM, Michael DeByl <michael.debyl@gmail.com>
wrote:
… I agree completely with Juame - any XHR style request by default is
already largely non-blocking to the main thread - so before you all get
bogged down in the gritty of how to do it can we answer 'why' specifically?
Sure there's some stats but that's not really indicative of much..
- Michael
On Mon, Oct 30, 2017 at 6:29 AM, Kai Salmen ***@***.***>
wrote:
> @donmccurdy <https://github.com/donmccurdy> Finally had time to create
> the WorkerDirector proof of concept. The adjusted example requires 1.5GB
> of spare graphics memory otherwise the browser will crash. Even with enough
> graphics memory available Chrome crashes now and then. Workers are
> destroyed properly, though. Let me know what you think?
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <#12456 (comment)>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AE3lcX1oT82ytab2Z3mz1dkMOlu2qpgYks5sxPxOgaJpZM4QBc-q>
> .
>
|
…Director. Adds latest updates from main branch to LoaderSupport and OBJLoader2.
It is not the request I am trying to offload from the main thread, but the image decoding triggered by the Will report results when I've verified this. It would certainly be more convenient if the worker is not needed. |
60-70% fewer dropped frames during texture upload is quite noticeable when the screen is strapped to your face. 😉 |
I've been talking to @paullewis about the relevance of the article, since it's from Jan 2016 and my experience from a few months ago showed that it might not be necessary any more. So in the time of the article, createImageBitmap would still occur in the main thread, but it's moved off it since then. The best way to show it, some profiling when clicking "Add ImageBitmap": You can see that Image Decode happens in a thread. If you profile with "Add Image", you'll see it happens in the main thread. So this is almost as good as it gets, no need for workers on Chrome. Can't say about other browsers -haven't tested- but I'm certain they'll all end up implementing async image decoding. |
f09f1f2
to
54ececc
Compare
Thanks @spite and all! Measuring here I get the same result: @kaisalmen thanks for your help and pointers! I will be wanting to use WorkerSupport soon for decoding DRACO-compressed assets in GLTFLoader, which definitely will benefit from a Web Worker. |
For now Chrome's createImageBitmap does image decoding off the main thread when the source is a blob, and it doesn't when it's an ImageElement or SVGElement. For the use case we have, i think it's fine with blobs. This is more or less what I hope we'll see in the future: https://www.chromestatus.com/features/5637156160667648 |
ImageBitmap just landed on Safari Technical Preview: https://webkit.org/blog/8016/release-notes-for-safari-technology-preview-43/ It appears to support options, but is currently failing on https://threejs.org/examples/?q=imagebitmap#webgl_loader_imagebitmap. |
Thanks! |
Update for ImageBitmapLoader:
options
argument tocreateImageBitmap()
. Also depends on Replace 'http-server' with 'serve'. #12455.Fetch and create the ImageBitmap in a worker by default. I believe (and please correct me if I'm wrong) there isn't much value in creating an ImageBitmap on the main thread.This can be used with any loader through a
THREE.Loader.Handlers
hook:Performance: Load and upload time (ms)
So far the results look promising. As expected, loading duration is much longer (2-3x). Texture upload is much faster, and the overall time to first render is only slightly slower (1.1x to 1.15x). But — and the key thing for WebXR purposes — parsing glTF does not typically block the main thread. In these examples, the only significant blocking happens during texture upload. With the ImageBitmap, we spend 60-70% less time uploading textures, and drop fewer frames as a result.
These aren't necessarily representative models, they're just two that had obvious render jank among glTF samples. But I'd be curious for ideas on how to proceed with this. Before merging there would probably need to be some support for pooling the workers to avoid browser limits.
@spite — Thanks for adding ImageBitmapLoader! Any opinion on the changes proposed?
@kaisalmen — I wasn't sure how to use
THREE.LoaderSupport.WorkerSupport
. Is there a better way to create the web worker using that? Or is it meant for use by model loaders more than texture loaders?@ngokevin — FYI I'm imagining A-Frame could enable this mode if the user loads a model after VR presentation has begun.
P.S. To anyone reading the diff, easier with ?w=1.