コード例 #1
0
bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) 
{
	// Load required symbols from the DLL
	try
	{
		dll.LoadSymbol("Atlas_StartWindow", Atlas_StartWindow);
		dll.LoadSymbol("Atlas_SetMessagePasser", Atlas_SetMessagePasser);
		dll.LoadSymbol("Atlas_SetDataDirectory", Atlas_SetDataDirectory);
		dll.LoadSymbol("Atlas_GLSetCurrent", Atlas_GLSetCurrent);
		dll.LoadSymbol("Atlas_GLSwapBuffers", Atlas_GLSwapBuffers);
		dll.LoadSymbol("Atlas_NotifyEndOfFrame", Atlas_NotifyEndOfFrame);
		dll.LoadSymbol("Atlas_DisplayError", Atlas_DisplayError);
		dll.LoadSymbol("Atlas_ReportError", Atlas_ReportError);
		dll.LoadSymbol("ShareableMalloc", ShareableMallocFptr);
		dll.LoadSymbol("ShareableFree", ShareableFreeFptr);
	}
	catch (PSERROR_DllLoader&)
	{
		debug_warn(L"Failed to initialise DLL");
		return false;
	}

	// Construct a message passer for communicating with Atlas
	MessagePasserImpl msgPasser;
	AtlasMessage::g_MessagePasser = &msgPasser;

	// Pass our message handler to Atlas
	Atlas_SetMessagePasser(&msgPasser);

	// Tell Atlas the location of the data directory
	const Paths paths(args);
	Atlas_SetDataDirectory(paths.RData().string().c_str());

	// Register all the handlers for message which might be passed back
	RegisterHandlers();

	// Create a new thread, and launch the Atlas window inside that thread
	const wchar_t* windowName = FindWindowName(args);
	pthread_t uiThread;
	pthread_create(&uiThread, NULL, LaunchWindow, reinterpret_cast<void*>(const_cast<wchar_t*>(windowName)));

	// Override ah_display_error to pass all errors to the Atlas UI
	AppHooks hooks = {0};
	hooks.display_error = AtlasDisplayError;
	app_hooks_update(&hooks);
	
	// Disable the game's cursor rendering
	extern CStrW g_CursorName;
	g_CursorName = L"";

	state.args = args;
	state.running = true;
	state.view = View::GetView_None();
	state.glCanvas = NULL;

	double last_activity = timer_Time();

	while (state.running)
	{
		bool recent_activity = false;

		//////////////////////////////////////////////////////////////////////////
		// (TODO: Work out why these things have to be in this order (to avoid
		// jumps when starting to move, etc))

		// Calculate frame length
		{
			double time = timer_Time();
			static double last_time = time;
			float length = (float)(time-last_time);
			last_time = time;
			ENSURE(length >= 0.0f);
			// TODO: filter out big jumps, e.g. when having done a lot of slow
			// processing in the last frame
			state.frameLength = length;
		}

		// Process the input that was received in the past
		if (g_Input.ProcessInput(&state))
			recent_activity = true;

		//////////////////////////////////////////////////////////////////////////
		
		{
			IMessage* msg;
			while ((msg = msgPasser.Retrieve()) != NULL)
			{
				recent_activity = true;

				std::string name (msg->GetName());

				msgHandlers::const_iterator it = GetMsgHandlers().find(name);
				if (it != GetMsgHandlers().end())
				{
					it->second(msg);
				}
				else
				{
					debug_warn(L"Unrecognised message");
					// CLogger might not be initialised, but this error will be sent
					// to the debug output window anyway so people can still see it
					LOGERROR(L"Unrecognised message (%hs)", name.c_str());
				}

				if (msg->GetType() == IMessage::Query)
				{
					// For queries, we need to notify MessagePasserImpl::Query
					// that the query has now been processed.
					sem_post((sem_t*) static_cast<QueryMessage*>(msg)->m_Semaphore);
					// (msg may have been destructed at this point, so don't use it again)

					// It's quite possible that the querier is going to do a tiny
					// bit of processing on the query results and then issue another
					// query, and repeat lots of times in a loop. To avoid slowing
					// that down by rendering between every query, make this
					// thread yield now.
					SDL_Delay(0);
				}
				else
				{
					// For non-queries, we need to delete the object, since we
					// took ownership of it.
					AtlasMessage::ShareableDelete(msg);
				}
			}
		}

		// Exit, if desired
		if (! state.running)
			break;

		//////////////////////////////////////////////////////////////////////////

		// Do per-frame processing:

		ReloadChangedFiles();

		RendererIncrementalLoad();

		// Pump SDL events (e.g. hotkeys)
		SDL_Event_ ev;
		while (SDL_PollEvent(&ev.ev))
			in_dispatch_event(&ev);

		if (g_GUI)
			g_GUI->TickObjects();

		state.view->Update(state.frameLength);

		state.view->Render();

		if (CProfileManager::IsInitialised())
			g_Profiler.Frame();


		double time = timer_Time();
		if (recent_activity)
			last_activity = time;

		// Be nice to the processor (by sleeping lots) if we're not doing anything
		// useful, and nice to the user (by just yielding to other threads) if we are
		bool yield = (time - last_activity > 0.5);

		// But make sure we aren't doing anything interesting right now, where
		// the user wants to see the screen updating even though they're not
		// interacting with it
		if (state.view->WantsHighFramerate())
			yield = false;

		if (yield) // if there was no recent activity...
		{
			double sleepUntil = time + 0.5; // only redraw at 2fps
			while (time < sleepUntil)
			{
				// To minimise latency when the user starts doing stuff, only
				// sleep for a short while, then check if anything's happened,
				// then go back to sleep
				// (TODO: This should probably be done with something like semaphores)
				Atlas_NotifyEndOfFrame(); // (TODO: rename to NotifyEndOfQuiteShortProcessingPeriodSoPleaseSendMeNewMessages or something)
				SDL_Delay(50);
				if (!msgPasser.IsEmpty())
					break;
				time = timer_Time();
			}
		}
		else
		{
			Atlas_NotifyEndOfFrame();
			SDL_Delay(0);
		}
	}

	// TODO: delete all remaining messages, to avoid memory leak warnings

	// Wait for the UI to exit
	pthread_join(uiThread, NULL);

	// Clean up
	View::DestroyViews();
	ScriptingHost::FinalShutdown();

	return true;
}
コード例 #2
0
static void* RunEngine(void* data)
{
	debug_SetThreadName("engine_thread");

	// Set new main thread so that all the thread-safety checks pass
	ThreadUtil::SetMainThread();

	g_Profiler2.RegisterCurrentThread("atlasmain");

	const CmdLineArgs args = *reinterpret_cast<const CmdLineArgs*>(data);

	MessagePasserImpl* msgPasser = (MessagePasserImpl*)AtlasMessage::g_MessagePasser;	

	// Register all the handlers for message which might be passed back
	RegisterHandlers();
	
	// Override ah_display_error to pass all errors to the Atlas UI
	AppHooks hooks = {0};
	hooks.display_error = AtlasDisplayError;
	app_hooks_update(&hooks);
	
	// Disable the game's cursor rendering
	extern CStrW g_CursorName;
	g_CursorName = L"";

	state.args = args;
	state.running = true;
	state.view = AtlasView::GetView_None();
	state.glCanvas = NULL;

	double last_activity = timer_Time();

	while (state.running)
	{
		bool recent_activity = false;

		//////////////////////////////////////////////////////////////////////////
		// (TODO: Work out why these things have to be in this order (to avoid
		// jumps when starting to move, etc))

		// Calculate frame length
		{
			const double time = timer_Time();
			static double last_time = time;
			const double realFrameLength = time-last_time;
			last_time = time;
			ENSURE(realFrameLength >= 0.0);
			// TODO: filter out big jumps, e.g. when having done a lot of slow
			// processing in the last frame
			state.realFrameLength = realFrameLength;
		}

		// Process the input that was received in the past
		if (g_Input.ProcessInput(&state))
			recent_activity = true;

		//////////////////////////////////////////////////////////////////////////
		
		{
			IMessage* msg;
			while ((msg = msgPasser->Retrieve()) != NULL)
			{
				recent_activity = true;

				std::string name (msg->GetName());

				msgHandlers::const_iterator it = GetMsgHandlers().find(name);
				if (it != GetMsgHandlers().end())
				{
					it->second(msg);
				}
				else
				{
					debug_warn(L"Unrecognised message");
					// CLogger might not be initialised, but this error will be sent
					// to the debug output window anyway so people can still see it
					LOGERROR(L"Unrecognised message (%hs)", name.c_str());
				}

				if (msg->GetType() == IMessage::Query)
				{
					// For queries, we need to notify MessagePasserImpl::Query
					// that the query has now been processed.
					sem_post((sem_t*) static_cast<QueryMessage*>(msg)->m_Semaphore);
					// (msg may have been destructed at this point, so don't use it again)

					// It's quite possible that the querier is going to do a tiny
					// bit of processing on the query results and then issue another
					// query, and repeat lots of times in a loop. To avoid slowing
					// that down by rendering between every query, make this
					// thread yield now.
					SDL_Delay(0);
				}
				else
				{
					// For non-queries, we need to delete the object, since we
					// took ownership of it.
					AtlasMessage::ShareableDelete(msg);
				}
			}
		}

		// Exit, if desired
		if (! state.running)
			break;

		//////////////////////////////////////////////////////////////////////////

		// Do per-frame processing:

		ReloadChangedFiles();

		RendererIncrementalLoad();

		// Pump SDL events (e.g. hotkeys)
		SDL_Event_ ev;
		while (SDL_PollEvent(&ev.ev))
			in_dispatch_event(&ev);

		if (g_GUI)
			g_GUI->TickObjects();

		state.view->Update(state.realFrameLength);

		state.view->Render();

		if (CProfileManager::IsInitialised())
			g_Profiler.Frame();


		double time = timer_Time();
		if (recent_activity)
			last_activity = time;

		// Be nice to the processor (by sleeping lots) if we're not doing anything
		// useful, and nice to the user (by just yielding to other threads) if we are
		bool yield = (time - last_activity > 0.5);

		// But make sure we aren't doing anything interesting right now, where
		// the user wants to see the screen updating even though they're not
		// interacting with it
		if (state.view->WantsHighFramerate())
			yield = false;

		if (yield) // if there was no recent activity...
		{
			double sleepUntil = time + 0.5; // only redraw at 2fps
			while (time < sleepUntil)
			{
				// To minimise latency when the user starts doing stuff, only
				// sleep for a short while, then check if anything's happened,
				// then go back to sleep
				// (TODO: This should probably be done with something like semaphores)
				Atlas_NotifyEndOfFrame(); // (TODO: rename to NotifyEndOfQuiteShortProcessingPeriodSoPleaseSendMeNewMessages or something)
				SDL_Delay(50);
				if (!msgPasser->IsEmpty())
					break;
				time = timer_Time();
			}
		}
		else
		{
			Atlas_NotifyEndOfFrame();
			SDL_Delay(0);
		}
	}

	return NULL;
}