为什么在OnPaint绘图比图像绘图更快?

7

我正在寻找一种加速我的游戏引擎绘图的方法,因为当前这是一个重要的瓶颈,导致游戏速度变慢。我正准备将其转换为XNA,但是我刚刚注意到了一些问题。

比如说,我有一个已经加载好的小图像。

    Image img = Image.FromFile("mypict.png");

我们在屏幕上有一个picturebox,我们想要在上面绘制内容。因此我们需要一个处理程序。
    pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);

我希望我们加载的图片能够在picturebox上平铺(毕竟这是一个游戏)。为什么这段代码会出问题:

    void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        for (int y = 0; y < 16; y++)
            for (int x = 0; x < 16; x++)
                e.Graphics.DrawImage(image, x * 16, y * 16, 16, 16);
    }

比这段代码快25倍以上:

    Image buff = new Bitmap(256, 256, PixelFormat.Format32bppPArgb); // actually a form member
    void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        using (Graphics g = Graphics.FromImage(buff))
        {
            for (int y = 0; y < 16; y++)
                for (int x = 0; x < 16; x++)
                    g.DrawImage(image, x * 16, y * 16, 16, 16);
        }
        e.Graphics.DrawImage(buff, 0, 0, 256, 256);
    }

为了排除显而易见的问题,我已经尝试注释掉最后一个e.Graphics.DrawImage(这意味着我看不到任何东西,但是可以摆脱第一个示例中没有的调用)。我还在第一个示例中保留了using块(不必要),但它仍然非常快。我设置了g的属性以匹配e.Graphics - 例如InterpolationMode,CompositingQuality等,但无论我做什么都无法弥合这种性能差距。我找不到两个Graphics对象之间的任何区别。怎么回事?
我的测试使用System.Diagnostics.Stopwatch表明,第一个代码片段的运行速度约为7100 fps,而第二个代码片段的运行速度仅为280 fps。我的参考图像是VS2010ImageLibrary\Objects\png_format\WinVista\SecurityLock.png,大小为48x48像素,并将其修改为72 dpi而不是96,但这些也没有任何区别。
3个回答

1

当你在屏幕上绘制时,操作系统能够利用图形适配器中的特殊硬件来执行简单的操作,例如复制图像。


1

我两个都得到了约5毫秒的时间。7100 fps对于GDI+执行的软件渲染来说太快了。视频驱动程序以欺骗方式赢得基准测试,它们可以检测到BitBlt不必执行,因为图像没有改变。尝试传递随机值到e.Graphics.TranslateTransform以消除作弊。


我在绘图循环中插入了 e.Graphics.TranslateTransform((float)rand.NextDouble(), (float)rand.NextDouble());。这导致帧速从7100下降到5000。要再试一次吗? - Tesserex
这是什么鬼……在速度较慢的方法上进行相同的更改,竟然使其速度更快了,从280提升到了380帧每秒! - Tesserex

0

你确定差异不是来自于 using 块,即设置 try-finally 块并从图像缓冲区创建 Graphics 实例。

我可以很容易地看出后者是一个昂贵的操作,不像绘画事件,你只需获得对已创建的图形实例的引用。


1
创建图形对象非常昂贵,我认为你是正确的,并且将设置作为using子句的一部分(并创建缓冲区)将影响性能,但这确实意味着您无需担心在此后销毁图形对象。尝试在启动时创建它们并在运行时重复使用它们,但请确保在退出时销毁它们。 - Frozenskys
我在帖子中说过,我尝试将代码保留在using块中。也就是说,把第二个片段注释掉e.Graphics.DrawImage,并将g.DrawImage更改为e.Graphics.DrawImage。这样它就突然像第一个片段一样快了。 - Tesserex

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