如何绕过SpriteBatch排序? C#XNA

时间:2013-02-24 01:19:34

标签: c#-4.0 xna

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

我已经尝试过使用SpriteSortMode和BlendMode,甚至每次sprite批量调用只绘制彩色像素,但到目前为止还没有运气。

此外,我绘制到屏幕上的像素从不具有相同的坐标,因此不需要混合等。

    //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个像素),直到我添加了额外的颜色。

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

好的,我会尽可能给出最好的答案,但我受到你发布的代码数量的限制。首先,我将引导您this answer了解影响效果的各种因素。

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

SpriteBatch(带SpriteSortMode.Texture)内按纹理排序本身就是一项昂贵的操作。你自己编写类似纹理的精灵的代码可能很慢或不正确 - 但是我不知道你给出的代码。

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

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

另一方面,纹理每像素仅 4字节。所以它应该更快。

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

首先,如果你打电话给Texture2D.SetData超过你所需要的。每次调用SetData都需要相当大的开销。如果你每个像素调用一次它将会非常慢。改为为整个图像调用一次。那个应该使它足够快以便可用 - 虽然我没有广泛地测试它。

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