Three.js: Textures not loading/returning on latest Chrome [Version 50.0.2661.75]

Created on 19 Apr 2016  路  19Comments  路  Source: mrdoob/three.js

Description of the problem

Hello all, I've run into an issue where I have a series of promises concurrently calling TextureLoader.load, and occasionally the call simply doesn't appear to be triggering its success/failure callback, which leaves promises unresolved indefinitely.

I put together the most basic JSFiddle I could here:
http://jsfiddle.net/b7bbbLxz/14/

You'll need to pop open web inspector's console to see the logs, but in general you'll see 10 promises resolve with success, and the Promise.all subsequently resolve with success. This would be the expected, good behaviour.

However, sometimes one promise will hang without ever returning, and the whole call then hangs. You'll need to Run the fiddle manually to ensure the resources go through a full load. With 1-2 seconds between runs, the issue occurs every ~10-15 attempts. Running more quickly (i.e. right after the previous success message is shown), the issue occurs every ~3-4 attempts.

This all seems related to the latest release of Chrome as the code was working in previous versions, and other browsers do not seem to present the issue. In Firefox, I can hammer on the Run button non-stop and always get a successful resolve back.

The issue appears to be producible more reliably when throttling the network connection to 2G, but that is not required.

This may be related to https://github.com/mrdoob/three.js/issues/8666, but it's hard to tell. Not related.

I've attached an image of the console output that displays the issue. The arrow points at a subsequent Run after the previous Promise.all was hanging without getting to PROMISES SUCCESS.

screen shot 2016-04-18 at 6 41 26 pm 1

Three.js version
  • [x] Dev
  • [x] r75
  • [ ] ...

    Browser
  • [ ] All of them

  • [x] Chrome
  • [ ] Firefox
  • [ ] Internet Explorer

    OS
  • [x] All of them

  • [ ] Windows
  • [ ] Linux
  • [ ] Android
  • [ ] IOS
    Hardware Requirements (graphics card, VR Device, ...)

None

Most helpful comment

I just debugged what I think is this same issue and also reached the conclusion that garbage collection is removing the object. So far the following simple change to ImageLoader.load() seems to correct the problem we were seeing:

    scope.manager.itemStart( url );

    image.src = url;
    this.image = image; // retain a reference to the object to prevent garbage collection

    return image;

All 19 comments

Also reproducible in Version 52.0.2711.0 canary

Confirmed on Chrome 50.0.2661.75 for Windows, smells like a race condition of some sort as it intermittently works if I refresh the page a couple times.

I load 3 large texture images as part of my app, and in situations where I hit this bug onProgress gets fired once, then just sits there.

Hey all, a bit more information/testing.

I've created two additional, similar JSFiddles.
(1) http://jsfiddle.net/b7bbbLxz/28/
(2) http://jsfiddle.net/b7bbbLxz/25/

The first uses the same image.src approach of loading images as the Three.TextureLoader does. With this approach, we can see 10 images added/loaded to the document.body, but from time to time the web inspector console log will be missing 1 or more 'load' events. I believe this is the crux of the problem, in that the latest version of Chrome appears to be dropping load callbacks and thus undermining the implementation of TextureLoader.load.

The second, is a similar approach but directly with XMLHttpRequest. With this approach, I was unable to reproduce the issue.

IMO, the current Three implementation is valid, but at the mercy of a bug in Chrome. Resolution would need to come from a fix to Chrome, or a different image loading implementation.

Confirmed on Chromium 50.0.2661.75 (64-bit) in Arch Linux using the ImageLoader, directly. I think @oros has pinned it down correctly to Chrome dropping load callbacks, for some reason. In my project I load over 100 images and there is always a failure around the half way point. And from that point on, no other load callbacks are fired.

For reference, have opened a Chromium bug here:
https://bugs.chromium.org/p/chromium/issues/detail?id=604844

Thank you for filing http://crbug.com/604844 and sorry for the regression. I've assigned it to an engineer who I think is closest to that area of the code.

Note that I wasn't able to reproduce it on my work laptop; could you work on making the test more stressful so that it provokes the problem more often? Thanks for any help you can provide in this area.

Also, perhaps this should be duplicated this into #8666 ?

@kenrussell apparently it's unrelated: https://github.com/mrdoob/three.js/issues/8666#issuecomment-212251706

@kenrussell Good call on the stressful test. Bumping up the loop to cycle 100 times seems to be able to produce it in my environment ~50% of the time so long as I kick off the next run fairly rapidly (right after the previous run completes.)

The easiest way to monitor it that I found was to just switch to the "Errors" panel in the console and let it tell me how many log files were filtered (101 means all logged, anything smaller means something was missed.)

Also, really appreciate your team's quick response, investigation, and turnaround.

Looking at the chromium bug
https://bugs.chromium.org/p/chromium/issues/detail?id=604844

it seems to me the issue is that the image that the loader returns is being garbage collected including the callbacks.
I modified the fiddle above to keep a reference to the result from textureLoader.load(), and this seems to work better. I say better because it still fails strangely.
http://jsfiddle.net/hbriceno/b7bbbLxz/34/

When it fails, there is a window.references value that seems to be cleared out. I am starting to worry that there is some chrome javascript bug here.

My fix/workaround to THREE.ImageUtils.loadTexture() changes:

    loader.load( url, function ( image ) {
        texture.image = image;
        texture.needsUpdate = true;
        if ( onLoad ) onLoad( texture );
    }, undefined, function ( event ) {
        if ( onError ) onError( event );
    } );

to

    texture._loadingimage = loader.load( url, function ( image ) {
        texture._loadingimage = null;

        texture.image = image;
        texture.needsUpdate = true;
        if ( onLoad ) onLoad( texture );
    }, undefined, function ( event ) {
        texture._loadingimage = null;

        if ( onError ) onError( event );
    } );

I also added the same kind of patch to create_texture() in the JSON loader, since it loads textures the same way.
function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) {
...
loader.load( fullPath, function ( image ) {

changed to

            texture._loadingimage = loader.load( fullPath, function ( image ) {

                texture._loadingimage = null;

                if ( THREE.Math.isPowerOfTwo( image.width ) === false ||
                     THREE.Math.isPowerOfTwo( image.height ) === false ) {

                    var width = nearest_pow2( image.width );
                    var height = nearest_pow2( image.height );

                    var canvas = document.createElement( 'canvas' );
                    canvas.width = width;
                    canvas.height = height;

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

                    texture.image = canvas;

                } else {

                    texture.image = image;

                }

                texture.needsUpdate = true;

            }, undefined, function ( event ) {

                texture._loadingimage = null;
            } );

Note this is on an old revision.

I just debugged what I think is this same issue and also reached the conclusion that garbage collection is removing the object. So far the following simple change to ImageLoader.load() seems to correct the problem we were seeing:

    scope.manager.itemStart( url );

    image.src = url;
    this.image = image; // retain a reference to the object to prevent garbage collection

    return image;

I think an update for Chrome 50 is imminent...

Wow, if only I could get all the hair back that I lost this afternoon trying to figure out why my texture loading was all of a sudden borked... sometimes. @jimcottrell's fix looks like it worked for me.

Thanks, @jimcottrell
I can confirm this fixed the TextureLoader hang in the newest version of Chrome.

FYI, just updated to Chrome 50.0.2661.94 (stable release channel) and am not seeing the issue in my environment anymore.

Yay!

Just noticed too that this looks to be fixed o/ Chrome 50.0.2661.94

same problem in chrome latest 69.0.3497.100 version

@Shweta-SourceCodeLabs Have you reported the bug to Chrome?

Was this page helpful?
0 / 5 - 0 ratings