package bogue

  1. Overview
  2. Docs

Source file b_time.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
(*

Warning: (from the SDL wiki)

SDL_GetTicks

This function is not recommended as of SDL 2.0.18; use SDL_GetTicks64() instead, where the value doesn't wrap every ~49 days. There are places in SDL where we provide a 32-bit timestamp that can not change without breaking binary compatibility, though, so this function isn't officially deprecated.

*)


open Tsdl
open B_utils

type t = int (* 1/1000 sec *)

let (+) t1 t2 = t1 + t2

let (-) t1 t2 = t1 - t2

let add t1 t2 = t1 + t2

let length t1 t2 = t2 - t1

let compare (t1 : t) (t2 : t) =
  Stdlib.compare t1 t2

let (>>) (t1 : t) (t2 : t) =
  t1 > t2

let float t = float t

(* Do not use! it is NOT nice to other threads *)
let delay_old d = Sdl.delay (Int32.of_int d);; (* attention ça freeze si c'est négatif *)

(* we use this instead *)
let delay x = Thread.delay (float x /. 1000.)

(* in principle one should use Int32.unsigned_to_int. This is ok until 2^31 -1,
   ie. about 24 days. TODO change this? *)
let now () : t = Int32.to_int (Sdl.get_ticks ())

let make_fps ?(min_delay=5) () =
  assert (min_delay >= 0);
  let start = ref 0 in
  (fun () -> start := now ()),
  fun fps ->
    if !start = 0 then (delay min_delay; start := now ())
    else
      let round_trip = now () - !start in begin
        let wait = max min_delay ((1000 / fps) - round_trip) in
        printd debug_graphics "FPS:%u (round_trip=%u)\n" (1000 / (round_trip + wait)) round_trip;
        if wait > 0 then
          delay wait;
        start := now ();
      end

let set_swap_interval =
    let swap_interval = ref min_int in
    fun desired ->
      if !swap_interval <> desired then begin
        match Sdl.gl_set_swap_interval desired with
        | Ok () ->
          swap_interval := desired;
          true;
        | Error (`Msg m) ->
          printd (debug_graphics+debug_warning) "Failed to set desired swap interval to %u: %s" desired m;
          false
      end else true

let adaptive_fps ?(vsync=false) fps =
  let start = ref 0 in
  let frame = ref 1 in
  let total_wait = ref 0 in (* only for debugging *)
  let vsync_used = ref false in

  (* the start function *)
  (fun () ->
     start := now ();
     total_wait := 0;
     if vsync then begin
       vsync_used := set_swap_interval 1;
       printd debug_graphics "VSync used: %b" !vsync_used;
     end;
     frame := 1),

  (* the main function *)
  fun () ->
    if !start = 0 then (delay 5; start := now (); assert(false))
    else
      let elapsed = now () - !start in
      let theoric = 1000 * !frame / fps in (* theoric time after this number of frames *)
      let wait = theoric - elapsed in
      total_wait := !total_wait + wait;
      let wait =
        if wait < 5
        then begin
          printd (debug_graphics + debug_warning)
            "Cannot keep up required FPS=%u (wait=%d)" fps wait;
          (* this can also happen when the animation was stopped; we reset the
             counter *)
          frame := 0;
          total_wait := 0;
          start := now ();
          if !vsync_used then
            (* turn on adaptive vsync if supported *)
            if set_swap_interval (-1) then
              printd (debug_graphics + debug_warning) "Adaptive VSync enabled"
            else begin
              printd (debug_graphics + debug_warning) "Disabling VSync";
              (* fall back to turning vsync off *)
              let (_:bool) = set_swap_interval 0 in
              vsync_used := false
            end;
          5
        end
        else if !vsync_used then
          (* trust VSync and the released runtime lock in Sdl.render_present
             to maintain FPS, but use the usual 5ms to allow other OCaml code
             to run if needed
          *)
          5
        else (printd debug_graphics "Wait=%u, Avg.=%u" wait (!total_wait / !frame);
              wait) in
      delay wait;
      incr frame;
      if !frame > 1000000 (* set lower? *)
      then (printd debug_graphics "Reset FPS counter";
            frame := 1;
            total_wait := 0;
            start := now ())