From 1daef171779ad048a55696177a36bc9bd8b54d5e Mon Sep 17 00:00:00 2001 From: the_fiddler Date: Sun, 31 May 2009 10:12:41 +0000 Subject: [PATCH] Added display list cache to GL1TextOutputProvider. This change improves peak TextPrinter speed by more than 10x (1.6M glyphs per second as measured on a 1.8GHz Core 2 with a 8400M card). We still need a cache eviction strategy. Modified ITextOutputProvider interface to pass TextBlocks by reference. --- .../Graphics/Text/GL1TextOutputProvider.cs | 166 ++++++++++-------- .../Graphics/Text/ITextOutputProvider.cs | 2 +- Source/Utilities/Graphics/TextPrinter.cs | 3 +- 3 files changed, 98 insertions(+), 73 deletions(-) diff --git a/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs b/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs index 98f4c03e..0c190164 100644 --- a/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs +++ b/Source/Utilities/Graphics/Text/GL1TextOutputProvider.cs @@ -54,6 +54,11 @@ namespace OpenTK.Graphics.Text Viewport viewport = new Viewport(); Matrix4 matrix = new Matrix4(); + // TextBlock - display list cache. + // Todo: we need a cache eviction strategy. + const int block_cache_capacity = 32; + readonly Dictionary block_cache = new Dictionary(block_cache_capacity); + bool disposed; #endregion @@ -71,7 +76,7 @@ namespace OpenTK.Graphics.Text #region Print - public void Print(TextBlock block, Color color, IGlyphRasterizer rasterizer) + public void Print(ref TextBlock block, Color color, IGlyphRasterizer rasterizer) { GL.PushAttrib(AttribMask.CurrentBit | AttribMask.TextureBit | AttribMask.EnableBit | AttribMask.ColorBufferBit | AttribMask.DepthBufferBit); @@ -83,90 +88,109 @@ namespace OpenTK.Graphics.Text RectangleF position; - using (TextExtents extents = rasterizer.MeasureText(ref block)) + int block_hash = block.GetHashCode(); + if (block_cache.ContainsKey(block_hash)) { - // Build layout - int current = 0; - foreach (Glyph glyph in block) + GL.CallList(block_cache[block_hash]); + } + else + { + using (TextExtents extents = rasterizer.MeasureText(ref block)) { - // Do not render whitespace characters or characters outside the clip rectangle. - if (glyph.IsWhiteSpace || extents[current].Width == 0 || extents[current].Height == 0) + // Build layout + int current = 0; + foreach (Glyph glyph in block) { - current++; - continue; - } - else if (!Cache.Contains(glyph)) - Cache.Add(glyph, rasterizer, TextQuality); - - CachedGlyphInfo info = Cache[glyph]; - position = extents[current++]; - - // Use the real glyph width instead of the measured one (we want to achieve pixel perfect output). - position.Size = info.Rectangle.Size; - - if (!active_lists.ContainsKey(info.Texture)) - { - if (inactive_lists.Count > 0) + // Do not render whitespace characters or characters outside the clip rectangle. + if (glyph.IsWhiteSpace || extents[current].Width == 0 || extents[current].Height == 0) { - List list = inactive_lists.Dequeue(); - list.Clear(); - active_lists.Add(info.Texture, list); + current++; + continue; } - else + else if (!Cache.Contains(glyph)) + Cache.Add(glyph, rasterizer, TextQuality); + + CachedGlyphInfo info = Cache[glyph]; + position = extents[current++]; + + // Use the real glyph width instead of the measured one (we want to achieve pixel perfect output). + position.Size = info.Rectangle.Size; + + if (!active_lists.ContainsKey(info.Texture)) { - active_lists.Add(info.Texture, new List()); + if (inactive_lists.Count > 0) + { + List list = inactive_lists.Dequeue(); + list.Clear(); + active_lists.Add(info.Texture, list); + } + else + { + active_lists.Add(info.Texture, new List()); + } + } + + { + // Interleaved array: Vertex, TexCoord, Vertex, ... + List current_list = active_lists[info.Texture]; + current_list.Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Top)); + current_list.Add(new Vector2(position.Left, position.Top)); + current_list.Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Bottom)); + current_list.Add(new Vector2(position.Left, position.Bottom)); + current_list.Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom)); + current_list.Add(new Vector2(position.Right, position.Bottom)); + + current_list.Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom)); + current_list.Add(new Vector2(position.Right, position.Bottom)); + current_list.Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Top)); + current_list.Add(new Vector2(position.Right, position.Top)); + current_list.Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Top)); + current_list.Add(new Vector2(position.Left, position.Top)); } } - - { - // Interleaved array: Vertex, TexCoord, Vertex, ... - List current_list = active_lists[info.Texture]; - current_list.Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Top)); - current_list.Add(new Vector2(position.Left, position.Top)); - current_list.Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Bottom)); - current_list.Add(new Vector2(position.Left, position.Bottom)); - current_list.Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom)); - current_list.Add(new Vector2(position.Right, position.Bottom)); - - current_list.Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Bottom)); - current_list.Add(new Vector2(position.Right, position.Bottom)); - current_list.Add(new Vector2(info.RectangleNormalized.Right, info.RectangleNormalized.Top)); - current_list.Add(new Vector2(position.Right, position.Top)); - current_list.Add(new Vector2(info.RectangleNormalized.Left, info.RectangleNormalized.Top)); - current_list.Add(new Vector2(position.Left, position.Top)); - } } - } - // Render - foreach (Texture2D key in active_lists.Keys) - { - List list = active_lists[key]; - - key.Bind(); - - SetColor(color); - - GL.Begin(BeginMode.Triangles); - - for (int i = 0; i < list.Count; i += 2) + // Render + int display_list = 0; + if ((block.Options & TextPrinterOptions.NoCache) == 0) { - GL.TexCoord2(list[i]); - GL.Vertex2(list[i + 1]); + display_list = GL.GenLists(1); + GL.NewList(display_list, ListMode.CompileAndExecute); + } + foreach (Texture2D key in active_lists.Keys) + { + List list = active_lists[key]; + + key.Bind(); + + SetColor(color); + + GL.Begin(BeginMode.Triangles); + + for (int i = 0; i < list.Count; i += 2) + { + GL.TexCoord2(list[i]); + GL.Vertex2(list[i + 1]); + } + + GL.End(); + } + if ((block.Options & TextPrinterOptions.NoCache) == 0) + { + GL.EndList(); + block_cache.Add(block_hash, display_list); } - GL.End(); + // Clean layout + foreach (List list in active_lists.Values) + { + //list.Clear(); + inactive_lists.Enqueue(list); + } + + active_lists.Clear(); } - - // Clean layout - foreach (List list in active_lists.Values) - { - //list.Clear(); - inactive_lists.Enqueue(list); - } - - active_lists.Clear(); - + GL.PopAttrib(); } diff --git a/Source/Utilities/Graphics/Text/ITextOutputProvider.cs b/Source/Utilities/Graphics/Text/ITextOutputProvider.cs index 2634ee9f..ab2fe5c2 100644 --- a/Source/Utilities/Graphics/Text/ITextOutputProvider.cs +++ b/Source/Utilities/Graphics/Text/ITextOutputProvider.cs @@ -34,7 +34,7 @@ namespace OpenTK.Graphics.Text { interface ITextOutputProvider : IDisposable { - void Print(TextBlock block, Color color, IGlyphRasterizer rasterizer); + void Print(ref TextBlock block, Color color, IGlyphRasterizer rasterizer); void Clear(); void Begin(); void End(); diff --git a/Source/Utilities/Graphics/TextPrinter.cs b/Source/Utilities/Graphics/TextPrinter.cs index 3ed3b8ae..cb8aac74 100644 --- a/Source/Utilities/Graphics/TextPrinter.cs +++ b/Source/Utilities/Graphics/TextPrinter.cs @@ -134,7 +134,8 @@ namespace OpenTK.Graphics if (!ValidateParameters(text, font, rect)) return; - TextOutput.Print(new TextBlock(text, font, rect, options, alignment, direction), color, Rasterizer); + TextBlock block = new TextBlock(text, font, rect, options, alignment, direction); + TextOutput.Print(ref block, color, Rasterizer); } #endregion