123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- import Uri from "../ThirdParty/Uri.js";
- import buildModuleUrl from "./buildModuleUrl.js";
- import defaultValue from "./defaultValue.js";
- import defer from "./defer.js";
- import defined from "./defined.js";
- import destroyObject from "./destroyObject.js";
- import DeveloperError from "./DeveloperError.js";
- import Event from "./Event.js";
- import FeatureDetection from "./FeatureDetection.js";
- import isCrossOriginUrl from "./isCrossOriginUrl.js";
- import Resource from "./Resource.js";
- import RuntimeError from "./RuntimeError.js";
- function canTransferArrayBuffer() {
- if (!defined(TaskProcessor._canTransferArrayBuffer)) {
- const worker = new Worker(
- getWorkerUrl("Workers/transferTypedArrayTest.js")
- );
- worker.postMessage = defaultValue(
- worker.webkitPostMessage,
- worker.postMessage
- );
- const value = 99;
- const array = new Int8Array([value]);
- try {
- // postMessage might fail with a DataCloneError
- // if transferring array buffers is not supported.
- worker.postMessage(
- {
- array: array,
- },
- [array.buffer]
- );
- } catch (e) {
- TaskProcessor._canTransferArrayBuffer = false;
- return TaskProcessor._canTransferArrayBuffer;
- }
- const deferred = defer();
- worker.onmessage = function (event) {
- const array = event.data.array;
- // some versions of Firefox silently fail to transfer typed arrays.
- // https://bugzilla.mozilla.org/show_bug.cgi?id=841904
- // Check to make sure the value round-trips successfully.
- const result = defined(array) && array[0] === value;
- deferred.resolve(result);
- worker.terminate();
- TaskProcessor._canTransferArrayBuffer = result;
- };
- TaskProcessor._canTransferArrayBuffer = deferred.promise;
- }
- return TaskProcessor._canTransferArrayBuffer;
- }
- const taskCompletedEvent = new Event();
- function completeTask(processor, data) {
- --processor._activeTasks;
- const id = data.id;
- if (!defined(id)) {
- // This is not one of ours.
- return;
- }
- const deferreds = processor._deferreds;
- const deferred = deferreds[id];
- if (defined(data.error)) {
- let error = data.error;
- if (error.name === "RuntimeError") {
- error = new RuntimeError(data.error.message);
- error.stack = data.error.stack;
- } else if (error.name === "DeveloperError") {
- error = new DeveloperError(data.error.message);
- error.stack = data.error.stack;
- }
- taskCompletedEvent.raiseEvent(error);
- deferred.reject(error);
- } else {
- taskCompletedEvent.raiseEvent();
- deferred.resolve(data.result);
- }
- delete deferreds[id];
- }
- function getWorkerUrl(moduleID) {
- let url = buildModuleUrl(moduleID);
- if (isCrossOriginUrl(url)) {
- //to load cross-origin, create a shim worker from a blob URL
- const script = `importScripts("${url}");`;
- let blob;
- try {
- blob = new Blob([script], {
- type: "application/javascript",
- });
- } catch (e) {
- const BlobBuilder =
- window.BlobBuilder ||
- window.WebKitBlobBuilder ||
- window.MozBlobBuilder ||
- window.MSBlobBuilder;
- const blobBuilder = new BlobBuilder();
- blobBuilder.append(script);
- blob = blobBuilder.getBlob("application/javascript");
- }
- const URL = window.URL || window.webkitURL;
- url = URL.createObjectURL(blob);
- }
- return url;
- }
- let bootstrapperUrlResult;
- function getBootstrapperUrl() {
- if (!defined(bootstrapperUrlResult)) {
- bootstrapperUrlResult = getWorkerUrl("Workers/cesiumWorkerBootstrapper.js");
- }
- return bootstrapperUrlResult;
- }
- function createWorker(processor) {
- const worker = new Worker(getBootstrapperUrl());
- worker.postMessage = defaultValue(
- worker.webkitPostMessage,
- worker.postMessage
- );
- const bootstrapMessage = {
- loaderConfig: {
- paths: {
- Workers: buildModuleUrl("Workers"),
- },
- baseUrl: buildModuleUrl.getCesiumBaseUrl().url,
- },
- workerModule: processor._workerPath,
- };
- worker.postMessage(bootstrapMessage);
- worker.onmessage = function (event) {
- completeTask(processor, event.data);
- };
- return worker;
- }
- function getWebAssemblyLoaderConfig(processor, wasmOptions) {
- const config = {
- modulePath: undefined,
- wasmBinaryFile: undefined,
- wasmBinary: undefined,
- };
- // Web assembly not supported, use fallback js module if provided
- if (!FeatureDetection.supportsWebAssembly()) {
- if (!defined(wasmOptions.fallbackModulePath)) {
- throw new RuntimeError(
- `This browser does not support Web Assembly, and no backup module was provided for ${processor._workerPath}`
- );
- }
- config.modulePath = buildModuleUrl(wasmOptions.fallbackModulePath);
- return Promise.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;
- });
- }
- /**
- * A wrapper around a web worker that allows scheduling tasks for a given worker,
- * returning results asynchronously via a promise.
- *
- * The Worker is not constructed until a task is scheduled.
- *
- * @alias TaskProcessor
- * @constructor
- *
- * @param {String} workerPath The Url to the worker. This can either be an absolute path or relative to the Cesium Workers folder.
- * @param {Number} [maximumActiveTasks=Number.POSITIVE_INFINITY] The maximum number of active tasks. Once exceeded,
- * scheduleTask will not queue any more tasks, allowing
- * work to be rescheduled in future frames.
- */
- function TaskProcessor(workerPath, maximumActiveTasks) {
- const uri = new Uri(workerPath);
- this._workerPath =
- uri.scheme().length !== 0 && uri.fragment().length === 0
- ? workerPath
- : TaskProcessor._workerModulePrefix + workerPath;
- this._maximumActiveTasks = defaultValue(
- maximumActiveTasks,
- Number.POSITIVE_INFINITY
- );
- this._activeTasks = 0;
- this._deferreds = {};
- this._nextID = 0;
- }
- const emptyTransferableObjectArray = [];
- /**
- * Schedule a task to be processed by the web worker asynchronously. If there are currently more
- * tasks active than the maximum set by the constructor, will immediately return undefined.
- * Otherwise, returns a promise that will resolve to the result posted back by the worker when
- * finished.
- *
- * @param {Object} parameters Any input data that will be posted to the worker.
- * @param {Object[]} [transferableObjects] An array of objects contained in parameters that should be
- * transferred to the worker instead of copied.
- * @returns {Promise.<Object>|undefined} Either a promise that will resolve to the result when available, or undefined
- * if there are too many active tasks,
- *
- * @example
- * const taskProcessor = new Cesium.TaskProcessor('myWorkerPath');
- * const promise = taskProcessor.scheduleTask({
- * someParameter : true,
- * another : 'hello'
- * });
- * if (!Cesium.defined(promise)) {
- * // too many active tasks - try again later
- * } else {
- * promise.then(function(result) {
- * // use the result of the task
- * });
- * }
- */
- TaskProcessor.prototype.scheduleTask = function (
- parameters,
- transferableObjects
- ) {
- if (!defined(this._worker)) {
- this._worker = createWorker(this);
- }
- if (this._activeTasks >= this._maximumActiveTasks) {
- return undefined;
- }
- ++this._activeTasks;
- const processor = this;
- return Promise.resolve(canTransferArrayBuffer()).then(function (
- canTransferArrayBuffer
- ) {
- if (!defined(transferableObjects)) {
- transferableObjects = emptyTransferableObjectArray;
- } else if (!canTransferArrayBuffer) {
- transferableObjects.length = 0;
- }
- const id = processor._nextID++;
- const deferred = defer();
- processor._deferreds[id] = deferred;
- processor._worker.postMessage(
- {
- id: id,
- parameters: parameters,
- canTransferArrayBuffer: canTransferArrayBuffer,
- },
- transferableObjects
- );
- return deferred.promise;
- });
- };
- /**
- * Posts a message to a web worker with configuration to initialize loading
- * and compiling a web assembly module asychronously, as well as an optional
- * fallback JavaScript module to use if Web Assembly is not supported.
- *
- * @param {Object} [webAssemblyOptions] 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.
- * @returns {Promise.<Object>} A promise that resolves to the result when the web worker has loaded and compiled the web assembly module and is ready to process tasks.
- */
- TaskProcessor.prototype.initWebAssemblyModule = function (webAssemblyOptions) {
- if (!defined(this._worker)) {
- this._worker = createWorker(this);
- }
- const deferred = defer();
- const processor = this;
- const worker = this._worker;
- getWebAssemblyLoaderConfig(this, webAssemblyOptions).then(function (
- wasmConfig
- ) {
- return Promise.resolve(canTransferArrayBuffer()).then(function (
- canTransferArrayBuffer
- ) {
- let transferableObjects;
- const binary = wasmConfig.wasmBinary;
- if (defined(binary) && canTransferArrayBuffer) {
- transferableObjects = [binary];
- }
- worker.onmessage = function (event) {
- worker.onmessage = function (event) {
- completeTask(processor, event.data);
- };
- deferred.resolve(event.data);
- };
- worker.postMessage(
- { webAssemblyConfig: wasmConfig },
- transferableObjects
- );
- });
- });
- return deferred.promise;
- };
- /**
- * Returns true if this object was destroyed; otherwise, false.
- * <br /><br />
- * If this object was destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
- *
- * @returns {Boolean} True if this object was destroyed; otherwise, false.
- *
- * @see TaskProcessor#destroy
- */
- TaskProcessor.prototype.isDestroyed = function () {
- return false;
- };
- /**
- * Destroys this object. This will immediately terminate the Worker.
- * <br /><br />
- * Once an object is destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
- */
- TaskProcessor.prototype.destroy = function () {
- if (defined(this._worker)) {
- this._worker.terminate();
- }
- return destroyObject(this);
- };
- /**
- * An event that's raised when a task is completed successfully. Event handlers are passed
- * the error object is a task fails.
- *
- * @type {Event}
- *
- * @private
- */
- TaskProcessor.taskCompletedEvent = taskCompletedEvent;
- // exposed for testing purposes
- TaskProcessor._defaultWorkerModulePrefix = "Workers/";
- TaskProcessor._workerModulePrefix = TaskProcessor._defaultWorkerModulePrefix;
- TaskProcessor._canTransferArrayBuffer = undefined;
- export default TaskProcessor;
|