Excessive garbage collection pausing my app during scene changes on Android devices. What can I do?

Whenever I change scenes I get a long pause of 3-8 seconds, before the next scene begins to load.  This pause only happens on Android devices, some devices worse than others (Nook seems particularly bad but and older Galaxy tab is pretty bad, too).  In logcat I see something like this during the pause:

D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 1648K, 54% free 12593K/27143K, paused 13ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 844K, 53% free 12786K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 13ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 16ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 14ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 14ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 830K, 53% free 12786K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 837K, 53% free 12787K/27143K, paused 12ms D/dalvikvm( 9313): GC\_CONCURRENT freed 732K, 52% free 13229K/27143K, paused 1ms+2ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 1575K, 54% free 12711K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 1051K, 54% free 12710K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 1051K, 54% free 12710K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 1051K, 54% free 12710K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 1051K, 54% free 12710K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 533K, 54% free 12710K/27143K, paused 11ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1090K, 53% free 12818K/27143K, paused 1ms+2ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 968K, 53% free 12820K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 964K, 53% free 12819K/27143K, paused 12ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1111K, 53% free 12964K/27143K, paused 2ms+2ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 438K, 53% free 12819K/27143K, paused 11ms I/dalvikvm-heap( 9313): Grow heap (frag case) to 13.804MB for 1293616-byte allocation D/dalvikvm( 9313): GC\_FOR\_ALLOC freed \<1K, 49% free 14082K/27143K, paused 11ms D/dalvikvm( 9313): GC\_CONCURRENT freed 239K, 48% free 14158K/27143K, paused 2ms+2ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 2536K, 53% free 12896K/27143K, paused 12ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1K, 48% free 14158K/27143K, paused 1ms+1ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 2536K, 53% free 12896K/27143K, paused 12ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1K, 48% free 14158K/27143K, paused 1ms+1ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 2536K, 53% free 12896K/27143K, paused 11ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1K, 48% free 14158K/27143K, paused 2ms+1ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 2536K, 53% free 12896K/27143K, paused 13ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1K, 48% free 14158K/27143K, paused 1ms+1ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 2536K, 53% free 12896K/27143K, paused 12ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1K, 48% free 14158K/27143K, paused 1ms+1ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 2536K, 53% free 12896K/27143K, paused 11ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1K, 48% free 14158K/27143K, paused 1ms+1ms D/dalvikvm( 9313): GC\_CONCURRENT freed 2341K, 51% free 13513K/27143K, paused 1ms+2ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 1503K, 53% free 12766K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 751K, 53% free 12766K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 751K, 53% free 12766K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 751K, 53% free 12766K/27143K, paused 13ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 751K, 53% free 12766K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 751K, 53% free 12766K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 751K, 53% free 12766K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 751K, 53% free 12766K/27143K, paused 11ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 751K, 53% free 12766K/27143K, paused 13ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 751K, 53% free 12766K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 1015K, 53% free 12767K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 1184K, 54% free 12663K/27143K, paused 13ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 998K, 54% free 12662K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 998K, 54% free 12663K/27143K, paused 12ms D/dalvikvm( 9313): GC\_FOR\_ALLOC freed 998K, 54% free 12663K/27143K, paused 13ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1008K, 53% free 12832K/27143K, paused 1ms+2ms D/dalvikvm( 9313): GC\_CONCURRENT freed 1246K, 54% free 12737K/27143K, paused 1ms+3ms &nbsp;

Garbage collection is going crazy during scene changes.  I’m not using storyboard for scene management.  I handle the scene changes myself but don’t do any forced garbage collection during the change.  I tried setting largeHeap = true but that didn’t help.  

I’m using daily build 1191 but I’ve experienced this problem for a quite a while.  I test/develop cycle mainly on iOS devices so I’ve put off dealing with this Android only issue, but I’m at a point where I need to fix it since I can’t ship with such huge pauses.

I saw a couple threads with similar issues that seemed due to old Widget bugs, plugin issues that have  since been fixed, and rendering a lot of text objects (which I’m not doing much of and certainly not during the scene change).

Any ideas what could be going on, or experiences similar with solutions to try?  The next thing I’m going to try is rewrite my scene change code to explicitly collect garbage and pause for 50ms prior to loading the next scene and see what it does.  

EDIT:   forcing a garbage collection after discarding the old scene and prior to loading the new scene didn’t fix the issue. 

Stephen,

The root cause of your performance bottleneck here is that your scene is loading a lot of new images and generating a lot of text.  This requires a lot of large memory allocations in the Java heap which is causing havoc with the garbage collector which is busy freeing up space to load the next image/text object.

The only way around this is to optimize your app to load images just once (when you can and if you have the memory to do it) and re-use these images between scenes.  Corona’s image sheets are the most optimized solution for this because they can store multiple sprites and are very memory efficient.

Also realize that text generated by display.newText() is very expensive.  This is because it involves creating a raw uncompressed bitmap on the CPU side, having the operating system draw text to it, and then pushing that bitmap to the GPU as an OpenGL texture.  This same process occurs every time you change the string of a text object too.  If your app uses a lot of text which changes often, then you should consider using a “bitmap font”.  The bitmap font technique is commonly used in games to render text quickly and in a memory efficient manner.  A bitmap font is essentially an image sheet containing all of the letters, numbers, and symbols you need to build your text using sprites.  The idea being that you load that bitmap font image sheet once and use it to construct text one symbol at a time… minimizing your use of display.newText() where you can.  I’ve seen other Corona developers contribute Lua libraries that do exactly this on our Code Exchange and Forums.

Thanks for the info, Joshua.  After some experiments it does indeed look like nearly all of my Android  bottleneck issues are caused by display.newText objects.  I didn’t think I was using that much text in scenes but apparently even a few text objects can start to bog things down, at least on relatively underpowered devices.

The solution you propose is pretty “ugh” inducing, though.  Converting all my text objects to use bitmap fonts this late in the game is going to be a major rewrite.  I’m using text objects for button labels with fancy shadows and outlines that use even more text objects to render them, I’ve got scrolling lists of IAP items with product descriptions using many text objects… 

I am using X-pressive’s Text Candy for a few fancy text effects, though, so maybe there’s a way I can retrofit it to the existing newText objects.  

Is getting rid of newText objects really the only way out of this quagmire?  Will graphics 2.0 perhaps solve the problem?  Is there any other potential fix that doesn’t involve tossing out all my newText objects?  Is there some way to manually disable garbage collection during scene changes to bypass the issue?  

Thanks for any info. you can provide.

-Stephen

Blarg.  Looks like Text Candy’s cool vector font features use… display.newText to render them!  So that’s not an option.

Really feeling kind of stuck here.  I can’t release for Android with these long pauses between scenes.  Looks like I’m going to have to redesign all my displayed text to use bitmap fonts.  

Are there any plans on the Corona labs dev. horizon to redo text displays, or at least create a more “official” bit font text API that plays nice and is perhaps backwards compatible with existing display.newText features?

There are some optimizations we could make on our end when transferring text bitmaps from Java to the C/C++ side when submitting it as a texture to OpenGL.  It could theoretically cut down the time by half, but still not as fast as iOS.  We “do” plan on making a similar optimization for image loading on Android, which we hope will improve the speed of loading images… but it’s more for cutting down memory usage during load times since the heap size for Android apps is so ridiculously small.

Another thing to note is that bitmap fonts should not be used if the text comes from an external source such as the Internet or user input.  This is because the text might include foreign characters or symbols that might not be in your bitmap font’s image sheet.  So, this is the case where you have no choice but to use display.newText().

Good to know there’s room for improvement.  I can’t see a way to do without newText objects entirely.  

My current plan is to use Text Candy bitmap style fonts and just brute force go through all my scenes and replace any low hanging fruit newText objects with bit text font objects.  It turns out I was using quite a lot more text than I thought due to having previously hacked a way to get outlined text by drawing each text object 12 times with different offsets to create the outline!  I hope to gain big improvements there by simply drawing a single bit text object instead.

Yeah, generating a text object 12 times with the same string will still create 12 bitmaps.  That’s pretty expensive.

Internally, we’ve been talking about changing Corona to re-use text bitmaps if another text object’s string, font, font size, and style are the same.  However, we didn’t think this would be a common case… so we were not planning on doing this in the near future.  But this would definitely improve the performance for what you (or Text Candy) are doing.

Yeah, it turns out that my hack to get pretty outlined fonts using bunches of display.newText objects was a massive texture memory hog.  Just a few of these as button labels and I was eating through something like 10MB of texture memory in a scene! I’ve been using this technique for so long I never realized it was so wasteful.

Anyway, Text Candy is actually saving me right now.  I’ve converted a couple scenes to use only bitfonts/Text Candy objects and the memory use is way down and, even better, the transitions between those scenes no longer pause with excessive garbage collection on my test Android devices!

So, problem solved for me(other than having to still whittle through a dozen scenes to replace the text).  I will be very wary of using any newText objects from now on.  Thanks for the info to help me narrow down where the problem was.

I’ve gone through and changed most of my text objects to bitmap fonts using Text Candy.  There is one huge disadvantage though, in that Text Candy doesn’t support unicode text, i.e. non-English letters such as á ñ  Ç etc.  This appears to be due to a known limitation of lua’s string library.  The “work around” for Text Candy as described by the TC developer uses lower case letters as placeholders for non-supported characters.  That won’t work in my case since I need to be able to use all characters. (I should also mention the TC developer appears to no longer update or even provide support for the product that he is still selling, so buyer beware.)

So, once again, I’m kind of stuck.  I’d like to have localized, multi-language support for my game.  I can do that with regular text objects and vector fonts but then I get the long pauses between scene changes.  Or, I can use fast and fancy bitmap fonts, but then lose ability to support multiple languages. This looks like a job for… Corona Labs!   Seems like essential functionality for a game centric mobile platform to support fast bitmap fonts and unicode.

Perhaps we can convince the Text Candy developer to add unicode support to his library?

Because the real issue here is that the Text Candy library does not support UTF-8 encoded strings.  That is, it assumes that all characters are 1 byte ASCII characters.  With UTF-8 strings, 1 character can be 1 to 6 bytes long.  This also means that the string length that you get in Lua (or even in C/C++) really represents the number of bytes in the string and not the number of characters.  So, in code, you can detect if the next character in the string is a unicode character if the most significant bit (ie: the 8th bit) is set to 1, in which case the following bits indicate the number of bytes the character uses.  If it is not set to 1, then it is a 1 byte ASCII character.  If you ever get bored, then you can read how UTF-8 encoded strings work here… :slight_smile:

   http://en.wikipedia.org/wiki/UTF-8

Alternatively, I suppose you could convert your UTF-8 strings to something that is compatible with Text Candy.  Such as a function that would replace a unicode character with place holder characters as suggested by the Text Candy developer.

Hi Joshua, thanks for the reply.

I get the feeling the Text Candy developer is no longer supporting his product.  I’ve emailed him over this issue as well as a couple others, and never did get a response.   The TC website mentions it doesn’t support multibyte characters “yet”, implying that it’s intended to in the future, but it’s been a long time since the last TC update. There are other threads on here going back to last year where others are asking for this same feature with limited or no response from the developer.

I did do a bunch of research on UTF8  and Corona/lua over the past couple days while trying to roll my own fix.  There are even a few posts on here where people have posted various code snippets and functions that supposedly fix this issue. There are functions similar to string.len and string.sub that are utf8 capable but I couldn’t quite get them all to work with TC (they also killed my framerate when I got them to partially work).  I’m guessing the developer tried, failed as I did, gave up, then abandoned his product.  Definitely a bummer since TC is actually quite powerful in every other aspect.

I’m not sure what Corona Labs could do to help, but given how slow the utf8 aware string functions are as written in lua perhaps there are ways to speed them up by making them part of the API?  Not sure that would even be enough for TC to work correctly, so perhaps a plugin or more comprehensive support of bitmap fonts is needed.

We have a plugin that allows you to do bit masking and bit manipulation.  It should be a lot faster since it is implemented in C/C++.  Have a look at the documentation for it here…
   http://docs.coronalabs.com/daily/plugin/bit/index.html
   http://bitop.luajit.org/api.html
 
In C/C++, you can determine the number of bytes one character takes like this…

int sourceStringLength = (int)strlen(sourceString); for (int index = 0; index \< sourceStringLength; index++) { char sourceCharacter = sourceString[index]; if ((sourceCharacter & 0x80) && ((sourceString[index + 1] & 0xC0) == 0x80)) { // This is a multibyte unicode character. // Determine how many bytes it takes within the given string. int unicodeByteCount = 1; if ((sourceCharacter & 0xE0) == 0xC0) { // This is an 11-bit unicode character. unicodeByteCount = 2; } else if ((sourceCharacter & 0xF0) == 0xE0) { // This is a 16-bit unicode character. unicodeByteCount = 3; } else if ((sourceCharacter & 0xF8) == 0xF0) { // This is a 21-bit unicode character. unicodeByteCount = 4; } else if ((sourceCharacter & 0xFC) == 0xF8) { // This is a 26-bit unicode character. unicodeByteCount = 5; } else if ((sourceCharacter & 0xFE) == 0xFC) { // This is a 31-bit unicode character. unicodeByteCount = 6; } } else { // This is a 1 byte ASCII character. } } &nbsp;

 
Note that I slapped the above code in a hurry.  You’ll have to convert it to Lua, but hopefully it might help you out.

Thanks, Joshua.  That looks similar to this lua function I found on another thread here:

-- returns the number of bytes used by the UTF-8 character at byte i in s -- also doubles as a UTF-8 character validator function utf8charbytes (s, i) &nbsp; &nbsp; -- argument defaults &nbsp; &nbsp; i = i or 1 &nbsp; &nbsp; local c = string.byte(s, i) &nbsp; &nbsp; -- determine bytes needed for character, based on RFC 3629 &nbsp; &nbsp; if c \> 0 and c \<= 127 then &nbsp; &nbsp; &nbsp; &nbsp; -- UTF8-1 &nbsp; &nbsp; &nbsp; &nbsp; return 1 &nbsp; &nbsp; elseif c \>= 194 and c \<= 223 then &nbsp; &nbsp; &nbsp; &nbsp; -- UTF8-2 &nbsp; &nbsp; &nbsp; &nbsp; local c2 = string.byte(s, i + 1) &nbsp; &nbsp; &nbsp; &nbsp; return 2 &nbsp; &nbsp; elseif c \>= 224 and c \<= 239 then &nbsp; &nbsp; &nbsp; &nbsp; -- UTF8-3 &nbsp; &nbsp; &nbsp; &nbsp; local c2 = s:byte(i + 1) &nbsp; &nbsp; &nbsp; &nbsp; local c3 = s:byte(i + 2) &nbsp; &nbsp; &nbsp; &nbsp; return 3 &nbsp; &nbsp; elseif c \>= 240 and c \<= 244 then &nbsp; &nbsp; &nbsp; &nbsp; -- UTF8-4 &nbsp; &nbsp; &nbsp; &nbsp; local c2 = s:byte(i + 1) &nbsp; &nbsp; &nbsp; &nbsp; local c3 = s:byte(i + 2) &nbsp; &nbsp; &nbsp; &nbsp; local c4 = s:byte(i + 3) &nbsp; &nbsp; &nbsp; &nbsp; return 4 &nbsp; &nbsp; end end &nbsp;

And here are the utf8len and utf8sub functions, as well as a utf8 character replace function, also found in another thread here

-- returns the number of characters in a UTF-8 string function utf8len (s) &nbsp; &nbsp; local pos = 1 &nbsp; &nbsp; local bytes = string.len(s) &nbsp; &nbsp; local len = 0 &nbsp; &nbsp; while pos \<= bytes and len ~= chars do &nbsp; &nbsp; &nbsp; &nbsp; local c = string.byte(s,pos) &nbsp; &nbsp; &nbsp; &nbsp; len = len + 1 &nbsp; &nbsp; &nbsp; &nbsp; pos = pos + utf8charbytes(s, pos) &nbsp; &nbsp; end &nbsp; &nbsp; if chars ~= nil then &nbsp; &nbsp; &nbsp; &nbsp; return pos - 1 &nbsp; &nbsp; end &nbsp; &nbsp; return len end -- functions identically to string.sub except that i and j are UTF-8 characters -- instead of bytes function utf8sub (s, i, j) &nbsp; &nbsp; j = j or -1 &nbsp; &nbsp; if i == nil then &nbsp; &nbsp; &nbsp; &nbsp; return "" &nbsp; &nbsp; end &nbsp; &nbsp; local pos = 1 &nbsp; &nbsp; local bytes = string.len(s) &nbsp; &nbsp; local len = 0 &nbsp; &nbsp; -- only set l if i or j is negative &nbsp; &nbsp; local l = (i \>= 0 and j \>= 0) or utf8len(s) &nbsp; &nbsp; local startChar = (i \>= 0) and i or l + i + 1 &nbsp; &nbsp; local endChar = (j \>= 0) and j or l + j + 1 &nbsp; &nbsp; -- can't have start before end! &nbsp; &nbsp; if startChar \> endChar then &nbsp; &nbsp; &nbsp; &nbsp; return "" &nbsp; &nbsp; end &nbsp; &nbsp; -- byte offsets to pass to string.sub &nbsp; &nbsp; local startByte, endByte = 1, bytes &nbsp; &nbsp; while pos \<= bytes do &nbsp; &nbsp; &nbsp; &nbsp; len = len + 1 &nbsp; &nbsp; &nbsp; &nbsp; if len == startChar then &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; startByte = pos &nbsp; &nbsp; &nbsp; &nbsp; end &nbsp; &nbsp; &nbsp; &nbsp; pos = pos + utf8charbytes(s, pos) &nbsp; &nbsp; &nbsp; &nbsp; if len == endChar then &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; endByte = pos - 1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break &nbsp; &nbsp; &nbsp; &nbsp; end &nbsp; &nbsp; end &nbsp; &nbsp; return string.sub(s, startByte, endByte) end -- replace UTF-8 characters based on a mapping table function utf8replace (s, mapping) &nbsp; &nbsp; local pos = 1 &nbsp; &nbsp; local bytes = string.len(s) &nbsp; &nbsp; local charbytes &nbsp; &nbsp; local newstr = "" &nbsp; &nbsp; while pos \<= bytes do &nbsp; &nbsp; &nbsp; &nbsp; charbytes = utf8charbytes(s, pos) &nbsp; &nbsp; &nbsp; &nbsp; local c = string.sub(s, pos, pos + charbytes - 1) &nbsp; &nbsp; &nbsp; &nbsp; newstr = newstr .. (mapping[c] or c) &nbsp; &nbsp; &nbsp; &nbsp; pos = pos + charbytes &nbsp; &nbsp; end &nbsp; &nbsp; return newstr end &nbsp;

I tried replaced all the string.len and string.sub in the Text Candy Library with calls to these functions instead, and it actually seemed to work somewhat.  That is, it didn’t crash, but the non-ascii characters in my bitmap font were still not being displayed, and I don’t understand the workings of the TC library enough to know why they didn’t.  And as I mentioned, the functions killed my framerate, even on my Mac desktop.  I’ll try to wrap my head around the bitop plugin, but at first glance I’m not sure how it could be used to speed up these functions.

Stephen,

The root cause of your performance bottleneck here is that your scene is loading a lot of new images and generating a lot of text.  This requires a lot of large memory allocations in the Java heap which is causing havoc with the garbage collector which is busy freeing up space to load the next image/text object.

The only way around this is to optimize your app to load images just once (when you can and if you have the memory to do it) and re-use these images between scenes.  Corona’s image sheets are the most optimized solution for this because they can store multiple sprites and are very memory efficient.

Also realize that text generated by display.newText() is very expensive.  This is because it involves creating a raw uncompressed bitmap on the CPU side, having the operating system draw text to it, and then pushing that bitmap to the GPU as an OpenGL texture.  This same process occurs every time you change the string of a text object too.  If your app uses a lot of text which changes often, then you should consider using a “bitmap font”.  The bitmap font technique is commonly used in games to render text quickly and in a memory efficient manner.  A bitmap font is essentially an image sheet containing all of the letters, numbers, and symbols you need to build your text using sprites.  The idea being that you load that bitmap font image sheet once and use it to construct text one symbol at a time… minimizing your use of display.newText() where you can.  I’ve seen other Corona developers contribute Lua libraries that do exactly this on our Code Exchange and Forums.

Thanks for the info, Joshua.  After some experiments it does indeed look like nearly all of my Android  bottleneck issues are caused by display.newText objects.  I didn’t think I was using that much text in scenes but apparently even a few text objects can start to bog things down, at least on relatively underpowered devices.

The solution you propose is pretty “ugh” inducing, though.  Converting all my text objects to use bitmap fonts this late in the game is going to be a major rewrite.  I’m using text objects for button labels with fancy shadows and outlines that use even more text objects to render them, I’ve got scrolling lists of IAP items with product descriptions using many text objects… 

I am using X-pressive’s Text Candy for a few fancy text effects, though, so maybe there’s a way I can retrofit it to the existing newText objects.  

Is getting rid of newText objects really the only way out of this quagmire?  Will graphics 2.0 perhaps solve the problem?  Is there any other potential fix that doesn’t involve tossing out all my newText objects?  Is there some way to manually disable garbage collection during scene changes to bypass the issue?  

Thanks for any info. you can provide.

-Stephen

Blarg.  Looks like Text Candy’s cool vector font features use… display.newText to render them!  So that’s not an option.

Really feeling kind of stuck here.  I can’t release for Android with these long pauses between scenes.  Looks like I’m going to have to redesign all my displayed text to use bitmap fonts.  

Are there any plans on the Corona labs dev. horizon to redo text displays, or at least create a more “official” bit font text API that plays nice and is perhaps backwards compatible with existing display.newText features?

There are some optimizations we could make on our end when transferring text bitmaps from Java to the C/C++ side when submitting it as a texture to OpenGL.  It could theoretically cut down the time by half, but still not as fast as iOS.  We “do” plan on making a similar optimization for image loading on Android, which we hope will improve the speed of loading images… but it’s more for cutting down memory usage during load times since the heap size for Android apps is so ridiculously small.

Another thing to note is that bitmap fonts should not be used if the text comes from an external source such as the Internet or user input.  This is because the text might include foreign characters or symbols that might not be in your bitmap font’s image sheet.  So, this is the case where you have no choice but to use display.newText().

Good to know there’s room for improvement.  I can’t see a way to do without newText objects entirely.  

My current plan is to use Text Candy bitmap style fonts and just brute force go through all my scenes and replace any low hanging fruit newText objects with bit text font objects.  It turns out I was using quite a lot more text than I thought due to having previously hacked a way to get outlined text by drawing each text object 12 times with different offsets to create the outline!  I hope to gain big improvements there by simply drawing a single bit text object instead.

Yeah, generating a text object 12 times with the same string will still create 12 bitmaps.  That’s pretty expensive.

Internally, we’ve been talking about changing Corona to re-use text bitmaps if another text object’s string, font, font size, and style are the same.  However, we didn’t think this would be a common case… so we were not planning on doing this in the near future.  But this would definitely improve the performance for what you (or Text Candy) are doing.

Yeah, it turns out that my hack to get pretty outlined fonts using bunches of display.newText objects was a massive texture memory hog.  Just a few of these as button labels and I was eating through something like 10MB of texture memory in a scene! I’ve been using this technique for so long I never realized it was so wasteful.

Anyway, Text Candy is actually saving me right now.  I’ve converted a couple scenes to use only bitfonts/Text Candy objects and the memory use is way down and, even better, the transitions between those scenes no longer pause with excessive garbage collection on my test Android devices!

So, problem solved for me(other than having to still whittle through a dozen scenes to replace the text).  I will be very wary of using any newText objects from now on.  Thanks for the info to help me narrow down where the problem was.