【问题标题】:How to bypass SpriteBatch sorting? C# XNA如何绕过 SpriteBatch 排序? C# XNA
【发布时间】:2013-02-26 14:53:11
【问题描述】:

我正在 XNA 中开发一款“落沙”游戏。当然,这意味着必须绘制大量像素!它开始很好,每个像素使用透明的蓝色纯粹用于测试算法。在失去任何帧速率(稳定的 60 FPS)之前,我能够在屏幕上绘制大约 100,000 个像素,这超出了我的需要!然而,当我最终添加了不同颜色的像素(蓝色和红色)并且没有其他变化时,我的 FPS 呈指数级下降,在 FPS 下降到大约 10 之前,只允许在屏幕上绘制大约 10,000 个像素。经过一些实验后,我得出结论精灵批次最有可能有问题,尽管我可能完全错了,如果您想到不同的东西,请告诉我,因为精灵批次会自动排序并将像素混合在一起。我认为将两种颜色混合在一起的行为是我失去速度的地方。那么,我怎样才能绕过精灵批处理的方法呢?还是我完全脱离了基地?

我尝试过使用 SpriteSortMode 和 BlendMode,甚至每次精灵批处理调用只绘制彩色像素,但到目前为止没有运气。

此外,我在屏幕上绘制的像素从一开始就没有相同的坐标,因此不需要混合等。

    //Game Draw Method
    protected override void Draw(GameTime gameTime)
    {                      
        stopWatch.Start();          

        GraphicsDevice.Clear(Color.Black);

        spriteBatch.Begin();

        //backgroundSprite.Draw(spriteBatch);

        terrainSprite.Draw(spriteBatch);

        resourceMap.Draw(spriteBatch, spriteFont);            

        frameCounter++;

        string fps = string.Format("fps: {0}", frameRate);

        int totalint = 0;

        for (int i = 0; i < resourceMap.activeCoordinates.Count; i++)
        {
            totalint += resourceMap.activeCoordinates[i].Count;
        }

        string total = string.Format("resource count: {0}", totalint);

        spriteBatch.DrawString(spriteFont, fps, new Vector2(33, 33), Color.White);
        spriteBatch.DrawString(spriteFont, total, new Vector2(33, 45), Color.White);
        spriteBatch.DrawString(spriteFont, totalTime, new Vector2(33, 17),    Color.White);            
        spriteBatch.DrawString(spriteFont, totalTime, new Vector2(33, 17),  Color.White); 


        spriteBatch.End();
        stopWatch.Stop();
        totalTime = "Draw Time:     " + stopWatch.Elapsed;
        stopWatch = new Stopwatch();


        base.Draw(gameTime);
    }

    //resourceMap.Draw(SpriteBatch spriteBatch, SpriteFont font) from Game Draw
    public void Draw(SpriteBatch spriteBatch, SpriteFont font)
    {
        //spriteBatch.End();
        //this was originally one for loop to draw all pixels, but was separated to draw each different color pixel after discovering performance issue
        for (int c = 0; c < activeCoordinates.Count(); c++)
        {
            //spriteBatch.Begin();
            for (int i = 0; i < activeCoordinates[c].Count; i++)
            {
                mapArray[(int)activeCoordinates[c][i].X][(int)activeCoordinates[c][i].Y].Draw(spriteBatch);
            }
            //spriteBatch.End();
        }
        //spriteBatch.Begin();
        spriteBatch.DrawString(font, timeInMilli, new Vector2(33, 0), Color.White);
    }

同样,对代码所做的唯一更改是在要绘制的像素列表中添加不同的颜色,因此这里的代码运行良好(以大约 60fps 的速度绘制 100,000 个像素),直到我添加了额外的颜色。

感谢您的帮助!

【问题讨论】:

  • 代码就像一个带有draw方法的for循环一样简单。它不应该像解释那样有帮助,但是你去了^^。
  • 您可以为mapArray[][].Draw 添加代码吗?尽管值得指出的是,将每个像素绘制为自己的单独精灵很可能是错误的解决方案。
  • 我认为您可以通过绘制有色白色像素来解决您的问题,这是所有沙粒的一种纹理,但颜色不同。它可能会提高帧速率,尽管问题可能出在您尚未发布的其他内容上。但是,是的,为每个沙粒绘制一个像素纹理是一种非常缓慢的方法。尝试用谷歌搜索 powder 游戏以及他们是如何做到的。
  • 作为一个猜测,我无法从您的代码中看到,如果您绘制的都是蓝色,那么所有的红色,或者如果您将它们作为混合绘制。如果可以的话,我会先画一种颜色,然后再画另一种颜色。根据逻辑,这可能是不可能的,但如果您可以最大限度地减少 Spritebatch 抓取纹理的次数,可能会有所帮助。
  • @Jastill 我认为如果您将 SpriteSortMode 设置为 Texture,sprite batch 会为您完成,但如果某些纹理重叠可能并不酷,因为它们的顺序可能会变得不受欢迎

标签: c#-4.0 xna


【解决方案1】:

好的,我会尽我所能给出最好的答案,但我受到您发布的代码数量的限制。首先,我建议您访问this answer,了解影响性能的各种因素的概述。

SpriteBatch 可能遇到的问题是您强制它启动太多批次。每次更改纹理时,它都必须开始批处理 - 听起来您正在为不同的像素使用不同的纹理,然后以随机顺序绘制它们。

SpriteBatch(使用SpriteSortMode.Texture)中按纹理排序本身可能是一项昂贵的操作。您自己的用于对类似纹理的精灵进行分组的代码可能很慢或不正确 - 但我无法用您提供的代码来判断。

比使用许多纹理更好的情况是使用单个白色纹理,然后使用 tint 参数将每个精灵动态着色为所需的颜色。然后每个精灵都可以在一个批次中发送。

但是,以及为这么多精灵设置顶点的大量处理,我们应该考虑实际发送到 GPU 的数据量。每个精灵有 4 个顶点,每个顶点都有一个位置、一个颜色和一个纹理坐标。每像素高达 96 字节。而且每帧都必须发送到 GPU!

另一方面,纹理每个像素只有 4 个字节。所以按理说它应该更快。

有两件事可能会减慢速度:

首先,如果您拨打Texture2D.SetData 的次数超出了您的要求。每次调用SetData 都需要相当大的开销。如果你每像素调用一次,它会非常慢。而是为整个图像调用一次。这应该让它足够快以供使用 - 尽管我自己没有对此进行过广泛的测试。

另一种可能性是您的 GPU 有一个深管道(取决于硬件)并且仍在使用您的纹理来渲染第 N 帧,而您正在尝试为第 N+1 帧更新它。这会强制 GPU 在接受新更改之前完成渲染帧 N。虽然这似乎不太可能成为问题,除非您正在做其他非常 GPU 密集型的事情,或者您的图形驱动程序正在做一些愚蠢的事情。

【讨论】:

  • 不幸的是,所有这些都是我已经修复的问题,但我需要的最终修复是一次将纹理数据设置一个像素,而不是重置整个纹理。在对一次设置一个像素进行了一些研究之后,我发现所有帖子都说这是不可能的,所以我放弃了。然后我终于想出了怎么做!问题已解决,但您涵盖了很多重要信息,因此您的答案大多是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多