WM#U绘制消息频率:C#(.Net Framework 4.7.2 WinForms)与C#x2B+; 我现在用C++ C++(FW 4.7.2)WiFrm实现了一些旧游戏,使用C++实现Direct3D。

WM#U绘制消息频率:C#(.Net Framework 4.7.2 WinForms)与C#x2B+; 我现在用C++ C++(FW 4.7.2)WiFrm实现了一些旧游戏,使用C++实现Direct3D。,c#,c++,.net,direct2d,wm-paint,C#,C++,.net,Direct2d,Wm Paint,目前,我所有的实时图形游戏都实现了OnPaint覆盖,绘制到从PaintEventArgs参数检索到的图形对象中,并在绘制当前帧后立即失效。(我不知道这是否不被推荐,但效果很好)。当然,这些应用程序使用双缓冲。这会导致一个核心的利用率达到100%,这是正常的 第一个障碍是我在移植游戏时C++的,Direct3D是窗口的刷新率,即使我在执行了 WNDCUP()/代码>,并在调用后调用无效的材质()>代码>。 我遵循了以下快速入门指南: 问题是, 在我的windows窗体应用程序中,在全尺寸背景图像

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

第一个障碍是我在移植游戏时C++的,Direct3D是窗口的刷新率,即使我在执行了<代码> WNDCUP()/代码>,并在调用后调用<代码>无效的材质()>代码>。 我遵循了以下快速入门指南:

问题是,

在我的windows窗体应用程序中,在全尺寸背景图像上的刷新速度约为60 fps,在空背景上的刷新速度为300-400 fps。下面的示例仅绘制一个矩形,产生了惊人的2500 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 fps,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窗体
控件.Invalidate()
方法源,它所做的是调用Invalidate(如果子控件也要无效,则调用RedrawWindow())

注意:我在这里使用的直接2d绘图有误吗?我是否应该继续在一个单独的线程上绘制,并让DirectX根据需要刷新渲染目标?

我发现了问题所在

以下语句在为Direct2D创建渲染目标时,使用默认的呈现选项
D2D1\u present\u OPTIONS\u NONE
,这会导致渲染引擎等待vSync,因此我的笔记本电脑上每秒的最大帧数等于60 Hz(笔记本电脑监视器的刷新速度)

通过将present选项更改为immediate,在调用
invalidate()

所以我很快就提出了这个问题,因为刷新率高于监视器的刷新率没有任何效果,我从中得到的是Direct2D引擎通过等待实际屏幕的下一次刷新来优化cpu使用,这很有意义

试图提取超过监视器刷新率的数据看起来像是在浪费CPU周期

需要考虑实现一个很好的时序机制,因为WiFrm和GDI最慢的时间比监视器刷新率慢(当屏幕上有很多精灵和几何图形),现在我们快了,应该适应。

< P>我发现了这个问题。 以下语句在为Direct2D创建渲染目标时,使用默认的呈现选项
D2D1\u present\u OPTIONS\u NONE
,这会导致渲染引擎等待vSync,因此我的笔记本电脑上每秒的最大帧数等于60 Hz(笔记本电脑监视器的刷新速度)

通过将present选项更改为immediate,在调用
invalidate()

所以我很快就提出了这个问题,因为刷新率高于监视器的刷新率没有任何效果,我从中得到的是Direct2D引擎通过等待实际屏幕的下一次刷新来优化cpu使用,这很有意义

试图提取超过监视器刷新率的数据看起来像是在浪费CPU周期


需要考虑实现一个很好的时序机制,因为WiFrm和GDI最慢的时间比监视器刷新率慢(当屏幕上有很多精灵和几何图形),现在我们快了,应该适应。

无效材料(hwnd,…本身在带有hwnd句柄的窗口上触发WM_PAINT消息。您刚刚创建了一个循环引用(race)。

你甚至根本不需要WM_PAINT,也不需要HDC或GDI(如果你需要的话,就有互操作的可能性)。你可以构建一个交换链(类似于双缓冲,具有DXGI_交换效果_翻转_顺序),尽快绘制后缓冲区(D2D RenderTarget/DeviceContext),并同步交换缓冲区(调用IDXGISwapChain::Present):Direct2D本身没有优化任何东西(除了它使用GPU)。@SimonMourier谢谢你的评论。没有优化,你是对的。我认为效率低下的原因在于窗体框架,因为windows窗体控件.Invalidate()方法试图立即绘制到表面(因此导致fps值超过实际屏幕刷新率,同时导致100%的cpu)。使用Direct2D和所有相关层(DXGI、Direct Composition等),您甚至不需要WM_绘制,也不需要HDC或GDI(如果需要,还可以进行互操作)。您可以构建交换链(类似于双缓冲,使用DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL),尽快绘制后缓冲区(D2D RenderTarget/DeviceContext),并同步交换缓冲区(调用IDXGISwapChain::Present):Direct2D本身没有优化任何东西(除了它使用GPU之外)@SimonMourier谢谢你的评论。没有优化,你是对的。效率低下在于
// Create a Direct2D render target.
hr = m_pDirect2dFactory->CreateHwndRenderTarget(
    D2D1::RenderTargetProperties(),
    D2D1::HwndRenderTargetProperties(m_hwnd, size),
    &m_pRenderTarget
);
D2D1::HwndRenderTargetProperties(m_hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),