【发布时间】:2012-10-30 00:11:56
【问题描述】:
我正在用 openGL 编写一个 2D 游戏,在渲染覆盖整个窗口的几个纹理时遇到了一些性能问题。
我所做的实际上是创建一个具有屏幕大小的纹理,使用 FBO 将我的场景渲染到该纹理上,然后使用不同的偏移量渲染纹理几次以获得一种“阴影”。但是当我这样做时,我会在使用集成视频卡时大幅降低性能。
总而言之,我在整个屏幕上渲染了 7 个四边形(背景图像、5 个带有黑色“色调”的“阴影图像”和具有真实颜色的相同纹理)。我正在使用大小为 1024x1024 的 RGBA 纹理,并将它们放在 900x700 的窗口中。当我不渲染纹理时,我得到 200 FPS 和 34 FPS(在这两种情况下,我实际上都创建了纹理并将场景渲染到它上面)。我觉得这很奇怪,因为我基本上只渲染 7 个四边形。奇怪的是,当我运行 CPU 分析器时,它并没有表明这是瓶颈(我知道 opengl 使用管道架构,这可能会发生,但大多数时候不会发生)。
当我使用我的外部视频卡时,我在进行上述测试时获得一致的 200 FPS。但是当我禁用场景渲染到纹理上并禁用纹理渲染到屏幕上时,我得到~1000 FPS。这只发生在我的外部显卡上——当我使用集成显卡禁用 FBO 时,我得到相同的 200 FPS。这真的让我很困惑。
谁能解释一下发生了什么以及上述数字是否正确?
集成显卡 - Intel HD Graphics 4000
外接显卡 - NVIDIA GeForce GTX 660M
附:我正在用 C# 编写我的游戏 - 如果有帮助,我会使用 OpenTK。
编辑:
首先感谢所有回复 - 它们在某种程度上都非常有帮助,但不幸的是,我认为除了“简化/优化您的代码”之外,还有更多内容。分享一下我的一些渲染代码:
//fields defined when the program is initialized
Rectangle viewport;
//Texture with the size of the viewport
Texture fboTexture;
FBO fbo;
//called every frame
public void Render()
{
//bind the texture to the fbo
GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbo.handle);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, fboTexture,
TextureTarget.Texture2D, texture.TextureID, level: 0);
//Begin rendering in Ortho 2D space
GL.MatrixMode(MatrixMode.Projection);
GL.PushMatrix();
GL.LoadIdentity();
GL.Ortho(viewport.Left, viewport.Right, viewport.Top, viewport.Bottom, -1.0, 1.0);
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadIdentity();
GL.PushAttrib(AttribMask.ViewportBit);
GL.Viewport(viewport);
//Render the scene - this is really simple I render some quads using shaders
RenderScene();
//Back to Perspective
GL.PopAttrib(); // pop viewport
GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Modelview);
GL.PopMatrix();
//Detach the texture
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, fboTexture, 0,
0, level: 0);
//Unbind the fbo
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
GL.PushMatrix();
GL.Color4(Color.Black.WithAlpha(128)); //Sets the color to (0,0,0,128) in a RGBA format
for (int i = 0; i < 5; i++)
{
GL.Translate(-1, -1, 0);
//Simple Draw method which binds the texture and draws a quad at (0;0) with
//its size
fboTexture.Draw();
}
GL.PopMatrix();
GL.Color4(Color.White);
fboTexture.Draw();
}
所以我认为 fbo 和渲染到纹理上实际上没有任何问题,因为这不会导致程序在我的两张卡上变慢。以前我每帧都初始化 fbo,这可能是我的 Nvidia 卡变慢的原因,但现在当我预初始化所有内容时,无论有没有 fbo,我都会得到相同的 FPS。
我认为问题一般不在于纹理,因为如果我禁用纹理并仅渲染无纹理的四边形,我会得到相同的结果。而且我仍然认为我的集成卡在屏幕上仅渲染 7 个四边形时运行速度应该超过 40 FPS,即使它们覆盖了整个屏幕。
你能给我一些提示,告诉我如何实际分析这个并回发结果吗?那真的很有用。
编辑 2:
好的,我进行了一些实验,并设法获得了更好的性能。首先,我尝试使用着色器渲染最终的四边形——这并没有像我预期的那样对性能产生任何影响。
然后我尝试运行分析器。但据我所知,SlimTune 只是一个 CPU 分析器,它并没有给我想要的结果。然后我尝试了gDEBUgger。它与 Visual Studio 集成,后来我发现它不支持 .NET 项目。我尝试运行外部版本,但它似乎不起作用(但也许我玩得还不够)。
真正做到这一点的是,我不是直接将 7 个四边形渲染到屏幕上,而是首先将它们渲染到纹理上,再次使用 fbo,然后将最终纹理渲染到屏幕上一次。这使我的 fps 从 40 提高到 120。至少可以说,这似乎是一种古玩。为什么渲染到纹理方式比直接渲染到屏幕更快?尽管如此,感谢大家的帮助 - 看来我已经解决了我的问题。如果有人对这种情况提出合理的解释,我将不胜感激。
【问题讨论】:
-
由于您将 1024x1024 图像缩小,每个像素必须至少读取 4 个纹素。此外,每个像素必须经过 7 次读取-修改-写入操作。因此,总的来说,假设 32 位深度,每帧有 145 MiB 的内存传输。您的集成 GPU 的理论带宽为 21 GiB/s,它与 CPU 共享。因此,如果CPU 不访问任何内存。你的数字似乎并不过分。
-
顺便说一句,在着色器中获取 7 个样本并在 FBO 中写出一个片段是否有障碍?这将消除读取-修改-写入操作并在将纹理提取到 L2 缓存命中时转换 80-90% 的内存访问。
-
感谢您提供这些数字 - 看来问题确实出在 GPU 的性能上。我会做一些测试,运行一个 gpu 分析器,用着色器中的 7 个样本尝试这个建议,然后我会将我的结果发布回来。
-
请不要使用
GL.MatrixMode(MatrixMode.Projection);之类的方法,固定函数管道已经被太多人使用了。