【问题标题】:Monogame XNA low framerate from few Draw() calls少数 Draw() 调用导致 Monogame XNA 低帧率
【发布时间】:2013-06-07 09:07:30
【问题描述】:

我一直在使用 MonoGame 将我在 XNA 中制作的滚动射击游戏移植到 Linux 上。几乎一切都进行得很顺利,但我在一个特定的地方遇到了一个问题,调用 SpriteBatch.Draw() 会削弱帧速率。大多数游戏运行良好,没有任何打嗝。它一次吸引大量敌人和大量子弹而没有任何减速。然而,导致丢帧的部分是分层滚动背景。代码的相关部分在这里。

Level.cs:

public void Draw(SpriteBatch spriteBatch)
{
    foreach (ScrollingBackgroundLayer sbl in scrollingBackground)
    {
        sbl.Draw(spriteBatch);
    }
}

上面的“scrollingBackground”是一个ScrollingBackgroundLayers的列表,其相关部分是:

ScrollingBackgroundLayers.cs:

Vector2[] scrollingBackgroundImages;
public float DrawLayer;

public ScrollingBackgroundLayer(GameScene newScene, Texture2D texture, float scrollSpeed, Color color)
{
    layerColor = color;
    layerSpeed = scrollSpeed;
    layerTexture = texture;
    thisScene = newScene;

    Initialize();
}

public void Initialize()
{
    scrollingBackgroundImages = new Vector2[2];

    for (int i = 0; i < 2; i++)
    {
        scrollingBackgroundImages[i] = new Vector2(0, thisScene.ScreenArea.Height - (layerTexture.Height * i));
    }
}


public void Update(GameTime gameTime)
{
    for (int i = 0; i < scrollingBackgroundImages.Length; i++)
    {
        scrollingBackgroundImages[i].Y += (float)gameTime.ElapsedGameTime.TotalSeconds * layerSpeed;

        if (layerSpeed > 0 && scrollingBackgroundImages[i].Y >= thisScene.ScreenArea.Height)
        {
            scrollingBackgroundImages[i] = new Vector2(scrollingBackgroundImages[i].X, (scrollingBackgroundImages[i].Y - layerTexture.Height * 2));
        }
        else if (layerSpeed < 0 && scrollingBackgroundImages[i].Y + layerTexture.Height <= 0)
        {
            scrollingBackgroundImages[i] = new Vector2(scrollingBackgroundImages[i].X, (scrollingBackgroundImages[i].Y + layerTexture.Height * 2));
        }
    }
}

public void Draw(SpriteBatch spriteBatch)
{
    foreach (Vector2 sb in scrollingBackgroundImages)
    {
        spriteBatch.Draw(layerTexture, sb, null, layerColor, 0f, Vector2.Zero, 1f, SpriteEffects.None, DrawLayer);
    }
}

一旦我注释掉对 ScrollingBackgroundLayer.Draw() 的调用,所有帧速率问题都会消失,因此这似乎是一个很大的暗示,即问题来自 SpriteBatch 尝试绘制滚动层。显然,我想弄清楚为什么会这样。我研究了 SpriteBatches 的其他一些问题,我发现可能指向答案的最常见的事情是,每当更改纹理时都会创建一个新的 SpriteBatch,但即使将 SpriteSortMode 设置为 Texture 也没有任何改进。

帧速率下降发生在具有 7 个不同 ScollingBackgroundLayers 的游戏阶段,每个阶段绘制一个大约 700 像素宽和 900 像素高的单独纹理(每个层应该这样做不超过 2 次,以解决滚动效果)。我觉得我必须遗漏一些明显的东西,因为游戏有时会使用完全相同的过载渲染多达 2-300 颗子弹,所以另外 14 次调用应该是杯水车薪。这仅仅是尝试绘制太大的纹理以使 SpriteBatch 无法有效处理的问题吗?这在 Windows 版本上不是问题,所以我想知道是否没有特定于平台的解决方法,或者 MonoGame 的 Draw 实现是否效率低下。欢迎任何反馈。

感兴趣的人的完整来源:

Linux - https://github.com/cmark89/AsteroidRebuttal-Linux
Windows - https://github.com/cmark89/AsteroidRebuttal

编辑:我尝试对其进行更多修改。我更改了对 Draw() 的调用以确保目标矩形等于屏幕的可绘制区域,但没有明显的变化。我将纹理换成一个非常小的纹理,这样就消除了延迟,所以我想它确实与纹理的大小有关。

编辑 2:好吧,我最终只是硬着头皮切开了我正在使用的透明层(就像某种不懒惰的人)。它解决了这个问题,大概是因为它不再需要渲染大量无用的像素,但它仍然让我想知道为什么这只是 Linux 上的一个问题。尽管我暂时已经解决了我的问题,但我还是会暂时搁置一下,以防万一有人知道是什么导致了巨大的性能差异。

【问题讨论】:

  • 我真的不记得不同事物的性能成本,但强制 GPU 绘制那么多高分辨率纹理可能不是一个好主意,你应该能够以不同的方式进行设计.如果记忆对我有用,那么这些纹理在发送到 GPU 时最终将是 1024*1024 纹理(由于 2-required 的幂并在运行时重新制作成该大小)。
  • 这种情况下的问题是背景的每张图片滚动速度不同,或者透明度不同。其中一些我绝对可以切碎并创建在屏幕较小区域绘制的图层,但其中一些(主要是透明图层)并不那么容易。无论如何,我不知道 GPU 调整了两个纹理的非功率;也许我会调整它们的大小,看看它是否改变了什么。不管我打算做哪些优化,我真的很困惑为什么这个问题不会在 Windows 上发生。

标签: xna draw frame-rate monogame spritebatch


【解决方案1】:

在这种情况下,我建议您改用着色器来完成此操作。这样做你会看到你的表现飞涨。看看以下内容:

http://www.david-gouveia.com/scrolling-textures-with-zoom-and-rotation/

这样做,将减少大量 CPU 使用率,并使您的 GPU 完成大部分工作。

您可能还希望将纹理存储为 2 的幂,而不是 700x900。 (做类似512x512的事情)。您的图形管道具有与具有 2 次幂的纹理相关的性能提升。

【讨论】:

  • 我正在研究这个,但这对于我需要做的事情可能有点过分了。也许这只是因为着色器让我害怕。当我有机会尝试这个时,我会回复你。
  • 这并不过分,我向你保证它非常简单。你甚至不需要了解任何关于着色器的知识就可以让它工作;)
  • 由于我最终只使用了一个更简单的解决方法,因此没有尝试过这个,但由于这是我将其标记为已接受的唯一答案。如果您可以让效果文件工作,它确实看起来是一个非常好的解决方案(切线,这显然是对当前版本的 MonoGame 的一种考验,这就是我没有尝试过的原因)。
  • 你是对的。尝试在monogame中编写自定义着色器,目前有点狡猾。我实际上认为这个特殊的着色器甚至不兼容它。
  • 真可惜。在不同的项目中实施似乎是一个非常酷的想法。希望着色器兼容性在未来得到更多发展。这样我就有更多的动力不再被他们吓呆了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-17
相关资源
最近更新 更多