Implement optimizations for 2D text rendering #23

Merged
Redacted merged 2 commits from ori_sky/JGL:master into master 2024-07-15 11:38:20 -04:00
Contributor

The following patches are included:

J2D: Rewrite text rendering

This patch rewrites text rendering for J2D::DrawString to now construct
a texture atlas for all ASCII-range glyphs in the FT font face, instead
of constructing a texture for every glyph.

This improves text rendering performance for several reasons:

  1. Binding textures is relatively expensive as the GPU is required to do
    a context switch for internal data like texture parameters, and also
    cannot optimize for accesses to the same texture across draw calls.
    This patch removes the need to call glBindTexture more than once per
    call to J2D::DrawString.
  2. As a consequence of the above, all glyphs for a given string can now
    be rendered in a single call to glDrawArrays. This is done by storing
    the cached texture coordinates on CachedGlyph and constructing a full
    array of vertices and texture coordinates for the entire string at
    once, resulting in only /one/ set of client-to-device attribute
    uploads and only one draw call, instead of being required to upload
    attribute data for each glyph separately.

FontCache: Use map for efficient glyph lookup

This patch updates CachedFont to now use an std::map for cached glyphs,
instead of an std::vector. std::map allows O(log n) lookup, whereas
std::vector only allows O(n) lookup.

Note: std::unordered_map technically has better lookup complexity here,
with amortized O(1) lookup. However, hashmaps have a higher inherent
overhead than red-black trees so this would only be viable when going
above around 1000 entries, which should never happen here for ASCII
glyphs.

The following patches are included: ## J2D: Rewrite text rendering This patch rewrites text rendering for `J2D::DrawString` to now construct a texture atlas for all ASCII-range glyphs in the FT font face, instead of constructing a texture for every glyph. This improves text rendering performance for several reasons: 1. Binding textures is relatively expensive as the GPU is required to do a context switch for internal data like texture parameters, and also cannot optimize for accesses to the same texture across draw calls. This patch removes the need to call `glBindTexture` more than once per call to `J2D::DrawString`. 2. As a consequence of the above, all glyphs for a given string can now be rendered in a single call to `glDrawArrays`. This is done by storing the cached texture coordinates on `CachedGlyph` and constructing a full array of vertices and texture coordinates for the entire string at once, resulting in only /one/ set of client-to-device attribute uploads and only one draw call, instead of being required to upload attribute data for each glyph separately. ## FontCache: Use map for efficient glyph lookup This patch updates `CachedFont` to now use an `std::map` for cached glyphs, instead of an `std::vector`. `std::map` allows O(log n) lookup, whereas `std::vector` only allows O(n) lookup. Note: `std::unordered_map` technically has better lookup complexity here, with amortized O(1) lookup. However, hashmaps have a higher inherent overhead than red-black trees so this would only be viable when going above around 1000 entries, which should never happen here for ASCII glyphs.
ori_sky added 2 commits 2024-07-15 06:04:40 -04:00
This patch rewrites text rendering for J2D::DrawString to now construct
a texture atlas for all ASCII-range glyphs in the FT font face, instead
of cosntructing a texture for every glyph.

This improves text rendering performance for several reasons:

1. Binding textures is relatively expensive as the GPU is required to do
   a context switch for internal data like texture parameters, and also
   cannot optimize for accesses to the same texture across draw calls.
   This patch removes the need to call glBindTexture more than once per
   call to J2D::DrawString.
2. As a consequence of the above, all glyphs for a given string can now
   be rendered in a single call to glDrawArrays. This is done by storing
   the cached texture coordinates on CachedGlyph and constructing a full
   array of vertices and texture coordinates for the entire string at
   once, resulting in only /one/ set of client-to-device attribute
   uploads and only one draw call, instead of being required to upload
   attribute data for each glyph separately.
This patch updates CachedFont to now use an std::map for cached glyphs,
instead of an std::vector. std::map allows O(log n) lookup, whereas
std::vector only allows O(n) lookup.

Note: std::unordered_map technically has better lookup complexity here,
with amortized O(1) lookup. However, hashmaps have a higher inherent
overhead than red-black trees so this would only be viable when going
above around 100 entries, which should never happen here for ASCII
glyphs.
Redacted merged commit 8625c52ee9 into master 2024-07-15 11:38:20 -04:00
Sign in to join this conversation.
No description provided.