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

时间:2012-10-30 00:11:56

标签: c# opengl opentk

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

我所做的实际上是创建一个具有屏幕大小的纹理,使用FBO将我的场景渲染到该纹理上,然后使用不同的偏移渲染纹理几次以获得一种“阴影”。但是当我这样做时,我在使用集成显卡时会出现大幅降低的性能。

总而言之,我将7个四边形渲染到整个屏幕上(背景图像,带有黑色“色调”的5个“阴影图像”和具有真实颜色的相同纹理)。我正在使用尺寸为1024x1024的RGBA纹理,并将它们安装在900x700窗口中。当我没有渲染纹理时,我得到200 FPS,而当我做渲染时,我得到34 FPS(在两种情况下,我实际创建纹理并将场景渲染到它上面)。我觉得这很奇怪,因为我只渲染了7个四边形。一个奇怪的事情是,当我运行CPU分析器时,它并不表示这是瓶颈(我知道opengl使用管道架构,这种情况可能发生,但大多数情况下它不会发生)。

当我使用外部视频卡时,当我进行上述测试时,我获得了200 FPS。但是当我禁用场景渲染到纹理并禁用纹理渲染到屏幕上时,我得到~1000 FPS。这只发生在我的外部视频卡上 - 当我使用集成的视频卡禁用FBO时,我得到相同的200 FPS。这真让我困惑。

任何人都可以解释发生了什么,以及上述数字是否合适?

集成显卡 - 英特尔高清显卡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卡速度减慢的原因,但现在当我预先初始化所有内容时,无论是否有fbo我都会得到相同的FPS。

我认为问题不在于纹理,因为如果我禁用纹理并只渲染无纹理的四边形,我会得到相同的结果。而且我认为我的集成卡在屏幕上只渲染7个四边形时运行速度应该超过40 FPS,即使它们覆盖了整个卡。

您能否告诉我一些如何实际剖析并回复结果的提示?这将非常有用。

编辑2:

好的,我尝试了一下,并设法获得了更好的性能。首先,我尝试使用着色器渲染最终的四边形 - 这对我的预期没有任何影响。

然后我尝试运行一个分析器。但据我所知,SlimTune只是一个CPU分析器,它没有给我我想要的结果。然后我尝试了gDEBugger。它与visual studio集成,后来我发现它不支持.NET项目。我尝试运行外部版本但它似乎没有用(但也许我还没有玩过它)。

真正做到这一点的是,不是直接将7个四边形渲染到屏幕上,而是首先在纹理上渲染它们,再次使用fbo,然后将最终纹理渲染到屏幕上一次。这使我的fps从40增加到120.再次看起来这似乎有点好奇。为什么渲染到纹理的方式比直接渲染到屏幕更快?不过感谢大家的帮助 - 似乎我已经解决了我的问题。如果有人能对这种情况做出合理的解释,我将非常感激。

3 个答案:

答案 0 :(得分:4)

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

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

关于后处理

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

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

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

  • Cel-shading - 修改颜色以呈现卡通式外观。

  • 动态模糊

  • 景深 - 游戏中的相机近似于真实的(或你的眼睛),其中只有特定距离的物体在焦点上,其余物体模糊。

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

简而言之,你可以使用后处理来获得很多巧妙的技巧。不幸的是...

后处理有成本

关于后期处理的一个很酷的事情是它的成本与场景的几何复杂性无关 - 无论你画了一百万个三角形还是画了十几个三角形都需要相同的时间。然而,这也是它的缺点。即使您只是反复渲染四边形来进行后处理,渲染每个像素也会产生成本。如果你使用更大的纹理,成本会更高。

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

你如何加快速度?

  • 使用较少的传球。如果将传球减半,则可以将后期处理所需的时间减半。为此,

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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

这不只是你渲染的四边形的数量,而且我相信你的情况更多的是你的视频卡需要做的三角形填充。

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

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

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

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