14
14
// You should have received a copy of the GNU General Public License
15
15
// along with Sulis. If not, see <http://www.gnu.org/licenses/>
16
16
17
+ use std:: num:: NonZeroU32 ;
17
18
use std:: time;
18
19
use std:: collections:: HashMap ;
19
20
use std:: error:: Error ;
@@ -25,11 +26,16 @@ use crate::resource::ResourceSet;
25
26
use crate :: ui:: { Cursor , Widget } ;
26
27
use crate :: util:: { Point , get_elapsed_millis} ;
27
28
29
+ use glium:: glutin:: context:: ContextAttributesBuilder ;
30
+ use glium:: glutin:: display:: GetGlDisplay ;
31
+ use glium:: glutin:: prelude:: { GlDisplay , NotCurrentGlContext } ;
28
32
use glium:: winit:: application:: ApplicationHandler ;
29
- use glium:: CapabilitiesSource ;
33
+ use glium:: winit:: raw_window_handle:: { HandleError , HasWindowHandle } ;
34
+ use glium:: { CapabilitiesSource , Display } ;
30
35
use glium:: backend:: Facade ;
36
+ use glium:: backend:: glutin:: simple_window_builder:: GliumEventLoop ;
31
37
use glium:: glutin:: config:: { ColorBufferType , ConfigTemplateBuilder } ;
32
- use glium:: glutin:: surface:: WindowSurface ;
38
+ use glium:: glutin:: surface:: { GlSurface , SurfaceAttributesBuilder , SwapInterval , WindowSurface } ;
33
39
use glium:: texture:: { RawImage2d , Texture2d } ;
34
40
use glium:: uniforms:: { MagnifySamplerFilter , MinifySamplerFilter , Sampler } ;
35
41
use glium:: winit:: dpi:: { LogicalPosition , LogicalSize } ;
@@ -39,6 +45,7 @@ use glium::winit::window::{Fullscreen, Window, WindowId};
39
45
use glium:: winit:: keyboard:: { KeyCode , PhysicalKey } ;
40
46
use glium:: winit:: event:: { ElementState , KeyEvent , MouseButton , MouseScrollDelta , StartCause , WindowEvent } ;
41
47
use glium:: { self , Rect , Surface } ;
48
+ use glutin_winit:: DisplayBuilder ;
42
49
43
50
const VERTEX_SHADER_SRC : & str = r#"
44
51
#version 140
@@ -345,9 +352,53 @@ fn get_monitor(window: &Window) -> MonitorHandle {
345
352
window. primary_monitor ( ) . unwrap ( )
346
353
}
347
354
355
+ #[ derive( Debug ) ]
356
+ pub enum DisplayCreationError {
357
+ CreateWindow ,
358
+ BuildDisplayError ( Box < dyn Error > ) ,
359
+ WindowHandle ( HandleError ) ,
360
+ WindowSurface ( glium:: glutin:: error:: Error ) ,
361
+ GlContext ( glium:: glutin:: error:: Error ) ,
362
+ GlContextCurrent ( glium:: glutin:: error:: Error ) ,
363
+ GlVersion ( glium:: IncompatibleOpenGl ) ,
364
+ SetVsync ( glium:: glutin:: error:: Error ) ,
365
+ }
366
+
367
+ impl std:: fmt:: Display for DisplayCreationError {
368
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
369
+ use DisplayCreationError :: * ;
370
+ match self {
371
+ CreateWindow => write ! ( f, "Unable to create display" ) ,
372
+ BuildDisplayError ( e) => write ! ( f, "Build display error: {e}" ) ,
373
+ WindowHandle ( e) => write ! ( f, "Unable to get window handle {e}" ) ,
374
+ WindowSurface ( e) => write ! ( f, "Unable to setup window surface {e}" ) ,
375
+ GlContext ( e) => write ! ( f, "Unable to create GL context {e}" ) ,
376
+ GlContextCurrent ( e) => write ! ( f, "Unable to make GL context current {e}" ) ,
377
+ GlVersion ( e) => write ! ( f, "GL Version incompatible: {e}" ) ,
378
+ SetVsync ( e) => write ! ( f, "Unable to set vsync mode: {e}" ) ,
379
+ }
380
+ }
381
+ }
382
+
383
+ impl Error for DisplayCreationError {
384
+ fn source ( & self ) -> Option < & ( dyn Error + ' static ) > {
385
+ use DisplayCreationError :: * ;
386
+ match self {
387
+ CreateWindow => None ,
388
+ BuildDisplayError ( e) => Some ( e. as_ref ( ) ) ,
389
+ WindowHandle ( e) => Some ( e) ,
390
+ WindowSurface ( e) => Some ( e) ,
391
+ GlContext ( e) => Some ( e) ,
392
+ GlContextCurrent ( e) => Some ( e) ,
393
+ GlVersion ( e) => Some ( e) ,
394
+ SetVsync ( e) => Some ( e) ,
395
+ }
396
+ }
397
+ }
398
+
348
399
fn try_get_display (
349
400
event_loop : & EventLoop < ( ) > ,
350
- ) -> Result < ( glium:: Display < WindowSurface > , Window ) , glium :: backend :: glutin :: DisplayCreationError > {
401
+ ) -> Result < ( glium:: Display < WindowSurface > , Window ) , DisplayCreationError > {
351
402
let ( res_x, res_y) = Config :: display_resolution ( ) ;
352
403
let vsync = Config :: vsync_enabled ( ) ;
353
404
@@ -363,10 +414,45 @@ fn try_get_display(
363
414
. with_alpha_size ( 8 )
364
415
. with_buffer_type ( ColorBufferType :: Rgb { r_size : 8 , g_size : 8 , b_size : 8 } ) ;
365
416
366
- let ( window, display) = glium:: backend:: glutin:: SimpleWindowBuilder :: new ( )
367
- . set_window_builder ( attrs)
368
- . with_config_template_builder ( config_template_builder)
369
- . build ( event_loop) ;
417
+ let display_builder = DisplayBuilder :: new ( ) . with_window_attributes ( Some ( attrs) ) ;
418
+
419
+ let ( window, gl_config) = event_loop. build ( display_builder, config_template_builder, |mut configs| {
420
+ configs. next ( ) . unwrap ( )
421
+ } ) . map_err ( DisplayCreationError :: BuildDisplayError ) ?;
422
+
423
+ let window = match window {
424
+ None => return Err ( DisplayCreationError :: CreateWindow ) ,
425
+ Some ( window) => window,
426
+ } ;
427
+
428
+ let handle = window. window_handle ( ) . map_err ( DisplayCreationError :: WindowHandle ) ?;
429
+
430
+ let ( w, h) : ( u32 , u32 ) = window. inner_size ( ) . into ( ) ;
431
+
432
+ let surface_attrs = SurfaceAttributesBuilder :: < WindowSurface > :: new ( )
433
+ . build ( handle. into ( ) , NonZeroU32 :: new ( w) . unwrap ( ) , NonZeroU32 :: new ( h) . unwrap ( ) ) ;
434
+
435
+ let surface = unsafe {
436
+ gl_config. display ( ) . create_window_surface ( & gl_config, & surface_attrs) . map_err ( DisplayCreationError :: WindowSurface ) ?
437
+ } ;
438
+
439
+ let swap_interval = if vsync {
440
+ SwapInterval :: Wait ( NonZeroU32 :: new ( 1 ) . unwrap ( ) )
441
+ } else {
442
+ SwapInterval :: DontWait
443
+ } ;
444
+
445
+ let context_attributes = ContextAttributesBuilder :: new ( ) . build ( Some ( handle. into ( ) ) ) ;
446
+
447
+ let context = unsafe {
448
+ gl_config. display ( ) . create_context ( & gl_config, & context_attributes) . map_err ( DisplayCreationError :: GlContext ) ?
449
+ } ;
450
+
451
+ let current_context = context. make_current ( & surface) . map_err ( DisplayCreationError :: GlContextCurrent ) ?;
452
+
453
+ surface. set_swap_interval ( & current_context, swap_interval) . map_err ( DisplayCreationError :: SetVsync ) ?;
454
+
455
+ let display = Display :: from_context_surface ( current_context, surface) . map_err ( DisplayCreationError :: GlVersion ) ?;
370
456
371
457
Ok ( ( display, window) )
372
458
}
@@ -573,14 +659,19 @@ struct GliumApp {
573
659
impl ApplicationHandler for GliumApp {
574
660
fn resumed ( & mut self , _event_loop : & ActiveEventLoop ) { }
575
661
576
- fn new_events ( & mut self , _event_loop : & ActiveEventLoop , _cause : StartCause ) {
577
- self . last_elapsed = get_elapsed_millis ( self . last_start_time . elapsed ( ) ) ;
578
- self . last_start_time = time:: Instant :: now ( ) ;
579
- self . total_elapsed = get_elapsed_millis ( self . main_loop_start_time . elapsed ( ) ) ;
662
+ fn new_events ( & mut self , _event_loop : & ActiveEventLoop , cause : StartCause ) {
663
+ if Config :: vsync_enabled ( ) {
664
+ self . io . window . request_redraw ( ) ;
665
+ }
666
+
667
+ if let StartCause :: ResumeTimeReached { .. } = cause {
668
+ self . io . window . request_redraw ( ) ;
669
+ }
580
670
}
581
671
582
672
fn exiting ( & mut self , _event_loop : & ActiveEventLoop ) {
583
673
let secs = self . render_time . as_secs ( ) as f64 + self . render_time . subsec_nanos ( ) as f64 * 1e-9 ;
674
+ log:: info!( "Runtime: {:.2} seconds" , ( time:: Instant :: now( ) - self . main_loop_start_time) . as_secs_f32( ) ) ;
584
675
info ! (
585
676
"Rendered {} frames with total render time {:.4} seconds" ,
586
677
self . frames, secs
@@ -591,10 +682,6 @@ impl ApplicationHandler for GliumApp {
591
682
) ;
592
683
}
593
684
594
- fn about_to_wait ( & mut self , _event_loop : & ActiveEventLoop ) {
595
- self . io . window . request_redraw ( ) ;
596
- }
597
-
598
685
fn window_event (
599
686
& mut self ,
600
687
event_loop : & ActiveEventLoop ,
@@ -612,6 +699,10 @@ impl ApplicationHandler for GliumApp {
612
699
event_loop. exit ( ) ;
613
700
} ,
614
701
WindowEvent :: RedrawRequested => {
702
+ self . last_elapsed = get_elapsed_millis ( self . last_start_time . elapsed ( ) ) ;
703
+ self . last_start_time = time:: Instant :: now ( ) ;
704
+ self . total_elapsed = get_elapsed_millis ( self . main_loop_start_time . elapsed ( ) ) ;
705
+
615
706
// merge all mouse move events into at most one per frame
616
707
if let Some ( ( mouse_x, mouse_y) ) = self . mouse_move {
617
708
InputAction :: mouse_move ( mouse_x, mouse_y) . handle ( & self . root ) ;
@@ -644,7 +735,10 @@ impl ApplicationHandler for GliumApp {
644
735
645
736
self . render_time += self . last_start_time . elapsed ( ) ;
646
737
self . frames += 1 ;
647
- event_loop. set_control_flow ( ControlFlow :: WaitUntil ( time:: Instant :: now ( ) + self . frame_time ) ) ;
738
+
739
+ if !Config :: vsync_enabled ( ) {
740
+ event_loop. set_control_flow ( ControlFlow :: WaitUntil ( self . last_start_time + self . frame_time ) ) ;
741
+ }
648
742
} ,
649
743
WindowEvent :: CursorMoved { position, .. } => {
650
744
let mouse_x = ( self . ui_x as f64 * position. x / self . display_size . width ) as f32 / self . scale as f32 ;
0 commit comments