Introduce system_scheduler
This commit is contained in:
parent
c082cc622f
commit
6efb8c070b
2 changed files with 233 additions and 0 deletions
182
tests/zecsy.cpp
182
tests/zecsy.cpp
|
@ -240,3 +240,185 @@ TEST_CASE("Test a systems ability to query and process only entities with a spec
|
|||
REQUIRE_FALSE(w.has<C1>(e1));
|
||||
REQUIRE_FALSE(w.has<C0>(e2));
|
||||
}
|
||||
|
||||
TEST_CASE("Systems execute at correct frequencies")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
||||
int fast_count = 0;
|
||||
int slow_count = 0;
|
||||
|
||||
// Add a fast system (60 Hz)
|
||||
scheduler.add_system(60, [&](float dt) {
|
||||
fast_count++;
|
||||
});
|
||||
|
||||
// Add a slow system (1 Hz)
|
||||
scheduler.add_system(1, [&](float dt) {
|
||||
slow_count++;
|
||||
});
|
||||
|
||||
// Simulate 2 seconds of updates at 120 FPS
|
||||
const float dt = 1.0f / 120.0f;
|
||||
for (int i = 0; i < 240; ++i) {
|
||||
scheduler.update(dt);
|
||||
}
|
||||
|
||||
// Verify counts
|
||||
REQUIRE(fast_count == 120); // 60 Hz system should execute 60 times
|
||||
REQUIRE(slow_count == 2); // 1 Hz system should execute 1 time
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle zero-frequency gracefully")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
||||
int zero_count = 0;
|
||||
|
||||
// Add a zero-frequency system (should never execute)
|
||||
scheduler.add_system(0, [&](float dt) {
|
||||
zero_count++;
|
||||
});
|
||||
|
||||
// Simulate 1 second of updates at 60 FPS
|
||||
const float dt = 1.0f / 60.0f;
|
||||
for (int i = 0; i < 60; ++i) {
|
||||
scheduler.update(dt);
|
||||
}
|
||||
|
||||
// Verify zero-frequency system never executes
|
||||
REQUIRE(zero_count == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle varying update rates")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
||||
int varying_count = 0;
|
||||
|
||||
// Add a system with varying frequency (10 Hz)
|
||||
scheduler.add_system(10, [&](float dt) {
|
||||
varying_count++;
|
||||
});
|
||||
|
||||
// Simulate 1 second of updates at 30 FPS
|
||||
const float dt = 1.0f / 30.0f;
|
||||
for (int i = 0; i < 30; ++i) {
|
||||
scheduler.update(dt);
|
||||
}
|
||||
|
||||
// Verify varying-frequency system executes 10 times
|
||||
REQUIRE(varying_count == 10);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle large time steps")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
||||
int large_step_count = 0;
|
||||
|
||||
// Add a system (1 Hz)
|
||||
scheduler.add_system(1, [&](float dt) {
|
||||
large_step_count++;
|
||||
});
|
||||
|
||||
// Simulate a large time step (2 seconds)
|
||||
scheduler.update(2.0f);
|
||||
|
||||
// Verify system executes twice (accumulator handles large steps)
|
||||
REQUIRE(large_step_count == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle multiple frequencies")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
||||
int fast_count = 0;
|
||||
int medium_count = 0;
|
||||
int slow_count = 0;
|
||||
|
||||
// Add systems with different frequencies
|
||||
scheduler.add_system(60, [&](float dt) { fast_count++; });
|
||||
scheduler.add_system(30, [&](float dt) { medium_count++; });
|
||||
scheduler.add_system(1, [&](float dt) { slow_count++; });
|
||||
|
||||
// Simulate 1 second of updates at 120 FPS
|
||||
const float dt = 1.0f / 120;
|
||||
for (int i = 0; i < 120; ++i) {
|
||||
scheduler.update(dt);
|
||||
}
|
||||
|
||||
// Verify counts
|
||||
REQUIRE(fast_count == 60); // 60 Hz system
|
||||
REQUIRE(medium_count == 30); // 30 Hz system
|
||||
REQUIRE(slow_count == 1); // 1 Hz system
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle fractional frequencies")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
||||
int fractional_count = 0;
|
||||
|
||||
// Add a system with fractional frequency (0.5 Hz)
|
||||
scheduler.add_system(0.5f, [&](float dt) {
|
||||
fractional_count++;
|
||||
});
|
||||
|
||||
// Simulate 4 seconds of updates at 60 FPS
|
||||
const float dt = 1.0f / 60;
|
||||
for (int i = 0; i < 240; ++i) {
|
||||
scheduler.update(dt);
|
||||
}
|
||||
|
||||
// Verify fractional-frequency system executes twice (0.5 Hz = 2 times in 4 seconds)
|
||||
REQUIRE(fractional_count == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle zero delta time")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
||||
int zero_dt_count = 0;
|
||||
|
||||
// Add a system (1 Hz)
|
||||
scheduler.add_system(1, [&](float dt) {
|
||||
zero_dt_count++;
|
||||
});
|
||||
|
||||
// Simulate zero delta time
|
||||
scheduler.update(0.0f);
|
||||
|
||||
// Verify system does not execute
|
||||
REQUIRE(zero_dt_count == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle negative delta time")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
||||
int count = 0;
|
||||
|
||||
// Add a system (1 Hz)
|
||||
scheduler.add_system(1, [&](float dt) {
|
||||
count++;
|
||||
});
|
||||
|
||||
// Simulate negative delta time
|
||||
scheduler.update(-1.0f);
|
||||
|
||||
// Verify system does not execute
|
||||
REQUIRE(count == 0);
|
||||
|
||||
scheduler.update(2.0f);
|
||||
|
||||
REQUIRE(count == 2);
|
||||
}
|
||||
|
|
51
zecsy.hpp
51
zecsy.hpp
|
@ -1,6 +1,9 @@
|
|||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <stdexcept>
|
||||
#include <typeindex>
|
||||
|
@ -70,6 +73,25 @@ namespace zecsy
|
|||
comp_to_reusable_ids reusable_id_queues;
|
||||
};
|
||||
|
||||
class system_scheduler final
|
||||
{
|
||||
public:
|
||||
void add_system(float freq, auto&& func);
|
||||
|
||||
void add_system(int freq, auto&& func);
|
||||
|
||||
void update(float dt);
|
||||
private:
|
||||
struct system_handler
|
||||
{
|
||||
double interval;
|
||||
double accumulator = 0.0f;
|
||||
std::function<void(float)> callback;
|
||||
};
|
||||
|
||||
std::vector<system_handler> systems;
|
||||
};
|
||||
|
||||
class world final
|
||||
{
|
||||
public:
|
||||
|
@ -267,4 +289,33 @@ namespace zecsy
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void system_scheduler::add_system(float freq, auto&& func)
|
||||
{
|
||||
systems.push_back({
|
||||
1.0f / freq,
|
||||
0.0f,
|
||||
std::forward<decltype(func)>(func)
|
||||
});
|
||||
}
|
||||
|
||||
inline void system_scheduler::add_system(int freq, auto&& func)
|
||||
{
|
||||
add_system(float(freq), func);
|
||||
}
|
||||
|
||||
inline void system_scheduler::update(float dt)
|
||||
{
|
||||
dt = std::max(0.0f, dt);
|
||||
|
||||
for(auto& s: systems)
|
||||
{
|
||||
s.accumulator += dt;
|
||||
while(s.accumulator >= s.interval)
|
||||
{
|
||||
s.callback(dt);
|
||||
s.accumulator -= s.interval;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue