【问题标题】:Opengl - "fullscreen" texture rendering performance issueOpengl - “全屏”纹理渲染性能问题
【发布时间】: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。至少可以说,这似乎是一种古玩。为什么渲染到纹理方式比直接渲染到屏幕更快?尽管如此,感谢大家的帮助 - 看来我已经解决了我的问题。如果有人对这种情况提出合理的解释,我将不胜感激。

【问题讨论】:

  • 您可能会认为仅 7 个四边形的 200 FPS 也是不够的。根据this one 等流行的基准测试,您的集成卡实际上比独立卡慢了近三倍。这与您的结果(200 FPS 与 40 FPS)非常吻合。要分析您的代码,您应该尝试使用像 SlimTune 这样的分析器。 (本来打算推荐之前用过的NProf,现在不支持了。)
  • 由于您将 1024x1024 图像缩小,每个像素必须至少读取 4 个纹素。此外,每个像素必须经过 7 次读取-修改-写入操作。因此,总的来说,假设 32 位深度,每帧有 145 MiB 的内存传输。您的集成 GPU 的理论带宽为 21 GiB/s,它与 CPU 共享。因此,如果CPU 不访问任何内存。你的数字似乎并不过分。
  • 顺便说一句,在着色器中获取 7 个样本并在 FBO 中写出一个片段是否有障碍?这将消除读取-修改-写入操作并在将纹理提取到 L2 缓存命中时转换 80-90% 的内存访问。
  • 感谢您提供这些数字 - 看来问题确实出在 GPU 的性能上。我会做一些测试,运行一个 gpu 分析器,用着色器中的 7 个样本尝试这个建议,然后我会将我的结果发布回来。
  • 请不要使用GL.MatrixMode(MatrixMode.Projection);之类的方法,固定函数管道已经被太多人使用了。

标签: c# opengl opentk


【解决方案1】:

显然这是一个猜测,因为我没有看到或分析过您的代码,但我猜集成卡只是在为您的后期处理而苦苦挣扎(多次绘制纹理以实现您的“阴影”效果)。

我不知道你对这些概念的熟悉程度,如果我在这里有点冗长,很抱歉。

关于后期处理

后期处理是在将完成的场景显示在屏幕上之前将完成的场景渲染为纹理并对图像应用效果的过程。后处理的典型用途包括:

  • Bloom - 通过将明亮像素“渗透”到相邻的较暗像素中来更自然地模拟亮度。

  • 高动态范围渲染 - Bloom 的老大哥。场景被渲染为浮点纹理,允许更大的颜色范围(而不是通常的黑色 0 和全亮度 1)。屏幕上显示的最终颜色是使用屏幕上所有像素的平均亮度计算的。所有这一切的效果是相机的行为有点像人眼 - 在黑暗的房间里,明亮的光线(例如,透过窗户)看起来非常明亮,但是一旦你到外面,相机就会调整并且光线看起来只是如果你直视太阳,它会很亮。

  • Cel-shading - 修改颜色以提供卡通外观。

  • 运动模糊

  • 景深 - 游戏内的摄像头近似于真实的一个(或你的眼睛),其中只有一定距离内的物体是清晰的,其余的都是模糊的。

  • 延迟着色 - 一种相当高级的后处理应用程序,在渲染场景后计算光照。这会消耗大量视频 RAM(它通常使用多个全屏纹理),但可以快速将大量灯光添加到场景中。

简而言之,您可以将后期处理用于许多巧妙的技巧。可惜……

后处理有成本

后期处理很酷的一点是,它的成本与场景的几何复杂度无关——无论是绘制一百万个三角形还是绘制十几个三角形,都将花费相同的时间。然而,这也是它的缺点。即使您只是一遍又一遍地渲染四边形来进行后处理,渲染每个像素也是有成本的。如果你要使用更大的纹理,成本会更高。

专用显卡显然有更多的计算资源来应用后处理,而集成显卡通常可以应用的资源要少得多。正是由于这个原因,视频游戏上的“低”图形设置通常会禁用许多后期处理效果。这不会显示为 CPU 分析器的瓶颈,因为延迟发生在显卡上。 CPU 正在等待显卡完成后再继续执行您的程序(或者更准确地说,CPU 在等待显卡完成时正在运行另一个程序)。

如何加快速度?

  • 使用更少的通行证。如果将通行证减半,则进行后处理所需的时间减半。为此,

  • 使用着色器。由于我没有看到您在任何地方提及它们,因此我不确定您是否使用着色器进行后期处理。着色器本质上允许您使用类 C 语言编写函数(因为您在 OpenGL 中,您可以使用 GLSL 或 Cg),该函数在对象的每个渲染像素上运行。它们可以采用您喜欢的任何参数,并且对于后期处理非常有用。您可以使用着色器设置要绘制的四边形,然后您可以插入您希望在场景的每个像素上运行的任何算法。

【讨论】:

  • 感谢您提供的信息丰富的回答。我将所有内容渲染到纹理上的原因正是出于后期处理的目的。将来我会应用更先进的技术(包括你提到的一些技术),但现在我需要解决这个问题。你认为 7 个“全屏”四边形应该同样减慢汽车的速度吗?
  • 我绝对可以想象后处理是导致减速的一个重要因素。但是,唯一可以确定的方法是配置文件。您需要使用可以同时跟踪 GPU 性能和 CPU 性能的东西。虽然我自己没用过,gDEBugger 是一个用于分析 OpenGL 程序的常用工具。
  • 另一种情况是说如果你想要绽放一些效果而不影响其他场景对象。您将所有 sfx 渲染到一个单独的渲染目标中。并且您对渲染目标进行模糊处理或一些色调映射处理。但是 sfx 通常使用渲染目标的一小块区域,但您必须将其混合回全屏后缓冲区。有什么建议可以优化吗?
【解决方案2】:

看到一些代码会很好。如果两者之间的唯一区别是是否使用外部 GPU,则区别可能在于内存管理(即创建 FBO 的方式和时间等),因为将数据流式传输到 GPU 可能会很慢。尝试移动任何创建任何类型的 OpenGL 缓冲区或向其发送任何类型的数据以进行初始化的东西。如果不确切地了解您在做什么,我真的无法给出更详细的建议。

【讨论】:

  • 哦,是的,我已经广泛使用了 OpenTK,它与普通 OpenGL 之间的唯一区别是,由于 OpenTK 使用委托绑定方法的方式,GL 方法的成本略高。我也喜欢 OpenTK 将常量组织成枚举的方式,通过自动完成让一切变得更快......
  • 因为我已经将opengl包装到我自己的类中,所以给出代码并不容易。不过我明天会这样做,因为这里已经很晚了。现在我可以说我几乎没有在每一帧上做任何初始化——一切都是预先初始化的,我只重用纹理和 FBO
  • 即使是对您正在做的事情的高级概述也会有所帮助,如果您可以按照代码并按顺序记下 GL 方法调用或类似的东西,那就更好了。
【解决方案3】:

这不仅仅是你渲染的四边形的数量,我相信在你的情况下,它与你的视频卡必须做的三角形数量有关。

如前所述,进行全屏后期处理的常用方法是使用着色器。如果您想在集成卡上获得更好的性能并且不能使用着色器,那么您应该简化您的渲染例程。

确保您确实需要 Alpha 混合。在某些卡/驱动程序上,使用 Alpha 通道渲染纹理会显着降低性能。

一种降低全屏填充量的低质量方法是首先在另一个较小的纹理(例如,256x256 而不是 1024x1024)上执行所有阴影绘制。然后,您将使用该复合阴影纹理绘制一个四边形到您的缓冲区。这种方式而不是 7 个 1024x1024 四边形,您只需要 6 个 256x256 和一个 1024x1024。但你会失去决心。

另一种技术,我不确定它是否适用于您的情况,是预渲染您的复杂背景,这样您就必须在渲染循环中进行更少的绘制。

【讨论】:

  • 感谢您的回复。我尝试禁用 alpha 混合 - 不走运。我只使用着色器渲染到纹理上。之后,正如我在编辑中发布的那样,我只是将它渲染到屏幕上,而不进行着色器处理,因为我现在不需要。低质量的后期处理技术非常简洁,我将来会使用它,但我不认为这是减速的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多