mirror of
				https://github.com/coop-deluxe/sm64coopdx.git
				synced 2025-10-30 08:01:01 +00:00 
			
		
		
		
	much better frame pacing
	
		
			
	
		
	
	
		
	
		
			Some checks are pending
		
		
	
	
		
			
				
	
				Build coop / build-linux (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-steamos (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-windows-opengl (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-windows-directx (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-macos-arm (push) Waiting to run
				
			
		
			
				
	
				Build coop / build-macos-intel (push) Waiting to run
				
			
		
		
	
	
				
					
				
			
		
			Some checks are pending
		
		
	
	Build coop / build-linux (push) Waiting to run
				
			Build coop / build-steamos (push) Waiting to run
				
			Build coop / build-windows-opengl (push) Waiting to run
				
			Build coop / build-windows-directx (push) Waiting to run
				
			Build coop / build-macos-arm (push) Waiting to run
				
			Build coop / build-macos-intel (push) Waiting to run
				
			plus limited frame rates on the loading and crash screens to go easy on the cpu. during gameplay, this new system is able to stay true to your fps limit without frame drops. Vsync causes occasional frame drops by just 1 frame. this is an sdl issue and i know of no workaround; luckily it's very minor. this commit changes frame pacing by moving frame delaying to just before the frame is displayed. a more precise delay function is also used.
This commit is contained in:
		
							parent
							
								
									fe2c31554d
								
							
						
					
					
						commit
						0a84ca725e
					
				
					 6 changed files with 68 additions and 20 deletions
				
			
		|  | @ -138,7 +138,7 @@ override_disallowed_functions = { | |||
|     "src/game/first_person_cam.h":              [ "first_person_update" ], | ||||
|     "src/pc/lua/utils/smlua_collision_utils.h": [ "collision_find_surface_on_ray" ], | ||||
|     "src/engine/behavior_script.h":             [ "stub_behavior_script_2", "cur_obj_update" ], | ||||
|     "src/pc/utils/misc.h":                      [ "str_.*", "file_get_line", "delta_interpolate_(normal|rgba|mtx)", "detect_and_skip_mtx_interpolation" ], | ||||
|     "src/pc/utils/misc.h":                      [ "str_.*", "file_get_line", "delta_interpolate_(normal|rgba|mtx)", "detect_and_skip_mtx_interpolation", "precise_delay_f64" ], | ||||
|     "src/engine/lighting_engine.h":             [ "le_calculate_vertex_lighting", "le_clear", "le_shutdown" ], | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1970,20 +1970,26 @@ void gfx_run(Gfx *commands) { | |||
|     //double t0 = gfx_wapi->get_time();
 | ||||
|     gfx_rapi->start_frame(); | ||||
|     gfx_run_dl(commands); | ||||
|     gfx_flush(); | ||||
|     //double t1 = gfx_wapi->get_time();
 | ||||
|     //printf("Process %f %f\n", t1, t1 - t0);
 | ||||
|     gfx_rapi->end_frame(); | ||||
|     gfx_wapi->swap_buffers_begin(); | ||||
| } | ||||
| 
 | ||||
| void gfx_end_frame(void) { | ||||
| void gfx_end_frame_render(void) { | ||||
|     gfx_flush(); | ||||
|     gfx_rapi->end_frame(); | ||||
| } | ||||
| 
 | ||||
| void gfx_display_frame(void) { | ||||
|     gfx_wapi->swap_buffers_begin(); | ||||
|     if (!dropped_frame) { | ||||
|         gfx_rapi->finish_render(); | ||||
|         gfx_wapi->swap_buffers_end(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void gfx_end_frame(void) { | ||||
|     gfx_end_frame_render(); | ||||
|     gfx_display_frame(); | ||||
| } | ||||
| 
 | ||||
| void gfx_shutdown(void) { | ||||
|     if (gfx_rapi) { | ||||
|         if (gfx_rapi->shutdown) gfx_rapi->shutdown(); | ||||
|  |  | |||
|  | @ -21,6 +21,8 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi, co | |||
| struct GfxRenderingAPI *gfx_get_current_rendering_api(void); | ||||
| void gfx_start_frame(void); | ||||
| void gfx_run(Gfx *commands); | ||||
| void gfx_end_frame_render(void); | ||||
| void gfx_display_frame(void); | ||||
| void gfx_end_frame(void); | ||||
| void gfx_shutdown(void); | ||||
| void gfx_pc_precomp_shader(uint32_t rgb1, uint32_t alpha1, uint32_t rgb2, uint32_t alpha2, uint32_t flags); | ||||
|  |  | |||
|  | @ -219,24 +219,29 @@ void produce_interpolation_frames_and_delay(void) { | |||
|         ); | ||||
|         gRenderingDelta = delta; | ||||
| 
 | ||||
|         // prepare interpolated frame
 | ||||
|         gfx_start_frame(); | ||||
|         if (!gSkipInterpolationTitleScreen) { patch_interpolations(delta); } | ||||
|         send_display_list(gGfxSPTask); | ||||
|         gfx_end_frame(); | ||||
| 
 | ||||
|         sDrawnFrames++; | ||||
| 
 | ||||
|         if (!is30Fps && configUncappedFramerate) { continue; } | ||||
|         gfx_end_frame_render(); | ||||
| 
 | ||||
|         // delay if our framerate is capped
 | ||||
|         f64 now = clock_elapsed_f64(); | ||||
|         f64 elapsedTime = now - loopStartTime; | ||||
|         expectedTime += (targetTime - curTime) / (f64) numFramesToDraw; | ||||
|         f64 delay = (expectedTime - elapsedTime) * 1000.0; | ||||
|         if (delay > 0.0) { | ||||
|             WAPI.delay((u32)delay); | ||||
|         if (!configUncappedFramerate && !configWindow.vsync) { | ||||
|             f64 now = clock_elapsed_f64(); | ||||
|             f64 elapsedTime = now - loopStartTime; | ||||
|             expectedTime += (targetTime - curTime) / (f64) numFramesToDraw; | ||||
|             f64 delay = (expectedTime - elapsedTime); | ||||
| 
 | ||||
|             numFramesToDraw--; | ||||
|             if (delay > 0.0) { | ||||
|                 precise_delay_f64(delay); | ||||
|             } | ||||
|         } | ||||
|         numFramesToDraw--; | ||||
| 
 | ||||
|         // send the frame to the screen (should be directly after the delay for good frame pacing)
 | ||||
|         gfx_display_frame(); | ||||
| 
 | ||||
|         sDrawnFrames++; | ||||
|     } while ((curTime = clock_elapsed_f64()) < targetTime && numFramesToDraw > 0); | ||||
| 
 | ||||
|     // compute and update the frame rate every second
 | ||||
|  | @ -271,7 +276,7 @@ inline static void buffer_audio(void) { | |||
|     for (s32 i = 0; i < 2; i++) { | ||||
|         create_next_audio_buffer(sAudioBuffer + i * (numAudioSamples * 2), numAudioSamples); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     if (!shouldMute) { | ||||
|         for (u16 i=0; i < ARRAY_COUNT(sAudioBuffer); i++) { | ||||
|             sAudioBuffer[i] *= gMasterVolume; | ||||
|  | @ -326,6 +331,10 @@ void produce_one_frame(void) { | |||
| 
 | ||||
| // used for rendering 2D scenes fullscreen like the loading or crash screens
 | ||||
| void produce_one_dummy_frame(void (*callback)(), u8 clearColorR, u8 clearColorG, u8 clearColorB) { | ||||
|     // measure frame start time
 | ||||
|     f64 frameStart = clock_elapsed_f64(); | ||||
|     f64 targetFrameTime = 1.0 / 60.0; // update at 60fps
 | ||||
| 
 | ||||
|     // start frame
 | ||||
|     gfx_start_frame(); | ||||
|     config_gfx_pool(); | ||||
|  | @ -353,6 +362,15 @@ void produce_one_dummy_frame(void (*callback)(), u8 clearColorR, u8 clearColorG, | |||
|     alloc_display_list(0); | ||||
|     gfx_run((Gfx*) gGfxSPTask->task.t.data_ptr); // send_display_list
 | ||||
|     display_and_vsync(); | ||||
| 
 | ||||
|     // delay to go easy on the cpu
 | ||||
|     f64 frameEnd = clock_elapsed_f64(); | ||||
|     f64 elapsed = frameEnd - frameStart; | ||||
|     f64 remaining = targetFrameTime - elapsed; | ||||
|     if (remaining > 0) { | ||||
|         WAPI.delay((u32)(remaining * 1000.0)); | ||||
|     } | ||||
| 
 | ||||
|     gfx_end_frame(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include "game/save_file.h" | ||||
| #include "engine/math_util.h" | ||||
| #include "pc/configfile.h" | ||||
| #include "pc/pc_main.h" | ||||
| 
 | ||||
| float smooth_step(float edge0, float edge1, float x) { | ||||
|     float t = (x - edge0) / (edge1 - edge0); | ||||
|  | @ -90,6 +91,25 @@ bool clock_is_date(u8 month, u8 day) { | |||
|     return tm_info->tm_mon == month - 1 && tm_info->tm_mday == day; | ||||
| } | ||||
| 
 | ||||
| // delay functions lack accuracy sometimes due to os scheduling
 | ||||
| // busy-waiting is bad practice but it's very accurate so we use a hybrid
 | ||||
| void precise_delay_f64(f64 delaySec) { | ||||
|     const f64 sleepMargin = 0.002; // 2 ms margin before we switch to busy-waiting
 | ||||
| 
 | ||||
|     f64 start = clock_elapsed_f64(); | ||||
|     f64 end = start + delaySec; | ||||
| 
 | ||||
|     // sleep until we're ~2ms away from the target
 | ||||
|     for (f64 remaining = end - clock_elapsed_f64(); remaining > sleepMargin; remaining = end - clock_elapsed_f64()) { | ||||
|         u32 sleepMs = (u32) ((remaining - sleepMargin) * 1000.0); | ||||
|         if (sleepMs < 1) { break; } // not enough time to sleep
 | ||||
|         WAPI.delay(sleepMs); | ||||
|     } | ||||
| 
 | ||||
|     // busy-wait until the target time is hit
 | ||||
|     while (clock_elapsed_f64() < end); | ||||
| } | ||||
| 
 | ||||
| void file_get_line(char* buffer, size_t maxLength, FILE* fp) { | ||||
|     char* initial = buffer; | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,6 +19,8 @@ u32 clock_elapsed_ticks(void); | |||
| /* |description|Checks whether it is the day given|descriptionEnd| */ | ||||
| bool clock_is_date(u8 month, u8 day); | ||||
| 
 | ||||
| void precise_delay_f64(f64 delaySec); | ||||
| 
 | ||||
| void file_get_line(char* buffer, size_t maxLength, FILE* fp); | ||||
| 
 | ||||
| /* |description|Linearly interpolates between `a` and `b` with `delta`|descriptionEnd| */ | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Isaac0-dev
						Isaac0-dev