1 | #include <chrono> |
2 | #include <atomic> |
3 | #include <thread> |
4 |
|
5 | #include "ZydecoCommon.hpp" |
6 | #include "Engine.hpp" |
7 | #include "IEventHandler.hpp" |
8 | #include "IWindow.hpp" |
9 |
|
10 |
|
11 | Logger LOGGER("ENGINE"); |
12 | Logger EVENT_LOGGER("ENGINE::EVENT"); |
13 | Logger RENDER_LOGGER("ENGINE::RENDER"); |
14 |
|
15 |
|
16 | Engine::Engine(IEventHandler& r_event_handler, IWindow& r_window): |
17 | m_rEventHandler(r_event_handler), |
18 | m_rWindow(r_window), |
19 | m_framerate(60), |
20 | m_frameRenderTimeUs(static_cast<int64_t>(1'000'000 / m_framerate)) |
21 | { |
22 | LOGGER.Log(Logger::DEBUG, "Engine creating"); |
23 | LOGGER.Log(Logger::VERBOSE, "Frame render time: {}us", m_frameRenderTimeUs.count()); |
24 |
|
25 | r_event_handler.RegisterQuitEventSubscriber(this); |
26 |
|
27 | std::atomic_init(&m_aIsExiting, false); |
28 |
|
29 | LOGGER.Log(Logger::DEBUG, "Engine created"); |
30 | } |
31 |
|
32 | Engine::~Engine() |
33 | { |
34 | LOGGER.Log(Logger::DEBUG, "Engine destroyed"); |
35 | } |
36 |
|
37 | void Engine::OnQuitEvent() |
38 | { |
39 | LOGGER.Log(Logger::VERBOSE, "QuitEvent occurred. Trigger exit."); |
40 |
|
41 | m_aIsExiting.store(true); |
42 | } |
43 |
|
44 | void Engine::Execute() |
45 | { |
46 | LOGGER.Log(Logger::INFO, "Entering engine loop (thread handler)"); |
47 |
|
48 | LOGGER.Log(Logger::DEBUG, "Starting event handler thread"); |
49 | std::thread event_thread(&Engine::DoEventLoop, this); |
50 | m_threads["Event Handler"] = &event_thread; |
51 |
|
52 | LOGGER.Log(Logger::DEBUG, "Starting render thread"); |
53 | std::thread render_thread(&Engine::DoRenderLoop, this); |
54 | m_threads["Renderer"] = &render_thread; |
55 |
|
56 | // std::thread(&Engine::DoEventLoop, this); |
57 | // std::thread(&Engine::DoGameLoop, this); |
58 |
|
59 | // Step clock while subsystems are running |
60 | while (m_aIsExiting.load() == false) |
61 | { |
62 | m_currentTime = std::chrono::time_point_cast<std::chrono::microseconds, std::chrono::steady_clock, std::chrono::nanoseconds>( |
63 | std::chrono::steady_clock::now() |
64 | ); |
65 | std::this_thread::sleep_for(std::chrono::microseconds(100)); |
66 | } |
67 |
|
68 | // Subsystem signaled exiting |
69 | LOGGER.Log(Logger::DEBUG, "Signal to quit"); |
70 |
|
71 | // Wait for program completion |
72 | for (auto active_thread : m_threads) |
73 | { |
74 | while (active_thread.second->joinable()) { |
75 | LOGGER.Log(Logger::DEBUG, "Waiting thread {} to exit", active_thread.first); |
76 | active_thread.second->join(); |
77 | } |
78 | } |
79 |
|
80 | LOGGER.Log(Logger::INFO, "Exiting engine loop (thread handler)"); |
81 | } |
82 |
|
83 | void Engine::DoEventLoop() |
84 | { |
85 | EVENT_LOGGER.Log(Logger::DEBUG, "Starting event loop"); |
86 |
|
87 | m_rEventHandler.Update(0); |
88 |
|
89 | EVENT_LOGGER.Log(Logger::DEBUG, "Exiting event loop"); |
90 | m_aIsExiting.store(true); |
91 | } |
92 |
|
93 | void Engine::DoRenderLoop() |
94 | { |
95 | RENDER_LOGGER.Log(Logger::DEBUG, "Starting render loop"); |
96 |
|
97 | std::chrono::time_point<std::chrono::steady_clock, std::chrono::microseconds> expected_end_time; |
98 | std::chrono::microseconds time_since_last_tick; |
99 | std::chrono::microseconds remaining_time_before_next_tick; |
100 |
|
101 | // Loop while not exiting |
102 | while (m_aIsExiting.load() == false) |
103 | { |
104 | // Calculate time since last tick, update tick start/end times |
105 | time_since_last_tick = std::chrono::duration_cast<std::chrono::microseconds>(m_currentTime - m_frameRenderStartTime); |
106 | m_frameRenderStartTime = m_currentTime; |
107 | expected_end_time = m_currentTime + m_frameRenderTimeUs; |
108 |
|
109 | // Update system with time since last tick |
110 | bool exiting = m_rWindow.Update(time_since_last_tick.count()); |
111 | if (exiting) { break; } |
112 |
|
113 | // Calculate remaining tick time; if >0, sleep until next tick |
114 | remaining_time_before_next_tick = expected_end_time - m_currentTime; |
115 | if (remaining_time_before_next_tick.count() > 0) |
116 | { |
117 | std::this_thread::sleep_until(expected_end_time); |
118 | } |
119 | else |
120 | { |
121 | int64_t tick_time = (m_currentTime - m_frameRenderStartTime).count(); |
122 | RENDER_LOGGER.Log(Logger::WARNING, "Overutilized! Tick time: {}us ({} ticks per second)", tick_time, 1'000'000./(float)tick_time); |
123 | } |
124 | } |
125 |
|
126 | RENDER_LOGGER.Log(Logger::INFO, "Exiting render loop"); |
127 | m_aIsExiting.store(true); |
128 | } |
129 |
|
130 | /* |
131 | class Engine |
132 | { |
133 | public: |
134 | Engine(); |
135 | ~Engine(); |
136 | |
137 | void Start(); |
138 | void Kill(); |
139 | |
140 | void Update(); |
141 | |
142 | protected: |
143 | Window m_sdlWindow; |
144 | Scene m_scene; |
145 | Renderer m_renderer; |
146 | EventHandler m_eventHandler; |
147 | }; |
148 | */ |
149 |
|