1 /*
2  * Copyright (C) 2014 Tim Mayberry <mojofunk@gmail.com>
3  * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include "timers.h"
21 
22 #include "pbd/timer.h"
23 #include "pbd/debug.h"
24 #include "pbd/compose.h"
25 #include "pbd/g_atomic_compat.h"
26 
27 #include "debug.h"
28 
29 namespace {
30 
31 class StandardTimer : public PBD::StandardTimer
32 {
33 public:
StandardTimer(unsigned int interval)34 	StandardTimer (unsigned int interval)
35 		: PBD::StandardTimer(interval)
36 	{ }
37 
on_elapsed()38 	virtual bool on_elapsed () {
39 		DEBUG_TIMING_ADD_ELAPSED(PBD::DEBUG::GUITiming, timing_interval_data);
40 		DEBUG_TIMING_START(PBD::DEBUG::GUITiming, timing_exec_data);
41 
42 		bool ret_val = PBD::StandardTimer::on_elapsed ();
43 
44 		DEBUG_TIMING_ADD_ELAPSED(PBD::DEBUG::GUITiming, timing_exec_data);
45 		DEBUG_TIMING_START(PBD::DEBUG::GUITiming, timing_interval_data);
46 		return ret_val;
47 	}
48 
49 #ifndef NDEBUG
50 	PBD::TimingData timing_interval_data;
51 	PBD::TimingData timing_exec_data;
52 #endif
53 };
54 
55 class BlinkTimer : public PBD::BlinkTimer
56 {
57 public:
BlinkTimer(unsigned int interval)58 	BlinkTimer (unsigned int interval)
59 		: PBD::BlinkTimer(interval)
60 	{ }
61 
on_elapsed()62 	virtual bool on_elapsed () {
63 		DEBUG_TIMING_ADD_ELAPSED(PBD::DEBUG::GUITiming, timing_interval_data);
64 		DEBUG_TIMING_START(PBD::DEBUG::GUITiming, timing_exec_data);
65 
66 		bool ret_val = PBD::BlinkTimer::on_elapsed ();
67 
68 		DEBUG_TIMING_ADD_ELAPSED(PBD::DEBUG::GUITiming, timing_exec_data);
69 		DEBUG_TIMING_START(PBD::DEBUG::GUITiming, timing_interval_data);
70 		return ret_val;
71 	}
72 
73 #ifndef NDEBUG
74 	PBD::TimingData timing_interval_data;
75 	PBD::TimingData timing_exec_data;
76 #endif
77 };
78 
79 
80 class UITimers
81 {
82 
83 public:
84 
UITimers()85 	UITimers ()
86 		: blink(240)
87 		, second(1000)
88 		, rapid(100)
89 		, super_rapid(40)
90 		, fps(40)
91 	{
92 	g_atomic_int_set (&_suspend_counter, 0);
93 #ifndef NDEBUG
94 		second.connect (sigc::mem_fun (*this, &UITimers::on_second_timer));
95 #endif
96 	}
97 
98 	BlinkTimer      blink;
99 	StandardTimer   second;
100 	StandardTimer   rapid;
101 	StandardTimer   super_rapid;
102 	StandardTimer   fps;
103 
104 	GATOMIC_QUAL gint _suspend_counter;
105 
106 #ifndef NDEBUG
107 	std::vector<int64_t> rapid_eps_count;
108 	std::vector<int64_t> super_rapid_eps_count;
109 	std::vector<int64_t> fps_eps_count;
110 
111 private:
112 
debug_rapid_timer()113 	void debug_rapid_timer () {
114 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("Rapid Connections: %1\n", rapid.connection_count ()));
115 
116 		rapid_eps_count.push_back (rapid.timing_exec_data.size());
117 
118 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("Rapid Exec Totals: %1", PBD::timing_summary (rapid_eps_count)));
119 
120 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("Rapid Interval: %1", rapid.timing_interval_data.summary()));
121 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("Rapid Exec: %1", rapid.timing_exec_data.summary()));
122 		DEBUG_TIMING_RESET(PBD::DEBUG::GUITiming, rapid.timing_interval_data);
123 		DEBUG_TIMING_RESET(PBD::DEBUG::GUITiming, rapid.timing_exec_data);
124 	}
125 
debug_super_rapid_timer()126 	void debug_super_rapid_timer () {
127 		// we don't use this timer on windows so don't display empty data for it
128 #ifndef PLATFORM_WINDOWS
129 
130 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("Super Rapid Connections: %1\n", super_rapid.connection_count ()));
131 
132 		super_rapid_eps_count.push_back (super_rapid.timing_exec_data.size());
133 
134 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("Super Rapid Exec Totals: %1", PBD::timing_summary (super_rapid_eps_count)));
135 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("Super Rapid Interval: %1", super_rapid.timing_interval_data.summary()));
136 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("Super Rapid Exec: %1", super_rapid.timing_exec_data.summary()));
137 		DEBUG_TIMING_RESET(PBD::DEBUG::GUITiming, super_rapid.timing_interval_data);
138 		DEBUG_TIMING_RESET(PBD::DEBUG::GUITiming, super_rapid.timing_exec_data);
139 #endif
140 	}
141 
debug_fps_timer()142 	void debug_fps_timer () {
143 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("FPS Connections: %1\n", fps.connection_count ()));
144 
145 		fps_eps_count.push_back (fps.timing_exec_data.size());
146 
147 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("FPS Exec Totals: %1", PBD::timing_summary (fps_eps_count)));
148 
149 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("FPS Interval: %1", fps.timing_interval_data.summary()));
150 		DEBUG_TRACE(PBD::DEBUG::GUITiming, string_compose ("FPS Exec: %1", fps.timing_exec_data.summary()));
151 		DEBUG_TIMING_RESET(PBD::DEBUG::GUITiming, fps.timing_interval_data);
152 		DEBUG_TIMING_RESET(PBD::DEBUG::GUITiming, fps.timing_exec_data);
153 	}
154 
on_second_timer()155 	void on_second_timer () {
156 		debug_rapid_timer ();
157 		debug_super_rapid_timer ();
158 		debug_fps_timer ();
159 	}
160 #endif
161 };
162 
163 UITimers&
get_timers()164 get_timers ()
165 {
166 	static UITimers timers;
167 	return timers;
168 }
169 
170 } // anon namespace
171 
172 namespace Timers {
173 
174 sigc::connection
blink_connect(const sigc::slot<void,bool> & slot)175 blink_connect(const sigc::slot<void,bool>& slot)
176 {
177 	return get_timers().blink.connect (slot);
178 }
179 
180 sigc::connection
second_connect(const sigc::slot<void> & slot)181 second_connect(const sigc::slot<void>& slot)
182 {
183 	return get_timers().second.connect (slot);
184 }
185 
186 sigc::connection
rapid_connect(const sigc::slot<void> & slot)187 rapid_connect(const sigc::slot<void>& slot)
188 {
189 	return get_timers().rapid.connect (slot);
190 }
191 
192 sigc::connection
super_rapid_connect(const sigc::slot<void> & slot)193 super_rapid_connect(const sigc::slot<void>& slot)
194 {
195 #ifdef PLATFORM_WINDOWS
196 	return get_timers().fps.connect (slot);
197 #else
198 	return get_timers().super_rapid.connect (slot);
199 #endif
200 }
201 
202 void
set_fps_interval(unsigned int interval)203 set_fps_interval (unsigned int interval)
204 {
205 	get_timers().fps.set_interval (interval);
206 }
207 
208 sigc::connection
fps_connect(const sigc::slot<void> & slot)209 fps_connect(const sigc::slot<void>& slot)
210 {
211 	return get_timers().fps.connect (slot);
212 }
213 
TimerSuspender()214 TimerSuspender::TimerSuspender ()
215 {
216 	if (g_atomic_int_add (&get_timers()._suspend_counter, 1) == 0) {
217 		get_timers().rapid.suspend();
218 		get_timers().super_rapid.suspend();
219 		get_timers().fps.suspend();
220 	}
221 }
222 
~TimerSuspender()223 TimerSuspender::~TimerSuspender ()
224 {
225 	if (g_atomic_int_dec_and_test (&get_timers()._suspend_counter)) {
226 		get_timers().rapid.resume();
227 		get_timers().super_rapid.resume();
228 		get_timers().fps.resume();
229 	}
230 }
231 
232 } // namespace Timers
233