mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.75k stars 35.38k forks source link

Web Worker compatibility #478

Closed jsermeno closed 13 years ago

jsermeno commented 13 years ago

I've been finding Web Workers to be indispensable when processing lots of data to render on screen. You can use them to keep performance smooth by creating geometries in the Web Workers and sending them over to the client window where they'll be turned into meshes and added to the scene.

I was thinking I'd go through the code and make any references to the window object be compatible with web workers. I'd also like to add a new build file called ThreeWorker.js that will include everything in common_files and also the files from extras that you need to work with geometries. I figure that most people working with Web Workers won't need scenes, renderers, cameras, or meshes, but let me know if I'm wrong.

mrdoob commented 13 years ago

The library already uses web workers when parsing loaded objects. Not sure if this is what you mean...

jsermeno commented 13 years ago

I was thinking more about using the library itself from within a Web Worker. So inside a Web Worker I could do something like

importScripts('/src/lib/Three.js')

But right now, you have to tweak the library a little bit since the main three.js file has some references to the window object.

mrdoob commented 13 years ago

What would be the changes?

jsermeno commented 13 years ago

I haven't tested this yet. I can do that later tonight, but something like

var THREE = THREE || {};

if ( ! self.Int32Array ) {
    self.Int32Array = Array;
    self.Float32Array = Array;
}

I think that should work everywhere. But just in case it doesn't.

if ( typeof window !== 'undefined' && ! window.Int32Array ) {
    window.Int32Array = Array;
    window.Float32Array = Array;
} else if ( typeof self !== 'undefined' ) {
    self.Int32Array = Array;
    self.Float32Array = Array;
}
mrdoob commented 13 years ago

Sounds scary :/

Wouldn't it make more sense if it was just the geometry generators that were Web Worker friendly?

jsermeno commented 13 years ago

I think it should be pretty safe. I was just being pre-cautious with that second statement before :). window and self should be exchangeable. However, Web Workers don't have a window reference.

By the geometry generators you're talking about those classes like BinaryLoader.js and JSONLoader.js? Those are great for when you have a predefined model you just want to load up, but there are cases where you may want to construct a geometry from a dataset that is not vertices and faces, or maybe you want to run a complex algorithm on it before generating the geometry. Also, imagine 3D data visualizations where I want to create a mesh based off of some dataset like the human genome, or demographics across a country...

And then!, imagine doing that while keeping it performing well enough for the user to interact with it :) In my case I'm creating an infinite procedurally generated world of blocks that I generate on the server in the form of one byte per block. Then on the client-side I process that data to generate the geometries, and do visualization checks.

I've seen at least one other person do something similar. http://blog.thejit.org/2010/12/10/animating-isosurfaces-with-webgl-and-workers/. This guy is actually using Three.js, but to generate the mesh he's actually calculating the vertices and normals manually in the Web Workers, sending them back to the client window, and rendering the mesh using plain ol' WebGL. Actually, I only see him using Three.js for the camera and matrix math.It'd be great to take advantage of Three.js in a case like this. And and we have to do is change a couple lines :)

var THREE = THREE || {};

if ( ! self.Int32Array ) {
    self.Int32Array = Array;
    self.Float32Array = Array;
}
jsermeno commented 13 years ago

Accidentally closed the issue for a moment. Btw, I saw an issue a while back about adding unit testing to Three.js. What ever happened to that?

snajjar commented 11 years ago

We've built an web application on top of THREE.js that use web workers to export a big part of the computations we need in child threads (to have the main page responsive).

We had to tweak THREE.js to remove all usages of window object, to be able to use it in the worker. Could this be done in the distribution version of three.js ?

Thoses are the part of three.js in which we identified the usage of the window object: 1) the requestAnimationFrame polyfill ( https://gist.github.com/paulirish/1579671 ) 2) the THREE.Clock object (using window.performance.now()) 3) the devicePixelRatio computation in THREE.CanvasRenderer

THREE.js is a powerful library and it's really useful to combine its power with the usability of web workers to performs 3D computations, and then pass the geometry back to main thread (using TransferableObjects).

If it's not possible to remove thoses use of the window object, could we consider distributing a worker-safe three.min.js version (like "three.ws.js" ?)

gero3 commented 11 years ago

I'll look into this. It shouldn't be too much of a hassle to get this working. Can you share what you have??

snajjar commented 11 years ago

I can, but it's and ugly "search and remove". Not sure you wanna see that.

gero3 commented 11 years ago

Okay That's what I needed to know.

mrdoob commented 11 years ago

Hmmm... As far as I can see it's only window.devicePixelRatio... no?

tartavull commented 9 years ago

+1

snajjar commented 8 years ago

Hello,

It seems that there is a new window reference (in the AudioContext), which prevent the direct usage of three.js in web workers.

THREE.AudioListener = function () {

    THREE.Object3D.call( this );

    this.type = 'AudioListener';

    this.context = new ( window.AudioContext || window.webkitAudioContext )();

};

How can we deal with this?

I understand that removing all reference to the window object can be quite limiting, so i may suggest a solution. A quite reliable way to check if we are in a web worker is to lookup for the global document, since the access to it is prevented (by design) from web workers.

var inWorker = false;
try {
    document;
} catch (e){
    inWorker = true;
}

Then we can use this to access window only if we're not in a worker:

THREE.AudioListener = function () {
    if( !inWorker ) {
        THREE.Object3D.call( this );
        this.type = 'AudioListener';
        this.context = new ( window.AudioContext || window.webkitAudioContext )();
    }
    else {
        throw "Can't use the THREE.AudioListener in a Worker context (yet)";
    }
};

Tested it locally and i was able to successfully import three.js in my worker.

@mrdoob @gero3 : do you think this would be an acceptable solution for dealing with workers?

btw: For this specific issue (web-audio-api in a Worker context), there's currently an open issue about it, but that wasn't really the point.

mrdoob commented 8 years ago

I think I would rather remove the window reference instead...