aboutsummaryrefslogtreecommitdiff
path: root/src/test/Profiling.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/Profiling.cpp')
-rw-r--r--src/test/Profiling.cpp230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/test/Profiling.cpp b/src/test/Profiling.cpp
new file mode 100644
index 0000000..2549c57
--- /dev/null
+++ b/src/test/Profiling.cpp
@@ -0,0 +1,230 @@
+#include "system/ParticleSystem.h"
+#include "system/PhysicsSystem.h"
+#include "system/RenderSystem.h"
+#include <cmath>
+#include <chrono>
+#include <gtest/gtest.h>
+
+#define private public
+#define protected public
+
+#include <crepe/ComponentManager.h>
+#include <crepe/api/Event.h>
+#include <crepe/api/EventManager.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Script.h>
+#include <crepe/api/Transform.h>
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/system/CollisionSystem.h>
+#include <crepe/system/ScriptSystem.h>
+#include <crepe/types.h>
+#include <crepe/util/Log.h>
+
+using namespace std;
+using namespace std::chrono_literals;
+using namespace crepe;
+using namespace testing;
+
+
+/*
+List of test cases with component settings/details
+1. Minimal test creates gameobject without additonal components
+2. Minimal 'Complex' gameobject. Has dynamic body without bounce and no collision handler
+3. Minimal 'Complex' gameobject. Same as test 2 but with particle emitter
+*/
+
+class TestScript : public Script {
+ bool oncollision(const CollisionEvent& test) {
+ Log::logf("Box {} script on_collision()", test.info.first_collider.game_object_id);
+ return true;
+ }
+ void init() {
+ subscribe<CollisionEvent>([this](const CollisionEvent& ev) -> bool {
+ return this->oncollision(ev);
+ });
+ }
+ void update() {
+ // Retrieve component from the same GameObject this script is on
+ }
+};
+
+class Profiling : public Test {
+public:
+ // Config for test
+ // Minimum amount to let test pass
+ const int min_gameobject_count = 100;
+ // Maximum amount to stop test
+ const int max_gameobject_count = 200;
+ // Amount of times a test runs to calculate average
+ const int average = 5;
+ // Maximum duration to stop test
+ const std::chrono::microseconds duration = 16000us;
+
+
+ ComponentManager mgr;
+ // Add system used for profling tests
+ CollisionSystem collision_sys{mgr};
+ PhysicsSystem physics_sys{mgr};
+ ParticleSystem particle_sys{mgr};
+ RenderSystem render_sys{mgr};
+ ScriptSystem script_sys{mgr};
+
+ // Test data
+ std::map<std::string, std::chrono::microseconds> timings;
+ int game_object_count = 0;
+ std::chrono::microseconds total_time = 0us;
+
+
+ void SetUp() override {
+
+ GameObject do_not_use = mgr.new_object("DO_NOT_USE","",{0,0});
+ do_not_use.add_component<Camera>(Color::WHITE);
+ // initialize systems here:
+ //calls init
+ script_sys.update();
+ //creates window
+ render_sys.update();
+ }
+
+ // Helper function to time an update call and store its duration
+ template <typename Func>
+ std::chrono::microseconds time_function(const std::string& name, Func&& func) {
+ auto start = std::chrono::steady_clock::now();
+ func();
+ auto end = std::chrono::steady_clock::now();
+ std::chrono::microseconds duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+ timings[name] += duration;
+ return duration;
+ }
+
+ // Run and profile all systems, return the total time in milliseconds
+ std::chrono::microseconds run_all_systems() {
+ std::chrono::microseconds total_microseconds = 0us;
+ total_microseconds += time_function("PhysicsSystem", [&]() { physics_sys.update(); });
+ total_microseconds += time_function("CollisionSystem", [&]() { collision_sys.update(); });
+ total_microseconds += time_function("ParticleSystem", [&]() { particle_sys.update(); });
+ total_microseconds += time_function("RenderSystem", [&]() { render_sys.update(); });
+ return total_microseconds;
+ }
+
+ // Print timings of all functions
+ void log_timings() const {
+ std::stringstream ss;
+ ss << "\nFunction timings:\n";
+ for (const auto& [name, duration] : timings) {
+ ss << name << " took " << duration.count() / 1000.0 / average << " ms (" << duration.count() / average << " µs).\n";
+ }
+ ss << "Total time: " << this->total_time.count() / 1000.0 / average << " ms (" << this->total_time.count() / average << " µs)\n";
+ ss << "Amount of gameobjects: " << game_object_count << "\n";
+ GTEST_LOG_(INFO) << ss.str();
+ }
+
+ void clear_timings() {
+ for (auto& [key, value] : timings) {
+ value = std::chrono::microseconds(0);
+ }
+ }
+};
+
+TEST_F(Profiling, Profiling_1) {
+ while (this->total_time/this->average < this->duration) {
+
+
+ {
+ //define gameobject used for testing
+ GameObject gameobject = mgr.new_object("gameobject","",{0,0});
+ }
+
+ this->game_object_count++;
+
+ this->total_time = 0us;
+ clear_timings();
+
+ for (int amount = 0; amount < this->average; amount++) {
+ this->total_time += run_all_systems();
+ }
+
+ if(this->game_object_count >= this->max_gameobject_count) break;
+ }
+ log_timings();
+ EXPECT_GE(this->game_object_count, this->min_gameobject_count);
+}
+
+TEST_F(Profiling, Profiling_2) {
+ while (this->total_time/this->average < this->duration) {
+
+ {
+ //define gameobject used for testing
+ GameObject gameobject = mgr.new_object("gameobject","",{static_cast<float>(game_object_count*2),0});
+ gameobject.add_component<Rigidbody>(Rigidbody::Data{
+ .body_type = Rigidbody::BodyType::STATIC,
+ .use_gravity = false,
+ });
+ gameobject.add_component<BoxCollider>(vec2{0, 0}, 1, 1);
+ gameobject.add_component<BehaviorScript>().set_script<TestScript>();
+ Color color(0, 0, 0, 0);
+ gameobject.add_component<Sprite>(
+ make_shared<Texture>("asset/texture/green_square.png"), color,
+ FlipSettings{true, true});
+ }
+
+ this->game_object_count++;
+
+ this->total_time = 0us;
+ clear_timings();
+ for (int amount = 0; amount < this->average; amount++) {
+ this->total_time += run_all_systems();
+ }
+
+ if(this->game_object_count >= this->max_gameobject_count) break;
+ }
+ log_timings();
+ EXPECT_GE(this->game_object_count, this->min_gameobject_count);
+}
+
+TEST_F(Profiling, Profiling_3) {
+ while (this->total_time/this->average < this->duration) {
+
+ {
+ //define gameobject used for testing
+ GameObject gameobject = mgr.new_object("gameobject","",{static_cast<float>(game_object_count*2),0});
+ gameobject.add_component<Rigidbody>(Rigidbody::Data{
+ .body_type = Rigidbody::BodyType::STATIC,
+ .use_gravity = false,
+ });
+ gameobject.add_component<BoxCollider>(vec2{0, 0}, 1, 1);
+ gameobject.add_component<BehaviorScript>().set_script<TestScript>();
+ Color color(0, 0, 0, 0);
+ gameobject.add_component<Sprite>(
+ make_shared<Texture>("asset/texture/green_square.png"), color,
+ FlipSettings{true, true});
+ Sprite & test_sprite = gameobject.add_component<Sprite>(
+ make_shared<Texture>("asset/texture/img.png"), color, FlipSettings{false, false});
+ auto & test = gameobject.add_component<ParticleEmitter>(ParticleEmitter::Data{
+ .max_particles = 10,
+ .emission_rate = 100,
+ .end_lifespan = 100000,
+ .boundary{
+ .width = 1000,
+ .height = 1000,
+ .offset = vec2{0, 0},
+ .reset_on_exit = false,
+ },
+ .sprite = test_sprite,
+ });
+ }
+ render_sys.update();
+ this->game_object_count++;
+
+ this->total_time = 0us;
+ clear_timings();
+ for (int amount = 0; amount < this->average; amount++) {
+ this->total_time += run_all_systems();
+ }
+
+ if(this->game_object_count >= this->max_gameobject_count) break;
+ }
+ log_timings();
+ EXPECT_GE(this->game_object_count, this->min_gameobject_count);
+}