[SOLVED] Crashes on mobile (HTML5) > how to optimize memory usage?

Hi all!

I recently played my game on mobile (HTML5) and looks like I have a pretty annoying problem:

Desktop (Defold) => no issue
HTML5 Desktop => no issue
HTML5 Mobile => frequent crashes :pleading_face:

I’m not a performance optimization expert but the way the game is crashing (always in the same conditions / after playing 3-4 battles, cf the “walkthrough” below) made it look like a “memory” issue.

I must say this is both depressing and scary because I never took care of that kind of stuff until now (I’m a game designer, not a developer… But I want the experience to be as fluid & bug/crash-free as possible). The game did not crash on mobile until now and the game run at 60FPS so I did not dig deeper and just kept adding features. But this time it looks like I have no other choice…

So I decided to take a look at the web profiler, for the very first time…!

—-------------

Performance walkthrough (maybe it’ll help understand the issue) :

1/ At launch, the main scene and all the characters are loaded (as they will be every time I’ll get back to the main scene).

At this point, Memory (and CPU) are already yellow/orange/red:


I don’t know if this baseline is acceptable or not (274k), but from now on it will only get bigger :)…

2/ Then I display the World Map (popup):


=> instant 335k when launched

But a few seconds later:

back to 295k

Note: even after closing the popup, it stays at this level :thinking:

3/ And finally I launch a battle.
And this is where there is something that could be investigated/improved but I’m not sure.
Some sort of “memory spike” (up to 700k!) when the scene is loaded:

The scene itself is not that crowded (once everything is on track), but at the moment the collection is created, many elements are spawned/calculated within a small delay (my army, the enemies, the tilemap parts etc.)

After X battles (depends on how lucky I am :’) ), the games crashes with various similar symptoms.

** Black screen*
** Chrome error message (can’t open the website)*
** Or the game restarts by itself*
But I feel that the cause is the same.

4/ Back to world Map
After the battle, the memory usage stays steady at ~340k

Like some stuff is still used in memory, but I don’t know what/why…

On device, impossible to launch a battle from a certain point -like 3-4 battles, like the updated baseline is too high and the accumulation created a “memory debt” or something.

—-------------

A few questions (because to be honest, I don’t really know where I should start…)

Based on your experience…

1/ Do you think this is a memory issue, or something else? If it’s not, how to identify the issue?

2/ What is the memory usage that I should never exceed to make it work on mobile? (if there are guidelines => in my case maybe the baseline level seems ok but the spikes may be a problem)

3/ What are the good/bad practices to optimize memory usage?
Ex: delay some element creation by 1-2 frames to avoid bottlenecks etc. Is this something that is commonly done? But there may be many other things, ex: reduce tilesource image as much as possible etc.

4/ Is there a way to “clean/purge” the memory (to some extent)? Because as said above, it’s like, even without the spike, there is an “accumulation” of memory usage (particularly when going from world map to battle and vice versa).
=> When a collection is unloaded via Monarch, the allocated memory is not supposed to be “freed”?

5/ What is the best method to efficiently test a game on mobile? For now, I created HTML5 bundles, put them online, and test them… Each iteration takes time in these conditions, but I was just checking that the game was playable on mobile.

6/ Any other advice? (ex: how to make the most of the profiler)

—-------------

Thank you!

If it memory issue, try to check your heap size

print(tonumber(html5.run("HEAP8.length"))/1024/1024)

Or “Module.HEAP8.length” not remember right name:)

2 Likes

Did you check the browser console for errors when crash occurs?

3 Likes

Thanks for your answers!
Note : the same issue occurs on Safari so this is not a Chrome-specific issue (but really mobile browser-specific).

it returns “256”

Is this the same value as in game.project?
image
Yesterday I turned it to 512 (just out of curiosity) but the crash issue remained :pensive: What is the recommended value (and the limit?) Why shouldn’t I set it to 1024 (or even 2048) for example?

I didn’t know that I could check the console on mobile, but it looks like I can have something with “chrome://script”

Here is a “formatted” version of the console log (which seems less readable than the raw version added after that), but I don’t know how to analyse/understand it…

LOGRunning...

LOG

LOGMade with Defold -=[ [https://www.defold.com](https://www.defold.com/) ]=-

ERRORAssertion failed: message_buffer, at: ../src/ddf/ddf.cpp,190,LoadMessage

ERRORexception thrown: RuntimeError: abort(Assertion failed: message_buffer, at: ../src/ddf/ddf.cpp,190,LoadMessage). Build with -s ASSERTIONS=1 for more info.,abort@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:11983 ___assert_fail@[https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:16244 wasm-stub@[wasm](https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:16244%0Dwasm-stub@%5Bwasm) code] <?>.wasm-function[2288]@[wasm code] <?>.wasm-function[543]@[wasm code] <?>.wasm-function[4371]@[wasm code] <?>.wasm-function[3882]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1106]@[wasm code] <?>.wasm-function[4713]@[wasm code] <?>.wasm-function[2240]@[wasm code] <?>.wasm-function[4886]@[wasm code] <?>.wasm-function[4880]@[wasm code] <?>.wasm-function[4889]@[wasm code] wasm-stub@[native code] browserIterationFunc@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:222799 runIter@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:106769 Browser_mainLoop_runner@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:105269

LOGINFO:CRASH: Successfully wrote Crashdump to file: /data/.Defold/_crash

ERRORERROR:CRASH: CALL STACK:

ERROR

ERROR

ERRORERROR:CRASH: ["abort@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:11983","___assert_fail@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:16244","wasm-stub@[wasm code]","<?>.wasm-function[2288]@[wasm code]","<?>.wasm-function[543]@[wasm code]","<?>.wasm-function[4371]@[wasm code]","<?>.wasm-function[3882]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1106]@[wasm code]","<?>.wasm-function[4713]@[wasm code]","<?>.wasm-function[2240]@[wasm code]","<?>.wasm-function[4886]@[wasm code]","<?>.wasm-function[4880]@[wasm code]","<?>.wasm-function[4889]@[wasm code]","wasm-stub@[native code]","browserIterationFunc@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:222799","runIter@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:106769","Browser_mainLoop_runner@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:105269"]

ERROR

ERRORERROR:CRASH:

ERROR

LOGException thrown, see JavaScript console

And here is a raw/unformatted version since the formatting seems to do weird things:
LOGRunning…

LOG

LOGMade with Defold -=[ https://www.defold.com ]=-

ERRORAssertion failed: message_buffer, at: …/src/ddf/ddf.cpp,190,LoadMessage

ERRORexception thrown: RuntimeError: abort(Assertion failed: message_buffer, at: …/src/ddf/ddf.cpp,190,LoadMessage). Build with -s ASSERTIONS=1 for more info.,abort@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:11983 ___assert_fail@[https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:16244 wasm-stub@wasm code] <?>.wasm-function[2288]@[wasm code] <?>.wasm-function[543]@[wasm code] <?>.wasm-function[4371]@[wasm code] <?>.wasm-function[3882]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1972]@[wasm code] <?>.wasm-function[1106]@[wasm code] <?>.wasm-function[4713]@[wasm code] <?>.wasm-function[2240]@[wasm code] <?>.wasm-function[4886]@[wasm code] <?>.wasm-function[4880]@[wasm code] <?>.wasm-function[4889]@[wasm code] wasm-stub@[native code] browserIterationFunc@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:222799 runIter@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:106769 Browser_mainLoop_runner@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:105269

LOGINFO:CRASH: Successfully wrote Crashdump to file: /data/.Defold/_crash

ERRORERROR:CRASH: CALL STACK:

ERROR

ERROR

ERRORERROR:CRASH: [“abort@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:11983","___assert_fail@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:16244","wasm-stub@[wasm code]”,"<?>.wasm-function[2288]@[wasm code]","<?>.wasm-function[543]@[wasm code]","<?>.wasm-function[4371]@[wasm code]","<?>.wasm-function[3882]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1972]@[wasm code]","<?>.wasm-function[1106]@[wasm code]","<?>.wasm-function[4713]@[wasm code]","<?>.wasm-function[2240]@[wasm code]","<?>.wasm-function[4886]@[wasm code]","<?>.wasm-function[4880]@[wasm code]","<?>.wasm-function[4889]@[wasm code]",“wasm-stub@[native code]”,“browserIterationFunc@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:222799",“runIter@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:106769”,"Browser_mainLoop_runner@https://website.com/perso/idle001/WIP_idle_tavern_20221118d/:1:105269”]

ERROR

ERRORERROR:CRASH:

ERROR

LOGException thrown, see JavaScript console






The game started to crash when I introduced the World Map feature, which contains some pretty big tilemap source images. Same for the feature right after that…

Maybe I could run some tests with smaller tilemap sources to see if the crashes remain… (but maybe there is absolutely no link between memory usage and tilemap sources? :thinking: )

It is reserving memory from the client.
E.g. to allocate all available memory from the user is not considered good manners, and may impact the performance, or might not even work.
There is no clear answer to your “what is a good value”. This up to each developer to figure out.

Following the file+line number of your error message:

        char* message_buffer = 0;
        dmMemory::AlignedMalloc((void**)&message_buffer, 16, message_buffer_size);
        assert(message_buffer);

As you can see from the code, it asserts because it couldn’t allocate memory.
So, you either need to increase the heap size, or use less resources.

2 Likes

Yes but it can be increased in runtime if game have not enought memory.
So mb it try to increased it, but mobile have not enought memory.

Do you have 256 in pc, after you play for some time?

1 Like

So look like you load a lot of assets in memory.
How many atlases you use? What size?

1 Like

fyi : you can use safari inspector for connected ios device . Something like this:

2 Likes

I’ll try to use less resources… I’m pretty sure there is a lot of room for improvement in my project.

I’d have 2 questions though (to help me optimize properly):
1/ Should tilemap source images have “powers of 2” dimensions like atlases? (1024, 2048 etc.) => it is a common rule for any texture?
2/ Are unused atlases / tilemaps taken into account (in memory usage) or not?
=> I ask this because I started my current project as a “reskinnable” prototype, where I could easily change the visual assets (characters, tilemaps etc.) while keeping the same gameplay.

I ended up focusing on 1 theme in particular, but I left the two others in the project… Each one has its own collections, atlases (like 5-6 from 4096x4096 + 2-3 tilesources images of similar dimensions…). They’re not directly used but are still in the project (sleeping).

But if it appears they are causing memory issues (while being unused) then I should be able to drastically improve the performances by removing these “ghost parts” of the project and optimizing all the remaining textures, tilemaps etc. I would have done it at some point, but in the short term it was more fun to add gameplay features… :sweat_smile:

In some way, it answers your question @d954mas but I’ll be able to provide a more precise and relevant answer once the project will be cleaned. At this moment, if the memory issue is not gone, then… I’ll have a problem :thinking:

Wow, good to know… thanks!
I’ll take a look at it after having optimized the resources I use.

2048x2048 rgba is 16mb of memory)
So 4096x4096 is 64mb.

All atlases used in collection loaded when collection loaded.

You can use dynamic loading in factory.

2 Likes

No. Resources used only If you have references in collection on it

2 Likes

Today I spent some time cleaning tilemap images (and reducing them as much as possible), removing unused stuff from the project etc.

edit: these little adjustments + increasing the heap size from 256 to 512 seem to prevent the crashes from occurring, but I’d like to get some room to maneuver and avoid future crashes. And learn a bit more about optimization in the process. :nerd_face:

Here is the build report so you can have an idea of what the resources look like:
https://rag20221120.tiiny.site/

Overall I don’t feel that I use too many atlases & tilemaps (perhaps I’m wrong?), but there are still 2 major attention points (leading to 2-3 questions):




Audio
Audio files seem to be a huuuuge part of my project resources.

Since I’m using the Openal extension, I can only use .wav files… which can be super large, particularly musics.

1/ Do you think that having such large files has a significant role in the memory usage issues?

2/ I was considering getting back to the Defold audio system, so I could use .ogg files. Do you think it would improve the situation?
(the only thing missing would be the pitch feature but I don’t use it, so except the implementation time… it may worth it)


Tilemaps
For some of my tilesources (not all for now), I made sure to have “powers of 2” images, but I noticed something:

Here is the “powers of 2” image:

When the “extrude borders” value is set to > 0 (note: default value = 2), then the tilesource dimensions seem to be slightly increased and rounded to the next “powers of 2”.

Ex in this case => 4096x4096 (instead of 2048x2048 for the original image).


After a few quick tests, it appears that I need to keep “extrude borders” higher than 0 to avoid texture glitches when the camera moves.

So I have 2 questions:

1/ In the end, which dimensions matter in terms of memory usage: the image (2048x2048) ou the tilesource (4096x4096)?

2/ If the tilesource has the last word, then I assume I just have to slightly reduce my image (ex: by removing 1 line/column), keep the “extrude borders” thing so the tilesource size stays at 2048x2048… right?


Thanks!

Wav files are generally uncompressed. This is why they are large on file size. But since they are not compressed, they do not need to uncompressed on runtime to memory.

Ogg files are compressed and must be uncompressed on runtime.

Generally speaking, the result is nearly the same on ram usage(file size is smaller of course). + ogg files needs to be uncompress on real time and this might cause extra cpu usage.

To make it easier to understand(these are not accurate, just to explain it):

1mb. wav file = 1mb ram
0.5mb ogg file = 1mb ram + cpu for uncompressing

Wav files are good for small file size stuff like sound fx. And Ogg files are fine for musics(this is my way of course).

It doesn’t matter if you are using tilesource or atlas. Dimensions has a impact on memory + gpu usage. If you are targeting older devices don’t go over 2048x2048. For mid-range devices it would be fine to use 4096x4096. For newer devices and for desktop you can go over 4096x4096(carefully).

1 Like

Thanks for the explanations! (Almost) everything is clearer now.

That’s what I thought I should do (even though I was not aware of the exact calculation). I assume it would have interesting side effects (like reducing the build size etc.)

Anyway, now I have the confirmation that I need to switch to the Defold audio system (if I want to be able to use ogg files).

I get the principle (and indeed I’d like to make it work on as many devices as possible), but just to be 101% sure: in the end, what matters is the “final” tilesource dimensions, right? (and not the dimensions of the image used in this tilesource)

Image (used in the tilesource) dimensions:
image

Tilesource dimensions (with “extrude borders” set to >0):
image

In this case, I’ll need to slightly reduce the image dimensions so the tilesource dimensions (incl. “extrude borders”) will be 2048x2048.

1 Like

Not sure if “collectgarbage()” can help:

1 Like

This is important not just because of the ram usage also because the hardware support. Most older(or recent low level devices with older Open GL ES support) mobile devices does not support atlas/tilesource bigger than 2048x2048. They crash with error.

I’m not 100% sure about that. Someone might have a better/correct answer. It might depend on how game engine handle stuff. Every sprite is a quad with material(image) on it at the end. And since every material must be drawn, so it might effect performance on large images.

2 Likes

That I wondered, too: do images used in an atlas/tilesource better be power of 2 as well? What about images in custom resources that are dynamically loaded?

1 Like

Correct. If you have a 2048x2048 tilesource and extrude borders the size of the final texture will grow to the next power of two dimension.

2 Likes

Ah, I totally misunderstood this one. I thought @Ragetto is asking about a single image in atlas… Language barriers :smiley: sorry :slight_smile:

1 Like

My memory usage issues are under control now :slight_smile:

The crashes are gone, I optimized the project (ex: much smaller tilesources) and learned new things in the process.

Thank you guys!

6 Likes