LCOV - code coverage report
Current view: top level - xenolith/backend/vk - XLVkLoop.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 330 416 79.3 %
Date: 2024-05-12 00:16:13 Functions: 62 72 86.1 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             :  Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       6             :  of this software and associated documentation files (the "Software"), to deal
       7             :  in the Software without restriction, including without limitation the rights
       8             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             :  copies of the Software, and to permit persons to whom the Software is
      10             :  furnished to do so, subject to the following conditions:
      11             : 
      12             :  The above copyright notice and this permission notice shall be included in
      13             :  all copies or substantial portions of the Software.
      14             : 
      15             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             :  THE SOFTWARE.
      22             :  **/
      23             : 
      24             : #include "XLVkLoop.h"
      25             : #include "XLVkInstance.h"
      26             : #include "XLVkDevice.h"
      27             : #include "XLVkTextureSet.h"
      28             : #include "XLVkConfig.h"
      29             : #include "XLCoreFrameCache.h"
      30             : 
      31             : #include "XLVkTransferQueue.h"
      32             : #include "XLVkRenderQueueCompiler.h"
      33             : #include "XLVkMeshCompiler.h"
      34             : #include "XLVkMaterialCompiler.h"
      35             : 
      36             : #define XL_VK_DEPS_DEBUG 0
      37             : 
      38             : namespace STAPPLER_VERSIONIZED stappler::xenolith::vk {
      39             : 
      40             : struct DependencyRequest : public Ref {
      41             :         Vector<Rc<core::DependencyEvent>> events;
      42             :         Function<void(bool)> callback;
      43             :         uint64_t initial = 0;
      44             :         uint32_t signaled = 0;
      45             :         bool success = true;
      46             : };
      47             : 
      48             : struct PresentationData {
      49          42 :         PresentationData() { }
      50             : 
      51             :         uint64_t getLastUpdateInterval() {
      52             :                 auto tmp = lastUpdate;
      53             :                 lastUpdate = platform::clock(core::ClockType::Monotonic);
      54             :                 return lastUpdate - tmp;
      55             :         }
      56             : 
      57             :         uint64_t now = platform::clock(core::ClockType::Monotonic);
      58             :         uint64_t last = 0;
      59             :         uint64_t updateInterval = config::PresentationSchedulerInterval;
      60             :         uint64_t lastUpdate = 0;
      61             : };
      62             : 
      63             : struct Loop::Internal final : memory::AllocPool {
      64          42 :         Internal(memory::pool_t *pool, Loop *l) : pool(pool), loop(l) {
      65          42 :                 timers = new (pool) memory::vector<Timer>(); timers->reserve(8);
      66          42 :                 reschedule = new (pool) memory::vector<Timer>(); reschedule->reserve(8);
      67          42 :                 autorelease = new (pool) memory::vector<Rc<Ref>>(); autorelease->reserve(8);
      68          42 :         }
      69             : 
      70          42 :         void setDevice(Rc<Device> &&dev) {
      71          42 :                 requiredTasks += 3;
      72             : 
      73          42 :                 device = move(dev);
      74             : 
      75          42 :                 device->begin(*loop, *queue, [this] (bool success) {
      76          42 :                         if (info->onDeviceStarted) {
      77          42 :                                 info->onDeviceStarted(*loop, *device);
      78             :                         }
      79             : 
      80          42 :                         onInitTaskPerformed(success, "DeviceResources");
      81          42 :                 });
      82             : 
      83          42 :                 transferQueue = Rc<TransferQueue>::create();
      84          42 :                 materialQueue = Rc<MaterialCompiler>::create();
      85          42 :                 renderQueueCompiler = Rc<RenderQueueCompiler>::create(*device, transferQueue, materialQueue);
      86             : 
      87          42 :                 compileQueue(transferQueue, [this] (bool success) {
      88          42 :                         onInitTaskPerformed(success, "TransferQueue");
      89          42 :                 });
      90          42 :                 compileQueue(materialQueue, [this] (bool success) {
      91          42 :                         onInitTaskPerformed(success, "MaterialCompiler");
      92          42 :                 });
      93          42 :         }
      94             : 
      95          42 :         void endDevice() {
      96          42 :                 if (!device) {
      97           0 :                         return;
      98             :                 }
      99             : 
     100          42 :                 fences.clear();
     101          42 :                 transferQueue = nullptr;
     102          42 :                 renderQueueCompiler = nullptr;
     103          42 :                 device->end();
     104             : 
     105          42 :                 if (info->onDeviceFinalized) {
     106          42 :                         info->onDeviceFinalized(*loop, *device);
     107             :                 }
     108             : 
     109          42 :                 device = nullptr;
     110             :         }
     111             : 
     112          42 :         void waitIdle() {
     113          42 :                 auto r = running->exchange(false);
     114             : 
     115          42 :                 queue->lock(); // lock to prevent new tasks
     116             : 
     117             :                 // wait for all pending tasks on fences
     118          42 :                 for (auto &it : scheduledFences) {
     119           0 :                         it->check(*loop, false);
     120             :                 }
     121          42 :                 scheduledFences.clear();
     122             : 
     123          42 :                 if (device) {
     124             :                         // wait for device
     125          42 :                         device->waitIdle();
     126             :                 }
     127             : 
     128          42 :                 queue->unlock();
     129             : 
     130             :                 // wait for all CPU tasks
     131          42 :                 queue->waitForAll();
     132             : 
     133             :                 // after this, there should be no CPU or GPU tasks pending or executing
     134             : 
     135          42 :                 if (r) {
     136           0 :                         running->exchange(true);
     137             :                 }
     138          42 :         }
     139             : 
     140          21 :         void compileResource(Rc<TransferResource> &&req) {
     141          21 :                 auto h = loop->makeFrame(transferQueue->makeRequest(move(req)), 0);
     142          21 :                 h->update(true);
     143          21 :         }
     144             : 
     145         189 :         void compileQueue(const Rc<core::Queue> &req, Function<void(bool)> &&cb) {
     146         189 :                 auto input = Rc<RenderQueueInput>::alloc();
     147         189 :                 input->queue = req;
     148             : 
     149         189 :                 auto h = Rc<DeviceFrameHandle>::create(*loop, *device, renderQueueCompiler->makeRequest(move(input)), 0);
     150         189 :                 if (cb) {
     151         189 :                         h->setCompleteCallback([cb = move(cb), req] (FrameHandle &handle) {
     152         189 :                                 cb(handle.isValid());
     153         189 :                         });
     154             :                 }
     155             : 
     156         189 :                 h->update(true);
     157         189 :         }
     158             : 
     159        1193 :         void compileMaterials(Rc<core::MaterialInputData> &&req, Vector<Rc<DependencyEvent>> &&deps) {
     160        1193 :                 if (materialQueue->inProgress(req->attachment)) {
     161           0 :                         materialQueue->appendRequest(req->attachment, move(req), move(deps));
     162             :                 } else {
     163        1193 :                         auto attachment = req->attachment;
     164        1193 :                         materialQueue->setInProgress(attachment);
     165        1193 :                         materialQueue->runMaterialCompilationFrame(*loop, move(req), move(deps));
     166             :                 }
     167        1193 :         }
     168             : 
     169         126 :         void onInitTaskPerformed(bool success, StringView view) {
     170         126 :                 if (success) {
     171         126 :                         -- requiredTasks;
     172         126 :                         if (requiredTasks == 0 && signalInit) {
     173          42 :                                 signalInit();
     174             :                         }
     175             :                 } else {
     176           0 :                         log::error("Loop", "Fail to initalize: ", view);
     177             :                 }
     178         126 :         }
     179             : 
     180        4392 :         void signalDependencies(const Vector<Rc<DependencyEvent>> &events, Queue *queue, bool success) {
     181        8784 :                 for (auto &it : events) {
     182        4392 :                         if (it->signal(queue, success)) {
     183        2302 :                                 auto iit = dependencyRequests.equal_range(it.get());
     184        2302 :                                 auto tmp = iit;
     185        6906 :                                 while (iit.first != iit.second) {
     186        4604 :                                         auto &v = iit.first->second;
     187        4604 :                                         if (!success) {
     188           0 :                                                 v->success = false;
     189             :                                         }
     190        4604 :                                         ++ v->signaled;
     191        4604 :                                         if (v->signaled == v->events.size()) {
     192             : #if XL_VK_DEPS_DEBUG
     193             :                                                 StringStream str; str << "signalDependencies:";
     194             :                                                 for (auto &it : v->events) {
     195             :                                                         str << " " << it->getId();
     196             :                                                 }
     197             :                                                 str << "\n";
     198             :                                                 log::debug("vk::Loop", "Signal: ", str.str());
     199             : #endif
     200        4352 :                                                 v->callback(iit.first->second->success);
     201             :                                         }
     202        4604 :                                         ++ iit.first;
     203             :                                 }
     204             : 
     205        2302 :                                 dependencyRequests.erase(tmp.first, tmp.second);
     206             :                         }
     207             :                 }
     208        4392 :         }
     209             : 
     210        4352 :         void waitForDependencies(Vector<Rc<DependencyEvent>> &&events, Function<void(bool)> &&cb) {
     211             : #if XL_VK_DEPS_DEBUG
     212             :                 StringStream str; str << "waitForDependencies:";
     213             :                 for (auto &it : events) {
     214             :                         str << " " << it->getId();
     215             :                 }
     216             :                 log::debug("vk::Loop", "Wait: ", str.str());
     217             : #endif
     218             : 
     219        4352 :                 auto req = Rc<DependencyRequest>::alloc();
     220        4352 :                 req->events = move(events);
     221        4352 :                 req->callback = move(cb);
     222        4352 :                 req->initial = platform::clock(core::ClockType::Monotonic);
     223             : 
     224        8956 :                 for (auto &it : req->events) {
     225        4604 :                         if (it->isSignaled()) {
     226           0 :                                 if (!it->isSuccessful()) {
     227           0 :                                         req->success = false;
     228             :                                 }
     229           0 :                                 ++ req->signaled;
     230             :                         } else {
     231        4604 :                                 dependencyRequests.emplace(it.get(), req);
     232             :                         }
     233             :                 }
     234             : 
     235        4352 :                 if (req->signaled == req->events.size()) {
     236             : #if XL_VK_DEPS_DEBUG
     237             :                         log::debug("vk::Loop", "Run: ", str.str());
     238             : #endif
     239           0 :                         req->callback(req->success);
     240             :                 }
     241        4352 :         }
     242             : 
     243           0 :         void wakeup() { }
     244             : 
     245             :         memory::pool_t *pool = nullptr;
     246             :         Loop *loop = nullptr;
     247             :         const core::LoopInfo *info = nullptr;
     248             : 
     249             :         memory::vector<Timer> *timers;
     250             :         memory::vector<Timer> *reschedule;
     251             :         memory::vector<Rc<Ref>> *autorelease;
     252             : 
     253             :         std::multimap<DependencyEvent *, Rc<DependencyRequest>, std::less<void>> dependencyRequests;
     254             : 
     255             :         Mutex resourceMutex;
     256             : 
     257             :         Rc<Device> device;
     258             :         Rc<thread::TaskQueue> queue;
     259             :         Vector<Rc<Fence>> fences;
     260             :         Set<Rc<Fence>> scheduledFences;
     261             : 
     262             :         Rc<RenderQueueCompiler> renderQueueCompiler;
     263             :         Rc<TransferQueue> transferQueue;
     264             :         Rc<MaterialCompiler> materialQueue;
     265             :         Rc<MeshCompiler> meshQueue;
     266             :         std::atomic<bool> *running = nullptr;
     267             :         uint32_t requiredTasks = 0;
     268             : 
     269             :         Function<void()> signalInit;
     270             : };
     271             : 
     272             : struct Loop::Timer final {
     273           0 :         Timer(uint64_t interval, Function<bool(Loop &)> &&cb, StringView t)
     274           0 :         : interval(interval), callback(move(cb)), tag(t) { }
     275             : 
     276             :         uint64_t interval;
     277             :         uint64_t value = 0;
     278             :         Function<bool(Loop &)> callback; // return true if timer is complete and should be removed
     279             :         StringView tag;
     280             : };
     281             : 
     282          84 : Loop::~Loop() { }
     283             : 
     284          42 : Loop::Loop() { }
     285             : 
     286          42 : bool Loop::init(Rc<Instance> &&instance, LoopInfo &&info) {
     287          42 :         if (!core::Loop::init(instance, move(info))) {
     288           0 :                 return false;
     289             :         }
     290             : 
     291          42 :         _vkInstance = move(instance);
     292          42 :         _thread = std::thread(Loop::workerThread, this, nullptr);
     293          42 :         return true;
     294             : }
     295             : 
     296          42 : void Loop::waitRinning() {
     297          42 :         std::unique_lock<std::mutex> lock(_mutex);
     298          42 :         if (_running.load()) {
     299           0 :                 lock.unlock();
     300           0 :                 return;
     301             :         }
     302             : 
     303          42 :         _cond.wait(lock);
     304          42 :         lock.unlock();
     305          42 : }
     306             : 
     307          42 : void Loop::threadInit() {
     308          42 :         thread::ThreadInfo::setThreadInfo("Gl::Loop");
     309          42 :         _threadId = std::this_thread::get_id();
     310          42 :         _shouldExit.test_and_set();
     311             : 
     312          42 :         memory::pool::initialize();
     313          42 :         auto pool = memory::pool::createTagged("Gl::Loop", mempool::custom::PoolFlags::ThreadSafeAllocator);
     314             : 
     315          42 :         memory::pool::push(pool);
     316             : 
     317          42 :         _internal = new (pool) vk::Loop::Internal(pool, this);
     318          42 :         _internal->pool = pool;
     319          42 :         _internal->running = &_running;
     320          42 :         _internal->info = &_info;
     321             : 
     322         210 :         _internal->signalInit = [this] {
     323             :                 // signal to main thread
     324          42 :                 _mutex.lock();
     325          42 :                 _running.store(true);
     326          42 :                 _cond.notify_all();
     327          42 :                 _mutex.unlock();
     328          42 :         };
     329             : 
     330          42 :         _internal->queue = Rc<thread::TaskQueue>::alloc("Vk::Loop::Queue");
     331          84 :         _internal->queue->spawnWorkers(thread::TaskQueue::Flags::Cancelable | thread::TaskQueue::Flags::Waitable, LoopThreadId,
     332          42 :                         _internal->info->threadsCount);
     333             : 
     334          42 :         if (auto dev = _vkInstance->makeDevice(_info)) {
     335          42 :                 _internal->setDevice(move(dev));
     336          42 :                 _frameCache = Rc<FrameCache>::create(*this, *_internal->device);
     337           0 :         } else if (_info.deviceIdx != core::Instance::DefaultDevice) {
     338           0 :                 log::warn("vk::Loop", "Unable to create device with index: ", _info.deviceIdx, ", fallback to default");
     339           0 :                 _info.deviceIdx = core::Instance::DefaultDevice;
     340           0 :                 if (auto dev = _vkInstance->makeDevice(_info)) {
     341           0 :                         _internal->setDevice(move(dev));
     342           0 :                         _frameCache = Rc<FrameCache>::create(*this, *_internal->device);
     343             :                 } else {
     344           0 :                         log::error("vk::Loop", "Unable to create device");
     345           0 :                 }
     346             :         } else {
     347           0 :                 log::error("vk::Loop", "Unable to create device");
     348          42 :         }
     349             : 
     350          42 :         memory::pool::pop();
     351          42 : }
     352             : 
     353          42 : void Loop::threadDispose() {
     354          42 :         auto pool = _internal->pool;
     355             : 
     356          42 :         memory::pool::push(pool);
     357             : 
     358          42 :         if (_frameCache) {
     359          42 :                 _frameCache->invalidate();
     360             :         }
     361             : 
     362          42 :         _internal->waitIdle();
     363             : 
     364          42 :         _internal->queue->waitForAll();
     365             : 
     366          42 :         _internal->queue->lock();
     367          42 :         _internal->timers->clear();
     368          42 :         _internal->reschedule->clear();
     369          42 :         _internal->autorelease->clear();
     370          42 :         _internal->queue->unlock();
     371             : 
     372          42 :         _internal->queue->cancelWorkers();
     373          42 :         _internal->queue = nullptr;
     374             : 
     375          42 :         _internal->endDevice();
     376             : 
     377          42 :         delete _internal;
     378          42 :         _internal = nullptr;
     379             : 
     380          42 :         memory::pool::pop();
     381          42 :         memory::pool::destroy(pool);
     382          42 : }
     383             : 
     384      796765 : static bool Loop_pollEvents(Loop::Internal *internal, PresentationData &data) {
     385      796765 :         bool timeoutPassed = false;
     386      796765 :         auto counter = internal->queue->getOutputCounter();
     387      796765 :         if (counter > 0) {
     388             :                 XL_PROFILE_BEGIN(queue, "gl::Loop::Queue", "queue", 500);
     389      400471 :                 internal->queue->update();
     390             :                 XL_PROFILE_END(queue)
     391             : 
     392      400471 :                 data.now = platform::clock(core::ClockType::Monotonic);
     393      400471 :                 if (data.now - data.last > data.updateInterval) {
     394       79270 :                         timeoutPassed = true;
     395             :                 }
     396             :         } else {
     397      396294 :                 data.now = platform::clock(core::ClockType::Monotonic);
     398      396294 :                 if (data.now - data.last > data.updateInterval) {
     399       34300 :                         timeoutPassed = true;
     400             :                 } else {
     401      361994 :                         if (internal->timers->empty() && internal->scheduledFences.empty()) {
     402             :                                 // no timers - just wait for events with 60FPS wakeups
     403      361994 :                                 auto t = std::max(data.updateInterval, uint64_t(1'000'000 / 60));
     404      361994 :                                 internal->queue->wait(TimeInterval::microseconds(t));
     405             :                         } else {
     406           0 :                                 if (!internal->queue->wait(TimeInterval::microseconds(data.updateInterval - (data.now - data.last)))) {
     407           0 :                                         data.now = platform::clock(core::ClockType::Monotonic);
     408           0 :                                         timeoutPassed = true;
     409             :                                 }
     410             :                         }
     411             :                 }
     412             :         }
     413      796765 :         return timeoutPassed;
     414             : }
     415             : 
     416      796765 : static void Loop_runFences(Loop::Internal *internal) {
     417      796765 :         auto it = internal->scheduledFences.begin();
     418   107860748 :         while (it != internal->scheduledFences.end()) {
     419   107063983 :                 if ((*it)->check(*internal->loop, true)) {
     420      101210 :                         it = internal->scheduledFences.erase(it);
     421             :                 }
     422             :         }
     423      796765 : }
     424             : 
     425      113570 : static void Loop_runTimers(Loop::Internal *internal, uint64_t dt) {
     426      113570 :         auto timers = internal->timers;
     427      113570 :         internal->timers = internal->reschedule;
     428             : 
     429      113570 :         auto it = timers->begin();
     430      113570 :         while (it != timers->end()) {
     431           0 :                 if (it->interval) {
     432           0 :                         it->value += dt;
     433           0 :                         if (it->value > it->interval) {
     434             : 
     435             :                                 XL_PROFILE_BEGIN(timers, "gl::Loop::Timers", it->tag, 1000);
     436             : 
     437           0 :                                 auto ret = it->callback(*internal->loop);
     438             : 
     439             :                                 XL_PROFILE_END(timers);
     440             : 
     441           0 :                                 if (!ret) {
     442           0 :                                         it->value -= it->interval;
     443             :                                 } else {
     444           0 :                                         it = timers->erase(it);
     445           0 :                                         continue;
     446             :                                 }
     447             :                         }
     448           0 :                         ++ it;
     449             :                 } else {
     450             :                         XL_PROFILE_BEGIN(timers, "gl::Loop::Timers", it->tag, 1000);
     451             : 
     452           0 :                         auto ret = it->callback(*internal->loop);
     453             : 
     454             :                         XL_PROFILE_END(timers);
     455             : 
     456           0 :                         if (ret) {
     457           0 :                                 it = timers->erase(it);
     458             :                         } else {
     459           0 :                                 ++ it;
     460             :                         }
     461             :                 }
     462             :         }
     463             : 
     464      113570 :         if (!internal->timers->empty()) {
     465           0 :                 for (auto &it : *internal->timers) {
     466           0 :                         timers->emplace_back(std::move(it));
     467             :                 }
     468           0 :                 internal->timers->clear();
     469             :         }
     470      113570 :         internal->timers = timers;
     471      113570 : }
     472             : 
     473          42 : bool Loop::worker() {
     474          42 :         if (!_internal->device) {
     475           0 :                 _running.store(false);
     476           0 :                 return false;
     477             :         }
     478             : 
     479          42 :         PresentationData data;
     480             : 
     481          42 :         auto pool = memory::pool::create(_internal->pool);
     482             : 
     483     1593614 :         while (_shouldExit.test_and_set()) {
     484      796765 :                 bool timeoutPassed = false;
     485             : 
     486      796765 :                 ++ _clock;
     487             : 
     488             :                 XL_PROFILE_BEGIN(loop, "vk::Loop", "loop", 1000);
     489             : 
     490             :                 XL_PROFILE_BEGIN(poll, "vk::Loop::Poll", "poll", 500);
     491      796765 :                 timeoutPassed = Loop_pollEvents(_internal, data);
     492             :                 XL_PROFILE_END(poll)
     493             : 
     494      796765 :                 Loop_runFences(_internal);
     495             : 
     496      796765 :                 if (timeoutPassed) {
     497      113570 :                         auto dt = data.now - data.last;
     498             :                         XL_PROFILE_BEGIN(timers, "vk::Loop::Timers", "timers", 500);
     499      113570 :                         Loop_runTimers(_internal, dt);
     500             :                         XL_PROFILE_END(timers)
     501      113570 :                         data.last = data.now;
     502             :                 }
     503             : 
     504             :                 XL_PROFILE_BEGIN(autorelease, "vk::Loop::Autorelease", "autorelease", 500);
     505      796765 :                 _internal->autorelease->clear();
     506             :                 XL_PROFILE_END(autorelease)
     507             : 
     508      796765 :                 _frameCache->clear();
     509             : 
     510             :                 XL_PROFILE_END(loop)
     511      796765 :                 memory::pool::clear(pool);
     512             :         }
     513             : 
     514          42 :         memory::pool::destroy(pool);
     515             : 
     516          42 :         _running.store(false);
     517          42 :         return false;
     518             : }
     519             : 
     520          42 : void Loop::cancel() {
     521          42 :         _shouldExit.clear();
     522          42 :         _thread.join();
     523          42 : }
     524             : 
     525          21 : void Loop::compileResource(Rc<core::Resource> &&req, Function<void(bool)> &&cb, bool preload) const {
     526          21 :         auto res = Rc<TransferResource>::create(_internal->device->getAllocator(), move(req), move(cb));
     527          21 :         if (preload) {
     528           0 :                 res->initialize();
     529             :         }
     530          21 :         performOnGlThread([this, res = move(res)] () mutable {
     531          21 :                 _internal->compileResource(move(res));
     532          21 :         }, const_cast<Loop *>(this), true);
     533          21 : }
     534             : 
     535         105 : void Loop::compileQueue(const Rc<Queue> &req, Function<void(bool)> &&callback) const {
     536         105 :         performOnGlThread([this, req, callback = move(callback)]() mutable {
     537         105 :                 _internal->compileQueue(req, move(callback));
     538         105 :         }, const_cast<Loop *>(this), true);
     539         105 : }
     540             : 
     541        1193 : void Loop::compileMaterials(Rc<core::MaterialInputData> &&req, const Vector<Rc<DependencyEvent>> &deps) const {
     542        1193 :         performOnGlThread([this, req = move(req), deps = deps] () mutable {
     543        1193 :                 _internal->compileMaterials(move(req), move(deps));
     544        1193 :         }, const_cast<Loop *>(this), true);
     545        1193 : }
     546             : 
     547          42 : void Loop::compileImage(const Rc<core::DynamicImage> &img, Function<void(bool)> &&callback) const {
     548          42 :         performOnGlThread([this, img, callback = move(callback)]() mutable {
     549          42 :                 _internal->device->compileImage(*this, img, move(callback));
     550          42 :         }, const_cast<Loop *>(this), true);
     551          42 : }
     552             : 
     553       26287 : void Loop::runRenderQueue(Rc<FrameRequest> &&req, uint64_t gen, Function<void(bool)> &&callback) {
     554       26287 :         performOnGlThread([this, req = move(req), gen, callback = move(callback)]() mutable {
     555       26287 :                 auto frame = makeFrame(move(req), gen);
     556       26287 :                 if (frame && callback) {
     557        1066 :                         frame->setCompleteCallback([callback = move(callback)] (FrameHandle &handle) {
     558        1066 :                                 callback(handle.isValid());
     559        1066 :                         });
     560             :                 }
     561       26287 :                 frame->update(true);
     562       26287 :         }, this, true);
     563       26287 : }
     564             : 
     565           0 : void Loop::schedule(Function<bool(core::Loop &)> &&cb, StringView tag) {
     566           0 :         if (isOnGlThread()) {
     567           0 :                 _internal->timers->emplace_back(0, move(cb), tag);
     568             :         } else {
     569           0 :                 performOnGlThread([this, cb = move(cb), tag] () mutable {
     570           0 :                         _internal->timers->emplace_back(0, move(cb), tag);
     571           0 :                 });
     572             :         }
     573           0 : }
     574             : 
     575           0 : void Loop::schedule(Function<bool(core::Loop &)> &&cb, uint64_t delay, StringView tag) {
     576           0 :         if (isOnGlThread()) {
     577           0 :                 _internal->timers->emplace_back(delay, move(cb), tag);
     578             :         } else {
     579           0 :                 performOnGlThread([this, cb = move(cb), delay, tag] () mutable {
     580           0 :                         _internal->timers->emplace_back(delay, move(cb), tag);
     581           0 :                 });
     582             :         }
     583           0 : }
     584             : 
     585      970873 : void Loop::performInQueue(Rc<thread::Task> &&task) const {
     586      970873 :         if (!_internal || !_internal->queue) {
     587           0 :                 auto &tasks = task->getCompleteTasks();
     588           0 :                 for (auto &it : tasks) {
     589           0 :                         it(*task, false);
     590             :                 }
     591           0 :                 return;
     592             :         }
     593             : 
     594      970873 :         _internal->queue->perform(move(task));
     595             : }
     596             : 
     597          42 : void Loop::performInQueue(Function<void()> &&func, Ref *target) const {
     598          42 :         if (!_internal || !_internal->queue) {
     599           0 :                 return;
     600             :         }
     601             : 
     602          42 :         _internal->queue->perform(move(func), target);
     603             : }
     604             : 
     605     1032689 : void Loop::performOnGlThread(Function<void()> &&func, Ref *target, bool immediate) const {
     606     1032689 :         if (!_internal || !_internal->queue) {
     607          84 :                 return;
     608             :         }
     609             : 
     610     1032618 :         if (immediate) {
     611      729665 :                 if (isOnGlThread()) {
     612      693545 :                         func();
     613      693545 :                         return;
     614             :                 }
     615             :         }
     616      339073 :         _internal->queue->onMainThread(move(func), target);
     617             : }
     618             : 
     619     1064377 : bool Loop::isOnGlThread() const {
     620     1064377 :         return _threadId == std::this_thread::get_id();
     621             : }
     622             : 
     623       37039 : auto Loop::makeFrame(Rc<FrameRequest> &&req, uint64_t gen) -> Rc<FrameHandle> {
     624       37039 :         if (_running.load()) {
     625       74078 :                 return Rc<DeviceFrameHandle>::create(*this, *_internal->device, move(req), gen);
     626             :         }
     627           0 :         return nullptr;
     628             : }
     629             : 
     630        9517 : Rc<core::Framebuffer> Loop::acquireFramebuffer(const PassData *data, SpanView<Rc<core::ImageView>> views) {
     631        9517 :         if (!_running.load()) {
     632           0 :                 return nullptr;
     633             :         }
     634             : 
     635        9517 :         return _frameCache->acquireFramebuffer(data, views);
     636             : }
     637             : 
     638        9517 : void Loop::releaseFramebuffer(Rc<core::Framebuffer> &&fb) {
     639        9517 :         _frameCache->releaseFramebuffer(move(fb));
     640        9517 : }
     641             : 
     642       28593 : auto Loop::acquireImage(const ImageAttachment *a, const AttachmentHandle *h, const core::ImageInfoData &i) -> Rc<ImageStorage> {
     643       28593 :         if (!_running.load()) {
     644           0 :                 return nullptr;
     645             :         }
     646             : 
     647       28593 :         core::ImageInfoData info(i);
     648       28593 :         if (a->isTransient()) {
     649       19034 :                 if ((info.usage & (core::ImageUsage::ColorAttachment | core::ImageUsage::DepthStencilAttachment | core::ImageUsage::InputAttachment))
     650       19034 :                                 != core::ImageUsage::None) {
     651       19034 :                         info.usage |= core::ImageUsage::TransientAttachment;
     652             :                 }
     653             :         }
     654             : 
     655       28593 :         auto views = a->getImageViews(info);
     656       28593 :         return _frameCache->acquireImage(a->getId(), info, views);
     657       28593 : }
     658             : 
     659       28572 : void Loop::releaseImage(Rc<ImageStorage> &&image) {
     660       28572 :         performOnGlThread([this, image = move(image)] () mutable {
     661       28572 :                 _frameCache->releaseImage(move(image));
     662       28572 :         }, this, true);
     663       28572 : }
     664             : 
     665      126189 : Rc<core::Semaphore> Loop::makeSemaphore() {
     666      126189 :         return _internal->device->makeSemaphore();
     667             : }
     668             : 
     669          21 : const Vector<core::ImageFormat> &Loop::getSupportedDepthStencilFormat() const {
     670          21 :         return _internal->device->getSupportedDepthStencilFormat();
     671             : }
     672             : 
     673      207420 : Rc<Fence> Loop::acquireFence(uint32_t v, bool init) {
     674      207420 :         auto initFence = [&] (const Rc<Fence> &fence) {
     675      207420 :                 if (!init) {
     676          21 :                         return;
     677             :                 }
     678             : 
     679      414798 :                 fence->setFrame([this, fence] () mutable {
     680      101210 :                         if (isOnGlThread()) {
     681             :                                 // schedule fence
     682      101210 :                                 _internal->scheduledFences.emplace(move(fence));
     683      101210 :                                 return true;
     684             :                         } else {
     685           0 :                                 performOnGlThread([this, fence = move(fence)] () mutable {
     686           0 :                                         if (!fence->check(*this, true)) {
     687           0 :                                                 return;
     688             :                                         }
     689             : 
     690           0 :                                         _internal->scheduledFences.emplace(move(fence));
     691             :                                 }, this, true);
     692           0 :                                 return true;
     693             :                         }
     694      622197 :                 }, [this, fence] () mutable {
     695      207399 :                         fence->clear();
     696      207399 :                         std::unique_lock<Mutex> lock(_internal->resourceMutex);
     697      207399 :                         _internal->fences.emplace_back(move(fence));
     698      414798 :                 }, v);
     699      207420 :         };
     700             : 
     701      207420 :         std::unique_lock<Mutex> lock(_internal->resourceMutex);
     702      207420 :         if (!_internal->fences.empty()) {
     703      207274 :                 auto ref = move(_internal->fences.back());
     704      207274 :                 _internal->fences.pop_back();
     705      207274 :                 initFence(ref);
     706      207274 :                 return ref;
     707      207274 :         }
     708         146 :         lock.unlock();
     709         146 :         auto ref = Rc<Fence>::create(*_internal->device);
     710         146 :         initFence(ref);
     711         146 :         return ref;
     712      207420 : }
     713             : 
     714        4392 : void Loop::signalDependencies(const Vector<Rc<DependencyEvent>> &events, Queue *q, bool success) {
     715        4392 :         if (!events.empty()) {
     716        4392 :                 if (isOnGlThread() && _internal) {
     717        4392 :                         _internal->signalDependencies(events, q, success);
     718        4392 :                         return;
     719             :                 }
     720             : 
     721           0 :                 performOnGlThread([this, events, success, q = Rc<Queue>(q)] () {
     722           0 :                         _internal->signalDependencies(events, q, success);
     723           0 :                 }, this, false);
     724             :         }
     725             : }
     726             : 
     727       40541 : void Loop::waitForDependencies(const Vector<Rc<DependencyEvent>> &events, Function<void(bool)> &&cb) {
     728       40541 :         if (events.empty()) {
     729       36189 :                 cb(true);
     730             :         } else {
     731        4352 :                 performOnGlThread([this, events = events, cb = move(cb)] () mutable {
     732        4352 :                         _internal->waitForDependencies(move(events), move(cb));
     733        4352 :                 }, this, true);
     734             :         }
     735       40541 : }
     736             : 
     737           0 : void Loop::wakeup() {
     738           0 :         if (_internal) {
     739           0 :                 _internal->wakeup();
     740             :         }
     741           0 : }
     742             : 
     743           0 : void Loop::waitIdle() {
     744           0 :         if (_internal) {
     745           0 :                 _internal->waitIdle();
     746             :         }
     747           0 : }
     748             : 
     749          21 : void Loop::captureImage(Function<void(const ImageInfoData &info, BytesView view)> &&cb, const Rc<core::ImageObject> &image, core::AttachmentLayout l) {
     750          21 :         performOnGlThread([this, cb = move(cb), image, l] () mutable {
     751          21 :                 _internal->device->getTextureSetLayout()->readImage(*_internal->device, *this, image.cast<Image>(), l, move(cb));
     752          21 :         }, this, true);
     753          21 : }
     754             : 
     755       25200 : void Loop::captureBuffer(Function<void(const BufferInfo &info, BytesView view)> &&cb, const Rc<core::BufferObject> &buf) {
     756       25200 :         if ((buf->getInfo().usage & core::BufferUsage::TransferSrc) != core::BufferUsage::TransferSrc) {
     757           0 :                 log::error("vk::Loop::captureBuffer", "Buffer '", buf->getName(), "' has no BufferUsage::TransferSrc flag to being captured");
     758             :         }
     759             : 
     760       25200 :         performOnGlThread([this, cb = move(cb), buf] () mutable {
     761       25200 :                 _internal->device->getTextureSetLayout()->readBuffer(*_internal->device, *this, buf.cast<Buffer>(), move(cb));
     762       25200 :         }, this, true);
     763       25200 : }
     764             : 
     765             : }

Generated by: LCOV version 1.14