缓慢的C++ DirectX 2D游戏

4

我是C++和DirectX的新手,来自XNA。 我开发了一个类似于飞行直升机的游戏。 我创建了一个名为Wall的类,在游戏运行时绘制所有的墙壁。 在XNA中,我将墙壁存储在ArrayList中,在C++中我使用了vector。 在XNA中,游戏运行非常快,在C++中运行非常慢。 以下是C++代码:

void GameScreen::Update()
{
    //Update Walls
    int len = walls.size();
    for(int i = wallsPassed; i < len; i++)
    {
        walls.at(i).Update();
        if (walls.at(i).pos.x <= -40)
            wallsPassed += 2;
    }
}

void GameScreen::Draw()
{
    //Draw Walls
    int len = walls.size();
    for(int i = wallsPassed; i < len; i++)
    {
        if (walls.at(i).pos.x < 1280)
            walls.at(i).Draw();
        else
            break;
    }
}

在Update方法中,我将X值减少4。 在Draw方法中,我调用sprite->Draw(Direct3DXSprite)。 这是游戏循环中唯一运行的代码。 我知道这是一个糟糕的代码,如果你有改进的想法,请帮忙。 谢谢,对我的英语表示抱歉。

这里的代码看起来相当聪明。问题很可能在于渲染管道和优化标志。 - aramadia
1
我会说你没有打开优化标志。 - Khaled Alshaya
知道它有多慢可能很有用。我们是指半速还是数量级? - jk.
8个回答

8
尝试使用[]运算符替换所有at()函数的出现。例如:
 walls[i].Draw();

然后打开所有优化。[]和at()都是函数调用-为了获得最大的性能,您需要确保它们被内联,这就是提高优化级别所做的。

您还可以对墙壁对象进行一些最小的缓存-例如:

 for(int i = wallsPassed; i < len; i++)
 {
    Wall & w = walls[i]; 
    w.Update();
    if (w.pos.x <= -40)
        wallsPassed += 2;
 }

[]at() 的主要区别是什么?编辑:哦,我刚找到答案了:at() 会检查边界。 - dreamlax
1
没有实际的区别,[]操作符被重载了,所以它只是一种简写形式。此外,我给它点了踩,因为这种优化()vs [] 是微不足道的。 - aramadia
8
at() 会进行范围检查,如果你不需要它,这将浪费时间并且毫无意义。而内联操作符[](或者 at())的效果非常显著。 - anon
正如dreamlax所指出的那样,at() 进行了检查而 [] 没有。因此,优化可能远非微不足道,因为您避免了每次数组访问时的条件语句。点赞。 - Peter
2
但是相比于实际绘图,一个或两个内联的[]操作的影响将是微不足道的。 - Thomas
显示剩余9条评论

2

尝试缩小性能问题的原因(也称为分析)。 我会尝试仅绘制一个对象,同时继续更新所有对象。 如果速度突然变快,则可能是DirectX绘图问题。

否则,请尝试绘制所有对象,但仅更新一堵墙。 如果速度更快,则您的update()函数可能太昂贵。


1
  • '快'有多快?
  • '真的很慢'有多慢?
  • 你正在绘制多少个精灵?
  • 每个精灵作为图像文件有多大,在屏幕上绘制的像素是多少?
  • 当你改变绘制的精灵数量时,性能如何扩展(在XNA/C++中)?
  • 如果不更新而进行绘制,或者反之,会有什么不同?

1
也许你只是忘记打开发布模式了 :) 我以前也遇到过这样的问题 - 我以为我的代码因为调试模式而非常慢。如果不是这个问题,你可能会在渲染部分或者对象数量过多方面遇到问题。你提供的代码看起来很好...

0

你尝试过使用多个缓冲区(也称为双缓冲)来处理位图吗?

典型的情况是在一个缓冲区中绘制,然后在第一个缓冲区被复制到屏幕时,在第二个缓冲区中绘制。

另一种技术是在内存中拥有一个巨大的“逻辑”屏幕。在物理显示器上绘制的部分是视口视图,它是逻辑屏幕中的一个小区域。移动背景(或屏幕)只需要在图形处理器的一部分进行复制即可。


0

您可以帮助批处理精灵绘制调用。假设您的绘制调用使用相关参数调用了ID3DXSprite :: Draw的唯一实例。

通过调用ID3DXSprite :: Begin(设置D3DXSPRITE_SORT_TEXTURE标志)并在完成所有渲染后调用ID3DXSprite :: End,您可以获得大大改进的性能。然后,ID3DXSprite将按纹理对所有精灵调用进行排序,以减少纹理切换的数量并将相关调用批处理在一起。这将极大地提高性能。

不过,如果没有看到您的Update和Draw调用的内部情况,很难再说更多。以上只是猜测...


0

用不同的绘制调用来绘制每一堵墙是一个不好的想法。尝试将数据批处理到单个顶点缓冲区/索引缓冲区中,并将它们发送到单个绘制中。这是一个更加明智的想法。

无论如何,为了了解为什么速度变慢,请使用一些CPU和GPU(PerfHud、Intel GPA等)首先知道瓶颈在哪里(如果是CPU还是GPU)。然后你可以努力减轻问题。


0

查找您的墙列表不太可能是您减速的源头。在3D中绘制对象的成本通常是限制因素。

重要的部分是您的绘制代码,您用于创建DirectX设备的标志以及您用于创建纹理的标志。我的猜测是...检查您是否将设备初始化为HAL(硬件3D)而不是REF(软件3D)。

此外,您正在绘制多少个精灵?每个绘制调用都有相当大的开销。如果您每帧绘制超过几百个,那么这将是您的限制因素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接