Beispiel #1
0
ScopedMtTimer::~ScopedMtTimer()
{
	profiler.AddTime(GetName(), spring_difftime(spring_gettime(), starttime), autoShowGraph);
#ifdef THREADPOOL
	auto& list = profiler.profileCore[ThreadPool::GetThreadNum()];
	list.emplace_back(starttime, spring_gettime());
#endif
}
Beispiel #2
0
void HangDetector() {
	while (keepRunning) {
		// increase multiplier during game load to prevent false positives e.g. during pathing
		const int hangTimeMultiplier = CrashHandler::gameLoading? 3 : 1;
		const spring_time hangtimeout = spring_msecs(spring_tomsecs(hangTimeout) * hangTimeMultiplier);

		spring_time curwdt = spring_gettime();
#if defined(USE_GML) && GML_ENABLE_SIM
		if (gmlMultiThreadSim) {
			spring_time cursimwdt = simwdt;
			if (spring_istime(cursimwdt) && curwdt > cursimwdt && (curwdt - cursimwdt) > hangtimeout) {
				HangHandler(true);
				simwdt = curwdt;
			}
		}
#endif
		spring_time curdrawwdt = drawwdt;
		if (spring_istime(curdrawwdt) && curwdt > curdrawwdt && (curwdt - curdrawwdt) > hangtimeout) {
			HangHandler(false);
			drawwdt = curwdt;
		}

		spring_sleep(spring_secs(1));
	}
}
Beispiel #3
0
void CTimeProfiler::Update()
{
	//FIXME non-locking threadsafe
	boost::unique_lock<boost::mutex> ulk(m, boost::defer_lock);
	while (!ulk.try_lock()) {}

	++currentPosition;
	currentPosition &= TimeRecord::frames_size-1;
	std::map<std::string,TimeRecord>::iterator pi;
	for (pi = profile.begin(); pi != profile.end(); ++pi)
	{
		pi->second.frames[currentPosition] = spring_notime;
	}

	const spring_time curTime = spring_gettime();
	const float timeDiff = spring_diffmsecs(curTime, lastBigUpdate);
	if (timeDiff > 500.0f) // twice every second
	{
		for (std::map<std::string,TimeRecord>::iterator pi = profile.begin(); pi != profile.end(); ++pi)
		{
			pi->second.percent = spring_tomsecs(pi->second.current) / timeDiff;
			pi->second.current = spring_notime;

			if(pi->second.percent > pi->second.peak) {
				pi->second.peak = pi->second.percent;
				pi->second.newpeak = true;
			}
			else
				pi->second.newpeak = false;
		}
		lastBigUpdate = curTime;
	}
}
Beispiel #4
0
void CCameraHandler::UpdateCam()
{
	//??? a lot CameraControllers depend on the calling every frame the 1st part of the if-clause 
	//if (cameraTimeEnd < 0.0f) {
	//	return;
	//}
	
	const float  wantedCamFOV = currCamCtrl->GetFOV();
	const float3 wantedCamPos = currCamCtrl->GetPos();
	const float3 wantedCamDir = currCamCtrl->GetDir();

	const float curTime = spring_tomsecs(spring_gettime()) / 1000.0f;
	if (curTime >= cameraTimeEnd) {
		camera->pos     = wantedCamPos;
		camera->forward = wantedCamDir;
		camera->SetFov(wantedCamFOV);
		//cameraTimeEnd   = -1.0f;
	}
	else {
		const float timeRatio = (cameraTimeEnd - curTime) / (cameraTimeEnd - cameraTimeStart);
		const float tweenFact = 1.0f - (float)math::pow(timeRatio, cameraTimeExponent);

		camera->pos     = mix(startCam.pos, wantedCamPos, tweenFact);
		camera->forward = mix(startCam.dir, wantedCamDir, tweenFact);
		camera->SetFov(   mix(startCam.fov, wantedCamFOV, tweenFact));
		camera->forward.Normalize();
	}
}
CGlobalUnsynced::CGlobalUnsynced()
{
	rng.Seed(time(NULL) % ((spring_gettime().toNanoSecsi() + 1) * 9007));

	assert(playerHandler == NULL);
	ResetState();
}
Beispiel #6
0
void GameSetupDrawer::StartCountdown(unsigned time)
{
    if (instance) {
        instance->lastTick = spring_gettime(); //FIXME
        instance->readyCountdown = spring_msecs(time);
    }
}
Beispiel #7
0
void CTimeProfiler::AddTime(const std::string& name, const spring_time time, const bool showGraph)
{
	std::map<std::string, TimeRecord>::iterator pi;
	if ( (pi = profile.find(name)) != profile.end() ) {
		// profile already exists
		//FIXME use atomic ints
		pi->second.total   += time;
		pi->second.current += time;
		pi->second.frames[currentPosition] += time;
	} else {
		boost::unique_lock<boost::mutex> ulk(m, boost::defer_lock);
		while (!ulk.try_lock()) {}

		// create a new profile
		auto& p = profile[name];
		p.total   = time;
		p.current = time;
		p.percent = 0;
		memset(p.frames, 0, TimeRecord::frames_size * sizeof(unsigned));
		static UnsyncedRNG rand;
		rand.Seed(spring_tomsecs(spring_gettime()));
		p.color.x = rand.RandFloat();
		p.color.y = rand.RandFloat();
		p.color.z = rand.RandFloat();
		p.showGraph = showGraph;
	}
}
Beispiel #8
0
CTimeProfiler::CTimeProfiler():
	lastBigUpdate(spring_gettime()),
	currentPosition(0)
{
#ifdef THREADPOOL
	profileCore.resize(ThreadPool::GetMaxThreads());
#endif
}
Beispiel #9
0
std::int64_t CPathManager::Finalize() {
	const spring_time t0 = spring_gettime();

	{
		// maxResPF only runs on the main thread, so can be unsafe
		maxResPF = pfMemPool.alloc<CPathFinder>(false);
		medResPE = peMemPool.alloc<CPathEstimator>(maxResPF, MEDRES_PE_BLOCKSIZE, "pe",  mapInfo->map.name);
		lowResPE = peMemPool.alloc<CPathEstimator>(medResPE, LOWRES_PE_BLOCKSIZE, "pe2", mapInfo->map.name);

		// make cached path data checksum part of synced state
		// so that when any client has a corrupted / incorrect
		// cache it desyncs from the start, not minutes later
		{ SyncedUint tmp(GetPathCheckSum()); }
	}

	const spring_time dt = spring_gettime() - t0;
	return (dt.toMilliSecsi());
}
Beispiel #10
0
void CCameraHandler::CameraTransition(float time)
{
	time = std::max(time, 0.0f) * cameraTimeFactor;

	cameraTimeStart = spring_tomsecs(spring_gettime()) / 1000.0f;
	cameraTimeEnd   = cameraTimeStart + time;
	startCam.pos = camera->pos;
	startCam.dir = camera->forward;
	startCam.fov = camera->GetFov();
}
Beispiel #11
0
int LuaParser::TimeCheck(lua_State* L)
{
	if (!lua_isstring(L, 1) || !lua_isfunction(L, 2)) {
		luaL_error(L, "Invalid arguments to TimeCheck('string', func, ...)");
	}
	const string name = lua_tostring(L, 1);
	lua_remove(L, 1);
	const spring_time startTime = spring_gettime();
	const int error = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0);
	if (error != 0) {
		const string errmsg = lua_tostring(L, -1);
		lua_pop(L, 1);
		luaL_error(L, errmsg.c_str());
	}
	const spring_time endTime = spring_gettime();
	const float elapsed = 1.0e-3f * (float)(spring_tomsecs(endTime - startTime));
	logOutput.Print("%s %f", name.c_str(), elapsed);
	return lua_gettop(L);
}
Beispiel #12
0
BasicTimer::BasicTimer(const char* myname)
: hash(hash_(myname))
, starttime(spring_gettime())

{
	nameIterator = hashToName.find(hash);
	if (nameIterator == hashToName.end()) {
		nameIterator = hashToName.insert(std::pair<int,std::string>(hash, myname)).first;
	}
}
Beispiel #13
0
// stops the currently playing stream
void COggStream::Stop()
{
	if (stopped) {
		return;
	}

	ReleaseBuffers();
	msecsPlayed = spring_nulltime;
	vorbisInfo = NULL;
	lastTick = spring_gettime();
}
Beispiel #14
0
	void ClearPrimaryTimers(bool disable)
	{
		if (hangDetectorThread == NULL)
			return; //! Watchdog isn't running

		for (unsigned int i = 0; i < WDT_LAST; ++i) {
			WatchDogThreadInfo* th_info = registeredThreads[i];
			if (threadSlots[i].primary)
				th_info->timer = disable ? spring_notime : spring_gettime();
		}
	}
Beispiel #15
0
void spring_time::Serialize(creg::ISerializer& s)
{
    if (s.IsWriting()) {
        int y = spring_tomsecs(*this - spring_gettime());
        s.SerializeInt(&y, 4);
    } else {
        int y;
        s.SerializeInt(&y, 4);
        *this = *this + spring_msecs(y);
    }
}
Beispiel #16
0
	void RegisterThread(std::string name)
	{
		boost::unique_lock<boost::shared_mutex> lock(mutex);
		Threading::NativeThreadHandle thread = Threading::_GetCurrentThread();
		Threading::NativeThreadId threadId = Threading::_GetCurrentThreadId();
		WatchDogThreadInfo& th_info = registeredThreads[threadId];
		th_info.thread = thread;
		th_info.timer = spring_gettime();
		th_info.name = name;
		threadNameToId[name] = threadId;
	}
Beispiel #17
0
// returns true if both buffers were
// filled with data from the stream
bool COggStream::StartPlaying()
{
	msecsPlayed = spring_nulltime;
	lastTick = spring_gettime();

	if (!DecodeStream(buffers[0])) { return false; }
	if (!DecodeStream(buffers[1])) { return false; }

	alSourceQueueBuffers(source, 2, buffers); CheckError("COggStream::StartPlaying");
	alSourcePlay(source); CheckError("COggStream::StartPlaying");

	return true;
}
Beispiel #18
0
	void ClearTimer(WatchdogThreadnum num, bool disable)
	{
		if (hangDetectorThread == NULL)
			return; //! Watchdog isn't running

		WatchDogThreadInfo* th_info;
		if (num >= WDT_LAST || !(th_info = registeredThreads[num])->numreg) {
			LOG_L(L_ERROR, "[Watchdog::ClearTimer] Invalid thread number %d", num);
			return;
		}

		th_info->timer = disable ? spring_notime : spring_gettime();
	}
Beispiel #19
0
void SmoothController::ScreenEdgeMove(float3 move)
{
	if (flipped) {
		move.x = -move.x;
		move.y = -move.y;
	}
	move *= math::sqrt(move.z) * 200.0f;

	const float3 thisMove(move.x * pixelSize * 2.0f * scrollSpeed, 0.0f, -move.y * pixelSize * 2.0f * scrollSpeed);

	static spring_time lastScreenMove = spring_gettime();
	const spring_time timeDiff = spring_gettime() - lastScreenMove;
	lastScreenMove = spring_gettime();

	if (thisMove.x != 0 || thisMove.z != 0) {
		// user want to move with mouse on screen edge
		lastSource = ScreenEdge;
		Move(thisMove, timeDiff.toMilliSecsf());
	} else if (lastSource == ScreenEdge) {
		// last move order was given by screen edge, so call Move() to break
		Move(thisMove, timeDiff.toMilliSecsf());
	}
}
Beispiel #20
0
	void ClearTimer(const std::string &name, bool disable)
	{
		if (hangDetectorThread == NULL)
			return; //! Watchdog isn't running

		std::map<std::string, unsigned int>::iterator i = threadNameToNum.find(name);
		unsigned int num;
		WatchDogThreadInfo* th_info;
		if (i == threadNameToNum.end() || (num = i->second) >= WDT_LAST || !(th_info = registeredThreads[num])->numreg) {
			LOG_L(L_ERROR, "[Watchdog::ClearTimer] Invalid thread name");
			return;
		}

		th_info->timer = disable ? spring_notime : spring_gettime();
	}
Beispiel #21
0
	static void HangDetectorLoop()
	{
		Threading::SetThreadName("watchdog");

		while (!hangDetectorThreadInterrupted) {
			spring_time curtime = spring_gettime();
			bool hangDetected = false;

			for (unsigned int i = 0; i < WDT_LAST; ++i) {
				if (!threadSlots[i].active)
					continue;

				WatchDogThreadInfo* th_info = registeredThreads[i];
				spring_time curwdt = th_info->timer;

				if (spring_istime(curwdt) && curtime > curwdt && (curtime - curwdt) > hangTimeout) {
					if (!hangDetected) {
						LOG_L(L_WARNING, "[Watchdog] Hang detection triggered for Spring %s.", SpringVersion::GetFull().c_str());
#ifdef USE_GML
						LOG_L(L_WARNING, "MT with %d threads.", GML::ThreadCount());
#endif
					}
					LOG_L(L_WARNING, "  (in thread: %s)", threadNames[i]);

					hangDetected = true;
					th_info->timer = curtime;
				}
			}

			if (hangDetected) {
				CrashHandler::PrepareStacktrace();

				for (unsigned int i = 0; i < WDT_LAST; ++i) {
					if (!threadSlots[i].active)
						continue;

					WatchDogThreadInfo* th_info = registeredThreads[i];
					CrashHandler::Stacktrace(th_info->thread, threadNames[i]);
					LOG_CLEANUP();
				}

				CrashHandler::CleanupStacktrace();
			}

			boost::this_thread::sleep(boost::posix_time::seconds(1));
		}
	}
Beispiel #22
0
	static void HangDetector()
	{
		while (!boost::this_thread::interruption_requested()) {
			{
				boost::shared_lock<boost::shared_mutex> lock(mutex);
				const spring_time curtime = spring_gettime();
				for (ThreadsMap::iterator it=registeredThreads.begin(); it != registeredThreads.end(); ++it) {
					WatchDogThreadInfo& th_info = it->second;
					if (spring_istime(th_info.timer) && (curtime - th_info.timer) > hangTimeout) {
						HangHandler(th_info);
						th_info.timer = curtime;
					}
				}
			}
			boost::this_thread::sleep(boost::posix_time::seconds(1));
		}
	}
Beispiel #23
0
	void RegisterThread(WatchdogThreadnum num, bool primary)
	{
		boost::mutex::scoped_lock lock(wdmutex);

		if (num >= WDT_LAST || registeredThreads[num]->numreg) {
			LOG_L(L_ERROR, "[Watchdog::RegisterThread] Invalid thread number");
			return;
		}

		Threading::NativeThreadHandle thread = Threading::GetCurrentThread();
		Threading::NativeThreadId threadId = Threading::GetCurrentThreadId();

		unsigned int inact = WDT_LAST;
		unsigned int i;
		for (i = 0; i < WDT_LAST; ++i) {
			WatchDogThreadInfo* th_info = &registeredThreadsData[i];
			if (!th_info->numreg)
				inact = std::min(i, inact);
			else if(Threading::NativeThreadIdsEqual(th_info->threadid, threadId))
				break;
		}
		if (i >= WDT_LAST)
			i = inact;
		if (i >= WDT_LAST) {
			LOG_L(L_ERROR, "[Watchdog::RegisterThread] Internal error");
			return;
		}
		registeredThreads[num] = &registeredThreadsData[i];

		// set threadname
		//Threading::SetThreadName(threadNames[num]);

		WatchDogThreadInfo* th_info = registeredThreads[num];
		th_info->thread = thread;
		th_info->threadid = threadId;
		th_info->timer = spring_gettime();
		++th_info->numreg;

		threadSlots[num].primary = primary;
		threadSlots[num].regorder = ++curorder;
		UpdateActiveThreads(threadId);
	}
Beispiel #24
0
void COggStream::Update()
{
	if (stopped) {
		return;
	}

	spring_time tick = spring_gettime();

	if (!paused) {
		UpdateBuffers();

		if (!IsPlaying()) {
			ReleaseBuffers();
		}

		msecsPlayed += (tick - lastTick);
	}

	lastTick = tick;
}
Beispiel #25
0
	void ClearTimer(bool disable, Threading::NativeThreadId* _threadId)
	{
		boost::shared_lock<boost::shared_mutex> lock(mutex);
		if (!hangdetectorthread)
			return; //! Watchdog isn't running
		Threading::NativeThreadId threadId;
			if (_threadId) {
			threadId = *_threadId;
		} else {
			threadId = Threading::_GetCurrentThreadId();
		}
		ThreadsMap::iterator it = registeredThreads.find(threadId);
		if (it == registeredThreads.end()) {
			return;
		}
		assert(it != registeredThreads.end()); //! the thread isn't registered!

		WatchDogThreadInfo& th_info = it->second;
		th_info.timer = disable ? spring_notime : spring_gettime();
	}
Beispiel #26
0
void CSoundSource::Update()
{
	if (asyncPlay.buffer != nullptr) {
		Play(asyncPlay.channel, asyncPlay.buffer, asyncPlay.pos, asyncPlay.velocity, asyncPlay.volume, asyncPlay.relative);
		asyncPlay = AsyncSoundItemData();
	}

	if (curPlaying != nullptr) {
		if (in3D && (efxEnabled != efx->enabled)) {
			alSourcef(id, AL_AIR_ABSORPTION_FACTOR, (efx->enabled) ? efx->GetAirAbsorptionFactor() : 0);
			alSource3i(id, AL_AUXILIARY_SEND_FILTER, (efx->enabled) ? efx->sfxSlot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);
			alSourcei(id, AL_DIRECT_FILTER, (efx->enabled) ? efx->sfxFilter : AL_FILTER_NULL);
			efxEnabled = efx->enabled;
			efxUpdates = efx->updates;
		}

		if (heightRolloffModifier != curHeightRolloffModifier) {
			curHeightRolloffModifier = heightRolloffModifier;
			alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * curPlaying->rolloff * heightRolloffModifier);
		}

		if (!IsPlaying(true) || ((curPlaying->loopTime > 0) && (spring_gettime() > loopStop)))
			Stop();
	}

	if (curStream.Valid()) {
		if (curStream.IsFinished()) {
			Stop();
		} else {
			curStream.Update();
			CheckError("CSoundSource::Update");
		}
	}

	if (efxEnabled && (efxUpdates != efx->updates)) {
		//! airAbsorption & LowPass aren't auto updated by OpenAL on change, so we need to do it per source
		alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx->GetAirAbsorptionFactor());
		alSourcei(id, AL_DIRECT_FILTER, efx->sfxFilter);
		efxUpdates = efx->updates;
	}
}
Beispiel #27
0
	void ClearTimer(bool disable, Threading::NativeThreadId* _threadId)
	{
		if (hangDetectorThread == NULL)
			return; //! Watchdog isn't running

		Threading::NativeThreadId threadId;
		if (_threadId)
			threadId = *_threadId;
		else
			threadId = Threading::GetCurrentThreadId();
		unsigned int num = 0;
		for (; num < WDT_LAST; ++num)
			if (Threading::NativeThreadIdsEqual(registeredThreads[num]->threadid, threadId))
				break;
		WatchDogThreadInfo* th_info;
		if (num >= WDT_LAST || !(th_info = registeredThreads[num])->numreg) {
			LOG_L(L_ERROR, "[Watchdog::ClearTimer] Invalid thread %d", num);
			return;
		}

		th_info->timer = disable ? spring_notime : spring_gettime();
	}
Beispiel #28
0
void BenchmarkSleepFnc(const std::string& name, void (*sleep)(int time), const int runs, const float toMilliSecondsScale)
{
	// waste a few cycles to push the cpu to higher frequency states
	for (auto spinStopTime = spring_gettime() + spring_secs(2); spring_gettime() < spinStopTime; ) {
	}

	spring_time t = spring_gettime();
	spring_time tmin, tmax;
	float tavg = 0;

	// check lowest possible sleep tick
	for (int i=0; i<runs; ++i) {
		sleep(0);

		spring_time diff = spring_gettime() - t;
		if ((diff > tmax) || !spring_istime(tmax)) tmax = diff;
		if ((diff < tmin) || !spring_istime(tmin)) tmin = diff;
		tavg = float(i * tavg + diff.toNanoSecsi()) / (i + 1);
		t = spring_gettime();
	}

	// check error in sleeping times
	spring_time emin, emax;
	float eavg = 0;
	if (toMilliSecondsScale != 0) {
		for (int i=0; i<100; ++i) {
			const auto sleepTime = (rand() % 50) * 0.1f + 2; // 2..7ms

			t = spring_gettime();
			sleep(sleepTime * toMilliSecondsScale);
			spring_time diff = (spring_gettime() - t) - spring_msecs(sleepTime);

			if ((diff > emax) || !emax.isDuration()) emax = diff;
			if ((diff < emin) || !emin.isDuration()) emin = diff;
			eavg = float(i * eavg + std::abs(diff.toNanoSecsf())) / (i + 1);
		}
	}

	LOG("[%35s] accuracy:={ err: %+.4fms %+.4fms erravg: %.4fms } min sleep time:={ min: %.6fms avg: %.6fms max: %.6fms }", name.c_str(), emin.toMilliSecsf(), emax.toMilliSecsf(), eavg * 1e-6, tmin.toMilliSecsf(), tavg * 1e-6, tmax.toMilliSecsf());
}
Beispiel #29
0
void CTimeProfiler::Update()
{
	//FIXME non-locking threadsafe
	boost::unique_lock<boost::mutex> ulk(m, boost::defer_lock);
	while (!ulk.try_lock()) {}

	++currentPosition;
	currentPosition &= TimeRecord::frames_size-1;
	for (auto& pi: profile) {
		pi.second.frames[currentPosition] = spring_notime;
	}

	const spring_time curTime = spring_gettime();
	const float timeDiff = spring_diffmsecs(curTime, lastBigUpdate);
	if (timeDiff > 500.0f) // twice every second
	{
		for (auto& pi: profile) {
			auto& p = pi.second;
			p.percent = spring_tomsecs(p.current) / timeDiff;
			p.current = spring_notime;
			p.newLagPeak = false;
			p.newPeak = false;
			if(p.percent > p.peak) {
				p.peak = p.percent;
				p.newPeak = true;
			}
		}
		lastBigUpdate = curTime;
	}

	if (curTime.toSecsi() % 6 == 0) {
		for (auto& pi: profile) {
			auto& p = pi.second;
			p.maxLag *= 0.5f;
		}
	}
}
Beispiel #30
0
bool CLoadScreen::Draw()
{
    //! Limit the Frames Per Second to not lock a singlethreaded CPU from loading the game
    if (mtLoading) {
        spring_time now = spring_gettime();
        unsigned diff_ms = spring_tomsecs(now - last_draw);
        static const unsigned wantedFPS = 50;
        static const unsigned min_frame_time = 1000 / wantedFPS;
        if (diff_ms < min_frame_time) {
            spring_time nap = spring_msecs(min_frame_time - diff_ms);
            spring_sleep(nap);
        }
        last_draw = now;
    }

    //! cause of `curLoadMessage`
    boost::recursive_mutex::scoped_lock lck(mutex);

    if (LuaIntro != nullptr) {
        LuaIntro->Update();
        LuaIntro->DrawGenesis();
        ClearScreen();
        LuaIntro->DrawLoadScreen();
    } else {
        ClearScreen();

        float xDiv = 0.0f;
        float yDiv = 0.0f;
        const float ratioComp = globalRendering->aspectRatio / aspectRatio;
        if (math::fabs(ratioComp - 1.0f) < 0.01f) { //! ~= 1
            //! show Load-Screen full screen
            //! nothing to do
        } else if (ratioComp > 1.0f) {
            //! show Load-Screen on part of the screens X-Axis only
            xDiv = (1.0f - (1.0f / ratioComp)) * 0.5f;
        } else {
            //! show Load-Screen on part of the screens Y-Axis only
            yDiv = (1.0f - ratioComp) * 0.5f;
        }

        //! Draw loading screen & print load msg.
        if (startupTexture) {
            glBindTexture(GL_TEXTURE_2D,startupTexture);
            glBegin(GL_QUADS);
            glTexCoord2f(0.0f, 1.0f);
            glVertex2f(0.0f + xDiv, 0.0f + yDiv);
            glTexCoord2f(0.0f, 0.0f);
            glVertex2f(0.0f + xDiv, 1.0f - yDiv);
            glTexCoord2f(1.0f, 0.0f);
            glVertex2f(1.0f - xDiv, 1.0f - yDiv);
            glTexCoord2f(1.0f, 1.0f);
            glVertex2f(1.0f - xDiv, 0.0f + yDiv);
            glEnd();
        }

        if (showMessages) {
            font->Begin();
            font->SetTextColor(0.5f,0.5f,0.5f,0.9f);
            font->glPrint(0.1f,0.9f,   globalRendering->viewSizeY / 35.0f, FONT_NORM,
                          oldLoadMessages);

            font->SetTextColor(0.9f,0.9f,0.9f,0.9f);
            float posy = font->GetTextNumLines(oldLoadMessages) * font->GetLineHeight() * globalRendering->viewSizeY / 35.0f;
            font->glPrint(0.1f,0.9f - posy * globalRendering->pixelY,   globalRendering->viewSizeY / 35.0f, FONT_NORM,
                          curLoadMessage);
            font->End();
        }
    }

    // Always render Spring's license notice
    font->Begin();
    font->SetOutlineColor(0.0f,0.0f,0.0f,0.65f);
    font->SetTextColor(1.0f,1.0f,1.0f,1.0f);
    font->glFormat(0.5f,0.06f, globalRendering->viewSizeY / 35.0f, FONT_OUTLINE | FONT_CENTER | FONT_NORM,
                   "Spring %s", SpringVersion::GetFull().c_str());
    font->glFormat(0.5f,0.02f, globalRendering->viewSizeY / 50.0f, FONT_OUTLINE | FONT_CENTER | FONT_NORM,
                   "This program is distributed under the GNU General Public License, see license.html for more info");
    font->End();

    if (!mtLoading)
        SDL_GL_SwapWindow(globalRendering->window);

    return true;
}