I am currently converting some of my old games implemented using C# (FW version 4.7.2) WinForms to Direct3D using C++.
Currently, all my real-time graphics games implement the OnPaint
override, draw into the Graphics object retrieved from the PaintEventArgs
parameter and invalidate right after drawing the current frame. (I don't know if this is not recommended, but it works great). These apps use double buffering of course. This causes 100% utilization of one core, which is OK.
The first obstacle I came accross when porting my games to C++ and Direct3D is the refresh rate of the window even if I had done the same thing by implementing the WndProc()
and calling InvalidateRect()
after drawing.
I followed this quick start guide: https://docs.microsoft.com/en-us/windows/win32/direct2d/direct2d-quickstart
The problem is,
With my windows forms apps, the refresh rate is around 60 fps drawing over a full-size background image, 300-400 fps when drawing on an empty background. The following example, drawing only a rectangle, produces an amazing 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();
}
}
Needless to say, I was expecting a much better frame rate when porting to DirectX, but the problem is that the WM_PAINT event is not fired frequently enough and I am stuck at 100 fps even if I don't draw anything, and cpu utilization is around 10% only.
I only added the following line to the source code of the quick start guide:
case WM_PAINT:
{
pDemoApp->OnRender();
ValidateRect(hwnd, NULL);
InvalidateRect(hwnd, NULL, TRUE); // THIS IS THE LINE I'VE ADDED
}
What am I doing wrong?
I read that the rendering target of Direct 2D is automatically double buffered,
So the question is, why the WM_PAINT event gets fired only 100 times per second?
Note: Checked the windows forms Control.Invalidate()
method source, and what it does is to call InvalidateRect (or RedrawWindow() if child controls are to be invalidated as well)
NOTE: Am I using direct 2d drawing incorrectly here? Should I continuously draw on a separate thread and let DirectX refresh the rendering target as it sees fit?