the Fiddler's picture

New text renderer

Project:The Open Toolkit library
Assigned:the Fiddler

I have just committed a WIP implementation of a new text renderer to the text branch. Improvements:

  1. Simpler to use.
  2. More modular. It is now possible to have different GL1/GL2/GL3-level output or text layout providers. It is also possible to specify your own implementations (for example if you wish to integrate this into your engine).
  3. More accurate rendering (better grid fitting).
  4. Improved speed and reduced GC pressure through caching.
  5. Lower texture memory consumption.

The new implementation is based on GdiPMeasureCharacterRanges, which provides better control over text layout, when compared to MeasureString (the old implementation). Text is now cached on two levels, rasterized glyphs (loaded in texture memory) and laid out text. The public API looks like this:

// No TextureFonts, TextHandles or anything.
TextPrinter text = new TextPrinter();
text.Print("Hello, World!", font);
text.Print(fps.ToString(), font, TextPrinterOptions.NoCache);
TextExtents extents = text.Measure("ABC", font);
RectangleF bbox = extents.BoundingBox;
RectangleF char_pos = extents[1];  // Returns the position of "B" - useful for e.g. drawing a caret or selecting text.

The implementation in SVN is still under heavy development. Caveats:

  • (Fixed) Single texture sheet limitation remains. This is proving more difficult than expected.
  • (Fixed) Sometimes binds the wrong texture for glyphs (funky results!)
  • Trying to draw / measure uncached strings with newlines will cause GC pressure. This is the unfortunate result of a bug in Mono's GdiPMeasureCharacterRanges implementation. Fortunately, this is unlikely to cause problems in real life (you don't usually create dynamic text with newlines in a tight loop).

Anyone still interested despite these problems, you can test the code from the text branch. Expect screenshots shortly.

Edit: Clarified GC-pressure caveat.


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture


It appeared gdiplus.dll was missing, reinstalling .Net 2.0 didn't fix the problem. Is this intended to work on later .Net versions only?
Just installed a clean version of XP SP3, pure .Net 2.0, no visual studio and it works. GdiPlus is a core component of Windows 2000 and higher. Maybe this is a permission problem (executing code from a network drive or something like this?)

Thanks for the explanation, that was a nice aha! moment :)

Inertia's picture


It's probably time for a reinstall...

[4 TMUs]
Well it's not the "official way to do this", just my dirty hack to tackle the problem. The clean ways would be using that texture array extension or try a Texture3D, but the first has high hardware requirements and the latter will be problematic due to size restrictions.
The hack should run on Geforce 5 and up and might even work on SM 1.0 hardware (low instruction count), but the conditionals per fragment won't be blazing fast. I've read that the hardware can use it to it's advantage that all fragments of the rastered primitive will fork into the same conditional, not sure about reliability of this information though.
I understand that you don't want to use on texture-array extension, but it should be clear that you will have to set *some* minimum requirement. No matter what you do, it won't run on my toaster.

@haymo I believe the better approach is to let the application programmer decide what text should be cached in textures by using FBO/PBO techniques. Textprinter cannot know better than the programmer what phrases need to be built once only.

the Fiddler's picture


Ok, I just committed support for multiple texture sheets in the GL1 code path. The implementation is not terribly bright, it builds one list of vertices for each texture and renders those with immediate mode. As long as this implementation does not generate garbage, I am not going to optimize this codepath any further. Faster codepaths will be implemented in the future as necessary (GL1.4 VBOs, GL2, GL3).

The current plan is to implement more layout options, then merge to master and release. Everyone, help hunt down remaining gremlins before release! Does this work on your systems? Do you see any layout issues? How is it speed-wise?

the Fiddler's picture


Status:in progress» in progress (review)
JTalton's picture


Grabbed the code from the branch. Looks pretty good. Font does not seem to be aligned correctly. See screen shot. Text is moved to the right of the red boxes.

FontIssue0001.png1.94 KB
the Fiddler's picture


Ah, the wonders of GDI+ text measurements... Let's hope the workaround of the old renderer can be used here.

Edit: I've committed a workaround that works for everything but italic text. Italic text needs the extra space for possible overhangs (e.g. f), which would get clipped otherwise. Unfortunately, that's the best we can do within the limits of the .Net framework.

JTalton's picture


The fix seems to work well. Woot!

objarni's picture


the Fiddler's picture


I just made a small breakthrough and managed to render text with subpixel antialiasing. The output is awesome. Apart from (missing) gamma correction and some possible (minor) layout differences, it achieves native quality. OpenTK text is now indistinguishable from the surrounding UI, which I feel is important for GUI-oriented applications, such as Golem3d.

The downside here is increased texture memory consumption (unlikely to be a problem) and a few necessary API changes regarding text color (you will have to specify it as a parameter in the Print function, instead of using GL.Color3).

I will push the code to svn and post screenshots asap.

Inertia's picture


Do you mind preparing a before/after screenshot with magnification of a region? Really curious about the difference.