如何在 OpenGL 中管理缓存纹理?
我正在为 OpenGL 应用程序编写一个文本渲染器。 大小、颜色、字体和抗锯齿功能可以在运行时调整(因此屏幕上可以同时显示多个字体)。 组合太多,无法为字符串和属性的每个组合分配一个纹理。 然而,在任何给定时间,屏幕上只会显示整个字符串数据库的一小部分。
这使我有机会为逐帧打印的字符串创建缓存。 我被要求在整个操作中仅使用一个纹理,因为创建许多纹理的缓存会导致从缓存打印的每个不同字符串产生纹理交换损失。
因此,我面前有一个 2048x2048 纹理,我可以在其中放置任何适合的字符串,因为应用程序出于缓存目的而请求它们。 我很快意识到跟踪二维空间中可用的自由空间并不是微不足道的。
我一直在研究“最佳拟合”和“下一个拟合”之类的东西,但这些似乎适合一维空间。
我如何在 OpenGL 中管理这个缓存纹理?
编辑:我后来了解到这是“二维包装问题”的一个实例。
I am writing a text renderer for an OpenGL application. Size, colour, font face, and anti-aliasing can be twiddled at run time (and so multiple font faces can appear on the screen at once). There are too many combinations to allocate one texture to each combination of string and attributes. However, only a small subset of the entire database of strings will be on the screen at any given time.
This leads me into the opportunity to create a cache for the strings that are being printed frame after frame. It has been mandated that I use only one texture for the entire operation, as creating a cache of many textures would incur a texture swapping penalty for every different string printed from the cache.
So I have before me a 2048x2048 texture, into which I can place whatever strings I can fit as they are being requested by the application for caching purposes. I have quickly realized that tracking the free space available in a two dimensional space is not trivial.
I have been looking at things like Best Fit and Next fit, but those seem to be suitable for 1d spaces.
How can I manage this cache texture in OpenGL?
Edit: I have since learned that this is an instance of a "2d packing problem".
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你遇到的是装箱问题。
首先是坏消息:它是 NP 困难的,因此值得寻找最佳解决方案。
我也为字体做了这样的纹理缓存。 我没有缓存整个单词,只是缓存了字形图像。 这使事情变得容易得多,因为所有图像都大致呈正方形。 一种简单的基于网格的方法来跟踪纹理内存效果非常好。
万一我得到的字形大于我的一个网格框,我只是使用强力搜索分配了两个或更多框(这种情况并不经常发生)。 如果我没有找到任何合适的块,我只是从缓存中随机删除一些字形以腾出可用空间。
这比将内容保存在最近使用的缓存中要容易得多,并且性能几乎一样好。
顺便说一句 - 对于这样的缓存,您总是会浪费一些纹理内存。 除非你的内存非常紧张,否则这应该不是问题。 您应该使用小型纹理格式(8 位 alpha 非常适合字体)。
另外:如果您将网格块设置为 8 像素的倍数,并且可以将抗锯齿功能降低到 4 位,则可以将字形动态压缩为压缩的 DXT 或 S3TC 格式之一。 这样浪费的纹理空间就不再是问题了。
What you have is the bin-packing problem.
Bad news first: It's NP-hard, so it's worth to find the optimal solution.
I've done such texture-caching for fonts as well. I didn't cached entire words but just the glyph images. That makes things a lot easier because all your images are roughly square-shaped. A simple grid based approach to keep track of the texture-memory worked pretty good.
In case I got glyphs that are larger than one of my grid-boxes I just allocated two or more boxes using brute force search (it didn't happend that often). In case I didn't found any suitable block I just randomly removed some glyphs from the cache to make free space.
That was much easier than keeping things in a last recently used cache and performed nearly as good.
Btw - you will always have some waste on texture memory for such a cache. Unless you're very tight on memory that shouldn't be a problem. You should use a small texture-format (8 bit alpha works well for fonts).
Also: If you make your grid-blocks a multiple of 8 pixels, and you can drop your antialiasing to 4 bits you can compress the glyphs into one of the compressed DXT or S3TC formats on the fly. The wasted texture-space becomes a non-issue that way.
如果您的纹理内存不足,您可以看看“距离场”或“有符号距离场”字体渲染技术。 您可以为每个字体系列使用 512x512 纹理,并且可以渲染任何大小的完美抗锯齿文本。
对于该算法,您需要生成一个特殊的纹理,其中包含从纹素到纹理边缘的距离。 看看 Valve 人员的原始论文: http://www.valvesoftware.com/publications /2007/SIGGRAPH2007_AlphaTestedMagnification.pdf 。 有一些框架利用了这一点。 例如,最新版本的 Qt 使用有符号距离场进行文本渲染。
If you are short on texture memory you could take a look at "Distance Field" or "Signed Distance Field" font rendering technique. You could use 512x512 texture per font family and you could render perfectly antialiased text of any size.
For that algorithm you need to generate a special texture, which contains distance from the texel to the edge of the texture. Take a look at original paper by Valve guys: http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf . There are some frameworks which utilize this. For instance latest version of Qt uses signed distance field for text rendering.
我选择使用一种简单的方法。 将纹理划分为可变高度的行。 一行中放置的第一个纹理决定行的高度。 如果纹理可以按高度适合现有行,请检查是否有足够的剩余宽度并将其放置在那里。 否则开始一个新行。 如果无法开始新行,则不缓存该字符串。
I have opted to use a simple approach. Divide the texture into variable height rows. The first texture to be placed in a row decides the height of the row. If a texture can fit into an existing row by height, check to see if there is enough width remaining and place it there. Otherwise start a new row. If a new row cannot be started, do not cache the string.