perseluspiton

too many assets?

Recommended Posts

Hi,

I have a code that loads > 30 MB of images. It works fine in the browser. When try it on mobile with Cordova,  the application simply quits during preload. When I reduce the number or size of images to be loaded, it works fine. Tried it on 2 cell phones and 1 tablet, all produce this problem, though the number of images that crash them differs. Sometimes I get a

WebGL out of memory error,

and a lot of 

[Violation] 'load' handler took 806ms

[Violation] 'requestAnimationFrame' handler took 245ms

messages.

 

Any ideas?

Share this post


Link to post
Share on other sites

most of them are already jpg or tinypng, and I'll need to add twice as many images as development progresses, so it wont't solve the problem.

continuosly removing unused images and loading new ones might work, but that would disturb the game's seamlesness.

 

Share this post


Link to post
Share on other sites

I've certainly noticed issues with a couple of my games causing browser tabs to crash on mobile devices, particularly my version of Asteroids where I was pre-caching lots of bitmaps with rotated asteroid images at various sizes and angles (blitting performs better than drawing operations with Canvas2D). I ended up detecting the environment and fairly drastically reducing the amount of generated content to make it stable:

  • Reduced number of rotated images at each size.
  • Reduced number of colours/shades for which I would generate rotated images.

I also do this for desktop browsers other than Chrome and Safari, where the generation phase appears to perform much more poorly.

Anyway, bottom line, I'm not aware of a hard limit. Like desktop OSes I suspect with mobile OSes it depends what else the device is running: how many applications, how much memory they're using, etc. It will also depend on the spec of the device: how much RAM it has, how much space to cache content, how much memory is available to GPU, and so on.

With that said, 30MB doesn't sound like that much. What device are you using?

If you want an indication of how much memory your page is really using, which can easily run into hundreds of MB if you're not careful, you could try running it in desktop Chrome and opening up the Chrome Task Manager, which is available via More Tools > Task Manager on the address bar menu, just above Developer Tools. You may find your memory usage is way in excess of 30MB, at which point it's time to start investigating why.

Share this post


Link to post
Share on other sites

Take into consideration that the source picture size doesn't really matter. It gets decoded and is stored uncompressed on the GPU. The GPU can also pad it to be power of 2 size if it wasn't, wasting even more space. There are some compressed formats, but I don't think Phaser uses that (plus I think it would need to be supplied in that format in the first place).
A few hundred KB png can easily be 16 MB on the GPU.

Share this post


Link to post
Share on other sites

Of course - I even commented on decompression of audio, and made a passing remark about images, on another thread. Doh! (In my case, since the images were generated, they were uncompressed from the off.)

Share this post


Link to post
Share on other sites

Memory usage never went above 20 MB. I've tried two Samsung phones, one with Android 5, the other with Android 7, upgraded from 5, and a  Samsung  Galaxy Tab S2, with Android 7. All three run into problems more or less at 30-40 MB.

I thought maybe it was some WebGl issue, so I started Phaser in Canvas mode, it could load only a few images more. Downsizing to the nearest power of 2 dimensions helps some, but not enough.

Finally I tried adding a 100 MB audio file, and it also crashed the app, so probably it's not some image specific issue, but device limitation. But 30 MB is ridiculously low, there must be some setting I've overlooked... 

 

Share this post


Link to post
Share on other sites

I may be wrong, but I don't think it's a garbage collection problem - in any case garbage collection, in the sense you're using it, only applies to JavaScript memory.

I obviously don't know much about your game, but you've mentioned WebGL, and 30MB of images in TINYPNG and JPG formats. How much compression you actually get with either of these formats very much depends on the images concerned: JPG is good for some images, less good for others, and likewise TINYPNG. However, it's probably not unreasonable to talk in terms of a 9:1 compression ratio versus the uncompressed 24-bit or 32-bit RGB or ARGB equivalent. Again, not sure if you're using transparency, but this would only apply to PNGs, which have the additional alpha channel, anyway.

A 9:1 ratio means that uncompressed your images are going to use 270MB, and let's say it's in 32-bit colour to make life a bit easier for ourselves, even if some of them are only 24-bit.

However, this isn't the format that OpenGL - which is what WebGL is based on - will use. I suspect your images are actually being stored in the GPU process as either GL_RGB32F (no transparency) or GL_RGBA32F (with transparency), or possibly a mix of the two. Whereas in 32-bit RGBA each component - red, green, blue, and alpha - takes 8-bits (one byte) for each pixel, in the GL_ formats each component is stored as a 32-bit float. I.e., storing RGB for a pixel will take 96-bits (3 floats, 12 bytes), and storing RGBA will take 128-bits (4 floats, 16 bytes). Depending on the format then, this means WebGL will likely use somewhere between 810MB and 1080MB. These figures are obviously only a back of an envelope estimate, but they do a decent job of showing why you're seeing a massive amount of memory in the GPU process, and thus why your game is crashing on mobile devices.

One caveat: that GPU process is shared between all tabs in Chrome, so it may be that some of that memory is being used by other web pages, not just your game.

On the JavaScript memory front, spiking up to 190MB and 360MB seems very high. I've never seen JS memory usage this high, so I wonder if this might also be a factor in the instability you're seeing when running on mobile. Is there a way you can get this under control? The fact that it's spiking at 360MB suggests you're holding references to a lot of objects that you're later letting go as the memory drops to 14MB - do you need to hold on to these objects?

Hope that's all useful.

 

Bart

Share this post


Link to post
Share on other sites

Thanks Bart, it is very useful. I wasn't aware that simply loading an image puts so much strain on the GPU.

The game is sort of a point'n click quest game with a lot of hand drawn background images, so they need to be loaded at some point. What I'm trying to do now is to remove the assets when changing scenes with game textures.remove(), and load the new ones. It seems to help the GPU memory problem, but is introducing an annoying lag between scenes.

Are you aware of a way to store all the compressed images in memory, like in binary or base64 format, and convert it to texture on demand, instead of loading it?

Finally, the spike on the JavaScript memory front : no clue, it's during the preload scene, I'm only using this.load.image, and this.load.spritesheet, no extra objects.

 

 

Share this post


Link to post
Share on other sites

I'm not familiar with the Phaser 3 framework itself but if I were approaching this problem a few thoughts spring to mind:

- Can you get away with just loading images for the current, next and previous scenes, even if there are multiple possible navigation tasks?

- If your images are hand-drawn, how much fidelity would you lose by reducing the resolution of each image and then scaling back up at render time?

- Instead of using straight images, can you load them as compressed textures? This is a PITA because you'll probably have to support multiple different formats, but you might cut your GPU memory usage by 80% or so by doing so: https://cesium.com/blog/2017/02/06/texture-compression/. Note that you can reduce the download size of compressed textures still further by gzipping them, but this is something your web server should be able to handle for you, and the browser will automatically decompress them.

- Can you load an image in Phaser 3 by supplying image data rather than a URL? If so then you can use XMLHttpRequest (or possibly a built-in AJAX API if Phaser has one) to load your images, store the response bodies, which will be ArrayBuffers. Then, when you want to load the image in phaser you'd make a clone of the ArrayBuffer from the response body using something like:

var imageDataToPassIntoPhaser = storedResponseDataContainingCompressedImage.slice(0);

//  Now create your image in phaser here, however that works.

(I suspect you'd run into problems if you just tried to use the ArrayBuffer directly more than once, just like you do when decoding audio with the Web Audio API - don't get me started on that because I'll descend into a multi-paragraph rant about what a terrible API it is.)

 

Bart

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.