Three.js: Non power-of-2 textures not supported in web workers

Created on 13 Feb 2019  路  8Comments  路  Source: mrdoob/three.js

Description of the problem

I'm using three.js in a web worker to load and render a scene in a separate thread.
I just noticed that non power-of-2 textures crash the rendering.

It is due to the fact that such textures are resized in WebGLTextures. The immediate error is that HTMLImageElement is undefined and below that there are also several references to document, which is not available in a web worker.

The following POC works in a web worker. It uses an OffscreenCanvas to resize the image and then returns the produced ImgeBitmap.

if ( isWebWorker && image instanceof ImageBitmap ) {

    var floor = needsPowerOfTwo ? _Math.floorPowerOfTwo : Math.floor;

    const width = floor( scale * image.width );
    const height = floor( scale * image.height );

    if ( _canvas === undefined ) _canvas = new OffscreenCanvas(width, height);

    var canvas = needsNewCanvas ? new OffscreenCanvas(width, height) : _canvas;

    var context = canvas.getContext( '2d' );
    context.drawImage( image, 0, 0, canvas.width, canvas.height );

    console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' );

    return canvas.transferToImageBitmap();
}
else {
   [previous code]
}

Is there interest for this fix? If so, I can create a pull request.
Have I missed something?

Thanks

Three.js version
  • [x ] r101
Browser
  • [x ] Chrome
OS
  • [x ] Windows
Suggestion

All 8 comments

I'm not sure we should clutter WebGLTextures with this extension in order to fix a very special use case. I would prefer when the applications ensures to not use NPOT-textures when using workers. Or it performs the resize itself. Another option should be the usage of a WebGL2 rendering context since you don't need POT for mipmaps generation anymore.

Hi @Mugen87

I don't think it would clutter WebGLTextures that much if it's done in a compact manner, like:

var canvas = typeof document !== 'undefined' ? document.createElement( 'canvas' ) : new OffscreenCanvas(header.width, header.height);

Which is made possible because the code is basically the same, except for the type of canvas used (OffscreenCanvas could actually be the default choice, if not for obvious backward compatibility reasons).

Beyond stylistic considerations, It might be best for three.js to stay consistent and provide the same level of functionalities whatever the context (main-thread/web worker), especially given the recent efforts to make Three compatible with web workers.

especially given the recent efforts to make Three compatible with web workers.

Not everything can be included into this scope. The whole resizing functionality is more of a
comfort feature and think expecting POT or using WebGL2 are valid solutions for worker usage.

Not everything can be included into this scope.

This issue can, however 馃檪 It's really not that different from all the recent changes that got rid of references to document so that web workers raise no errors. In that case, it's basically a two-lines fix.

For users switching to web workers, it would be way more convenient & consistent to just get the same results as if rendering from the main-thread. Right now, it crashes.

In that case, it's basically a two-lines fix.

I'm confused. You initially presented a much larger code section...

I may have been unclear. The code section of the first post is a quick and dirty test based on this code, to illustrate the issue.

The original Three code has the same structure as the code sample. So the two main changes would be (when document is not available):

  • replace the document.createElementNS() with a new OffscreenCanvas()
  • return the canvas' ImageBitmap

Okay, thanks for the clarification. In this case I think it's worth a PR since the code changes are manageable.

A bit off topic. We may use createImageBitmap() to resize image once all modern browsers start to support resizeWidth/Height options. createImageBitmap() should work in both main thread and workers.

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap

But right now, the resize options aren't practical yet. Chrome requires to turn on special flag to enable the resize options (unless it started to support as default, I haven't tried for a while) and FireFox createImageBitmap() doesn't take any options (I'm working on fixing).

Was this page helpful?
0 / 5 - 0 ratings