Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Stuttering game? Important - You need to call dwmflush()

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Game Development
View previous topic :: View next topic  
Author Message
dlpb
Advanced Cheater
Reputation: 0

Joined: 11 Dec 2013
Posts: 78

PostPosted: Tue Sep 17, 2024 2:28 am    Post subject: Stuttering game? Important - You need to call dwmflush() Reply with quote

Hi, all

I lead a project to relocalize Final Fantasy VII and correct bugs in the game - see https://thereunion.live

and

https://www.eurogamer.net/why-would-someone-spend-five-years-retranslating-all-of-final-fantasy-7

As part of the on-going efforts to improve the game, I recently replaced the entire game loop with my own and created a proper frame limiter.

However, despite all my best efforts and tests, the game would still stutter in window mode. And I've noticed many other games and emulators do it too. It seems the reason behind it isn't as well known as it should be, so I'll be doing my best to update people.

The reason is DWM - Desktop Window Manager. It is forced in Windows 10 and 11. You cannot disable it without causing an even bigger problem.

If you are in window mode with vsync on, you need to be calling

https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmflush

immediately before SwapBuffers() - or the Directx equivalent.

This will force the sync to be in line with DWM. If you don't do this, you WILL get stuttering in window mode. In DirectX full screen Exclusive mode, DWM is disabled. With OpenGL, in my own tests, it seems to "disable" as long as you use a borderless window that exactly matches the desktop resolution, but I can't find any documentation online from Microsoft to confirm it.

Regardless, you will want to call dwmflush() whenever vsync is enabled (window mode or not). Do not call it when vsync is disabled, as it will cause vsync-like behaviour.

if using Opengl, it is sometimes beneficial to also call glFinish() after the SwapBuffer, but that depends.


The following is my code


main
Code:

   if (is_vsync && dwm_enabled)
   DwmFlushFunc();


   if (!SwapBuffers(hDC))
   {
      Log(1, "SwapBuffers failed.\n");
      windows_error(0);
   }


Global var
Code:
bool_ dwm_enabled = false;
typedef HRESULT(WINAPI* pfnDwmFlush)();
pfnDwmFlush DwmFlushFunc = NULL;



Code:
void CheckDwm() {

   HMODULE hDwmapi = LoadLibrary("Dwmapi.dll");

   bool_ dwm_bool = false;
        dwm_enabled = false;

   if (hDwmapi)
   {
      typedef HRESULT(WINAPI* pfnDwmIsCompositionEnabled)(BOOL*);
      pfnDwmIsCompositionEnabled DwmIsCompositionEnabledFunc = (pfnDwmIsCompositionEnabled)GetProcAddress(hDwmapi, "DwmIsCompositionEnabled");


      if (DwmIsCompositionEnabledFunc)
      {
         HRESULT hr = DwmIsCompositionEnabledFunc(&dwm_bool);

         if (SUCCEEDED(hr) && dwm_bool)
         {
            
            DwmFlushFunc = (pfnDwmFlush)GetProcAddress(hDwmapi, "DwmFlush");

            if (DwmFlushFunc)
            {
               Log(0, "Desktop Window Manager has been detected.\n");
               dwm_enabled = true;
            }

         }
               
      }

   }
}



DELPHI CODE

Code:
var
  dwm_enabled: boolean = False;
  DwmFlushFunc: function: HRESULT; stdcall = nil;


Code:
procedure CheckDwm;
var
  hDwmapi: HMODULE;
  hr: HRESULT;
  DwmIsCompositionEnabledFunc: function(out pfEnabled: boolean): HRESULT; stdcall;
  dwm_bool: boolean;
begin
  hDwmapi := LoadLibrary('Dwmapi.dll');
  dwm_enabled := False;

  if hDwmapi <> 0 then
  begin
    @DwmIsCompositionEnabledFunc := GetProcAddress(hDwmapi, 'DwmIsCompositionEnabled');

    if Assigned(DwmIsCompositionEnabledFunc) then
    begin
      hr := DwmIsCompositionEnabledFunc(dwm_bool);

       if (Succeeded(hr) ) and (dwm_bool) then
      begin
        @DwmFlushFunc := GetProcAddress(hDwmapi, 'DwmFlush');

        if Assigned(DwmFlushFunc) then
        begin
          // Replace this with your logging function or remove
          Showmessage('Desktop Window Manager has been detected.'#13#10);
          dwm_enabled := True;
        end;
      end;
    end;


  end;
end;
Quote:
Back to top
View user's profile Send private message
atom0s
Moderator
Reputation: 202

Joined: 25 Jan 2006
Posts: 8546
Location: 127.0.0.1

PostPosted: Wed Sep 18, 2024 2:04 pm    Post subject: Reply with quote

This is actually a known issue that has been around since the Windows Vista era when DWM was first introduced and added to Windows. The issue stems from the fact that when calling 'SwapBuffers', the sync handling is still attempting to sync to the monitor itself by default rather than syncing to DWM. GLFW has a fix already inside of it that will automatically call 'DwmFlush()' when swapping the buffers if it detects that DWM is present. However, I don't think they ever updated their fix to work with Win 10/11 and was instead limited to just Vista/7 at the time.

The commit for that can be found here:
https://github.com/glfw/glfw/commit/4005f70eef41a691b849cd06e085d9005df6b66b

You can find several other examples of people running into the same problem and fix such as:
https://www.reddit.com/r/opengl/comments/8754el/stuttering_with_learnopengl_tutorials/dwdd4cj/
https://www.reddit.com/r/GraphicsProgramming/comments/wcira5/windowed_opengl_with_windows_dwm/
https://forum.juce.com/t/opengl-on-windows-cpu-usage-and-dwmflush/57381
https://stackoverflow.com/questions/45676892/reliable-windowed-vsync-with-opengl-on-windows

And many others etc.

Quote:
I lead a project to relocalize Final Fantasy VII and correct bugs in the game - see https://thereunion.live


Best of luck with your project, nice to see people still putting in time and effort into FF7. Smile

_________________
- Retired.
Back to top
View user's profile Send private message Visit poster's website
dlpb
Advanced Cheater
Reputation: 0

Joined: 11 Dec 2013
Posts: 78

PostPosted: Wed Sep 18, 2024 4:59 pm    Post subject: Reply with quote

You'd be surprised how many people don't know about this - It took me ages to find some decent documentation. So I think it's worth posting it with full code so people can be aware. It's not enough to just post "dwmflush" like most are - that helps no one.

Esp the fact Opengl doesnt have an exclusive mode. And then other solutions like GLEW wrangler don't call dwmflush - so yeah I def think a post like this is a good idea.

Further to that, it does look like MS check fir a full screen borderless window when OPENGL is running, because it goes smooth without dwmflush. Is there a confirmation on this?
Back to top
View user's profile Send private message
dlpb
Advanced Cheater
Reputation: 0

Joined: 11 Dec 2013
Posts: 78

PostPosted: Thu Sep 19, 2024 8:44 pm    Post subject: Reply with quote

https://devblogs.microsoft.com/directx/demystifying-full-screen-optimizations/

Here is an official statement that shows they turn it off when borderless window - so that would explain OpenGL not having issues in full screen.

"To get back this performance overhead, we enhanced the DWM to recognize when a game is running in a borderless full screen window with no other applications on the screen. "
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Game Development All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites