|
|
@@ -103,7 +103,7 @@ Engine::Engine(Context* context) :
|
|
|
{
|
|
|
// Register self as a subsystem
|
|
|
context_->RegisterSubsystem(this);
|
|
|
-
|
|
|
+
|
|
|
// Create subsystems which do not depend on engine initialization or startup parameters
|
|
|
context_->RegisterSubsystem(new Time(context_));
|
|
|
context_->RegisterSubsystem(new WorkQueue(context_));
|
|
|
@@ -119,12 +119,12 @@ Engine::Engine(Context* context) :
|
|
|
context_->RegisterSubsystem(new Input(context_));
|
|
|
context_->RegisterSubsystem(new Audio(context_));
|
|
|
context_->RegisterSubsystem(new UI(context_));
|
|
|
-
|
|
|
+
|
|
|
// Register object factories for libraries which are not automatically registered along with subsystem creation
|
|
|
RegisterSceneLibrary(context_);
|
|
|
RegisterPhysicsLibrary(context_);
|
|
|
RegisterNavigationLibrary(context_);
|
|
|
-
|
|
|
+
|
|
|
SubscribeToEvent(E_EXITREQUESTED, HANDLER(Engine, HandleExitRequested));
|
|
|
}
|
|
|
|
|
|
@@ -136,12 +136,12 @@ bool Engine::Initialize(const VariantMap& parameters)
|
|
|
{
|
|
|
if (initialized_)
|
|
|
return true;
|
|
|
-
|
|
|
+
|
|
|
PROFILE(InitEngine);
|
|
|
-
|
|
|
+
|
|
|
// Set headless mode
|
|
|
headless_ = GetParameter(parameters, "Headless", false).GetBool();
|
|
|
-
|
|
|
+
|
|
|
// Register the rest of the subsystems
|
|
|
if (!headless_)
|
|
|
{
|
|
|
@@ -153,14 +153,14 @@ bool Engine::Initialize(const VariantMap& parameters)
|
|
|
// Register graphics library objects explicitly in headless mode to allow them to work without using actual GPU resources
|
|
|
RegisterGraphicsLibrary(context_);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// In debug mode, check now that all factory created objects can be created without crashing
|
|
|
#ifdef _DEBUG
|
|
|
const HashMap<ShortStringHash, SharedPtr<ObjectFactory> >& factories = context_->GetObjectFactories();
|
|
|
for (HashMap<ShortStringHash, SharedPtr<ObjectFactory> >::ConstIterator i = factories.Begin(); i != factories.End(); ++i)
|
|
|
SharedPtr<Object> object = i->second_->CreateObject();
|
|
|
#endif
|
|
|
-
|
|
|
+
|
|
|
// Start logging
|
|
|
Log* log = GetSubsystem<Log>();
|
|
|
if (log)
|
|
|
@@ -170,36 +170,36 @@ bool Engine::Initialize(const VariantMap& parameters)
|
|
|
log->SetQuiet(GetParameter(parameters, "LogQuiet", false).GetBool());
|
|
|
log->Open(GetParameter(parameters, "LogName", "Urho3D.log").GetString());
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Set maximally accurate low res timer
|
|
|
GetSubsystem<Time>()->SetTimerPeriod(1);
|
|
|
-
|
|
|
+
|
|
|
// Configure max FPS
|
|
|
if (GetParameter(parameters, "FrameLimiter", true) == false)
|
|
|
SetMaxFps(0);
|
|
|
-
|
|
|
+
|
|
|
// Set amount of worker threads according to the available physical CPU cores. Using also hyperthreaded cores results in
|
|
|
// unpredictable extra synchronization overhead. Also reserve one core for the main thread
|
|
|
unsigned numThreads = GetParameter(parameters, "WorkerThreads", true).GetBool() ? GetNumPhysicalCPUs() - 1 : 0;
|
|
|
if (numThreads)
|
|
|
{
|
|
|
GetSubsystem<WorkQueue>()->CreateThreads(numThreads);
|
|
|
-
|
|
|
+
|
|
|
LOGINFO(ToString("Created %u worker thread%s", numThreads, numThreads > 1 ? "s" : ""));
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Add resource paths
|
|
|
ResourceCache* cache = GetSubsystem<ResourceCache>();
|
|
|
FileSystem* fileSystem = GetSubsystem<FileSystem>();
|
|
|
String exePath = fileSystem->GetProgramDir();
|
|
|
-
|
|
|
+
|
|
|
Vector<String> resourcePaths = GetParameter(parameters, "ResourcePaths", "CoreData;Data").GetString().Split(';');
|
|
|
Vector<String> resourcePackages = GetParameter(parameters, "ResourcePackages").GetString().Split(';');
|
|
|
-
|
|
|
+
|
|
|
for (unsigned i = 0; i < resourcePaths.Size(); ++i)
|
|
|
{
|
|
|
bool success = false;
|
|
|
-
|
|
|
+
|
|
|
// If path is not absolute, prefer to add it as a package if possible
|
|
|
if (!IsAbsolutePath(resourcePaths[i]))
|
|
|
{
|
|
|
@@ -213,7 +213,7 @@ bool Engine::Initialize(const VariantMap& parameters)
|
|
|
success = true;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (!success)
|
|
|
{
|
|
|
String pathName = exePath + resourcePaths[i];
|
|
|
@@ -227,19 +227,19 @@ bool Engine::Initialize(const VariantMap& parameters)
|
|
|
if (fileSystem->DirExists(pathName))
|
|
|
success = cache->AddResourceDir(pathName);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (!success)
|
|
|
{
|
|
|
LOGERROR("Failed to add resource path " + resourcePaths[i]);
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Then add specified packages
|
|
|
for (unsigned i = 0; i < resourcePackages.Size(); ++i)
|
|
|
{
|
|
|
bool success = false;
|
|
|
-
|
|
|
+
|
|
|
String packageName = exePath + resourcePackages[i];
|
|
|
if (fileSystem->FileExists(packageName))
|
|
|
{
|
|
|
@@ -250,21 +250,21 @@ bool Engine::Initialize(const VariantMap& parameters)
|
|
|
success = true;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (!success)
|
|
|
{
|
|
|
LOGERROR("Failed to add resource package " + resourcePackages[i]);
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
// Initialize graphics & audio output
|
|
|
if (!headless_)
|
|
|
{
|
|
|
Graphics* graphics = GetSubsystem<Graphics>();
|
|
|
Renderer* renderer = GetSubsystem<Renderer>();
|
|
|
-
|
|
|
+
|
|
|
if (HasParameter(parameters, "ExternalWindow"))
|
|
|
graphics->SetExternalWindow(GetParameter(parameters, "ExternalWindow").GetPtr());
|
|
|
graphics->SetForceSM2(GetParameter(parameters, "ForceSM2", false).GetBool());
|
|
|
@@ -279,13 +279,13 @@ bool Engine::Initialize(const VariantMap& parameters)
|
|
|
GetParameter(parameters, "MultiSample", 1).GetInt()
|
|
|
))
|
|
|
return false;
|
|
|
-
|
|
|
+
|
|
|
if (HasParameter(parameters, "RenderPath"))
|
|
|
renderer->SetDefaultRenderPath(cache->GetResource<XMLFile>(GetParameter(parameters, "RenderPath").GetString()));
|
|
|
renderer->SetDrawShadows(GetParameter(parameters, "Shadows", true).GetBool());
|
|
|
if (renderer->GetDrawShadows() && GetParameter(parameters, "LowQualityShadows", false).GetBool())
|
|
|
renderer->SetShadowQuality(SHADOWQUALITY_LOW_16BIT);
|
|
|
-
|
|
|
+
|
|
|
if (GetParameter(parameters, "Sound", true).GetBool())
|
|
|
{
|
|
|
GetSubsystem<Audio>()->SetMode(
|
|
|
@@ -296,12 +296,12 @@ bool Engine::Initialize(const VariantMap& parameters)
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Init FPU state of main thread
|
|
|
InitFPU();
|
|
|
-
|
|
|
+
|
|
|
frameTimer_.Reset();
|
|
|
-
|
|
|
+
|
|
|
initialized_ = true;
|
|
|
return true;
|
|
|
}
|
|
|
@@ -309,22 +309,22 @@ bool Engine::Initialize(const VariantMap& parameters)
|
|
|
void Engine::RunFrame()
|
|
|
{
|
|
|
assert(initialized_);
|
|
|
-
|
|
|
+
|
|
|
// If not headless, and the graphics subsystem no longer has a window open, assume we should exit
|
|
|
if (!headless_ && !GetSubsystem<Graphics>()->IsInitialized())
|
|
|
exiting_ = true;
|
|
|
-
|
|
|
+
|
|
|
if (exiting_)
|
|
|
return;
|
|
|
-
|
|
|
+
|
|
|
// Note: there is a minimal performance cost to looking up subsystems (uses a hashmap); if they would be looked up several
|
|
|
// times per frame it would be better to cache the pointers
|
|
|
Time* time = GetSubsystem<Time>();
|
|
|
Input* input = GetSubsystem<Input>();
|
|
|
Audio* audio = GetSubsystem<Audio>();
|
|
|
-
|
|
|
+
|
|
|
time->BeginFrame(timeStep_);
|
|
|
-
|
|
|
+
|
|
|
// If pause when minimized -mode is in use, stop updates and audio as necessary
|
|
|
if (pauseMinimized_ && input->IsMinimized())
|
|
|
{
|
|
|
@@ -342,13 +342,13 @@ void Engine::RunFrame()
|
|
|
audio->Play();
|
|
|
audioPaused_ = false;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
Update();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
Render();
|
|
|
ApplyFrameLimit();
|
|
|
-
|
|
|
+
|
|
|
time->EndFrame();
|
|
|
}
|
|
|
|
|
|
@@ -356,7 +356,7 @@ Console* Engine::CreateConsole()
|
|
|
{
|
|
|
if (headless_ || !initialized_)
|
|
|
return 0;
|
|
|
-
|
|
|
+
|
|
|
// Return existing console if possible
|
|
|
Console* console = GetSubsystem<Console>();
|
|
|
if (!console)
|
|
|
@@ -364,7 +364,7 @@ Console* Engine::CreateConsole()
|
|
|
console = new Console(context_);
|
|
|
context_->RegisterSubsystem(console);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return console;
|
|
|
}
|
|
|
|
|
|
@@ -372,7 +372,7 @@ DebugHud* Engine::CreateDebugHud()
|
|
|
{
|
|
|
if (headless_ || !initialized_)
|
|
|
return 0;
|
|
|
-
|
|
|
+
|
|
|
// Return existing debug HUD if possible
|
|
|
DebugHud* debugHud = GetSubsystem<DebugHud>();
|
|
|
if (!debugHud)
|
|
|
@@ -380,7 +380,7 @@ DebugHud* Engine::CreateDebugHud()
|
|
|
debugHud = new DebugHud(context_);
|
|
|
context_->RegisterSubsystem(debugHud);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return debugHud;
|
|
|
}
|
|
|
|
|
|
@@ -447,20 +447,20 @@ void Engine::DumpResources()
|
|
|
ResourceCache* cache = GetSubsystem<ResourceCache>();
|
|
|
const HashMap<ShortStringHash, ResourceGroup>& resourceGroups = cache->GetAllResources();
|
|
|
LOGRAW("\n");
|
|
|
-
|
|
|
+
|
|
|
for (HashMap<ShortStringHash, ResourceGroup>::ConstIterator i = resourceGroups.Begin();
|
|
|
i != resourceGroups.End(); ++i)
|
|
|
{
|
|
|
unsigned num = i->second_.resources_.Size();
|
|
|
unsigned memoryUse = i->second_.memoryUse_;
|
|
|
-
|
|
|
+
|
|
|
if (num)
|
|
|
{
|
|
|
LOGRAW("Resource type " + i->second_.resources_.Begin()->second_->GetTypeName() +
|
|
|
": count " + String(num) + " memory use " + String(memoryUse) + "\n");
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
LOGRAW("Total memory use of all resources " + String(cache->GetTotalMemoryUse()) + "\n\n");
|
|
|
#endif
|
|
|
}
|
|
|
@@ -474,7 +474,7 @@ void Engine::DumpMemory()
|
|
|
_CrtMemBlockHeader* block = state.pBlockHeader;
|
|
|
unsigned total = 0;
|
|
|
unsigned blocks = 0;
|
|
|
-
|
|
|
+
|
|
|
for (;;)
|
|
|
{
|
|
|
if (block && block->pBlockHeaderNext)
|
|
|
@@ -482,7 +482,7 @@ void Engine::DumpMemory()
|
|
|
else
|
|
|
break;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
while (block)
|
|
|
{
|
|
|
if (block->nBlockUse > 0)
|
|
|
@@ -491,13 +491,13 @@ void Engine::DumpMemory()
|
|
|
LOGRAW("Block " + String((int)block->lRequest) + ": " + String(block->nDataSize) + " bytes, file " + String(block->szFileName) + " line " + String(block->nLine) + "\n");
|
|
|
else
|
|
|
LOGRAW("Block " + String((int)block->lRequest) + ": " + String(block->nDataSize) + " bytes\n");
|
|
|
-
|
|
|
+
|
|
|
total += block->nDataSize;
|
|
|
++blocks;
|
|
|
}
|
|
|
block = block->pBlockHeaderPrev;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
LOGRAW("Total allocated memory " + String(total) + " bytes in " + String(blocks) + " blocks\n\n");
|
|
|
#else
|
|
|
LOGRAW("DumpMemory() supported on MSVC debug mode only\n\n");
|
|
|
@@ -508,20 +508,20 @@ void Engine::DumpMemory()
|
|
|
void Engine::Update()
|
|
|
{
|
|
|
PROFILE(Update);
|
|
|
-
|
|
|
+
|
|
|
// Logic update event
|
|
|
using namespace Update;
|
|
|
-
|
|
|
+
|
|
|
VariantMap eventData;
|
|
|
eventData[P_TIMESTEP] = timeStep_;
|
|
|
SendEvent(E_UPDATE, eventData);
|
|
|
-
|
|
|
+
|
|
|
// Logic post-update event
|
|
|
SendEvent(E_POSTUPDATE, eventData);
|
|
|
-
|
|
|
+
|
|
|
// Rendering update event
|
|
|
SendEvent(E_RENDERUPDATE, eventData);
|
|
|
-
|
|
|
+
|
|
|
// Post-render update event
|
|
|
SendEvent(E_POSTRENDERUPDATE, eventData);
|
|
|
}
|
|
|
@@ -530,14 +530,14 @@ void Engine::Render()
|
|
|
{
|
|
|
if (headless_)
|
|
|
return;
|
|
|
-
|
|
|
+
|
|
|
PROFILE(Render);
|
|
|
-
|
|
|
+
|
|
|
// If device is lost, BeginFrame will fail and we skip rendering
|
|
|
Graphics* graphics = GetSubsystem<Graphics>();
|
|
|
if (!graphics->BeginFrame())
|
|
|
return;
|
|
|
-
|
|
|
+
|
|
|
GetSubsystem<Renderer>()->Render();
|
|
|
GetSubsystem<UI>()->Render();
|
|
|
graphics->EndFrame();
|
|
|
@@ -547,27 +547,27 @@ void Engine::ApplyFrameLimit()
|
|
|
{
|
|
|
if (!initialized_)
|
|
|
return;
|
|
|
-
|
|
|
+
|
|
|
int maxFps = maxFps_;
|
|
|
Input* input = GetSubsystem<Input>();
|
|
|
if (input && !input->HasFocus())
|
|
|
maxFps = Min(maxInactiveFps_, maxFps);
|
|
|
-
|
|
|
+
|
|
|
long long elapsed = 0;
|
|
|
-
|
|
|
+
|
|
|
// Perform waiting loop if maximum FPS set
|
|
|
if (maxFps)
|
|
|
{
|
|
|
PROFILE(ApplyFrameLimit);
|
|
|
-
|
|
|
+
|
|
|
long long targetMax = 1000000LL / maxFps;
|
|
|
-
|
|
|
+
|
|
|
for (;;)
|
|
|
{
|
|
|
elapsed = frameTimer_.GetUSec(false);
|
|
|
if (elapsed >= targetMax)
|
|
|
break;
|
|
|
-
|
|
|
+
|
|
|
// Sleep if 1 ms or more off the frame limiting goal
|
|
|
if (targetMax - elapsed >= 1000LL)
|
|
|
{
|
|
|
@@ -576,9 +576,9 @@ void Engine::ApplyFrameLimit()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
elapsed = frameTimer_.GetUSec(true);
|
|
|
-
|
|
|
+
|
|
|
// If FPS lower than minimum, clamp elapsed time
|
|
|
if (minFps_)
|
|
|
{
|
|
|
@@ -586,7 +586,7 @@ void Engine::ApplyFrameLimit()
|
|
|
if (elapsed > targetMin)
|
|
|
elapsed = targetMin;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Perform timestep smoothing
|
|
|
timeStep_ = 0.0f;
|
|
|
lastTimeSteps_.Push(elapsed / 1000000.0f);
|
|
|
@@ -605,23 +605,16 @@ void Engine::ApplyFrameLimit()
|
|
|
VariantMap Engine::ParseParameters(const Vector<String>& arguments)
|
|
|
{
|
|
|
VariantMap ret;
|
|
|
-
|
|
|
+
|
|
|
for (unsigned i = 0; i < arguments.Size(); ++i)
|
|
|
{
|
|
|
if (arguments[i].Length() > 1 && arguments[i][0] == '-')
|
|
|
{
|
|
|
String argument = arguments[i].Substring(1).ToLower();
|
|
|
String value = i + 1 < arguments.Size() ? arguments[i + 1] : String::EMPTY;
|
|
|
-
|
|
|
+
|
|
|
if (argument == "headless")
|
|
|
ret["Headless"] = true;
|
|
|
- else if (argument.Substring(0, 3) == "log")
|
|
|
- {
|
|
|
- argument = argument.Substring(3);
|
|
|
- int logLevel = GetStringListIndex(argument.CString(), logLevelPrefixes, -1);
|
|
|
- if (logLevel != -1)
|
|
|
- ret["LogLevel"] = logLevel;
|
|
|
- }
|
|
|
else if (argument == "nolimit")
|
|
|
ret["FrameLimiter"] = false;
|
|
|
else if (argument == "nosound")
|
|
|
@@ -652,6 +645,15 @@ VariantMap Engine::ParseParameters(const Vector<String>& arguments)
|
|
|
ret["WindowResizable"] = true;
|
|
|
else if (argument == "q")
|
|
|
ret["LogQuiet"] = true;
|
|
|
+ else if (argument == "log" && !value.Empty())
|
|
|
+ {
|
|
|
+ int logLevel = GetStringListIndex(value.CString(), logLevelPrefixes, -1);
|
|
|
+ if (logLevel != -1)
|
|
|
+ {
|
|
|
+ ret["LogLevel"] = logLevel;
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ }
|
|
|
else if (argument == "x" && !value.Empty())
|
|
|
{
|
|
|
ret["WindowWidth"] = ToInt(value);
|
|
|
@@ -684,7 +686,7 @@ VariantMap Engine::ParseParameters(const Vector<String>& arguments)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
@@ -716,7 +718,7 @@ void Engine::DoExit()
|
|
|
Graphics* graphics = GetSubsystem<Graphics>();
|
|
|
if (graphics)
|
|
|
graphics->Close();
|
|
|
-
|
|
|
+
|
|
|
exiting_ = true;
|
|
|
}
|
|
|
|