Advertisement

DirectX: MFC OnIdle limited to 60 fps

Started by February 24, 2020 09:34 AM
3 comments, last by kubera 4 years, 9 months ago

Hi guys ,

i have a very particular issue…

i have directx classes for rendering to a window. I have created a GUI with MFC c++ and integrated my DirectX classes with it. I am initialising on OnInitDialog function and then doing a rendering loop at OnIdle function. I have over ridden WindowProc function. It is a model dialog.

The problem is, irrespective of whatever the VSYNC state of my directx component is(i know the present flags of swap chain btw), i am always getting 60 fps frame rate. when i minimise the window the frame rates unlocks and becomes very high. On maximise again it again drops to 60 fps.

A picture control is my rendering area which is a frame.

rendering starts when user click on a button.

so how to unlock frame rate in MFC OnIdle.

I don't think “OnIdle” is the right thing at all to be using for game updates or rendering. The Microsoft docs on that function don't appear to specify any frequency, including the possibility that if the message loop is not “idle”, it might not be called for a while (at least in the time it is OK to wait in a game and not cause stutter). OnIdle is intended to clean up resources/memory and such (see the default implementation).

For things that are less demanding (like a basic application/tool not needing high or perfectly consistent fps) using timers should be good enough.

I never tried to use MFC for a game. If you really want MFC for ease of having some other UI elements, I think the way to go is override `CWinThread::Run`, that can then implement a traditional game loop where you have full control over timing.

The other solution I found looking around involved spawning a new thread, Direct3D9-11 does allow for the full render to be done in a different thread completely independently to the window itself (believe D3D12, OpenGL, and Vulkan do as well but not tried personally).

Advertisement

Like syncview said: this is not how it is supposed to be done. You can not change the on idle frequency NEITHER you are guaranteed, that it will happen every 1/60th of a second. It could be slower too.

The OnIdle event is ment for APPLICATIONS not game, when there is some extra cpu time available.

What you are supposed to do is write your own message pump loop (Get message and post message) and handle the frame update request there. This means that you get maximum amount of processing time per application.

You should also avoid functions like “sleep” and “GetTickCount()” because they are all limited to 60Hz. Also avoid any on timer event, because they are all bound to 60 Hz max.

You should use query performance timer instead (couppled with frequency).

Every MFC application has a message loop also. You could inject your update/render code there.

OnIdle() is useful if you want your game to keep simulating while the user moves the window around, for example, if your simulation runs in the main thread. It is, however, quite jittery, as discussed above.

It turns out that, if your game needs to run on the main thread, there exists a surprisingly good way of getting to run “as frequent as possible.” That way is to use the WM_PAINT messages.

WM_PAINT is generated when a window has a non-empty invalid region, and no other message is pending in the message queue. This means that input events all get dispatched before the paint message, which is what you want.

So, inside your PAINT handler, you should BeginPaint()/EndPaint(), followed by InvalidateRect(), followed by actually running your simulation (assuming sufficient time has advanced for the simulation to advance,) followed by a full-screen update. Because you're outside of BeginPaint()/EndPaint(), the entire window will be drawn, not clipped.

Because you're using MFC, you have to override a few functions to end up simulating/drawing outside the BeginPaint() brackets; I forget the actual details, but it is possible.

enum Bool { True, False, FileNotFound };

You could create a child window with its own message loop and new thread.

It should work. Maybe easier way to create such renderer is development of DirectX functionality in the raw Windows API, but the parent could still be the MFC type.

This topic is closed to new replies.

Advertisement