ad8e/vsync_blurbusters

Add cross-platform VSYNC estimator based on Blur Busters open source

Opened this issue · 5 comments

@ad8e - we just released this today:

https://github.com/blurbusters/RefreshRateCalculator

Although your algorithm is good, there are some things RefreshRateCalculator.js does in a more cross-platform way (using generic time-offsets between VSYNC's to avoid the need for platform specific raster polls such as D3DKMTGetScanLine). Our algorithm could become the fallback default when the specific platform is not detected (e.g. Windows or Linux).

We released as Apache 2.0 so you may use it in your other projects that requires a vsync estimator. If you create a C++ version of RefreshRateCalculator.js please give us a pull request to add additional platforms.

ad8e commented

It works in VSYNC OFF, if one use a separate thread for the timestamps. You can use VSYNC OFF and instead use this approach already documented.

  • APIs for zero-graphics timestamps (e.g. independent/separate thread) include Windows D3DKMTWaitForVerticalBlankEvent()

More platforms have a VSYNC listener than a raster poll. Even D3DKMTWaitForVerticalBlankEvent() has some timing jitter, and the dejitterer improves that even further, and ignores missed vsync's (e.g. from computer freezes). Then you don't need to use VSYNC ON for the visible framebuffer. Cross-platform rasters are difficult without a module similar to this. This module is fairly multipurpose (emulators, for syncing emuHz to realHz); and overlaps your purpose.

  • use VSYNC OFF in your main, but use this module to dejitter your platform-specific VSYNC listener
  • waitable swapchain timestamps
  • background desktop compositor timestamps (if VSYNC'd)
  • low level graphics driver API timestamps
  • Can be used for tearingless VSYNC OFF (scanline-specific tearline steering) as long as a separate thread is able to monitor VSYNC timestamps. Ala RTSS Scanline Sync or Special K Latent Sync, implemented more crossplatform.
  • etc.

Oh -- and something I discovered. Some platforms let you create two graphics contexts; e.g. an offscreen VSYNC ON frame buffer (sometimes it's sync'd to display Hz) while your visible frame buffer is VSYNC OFF. You feed the offscreen VSYNC ON to this module, which generates cross platform raster estimates for this VSYNC OFF.

This is not always reliable, but it's another hack/technique to have simultaneous VSYNC ON and VSYNC OFF in two processes / two windows / two threads. It depends on how the graphics framework decides to treat VSYNC ON on the offscreen buffer / offscreen window / etc.

No rush, just might implement it myself -- but it's fantastically "extra options" because more platforms have a VSYNC/VBI detection or listener (that can run concurrent with VSYNC OFF) than platforms having a raster poll.

This is just a "math tool" module. Doesn't have external dependencies.

ad8e commented

In that case, the chain for D3DKMTWaitForVerticalBlankEvent() was already implemented. You can toggle it on by going to https://github.com/ad8e/vsync_blurbusters/blob/main/platform_vsync_windows.cpp and reading the comments at the top. The math side is in vsync.cpp, which outperforms the linear regression and filter methods. It does a clever thing with pivot points to handle the unique noise distribution of waiting.

I took a look at the filter in RefreshRateCalculator.js; an exponentially-weighted linear regression would do better. It's the same as Jongerius's old algorithm, or the one in validate(), just with farther back points decreased in weight. If you really want to use a filter, might as well use a delay line instead of 4 1-unit delays, or an IIR filter, although it won't matter much.

It's not for Windows version (yours is good) -- but for other platforms if additionals are implemented (e.g. Android, iOS, different Linux graphics frameworks, Mac, etc). There's a listener available for MacOS.

The filtering most certainly can be improved, so commits are welcome. The RefreshRateCalculator is identical to Jongerius' old algorithm, and Jongerius' code was easier to port to a standalone JavaScript module for TestUFO needs. So this was the least-effort open source release.

Now that being said, it's likely your vsync.cpp algorithm is better -- just more work to port C++ to JavaScript, than JavaScript-to-JavaScript. And since I needed it in year 2017, it's a variant of existing code that I refactored in year 2017

I'll add an issue tracking, to consider implementing the vsync.cpp algorithm into JavaScript and benchmark the two algorithms together simultaneously (software-calculated raster estimates versus D3DKMTGetScanline() ...) for error margins, and see which raster estimator is more accurate!

Would be fun (although I'm too busy right now to do that -- I just released a mostly unmodified module that's been closed-source for ages).