1 //
2 // SuperTuxKart - a fun racing game with go-kart
3 // Copyright (C) 2009-2015 Joerg Henrichs
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 3
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "modes/profile_world.hpp"
20
21 #include "main_loop.hpp"
22 #include "graphics/camera.hpp"
23 #include "graphics/irr_driver.hpp"
24 #include "karts/kart_with_stats.hpp"
25 #include "karts/controller/controller.hpp"
26 #include "tracks/track.hpp"
27
28 #include <ISceneManager.h>
29
30 #include <iomanip>
31 #include <iostream>
32 #include <sstream>
33
34 ProfileWorld::ProfileType ProfileWorld::m_profile_mode=PROFILE_NONE;
35 int ProfileWorld::m_num_laps = 0;
36 float ProfileWorld::m_time = 0.0f;
37
38 //-----------------------------------------------------------------------------
39 /** The constructor sets the number of (local) players to 0, since only AI
40 * karts are used.
41 */
ProfileWorld()42 ProfileWorld::ProfileWorld()
43 {
44 RaceManager::get()->setNumPlayers(0);
45 // Set number of laps so that the end of the race can be detected by
46 // quering the number of finished karts from the race manager (in laps
47 // based profiling) - in case of time based profiling, the number of
48 // laps is set to 99999.
49 RaceManager::get()->setNumLaps(m_num_laps);
50 setPhase(RACE_PHASE);
51 m_frame_count = 0;
52 m_start_time = irr_driver->getRealTime();
53 m_num_triangles = 0;
54 m_num_culls = 0;
55 m_num_solid = 0;
56 m_num_transparent = 0;
57 m_num_trans_effect = 0;
58 m_num_calls = 0;
59 } // ProfileWorld
60
61 //-----------------------------------------------------------------------------
62 /** Sets profile mode off again.
63 * Needed because demo mode's closing allows the player to continue playing
64 * STK. If we didn't set it off, profile mode would stay activated.
65 */
~ProfileWorld()66 ProfileWorld::~ProfileWorld()
67 {
68 m_profile_mode = PROFILE_NONE;
69 }
70
71 //-----------------------------------------------------------------------------
72 /** Enables profiling for a certain amount of time. It also sets the
73 * number of laps to a high number (so that the lap count will not finish
74 * a race before the time is over).
75 * \param time Time to profile a race for.
76 */
setProfileModeTime(float time)77 void ProfileWorld::setProfileModeTime(float time)
78 {
79 m_profile_mode = PROFILE_TIME;
80 m_num_laps = 99999;
81 m_time = time;
82 } // setProfileModeTime
83
84 //-----------------------------------------------------------------------------
85 /** Enables profiling for a certain number of laps. The race will end when all
86 * karts have done (at least) this number of laps.
87 * \param laps The number of laps.
88 */
setProfileModeLaps(int laps)89 void ProfileWorld::setProfileModeLaps(int laps)
90 {
91 m_profile_mode = PROFILE_LAPS;
92 m_num_laps = laps;
93 } // setProfileModeLaps
94
95 //-----------------------------------------------------------------------------
96 /** Creates a kart, having a certain position, starting location, and local
97 * and global player id (if applicable).
98 * \param kart_ident Identifier of the kart to create.
99 * \param index Index of the kart.
100 * \param local_player_id If the kart is a player kart this is the index of
101 * this player on the local machine.
102 * \param global_player_id If the akrt is a player kart this is the index of
103 * this player globally (i.e. including network players).
104 * \param init_pos The start XYZ coordinates.
105 */
createKart(const std::string & kart_ident,int index,int local_player_id,int global_player_id,RaceManager::KartType kart_type,HandicapLevel handicap)106 std::shared_ptr<AbstractKart> ProfileWorld::createKart
107 (const std::string &kart_ident, int index, int local_player_id,
108 int global_player_id, RaceManager::KartType kart_type,
109 HandicapLevel handicap)
110 {
111 btTransform init_pos = getStartTransform(index);
112
113 std::shared_ptr<KartWithStats> new_kart =
114 std::make_shared<KartWithStats>(kart_ident, /*world kart id*/ index,
115 /*position*/ index + 1, init_pos, handicap);
116 new_kart->init(RaceManager::KT_AI);
117 Controller *controller = loadAIController(new_kart.get());
118 new_kart->setController(controller);
119
120 // Create a camera for the last kart (since this way more of the
121 // karts can be seen.
122 if (!GUIEngine::isNoGraphics() &&
123 index == (int)RaceManager::get()->getNumberOfKarts()-1)
124 {
125 // The camera keeps track of all cameras and will free them
126 Camera::createCamera(new_kart.get(), local_player_id);
127 }
128 return new_kart;
129 } // createKart
130
131 //-----------------------------------------------------------------------------
132 /** The race is over if either the requested number of laps have been done
133 * or the requested time is over.
134 */
isRaceOver()135 bool ProfileWorld::isRaceOver()
136 {
137 if(m_profile_mode==PROFILE_TIME)
138 return getTime()>m_time;
139
140 if(m_profile_mode == PROFILE_LAPS )
141 {
142 // Now it must be laps based profiling:
143 return RaceManager::get()->getFinishedKarts()==getNumKarts();
144 }
145 // Unknown profile mode
146 assert(false);
147 return false; // keep compiler happy
148 } // isRaceOver
149
150 //-----------------------------------------------------------------------------
151 /** Counts the number of frames.
152 * \param ticks number of physics time steps - should be 1.
153 */
update(int ticks)154 void ProfileWorld::update(int ticks)
155 {
156 StandardRace::update(ticks);
157
158 m_frame_count++;
159 video::IVideoDriver *driver = irr_driver->getVideoDriver();
160 io::IAttributes *attr = irr_driver->getSceneManager()->getParameters();
161 m_num_triangles += (int)(driver->getPrimitiveCountDrawn( 0 )
162 * ( 1.f / 1000.f ));
163 m_num_calls += attr->getAttributeAsInt("calls");
164 m_num_culls += attr->getAttributeAsInt("culled" );
165 m_num_solid += attr->getAttributeAsInt("drawn_solid" );
166 m_num_transparent += attr->getAttributeAsInt("drawn_transparent" );
167 m_num_trans_effect += attr->getAttributeAsInt("drawn_transparent_effect" );
168
169 } // update
170
171 //-----------------------------------------------------------------------------
172 /** This function is called when the race is finished, but end-of-race
173 * animations have still to be played. In the case of profiling,
174 * we can just abort here without waiting for the animations.
175 */
enterRaceOverState()176 void ProfileWorld::enterRaceOverState()
177 {
178 // If in timing mode, the number of laps is way too high (which avoids
179 // aborting too early). So in this case determine the maximum number
180 // of laps and set this +1 as the number of laps to get more meaningful
181 // time estimations.
182 if(m_profile_mode==PROFILE_TIME)
183 {
184 int max_laps = -2;
185 for(unsigned int i=0; i<RaceManager::get()->getNumberOfKarts(); i++)
186 {
187 if(m_kart_info[i].m_finished_laps>max_laps)
188 max_laps = m_kart_info[i].m_finished_laps;
189 } // for i<getNumberOfKarts
190 RaceManager::get()->setNumLaps(max_laps+1);
191 }
192
193 StandardRace::enterRaceOverState();
194 // Estimate finish time and set all karts to be finished.
195 for (unsigned int i=0; i<RaceManager::get()->getNumberOfKarts(); i++)
196 {
197 // ---------- update rank ------
198 if (m_karts[i]->hasFinishedRace() || m_karts[i]->isEliminated())
199 continue;
200 m_karts[i]->finishedRace(estimateFinishTimeForKart(m_karts[i].get()));
201 }
202
203 // Print framerate statistics
204 float runtime = (irr_driver->getRealTime()-m_start_time)*0.001f;
205 Log::verbose("profile", "Number of frames: %d time %f, Average FPS: %f",
206 m_frame_count, runtime, (float)m_frame_count/runtime);
207
208 // Print geometry statistics if we're not in no-graphics mode
209 if(!GUIEngine::isNoGraphics())
210 {
211 Log::verbose("profile", "Average # drawn nodes %f k",
212 (float)m_num_triangles/m_frame_count);
213 Log::verbose("profile", "Average # culled nodes: %f k",
214 (float)m_num_culls/m_frame_count);
215 Log::verbose("profile", "Average # solid nodes: %f k",
216 (float)m_num_solid/m_frame_count);
217 Log::verbose("profile", "Average # transparent nodes: %f",
218 (float)m_num_transparent/m_frame_count);
219 Log::verbose("profile", "Average # transp. effect nodes: %f",
220 (float)m_num_trans_effect/m_frame_count);
221 }
222
223 // Print race statistics for each individual kart
224 float min_t=999999.9f, max_t=0.0, av_t=0.0;
225 Log::verbose("profile", "name start_position end_position time average_speed top_speed "
226 "skid_time rescue_time rescue_count brake_count "
227 "explosion_time explosion_count bonus_count banana_count "
228 "small_nitro_count large_nitro_count bubblegum_count");
229
230 std::set<std::string> all_groups;
231
232 for (KartList::size_type i = 0; i < m_karts.size(); ++i)
233 {
234 auto kart = std::dynamic_pointer_cast<KartWithStats>(m_karts[i]);
235
236 max_t = std::max(max_t, kart->getFinishTime());
237 min_t = std::min(min_t, kart->getFinishTime());
238 av_t += kart->getFinishTime();
239 std::ostringstream ss;
240 ss << kart->getIdent() << " "
241 << kart->getController()->getControllerName() << " ";
242 ss << 1+(int)i << " " << kart->getPosition() << " "
243 << kart->getFinishTime() << " ";
244
245 all_groups.insert(kart->getController()->getControllerName());
246 float distance = (float)(m_profile_mode==PROFILE_LAPS
247 ? RaceManager::get()->getNumLaps() : 1);
248 distance *= Track::getCurrentTrack()->getTrackLength();
249 ss << distance/kart->getFinishTime() << " " << kart->getTopSpeed() << " ";
250 ss << kart->getSkiddingTime() << " " << kart->getRescueTime() << " ";
251 ss << kart->getRescueCount() << " " << kart->getBrakeCount() << " ";
252 ss << kart->getExplosionTime() << " " << kart->getExplosionCount() << " ";
253 ss << kart->getBonusCount() << " " << kart->getBananaCount() << " ";
254 ss << kart->getSmallNitroCount() << " " << kart->getLargeNitroCount() << " ";
255 ss << kart->getBubblegumCount() << " " << kart->getOffTrackCount() << " ";
256 Log::verbose("profile", ss.str().c_str());
257 }
258
259 // Print group statistics of all karts
260 Log::verbose("profile", "min %f max %f av %f\n",
261 min_t, max_t, av_t/m_karts.size());
262
263 // Determine maximum length of group name
264 unsigned int max_len=4; // for 'name' heading
265 for(std::set<std::string>::iterator it = all_groups.begin();
266 it !=all_groups.end(); it++)
267 {
268 if(it->size()>max_len) max_len = (unsigned int) it->size();
269 }
270 max_len++; // increase by 1 for one additional space after the name
271
272 std::ostringstream ss;
273 Log::verbose("profile", "");
274 ss << "name" << std::setw(max_len-4) << " "
275 << "Strt End Time AvSp Top Skid Resc Rsc Brake Expl Exp Itm Ban SNitLNit Bub Off Energy";
276 Log::verbose("profile", ss.str().c_str());
277 for(std::set<std::string>::iterator it = all_groups.begin();
278 it !=all_groups.end(); it++)
279 {
280 int count = 0, position_gain = 0, rescue_count = 0;
281 int brake_count = 0, bonus_count = 0, banana_count = 0;
282 int l_nitro_count = 0, s_nitro_count = 0, bubble_count = 0;
283 int expl_count = 0, off_track_count = 0;
284 float skidding_time = 0.0f, rescue_time = 0.0f, expl_time = 0.0f;
285 float av_time = 0.0f, energy = 0;
286 for ( unsigned int i = 0; i < (unsigned int)m_karts.size(); ++i)
287 {
288 auto kart = std::dynamic_pointer_cast<KartWithStats>(m_karts[i]);
289 const std::string &name=kart->getController()->getControllerName();
290 if(name!=*it)
291 continue;
292 count ++;
293 std::ostringstream ss;
294 ss.setf(std::ios::fixed, std::ios::floatfield);
295 ss.precision(2);
296 ss << name << std::setw(max_len-name.size()) << " ";
297 ss << std::setw(4) << 1 + i
298 << std::setw(4) << kart->getPosition()
299 << std::setw(7) << kart->getFinishTime();
300 position_gain += 1+i - kart->getPosition();
301
302 float distance = (float)(m_profile_mode==PROFILE_LAPS
303 ? RaceManager::get()->getNumLaps() : 1);
304 distance *= Track::getCurrentTrack()->getTrackLength();
305
306 Log::verbose("profile",
307 "%s %4.2f %3.2f %6.2f %4.2f %3d %5d %4.2f %3d %3d %3d %3d %3d %3d %5d %4.2f",
308 ss.str().c_str(), distance/kart->getFinishTime(),
309 kart->getTopSpeed(),
310 kart->getSkiddingTime(), kart->getRescueTime(),
311 kart->getRescueCount(), kart->getBrakeCount(),
312 kart->getExplosionTime(), kart->getExplosionCount(),
313 kart->getBonusCount(), kart->getBananaCount(),
314 kart->getSmallNitroCount(), kart->getLargeNitroCount(),
315 kart->getBubblegumCount(), kart->getOffTrackCount(),
316 kart->getEnergy()
317 );
318 av_time += kart->getFinishTime();
319 skidding_time += kart->getSkiddingTime();
320 rescue_time += kart->getRescueTime();
321 rescue_count += kart->getRescueCount();
322 brake_count += kart->getBrakeCount();
323 bonus_count += kart->getBonusCount();
324 banana_count += kart->getBananaCount();
325 l_nitro_count += kart->getLargeNitroCount();
326 s_nitro_count += kart->getSmallNitroCount();
327 bubble_count += kart->getBubblegumCount();
328 expl_time += kart->getExplosionTime();
329 expl_count += kart->getExplosionCount();
330 off_track_count += kart->getOffTrackCount();
331 energy += kart->getEnergy();
332 } // for i < m_karts.size
333
334 Log::verbose("profile", std::string(max_len+90, '-').c_str());
335 ss.clear();
336 ss.str("");
337 ss << *it << std::string(max_len-it->size(),' ');
338 ss << std::showpos << std::setw(4) << position_gain
339 << std::noshowpos << std::setw(13) << av_time/count
340 << std::string(11,' ');
341
342 Log::verbose("profile", "%s%6.2f %4.2f %3d %5d %4.2f %3d %3d %3d %3d %3d %3d %5d %4.2f",
343 ss.str().c_str(), skidding_time/count, rescue_time/count,
344 rescue_count,brake_count, expl_time, expl_count, bonus_count,
345 banana_count, s_nitro_count, l_nitro_count, bubble_count,
346 off_track_count, energy);
347 Log::verbose("profile", "");
348 } // for it !=all_groups.end
349 delete this;
350 main_loop->abort();
351 } // enterRaceOverState
352