Get the real display refresh rate for Windows DWM

Share your advanced PureBasic knowledge/code with the community.
User avatar
Roger Hågensen
User
User
Posts: 47
Joined: Wed Mar 25, 2015 1:06 pm
Location: Norway

Get the real display refresh rate for Windows DWM

Post by Roger Hågensen »

Code: Select all

;This quick'n'dirty example will tell you the frequency (Hz) of the primary system display.
;In a multimonitor system the DWM uses the frequency of the primary display.
;The VSync of windowed programs should be tied to this.

;You still need to do your own timing loop and calculate a frame delta. But at least getting the accurate Hz should make that easier.
;It is very common for the real Hz of a refresh rate to be 60.001 or 60.002 or 59.94 but still be shown as 60Hz.
;Over time yur frame delta will end up drifting so that you end up 1 frame ahead/behind and you get a stutter.
;If you have ever seen a game have this odd stutter that occurs once every second ten this could be one reason.

;This code is hereby placed in the public domain, use as you wish.

EnableExplicit

#DLL_dwmapi = 1

Structure UNSIGNED_RATIO
  uiNumerator.l
  uiDenominator.l
EndStructure
Macro DWM_FRAME_COUNT
  q
EndMacro
Macro QPC_TIME
  q
EndMacro
Macro UINT32
  l
EndMacro
Macro UINT
  l
EndMacro
Macro ULONGLONG
  q
EndMacro

Structure DWM_TIMING_INFO
  cbSize.UINT32
  rateRefresh.UNSIGNED_RATIO
  qpcRefreshPeriod.QPC_TIME
  rateCompose.UNSIGNED_RATIO
  qpcVBlank.QPC_TIME
  cRefresh.DWM_FRAME_COUNT
  cDXRefresh.UINT
  qpcCompose.QPC_TIME
  cFrame.DWM_FRAME_COUNT
  cDXPresent.UINT
  cRefreshFrame.DWM_FRAME_COUNT
  cFrameSubmitted.DWM_FRAME_COUNT
  cDXPresentSubmitted.UINT
  cFrameConfirmed.DWM_FRAME_COUNT
  cDXPresentConfirmed.UINT
  cRefreshConfirmed.DWM_FRAME_COUNT
  cDXRefreshConfirmed.UINT
  cFramesLate.DWM_FRAME_COUNT
  cFramesOutstanding.UINT
  cFrameDisplayed.DWM_FRAME_COUNT
  qpcFrameDisplayed.QPC_TIME
  cRefreshFrameDisplayed.DWM_FRAME_COUNT
  cFrameComplete.DWM_FRAME_COUNT
  qpcFrameComplete.QPC_TIME
  cFramePending.DWM_FRAME_COUNT
  qpcFramePending.QPC_TIME
  cFramesDisplayed.DWM_FRAME_COUNT
  cFramesComplete.DWM_FRAME_COUNT
  cFramesPending.DWM_FRAME_COUNT
  cFramesAvailable.DWM_FRAME_COUNT
  cFramesDropped.DWM_FRAME_COUNT
  cFramesMissed.DWM_FRAME_COUNT
  cRefreshNextDisplayed.DWM_FRAME_COUNT
  cRefreshNextPresented.DWM_FRAME_COUNT
  cRefreshesDisplayed.DWM_FRAME_COUNT
  cRefreshesPresented.DWM_FRAME_COUNT
  cRefreshStarted.DWM_FRAME_COUNT
  cPixelsReceived.ULONGLONG
  cPixelsDrawn.ULONGLONG
  cBuffersEmpty.DWM_FRAME_COUNT
EndStructure

Define dwmapi.i, qpcfrequency.q, hzd.d, hzd2.d
Define title$, text$
Define *DwmGetCompositionTimingInfo, timinginfo.DWM_TIMING_INFO

dwmapi = OpenLibrary(#DLL_dwmapi, "dwmapi.dll")
If dwmapi = #Null
  Debug "Error: OpenLibrary dwmapi"
EndIf

*DwmGetCompositionTimingInfo = GetFunction(#DLL_dwmapi, "DwmGetCompositionTimingInfo")
If *DwmGetCompositionTimingInfo = #Null
  Debug "Error: GetFunctionEntry DwmGetCompositionTimingInfo"
  End
EndIf

timinginfo\cbSize = SizeOf(timinginfo)
If CallFunctionFast(*DwmGetCompositionTimingInfo, #Null, @timinginfo) <> #S_OK
  Debug "Error: DwmGetCompositionTimingInfo"
  End
EndIf
;Success means that DWM is active
If QueryPerformanceFrequency_(@qpcfrequency) = #False
  Debug "Error: QueryPerformanceFrequency"
  End
EndIf

hzd = (qpcfrequency / timinginfo\qpcRefreshPeriod)

title$ = "System Monitor Frequency"

If (timinginfo\rateRefresh\uiDenominator > 0) And (timinginfo\rateRefresh\uiNumerator > 0)
  hzd2 = 1000.0 / ((timinginfo\rateRefresh\uiDenominator * 1000.0) / timinginfo\rateRefresh\uiNumerator)
  ;If any old OS or drivers has issues/bugs with qpcRefreshPeriod this should be detected at program start.
  ;No need to test this constantly during runtime, that would waste CPU and cause additional overhead.
  ;The paranoid could perhaps do a quick check during load screens, or when opening/closing a ingame menu.
  If Round(hzd, #PB_Round_Nearest) = Round(hzd2, #PB_Round_Nearest)
    text$ = StrD(hzd2,0) + " (" + StrD(hzd,14) + ") Hz"
  Else
    text$ = "Unreliable qpcRefreshPeriod" + #CRLF$ + #CRLF$ + StrD(hzd2,0) + " (" + StrD(hzd,14) + ") Hz"
  EndIf
EndIf

MessageRequester(title$, text$)

CloseLibrary(#DLL_dwmapi)
__________________________________________________
Thread moved
Game Programming>Tricks 'n' Tips
17.01.2018
RSBasic
4 music albums under CC BY license available for free (any use, even commercial) at Skuldwyrm.no
User avatar
RSBasic
Moderator
Moderator
Posts: 1218
Joined: Thu Dec 31, 2009 11:05 pm
Location: Gernsbach (Germany)
Contact:

Re: Get the real display refresh rate for Windows DWM

Post by RSBasic »

Nice code. Image
Image
Image
Post Reply