我还没有处理过您所面临的具体问题,但我发现纹理池在 OpenGL 中非常有用,无需花太多心思就能获得高效的结果。
在我的情况下,问题是我不能使用与用于输出结果的纹理相同的纹理作为延迟着色器的输入。然而我经常想这样做:
// Make the texture blurry.
blur(texture);
然而,我不得不创建 11 种不同分辨率的不同纹理,并且必须在它们之间交换作为水平/垂直模糊着色器的输入和输出,使用 FBO 来获得看起来不错的模糊效果。我从不非常喜欢 GPU 编程,因为我遇到过的一些最复杂的状态管理经常在那里。由于着色器的纹理输入也不能用作纹理输出的基本要求,我需要去绘图板弄清楚如何最小化分配的纹理数量,这感觉非常错误。
所以我创建了一个纹理池和 OMG,它大大简化了事情!它做到了,所以我可以左右创建临时纹理对象而不必担心,因为销毁纹理对象实际上并没有调用glDeleteTextures,它只是将它们返回到池中。所以我终于能够做到:
blur(texture);
...正如我一直想要的那样。出于某种有趣的原因,当我开始越来越多地使用池时,它加快了帧速率。我想即使我考虑到尽量减少分配的纹理数量,我仍然以消除池的方式分配了比我需要的更多的内容(请注意,实际的现实世界示例所做的不仅仅是包括 DOF 在内的模糊, bloom、hipass、lowpass、CMAA 等,而 GLSL 代码实际上是基于可视化编程语言动态生成的,用户可以使用它来动态创建新的着色器。
所以我真的建议从探索这个想法开始。听起来它对您的问题有帮助。就我而言,我使用了这个:
struct GlTextureDesc
{
...
};
...考虑到我们可以指定多少纹理参数(像素格式、颜色分量的数量、LOD 级别、宽度、高度等),这是一个相当庞大的结构。
然而,该结构是可比较的和可散列的,最终被用作散列表中的键(如unordered_multimap)以及作为关联值的实际纹理句柄。
这让我们可以这样做:
// Provides a pool of textures. This allows us to conveniently rapidly create,
// and destroy texture objects without allocating and freeing an excessive number
// of textures.
class GlTexturePool
{
public:
// Creates an empty pool.
GlTexturePool();
// Cleans up any textures which haven't been accessed in a while.
void cleanup();
// Allocates a texture with the specified properties, retrieving an existing
// one from the pool if available. The function returns a handle to the
// allocated texture.
GLuint allocate(const GlTextureDesc& desc);
// Returns the texture with the specified key and handle to the pool.
void free(const GlTextureDesc& desc, GLuint texture);
private:
...
};
此时我们可以左右创建临时纹理对象,而不必担心过度调用glTexImage2D 和glDeleteTextures。我发现它非常有帮助。
最后要注意的是上面的cleanup函数。当我将纹理存储在哈希表中时,我在它们上放置了一个时间戳(使用系统实时)。我定期调用此清理函数,然后扫描哈希表中的纹理并检查时间戳。如果他们只是在池中闲置了一段时间(例如 8 秒),我会致电 glDeleteTextures 并将它们从池中移除。我使用一个单独的线程和一个条件变量来构建一个纹理列表,以便在下次有效上下文可用时通过定期扫描哈希表来删除,但如果您的应用程序都是单线程的,您可能只需调用此清理在主循环中每隔几秒运行一次。
也就是说,我从事的视觉特效工作不像 AAA 游戏那样对实时性有严格的要求。我的领域更关注离线渲染,我离 GPU 向导还很远。可能有更好的方法来解决这个问题。但是,我发现从这个纹理池开始非常有帮助,我认为它对你的情况也可能有帮助。而且实现起来相当简单(只花了我半个小时左右)。
如果您请求分配/释放的纹理大小、格式和参数到处都是,这仍然可能导致分配和删除大量纹理。在那里它可能有助于统一一些东西,比如至少使用 POT(2 的幂)大小等等,并决定要使用的最小像素格式数量。在我的情况下,这不是什么大问题,因为我只使用一种像素格式,而且我想要创建的大部分纹理临时文件都是放大到天花板 POT 的视口大小。
至于 FBO,我不确定它们如何帮助您解决过度纹理分配/释放的直接问题。我主要将它们用于延迟着色,以便在以合成风格的方式在多个通道中渲染几何图形后对 DOF 等效果进行后处理,并将其应用于生成的 2D 纹理。我自然而然地使用 FBO,但我想不出 FBO 如何立即减少您必须分配/取消分配的纹理数量,除非您可以只使用一个带有 FBO 的大纹理并将多个输入纹理渲染到屏幕外输出质地。在这种情况下,它不会是 FBO 直接提供帮助,而只是能够创建一个巨大的纹理,您可以将其部分用作输入/输出而不是许多较小的部分。