提交 05a07fbb 编写于 作者: K Kai Salmen

OBJLoader2Parallel: isolated build function and added code docs

WorkerExecutionSupport: buildWorker uses CodeBuilderInstructions to decide how to internally build the worker (standard or jsm based)
上级 f42bb736
...@@ -20,69 +20,83 @@ import { ...@@ -20,69 +20,83 @@ import {
} from "./obj2/worker/parallel/WorkerRunner.js"; } from "./obj2/worker/parallel/WorkerRunner.js";
/** /**
* Extends {OBJLoader2} with the capability to run the parser {OBJLoader2Parser} in web worker
* with help of {WorkerExecutionSupport}.
* *
* @param [LoadingManager] manager * @param [LoadingManager] manager
* @constructor * @constructor
*/ */
const OBJLoader2Parallel = function ( manager ) { const OBJLoader2Parallel = function ( manager ) {
OBJLoader2.call( this, manager ); OBJLoader2.call( this, manager );
this.useJsmWorker = false; this.preferJsmWorker = false;
this.callbackOnLoad = null; this.callbacks.onParseComplete = null;
this.executeParallel = true; this.executeParallel = true;
this.workerExecutionSupport = new WorkerExecutionSupport(); this.workerExecutionSupport = new WorkerExecutionSupport();
}; };
OBJLoader2.OBJLOADER2_PARALLEL_VERSION = '3.0.0-beta2';
console.info( 'Using OBJLoader2Parallel version: ' + OBJLoader2.OBJLOADER2PARALLEL_VERSION );
OBJLoader2Parallel.prototype = Object.create( OBJLoader2.prototype ); OBJLoader2Parallel.prototype = Object.create( OBJLoader2.prototype );
OBJLoader2Parallel.prototype.constructor = OBJLoader2Parallel; OBJLoader2Parallel.prototype.constructor = OBJLoader2Parallel;
OBJLoader2Parallel.prototype.setUseJsmWorker = function ( useJsmWorker ) { OBJLoader2.OBJLOADER2_PARALLEL_VERSION = '3.0.0-beta2';
this.useJsmWorker = useJsmWorker === true; console.info( 'Using OBJLoader2Parallel version: ' + OBJLoader2.OBJLOADER2_PARALLEL_VERSION );
OBJLoader2Parallel.prototype.setPreferJsmWorker = function ( preferJsmWorker ) {
this.preferJsmWorker = preferJsmWorker === true;
return this; return this;
}; };
OBJLoader2Parallel.prototype.setCallbackOnLoad = function ( callbackOnLoad ) { /**
if ( callbackOnLoad !== undefined && callbackOnLoad !== null ) { * If this call back is not set, then the completion message from worker will not be received.
this.callbackOnLoad = callbackOnLoad; *
* @param {function} onParseComplete
* @return {OBJLoader2Parallel}
*/
OBJLoader2Parallel.prototype.setCallbackOnParseComplete = function ( onParseComplete ) {
if ( onParseComplete !== undefined && onParseComplete !== null ) {
this.callbacks.onParseComplete = onParseComplete;
} }
else { else {
throw "No callbackOnLoad was provided! Aborting!" throw "No callbackOnLoad was provided! Aborting!";
} }
return this; return this;
}; };
/**
* Execution of parse in parallel via Worker is default, but normal {OBJLoader2} parsing can be enforced via false here.
*
* @param executeParallel
* @return {OBJLoader2Parallel}
*/
OBJLoader2Parallel.prototype.setExecuteParallel = function ( executeParallel ) { OBJLoader2Parallel.prototype.setExecuteParallel = function ( executeParallel ) {
this.executeParallel = executeParallel === true; this.executeParallel = executeParallel === true;
return this; return this;
}; };
/**
* Allow to get hold of {WorkerExecutionSupport} for configuratin purposes
*
* @return {WorkerExecutionSupport|WorkerExecutionSupport}
*/
OBJLoader2Parallel.prototype.getWorkerExecutionSupport = function () { OBJLoader2Parallel.prototype.getWorkerExecutionSupport = function () {
return this.workerExecutionSupport; return this.workerExecutionSupport;
}; };
OBJLoader2Parallel.prototype._configure = function () { /**
if ( this.callbackOnLoad === null ) { * Provides instructions on what is to be contained in the worker
"No callbackOnLoad was provided! Aborting!" *
} * @return {CodeBuilderInstructions}
*/
// check if worker is already available and if so, then fast-fail OBJLoader2Parallel.prototype.buildWorkerCode = function () {
if ( this.workerExecutionSupport.isWorkerLoaded( this.useJsmWorker ) ) return; let codeBuilderInstructions = new CodeBuilderInstructions( true, true, this.preferJsmWorker );
if ( codeBuilderInstructions.isSupportsJsmWorker() ) {
let codeBuilderInstructions = new CodeBuilderInstructions();
let jsmSuccess = false;
if ( this.useJsmWorker ) {
codeBuilderInstructions.setJsmWorkerFile( '../../src/loaders/worker/parallel/jsm/OBJLoader2Worker.js' ); codeBuilderInstructions.setJsmWorkerFile( '../../src/loaders/worker/parallel/jsm/OBJLoader2Worker.js' );
jsmSuccess = this.workerExecutionSupport.buildWorkerJsm( codeBuilderInstructions );
}
if ( ! jsmSuccess ) { }
if ( codeBuilderInstructions.isSupportsStandardWorker() ) {
let codeOBJLoader2Parser = CodeSerializer.serializeClass( 'OBJLoader2Parser', OBJLoader2Parser ); let codeOBJLoader2Parser = CodeSerializer.serializeClass( 'OBJLoader2Parser', OBJLoader2Parser );
let codeObjectManipulator = CodeSerializer.serializeObject( 'ObjectManipulator', ObjectManipulator ); let codeObjectManipulator = CodeSerializer.serializeObject( 'ObjectManipulator', ObjectManipulator );
...@@ -94,33 +108,54 @@ OBJLoader2Parallel.prototype._configure = function () { ...@@ -94,33 +108,54 @@ OBJLoader2Parallel.prototype._configure = function () {
codeBuilderInstructions.addCodeFragment( codeParserPayloadHandler ); codeBuilderInstructions.addCodeFragment( codeParserPayloadHandler );
codeBuilderInstructions.addCodeFragment( codeWorkerRunner ); codeBuilderInstructions.addCodeFragment( codeWorkerRunner );
// allows to include full libraries as importScripts
// codeBuilderInstructions.addLibraryImport( '../../node_modules/three/build/three.js' ); // codeBuilderInstructions.addLibraryImport( '../../node_modules/three/build/three.js' );
codeBuilderInstructions.addStartCode( 'new WorkerRunner( new DefaultWorkerPayloadHandler( new OBJLoader2Parser() ) );' ); codeBuilderInstructions.addStartCode( 'new WorkerRunner( new DefaultWorkerPayloadHandler( new OBJLoader2Parser() ) );' );
this.workerExecutionSupport.buildWorkerStandard( codeBuilderInstructions ); }
return codeBuilderInstructions;
};
/**
* @private
*/
OBJLoader2Parallel.prototype._configure = function () {
if ( this.callbacks.onParseComplete === null ) {
"No callbackOnLoad was provided! Aborting!"
} }
// check if worker is already available and if so, then fast-fail
if ( this.workerExecutionSupport.isWorkerLoaded( this.preferJsmWorker ) ) return;
this.workerExecutionSupport.buildWorker( this.buildWorkerCode() );
let scope = this; let scope = this;
let scopedOnAssetAvailable = function ( payload ) { let scopedOnAssetAvailable = function ( payload ) {
scope._onAssetAvailable( payload ); scope._onAssetAvailable( payload );
}; };
this.workerExecutionSupport.updateCallbacks( scopedOnAssetAvailable, this.callbackOnLoad ); this.workerExecutionSupport.updateCallbacks( scopedOnAssetAvailable, this.callbacks.onParseComplete );
}; };
/** /**
* Load is intercepted from OBJLoader2. * Load is intercepted from {OBJLoader2}. It replaces the regular onLoad callback as the final worker result will be
* @inheritDoc * returned later by its own callbackOnLoad.
*
* @param {string} url A string containing the path/URL of the file to be loaded.
* @param {function} onLoad A function to be called after loading is successfully completed. The function receives loaded Object3D as an argument.
* @param {function} [onFileLoadProgress] A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains total and Integer bytes.
* @param {function} [onError] A function to be called if an error occurs during loading. The function receives the error as an argument.
* @param {function} [onMeshAlter] Called after worker successfully delivered a single mesh
*/ */
OBJLoader2Parallel.prototype.load = function( content, onLoad, onFileLoadProgress, onError, onMeshAlter ) { OBJLoader2Parallel.prototype.load = function( content, onLoad, onFileLoadProgress, onError, onMeshAlter ) {
this.setCallbackOnLoad( onLoad ); this.setCallbackOnParseComplete( onLoad );
OBJLoader2.prototype.load.call( this, content, function () {}, onFileLoadProgress, onError, onMeshAlter ); OBJLoader2.prototype.load.call( this, content, function () {}, onFileLoadProgress, onError, onMeshAlter );
}; };
/** /**
* @inheritDoc * Parses OBJ data in parallel with web worker.
*
* @param {arraybuffer} content OBJ data as Uint8Array or String
*/ */
OBJLoader2Parallel.prototype.parse = function( content ) { OBJLoader2Parallel.prototype.parse = function( content ) {
if ( this.executeParallel ) { if ( this.executeParallel ) {
...@@ -150,7 +185,7 @@ OBJLoader2Parallel.prototype.parse = function( content ) { ...@@ -150,7 +185,7 @@ OBJLoader2Parallel.prototype.parse = function( content ) {
} else { } else {
this.callbackOnLoad( OBJLoader2.prototype.parse.call( this, content ) ); this.callbacks.onParseComplete( OBJLoader2.prototype.parse.call( this, content ) );
} }
}; };
......
...@@ -3,12 +3,22 @@ ...@@ -3,12 +3,22 @@
* Development repository: https://github.com/kaisalmen/WWOBJLoader * Development repository: https://github.com/kaisalmen/WWOBJLoader
*/ */
const CodeBuilderInstructions = function () { /**
* These instructions are used by {WorkerExecutionSupport} to build code for the web worker or to assign code
*
* @param {boolean} supportsStandardWorker
* @param {boolean} supportsJsmWorker
* @constructor
*/
const CodeBuilderInstructions = function ( supportsStandardWorker, supportsJsmWorker, preferJsmWorker ) {
this.supportsStandardWorker = supportsStandardWorker;
this.supportsJsmWorker = supportsJsmWorker;
this.preferJsmWorker = preferJsmWorker;
this.startCode = ''; this.startCode = '';
this.codeFragments = []; this.codeFragments = [];
this.importStatements = []; this.importStatements = [];
this.jsmWorkerFile; this.jsmWorkerFile;
this.jsmWorker = false;
this.defaultGeometryType = 0; this.defaultGeometryType = 0;
}; };
...@@ -16,16 +26,20 @@ CodeBuilderInstructions.prototype = { ...@@ -16,16 +26,20 @@ CodeBuilderInstructions.prototype = {
constructor: CodeBuilderInstructions, constructor: CodeBuilderInstructions,
setJsmWorkerFile: function ( jsmWorkerFile ) { isSupportsStandardWorker: function () {
this.jsmWorkerFile = jsmWorkerFile; return this.supportsStandardWorker;
},
isSupportsJsmWorker: function () {
return this.supportsJsmWorker;
}, },
setJsmWorker: function ( jsmWorker ) { isPreferJsmWorker: function () {
this.jsmWorker = jsmWorker; return this.preferJsmWorker;
}, },
isJsmWorker: function () { setJsmWorkerFile: function ( jsmWorkerFile ) {
return this.jsmWorker; this.jsmWorkerFile = jsmWorkerFile;
}, },
addStartCode: function ( startCode ) { addStartCode: function ( startCode ) {
...@@ -68,7 +82,7 @@ const WorkerExecutionSupport = function () { ...@@ -68,7 +82,7 @@ const WorkerExecutionSupport = function () {
this._reset(); this._reset();
}; };
WorkerExecutionSupport.WORKER_SUPPORT_VERSION = '3.0.0-beta2'; WorkerExecutionSupport.WORKER_SUPPORT_VERSION = '3.0.0-preview';
console.info( 'Using WorkerSupport version: ' + WorkerExecutionSupport.WORKER_SUPPORT_VERSION ); console.info( 'Using WorkerSupport version: ' + WorkerExecutionSupport.WORKER_SUPPORT_VERSION );
...@@ -95,7 +109,7 @@ WorkerExecutionSupport.prototype = { ...@@ -95,7 +109,7 @@ WorkerExecutionSupport.prototype = {
usesMeshDisassembler: false, usesMeshDisassembler: false,
defaultGeometryType: 0 defaultGeometryType: 0
}, },
terminateWorkerOnLoad: false, terminateWorkerOnLoad: true,
forceWorkerDataCopy: false, forceWorkerDataCopy: false,
started: false, started: false,
queuedMessage: null, queuedMessage: null,
...@@ -179,7 +193,33 @@ WorkerExecutionSupport.prototype = { ...@@ -179,7 +193,33 @@ WorkerExecutionSupport.prototype = {
} }
}, },
buildWorkerJsm: function ( codeBuilderInstructions ) { /**
* Builds the worker code according the provided Instructions.
* If jsm worker code shall be built, then function may fall back to standard if lag is set
*
* @param {CodeBuilderInstructions} codeBuilderInstructions
*/
buildWorker: function ( codeBuilderInstructions ) {
let jsmSuccess = false;
if ( codeBuilderInstructions.isSupportsJsmWorker() && codeBuilderInstructions.isPreferJsmWorker() ) {
jsmSuccess = this._buildWorkerJsm( codeBuilderInstructions );
}
if ( ! jsmSuccess && codeBuilderInstructions.isSupportsStandardWorker() ) {
this._buildWorkerStandard( codeBuilderInstructions );
}
},
/**
*
* @param {CodeBuilderInstructions} codeBuilderInstructions
* @return {boolean} Whether loading of jsm worker was successful
* @private
*/
_buildWorkerJsm: function ( codeBuilderInstructions ) {
let jsmSuccess = true; let jsmSuccess = true;
this._buildWorkerCheckPreconditions( true, 'buildWorkerJsm' ); this._buildWorkerCheckPreconditions( true, 'buildWorkerJsm' );
...@@ -187,9 +227,7 @@ WorkerExecutionSupport.prototype = { ...@@ -187,9 +227,7 @@ WorkerExecutionSupport.prototype = {
try { try {
let worker = new Worker( workerFileUrl, { type: "module" } ); let worker = new Worker( workerFileUrl, { type: "module" } );
codeBuilderInstructions.setJsmWorker( true ); this._configureWorkerCommunication( worker, true, codeBuilderInstructions.defaultGeometryType, 'buildWorkerJsm' );
this._configureWorkerCommunication( worker, codeBuilderInstructions, 'buildWorkerJsm' );
} }
catch ( e ) { catch ( e ) {
...@@ -201,7 +239,6 @@ WorkerExecutionSupport.prototype = { ...@@ -201,7 +239,6 @@ WorkerExecutionSupport.prototype = {
} }
} }
return jsmSuccess; return jsmSuccess;
}, },
...@@ -210,7 +247,13 @@ WorkerExecutionSupport.prototype = { ...@@ -210,7 +247,13 @@ WorkerExecutionSupport.prototype = {
* *
* @param {CodeBuilderIns} buildWorkerCode The function that is invoked to create the worker code of the parser. * @param {CodeBuilderIns} buildWorkerCode The function that is invoked to create the worker code of the parser.
*/ */
buildWorkerStandard: function ( codeBuilderInstructions ) {
/**
*
* @param {CodeBuilderInstructions} codeBuilderInstructions
* @private
*/
_buildWorkerStandard: function ( codeBuilderInstructions ) {
this._buildWorkerCheckPreconditions( false,'buildWorkerStandard' ); this._buildWorkerCheckPreconditions( false,'buildWorkerStandard' );
let concatenateCode = ''; let concatenateCode = '';
...@@ -226,9 +269,8 @@ WorkerExecutionSupport.prototype = { ...@@ -226,9 +269,8 @@ WorkerExecutionSupport.prototype = {
let blob = new Blob( [ concatenateCode ], { type: 'application/javascript' } ); let blob = new Blob( [ concatenateCode ], { type: 'application/javascript' } );
let worker = new Worker( window.URL.createObjectURL( blob ) ); let worker = new Worker( window.URL.createObjectURL( blob ) );
codeBuilderInstructions.setJsmWorker( false );
this._configureWorkerCommunication( worker, codeBuilderInstructions, 'buildWorkerStandard' ); this._configureWorkerCommunication( worker, false, codeBuilderInstructions.defaultGeometryType, 'buildWorkerStandard' );
}, },
_buildWorkerCheckPreconditions: function ( requireJsmWorker, timeLabel ) { _buildWorkerCheckPreconditions: function ( requireJsmWorker, timeLabel ) {
...@@ -242,18 +284,18 @@ WorkerExecutionSupport.prototype = { ...@@ -242,18 +284,18 @@ WorkerExecutionSupport.prototype = {
} }
}, },
_configureWorkerCommunication: function ( worker, codeBuilderInstructions, timeLabel ) { _configureWorkerCommunication: function ( worker, haveJsmWorker, defaultGeometryType, timeLabel ) {
this.worker.native = worker; this.worker.native = worker;
this.worker.jsmWorker = codeBuilderInstructions.isJsmWorker(); this.worker.jsmWorker = haveJsmWorker;
let scope = this; let scope = this;
let scopedReceiveWorkerMessage = function ( event ) { let scopedReceiveWorkerMessage = function ( event ) {
scope._receiveWorkerMessage( event ); scope._receiveWorkerMessage( event );
}; };
this.worker.native.onmessage = scopedReceiveWorkerMessage; this.worker.native.onmessage = scopedReceiveWorkerMessage;
if ( codeBuilderInstructions.defaultGeometryType !== undefined && codeBuilderInstructions.defaultGeometryType !== null ) { if ( defaultGeometryType !== undefined && defaultGeometryType !== null ) {
this.worker.workerRunner.defaultGeometryType = codeBuilderInstructions.defaultGeometryType; this.worker.workerRunner.defaultGeometryType = defaultGeometryType;
} }
if ( this.logging.enabled ) { if ( this.logging.enabled ) {
...@@ -263,6 +305,11 @@ WorkerExecutionSupport.prototype = { ...@@ -263,6 +305,11 @@ WorkerExecutionSupport.prototype = {
} }
}, },
/**
* Returns if Worker code is available and complies with expectation.
* @param {boolean} requireJsmWorker
* @return {boolean|*}
*/
isWorkerLoaded: function ( requireJsmWorker ) { isWorkerLoaded: function ( requireJsmWorker ) {
return this.worker.native !== null && return this.worker.native !== null &&
( ( requireJsmWorker && this.worker.jsmWorker ) || ( ! requireJsmWorker && ! this.worker.jsmWorker ) ); ( ( requireJsmWorker && this.worker.jsmWorker ) || ( ! requireJsmWorker && ! this.worker.jsmWorker ) );
......
...@@ -284,8 +284,8 @@ ...@@ -284,8 +284,8 @@
.setModelName( local.name ) .setModelName( local.name )
.setBaseObject3d( local ); .setBaseObject3d( local );
// Configure WorkerExecutionSupport to disregard worker after execution // Configure WorkerExecutionSupport to not disregard worker after execution
objLoader2Parallel.getWorkerExecutionSupport().setTerminateWorkerOnLoad( true ); objLoader2Parallel.getWorkerExecutionSupport().setTerminateWorkerOnLoad( false );
function callbackMeshAlter ( event ) { function callbackMeshAlter ( event ) {
let override = new LoadedMeshUserOverride( false, true ); let override = new LoadedMeshUserOverride( false, true );
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册