haymo's picture

Multiple Font objects

I experience strange behavior with TextureFont and TextRenderer: TextureFont seems to shuffle the glyph <-> char mapping _sometimes_ :(
Steps to reproduce the behavior:

Call the following snippet more than 15 times - once per rendering frame. Every time 'text' should be only one char. Each char should be different.

 //... other rendering stuff
TextHandle handle; 
m_textPrinter.Prepare(text,m_font,out handle);
m_textPrinter.Begin();
using (handle) 
    m_textPrinter.Draw(handle); 
m_textPrinter.End();
//...finish other rendering stuff + swapbuffers

Once 15 chars are loaded into the font, the output gets scrambled, ie. where "a" should be - is now "5" or something completely different, even blank space. It is not deterministic, what is drawn instead. But it is deterministic, that a false texture is drawn.

Also, the bahavior does not happen, when loading more than 15 chars at once, but only by incrementally loading the chars in the way shown above.

Dont know if '15' is specific to my machine either.


Comments

Comment viewing options

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

I find it somehow hard to get into the class hierarchy and believe, the whole TexturePacker stuff might be implemented in a more simple way? But - again - just guessing, since I didnt do it yet and dont _really_ understand, whats going on in your way. Definitely, I would expect the texture to be specific to an individual font. Not necessarily TextureFont object but to one specific Font type + size. And this should be shared between similar TextureFont instances.

2 Questions:
1) Is there any reason, you have implemented the textures as a bitmap of a size, holding multiple lines of glyphs? Why not only one "line" of glyphs, all laying next to each other? Wouldn't this get rid of the binary tree (which in my opinion is not neseccary here)? It might decrease the resolution (exactness) for each glyph to be found in the texture, but this should not be remarkable, would it?

2) Since I dont really understand your implementation -> could you think about my 'solution' of changing static TextureFont members into instance members for a minute? Do you see any risk by doing so?

the Fiddler's picture

Ok, let me explain the design decisions behind the current architecture.

The "font engine" has two main parts at this point:
1. The TextureFont, which encapsulates a font object. It can load font data, upload glyphs into a texture and return information about itself and specific glyphs.
2. The TextPrinter, which can use TextureFonts to print formatted text.

The main idea is that we should be able to change the implementation of a specific part, without touching the others. For example, once GL3 comes out, we should only need to add a new ITextPrinterImplementation. Or, if we want to change TextureFont to use FreeType or native GDI/CoreFonts/etc instead of GDI+, we should be able to do that without touching the layout logic (TextPrinter). Or, at some point we might add a VectorFont implementation - you get the idea.

I'm not saying that the current implementation is flexible enough for all of these at this point (besides, I've stopped working on fonts until the core OpenTK.dll is ready), but that's what I'm shooting for.

That's the general idea at least. Now, for the specific implementation:

[TextureFont storage]
Using a unified storage "backend" for all TextureFont objcets has two advantages over a single (or multiple) texture sheets per TextureFont.

a. It minimizes wasted texture space (imagine creating a TextureFont with only ten glyphs - 90% of the texture sheet would stay empty!)

b. It minimizes texture switches when rendering.

Ideally, the TextureFont would not know how the storage backend stores/recalls glyphs (that's not the case right now, but it's in the todo list).

[TexturePacker and binary tree]
AFAIK, 2d packing is an NP problem. The binary tree provides a good enough solution, without being too complicated.

The simple solution (aligned lines) would work too (without change in quality), but it would give worse cover than the binary tree - especially if you try to pack glyphs with different heights.

[Change static texture sheets to instance]
I'm sorry if I gave the appearance that I discarded the idea without thought. However, this would increase the coupling between TextureFont and the storage backend, which (IMHO) is a move in the wrong direction. Risk: GL3 arrives, and TextureFont stops working (since it relies on GL2).

The "right" solution would be to decouple storage from the TextureFont and hide the implementation details from the latter.

haymo's picture

Many thanks for those details. Thats enough stuff to think about for a while. Without having done yet, I guess, the main challenge will be to find the right balance between uncoupling (interfaces) and code clearness. I personally prefer inheritance over interfaces most the time. I find it easier to debug and to get a clue of the hierarchy. Access modifiers are more flexible this way. But at the end this is mostly a question of taste.

If I get you right, we actually have 3 main parts here:

  1. the font object, which will mainly be of informational use
  2. the texture storage, uploading and managing textures to the GL, and
  3. a printer, drawing the (textured) vertices.

But I will have to think about it a little .... Interesting enough ;)

objarni's picture

Uncoupling via interfaces has at least one big advantage compared to class hierarcies: It is simpler to write unit tests for interface-based code (since you can "fake" an interface easier than a class). But traditionally, not many coders write unit tests at all.

the Fiddler's picture

Good point. Problem is, how would you go on testing this code? Text layout will differ according to the font used, and the underlying layout engine. Different video cards will render differently too. I guess you could test loading/disposing of fonts as well as glyph packing, but that's only a small part of what OpenTK.Fonts does.

Interested in hearing ideas here.

objarni's picture

Sorry if this goes slightly off-topic :)

Writing testable code is an art.

I try to minimize "untestable code" -- it's quite obvious when you think a bit about it. Code that is untestable, is more likely to contain bugs than testable code. With "testable" I mean "unit-testable". I don't mean testable in the "run program and point-and-click" sense. The latter is another way of testing, altough it is a more work-intensive way of testing code, at least in the long run. (for example in four weeks, when you want to change your code you don't really want to manually redo all tests you did the first time around, would you?)

I don't know the details of the font system in OpenTK. The packing seems easily testable, yes.

Sometimes, you can "wrap" functionality into interfaces, exposing only the interfaces to algorithms. That way, you can actually test the algorithms.

Eg. lets say we want to write an algorithm to draw a square onscreen, making sure it is easily testable in a unit test. Instead of drawing lines directly on screen using some graphics API, extract an interface for the line drawing method:

interface ILineDrawer { void DrawLine(int x1, int y1, int x2, int y2, int color); }

Now the actual implementation of this will be using the graphics API, it's just a redirection:

class GDILineDrawer : ILineDrawer {
  public GDILineDrawer(Graphics g) { ... }
  public void DrawLine(int x1, int y1, int x2, int y2, int color) {
     g.DrawLine(...);
  }
}

.. and in the unit test, we can just "fake" a LineDrawer for the purpose of testing our square-drawing algorithm:

class FakeLineDrawer() : ILineDrawer {
  public List<int> x1s, y1s, x2s, y2s, colors;
  public void DrawLine(int x1, int y1, int x2, int y2, int color) {
    x1s.Add(x1);
    ...
    colors.Add(color);
  }
}
 
// example code using NUnit
void TestDrawSquare() {
   ILineDrawer drawer = new FakeLineDrawer();
   MyGraphicsAlgos.DrawSquare(drawer, 0, 0, 10, 0xff);
   // assume lines are drawn in order: bottom,top,left,right
   // with smallest coordinate first in each line
   List<int> expectedX1s = new List<int>{0,0,0,10};
   List<int> expectedY1s = new List<int>{0,10,0,0};
   List<int> expectedX2s = new List<int>{10,10,0,10};
   List<int> expectedY2s = new List<int>{0,10,10,10};
   List<int> expectedCols = new List<int>{0xff,0xff,0xff,0xff};
   CollectionAssert.AreEqual(expectedX1s, drawer.x1s);
   CollectionAssert.AreEqual(expectedY1s, drawer.y1s);
   CollectionAssert.AreEqual(expectedX2s, drawer.x2s);
   CollectionAssert.AreEqual(expectedY2s, drawer.y2s);
   CollectionAssert.AreEqual(expectedCols, drawer.colors);
}

In MyGraphicsAlgos.cs:

  public DrawSquare(ILineDrawer drawer, int x, int y, int size, int color) {
    int xend = x+size-1;
    int yend = y+size-1;
    drawer.DrawLine(x,y,xend,y,color);//bottom
    drawer.DrawLine(x,yend,xend,y,color);//top
    drawer.DrawLine(x,y,x,yend,color);//left
    drawer.DrawLine(xend,y,xend,yend,color);//right
  }

An additional advantage of using interfaces, is that it becomes really easy to extend systems written using them. For example, it would not be hard at all to add LineDrawers for printing to paper, writing an ASCII file or using OpenTK in the above (childish) example. Algorithms such as DrawSquare() will work with any of them out-of-the-box.

What is the price to pay for this? Well, in the above example there's one more concept to understand, namely the ILineDrawer interface.

objarni's picture

A note on testing the font system: you don't really want to unit-test already-existent functionality (the .NET font functionality, or OpenGL). After all, we have to trust them since we can't change them easily.

What you test, is all you "homebrewn" algorithms/classes - packing, layout, API/usage. The things that are under your control, simply put.

- What algorithms are there in the font system?

the Fiddler's picture

There's a binary tree for the TexturePacker (insertion, access). There's a text layout algorithm which creates arrays of vertices/indices for printing text (rather simple).

More or less that's it - the rest are simple calls into .Net functions (rasetrizing glyphs, measuring text).

objarni's picture

Then those two algorithms would be candidates for two different unit tests.

Is it possible to break out functionality so that they can be tested easily in a unit test? That is, is it possible to write the algorithm without any API calls (neither .NET nor GL)?

For example, generating/deleting texture names could be extracted into an interface:

interface INameAllocator
{
  int Gen();
  void Delete(int);
}

Any other such API calls in the algorithms?

In my experience, the closer the class is to an (abstract) datastructure, the simpler it is to test. Maybe the binary tree is close to that..?

objarni's picture

Anyone interested in unit-testing and it's "taken-to-it's-extreme"-method, check this article about some guys writing games using it:

http://www.gamesfromwithin.com/articles/0603/000107.html