@@ -796,113 +796,54 @@ static void report_stats(void)
796796}
797797#endif
798798
799- typedef struct VBlankPhaseLockedLoop {
800- int64_t vblank_period_ns ;
801- int64_t expected_vblank_ns ;
802- int64_t last_vblank_ns ;
803- int64_t integral_gain_divisor ;
804- int64_t proportional_gain_divisor ;
805- bool phase_locked ;
806- bool resync_required ;
807- } VBlankPLL ;
808- static VBlankPLL vblank_pll_state = { 0 };
809-
810- static inline int64_t pll_wrap_phase_error (int64_t error , int64_t period )
811- {
812- const int64_t wrapped = error % period ;
813- const int64_t half_period = period / 2 ;
814-
815- if (wrapped > half_period ) {
816- return wrapped - period ;
817- }
818799
819- if (wrapped < - half_period ) {
820- return wrapped + period ;
821- }
800+ enum VsyncOverrideBehavior {
801+ VOB_FORCE_OFF ,
802+ VOB_FORCE_ON ,
803+ VOB_NO_OVERRIDE ,
804+ };
822805
823- return wrapped ;
824- }
806+ static enum VsyncOverrideBehavior host_vsync_override = VOB_NO_OVERRIDE ;
807+ static int64_t host_vblank_period_ns = 0 ;
825808
826809// Time reserved for SDL_GL_SwapWindow to perform a swap. This value is chosen
827810// empirically as the best balance between minimizing guest stalls while still
828811// hitting the target host refresh rate.
829- #define VSYNC_SWAP_GRACE_PERIOD_NS 1400000LL
830- #define MISSED_FRAME_RESYNC_COUNT 8
812+ #define VSYNC_SWAP_GRACE_PERIOD_NS 1000000LL
813+ #define VSYNC_RESYNC_FRAMES 2
831814
832- #define PLL_BASE_JITTER_NS 1500000LL
833-
834- static inline void pll_swap_window (SDL_Window * window )
815+ static inline void vsync_swap_window (SDL_Window * window )
835816{
836- if (qatomic_xchg (& vblank_pll_state .resync_required , false)) {
837- vblank_pll_state .last_vblank_ns = 0 ;
838- vblank_pll_state .phase_locked = false;
839- }
840-
841- if (vblank_pll_state .phase_locked ) {
842- int64_t now = (int64_t )SDL_GetTicksNS ();
843- int64_t sleep_target_ns =
844- vblank_pll_state .expected_vblank_ns - VSYNC_SWAP_GRACE_PERIOD_NS ;
845- int64_t delay_ns = sleep_target_ns - now ;
817+ static int64_t last_swap_complete = 0 ;
818+ int64_t now = qemu_clock_get_ns (QEMU_CLOCK_REALTIME );
846819
847- if (delay_ns > 0 && delay_ns < vblank_pll_state .vblank_period_ns ) {
848- SDL_DelayPrecise ((uint64_t )delay_ns );
820+ if (last_swap_complete > 0 ) {
821+ int64_t next_vblank = last_swap_complete + host_vblank_period_ns ;
822+ if (now <= next_vblank + host_vblank_period_ns ) {
823+ int64_t target_swap_time = next_vblank - VSYNC_SWAP_GRACE_PERIOD_NS ;
824+ int64_t sleep_duration_ns = target_swap_time - now ;
825+ if (sleep_duration_ns > 0 ) {
826+ SDL_DelayPrecise (sleep_duration_ns );
827+ }
849828 }
850829 }
851830
852831 SDL_GL_SwapWindow (window );
853- const int64_t swap_finish = (int64_t )SDL_GetTicksNS ();
854-
855- if (!vblank_pll_state .phase_locked ) {
856- if (vblank_pll_state .last_vblank_ns != 0 ) {
857- int64_t delta = pll_wrap_phase_error (
858- swap_finish - vblank_pll_state .last_vblank_ns ,
859- vblank_pll_state .vblank_period_ns );
860-
861- const int64_t bootstrap_tol =
862- MIN (PLL_BASE_JITTER_NS , vblank_pll_state .vblank_period_ns / 5 );
863- if (llabs (delta ) < bootstrap_tol ) {
864- vblank_pll_state .expected_vblank_ns = swap_finish ;
865- vblank_pll_state .phase_locked = true;
866- }
867- }
868- vblank_pll_state .last_vblank_ns = swap_finish ;
869- return ;
870- }
871832
872- int64_t error_ns = swap_finish - vblank_pll_state .expected_vblank_ns ;
873- if (llabs (error_ns ) >
874- vblank_pll_state .vblank_period_ns * MISSED_FRAME_RESYNC_COUNT ) {
875- vblank_pll_state .expected_vblank_ns = swap_finish ;
833+ int64_t post_swap = qemu_clock_get_ns (QEMU_CLOCK_REALTIME );
834+
835+ if (!last_swap_complete ||
836+ post_swap >
837+ last_swap_complete + VSYNC_RESYNC_FRAMES * host_vblank_period_ns ) {
838+ last_swap_complete = post_swap ;
876839 } else {
877- int64_t phase_error_ns =
878- pll_wrap_phase_error (error_ns , vblank_pll_state .vblank_period_ns );
879- int64_t nearest_vblank = swap_finish - phase_error_ns ;
880-
881- const int64_t tear_tol =
882- MIN (PLL_BASE_JITTER_NS * 2 , vblank_pll_state .vblank_period_ns / 4 );
883- if (llabs (phase_error_ns ) > tear_tol ) {
884- // Handle adaptive vsync tearing swaps. These can occur
885- // significantly out of phase and should be ignored.
886- vblank_pll_state .expected_vblank_ns =
887- nearest_vblank + vblank_pll_state .vblank_period_ns ;
888- } else {
889- vblank_pll_state .vblank_period_ns +=
890- phase_error_ns / vblank_pll_state .integral_gain_divisor ;
891- vblank_pll_state .expected_vblank_ns =
892- nearest_vblank + vblank_pll_state .vblank_period_ns +
893- (phase_error_ns / vblank_pll_state .proportional_gain_divisor );
894- }
840+ int64_t elapsed_frames =
841+ (post_swap - last_swap_complete + host_vblank_period_ns / 2 ) /
842+ host_vblank_period_ns ;
843+ last_swap_complete += elapsed_frames * host_vblank_period_ns ;
895844 }
896845}
897846
898- enum VsyncOverrideBehavior {
899- VOB_FORCE_OFF ,
900- VOB_FORCE_ON ,
901- VOB_NO_OVERRIDE ,
902- };
903-
904- static enum VsyncOverrideBehavior vsync_override = VOB_NO_OVERRIDE ;
905-
906847/**
907848 * Renders the main interface. Usually called from the main thread,
908849 * but may sometimes be called from another thread.
@@ -958,7 +899,7 @@ static void gl_render_frame(struct xemu_console *scon)
958899 xemu_main_loop_unlock ();
959900
960901 xemu_hud_render ();
961- glFinish ();
902+ glFlush ();
962903
963904 if (release_surface_texture ) {
964905 xemu_main_loop_lock ();
@@ -968,9 +909,10 @@ static void gl_render_frame(struct xemu_console *scon)
968909
969910 nv2a_release_framebuffer_surface ();
970911
971- if (vsync_override == VOB_FORCE_ON ||
972- (vsync_override == VOB_NO_OVERRIDE && g_config .display .window .vsync )) {
973- pll_swap_window (scon -> real_window );
912+ if (host_vsync_override == VOB_FORCE_ON ||
913+ (host_vsync_override == VOB_NO_OVERRIDE &&
914+ g_config .display .window .vsync )) {
915+ vsync_swap_window (scon -> real_window );
974916 } else {
975917 SDL_GL_SwapWindow (scon -> real_window );
976918 }
@@ -983,31 +925,23 @@ static void gl_render_frame(struct xemu_console *scon)
983925#endif
984926}
985927
986- static inline void apply_vsync_interval (const int64_t interval )
987- {
988- vblank_pll_state .vblank_period_ns = interval ;
989- // Gain is calculated with a target of locking within ~1.6 seconds at 60Hz.
990- vblank_pll_state .integral_gain_divisor = 1666666666LL / interval ;
991- vblank_pll_state .proportional_gain_divisor = 166666666LL / interval ;
992- }
993-
994928static void calculate_vsync_interval_ns (void )
995929{
996930 SDL_DisplayID display_idx = SDL_GetDisplayForWindow (m_window );
997931 const SDL_DisplayMode * mode = SDL_GetCurrentDisplayMode (display_idx );
998932 if (mode && mode -> refresh_rate_numerator > 0 &&
999933 mode -> refresh_rate_denominator > 0 ) {
1000- apply_vsync_interval ( (1000000000LL * mode -> refresh_rate_denominator ) /
1001- mode -> refresh_rate_numerator ) ;
934+ host_vblank_period_ns = (1000000000LL * mode -> refresh_rate_denominator ) /
935+ mode -> refresh_rate_numerator ;
1002936 return ;
1003937 }
1004938
1005939 if (mode && mode -> refresh_rate > 0 ) {
1006- apply_vsync_interval (( int64_t )(1000000000.0 / mode -> refresh_rate ) );
940+ host_vblank_period_ns = ( int64_t )(1000000000.0 / mode -> refresh_rate );
1007941 return ;
1008942 }
1009943
1010- apply_vsync_interval ( 1000000000LL / 60 ) ;
944+ host_vblank_period_ns = 1000000000LL / 60 ;
1011945}
1012946
1013947static bool event_watch_callback (void * userdata , SDL_Event * event )
@@ -1020,10 +954,6 @@ static bool event_watch_callback(void *userdata, SDL_Event *event)
1020954 } else if (event -> type == SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED ||
1021955 event -> type == SDL_EVENT_WINDOW_DISPLAY_CHANGED ) {
1022956 calculate_vsync_interval_ns ();
1023- } else if (event -> type == SDL_EVENT_WINDOW_RESTORED ||
1024- event -> type == SDL_EVENT_WINDOW_MAXIMIZED ||
1025- event -> type == SDL_EVENT_WINDOW_FOCUS_GAINED ) {
1026- qatomic_set (& vblank_pll_state .resync_required , true);
1027957 }
1028958
1029959 return true; // Ignored
@@ -1408,11 +1338,11 @@ static void setup_nvidia_profile(void)
14081338 nvapi_finalize ();
14091339
14101340 if (current_state .vsync_mode == VSYNC_MODE_FORCE_OFF ) {
1411- vsync_override = VOB_FORCE_OFF ;
1341+ host_vsync_override = VOB_FORCE_OFF ;
14121342 } else if (current_state .vsync_mode == VSYNC_MODE_FORCE_ON ) {
1413- vsync_override = VOB_FORCE_ON ;
1343+ host_vsync_override = VOB_FORCE_ON ;
14141344 } else {
1415- vsync_override = VOB_NO_OVERRIDE ;
1345+ host_vsync_override = VOB_NO_OVERRIDE ;
14161346 }
14171347 }
14181348}
0 commit comments