WM_PAINT 消息频率:C# (.Net Framework 4.7.2 WinForms) vs. C++。

问题描述 投票:0回答:1

我目前正在将我的一些使用C#(FW版本4.7.2)WinForms实现的老游戏用C++转换为Direct3D。

目前,我所有的实时图形游戏都是用C#(FW版本4.7.2)实现的。OnPaint 覆盖,绘制到从图形对象中检索到的 PaintEventArgs 参数,并在绘制完当前帧后立即失效。我不知道是不是不推荐这样做,但很好用)。当然,这些应用程序使用双缓冲。这导致了一个核心的100%利用率,这是确定的。

在将我的游戏移植到C++和Direct3D时,我遇到的第一个障碍是窗口的刷新率,即使我通过实现相同的 WndProc() 并称 InvalidateRect() 画完之后。

我按照这个快速入门指南。https:/docs.microsoft.comen-uswindowswin32direct2ddirect2d-quickstart。

问题是:

在我的windows窗体应用程序中,在全尺寸背景图像上绘制的刷新率约为60 fps,在空背景上绘制时为300-400 fps。下面的例子,只画一个矩形,产生了惊人的2,500 fps。

public class Surface : Form
{
    private int fps = 0;
    private int lastFPS = 0;
    private double lastSeconds = 0;
    private Stopwatch chronometer = Stopwatch.StartNew();
    Font font = new Font("Courier New", 10f);

    public Surface()
    {
        BackColor = Color.Black;
        DoubleBuffered = true;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.DrawRectangle(Pens.White, 100, 100, 100, 100);
        e.Graphics.DrawString("FPS: " + lastFPS, font, Brushes.White, 5, 5);

        fps++;
        if (chronometer.Elapsed.Seconds > lastSeconds)
        {
            lastSeconds = chronometer.Elapsed.Seconds;
            lastFPS = fps;
            fps = 0;
        }
        Invalidate();
    }
}

不用说,当我移植到DirectX上时,我期待着更高的帧数,但问题是WM_PAINT事件没有足够频繁地被触发,即使我不画任何东西,我也停留在100帧/秒,而CPU利用率只有10%左右。

我只在快速入门指南的源代码中添加了以下一行。

case WM_PAINT:
{
    pDemoApp->OnRender();
    ValidateRect(hwnd, NULL);
    InvalidateRect(hwnd, NULL, TRUE); // THIS IS THE LINE I'VE ADDED
}

我到底做错了什么?

我看到Direct 2D的渲染目标是自动双缓冲的。

所以问题是,为什么WM_PAINT事件每秒只被触发100次?

注:检查了windows窗体 Control.Invalidate() 方法源,它的作用是调用InvalidateRect(或RedrawWindow(),如果子控件也要被无效化的话)

注:我这里是不是直接使用2D绘图不正确?我是否应该在一个单独的线程上连续绘制,并让DirectX在它认为合适的时候刷新渲染目标?

c# c++ .net direct2d wm-paint
1个回答
0
投票

我已经找到了问题所在。

下面的语句,在为Direct2D创建渲染目标时,使用了默认的本选项。D2D1_PRESENT_OPTIONS_NONE这将导致渲染引擎等待vSync,因此我的笔记本电脑上的最大帧/秒等于60 Hz(笔记本电脑显示器的刷新速度)。

// Create a Direct2D render target.
hr = m_pDirect2dFactory->CreateHwndRenderTarget(
    D2D1::RenderTargetProperties(),
    D2D1::HwndRenderTargetProperties(m_hwnd, size),
    &m_pRenderTarget
);

通过将当前选项改为立即,WM_PAINT消息在调用了 InvalidateRect()

D2D1::HwndRenderTargetProperties(m_hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),

所以我太急于问这个问题了,因为让刷新率超过显示器的刷新率并没有任何影响,而我从中得到的是Direct2D引擎通过等待实际屏幕的下一次刷新来优化cpu的使用,这很有道理。

试图绘制超过显示器刷新率的画面,看起来是在浪费CPU周期。

需要考虑实现一个好的定时机制,因为winforms和GDI大部分时间都比显示器刷新率慢(当你在屏幕上有很多精灵和几何图形时),现在我们比这个快,应该适应。


0
投票

InvalidateRect(hwnd,......本身会在有hwnd句柄的窗口上发射WM_PAINT消息。你刚刚创建了一个循环引用(race)。

© www.soinside.com 2019 - 2024. All rights reserved.