-
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
Speed up Draco loading #6420
Speed up Draco loading #6420
Conversation
@ggetz, thanks for the pull request! Maintainers, we have a signed CLA from @ggetz, so you can review this at any time.
I am a bot who helps you make Cesium awesome! Contributions to my configuration are welcome. 🌍 🌎 🌏 |
Cool! Do you have time comparisons between master and here? |
Making some now. |
For the NYC Dataset:
So faster overall, and much better once the module is compiled once and we're just loading a lot of tiles. |
Test are failing only in |
@ggetz why is |
Correct. I've been testing the numbers with no caching, caching does not come for free, but is possible to implement (https://developer.mozilla.org/en-US/docs/WebAssembly/Caching_modules). |
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'm definitely seeing a nice speedup here. Works well in Chrome, Firefox, and Edge.
Overall the code looks good but I think it would be best if someone with more experience in the TaskProcessor
side of Cesium could take a look as well. Maybe @shunter?
CHANGES.md
Outdated
### 1.45 - 2018-05-01 | ||
|
||
##### Additions :tada: | ||
* Added `webAssemblyOptions` parameter to `TaskProcessor` to specify options for loading a Web Assembly module in a web worker. |
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.
Add links to the PR in the new entries.
CHANGES.md
Outdated
### 1.45 - 2018-05-01 | ||
|
||
##### Additions :tada: | ||
* Added `webAssemblyOptions` parameter to `TaskProcessor` to specify options for loading a Web Assembly module in a web worker. |
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.
Mention the addition to FeatureDetection
.
Specs/Core/FeatureDetectionSpec.js
Outdated
@@ -20,6 +20,11 @@ defineSuite([ | |||
expect(typeof supportsTypedArrays).toEqual('boolean'); | |||
}); | |||
|
|||
it('detects typed array support', function() { |
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.
Change test name.
gulpfile.js
Outdated
@@ -62,6 +62,7 @@ var sourceFiles = ['Source/**/*.js', | |||
'!Source/Workers/**', | |||
'!Source/ThirdParty/Workers/**', | |||
'!Source/ThirdParty/draco-decoder-gltf.js', |
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.
draco-decoder-gltf
got renamed.
CHANGES.md
Outdated
* Added `webAssemblyOptions` parameter to `TaskProcessor` to specify options for loading a Web Assembly module in a web worker. | ||
|
||
##### Fixes :wrench: | ||
* Faster loading of Draco compressed glTF assets. |
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.
Make some mention of WebAssembly, maybe "Faster loading of Draco compressed glTF assets in browsers that support WebAssembly".
Source/Workers/decodeDraco.js
Outdated
draco, | ||
createTaskProcessorWorker) { | ||
createTaskProcessorWorker | ||
) { |
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.
Formatting: change back to createTaskProcessorWorker) {
Source/Workers/decodeDraco.js
Outdated
@@ -125,6 +127,7 @@ define([ | |||
|
|||
function decodeDracoPrimitive(parameters) { | |||
if (!defined(dracoDecoder)) { | |||
draco = self.wasmModule; |
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.
Is there a better way to get the wasm module? This doesn't feel right.
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.
Or is this standard procedure for web worker tasks?
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 isn't necessarily standard. @shunter Do you know if there was a more straightforward way of passing this module to the web worker functions without altering their function signatures (too much).
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.
Well it doesn't seem like there's actually any relationship between loading Web Workers and loading a Web Assembly module, right? Why wouldn't the decodeDraco worker just load the module itself (using whatever helper method necessary)?
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.
Right now it's assumed all web worker function are synchronous, and compiling the module is always asynchronous. I could restructure createTaskProcessorWorker
to wait for a promise to be fulfilled if one is returned.
But additionally, which module to require is based on whether or not Web Assembly is supported, as we pass the backup js module if it is not. That's why I have this separate from the decodeDraco worker itself, as I don't want to require both modules.
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 see now that you're actually loading the wasm binary on the main thread and transferring the data over to the worker.
I guess in general, my suggestion is that the protocol between the the main thread (DracoLoader
) and the worker (decodeDraco
) can be whatever you want.
Our existing workers have sort of a two-stage loading process:
- The Worker is instantiated with
cesiumWorkerBootstrapper
, a special Worker which only understands bootstrap messages. TaskProcessor
posts a bootstrap message describing what worker module to transmogrify into.cesiumWorkerBootstrapper
loads that module and replacesonmessage
in order to hand off control.
At this point all of our existing workers are fully-formed (I think) but that doesn't necessarily have to be the case. You could add additional loading steps of your own design. decodeDraco
could expect its first message to contain the wasm binary or the path to the fallback script, which it could then load synchronously (which is preferable in a worker). Then, it could switch modes and expect subsequent messages to be traditional task messages and process as usual.
On the main thread, DracoLoader
could handle this protocol transparently by creating the TaskProcessor
and then immediately scheduling the first message to finish the load.
Ultimately you can do what you want, but the point of all this would be to tease apart these two concepts rather than fusing them together such that they have to know about each other.
@@ -6,8 +6,10 @@ define([ | |||
'./destroyObject', |
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.
Even though Draco doesn't work in IE yet I went to open Sandcastle in IE and it doesn't get past the loading screen. master
seems fine. Is this a result of any of the changes here?
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.
Merged in master, that should incorporate the error message fix.
@ggetz with point cloud decoding coming soon I think it would make sense to replace the Alternatively we could ship two versions, a _gltf version and a _pointcloud version. That would mean building the _pointcloud version ourselves (https://github.com/google/draco#webassembly-point-cloud-only-decoder). This would reduce upfront decode cost by cutting the wasm file by 100kb, but maybe not worth it. |
When we build the modules ourselves (CC #6404), it might be worth splitting them up since the initial decoding will be faster with smaller modules. For now I think the best solution is to swap out for the non-gltf modules here. Added TODO to the PR description. |
@lilleyse This should be ready for anther look. |
Fixes #6480 |
Source/Core/TaskProcessor.js
Outdated
@@ -182,6 +212,7 @@ define([ | |||
this._activeTasks = 0; | |||
this._deferreds = {}; | |||
this._nextID = 0; | |||
this._supportsWasm = FeatureDetection.supportsWebAssembly(); // exposed for testing purposes |
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.
Instead of adding this member variable for testing purposes, the test could mock FeatureDetection.supportsWebAssembly
returning false
.
Source/Scene/DracoLoader.js
Outdated
DracoLoader._getDecoderTaskProcessor = function () { | ||
if (!defined(DracoLoader._decoderTaskProcessor)) { | ||
DracoLoader._decoderTaskProcessor = new TaskProcessor('decodeDraco', DracoLoader._maxDecodingConcurrency); | ||
var processor = new TaskProcessor('decodeDraco', DracoLoader._maxDecodingConcurrency); | ||
processor._supportsWasm = false; |
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 line should be removed.
Source/Core/TaskProcessor.js
Outdated
// Web assembly not supported, use fallback js module if provided | ||
if (!processor._supportsWasm) { | ||
if (defined(wasmOptions.fallbackModulePath)) { | ||
config.modulePath = buildModuleUrl(wasmOptions.fallbackModulePath); |
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.
What happens if wasm is not supported and fallbackModulePath
is not provided? Should an error be thrown?
Source/Workers/decodeDraco.js
Outdated
|
||
// Expect the first message to be to load a web assembly module | ||
var wasmConfig = data.webAssemblyConfig; | ||
if (wasmConfig !== undefined) { |
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.
Use defined
here?
Source/Workers/decodeDraco.js
Outdated
// Require and compile WebAssembly module, or use fallback if not supported | ||
return require([wasmConfig.modulePath], function() { | ||
var dracoModule = self.DracoDecoderModule; | ||
if (wasmConfig.wasmBinaryFile !== undefined) { |
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.
Also here?
CHANGES.md
Outdated
@@ -3,18 +3,21 @@ Change Log | |||
|
|||
### 1.45 - 2018-05-01 | |||
|
|||
##### Additions :tada: | |||
* Improved `MapboxImageryProvider` performance by 300% via `tiles.mapbox.com` subdomain switching. [#6426](https://github.com/AnalyticalGraphicsInc/cesium/issues/6426) | |||
* Added `initWebAssemblyModule` function to `TaskProcessor` to loading a Web Assembly module in a web worker. [#6420](https://github.com/AnalyticalGraphicsInc/cesium/pull/6420) |
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.
Small typo: to loading
Draco 1.3.0 was released recently so we should update our Draco related files. One nice change is:
So there are a lot more functions for retrieving attributes now: https://github.com/google/draco/blob/master/src/draco/javascript/emscripten/draco_web_decoder.idl#L221-L236 |
Offline we had talked about supporting more
|
Given some of the architectural updates, it would be good to confirm that the performance numbers from #6420 (comment) are still correct. |
New numbers for the NYC Dataset:
|
Thanks @lilleyse ! Addressed your comments, updated Draco, and updated numbers. |
The new code changes look good. I just want to be sure about the numbers - are all the tilesets in #6420 (comment) gzipped whereas none of the old tilesets in #6420 (comment) were gzipped? Also do you know what might explain the much smaller gap between |
Correct
I found the original tilesets I ran the numbers against, I'll use that and update the numbers in my second comment. |
@lilleyse Updated numbers, they are pretty much identical. |
Thanks @ggetz! |
TaskProcessor
load binaries and compile Web Assembly modules in web workersTODO:
attributeData