Line data Source code
1 : /**
2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "XLApplication.h"
24 : #include "XLEvent.h"
25 : #include "XLEventHandler.h"
26 : #include "XLResourceCache.h"
27 : #include "XLCoreDevice.h"
28 : #include "XLCoreQueue.h"
29 :
30 :
31 : #if MODULE_XENOLITH_SCENE
32 :
33 : #include "XLView.h"
34 :
35 : #endif
36 :
37 :
38 : #if MODULE_XENOLITH_FONT
39 :
40 : #include "XLFontExtension.h"
41 :
42 : #endif
43 :
44 :
45 : namespace STAPPLER_VERSIONIZED stappler::xenolith {
46 :
47 : static Application *s_mainLoop = nullptr;
48 :
49 : XL_DECLARE_EVENT_CLASS(Application, onMessageToken)
50 : XL_DECLARE_EVENT_CLASS(Application, onRemoteNotification)
51 :
52 16314 : Application *Application::getInstance() {
53 16314 : return s_mainLoop;
54 : }
55 :
56 45 : Application::~Application() {
57 30 : if (_updatePool) {
58 30 : memory::pool::destroy(_updatePool);
59 : }
60 :
61 30 : _instance = nullptr;
62 :
63 30 : if (s_mainLoop == this) {
64 30 : s_mainLoop = nullptr;
65 : }
66 45 : }
67 :
68 30 : bool Application::init(CommonInfo &&info, Rc<core::Instance> &&instance) {
69 30 : if (instance == nullptr) {
70 0 : return false;
71 : }
72 :
73 30 : s_mainLoop = this;
74 :
75 30 : _info = move(info);
76 30 : _info.applicationVersionCode = XL_MAKE_API_VERSION(_info.applicationVersion);
77 30 : _name = _info.applicationName;
78 30 : _instance = move(instance);
79 30 : _updatePool = memory::pool::create(static_cast<memory::pool_t *>(nullptr));
80 30 : return true;
81 : }
82 :
83 30 : void Application::run(const CallbackInfo &cb, core::LoopInfo &&loopInfo, uint32_t threadsCount, TimeInterval iv) {
84 30 : _shouldQuit.test_and_set();
85 30 : _threadId = std::this_thread::get_id();
86 30 : _resourceCache = Rc<ResourceCache>::create(this);
87 :
88 30 : auto tmpStarted = move(loopInfo.onDeviceStarted);
89 30 : auto tmpFinalized = move(loopInfo.onDeviceFinalized);
90 :
91 90 : loopInfo.onDeviceStarted = [this, tmpStarted = move(tmpStarted)] (const core::Loop &loop, const core::Device &dev) {
92 30 : if (tmpStarted) {
93 0 : tmpStarted(loop, dev);
94 : }
95 :
96 30 : handleDeviceStarted(loop, dev);
97 30 : };
98 60 : loopInfo.onDeviceFinalized = [this, tmpFinalized = move(tmpFinalized)] (const core::Loop &loop, const core::Device &dev) {
99 30 : handleDeviceFinalized(loop, dev);
100 :
101 30 : if (tmpFinalized) {
102 0 : tmpFinalized(loop, dev);
103 : }
104 30 : };
105 :
106 30 : auto loop = _instance->makeLoop(move(loopInfo));
107 :
108 30 : if (!spawnWorkers(thread::TaskQueue::Flags::Waitable, 0, threadsCount, _name)) {
109 0 : log::error("MainLoop", "Fail to spawn worker threads");
110 0 : return;
111 : }
112 :
113 30 : if (cb.initCallback) {
114 15 : cb.initCallback(*this);
115 : }
116 :
117 75 : for (auto &it : _extensions) {
118 45 : it.second->initialize(this);
119 : }
120 :
121 30 : nativeInit();
122 :
123 30 : loop->waitRinning();
124 :
125 30 : _glLoop = move(loop);
126 :
127 30 : _resourceCache->initialize(*_glLoop);
128 :
129 45 : for (auto &it : _glWaitCallback) {
130 15 : _glLoop->performOnGlThread(move(it.func), it.target, it.immediate);
131 : }
132 30 : _glWaitCallback.clear();
133 :
134 : #if MODULE_XENOLITH_FONT
135 30 : auto lib = Rc<font::FontExtension>::create(this, _instance->makeFontQueue());
136 :
137 30 : auto builder = lib->makeDefaultControllerBuilder("ApplicationFontController");
138 :
139 30 : addExtension(lib->acquireController(move(builder)));
140 30 : addExtension(move(lib));
141 : #endif
142 :
143 30 : uint32_t count = 0;
144 30 : uint64_t clock = platform::clock(core::ClockType::Monotonic);
145 30 : uint64_t lastUpdate = clock;
146 30 : uint64_t startTime = clock;
147 :
148 30 : _time.delta = 0;
149 30 : _time.global = clock;
150 30 : _time.app = 0;
151 30 : _time.dt = 0.0f;
152 :
153 30 : update(cb, _time);
154 :
155 30 : log::debug("Application", "started");
156 :
157 : do {
158 71704 : count = 0;
159 71704 : if (!_immediateUpdate) {
160 71704 : wait(iv - TimeInterval::microseconds(clock - lastUpdate), &count);
161 : }
162 71704 : if (count > 0) {
163 70618 : memory::pool::push(_updatePool);
164 70618 : TaskQueue::update();
165 70618 : memory::pool::pop();
166 70618 : memory::pool::clear(_updatePool);
167 : }
168 71704 : clock = platform::clock(core::ClockType::Monotonic);
169 :
170 71704 : auto dt = TimeInterval::microseconds(clock - lastUpdate);
171 71704 : if (dt >= iv || _immediateUpdate) {
172 2876 : _time.delta = dt.toMicros();
173 2876 : _time.global = clock;
174 2876 : _time.app = startTime - clock;
175 2876 : _time.dt = float(_time.delta) / 1'000'000;
176 :
177 2876 : update(cb, _time);
178 2876 : lastUpdate = clock;
179 2876 : _immediateUpdate = false;
180 : }
181 143408 : } while (_shouldQuit.test_and_set());
182 :
183 30 : if (cb.finalizeCallback) {
184 0 : cb.finalizeCallback(*this);
185 : }
186 :
187 135 : for (auto &it : _extensions) {
188 105 : it.second->invalidate(this);
189 : }
190 :
191 30 : nativeDispose();
192 :
193 30 : _glLoop->cancel();
194 :
195 30 : waitForAll();
196 30 : TaskQueue::update();
197 :
198 30 : _resourceCache->invalidate();
199 :
200 : #if SP_REF_DEBUG
201 : if (_glLoop->getReferenceCount() > 1) {
202 : auto loop = _glLoop.get();
203 : _glLoop = nullptr;
204 :
205 : loop->foreachBacktrace([] (uint64_t id, Time time, const std::vector<std::string> &vec) {
206 : StringStream stream;
207 : stream << "[" << id << ":" << time.toHttp<Interface>() << "]:\n";
208 : for (auto &it : vec) {
209 : stream << "\t" << it << "\n";
210 : }
211 : log::debug("core::Loop", stream.str());
212 : });
213 : } else {
214 : _glLoop = nullptr;
215 : }
216 : #else
217 30 : _glLoop = nullptr;
218 : #endif
219 30 : _resourceCache = nullptr;
220 :
221 30 : cancelWorkers();
222 30 : }
223 :
224 30 : void Application::end() {
225 30 : _shouldQuit.clear();
226 30 : if (!isOnMainThread()) {
227 15 : wakeup();
228 : }
229 30 : }
230 :
231 1691 : void Application::wakeup() {
232 1691 : if (isOnMainThread()) {
233 793 : _immediateUpdate = true;
234 : #if MODULE_XENOLITH_SCENE
235 1586 : for (auto &it : _activeViews) {
236 793 : it->setReadyForNextFrame();
237 : }
238 : #endif
239 : } else {
240 898 : performOnMainThread([this] {
241 898 : _immediateUpdate = true;
242 : #if MODULE_XENOLITH_SCENE
243 1781 : for (auto &it : _activeViews) {
244 883 : it->setReadyForNextFrame();
245 : }
246 : #endif
247 898 : }, this);
248 : }
249 1691 : }
250 :
251 51146 : bool Application::isOnMainThread() const {
252 51146 : return _threadId == std::this_thread::get_id();
253 : }
254 :
255 15 : void Application::performOnGlThread(Function<void()> &&func, Ref *target, bool immediate) const {
256 15 : if (_glLoop) {
257 0 : _glLoop->performOnGlThread(move(func), target, immediate);
258 : } else {
259 15 : _glWaitCallback.emplace_back(WaitCallbackInfo{move(func), target, immediate});
260 : }
261 15 : }
262 :
263 49425 : void Application::performOnMainThread(Function<void()> &&func, Ref *target, bool onNextFrame) {
264 49425 : if (isOnMainThread() && !onNextFrame) {
265 7338 : func();
266 : } else {
267 42087 : TaskQueue::onMainThread(Rc<Task>::create([func = move(func)] (const thread::Task &, bool success) {
268 42088 : if (success) { func(); }
269 42088 : }, target));
270 : }
271 49426 : }
272 :
273 0 : void Application::performOnMainThread(Rc<Task> &&task, bool onNextFrame) {
274 0 : if (isOnMainThread() && !onNextFrame) {
275 0 : task->onComplete();
276 : } else {
277 0 : onMainThread(std::move(task));
278 : }
279 0 : }
280 :
281 120 : void Application::perform(ExecuteCallback &&exec, CompleteCallback &&complete, Ref *obj) {
282 120 : perform(Rc<Task>::create(move(exec), move(complete), obj));
283 120 : }
284 :
285 150 : void Application::perform(Rc<Task> &&task) {
286 150 : TaskQueue::perform(std::move(task));
287 150 : }
288 :
289 0 : void Application::perform(Rc<Task> &&task, bool performFirst) {
290 0 : TaskQueue::perform(std::move(task), performFirst);
291 0 : }
292 :
293 2270 : void Application::addEventListener(const EventHandlerNode *listener) {
294 2270 : auto it = _eventListeners.find(listener->getEventID());
295 2270 : if (it != _eventListeners.end()) {
296 2225 : it->second.insert(listener);
297 : } else {
298 45 : _eventListeners.emplace(listener->getEventID(), HashSet<const EventHandlerNode *>{listener});
299 : }
300 2270 : }
301 :
302 2270 : void Application::removeEventListner(const EventHandlerNode *listener) {
303 2270 : auto it = _eventListeners.find(listener->getEventID());
304 2270 : if (it != _eventListeners.end()) {
305 2270 : it->second.erase(listener);
306 : }
307 2270 : }
308 :
309 0 : void Application::removeAllEventListeners() {
310 0 : _eventListeners.clear();
311 0 : }
312 :
313 2497 : void Application::dispatchEvent(const Event &ev) {
314 2497 : if (_eventListeners.size() > 0) {
315 2467 : auto it = _eventListeners.find(ev.getHeader().getEventID());
316 2467 : if (it != _eventListeners.end() && it->second.size() != 0) {
317 30 : Vector<const EventHandlerNode *> listenersToExecute;
318 30 : auto &listeners = it->second;
319 120 : for (auto l : listeners) {
320 90 : if (l->shouldRecieveEventWithObject(ev.getEventID(), ev.getObject())) {
321 90 : listenersToExecute.push_back(l);
322 : }
323 : }
324 :
325 120 : for (auto l : listenersToExecute) {
326 90 : l->onEventRecieved(ev);
327 : }
328 30 : }
329 : }
330 2497 : }
331 :
332 13795 : uint64_t Application::getClock() const {
333 13795 : return platform::clock(core::ClockType::Monotonic);
334 : }
335 :
336 44196 : thread::TaskQueue *Application::getQueue() {
337 44196 : return this;
338 : }
339 :
340 2906 : void Application::update(const CallbackInfo &cb, const UpdateTime &t) {
341 2906 : memory::pool::push(_updatePool);
342 2906 : if (cb.updateCallback) {
343 2906 : cb.updateCallback(*this, t);
344 : }
345 :
346 16986 : for (auto &it : _extensions) {
347 14080 : it.second->update(this, t);
348 : }
349 :
350 2906 : memory::pool::pop();
351 2906 : memory::pool::clear(_updatePool);
352 2906 : }
353 :
354 30 : void Application::handleDeviceStarted(const core::Loop &loop, const core::Device &dev) {
355 30 : log::debug("Application", "handleDeviceStarted");
356 :
357 30 : auto emptyObject = dev.getEmptyImageObject();
358 30 : auto solidObject = dev.getSolidImageObject();
359 :
360 30 : performOnMainThread([this, emptyObject = dev.getEmptyImageObject(), solidObject = dev.getSolidImageObject()] () {
361 30 : _resourceCache->addImage(core::EmptyTextureName, emptyObject);
362 30 : _resourceCache->addImage(core::SolidTextureName, solidObject);
363 30 : });
364 :
365 30 : _device = &dev;
366 :
367 : #if MODULE_XENOLITH_SCENE
368 30 : for (auto &it : _tmpViews) {
369 0 : addView(move(it));
370 : }
371 :
372 30 : _tmpViews.clear();
373 : #endif
374 30 : }
375 :
376 30 : void Application::handleDeviceFinalized(const core::Loop &loop, const core::Device &dev) {
377 30 : _device = nullptr;
378 :
379 30 : performOnMainThread([cache = _resourceCache] {
380 30 : cache->invalidate();
381 60 : }, Rc<core::Device>(const_cast<core::Device *>(&dev)));
382 30 : }
383 :
384 0 : void Application::handleMessageToken(String &&str) {
385 0 : _messageToken = move(str);
386 0 : onMessageToken(this, _messageToken);
387 0 : }
388 :
389 0 : void Application::handleRemoteNotification(Value &&val) {
390 0 : onRemoteNotification(this, move(val));
391 0 : }
392 :
393 : #if MODULE_XENOLITH_SCENE
394 15 : bool Application::addView(ViewInfo &&info) {
395 15 : performOnGlThread([this, info = move(info)] () mutable {
396 15 : if (_device) {
397 15 : if (info.onClosed) {
398 15 : auto tmp = move(info.onClosed);
399 30 : info.onClosed = [this, tmp = move(tmp)] (xenolith::View &view) {
400 15 : performOnMainThread([this, view = Rc<xenolith::View>(&view)] {
401 15 : _activeViews.erase(view);
402 15 : }, this);
403 15 : tmp(view);
404 30 : };
405 15 : } else {
406 0 : info.onClosed = [this] (xenolith::View &view) {
407 0 : performOnMainThread([this, view = Rc<xenolith::View>(&view)] {
408 0 : _activeViews.erase(view);
409 0 : }, this);
410 0 : };
411 : }
412 :
413 15 : auto v = _instance->makeView(*this, *_device, move(info));
414 15 : performOnMainThread([this, v] {
415 15 : _activeViews.emplace(v.get());
416 15 : }, this);
417 15 : } else {
418 0 : _tmpViews.emplace_back(move(info));
419 : }
420 15 : }, this);
421 :
422 15 : return true;
423 : }
424 : #endif
425 :
426 : }
|