-
Notifications
You must be signed in to change notification settings - Fork 3.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
Use createImageBitmap when available #7579
Conversation
Thanks for the pull request @OmarShehata!
Reviewers, don't forget to make sure that:
|
Ok @mramato I've refactored it to use The only outstanding issue is #7579 (comment). I'm still using the fetch API in |
One idea would be to move the feature detection code into |
Ok, the good news is that the new approach works and not only simplifies the code but things are functionally better too. Here's some highlights:
The other good news is that I found out why there was an extra parameter being passed to https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Core/Resource.js#L911-L916 Or just remove that parameter from I will bump this when CI passes. |
@mramato this is good to look at. See #7579 (comment) for a summary of the changes. |
Nice, thanks again @OmarShehata. I'll do another pass on this either later tonight or first thing tomorrow. |
What's the status of this PR? Last I heard we were trying to get some info/advisement from the Firefox team? Do we want to get this into the Friday release? |
I still haven't heard back. Perhaps this might be better to merge after release at this point? |
Merging after is fine. I would re-recommend doing 2 and then making additional changes once we hear back from the FF team. Just bump this when it's ready. Thanks! |
…into image-bitmap
We didn't get a definitive answer back, but it looks like there are no known plans for those bugs. The consensus in this ThreeJS thread is to just disable for Firefox. We're already detecting it by actually calling it to see if it works, so no need to detect and disable for any specific browser. I've updated the code to only use createImageBitmap when ImageBitmapOptions is supported. This does simplify the calling code a bit since we no longer need to account for the two paths. @mramato no rush on this review at the moment, but would be good to get this far-reaching in sometime this/next week. I did one final series of tests to make sure models and imagery still all loaded correctly with a couple of Sandcastle examples on Chrome, Firefox and Edge. |
Source/Core/Resource.js
Outdated
return; | ||
} | ||
|
||
Resource.fetchBlob({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have a return statement and then you should move all of the following then
calls up a level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why? I'm assuming this is what you mean:
Resource.supportsImageBitmapOptions()
.then(function(result) {
if (!result) {
loadImageElement(url, crossOrigin, deferred);
return;
}
return Resource.fetchBlob({
url: url
});
})
.then(function(blob) {
return Resource._Implementations.createImageBitmapFromBlob(blob, flipY);
})
.then(deferred.resolve).otherwise(deferred.reject);
This will run the .then(function(blob)
anyway even if we loadImageElement
and don't fetch the blob, and then blob
will be undefined
, which we don't want, right?
I can just move the .then(deferred.resolve).otherwise(deferred.reject);
outside and I think that should work fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see Resource.prototype.fetchImage
checks if the result is defined at each step in the promise chain to make sure it doesn't run when it terminates early like that. I can follow that pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because if for some reason supportsImageBitmapOptions
rejected, you would never call deferred.reject
(or deferred.resolve
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see
Resource.prototype.fetchImage
checks if the result is defined at each step in the promise chain to make sure it doesn't run when it terminates early like that. I can follow that pattern.
I'm not sure what you mean by this, please stop by if you still have questions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation. I'm pretty sure I've got it locked down now. What I meant was, Resource has a similar promise chain when fetching an image. When we return undefined, all the subsequent then's are still going to run. So you need to add a check at each subsequent then() that the result is defined.
Here is where Resource is doing this:
I just followed a similar pattern in createImage
:
Notice that deferred.resolve
is only called in the ImageBitmap codepath. Since loadImageElement
calls it on the Image
on onload
.
Will bump when CI passes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this whole file could probably use some clean-up. Some of the (old) if checks make reading the code pretty confusing. No action on your part, and this is nothing you did; just mentioning it because I may open a small clean-up PR to take a pass (after merging this).
@mramato all good now. Last commit makes all testing rely just on the |
Resource) { | ||
'use strict'; | ||
|
||
function isImageOrImageBitmap(image) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be better as a custom Jasmine matcher than a standlone function, I'll just submit a fix to master after merging.
Thanks @OmarShehata! I think we can finally call this done. Hopefully Firefox and Safari will get in line and add full support so we can benefit there as well. |
This PR adds support for using
createImageBitmap
to move image decoding off the main thread. This doesn't make loading any particular image faster, but it greatly improves frame stutters by avoiding the expensive blocking image decode that happens on texture upload. It might also improve loading scene time since images can be decoded in parallel.This builds off the other PR #6625. The problem there was that images created with
createImageBitmap
can be flipped on texture upload only when passingImageBitmapOptions
to thecreateImageBitmap
was not supported. This is because it's faster to flip the image during decode. So I had to add a way to flip an image when fetching it.@mramato to make this easier to review, I recommend reading each commit separately:
flipImage
onResource
, and usescreateImageBitmap
on all image requests, as well as inloadImageFromTypedArray
. It also kicks off the support check in the Viewer constructor.createImageBitmap
behaves how we expect it to. Specifically, it tests that it's possible to flip the image either on fetch or on texture upload as expected, and reads back the pixels since this is the only way to know if the operation truly worked.ImageBitmap
instead ofImage
when it's supported.Why it's necessary to expose
flipY
onfetchImage
We can't make decisions about whether to flip inside
Resource
because whether we flip or not depends on what we're going to do with this image. All textures in CesiumJS are flipped by default during texture upload. This seems to be because imagery expects this orientation.Models with textures will disable this flipping to get the correct orientation. This means that there's no one correct orientation. If we always flip, models will be upside down. If we never flip, imagery will be upside down. I spent some time trying to see if I could get all imagery to be flipped in the shader so we can just simply never have to worry about flipping during texture upload but I couldn't get it to work correctly with the projection.
Tests & Results
To compare before/after, simply add
return false
to the first line ofFeatureDetection.supportsCreateImageBitmap
.This bike model is a good test, since it has huge textures that severely block the main thread when loading in.
If the glTF model is in
SampleData/models/Bike
you can use this local Sandcastle to see it loading while rotating, which makes the frame jank obvious.Image bitmap on the left:
Image bitmap on top:
It does also make a big difference with tilesets, which often load in lots of images during camera fly throughs.
Another test is on the Melbourne 3D Tileset, during a fly through, we see the following before (top) and after: