Opengl - “全屏”纹理渲染性能问题

3

我正在使用OpenGL编写一个2D游戏,但在渲染覆盖整个窗口的一些纹理时遇到了性能问题。

我的做法是创建一个与屏幕大小相同的纹理,使用FBO将场景渲染到该纹理上,然后使用不同的偏移量多次渲染该纹理以获得一种“阴影”效果。但是当我使用集成显卡时,性能会大幅下降。

总体而言,我在整个屏幕上渲染了7个四边形(背景图像、带有黑色“着色”的5个“阴影图像”和相同的真彩纹理)。我使用大小为1024x1024的RGBA纹理,并将其适应于900x700的窗口。当我不渲染纹理时,我可以获得200帧/秒,而当我渲染纹理时,无论是哪种情况,都只有34帧/秒(实际上我都会创建纹理并将场景渲染到其中)。我认为这很奇怪,因为本质上我只渲染了7个四边形。另一个奇怪的事情是,当我运行CPU分析器时,它并没有表明这是瓶颈(我知道OpenGL使用了管道架构,这种情况可能会发生,但大多数情况下却不会)。

当我使用外部显卡时,上述测试结果始终为200帧/秒。但是当我禁用将场景渲染到纹理并禁用将纹理渲染到屏幕上时,我可以获得约1000帧/秒。这仅适用于我的外部显卡-当我使用集成显卡禁用FBO时,我也只能获得相同的200帧/秒。这让我感到非常困惑。

有谁能解释一下发生了什么,并且上述数字是否正确?

集成显卡-Intel HD Graphics 4000

外部显卡-NVIDIA GeForce GTX 660M

P.S.我使用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卡变慢的原因,但现在当我预先初始化所有内容时,我得到了相同的FPS,无论是否使用fbo。
我认为问题不在于纹理,因为如果我禁用纹理,只渲染未经纹理处理的四边形,我会得到相同的结果。而且我仍然认为,即使它们覆盖整个屏幕,我的集成显卡在仅渲染7个四边形时也应该运行得更快,甚至快过40 FPS。
你能给我一些提示,如何实际分析这个问题并返回结果吗?那将非常有用。
好的,我进行了一些实验,并取得了更好的性能。首先我尝试使用着色器渲染最终的四边形-这对性能没有影响,正如我所预期的那样。
然后我尝试运行分析器。但据我所知,SlimTune只是一个CPU分析器,它没有给我想要的结果。然后我尝试使用gDEBugger。它与Visual Studio集成,后来我发现它不支持.NET项目。我尝试运行外部版本,但似乎没有起作用(也许我还没有充分使用)。
真正起作用的是,而不是直接将7个四边形渲染到屏幕上,我首先将它们渲染到纹理上,再次使用fbo,然后一次将最终纹理渲染到屏幕上。这使我的fps从40提高到了120。同样,这似乎非常奇怪。为什么渲染到纹理比直接渲染到屏幕要快得多?无论如何,感谢大家的帮助-看来我已经解决了我的问题。如果有人能对这种情况提出合理的解释,我将不胜感激。

1
你可能认为仅7个四边形的200 FPS是不够的。根据像这个这样的流行基准测试,您的集成显卡实际上比独立显卡慢近三倍。这非常符合您的结果(200 FPS vs 40 FPS)。要对代码进行分析,请尝试使用 SlimTune 这样的分析工具。(我本来打算推荐以前用过的 NProf,但它已经不再支持了。) - MisterMetaphor
1
由于您将1024x1024图像缩小,因此每个像素必须至少读取4个纹理。此外,每个像素必须经过7次读取-修改-写入操作。因此,总体而言,假设深度为32位,则每帧需要145 MiB的内存传输。您的集成GPU具有21 GiB/s的理论带宽,它与CPU共享。因此,“理论”极限(尽管没有考虑缓存,但我从未见过CPU / GPU接近其“理论”幻想规格)是147FPS,如果CPU不访问任何内存。您的数字似乎并不过分。 - Damon
顺便问一下,在着色器中获取7个样本并将一个片段写入您的FBO是否有障碍?这将消除读取-修改-写入操作,并将80-90%的内存访问转换为从纹理获取L2缓存命中。 - Damon
谢谢这些数字 - 看起来问题确实是GPU的性能。我将进行一些测试,运行GPU分析器,尝试在着色器中使用7个样本的建议,然后再发布我的结果。 - Milcho
请不要使用 GL.MatrixMode(MatrixMode.Projection); 这样的方法,因为太多人使用了固定功能管线。 - Felix K.
很高兴你把事情搞定了!为了澄清,当我建议使用着色器时,我是在问你是否可以使用着色器以更少的步骤实现相同的效果,而不是试图暗示仅仅使用它们来做同样的事情会导致加速。 - Matt Kline
3个回答

6
显然,这只是我的猜测,因为我没有看到或分析过你的代码,但我猜想集成显卡可能只是在处理后期效果(将纹理绘制多次以实现“阴影”效果)时遇到了困难。
对于这些概念,我不知道你的熟悉程度,所以如果我表达得有点啰嗦,请见谅。
关于后期处理
后期处理是将渲染场景完整生成纹理后,在显示在屏幕上前对图像应用效果的过程。后期处理的典型用途包括:
- Bloom - 通过将明亮像素“渗透”到周围较暗区域来更自然地模拟亮度。 - 高动态范围(HDR)渲染 - Bloom 的姐妹篇。场景被渲染到浮点纹理中,允许更大的色彩范围(而不是通常的黑色为0和全亮度为1)。显示在屏幕上的最终颜色使用屏幕上所有像素的平均亮度计算。所有这些的效果是相机的操作类似于人眼 - 在黑暗的房间里,亮光(例如,通过窗户)看起来非常亮,但一旦你走到外面,相机会调整,只有直接盯着太阳看时光才会那么亮。 - 卡通渲染 - 修改颜色以使其具有卡通样式的外观。 - 运动模糊 - 景深 - 游戏中的相机近似于真实相机(或您的眼睛),只有在一定距离内的物体才处于焦点状态,其余部分会模糊。 - 延迟渲染 - 后期处理的一种相当先进的应用程序,在场景渲染完成后计算照明。这需要大量的视频RAM(通常使用几个全屏幕纹理),但可以快速添加大量灯光到场景中。
简而言之,你可以使用后期处理进行许多有趣的技巧。不幸的是,它也有代价。
后期处理会消耗性能
后期处理的好处是,它的成本与场景的几何复杂性无关 - 无论你绘制了一百万个三角形还是只绘制了十几个,它都需要相同的时间。然而,这也是它的缺点。即使你只是再次渲染一个四边形进行后期处理,每个像素的渲染成本仍然很高。如果你使用更大的纹理,则成本会更高。
显卡显然拥有更多的计算资源来应用后期处理,而集成显卡通常只有很少的资源可用。这就是为什么电子游戏的“低”图形设置通常会禁用许多后期处理效果的原因。这不会在CPU分析器中显示瓶颈,因为延迟发生在显卡上。 CPU在等待显卡完成之前继续运行程序(更准确地说,CPU在等待显卡完成时运行另一个程序)。
如何加速?
- 尽量减少需要进行后期处理的像素数量。 - 降低后期处理效果质量或完全禁用它们。 - 使用专用显卡而不是集成显卡
  • 使用较少的步骤。如果你减半步骤,那么做后处理所需的时间也会减半。为此,

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


感谢您提供这么详细的答案。我将所有内容都渲染到纹理上,正是为了后期处理的目的。未来我会应用更高级的技术(包括您提到的一些技术),但现在我需要解决这个问题。您认为7个“全屏”四边形会减慢汽车的速度吗? - Milcho
我可以想象后处理是导致减速的一个重要因素。然而,确保的唯一方法是进行分析。您需要使用能够跟踪GPU性能和CPU性能的工具。虽然我自己没有使用过,但gDEBugger是用于分析OpenGL程序的常见工具。 - Matt Kline
另一种情况是,如果您想要绽放一些效果而不影响其他场景对象。您可以将所有的SFX渲染到单独的渲染目标中。然后对渲染目标进行模糊或进行一些色调映射处理。但是,SFX通常只使用渲染目标的一小部分区域,但您必须将其混合回全屏背景缓冲区。有什么优化建议吗? - Joey.Z

1

看到一些代码会很好。如果两者之间唯一的区别是使用外部GPU还是不使用,那么差异可能在于内存管理(即何时以及如何创建FBO等),因为将数据流式传输到GPU可能会很慢。尝试将任何创建任何类型的OpenGL缓冲区或将任何类型的数据发送到其中的内容移动到初始化中。如果没有看到您正在做什么,我无法给出更详细的建议。


哦,是的,我广泛使用了OpenTK,它与普通的OpenGL唯一的区别在于,由于OpenTK使用委托来绑定方法,GL方法略微更昂贵。我也喜欢OpenTK将常量组织成枚举的方式,这样自动完成就可以让一切变得更快... - Robert Rouhani
由于我已经将OpenGL封装到自己的类中,因此很难提供代码。不过,由于现在已经很晚了,明天我会提供代码。目前,我可以说的是,在每个帧上,我几乎不进行任何初始化 - 一切都是预先初始化的,我只重复使用纹理和FBO。 - Milcho
即使只是一个高级概述也会有所帮助,更好的方法是您可以跟踪代码并按顺序记录GL方法调用或类似内容。 - Robert Rouhani

1

这不仅仅是关于你渲染的四边形数量,我认为在你的情况下,更多的是与你的视频卡填充三角形的数量有关。

正如所提到的,全屏后处理的常见方法是使用着色器。如果您想在集成卡上获得更好的性能并且无法使用着色器,则应简化渲染例程。

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

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

另一种技术,我不确定它是否适用于您的情况,是预先渲染复杂背景,以便在渲染循环中需要绘制的内容更少。


谢谢回复。我尝试禁用了alpha混合,但没有成功。我只是使用着色器来渲染纹理。然后,就像我在编辑中发布的那样,我只是将其渲染到屏幕上,没有进行任何着色器处理,因为现在我不需要。低质量的后处理技术非常好,我将来会使用它,但我不认为这是减速的原因。 - Milcho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接