First post, by mdrejhon
I'm the founder of Blur Busters and creator of TestUFO, and want to make an announcement of one open source code module (used in TestUFO and VSYNCtester) that will be useful for some emulator authors.
I happen to have experimented with the world's first cross platform Kefrens Bars (works on PC and Mac via generic OpenGL VSYNC OFF tearline-position beamracing)
Here's some fruits I'd like to share.
Excellent news: New VSYNC Estimator Source Code
- Accurate estimate of your real refresh rate (to many decimal digits)
- Accurate estimate of next VSYNC
- Useful for improved lag-reducing inputdelay algorithms (vsync phase offsetting between the executing emuHz and the graphics drivers' realHz)
- Accurate enough for cross platform beam racing applications (to help sync emulator display's beam to real display's beam ala WinUAE Lagless VSYNC)
- Useful for both non-beamracing and beamracing purposes
https://github.com/blurbusters/RefreshRateCalculator
- One "RefreshRateCalculator()" class object, self contained.
- About 200 lines of code (+ ~100 lines of comments)
- No external dependancies
- Easy to port to almost any language on almost any platform
Purposes for emulators
- Non-beamraced:
- .....This can simply be used for crossplatform nudging/flywheeling emuHz (CPU clocked) slowly towards realHz (GPU clocked) to prevent latency phase slewing effects
- .....This can be used for crossplatform VSYNC phase offsets (input delay algorithms).
- Beamraced:
- .....It is also accurate enough for beam racing applications, such as cross platform Lagless VSYNC (like WinUAE, but crossplatform on any VSYNC OFF supported platform)
It's up -- https://github.com/blurbusters/RefreshRateCalculator
________________
RefreshRateCalculator CLASS
- PURPOSE: Accurate cross-platform display refresh rate estimator / dejittered VSYNC timestamp estimator.
- Input: Series of frame timestamps during framerate=Hz (Jittery/lossy)
- Output: Accurate filtered and dejittered floating-point Hz estimate & refresh cycle timestamps.
- Algorithm: Combination of frame counting, jitter filtering, ignoring missed frames, and averaging.
- This is also a way to measure a GPU clock source indirectly, since the GPU generates the refresh rate during fixed Hz.
- IMPORTANT VRR NOTE: This algorithm does not generate a GPU clock source when running this on a variable refresh rate display (e.g. GSYNC/FreeSync), but can still measure the foreground software application's fixed-framerate operation during windowed-VRR-enabled operation, such as desktop compositor (e.g. DWM). This can allow a background application to match the frame rate of the desktop compositor or foreground application (e.g. 60fps capped app on VRR display). This algorithm currently degrades severely during varying-framerate operation on a VRR display.
LICENSE - Apache-2.0
Copyright 2014-2023 by Jerry Jongerius of DuckWare (https://www.duckware.com) - original code and algorithm
Copyright 2017-2023 by Mark Rejhon of Blur Busters / TestUFO (https://www.testufo.com) - refactoring and improvements
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*** First publicly released July 2023 under mutual agreement
*** between Rejhon Technologies Inc. (Blur Busters) and Jongerius LLC (DuckWare)
*** PLEASE DO NOT DELETE THIS COPYRIGHT NOTICE
JAVASCRIPT VSYNC API / REFRESH CYCLE TIME STAMPS
- Info: https://www.vsynctester.com/howtocomputevsync.html
- Used by both https://www.vsynctester.com and https://www.testufo.com/refreshrate
- requestAnimationFrame() generally tries to syncs to VSYNC, so that is the source of VSYNC in web browsers, for deriving refresh cycle timestamps from. The longer this algorithm runs, the more accurate the refresh rate estimate becomes.
- JavaScript Compatibility: ES6 / ECMAScript 2015 (Chrome, FireFox, Edge, Safari, Opera)
CODE PORTING
- This algorithm is very portable to most languages, on most platforms, via high level and low level graphics frameworks.
- Generic VSYNC timestamps is usually immediately after exit of almost any frame presentation API during VSYNC ON framerate=Hz
- APIs for timestamps include RTDSC / QueryPerformanceCounter() / std::chrono::high_resolution_clock::now()
- APIs for low level frame presentation include DirectX Present(), OpenGL glFinish(), Vulkan vkQueuePresentKHR()
- APIs for high level frame presentation include XBox/MonoGame Draw(), Unity3D Update(), etc.
- APIs for zero-graphics timestamps (e.g. independent/separate thread) include Windows D3DKMTWaitForVerticalBlankEvent()
- While not normally used for beam racing, this algorithm is sufficiently accurate enough for cross-platform raster estimates for beam racing applications, based on a time offset between refresh cycle timestamps! (~1% error vs vertical resolution is possible on modern AMD/NVIDIA GPUs)
- Can be used for tearingless VSYNC OFF algorithms (scanline-specific tearline steering offscreen ala RTSS Scanline Sync or SpecialK Latent Sync) as long as separate thread is able to monitor and provide your (jittery) VSYNC or refresh cycle timestamps. Or if your platform/framework supports simultaneous VSYNC ON (offscreen) and VSYNC OFF (visible) in separate threads/contexts.
SIMPLE CODE EXAMPLE
var hertz = new RefreshRateCalculator();
[...]
// Call this inside your full frame rate VSYNC ON frame presentation or your VSYNC listener.
// It will automatically filter-out the jitter and dropped frames.
// For JavaScript, most accurate timestamp occurs if called at very top of your requestAnimationFrame() callback.
hertz.countCycle(performance.now());
[...]
// This data becomes accurate after a few seconds
var accurateRefreshRate = hertz.getCurrentFrequency();
var accurateRefreshCycleTimestamp = hertz.getFilteredCycleTimestamp();
// See code for more good helper functions
OPTIONAL: If you use this for cross platform "lagless vsync"
WinUAE implements a "lagless vsync" algorithm based on beam raced synchronization between emulator refresh cycle and real refresh cycle.
For cross platform beam racing, you'd do your code-ported version of this JavaScript (!) code:
Remember, you need VSYNC OFF while also concurrently being able to listen to the real displays' VSYNC.
// Run this after a 10-second refresh cycle counting initialization at startup (but keep counting beyond, to incrementally improve accuracy sufficiently enough for beam racing apps)
var accurateRefreshRate = hertz.getCurrentFrequency();
var accurateRefreshInterval = 1.0 / accurateRefreshRate;
var accurateRefreshCycleTimestamp = hertz.getFilteredCycleTimestamp();
// Vertical screen resolution
var height = screen.height;
// Common VBI size for maximum raster accuracy, adjust as needed. VGA 480p has 45, and HDTV 1080p has 45
// Or optionally use #ifdef type for plat-specific APIs like Linux modelines or Windows QueryDisplayConfig()
var blanking = 45;
var verticaltotal = height + blanking;
var elapsed = performance.now() - accurateRefreshCycleTimestamp;
var raster = Math.round(verticaltotal * (elapsed / accurateRefreshCycleTimestamp));
// OPTIONAL: If your VSYNC timestamp is end-of-VBI rather than start-of-VBI, then compensate
raster += blanking
raster = (raster % verticaltotal);
While this will freaking actually (uselessly) work in a web browser (I got roughly ~5-10% raster scan line position accuracy guesstimated in a WEB BROWSER running on an NVIDIA GPU, fer crissakes), e.g. a raster guesstimate vertically 50-75 pixels on a 1080p display, NVIDIA-type GPU, i7-type CPU.
...This won't be useful for rasterdemos in a web browser since they're permanently VSYNC ON and do not generate tearlines (no way to listen to VSYNC ON tick-tocks while running in VSYNC OF mode) -- but will work with high-framerate VSYNC OFF standalone software, for precise tearline steering, but remember to Flush() before timstamping for more accurate raster guesstimates. But the fact, I could get a raster scan line estimate in a FREAKING WEB BROWSER to roughly a 5% error margin (on landscape desktop displays, i7 CPU, NVIDIA GPU)....
Don't expect any good rasterdemo accuracy on Intel GPUs (but it's sufficiently accurate enough for emulator frameslice beamracing)
Now, if you do want to raster-estimate a mobile LCD or OLED display, make sure you rotate to its landscape default orientation (top-to-bottom scanout, and verify with high speed camera on https://www.testufo.com/scanout creating videos similar to ala https://www.blurbusters.com/scanout ...) ... Some mobile displays scans sideways, and you can't detect scanout direction in javascript. Boo. However, GPUs scan top-to-bottom to the GPU output, so landscape-monitor-mode will always be displaying a signal that's being scanned top-to-bottom, so you can certainly cross-platform beam race that (more or less).
If ported to C#, you can get sub-1% accuracy, much like Tearline Jedi.
If ported to C / C++ / Rust and using lower level VSYNC listeners, you can sometimes (on high performance platforms) get even better accuracy to as little as 1-scanline on certain less-hyperpipelined GPUs, although likely with a fixed offset that needs to be compensated-for. Emulator frameslice beamracing only need a worse error margin of one frameslice worth of jitter (e.g. 10 frameslices per refresh cycle at 60Hz = 1/600sec beamrace jitter is allowed before artifacts appear)
Remember, this cross platform module need not necessarily be used for beamracing;
- Non-beamraced:
- .....This can simply be used for crossplatform nudging/flywheeling emuHz (CPU clocked) slowly towards realHz (GPU clocked) to prevent latency phase slewing effects
- .....This can be used for crossplatform VSYNC phase offsets (input delay algorithms).
- Beamraced:
- .....It is also accurate enough for beam racing applications, such as cross platform Lagless VSYNC (like WinUAE, but crossplatform on any VSYNC OFF supported platform)
For more information about lagless VSYNC algorithms, see HOWTO: Possible Lagless VSYNC for Emulator Devs (implemented in WinUAE/etc), via beam-raced tearingless VSYNC OFF
Founder of www.blurbusters.com and www.testufo.com
- Research Portal
- Beam Racing Modern GPUs
- Lagless VSYNC for Emulators