Skip to content

Commit

Permalink
OBJLoader2Parallel now utilitzes TaskManager to parse data in worker
Browse files Browse the repository at this point in the history
WIP: Configure existing TaskManager to OBJLoader2Parallel and prevent init race conditions
All context (legacy, module or main) use TaskManagerDefaultRouting.comRouting if no other routing function is provided
webgl_loader_taskmanager.html uses code from existing modules for export to legacy
  • Loading branch information
kaisalmen committed Jul 30, 2020
1 parent 467b39f commit 827c658
Show file tree
Hide file tree
Showing 21 changed files with 228 additions and 1,057 deletions.
18 changes: 4 additions & 14 deletions examples/jsm/loaders/OBJLoader2.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/**
* @author Kai Salmen / https://kaisalmen.de
* Development repository: https://github.com/kaisalmen/WWOBJLoader
*/

Expand Down Expand Up @@ -38,12 +39,11 @@ const OBJLoader2 = function ( manager ) {
scope._onAssetAvailable( payload );

};

this.parser.setCallbackOnAssetAvailable( defaultOnAssetAvailable );

};

OBJLoader2.OBJLOADER2_VERSION = '3.2.0';
OBJLoader2.OBJLOADER2_VERSION = '4.0.0-dev';
console.info( 'Using OBJLoader2 version: ' + OBJLoader2.OBJLOADER2_VERSION );


Expand Down Expand Up @@ -239,41 +239,36 @@ OBJLoader2.prototype = Object.assign( Object.create( Loader.prototype ), {
this.parser.setCallbackOnLoad( onLoad );

}

if ( onError === null || onError === undefined || ! ( onError instanceof Function ) ) {

onError = function ( event ) {

let errorMessage = event;

if ( event.currentTarget && event.currentTarget.statusText !== null ) {

errorMessage = 'Error occurred while downloading!\nurl: ' + event.currentTarget.responseURL + '\nstatus: ' + event.currentTarget.statusText;

}

scope.parser.callbacks.onError( errorMessage );

};

}

if ( ! url ) {

onError( 'An invalid url was provided. Unable to continue!' );

}

let urlFull = new URL( url, window.location.href ).href;
let filename = urlFull;
let urlParts = urlFull.split( '/' );
if ( urlParts.length > 2 ) {

filename = urlParts[ urlParts.length - 1 ];
this.path = urlParts.slice( 0, urlParts.length - 1 ).join( '/' ) + '/';
let urlPartsPath = urlParts.slice( 0, urlParts.length - 1 ).join( '/' ) + '/';
if ( urlPartsPath !== undefined && urlPartsPath !== null ) this.path = urlPartsPath;

}

if ( onFileLoadProgress === null || onFileLoadProgress === undefined || ! ( onFileLoadProgress instanceof Function ) ) {

let numericalValueRef = 0;
Expand All @@ -283,7 +278,6 @@ OBJLoader2.prototype = Object.assign( Object.create( Loader.prototype ), {
if ( ! event.lengthComputable ) return;

numericalValue = event.loaded / event.total;

if ( numericalValue > numericalValueRef ) {

numericalValueRef = numericalValue;
Expand All @@ -302,7 +296,6 @@ OBJLoader2.prototype = Object.assign( Object.create( Loader.prototype ), {
scope.parser.callbacks.onLoad( scope.parse( content ), "OBJLoader2#load: Parsing completed" );

};

let fileLoader = new FileLoader( this.manager );
fileLoader.setPath( this.path || this.resourcePath );
fileLoader.setResponseType( 'arraybuffer' );
Expand All @@ -324,7 +317,6 @@ OBJLoader2.prototype = Object.assign( Object.create( Loader.prototype ), {
throw 'Provided content is not a valid ArrayBuffer or String. Unable to continue parsing';

}

if ( this.parser.logging.enabled ) {

console.time( 'OBJLoader parse: ' + this.modelName );
Expand Down Expand Up @@ -352,13 +344,11 @@ OBJLoader2.prototype = Object.assign( Object.create( Loader.prototype ), {
this.parser.callbacks.onError( 'Provided content was neither of type String nor Uint8Array! Aborting...' );

}

if ( this.parser.logging.enabled ) {

console.timeEnd( 'OBJLoader parse: ' + this.modelName );

}

return this.baseObject3d;

},
Expand Down
168 changes: 87 additions & 81 deletions examples/jsm/loaders/OBJLoader2Parallel.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
/**
* @author Kai Salmen / https://kaisalmen.de
* Development repository: https://github.com/kaisalmen/WWOBJLoader
*/

// Imports only related to wrapper
import {
Object3D
} from "../../../build/three.module.js";
import {
CodeBuilderInstructions,
WorkerExecutionSupport
} from "./obj2/worker/main/WorkerExecutionSupport.js";
import { CodeSerializer } from "./obj2/utils/CodeSerializer.js";
import { Object3D } from "../../../build/three.module.js";
import { OBJLoader2 } from "./OBJLoader2.js";

// Imports only related to worker (when standard workers (modules aren't supported) are used)
import { OBJLoader2Parser } from "./obj2/OBJLoader2Parser.js";
import {
WorkerRunner,
DefaultWorkerPayloadHandler,
ObjectManipulator
} from "./obj2/worker/parallel/WorkerRunner.js";
import { TaskManager } from "../taskmanager/TaskManager.js";
import { OBJ2LoaderWorker } from "../taskmanager/worker/tmOBJLoader2.js";


/**
Expand All @@ -33,16 +24,18 @@ const OBJLoader2Parallel = function ( manager ) {

OBJLoader2.call( this, manager );
this.preferJsmWorker = false;
this.initPerformed = false;
this.jsmWorkerUrl = null;

this.executeParallel = true;
this.workerExecutionSupport = new WorkerExecutionSupport();

this.taskManager = new TaskManager();
this.taskName = 'tmOBJLoader2';
};

OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION = '3.2.0';
OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION = '4.0.0-dev';
console.info( 'Using OBJLoader2Parallel version: ' + OBJLoader2Parallel.OBJLOADER2_PARALLEL_VERSION );
OBJLoader2Parallel.DEFAULT_JSM_WORKER_PATH = './jsm/loaders/obj2/worker/parallel/OBJLoader2JsmWorker.js';
OBJLoader2Parallel.DEFAULT_JSM_WORKER_PATH = './jsm/taskmanager/worker/tmOBJLoader2.js';

OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototype ), {

Expand All @@ -61,6 +54,18 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp

},

/**
*
* @param [TaskManager] taskManager
* @return {OBJLoader2}
*/
setTaskManager: function ( taskManager ) {

this.taskManager = taskManager;
return this;

},

/**
* Set whether jsm modules in workers should be used. This requires browser support which is currently only experimental.
* @param {boolean} preferJsmWorker True or False
Expand All @@ -79,43 +84,45 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp
},

/**
* Allow to get hold of {@link WorkerExecutionSupport} for configuration purposes.
* @return {WorkerExecutionSupport}
* Request termination of worker once parser is finished.
*
* @param {boolean} terminateWorkerOnLoad True or false.
* @return {OBJLoader2Parallel}
*/
getWorkerExecutionSupport: function () {
setTerminateWorkerOnLoad: function ( terminateWorkerOnLoad ) {

return this.workerExecutionSupport;
this.terminateWorkerOnLoad = terminateWorkerOnLoad === true;
return this;

},

/**
* Provide instructions on what is to be contained in the worker.
* @return {CodeBuilderInstructions}
*/
buildWorkerCode: function () {
_buildWorkerCode: async function () {

let codeBuilderInstructions = new CodeBuilderInstructions( true, true, this.preferJsmWorker );
if ( codeBuilderInstructions.isSupportsJsmWorker() ) {
if ( ! this.taskManager.supportsTaskType( this.taskName ) ) {

codeBuilderInstructions.setJsmWorkerUrl( this.jsmWorkerUrl );
if ( this.preferJsmWorker ) {

}
if ( codeBuilderInstructions.isSupportsStandardWorker() ) {
this.taskManager.registerTaskTypeModule( this.taskName, OBJLoader2Parallel.DEFAULT_JSM_WORKER_PATH );

} else {

let obj2ParserDep = 'const OBJLoader2Parser = ' + OBJLoader2Parser.toString() + ';\n\n';
this.taskManager.registerTaskType( this.taskName, OBJ2LoaderWorker.init, OBJ2LoaderWorker.execute, null, false, [{ code: obj2ParserDep }] );

let objectManipulator = new ObjectManipulator();
let defaultWorkerPayloadHandler = new DefaultWorkerPayloadHandler( this.parser );
let workerRunner = new WorkerRunner( {} );
}
await this.taskManager.initTaskType( this.taskName, {} ).catch( e => console.error( e ) );

codeBuilderInstructions.addCodeFragment( 'const OBJLoader2Parser = ' + OBJLoader2Parser.toString() + ';\n\n' );
codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( ObjectManipulator, objectManipulator ) );
codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( DefaultWorkerPayloadHandler, defaultWorkerPayloadHandler ) );
codeBuilderInstructions.addCodeFragment( CodeSerializer.serializeClass( WorkerRunner, workerRunner ) );
}
else {

let startCode = 'new ' + workerRunner.constructor.name + '( new ' + defaultWorkerPayloadHandler.constructor.name + '( new ' + this.parser.constructor.name + '() ) );';
codeBuilderInstructions.addStartCode( startCode );
await new Promise( resolve => resolve( true ) );

}
return codeBuilderInstructions;
this.initPerformed = true;
return this.initPerformed;

},

Expand Down Expand Up @@ -156,56 +163,21 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp

if ( this.executeParallel ) {

if ( this.parser.callbacks.onLoad === this.parser._onLoad ) {

throw "No callback other than the default callback was provided! Aborting!";

}
// check if worker has been initialize before. If yes, skip init
if ( ! this.workerExecutionSupport.isWorkerLoaded( this.preferJsmWorker ) ) {

this.workerExecutionSupport.buildWorker( this.buildWorkerCode() );

let scope = this;
let scopedOnAssetAvailable = function ( payload ) {

scope._onAssetAvailable( payload );

};
function scopedOnLoad( message ) {

scope.parser.callbacks.onLoad( scope.baseObject3d, message );
if ( ! this.initPerformed ) {

}

this.workerExecutionSupport.updateCallbacks( scopedOnAssetAvailable, scopedOnLoad );
this._buildWorkerCode()
.then( x => {
if ( this.parser.logging.debug ) console.log( 'OBJLoader2Parallel init was performed: ' + x );
this._executeWorkerParse( content )
} );

}
else {

// Create default materials beforehand, but do not override previously set materials (e.g. during init)
this.materialHandler.createDefaultMaterials( false );

this.workerExecutionSupport.executeParallel(
{
params: {
modelName: this.modelName,
instanceNo: this.instanceNo,
useIndices: this.parser.useIndices,
disregardNormals: this.parser.disregardNormals,
materialPerSmoothingGroup: this.parser.materialPerSmoothingGroup,
useOAsMesh: this.parser.useOAsMesh,
materials: this.materialHandler.getMaterialsJSON()
},
data: {
input: content,
options: null
},
logging: {
enabled: this.parser.logging.enabled,
debug: this.parser.logging.debug
}
} );
this._executeWorkerParse( content );

}
let dummy = new Object3D();
dummy.name = 'OBJLoader2ParallelDummy';
return dummy;
Expand All @@ -218,6 +190,40 @@ OBJLoader2Parallel.prototype = Object.assign( Object.create( OBJLoader2.prototyp

},

_executeWorkerParse: function ( content ) {

// Create default materials beforehand, but do not override previously set materials (e.g. during init)
this.materialHandler.createDefaultMaterials( false );

let config = {
id: 42,
params: {
modelName: this.modelName,
instanceNo: this.instanceNo,
useIndices: this.parser.useIndices,
disregardNormals: this.parser.disregardNormals,
materialPerSmoothingGroup: this.parser.materialPerSmoothingGroup,
useOAsMesh: this.parser.useOAsMesh,
materials: this.materialHandler.getMaterialsJSON()
},
buffer: content,
logging: {
enabled: this.parser.logging.enabled,
debug: this.parser.logging.debug
}
};
this.taskManager.enqueueForExecution( this.taskName, config,data => this._onAssetAvailable( data ), { buffer: content.buffer } )
.then( data => {
this._onAssetAvailable( data );
this.parser.callbacks.onLoad( this.baseObject3d, 'finished' );
if ( this.terminateWorkerOnLoad ) this.taskManager.dispose();
} )
.catch( e => console.error( e ) )

}



} );

export { OBJLoader2Parallel };
Loading

0 comments on commit 827c658

Please sign in to comment.