gouessej
JGO Kernel      Posts: 3560 Medals: 30
TUER
|
 |
«
on:
2010-02-09 10:44:21 » |
|
Hi!
I display a lot of text and I have a problem of memory leak. How can I increase the size of the cache used by TextRenderer?
|
Julien Gouesse
|
|
|
JL235
JGO Ninja    Posts: 660 Medals: 21
|
 |
«
Reply #1 on:
2010-02-13 07:29:36 » |
|
I've notived memory leaks with the TextRenderer too. The trick as I've noticed it is to only make only one for each font your using, and never make any more.
I personally use my own graphics layer but inside there is a Map<Font, TextRenderer> inside. So when I set the Font to use when drawing text it gets the TextRenderer I used last time for the same Font.
|
|
|
|
emzic
|
 |
«
Reply #2 on:
2010-02-13 12:23:40 » |
|
i have been annoyed by textrenderer myself, so i have written a loader and renderer for angelcode's bmfont http://www.angelcode.com/products/bmfont/imo this little tool produces very nice text that can compete with textrenderer!
|
|
|
|
Games published by our own members! Go get 'em!
|
|
gouessej
JGO Kernel      Posts: 3560 Medals: 30
TUER
|
 |
«
Reply #3 on:
2010-02-13 13:16:00 » |
|
I've notived memory leaks with the TextRenderer too. The trick as I've noticed it is to only make only one for each font your using, and never make any more.
I personally use my own graphics layer but inside there is a Map<Font, TextRenderer> inside. So when I set the Font to use when drawing text it gets the TextRenderer I used last time for the same Font.
I reproduce the memory leak even with a single text renderer  The demo "TextFlow" has the same problem but in my case, the memory leak is hugely bigger  i have been annoyed by textrenderer myself, so i have written a loader and renderer for angelcode's bmfont http://www.angelcode.com/products/bmfont/imo this little tool produces very nice text that can compete with textrenderer! Thank you, I will have a look at it too 
|
Julien Gouesse
|
|
|
Nate
JGO Neuromancer     Posts: 1063 Medals: 30
mooooo
|
 |
«
Reply #4 on:
2010-02-13 22:25:57 » |
|
|
|
|
|
gouessej
JGO Kernel      Posts: 3560 Medals: 30
TUER
|
 |
«
Reply #5 on:
2010-02-16 11:19:22 » |
|
The memory leak is there in the class "TextRenderer": 1 2 3 4 5
| public List getGlyphs(CharSequence inString) { glyphsOutput.clear(); iter.initFromCharSequence(inString); GlyphVector fullRunGlyphVector = font.createGlyphVector(getFontRenderContext(), iter); |
We should not create a glyph vector at each call. Who wrote this source code?
|
Julien Gouesse
|
|
|
Riven
« League of Dukes » JGO Kernel      Posts: 5870 Medals: 255
Hand over your head.
|
 |
«
Reply #6 on:
2010-02-16 12:44:34 » |
|
The memory leak is there in the class "TextRenderer": 1 2 3 4 5
| public List getGlyphs(CharSequence inString) { glyphsOutput.clear(); iter.initFromCharSequence(inString); GlyphVector fullRunGlyphVector = font.createGlyphVector(getFontRenderContext(), iter); |
We should not create a glyph vector at each call. Who wrote this source code? http://www.java-gaming.org/topics/the-last-word-in-text-rendering/15634/view.html
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings
|
|
|
princec
« League of Dukes » JGO Kernel      Posts: 8089 Medals: 96
Eh? Who? What? ... Me?
|
 |
«
Reply #7 on:
2010-02-16 13:34:48 » |
|
And he's gone to Google now... Cas 
|
|
|
|
gouessej
JGO Kernel      Posts: 3560 Medals: 30
TUER
|
 |
«
Reply #8 on:
2010-02-17 03:56:48 » |
|
And he's gone to Google now... Cas  Ok I will have to understand some Sun internal classes without any help.
|
Julien Gouesse
|
|
|
Nate
JGO Neuromancer     Posts: 1063 Medals: 30
mooooo
|
 |
«
Reply #9 on:
2010-02-17 05:29:03 » |
|
Woa. I would have never imagined that TextRenderer caches complete Strings and not each glyph. The "last word" in text rendering, indeed!  UnicodeFont caches individual glyphs to the backing texture and allocates multiple backing textures as needed. Also, and this may be the best part, it doesn't leak memory. 
|
|
|
|
Games published by our own members! Go get 'em!
|
|
delt0r
Sr. Member   Posts: 412 Medals: 12
Computers can do that?
|
 |
«
Reply #10 on:
2010-02-17 08:20:58 » |
|
Woa. I would have never imagined that TextRenderer caches complete Strings and not each glyph. The "last word" in text rendering, indeed! The reason it does that is for correct kerning I expect. Kerning is a pain to get right, and if you don't things really do look wrong. Or you need a good fixed width font.
|
I have no special talents. I am only passionately curious.--Albert Einstein
|
|
|
Riven
« League of Dukes » JGO Kernel      Posts: 5870 Medals: 255
Hand over your head.
|
 |
«
Reply #11 on:
2010-02-17 09:11:37 » |
|
The reason it does that is for correct kerning I expect. Kerning is a pain to get right, and if you don't things really do look wrong. Or you need a good fixed width font.
Not only that. If you mix western/arabic text (left-to-right => right-to-left) in the same sentence, things become seriously hard. Java2D's text drawing code is almost as good as MS Office (Word). Much better than any browser I tested pulls off. Rendering glyph-by-glyph would mean you'd have to code that too, which might take a few man-years.
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings
|
|
|
princec
« League of Dukes » JGO Kernel      Posts: 8089 Medals: 96
Eh? Who? What? ... Me?
|
 |
«
Reply #12 on:
2010-02-17 09:46:20 » |
|
I worked around the kerning by drawing every single pair of glyphs next to each other using AWT and then recording the difference in position between them. Seems to work perfectly. Somehow does Arabic too - don't know how, it just works. Apparently. I can't read Arabic  Cas 
|
|
|
|
Nate
JGO Neuromancer     Posts: 1063 Medals: 30
mooooo
|
 |
«
Reply #13 on:
2010-02-17 17:18:18 » |
|
I realize this isn't a UnicodeFont thread, and I promise to shut up about it soon  ... but UnicodeFont doesn't just output a glyph, x-advance, and then the next glyph. Instead it uses AWT's GlyphVector to determine the layout of the entire String to be rendered glyph by glyph. This way kerning, Unicode combining marks, etc are all handled properly. Here is an example in Japanese: http://singthegame.com/static/unicode-japanese.jpgAnd one in Punjabi: http://singthegame.com/static/unicode-punjabi.jpg
|
|
|
|
JL235
JGO Ninja    Posts: 660 Medals: 21
|
 |
«
Reply #14 on:
2010-02-18 13:09:17 » |
|
You could also just pre-draw your text to a BufferedImage using Java2D and then load the image as a texture. You could also do this on the fly whilst running for small pieces of text.
|
|
|
|
gouessej
JGO Kernel      Posts: 3560 Medals: 30
TUER
|
 |
«
Reply #15 on:
2010-02-19 05:26:19 » |
|
My fix is below; it requires some more work but the performance is noticeably better. The most worrying point is the cache of GlyphVector instances. Let me know what you think about it: 1 2 3
| HashMap<String, GlyphVector> fullGlyphVectorCache = new HashMap<String, GlyphVector>();
HashMap<Character, GlyphMetrics> glyphMetricsCache = new HashMap<Character, GlyphMetrics>(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public List<Glyph> getGlyphs(CharSequence inString) { glyphsOutput.clear(); GlyphVector fullRunGlyphVector; fullRunGlyphVector = fullGlyphVectorCache.get(inString.toString()); if (fullRunGlyphVector == null) { iter.initFromCharSequence(inString); fullRunGlyphVector = font.createGlyphVector(getFontRenderContext(), iter); fullGlyphVectorCache.put(inString.toString(), fullRunGlyphVector); } boolean complex = (fullRunGlyphVector.getLayoutFlags() != 0); if (complex || DISABLE_GLYPH_CACHE) { glyphsOutput.add(new Glyph(inString.toString(), false)); return glyphsOutput; }
int lengthInGlyphs = fullRunGlyphVector.getNumGlyphs(); int i = 0; while (i < lengthInGlyphs) { Character letter = CharacterCache.valueOf(inString.charAt(i)); GlyphMetrics metrics = glyphMetricsCache.get(letter); if (metrics == null) { metrics = fullRunGlyphVector.getGlyphMetrics(i); glyphMetricsCache.put(letter, metrics); } Glyph glyph = getGlyph(inString, metrics, i); if (glyph != null) { glyphsOutput.add(glyph); i++; } else { StringBuffer buf = new StringBuffer(); while (i < lengthInGlyphs && getGlyph(inString, fullRunGlyphVector.getGlyphMetrics(i), i) == null) { buf.append(inString.charAt(i++)); } glyphsOutput.add(new Glyph(buf.toString(), i < lengthInGlyphs)); } } return glyphsOutput; } |
I added this because I cannot use directly Character.valueOf(char). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| private static class CharacterCache { private CharacterCache() { }
static final Character cache[] = new Character[127 + 1];
static { for (int i = 0; i < cache.length; i++) { cache[i] = new Character((char) i); } }
public static Character valueOf(char c) { if (c <= 127) { return CharacterCache.cache[c]; } return new Character(c); } } |
I need to remove generics too.
|
Julien Gouesse
|
|
|
|