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