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

Re: Multiple Font objects
posted by the Fiddler

Can you please upload a complete test case?

Re: Multiple Font objects
posted by haymo

Hm, was afraid, you could request this :| - ok. the testprogram attached reproduces the situation on my machine. Unfortunately It does not on another machine. (But I think, I have seen it there also - not sure about this).

the package attached contains the code, an VS2005 compiled exe and an altered version of OpenTK. (To reproduce the modifications, you would have to change the GLControl related 'internal' classes to 'public' in OpenTK - thats it.) Also, there are 3 images contained, showing the visual effect on my machine.

After starting the app, enter the following string into the textbox:

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789...!

(one by one character, not copy and paste!) On my machine, the effect sometimes shows after the "r" - sometimes later.

To me, it seems, the effect is caused by having too many TextureFonts created. Is there an official limit? And if it is related to my machine - how can I prevent it and/or query the maximum number allowed at runtime ?

[Dont know, how to attach a file here, so I uploaded it to: http://ilnumerics.net/files/WindowsApplication3.zip ]
Thanks so far!

Re: Multiple Font objects
posted by the Fiddler

Thanks. The reason I asked for the test case, is that I have limited time to work on OpenTK, so it's best to minimize overhead when developing/debugging :)

Can you try this? Hold '1' for a little while, then type '2': "111111111111111112". Does the string display ok? Here, all letters turn to '2' - pressing 1 or 2 results in a 2 being displayed.

(Investigating)

Re: Multiple Font objects
posted by haymo

Ah, great enough, you can follow the issue!
On the machine, which always failes (WinXP Tablet PC Ed., Mobile Intel 915GM, 910GML Express Chipset) all 1 turn into 2.
On another machine (Ubuntu 7.10, GeForce FX 5200) it runs smoothly - no change while pressing 2.

Re: Multiple Font objects
posted by the Fiddler

Ok, individual glyphs are currently inserted into a binary tree for packing - it seems that narrow letters, like 'l', 'I' or '1' may cause problems.

Argh, too tired right now, will check this hypothesis tomorrow.

Re: Multiple Font objects
posted by haymo

you are probably right. I also expect the source of the problem somewhere near or inside TexturePacker. Couldn't really get into it yet. But I'm not sure, if this is the only problem associated with this issue. Im only guessing, since I dont even understand in detail, how the TextPrinter works. I assume, all textures for chars are layed out on an area. Drawing a char than means, drawing a rectangle with a texture bind to it, where that texture will point to the subrectangle in the source area, corresponding to the char. Is this correct understanding?
Now, in the failing situation, after adding a new Glyph to the Font, this offset into the texture-char-area seems somehow to be incorrect. While your '1111112' example demonstrated, that the mapping of Glyphs to chars is malfunctioning, here I most the time see the offset to point somewhere outside the texture area. This causes the corrupted 'chars' to look like beeing composed out of random lines or even random noise?
Maybe some Size measurement problem? Strange, that it works fine on another machine... Hm.

Re: Multiple Font objects
posted by the Fiddler

I find it interesting that it breaks on individual characters, but not on larger strings. It is also strange that it works in some computers, but not on others.

The current implementation works like this: individual glyphs are rasterized on demand, and "packed" into an OpenGL texture using the TexturePacker. TextPrinter composes strings into VBOs of quads, with each quad displaying a single character.

This looks like an error in TexturePacker, which either packs some glyphs incorrectly (overwriting existing entries), or returns wrong positions for packed glyphs. Try modifying your test case to render a single textured quad inside the TextPrinter.Begin/End block (texcoords from 0.0 to 1.0) - this will display glyphs as they are packed into the texture.

I've just found the root of some crashes on Linux, will focus on this issue once these are fixed.

Re: Multiple Font objects
posted by the Fiddler

I find it interesting that it breaks on individual characters, but not on larger strings. It is also strange that it works in some computers, but not on others.

The current implementation works like this: individual glyphs are rasterized on demand, and "packed" into an OpenGL texture using the TexturePacker. TextPrinter composes strings into VBOs of quads, with each quad displaying a single character.

This looks like an error in TexturePacker, which either packs some glyphs incorrectly (overwriting existing entries), or returns wrong positions for packed glyphs. Try modifying your test case to render a single textured quad inside the TextPrinter.Begin/End block (texcoords from 0.0 to 1.0) - this will display glyphs as they are packed into the texture.

I've just found the root of some crashes on Linux, will focus on this issue once these are fixed.

Re: Multiple Font objects
posted by haymo

The problem comes from the TexturePacker beeing static for all TextureFont objects. Once a Font is disposed, it sets the TexturePacker object to null, which causes it to loose all previously creates glyphs.
I (hopefully) solved the issue provisional by making texture packer and texture an instance member. This seems to work - but do you see any drawbacks for it?

For the future, I could imagine, improving some of the TexturePrinter design. Maybe I'll try to clean up the class hirarchy a little (less interfaces, more abstract classes, but its only my taste). Would you be interested in contributions regarding to that?

Re: Multiple Font objects
posted by the Fiddler

Thanks, I see the problem now. The TexturePacker is shared between TextureFonts to improve performance (minimize texture switches) and reduce wasted video memory.

That said, the text engine is still in a primitive state - improvements are welcome. :)

What changes do you have in mind?

Re: Multiple Font objects
posted by haymo

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?

Re: Multiple Font objects
posted by the Fiddler

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.

Re: Multiple Font objects
posted by haymo

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 ;)

Re: Multiple Font objects
posted by objarni

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.

Re: Multiple Font objects
posted by the Fiddler

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.

Re: Multiple Font objects
posted by objarni

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.

Re: Multiple Font objects
posted by objarni

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?

Re: Multiple Font objects
posted by the Fiddler

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

Re: Multiple Font objects
posted by objarni

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

Re: Multiple Font objects
posted by objarni

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

Re: Multiple Font objects
posted by the Fiddler

Actually, both algorithms are generic and don't rely on .Net or GL.

If my understanding is correct, the best candidate for unit tests is OpenTK.Math. Besides that and OpenTK.Fonts, there are a few other suitable candidates, but unfortunately the bulk of OpenTK is low-level code (window creation etc) and I don't think this can be verified with unit tests (unless I'm wrong?)

I'll be reading up on unit testing, it looks like a very useful skill/tool.

Re: Multiple Font objects
posted by objarni

Yup, I think those are the best candidates right now. Therefore, those two have the potential of being the most "stable" things in OpenTK.

I discovered unit testing a little more than a year ago, and it has been a paradigm shift in the way that I do software development. Good luck and please share your experiences! I'm still learning, it's a huge topic.

Testing wrapping functionality should not be necessary, if it's a thin enough wrapper, which I think GL is for the most part.

The windowing code is the toughest part. Keeping it as small as possible, and writing lots of examples to be run "manually" is a good beginning.

Re: Multiple Font objects
posted by haymo

... thinking about this Font rendering stuff again:

Storage:
Taking Fiddler's considerations above in account, I agree with you, to have one single storage texture for all Fonts involved. This would (at least in all situations I can think of right now) be a texture sheet, loaded up into the GL and used for all subsequent renderings - no matter wich font currently is targeted.

** Are there any needs to keep the implementation for the storage (TextureFont) abstract? My concern is, that the way, the glyphs are stored will highly influence the way the printer works. Therefore, it does not seem to make sence to implement them seperately ?!? Or the printer may be created via a factory by TextureFont. Personally I would expect the increase in flexibility for an abstract TextureFont implementation not paying off the increase in complexity and the (possible?) decrease in performance. Therefore, I suggest to fix the TextureFont (storage) in a concrete class at the root of the whole Font implementation. That implementation would ALWAYS store the glyphs as texture sheets - its it.

** As soon as a sheet gets filled and needs more space for new glyphs, question arises, which strategy to choose:
i) replace the existing texture sheet by a larger one (f.e. twice as large, if possible) and copy all existing glyphs, possibly compacting the glyphs at the same time, or
ii) create and manage multiple sheets at the same time. This attempt IMO is better regarding memory consumption and (card) compatibility, but worse regarding the original design decisions (texture switching).
Both attempts come with the drawback of having to handle the case, the requested size for the sheets beeing too large for the memory avaliable.
Any comments on this?

** In order to store and manage glyphs from multiple fonts on the same texture sheet, we need a way to hash-map a glyph. For this, the information, which System.Font the character belongs to must be taken into account also. Right now this is solved by having TextureFont objects for each font individually - all share the same global texture sheet. But there are some drawbacks for this design too:
i) if a TextureFont is destroyed, it leaves its Glyphs in the sheet. We would have to clean / recompact the texture sheet in such a case (or did I miss this part in the code?)
ii) multiple TextureFonts could be created, based on the same System.Font. Possibly this would result in multiple identical glyphs in the sheet.
My favorite is to have only one singleton implementation of texture storage (TexturePacker?) also managing all Fonts it currently stores the Glyphs for. Right now I'm seeking the best way to identify a FontFamily-Name-Size-Weight-(...?)-Character combination uniquely.??

Printers
** Am I right, thinking that the printer implementation is the only part which should be flexible enough and implemented via interfaces/ abstract base classes (my favorite)? This part I haven't thought about a lot yet. But it seems the current implementations are pretty straight forward and already pointing in the best direction here ...?

Hope, my understanding is not going in the complete wrong direction on this... thanks.

Who's online

There is currently 1 user and 1 guest online.

Online users

  • fukqueup