Extra2D/src/core/scheduler.cpp

241 lines
5.5 KiB
C++
Raw Normal View History

#include <core/scheduler.h>
#include <algorithm>
namespace extra2d {
namespace {
class IntervalTimer : public Timer {
public:
IntervalTimer(Scheduler::Cb cb, float interval, uint32 repeat, float delay)
: cb_(std::move(cb)) {
interval_ = interval;
repeat_ = repeat;
delay_ = delay;
useDelay_ = delay > 0.0f;
runForever_ = repeat == 0;
elapsed_ = useDelay_ ? 0.0f : -interval_;
}
void update(float dt) override {
if (paused_ || done_) return;
elapsed_ += dt;
if (useDelay_) {
if (elapsed_ < delay_) return;
elapsed_ -= delay_;
useDelay_ = false;
}
if (elapsed_ >= interval_) {
trigger();
elapsed_ -= interval_;
if (!runForever_) {
timesExecuted_++;
if (timesExecuted_ >= repeat_) {
done_ = true;
}
}
}
}
void trigger() override {
if (cb_) cb_(elapsed_);
}
private:
Scheduler::Cb cb_;
};
class OnceTimer : public Timer {
public:
OnceTimer(Scheduler::VoidCb cb, float delay)
: cb_(std::move(cb)) {
delay_ = delay;
elapsed_ = 0.0f;
}
void update(float dt) override {
if (paused_ || done_) return;
elapsed_ += dt;
if (elapsed_ >= delay_) {
trigger();
done_ = true;
}
}
void trigger() override {
if (cb_) cb_();
}
private:
Scheduler::VoidCb cb_;
};
}
Scheduler& Scheduler::inst() {
static Scheduler instance;
return instance;
}
TimerHdl Scheduler::scheduleUpdate(TimerTarget* target, int pri) {
if (!target) return INVALID_HDL;
UpdateEntry entry{target, pri, false, false};
updates_.push_back(entry);
updateIndex_.insert({target, updates_.size() - 1});
return genHdl();
}
void Scheduler::unscheduleUpdate(TimerTarget* target) {
if (!target) return;
tbb::concurrent_hash_map<TimerTarget*, size_t>::accessor acc;
if (updateIndex_.find(acc, target)) {
size_t idx = acc->second;
if (idx < updates_.size()) {
updates_[idx].markedForDel = true;
}
updateIndex_.erase(acc);
}
}
TimerHdl Scheduler::schedule(Cb cb, float interval, uint32 repeat, float delay) {
if (!cb) return INVALID_HDL;
auto timer = makePtr<IntervalTimer>(std::move(cb), interval, repeat, delay);
TimerHdl hdl = genHdl();
timer->hdl_ = hdl;
timers_.insert({hdl, timer});
return hdl;
}
TimerHdl Scheduler::scheduleOnce(VoidCb cb, float delay) {
if (!cb) return INVALID_HDL;
auto timer = makePtr<OnceTimer>(std::move(cb), delay);
TimerHdl hdl = genHdl();
timer->hdl_ = hdl;
timers_.insert({hdl, timer});
return hdl;
}
TimerHdl Scheduler::scheduleForever(Cb cb, float interval) {
return schedule(std::move(cb), interval, 0, 0.0f);
}
void Scheduler::unschedule(TimerHdl hdl) {
timers_.erase(hdl);
}
void Scheduler::unscheduleAll() {
timers_.clear();
updates_.clear();
updateIndex_.clear();
}
void Scheduler::pause(TimerHdl hdl) {
tbb::concurrent_hash_map<TimerHdl, Ptr<Timer>>::accessor acc;
if (timers_.find(acc, hdl)) {
acc->second->pause();
}
}
void Scheduler::resume(TimerHdl hdl) {
tbb::concurrent_hash_map<TimerHdl, Ptr<Timer>>::accessor acc;
if (timers_.find(acc, hdl)) {
acc->second->resume();
}
}
void Scheduler::update(float dt) {
float scaledDt = dt * timeScale_.load();
locked_ = true;
for (auto& entry : updates_) {
if (!entry.markedForDel && !entry.paused && entry.target) {
entry.target->update(scaledDt);
}
}
updates_.erase(
std::remove_if(updates_.begin(), updates_.end(),
[](const UpdateEntry& e) { return e.markedForDel; }),
updates_.end()
);
std::vector<TimerHdl> toRemove;
for (auto it = timers_.begin(); it != timers_.end(); ++it) {
auto& timer = it->second;
timer->update(scaledDt);
if (timer->isDone()) {
toRemove.push_back(it->first);
}
}
for (auto hdl : toRemove) {
timers_.erase(hdl);
}
locked_ = false;
}
void Scheduler::updateParallel(float dt) {
float scaledDt = dt * timeScale_.load();
locked_ = true;
tbb::parallel_for(tbb::blocked_range<size_t>(0, updates_.size()),
[this, scaledDt](const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
auto& entry = updates_[i];
if (!entry.markedForDel && !entry.paused && entry.target) {
entry.target->update(scaledDt);
}
}
});
updates_.erase(
std::remove_if(updates_.begin(), updates_.end(),
[](const UpdateEntry& e) { return e.markedForDel; }),
updates_.end()
);
std::vector<TimerHdl> toRemove;
for (auto it = timers_.begin(); it != timers_.end(); ++it) {
auto& timer = it->second;
timer->update(scaledDt);
if (timer->isDone()) {
toRemove.push_back(it->first);
}
}
for (auto hdl : toRemove) {
timers_.erase(hdl);
}
locked_ = false;
}
bool Scheduler::isScheduled(TimerHdl hdl) const {
tbb::concurrent_hash_map<TimerHdl, Ptr<Timer>>::const_accessor acc;
return timers_.find(acc, hdl);
}
size_t Scheduler::count() const {
return timers_.size();
}
TimerHdl Scheduler::genHdl() {
return nextHdl_.fetch_add(1, std::memory_order_relaxed);
}
} // namespace extra2d