Web workers for three

I am merging all object of a scene and using another modifier subsequently, this can ttake up to half a minute.
The idea is to move these operations (modify geometries, merge then tessellate) to a web worker, but I get errors as soon as i set up the imports. Is this the best way to proceed?
Note that i am using Angular

/// <reference lib="webworker" />

//import * as THREE from "three";
//import * as BGU from "three/examples/jsm/utils/BufferGeometryUtils.js";
//import { TessellateModifier } from "three/examples/jsm//modifiers/TessellateModifier.js";
//import toonShader from "~shaders/toon-shader";
//import { SceneStore } from "~stores/scene.store";

addEventListener("message", ({ data }) => {
  const response = `worker response to ${data}`;
  postMessage(response);
});

const wireScene = (scene: any, sceneStore: any): string => {
  return "done";
};

Any idea anyone?

See if you might adapt and use anything from this topic.

I can’t make out much from your brief code. A short live example would be appropriate.

I use a worker for the complex calculation of several geometries. The parameters are passed as an array of { } - objects.

For example, for a number of geometries, the parameters in the array param:

in the main program:

//Assignment for the individual geometries in loop(s) ........
param[ i ] = {
	triangleSide: triangleSide,
	radius: param[ 0 ].holes[ i1 ][ 2 ], //  from first Geo index 0  
	height: iHeight,
	radiusBottom: param[ 0 ].radius, //  radius from first Geo index 0 
	btmDiff: btmDiff[ i ],
	topDiff: topDiff[ i ]
}
//..........................................................

worker.postMessage( param );

worker.onmessage = function( e ) {

	 gc = e.data;  // calculated data

	for ( let i = 0; i <= count; i ++ ) { // create geometries
				
		geo[ i ] = new BufferGeometry( );
		
		geo[ i ].type           = gc[ i ].type;
		geo[ i ].ang            = gc[ i ].ang;
		 geo[ i ].div4           = gc[ i ].div4;
		// ... further data on geometry
		geo[ i ].setIndex( new BufferAttribute( gc[ i ].indices, 1 ) );
		geo[ i ].setAttribute( 'position', new BufferAttribute( gc[ i ].positions, 3 ) );

		//  further calculations for the geometries
		...
	}
		
}

in the worker

onmessage = function( e ) { 
                  
    const param = e.data;
    
    const gc = [];
    const buffers = [];
    
    for ( let j = 0; j < param.length; j ++ ) { // calculate geometry values
         
        gc[ j ] = Calculate( param[ j ] );
        
        buffers.push( gc[ j ].positions.buffer );
            
    }
    
    postMessage( gc, buffers );

}

function Calculate( p ) {

    indices = [];
    positions = [];
	
   // calculation in loop(s) ...
   indices[ idx ] = ...
   positions[ pIdx ] = ...
   //..........................
    gc.indices = new Uint32Array( indices );
    gc.positions = new Float32Array( positions );

    return gc;

}
3 Likes

Hi hofk, I have a live example on github but it’s a bit more extensive. But the part that sends data to the worker and receives from the worker is manageable

//your functions and classes

self.onmessage = (msg) => {

   const received datas = msg.data;

   //include your code
   const result = ...

   self.postMessage({data: result});

};

I think I’m doing something similar to what you’re trying to do. My worker is called:

ocean-builder-threaded-worker.js

in the ocean folder. ocean-builder-threaded.js then sends the geometry data created by the worker to ocean-chunk.js, where the geometries are then generated from the data. I use a slightly more extensive worker manager because I distribute the work to 7 workers. You probably won’t need it to that extent. The reason why your worker doesn’t want to do this is, as mentioned, the missing self. I don’t see anything in your worker that triggers it.

For a few connected geometries (my profile picture here shows 3) you don’t need a worker at all. However, if a large number of geometries are involved and the triangle size must therefore be very small, it is no longer possible without a worker.

Using several workers for many partial geometries certainly requires some additional effort. The problem is that the number of geometries can change constantly and within

worker.onmessage = function( e ) { ...

further complex calculations are performed and additional geometries are created. To do this, all geometries geo[ i ] must be retrievable in sequence in the data field geo.

wrong recipient :sweat_smile:
This was intended for Gaelle_Berton

No problem, it made me think :thinking: about it again. :slightly_smiling_face:

3 Likes

When transfering geometry attributes is is more efficient to take advantage of the fact ArrayBuffers() are ‘transferable’, where the data isn’t copied, just the ownership/visibility of the underlying memory changed.

1 Like

Hi aardgoose, I usually use SharedArrayBuffers to avoid copies, but I deactivated it in the ocean repo on github because otherwise it wouldn’t work with the guthub server.
That’s why I already use ArrayBuffers instead of SharedArrayBuffers. But I didn’t know that ArrayBuffers also avoid copies. I learned something again there.