LCOV - code coverage report
Current view: top level - xenolith/application - XLApplication.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 199 231 86.1 %
Date: 2024-05-12 00:16:13 Functions: 31 38 81.6 %

          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             : }

Generated by: LCOV version 1.14