Introduce system_scheduler

This commit is contained in:
NukeBird 2025-02-16 20:39:41 +03:00
parent c082cc622f
commit 6efb8c070b
2 changed files with 233 additions and 0 deletions

View file

@ -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);
}

View file

@ -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;
}
}
}
};