Jump to content

unexpected delay when loading texture atlases dynamiclly


html5gamedev
 Share

Recommended Posts

I am trying to load texture atlas dynamically. The load happens when the user clicks on canvas. At the same time an animation fires. The next time the user clicks on canvas, previously loaded animation runs.

Perhaps it is best to demonstrate the problem. I uploaded the files on a test server. You can see the delay here: http://forwardingsolutions.club/test1/px3.html. I hardcoded the first animation load, meaning that you will see a delay on second canvas click.

Run this code on a low end mobile device, and you will se a huge delay. I am getting a 4s delay on a motorola G and 0.5s on an iphone 5. What can I do solve this annoying issue? you can inspect the code on a website, but I will also paste it here:

 

$(function(){


    var current;
    var next;

    var canRunNext = false;
    var isAnimating = false;
    var firstRun = true;

    var container = document.getElementById("canvas-container");
    var app = new PIXI.Application(550,584,{transparent:true});
    var canvas = container.appendChild(app.view);

    setupCanvas();
    loadRotationsAndCurrent();

    var infiniteLoader = PIXI.loader;



    function setupCanvas(){
        $(canvas).attr("id","canvas");
        if (window.screen.width < 600){
            $(canvas).css("width","100%");
            $(canvas).css("height","100%");
            $("#canvas-container").css("width","100%");
        }else{
            $("#canvas-container").css("width","50%");
        }
    }

    function loadRotationsAndCurrent(){
        PIXI.loader
            .add(['jimages/1s.png.json'])  // load all rotations
            .load(onRotationsLoaded);
    }

    function loadNext(){
        if (firstRun){
            infiniteLoader.add('jimages/2s.png.json');
            infiniteLoader.load(function(loader, resources) {
                next = new PIXI.extras.AnimatedSprite(setupFrames("2_000"));
                next.visible=false;
                next.animationSpeed = 0.5;
                next.loop= false;
                next.x = app.renderer.width / 2;
                next.y = app.renderer.height / 2;
                next.anchor.set(0.5);

                next.onComplete = function() { 
                    console.log('onComplete'); 
                        isAnimating=false;
                };

                app.stage.addChild(next);

                //                app.renderer.bindTexture(next);



                canRunNext=true; 

                console.log("next loaded");
            }); 

        }else{
            infiniteLoader.add('jimages/2s.png.json');
            infiniteLoader.load(function(loader, resources) {
                next = new PIXI.extras.AnimatedSprite(setupFrames("2_000"));
                canRunNext=true; 
                console.log("next loaded");
            });  
        }
    }

    function setupFrames(name){
        var frames = [];

        for (var i = 0; i < 30; i++) {
            var val = i < 10 ? '0' + i : i;

            // magically works since the spritesheet was loaded with the pixi loader
            frames.push(PIXI.Texture.fromFrame(name + val + '.png'));
        }  
        return frames;
    }

    function onRotationsLoaded(){  // all rotations loaded 


        current = new PIXI.extras.AnimatedSprite(setupFrames("1_000"));


        current.x = app.renderer.width / 2;


        current.y = app.renderer.height / 2;

        current.anchor.set(0.5);


        current.loop=false;


        current.animationSpeed = 0.5;

        current.visible = true;
        isAnimating = false;

        current.onComplete = function() { 
            console.log('onComplete'); 
            isAnimating=false;
             };


        app.stage.addChild(current);
    }
    var run = true;

    $("#canvas").on("click touch touchstart",function(){
        if (firstRun && !isAnimating){
            loadNext();
            isAnimating=true;
            current.gotoAndPlay(0);
            console.log("first run");
            firstRun=false;

        }else{
            if (canRunNext && !isAnimating){
                isAnimating=true;
                next.visible=true;
                current.visible=false;

                next.gotoAndPlay(0);
                console.log("can run next");
            }else{
                console.log("cannot run next");
            }
        }

    });

});      

 

Link to comment
Share on other sites

Have you checked the profiler to see what takes so long? I did, and got this:

profile.png.1bd96d85d4a22345844ea7a9e62df7b3.png

Ok, so Texture.upload is taking a really long time. Next I then looked at your texture that was being uploaded to the GPU and it is HUGE, 3300x2920. That is 38.5MB of pixel data that needs to go to the GPU. It takes some time.

You can mitigate the loading time by uploading the textured before they are used, using renderer.plugins.prepare to upload your texture at a loading screen. Low-end devices just can't move that much pixel data immediately, it takes time to upload.

Link to comment
Share on other sites

Thanks for stopping by. I have a huge number of textures(and they won't be used at the same time). The idea was to upload one by one dynamically, since I cannot leave user staring at the loading screen for 10min :D

I used your suggestion and it works. Kinda. This is what I used in load next:

                app.renderer.plugins.prepare.upload(next, () => {
                    canRunNext = true;
                    $("body").css("background-color","green");
                });

The new problem is that when the animation finishes uploading to the gpu, there is a freeze. I change color of body to signify this event. You can see it here:

http://forwardingsolutions.club/updated/px3.html

Now this rectangle animation is jaggy itself, but you can definitively see it even on desktop machines. A small freeze when the body turns green.

How can I fix this?

Link to comment
Share on other sites

Always check the profiler, it will tell you why things freeze:

profile.png.b694f68bb7e30d51ee920f32f66cde3e.png

Some large texture is being uploaded, and causing that freeze. Probably a different texture than you preloaded. If you have a ton of huge textures to do these animations, then you probably want to do them in a different way than a spritesheet. Many mobile gpus can't even handle the texture sizes you are using.

Link to comment
Share on other sites

Thanks. You know, this is exactly what I want to achieve. Load new texture atlases and run them without any freeze or delay. As far as texture atlas size goes, I found this page https://webglstats.com/webgl2/parameter/MAX_TEXTURE_SIZE, which states that 4096 by 4096 is universally supported (not sure what they mean by VRAM though..).

 

What do you recommend that I do? there is gotta be some way to load textures atlases and run them..

Link to comment
Share on other sites

17 minutes ago, html5gamedev said:

Thanks. You know, this is exactly what I want to achieve. Load new texture atlases and run them without any freeze or delay. As far as texture atlas size goes, I found this page https://webglstats.com/webgl2/parameter/MAX_TEXTURE_SIZE, which states that 4096 by 4096 is universally supported (not sure what they mean by VRAM though..).

 

What do you recommend that I do? there is gotta be some way to load textures atlases and run them..

That tells you that 100% of measured devices have a GL version that supports 4096x4096. But say for example, it has 16MB of memory and can support the texture size you want. That means you can only have 1 uploaded at a time. When you want to use the second one, you have to replace the texture you have on the GPU with a new one, requiring a new upload.

The reality is that with atlases this large you just can't preupload them all to the GPU. You need to either reduce the size of your textures or not use texture atlases for the animation you want to do. The second you start using texture atlases this large (and multiple of them to boot) you need to look into other ways to do animation because sheets like this aren't what you want. Unless you are OK with the required upload time. There is no code that will make the upload time go away, only make you spend that time at different points (like a loading screen). But you can't upload 2 4096x4096 textures at one time, not on old mobile devices.

 

Link to comment
Share on other sites

Ooh, I see! So if you want to load multiple texture atlases this big, the bottle neck is actually RAM of mobile devices. Thanks, you are very informative.

regarding upload time of atlases... I am totally fine with 4-5 second upload time, as long as the loading and preparing happens when another animation is running (and the atlas can play immediately when requested), so the user won't even notice. What bugs me (this issue shows in the updated version http://forwardingsolutions.club/updated/px3.html) is that little freeze of the currently running animation when the next atlas is loaded into the gpu. Maybe it freezes for just about 100ms, but it is very noticeable. If you can point me in the right direction regarding this issue, I will me most thankful.:)

Link to comment
Share on other sites

Quote

the bottle neck is actually RAM of mobile devices

No, it is the VRAM of the GPU. The RAM (main memory) of the unit is irrelevant. The GPU has its own VRAM and that is where the texture is stored. The delay is uploading your texture from main RAM to the GPU VRAM (video RAM).

When uploading to the GPU, you can't render anything else (like an animation) because the GPU is busy receiving your new texture. This is why you normally do this on a loading screen. Do your animations without a spritesheet. Instead of having a box moving side to side and having each frame in a sheet, just have one image (the box) and move the position around over a timeline.

Link to comment
Share on other sites

Thank you so much. I've spent a week puzzling with this issue (and I would probably spent another one) just to find out that I can't code it the way I want with pixi (and possibly any other tool). The box example is just a placeholder to represent my issue. 

Are you aware of any tricks I can use with plain css and JS, that could mimic a smooth texture atlas animation, when  big images are in use? I've already used css's step() method (can't run it smooth), I've tried setting up timer and changed background-position of the image (also can't run it smoothly) and I've used plain canvas("canvas") (Thats the worse of all 3 methods). I think I am gonna try to recreate the issue with canvas("webGL") next. 

 

 

Link to comment
Share on other sites

I feel like you are missing what I am saying. The issue you are encountering is not a JS issue, not a pixi issue, not even a WebGL issue. It is a GPU issue. You cannot fit texture this large on the hardware itself. You will have to use a different method of animating.

When you use pixi above, it is using WebGL. You won't get different results doing it on your own. The textures still have to upload to the GPU and they still will take just as long.

Look into skeletal animations, tweening, and timeline animations.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...