Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation2/src/libraries/core/Core.cc @ 6384

Last change on this file since 6384 was 6384, checked in by rgrieder, 14 years ago

Playing Orxonox on a specific core makes it faster on Windows.
The problem is in fact that the timer NEEDS to be queried on the same core in order to avoid offsets. But if the querying thread is on the wrong core, the thread has to be rescheduled and that takes time. Considering that this happens quite often (Windows tries to play it on both cores), the performance penalty is serious (9% in may case).
I imagine that this issue is even more serious on a quad core.

  • Property svn:eol-style set to native
File size: 12.2 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *      Reto Grieder
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30/**
31@file
32@brief
33    Implementation of the Core singleton with its global variables (avoids boost include)
34*/
35
36#include "Core.h"
37
38#include <cassert>
39#include <vector>
40
41#ifdef ORXONOX_PLATFORM_WINDOWS
42#  ifndef WIN32_LEAN_AND_MEAN
43#    define WIN32_LEAN_AND_MEAN
44#  endif
45#  include <windows.h>
46#  undef min
47#  undef max
48#endif
49
50#include "util/Clock.h"
51#include "util/Debug.h"
52#include "util/Exception.h"
53#include "util/Scope.h"
54#include "util/SignalHandler.h"
55#include "PathConfig.h"
56#include "CommandExecutor.h"
57#include "CommandLineParser.h"
58#include "ConfigFileManager.h"
59#include "ConfigValueIncludes.h"
60#include "CoreIncludes.h"
61#include "DynLibManager.h"
62#include "GameMode.h"
63#include "GraphicsManager.h"
64#include "GUIManager.h"
65#include "Identifier.h"
66#include "Language.h"
67#include "IOConsole.h"
68#include "LuaState.h"
69#include "ScopedSingletonManager.h"
70#include "TclBind.h"
71#include "TclThreadManager.h"
72#include "input/InputManager.h"
73
74namespace orxonox
75{
76    //! Static pointer to the singleton
77    Core* Core::singletonPtr_s  = 0;
78
79    SetCommandLineArgument(settingsFile, "orxonox.ini").information("THE configuration file");
80#ifdef ORXONOX_PLATFORM_WINDOWS
81    SetCommandLineArgument(limitToCPU, 1).information("Limits the program to one CPU/core (1, 2, 3, etc.). Default is the first core (faster than off)");
82#endif
83
84    Core::Core(const std::string& cmdLine)
85        // Cleanup guard for identifier destruction (incl. XMLPort, configValues, consoleCommands)
86        : identifierDestroyer_(Identifier::destroyAllIdentifiers)
87        // Cleanup guard for external console commands that don't belong to an Identifier
88        , consoleCommandDestroyer_(CommandExecutor::destroyExternalCommands)
89        , bGraphicsLoaded_(false)
90    {
91        // Set the hard coded fixed paths
92        this->pathConfig_.reset(new PathConfig());
93
94        // Create a new dynamic library manager
95        this->dynLibManager_.reset(new DynLibManager());
96
97        // Load modules
98        const std::vector<std::string>& modulePaths = this->pathConfig_->getModulePaths();
99        for (std::vector<std::string>::const_iterator it = modulePaths.begin(); it != modulePaths.end(); ++it)
100        {
101            try
102            {
103                this->dynLibManager_->load(*it);
104            }
105            catch (...)
106            {
107                COUT(1) << "Couldn't load module \"" << *it << "\": " << Exception::handleMessage() << std::endl;
108            }
109        }
110
111        // Parse command line arguments AFTER the modules have been loaded (static code!)
112        CommandLineParser::parseCommandLine(cmdLine);
113
114        // Set configurable paths like log, config and media
115        this->pathConfig_->setConfigurablePaths();
116
117        // create a signal handler (only active for Linux)
118        // This call is placed as soon as possible, but after the directories are set
119        this->signalHandler_.reset(new SignalHandler());
120        this->signalHandler_->doCatch(PathConfig::getExecutablePathString(), PathConfig::getLogPathString() + "orxonox_crash.log");
121
122        // Set the correct log path. Before this call, /tmp (Unix) or %TEMP% (Windows) was used
123        OutputHandler::getInstance().setLogPath(PathConfig::getLogPathString());
124
125        // Parse additional options file now that we know its path
126        CommandLineParser::parseFile();
127
128#ifdef ORXONOX_PLATFORM_WINDOWS
129        // limit the main thread to the first core so that QueryPerformanceCounter doesn't jump
130        // do this after ogre has initialised. Somehow Ogre changes the settings again (not through
131        // the timer though).
132        int limitToCPU = CommandLineParser::getValue("limitToCPU");
133        if (limitToCPU > 0)
134            setThreadAffinity(static_cast<unsigned int>(limitToCPU));
135#endif
136
137        // Manage ini files and set the default settings file (usually orxonox.ini)
138        this->configFileManager_.reset(new ConfigFileManager());
139        this->configFileManager_->setFilename(ConfigFileType::Settings,
140            CommandLineParser::getValue("settingsFile").getString());
141
142        // Required as well for the config values
143        this->languageInstance_.reset(new Language());
144
145        // Do this soon after the ConfigFileManager has been created to open up the
146        // possibility to configure everything below here
147        ClassIdentifier<Core>::getIdentifier("Core")->initialiseObject(this, "Core", true);
148        this->setConfigValues();
149
150        // create persistent io console
151        this->ioConsole_.reset(new IOConsole());
152
153        // creates the class hierarchy for all classes with factories
154        Identifier::createClassHierarchy();
155
156        // Load OGRE excluding the renderer and the render window
157        this->graphicsManager_.reset(new GraphicsManager(false));
158
159        // initialise Tcl
160        this->tclBind_.reset(new TclBind(PathConfig::getDataPathString()));
161        this->tclThreadManager_.reset(new TclThreadManager(tclBind_->getTclInterpreter()));
162
163        // Create singletons that always exist (in other libraries)
164        this->rootScope_.reset(new Scope<ScopeID::Root>());
165    }
166
167    /**
168    @brief
169        All destruction code is handled by scoped_ptrs and ScopeGuards.
170    */
171    Core::~Core()
172    {
173        // Remove us from the object lists again to avoid problems when destroying them
174        this->unregisterObject();
175    }
176
177    //! Function to collect the SetConfigValue-macro calls.
178    void Core::setConfigValues()
179    {
180#ifdef ORXONOX_RELEASE
181        const unsigned int defaultLevelLogFile = 3;
182#else
183        const unsigned int defaultLevelLogFile = 4;
184#endif
185        setConfigValueGeneric(this, &this->softDebugLevelLogFile_, ConfigFileType::Settings, "OutputHandler", "softDebugLevelLogFile", defaultLevelLogFile)
186            .description("The maximum level of debug output shown in the log file");
187        OutputHandler::getInstance().setSoftDebugLevel(OutputHandler::logFileOutputListenerName_s, this->softDebugLevelLogFile_);
188
189        SetConfigValue(language_, Language::getInstance().defaultLanguage_)
190            .description("The language of the in game text")
191            .callback(this, &Core::languageChanged);
192        SetConfigValue(bInitRandomNumberGenerator_, true)
193            .description("If true, all random actions are different each time you start the game")
194            .callback(this, &Core::initRandomNumberGenerator);
195    }
196
197    //! Callback function if the language has changed.
198    void Core::languageChanged()
199    {
200        // Read the translation file after the language was configured
201        Language::getInstance().readTranslatedLanguageFile();
202    }
203
204    void Core::initRandomNumberGenerator()
205    {
206        static bool bInitialized = false;
207        if (!bInitialized && this->bInitRandomNumberGenerator_)
208        {
209            srand(static_cast<unsigned int>(time(0)));
210            rand();
211            bInitialized = true;
212        }
213    }
214
215    void Core::loadGraphics()
216    {
217        // Any exception should trigger this, even in upgradeToGraphics (see its remarks)
218        Loki::ScopeGuard unloader = Loki::MakeObjGuard(*this, &Core::unloadGraphics);
219
220        // Upgrade OGRE to receive a render window
221        graphicsManager_->upgradeToGraphics();
222
223        // Calls the InputManager which sets up the input devices.
224        inputManager_.reset(new InputManager());
225
226        // Load the CEGUI interface
227        guiManager_.reset(new GUIManager(graphicsManager_->getRenderWindow(),
228            inputManager_->getMousePosition(), graphicsManager_->isFullScreen()));
229
230        bGraphicsLoaded_ = true;
231        GameMode::bShowsGraphics_s = true;
232
233        // Load some sort of a debug overlay (only denoted by its name, "debug.oxo")
234        graphicsManager_->loadDebugOverlay();
235
236        // Create singletons associated with graphics (in other libraries)
237        graphicsScope_.reset(new Scope<ScopeID::Graphics>());
238
239        unloader.Dismiss();
240    }
241
242    void Core::unloadGraphics()
243    {
244        this->graphicsScope_.reset();
245        this->guiManager_.reset();
246        this->inputManager_.reset();
247        this->graphicsManager_.reset();
248
249        // Load Ogre::Root again, but without the render system
250        try
251            { this->graphicsManager_.reset(new GraphicsManager(false)); }
252        catch (...)
253        {
254            COUT(0) << "An exception occurred during 'unloadGraphics':" << Exception::handleMessage() << std::endl
255                    << "Another exception might be being handled which may lead to undefined behaviour!" << std::endl
256                    << "Terminating the program." << std::endl;
257            abort();
258        }
259
260        bGraphicsLoaded_ = false;
261        GameMode::bShowsGraphics_s = false;
262    }
263
264    //! Sets the language in the config-file back to the default.
265    void Core::resetLanguage()
266    {
267        ResetConfigValue(language_);
268    }
269
270    /**
271    @note
272        The code of this function has been copied and adjusted from OGRE, an open source graphics engine.
273            (Object-oriented Graphics Rendering Engine)
274        For the latest info, see http://www.ogre3d.org/
275
276        Copyright (c) 2000-2008 Torus Knot Software Ltd
277
278        OGRE is licensed under the LGPL. For more info, see OGRE license.
279    */
280    void Core::setThreadAffinity(int limitToCPU)
281    {
282#ifdef ORXONOX_PLATFORM_WINDOWS
283
284        if (limitToCPU <= 0)
285            return;
286
287        unsigned int coreNr = limitToCPU - 1;
288        // Get the current process core mask
289        DWORD procMask;
290        DWORD sysMask;
291#  if _MSC_VER >= 1400 && defined (_M_X64)
292        GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)&procMask, (PDWORD_PTR)&sysMask);
293#  else
294        GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
295#  endif
296
297        // If procMask is 0, consider there is only one core available
298        // (using 0 as procMask will cause an infinite loop below)
299        if (procMask == 0)
300            procMask = 1;
301
302        // if the core specified with coreNr is not available, take the lowest one
303        if (!(procMask & (1 << coreNr)))
304            coreNr = 0;
305
306        // Find the lowest core that this process uses and coreNr suggests
307        DWORD threadMask = 1;
308        while ((threadMask & procMask) == 0 || (threadMask < (1u << coreNr)))
309            threadMask <<= 1;
310
311        // Set affinity to the first core
312        SetThreadAffinityMask(GetCurrentThread(), threadMask);
313#endif
314    }
315
316    void Core::preUpdate(const Clock& time)
317    {
318        // Update singletons before general ticking
319        ScopedSingletonManager::preUpdate<ScopeID::Root>(time);
320        if (this->bGraphicsLoaded_)
321        {
322            // Process input events
323            this->inputManager_->preUpdate(time);
324            // Update GUI
325            this->guiManager_->preUpdate(time);
326            // Update singletons before general ticking
327            ScopedSingletonManager::preUpdate<ScopeID::Graphics>(time);
328        }
329        // Process console events and status line
330        this->ioConsole_->preUpdate(time);
331        // Process thread commands
332        this->tclThreadManager_->preUpdate(time);
333    }
334
335    void Core::postUpdate(const Clock& time)
336    {
337        // Update singletons just before rendering
338        ScopedSingletonManager::postUpdate<ScopeID::Root>(time);
339        if (this->bGraphicsLoaded_)
340        {
341            // Update singletons just before rendering
342            ScopedSingletonManager::postUpdate<ScopeID::Graphics>(time);
343            // Render (doesn't throw)
344            this->graphicsManager_->postUpdate(time);
345        }
346    }
347}
Note: See TracBrowser for help on using the repository browser.