Java-Gaming.org Java4K winners: [ by our judges | by the community ]         
Featured games (67)
games approved by the League of Dukes
Games in Showcase (∞)
games submitted by our members



News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  Print  
  Memory leak in TextRenderer  (Read 2043 times)
0 Members and 1 Guest are viewing this topic.
Offline 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
Offline 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.

Offline emzic

JGO Ninja
***

Posts: 506



« 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!

www.embege.com - personal website
webstart blendinspect - OpenGL BlendingModes visualization.
Games published by our own members! Go get 'em!
Offline 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 Sad The demo "TextFlow" has the same problem but in my case, the memory leak is hugely bigger Sad

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 Cheesy

Julien Gouesse
Offline Nate

JGO Neuromancer
****

Posts: 1063
Medals: 30


mooooo


« Reply #4 on: 2010-02-13 22:25:57 »

You could port UnicodeFont to JOGL:
https://bob.newdawnsoftware.com/repos/slick/trunk/Slick/src/org/newdawn/slick/UnicodeFont.java
Or as mentioned, just support the BMFont (AngelCode) format and then you could use Hiero:
http://slick.cokeandcode.com/demos/hiero.jnlp

Offline 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/*<Glyph>*/ 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
Offline 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/*<Glyph>*/ 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
Offline 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 Smiley

Offline 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 Smiley
Ok I will have to understand some Sun internal classes without any help.

Julien Gouesse
Offline 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! Roll Eyes

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.  Tongue

Games published by our own members! Go get 'em!
Offline delt0r

Sr. Member
**

Posts: 412
Medals: 12


Computers can do that?


« Reply #10 on: 2010-02-17 08:20:58 »

Quote
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
Offline 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
Offline 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 Roll Eyes

Cas Smiley

Offline 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  Kiss... 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.jpg
And one in Punjabi:
http://singthegame.com/static/unicode-punjabi.jpg

Offline 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.

Offline 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) {
                // Punt to the robust version of the renderer
               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 {
                    // Assemble a run of characters that don't fit in
                   // the cache
                   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(),
                    // Any more glyphs after this run?
                           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) { // must cache
               return CharacterCache.cache[c];
            }
            return new Character(c);
        }
    }


I need to remove generics too.

Julien Gouesse
Pages: [1]
  Print  
 
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.16 | SMF © 2011, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.219 seconds with 21 queries.