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

Speed up Draco loading #6420

Merged
merged 12 commits into from
Apr 24, 2018
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Change Log
==========

### 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.
Copy link
Contributor

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.

Copy link
Contributor

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.


##### Fixes :wrench:
* Faster loading of Draco compressed glTF assets.
Copy link
Contributor

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".


### 1.44 - 2018-04-02

##### Breaking Changes :mega:
Expand Down
9 changes: 9 additions & 0 deletions Source/Core/FeatureDetection.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ define([
return hasPointerEvents;
}

var hasWebAssembly;
function supportsWebAssembly() {
if (!defined(hasWebAssembly)) {
hasWebAssembly = typeof WebAssembly === 'object';
}
return hasWebAssembly;
}

var imageRenderingValueResult;
var supportsImageRenderingPixelatedResult;
function supportsImageRenderingPixelated() {
Expand Down Expand Up @@ -218,6 +226,7 @@ define([
isWindows : isWindows,
hardwareConcurrency : defaultValue(theNavigator.hardwareConcurrency, 3),
supportsPointerEvents : supportsPointerEvents,
supportsWebAssembly : supportsWebAssembly,
supportsImageRenderingPixelated: supportsImageRenderingPixelated,
imageRenderingValue: imageRenderingValue
};
Expand Down
91 changes: 84 additions & 7 deletions Source/Core/TaskProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ define([
'./destroyObject',
Copy link
Contributor

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?

Copy link
Contributor Author

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.

'./DeveloperError',
'./Event',
'./FeatureDetection',
'./getAbsoluteUri',
'./isCrossOriginUrl',
'./Resource',
'./RuntimeError',
'require'
], function(
Expand All @@ -18,8 +20,10 @@ define([
destroyObject,
DeveloperError,
Event,
FeatureDetection,
getAbsoluteUri,
isCrossOriginUrl,
Resource,
RuntimeError,
require) {
'use strict';
Expand Down Expand Up @@ -132,13 +136,49 @@ define([
return bootstrapperUrlResult;
}

function getWebAssemblyLoaderConfig(processor) {
if (defined(processor._webAssemblyConfig)) {
return when.resolve(processor._webAssemblyConfig);
}

var config = {
modulePath : undefined,
wasmBinaryFile : undefined,
wasmBinary : undefined
};

var wasmOptions = processor._webAssemblyOptions;
if (!defined(wasmOptions)) {
return when.resolve(config);
}

// Web assembly not supported, use fallback js module if provided
if (!FeatureDetection.supportsWebAssembly()) {
if (defined(wasmOptions.fallbackModulePath)) {
config.modulePath = buildModuleUrl(wasmOptions.fallbackModulePath);
}
return when.resolve(config);
}

config.modulePath = buildModuleUrl(wasmOptions.modulePath);
config.wasmBinaryFile = buildModuleUrl(wasmOptions.wasmBinaryFile);

return Resource.fetchArrayBuffer({
url: config.wasmBinaryFile
}).then(function (arrayBuffer) {
config.wasmBinary = arrayBuffer;
return config;
});
}

function createWorker(processor) {
var worker = new Worker(getBootstrapperUrl());
worker.postMessage = defaultValue(worker.webkitPostMessage, worker.postMessage);

var bootstrapMessage = {
loaderConfig : {},
workerModule : TaskProcessor._workerModulePrefix + processor._workerName
workerModule : TaskProcessor._workerModulePrefix + processor._workerName,
webAssemblyConfig : undefined
};

if (defined(TaskProcessor._loaderConfig)) {
Expand All @@ -152,11 +192,39 @@ define([
};
}

worker.postMessage(bootstrapMessage);
getWebAssemblyLoaderConfig(processor).then(function(wasmConfig) {
processor._webAssemblyConfig = wasmConfig;

worker.onmessage = function(event) {
completeTask(processor, event.data);
};
return when(canTransferArrayBuffer(), function(canTransferArrayBuffer) {
if (!defined(wasmConfig.modulePath)) {
worker.postMessage(bootstrapMessage);

worker.onmessage = function(event) {
completeTask(processor, event.data);
};

return processor._workerReadyPromise.resolve(canTransferArrayBuffer);
}

bootstrapMessage.webAssemblyConfig = wasmConfig;

var transferableObjects;
var binary = wasmConfig.wasmBinary;
if (defined(binary) && canTransferArrayBuffer) {
transferableObjects = [binary];
}

worker.postMessage(bootstrapMessage, transferableObjects);

worker.onmessage = function() {
worker.onmessage = function(event) {
completeTask(processor, event.data);
};

processor._workerReadyPromise.resolve(canTransferArrayBuffer);
};
});
});

return worker;
}
Expand All @@ -175,13 +243,21 @@ define([
* @param {Number} [maximumActiveTasks=5] The maximum number of active tasks. Once exceeded,
* scheduleTask will not queue any more tasks, allowing
* work to be rescheduled in future frames.
* @param {Object} [webAssemblyOptions] If specified, the worker will load and compile a Web Assembly module before scheduling tasks. An object with the following properties:
* @param {String} [webAssemblyOptions.modulePath] The path of the web assembly JavaScript wrapper module.
* @param {String} [webAssemblyOptions.wasmBinaryFile] The path of the web assembly binary file.
* @param {String} [webAssemblyOptions.fallbackModulePath] The path of the fallback JavaScript module to use if web assembly is not supported.
*/
function TaskProcessor(workerName, maximumActiveTasks) {
function TaskProcessor(workerName, maximumActiveTasks, webAssemblyOptions) {
this._workerName = workerName;
this._maximumActiveTasks = defaultValue(maximumActiveTasks, 5);
this._webAssemblyOptions = webAssemblyOptions;
this._activeTasks = 0;
this._deferreds = {};
this._nextID = 0;
this._workerReadyPromise = when.defer();

this._webAssemblyConfig = undefined;
}

var emptyTransferableObjectArray = [];
Expand Down Expand Up @@ -224,7 +300,8 @@ define([
++this._activeTasks;

var processor = this;
return when(canTransferArrayBuffer(), function(canTransferArrayBuffer) {
var readyPromise = processor._workerReadyPromise;
return readyPromise.then(function (canTransferArrayBuffer) {
if (!defined(transferableObjects)) {
transferableObjects = emptyTransferableObjectArray;
} else if (!canTransferArrayBuffer) {
Expand Down
6 changes: 5 additions & 1 deletion Source/Scene/DracoLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ define([
DracoLoader._decoderTaskProcessor = undefined;
DracoLoader._getDecoderTaskProcessor = function () {
if (!defined(DracoLoader._decoderTaskProcessor)) {
DracoLoader._decoderTaskProcessor = new TaskProcessor('decodeDraco', DracoLoader._maxDecodingConcurrency);
DracoLoader._decoderTaskProcessor = new TaskProcessor('decodeDraco', DracoLoader._maxDecodingConcurrency, {
modulePath : 'ThirdParty/draco_wasm_wrapper_gltf.js',
wasmBinaryFile : 'ThirdParty/draco_decoder_gltf.wasm',
fallbackModulePath : 'ThirdParty/draco_decoder_gltf.js'
});
}

return DracoLoader._decoderTaskProcessor;
Expand Down
Binary file added Source/ThirdParty/draco_decoder_gltf.wasm
Binary file not shown.
Loading