XNA - 合并精灵以获得更好的绘图性能?

时间:2009-10-28 12:53:25

标签: performance xna 2d sprite

我在某处读到,当渲染大量3D对象时,可以将它们合并形成一个巨大的网格,这样就只能进行一次绘制调用。因此,让引用:“GPU做它的魔力”,而 CPU可以免费进行其他调用而不是绘制。

那么就我的问题而言,在 2D环境中考虑性能是否可行?

例如,假设我们有一个简单的tile系统,而不是为视图中的每个tile进行绘制调用,而是合并所有tile 以形成一个大的sprite,然后调用一个draw在它上面。

对此事的任何见解 - 提示,链接或诸如此类 - 都非常受欢迎,因为我之前没有图形性能方面的经验。

修改:对不起,我的解释很糟糕。我正在创造一个供个人使用的瓷砖发动机,并希望它尽可能多样化。因此,我希望优化,以防万一我必须在不久的将来绘制大量的瓷砖。

使用了一个瓷砖表,但我对这个问题的意思是,将要从工作表中绘制的所有瓷砖合并到一个新的Texture2D中是否会获得性能。例如:

我们在屏幕上绘制了128x72个瓷砖。我们不是循环遍历所有图块并为每个要绘制的图块调用绘图,而是将所有图块合并为一个大小为1280x720的新图形并绘制它。这样,draw()方法每帧只会被调用一次。我的问题是否会提高性能,因为它会在执行3D时将3d对象合并为单个网格。

因为我收集的信息是调用draw()是一个性能值,应该尽可能少地调用。有人确认或否认? :)

4 个答案:

答案 0 :(得分:10)

我对XNA和3D没有任何经验,但我会给你一些关于2D游戏的建议。我花了一些时间在今年年初在XNA创建一个磁贴引擎,并想知道同样的事情。我认为这里的简短答案是肯定的,如果你担心性能,将你的瓷砖组合成一个更大的精灵是一个好主意。但是,如果你感兴趣,还有更长的答案。

一般来说,在性能优化方面,答案几乎总是“不要这样做”。如果您确定需要针对性能进行优化,那么下一个答案几乎总是“不要这样做”。最后,如果您尝试优化性能,那么您可以做的最重要的事情是使用基准测试来收集更改前后性能的精确度量。没有它,你不知道你是否成功了!

现在,更多地涉及到2D游戏,我了解到我在我的磁贴引擎中看到了更好的性能,我切换的纹理越少。例如,假设我有草砖和砾石砖。如果这些是内存中的两个独立纹理,我绘制草砖,然后是砾石砖,然后是草砖到屏幕,GPU将加载草纹理,然后将其切换出来加载砾石纹理,然后切换草纹理回来画另一个草瓦。这很快就杀死了性能!解决这个问题的最简单方法是使用spritesheet,将草和砾石瓷砖放入一个纹理中,然后告诉SpriteBatch每次都从同一纹理上的不同区域进行绘制。

另一件需要考虑的事情是,您将同时在屏幕上绘制多少个图块。我不记得具体,但我一次画了数千个瓷砖(在缩小的视图中)。我注意到,当我使用更大的瓷砖并减少它们时,正如您在问题中所建议的那样,性能也得到了提升。这并没有我在上一段中描述的那么大,我仍然鼓励你测量不同实现产生的性能变化。此外,如果您只绘制十几个或几百个瓷砖,那么现在尝试优化它可能是不值得的(参见第2段)。

只是你知道我并没有完全做到这一点,这里是Shawn Hargreaves关于纹理交换,spritesheets等的帖子的链接。在XNA论坛上可能有更好的帖子以及Shawn Hargreaves的博客如果你搜索这个主题。

http://forums.xna.com/forums/p/24254/131437.aspx#131437

<强>更新

由于您更新了问题,请让我更新我的帖子。我决定只对一些样本进行基准测试,以便了解性能差异可能是什么。在我的Draw()函数中,我有以下内容:

        GraphicsDevice.Clear(Color.CornflowerBlue);

        Stopwatch sw = new Stopwatch();
        sw.Start();

        spriteBatch.Begin();

#if !DEBUG
        spriteBatch.Draw(tex, new Rectangle(0, 0, 
                         GraphicsDevice.Viewport.Width,
                         GraphicsDevice.Viewport.Height), 
                         Color.White);            
#else
        for (int i = 0; i < 128; i++)
            for (int j = 0; j < 72; j++)
            {
                Rectangle r = new Rectangle(i * 10, j * 10, 10, 10);
                spriteBatch.Draw(tex, r, r, Color.White);
            }
#endif
        spriteBatch.End();

        sw.Stop();

        if (draws > 60)
        {
            numTicks += sw.ElapsedTicks;
        }
        draws++;

        if (draws % 100 == 0)
            Console.WriteLine("avg ticks: " + numTicks / (draws - 60));

        base.Draw(gameTime);

只需删除“#if!DEBUG”语句中的感叹号即可在两种方法之间切换。我跳过前60次抽签,因为他们包括一些初始设置(不确定是什么)并且偏向平均值。我下载了一个1280x720图像,对于顶级测试用例,我只画了一次。对于底部测试用例,我在平铺中绘制了一个源图像,128x72就像您在问题中询问的那样。结果如下。

绘制一张图片

avg ticks: 68566
avg ticks: 73668
avg ticks: 82659
avg ticks: 81654
avg ticks: 81104
avg ticks: 84664
avg ticks: 86626
avg ticks: 88211
avg ticks: 87677
avg ticks: 86694
avg ticks: 86713
avg ticks: 88116
avg ticks: 89380
avg ticks: 92158

绘制128x72图块:

avg ticks: 7902592
avg ticks: 8052819
avg ticks: 8012696
avg ticks: 8008819
avg ticks: 7985545
avg ticks: 8028217
avg ticks: 8046837
avg ticks: 8291755
avg ticks: 8309384
avg ticks: 8336120
avg ticks: 8320260
avg ticks: 8322150
avg ticks: 8381845
avg ticks: 8364629

正如你所看到的,那里有几个数量级的差异,所以它非常重要。测试这种东西非常简单,我建议你为你的特定设置运行自己的基准测试,以考虑我可能错过的东西。

答案 1 :(得分:3)

显卡切换纹理越少,性能越好。如果您愿意,可以在SpriteBatch.Begin()方法中按纹理排序,以便图形卡尽可能少地切换纹理。

我把瓷砖/精灵放在床单上才有意义。例如,一个字符可以在一个薄片中,一个瓷砖层可以在同一个薄片中。到目前为止,这种方法运作良好。

我会说,直到你必须要优化。如果您的游戏运行速度足够快,那么为什么还要进行优化呢。

答案 2 :(得分:1)

  

矩形r =新矩形(i * 10,j * 10,10,10);

创建许多新对象将调用垃圾收集,这可能会减慢很多! 你不应该在大循环中分配新对象:

答案 3 :(得分:0)

表演不是普遍的命令,也不是上帝的诫命;这是达到目的的手段。如果你所拥有的表现足够好,那么你在改善其表现方面几乎无法获得任何好处。保持“始终保持最佳性能”是过早优化的定义,它会引起比预防更多的麻烦。