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