diff options
Diffstat (limited to 'src/test/Profiling.cpp')
-rw-r--r-- | src/test/Profiling.cpp | 230 |
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); +} |