aboutsummaryrefslogtreecommitdiff
path: root/src/test/Profiling.cpp
blob: d7a4f2eb19d8922802f109788d4d3b04fcc1c3e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#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/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;

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

	// Store individual function timings
	std::map<std::string, long long> timings; 

	// Min and max gameobject that should and can be created
	int min_gameobject_count = 100;
	int max_gameobject_count = 1000;
	
	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>
	long long time_function(const std::string& name, Func&& func) {
		auto start = std::chrono::steady_clock::now();
		func();
		auto end = std::chrono::steady_clock::now();
		auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
		timings[name] = duration; // Store the duration in microseconds
		return duration; // Return the duration in microseconds
	}

	// Run and profile all systems, return the total time in milliseconds
	long long run_all_systems() {
		long long total_microseconds = 0;
		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(long long total_time,int game_object_count) const {
		std::stringstream ss;
		ss <<  std::endl <<"Function timings:\n";  // Starting with a header
		for (const auto& [name, duration] : timings) {
				ss << name << " took " << duration / 1000.0 << " ms (" << duration << " µs). " << std::endl;
		}
		ss << "Total time: " << total_time / 1000.0  << "ms (" << total_time  << " µs)" << std::endl;
		ss << "Amount of gameobjects: " << game_object_count << std::endl;
		// Use GTest INFO macro to print the accumulated log without extra newlines
		GTEST_LOG_(INFO) << ss.str();
	}
};

TEST_F(Profiling, Profiling_example) {
	int game_object_count = 0;
	long long total_time = 0;
	while (total_time < 16000) {
		
		{
			//define gameobject used for testing
			GameObject gameobject = mgr.new_object("gameobject","",{0,0});
		}

		game_object_count++;
		total_time = run_all_systems();
		if(game_object_count >= max_gameobject_count) break;
	}
	log_timings(total_time,game_object_count);
	EXPECT_GE(game_object_count, min_gameobject_count);
}

TEST_F(Profiling, Profiling_small_object_no_collision) {
	int game_object_count = 0;
	long long total_time = 0;
	while (total_time < 16000) {
	
		{
			//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>("/home/jaro/crepe/asset/texture/green_square.png"), color,
			FlipSettings{true, true});
		}
		
		render_sys.update();
		game_object_count++;
		total_time = run_all_systems();
		if(game_object_count >= max_gameobject_count) break;
	}
	log_timings(total_time,game_object_count);
	EXPECT_GE(game_object_count, min_gameobject_count);
}