Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Extending Font & Text to support multiple textures (bug fix)  (Read 10716 times)

0 Members and 1 Guest are viewing this topic.

hobby

  • Newbie
  • *
  • Posts: 43
    • View Profile
Extending Font & Text to support multiple textures (bug fix)
« on: December 19, 2015, 08:22:35 am »
EDIT: This thread has come to implementing a bug fix for the problem of discarded glyphs when the texture used by Font and Text gets filled. It can be useful in case of big text, large alphabets or software renderers (e.g. Windows without GPU).


Use case: an application that can be resized by the user and draws some text in various character sizes. The size of the text varies according to the size of the window and the set of font glyphs that are needed changes over the course of the application.

The current behavior of SFML is to discard new glyphs if the underlying texture cannot be enlarged. The maximum texture size is implementation-dependent and is especially small in SW renderers (e.g. 512x512), resulting in some of the characters not displaying. To prevent that, I use some heuristics to calculate a limit on the character size and then use Text::setScale() to scale the font for the desired size. Large text can become a bit blurry on SW renderers, but this is more tolerable than characters not displaying.

The problem is that with multiple fonts, and new characters being added dynamically, it is not simple to estimate a safe limit on the character size that the maximum texture size will be able to accommodate. It would be easy to start high and decrease the limit on the fly if SFML provided a simple way of determining whether Font had discarded any glyphs for a given character size. (I guess I could redirect sf:err() to a stringbuf and test it at runtime, but I'm looking for a more standardized approach...)
« Last Edit: March 18, 2016, 09:50:25 am by hobby »

hobby

  • Newbie
  • *
  • Posts: 43
    • View Profile
Reliable solution for discarded glyphs?
« Reply #1 on: December 26, 2015, 09:23:56 am »
I managed to partially workaround over the problem for large character sizes (titles/subtitles) by creating multiple instances of sf::Font for the same font, and re-creating them whenever the "context" changes (suggesting different titles/subtitles).

However, taking into account the trend in PC screen resolutions, this method is not too reliable. How can I be sure that what I write today will also work X years from now, on larger screens? Am I the only one that has run into dropped glyphs? I can think of other scenarios, such as big marquees, that are risky to do with SFML.

Is there any interest in extending sf::Font and sf::Text to support multiple textures so that all characters are displayed regardless of how big they are?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Determining if glyphs for a given character size have been discarded
« Reply #2 on: December 26, 2015, 10:35:51 am »
Quote
Is there any interest in extending sf::Font and sf::Text to support multiple textures so that all characters are displayed regardless of how big they are?
Of course, we'll have to fix this one day, we can't leave something broken in the API.
Laurent Gomila - SFML developer

hobby

  • Newbie
  • *
  • Posts: 43
    • View Profile
Re: Determining if glyphs for a given character size have been discarded
« Reply #3 on: December 26, 2015, 01:21:01 pm »
Of course, we'll have to fix this one day, we can't leave something broken in the API.
Can't it be fixed already in SFML 2? The only issue is designing how to extend the interface of Font (and possibly Glyph) in a backward-compatible way. I could come up with some suggestions, but I'm not familiar with your practices so it may be a waste of time.

For example, suppose that we have a method Font::getTextureCount(). One option is to add some textureIndex as a field to Glyph and an optional parameter to Font::getTexture(). Another option, that is less clean in terms of interface but may be simpler to implement, is to not modify Glyph at all, but rather add a new method Font::getGlyphAndTexture() that returns a pair.

Anyway, since new glyphs are currently discarded, SFML users that assume that Font has only one Texture and use the coordinates returned by Font::getGlyph() on the wrong texture may not be impacted too much. If the current behavior must be preserved, a method such as Font::setMultipleTextures() can be added.

How would you extend the existing interface?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Determining if glyphs for a given character size have been discarded
« Reply #4 on: December 26, 2015, 01:47:13 pm »
What about simply adding a pointer to the texture in sf::Glyph? So there would only be Font::getGlyph, no more Font::getTexture.
Laurent Gomila - SFML developer

hobby

  • Newbie
  • *
  • Posts: 43
    • View Profile
Re: Determining if glyphs for a given character size have been discarded
« Reply #5 on: December 27, 2015, 11:10:13 pm »
What about simply adding a pointer to the texture in sf::Glyph? So there would only be Font::getGlyph, no more Font::getTexture.
It's great in terms of interface, but can make it a bit harder to optimize if needed.

Anyway, you can see my implementation here. For start, I favored a simpler implementation over a more optimized one. In the case of underlined/strike-through text, this results in one more RenderTarget::draw() call (can be removed easily later).

I suggest that you or one from the SFML team will review the changes, not in terms of functionality but in terms of SFML practices, as there were many options throughout the implementation. I prefer to discuss everything here (not in GitHub).

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Determining if glyphs for a given character size have been discarded
« Reply #6 on: December 28, 2015, 09:25:50 am »
I just made a quick review without looking too much at the details, but I think it looks good. The only drawback is that your implementation forces to free and reallocate the vertex arrays' memory every time the geometry changes, whereas the memory is always kept allocated in the current implementation (std::vector::clear doesn't deallocate). But as you said, you started with a simple implementation, which is a good thing.

Oh, and the texture member of sf::Glyph should be const :P
Laurent Gomila - SFML developer

hobby

  • Newbie
  • *
  • Posts: 43
    • View Profile
Re: Determining if glyphs for a given character size have been discarded
« Reply #7 on: December 28, 2015, 07:15:05 pm »
Thanks for the quick feedback. Fixed both issues and created pull request #1034.

The implementation can be tested by manually modifying the maximum texture size in Font::findGlyphRect() to a small hard-coded value, thereby forcing the use of multiple textures.

Backward-compatibility can be tested by additionally taking my version of Font and Glyph but keeping the existing implementation of Text.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Determining if glyphs for a given character size have been discarded
« Reply #8 on: December 28, 2015, 10:58:58 pm »
Quote
Fixed both issues
Now the entries in the vertex map will stay forever, even if never used again by the sf::Text instance.
Laurent Gomila - SFML developer

hobby

  • Newbie
  • *
  • Posts: 43
    • View Profile
Re: Determining if glyphs for a given character size have been discarded
« Reply #9 on: December 29, 2015, 06:56:53 pm »
Quote
Fixed both issues
Now the entries in the vertex map will stay forever, even if never used again by the sf::Text instance.

EDIT: Perhaps you have missed the elusive line that frees the entire vertex map on calls to Text::setFont()? If that's what you were looking for, then I can add this line also to Text::setCharacterSize() and I think it will work fine. Anyway, below you may find my reasoning for the current implementation. /EDIT

My rationale for this particular point along the performance/memory/code complexity tradeoff in sf::Text stemmed from assumptions regarding the most likely scenario of sf::Text instances reuse.

More specifically:
1. Allocations (~120 bytes per char) are reused in all the following cases:
  • change in text string
  • change in color
  • change in text style (underline/strike-through/italics/bold)
2. Allocations are not reused in case of:
  • change in character size
3. And lastly, all allocations are freed on:
  • change in font
I assumed that if anything in the sf::Text instance changes between draw() calls, it will most likely belong to group (1) above. And that the likelihood of changing (2) without changing (3) is low. And that anyway, while changes to (3) are unlimited in nature, (2) is a discrete number so it can have some upper bound (I was thinking more on PCs in this last statement).

Sure, I can optimize for both performance & memory usage by always reusing allocations done by previously-clear'ed vector's. It will add some bits to the code. Shall I do that? Or did you have other things in mind?
« Last Edit: December 29, 2015, 11:45:37 pm by hobby »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Determining if glyphs for a given character size have been discarded
« Reply #10 on: December 30, 2015, 06:06:10 pm »
Quote
Perhaps you have missed the elusive line that frees the entire vertex map on calls to Text::setFont()?
Indeed :)
Laurent Gomila - SFML developer

hobby

  • Newbie
  • *
  • Posts: 43
    • View Profile
Re: Determining if glyphs for a given character size have been discarded
« Reply #11 on: December 30, 2015, 11:13:03 pm »
Quote
Perhaps you have missed the elusive line that frees the entire vertex map on calls to Text::setFont()?
Indeed :)
That was for the better :) - I've just added a few comments to sf::Text and also aligned setCharacterSize() to match the behavior of setFont().

Below please find a program that drops glyphs on Win7 64 bit if no GPU is installed (i.e. 512x512 max texture size), and additionally some version of my test program. I've performed both black-box and white-box testing, including backward-compatibility for users of Font::getTexture(), and also used apitrace to verify the textures and drawing sequence.

(click to show/hide)

(click to show/hide)

Do you consider this issue a bug or a feature?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Determining if glyphs for a given character size have been discarded
« Reply #12 on: December 31, 2015, 10:25:21 am »
Thanks for the tests :)

Quote
Do you consider this issue a bug or a feature?
Glyphs not showing is definitely a bug.
Laurent Gomila - SFML developer

hobby

  • Newbie
  • *
  • Posts: 43
    • View Profile
Re: Determining if glyphs for a given character size have been discarded
« Reply #13 on: February 08, 2016, 09:40:27 pm »
Quote
Do you consider this issue a bug or a feature?
Glyphs not showing is definitely a bug.
I've noticed #1034 has been tagged with "feature". Are you not convinced glyphs are dropped? Or is it not that important considering the target audience of SFML?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Determining if glyphs for a given character size have been discarded
« Reply #14 on: February 08, 2016, 10:18:26 pm »
This is most likely an oversight. The title of the PR is also a bit misleading, "extended" usually refers to an additional feature rather than a bug fix.
Laurent Gomila - SFML developer