Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,862 @@
|
||||
#include "main/tick.h"
|
||||
#include "main/input_handling.h"
|
||||
#include "main/references.h"
|
||||
#include "main/logging.h"
|
||||
#include "processing.h"
|
||||
#include "obj/object.h"
|
||||
#include "scene/animation/anim_node_sync.h"
|
||||
#include "main/console.h"
|
||||
#include "main/initialization.h"
|
||||
#include "network/network_manager.h"
|
||||
#include "ISoundDevice.h"
|
||||
#include "files.h"
|
||||
#include "design/projectiles.h"
|
||||
#include "render/vertexBuffer.h"
|
||||
#include "render/gl_framebuffer.h"
|
||||
#include "save_load.h"
|
||||
#include <cmath>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern unsigned drawnSteps, bufferFlushes;
|
||||
|
||||
GameState game_state = GS_Menu;
|
||||
std::string game_locale;
|
||||
bool game_running = false;
|
||||
bool reload_gui = false;
|
||||
|
||||
bool fullGC = false;
|
||||
|
||||
threads::Mutex texDestroyLock;
|
||||
std::vector<const render::Texture*> queuedDestroyTextures;
|
||||
void queueDestroyTexture(const render::Texture* tex) {
|
||||
threads::Lock lock(texDestroyLock);
|
||||
queuedDestroyTextures.push_back(tex);
|
||||
}
|
||||
|
||||
void destroyQueuedTextures() {
|
||||
if(queuedDestroyTextures.empty())
|
||||
return;
|
||||
|
||||
threads::Lock lock(texDestroyLock);
|
||||
for(unsigned i = 0; i < queuedDestroyTextures.size(); ++i)
|
||||
delete queuedDestroyTextures[i];
|
||||
queuedDestroyTextures.clear();
|
||||
}
|
||||
|
||||
const render::Shader* fsShader = nullptr;
|
||||
bool hide_ui = false;
|
||||
double ui_scale = 1.0;
|
||||
double scale_3d = 1.0;
|
||||
double pixelSizeRatio = 1.0;
|
||||
double* maxfps = nullptr;
|
||||
|
||||
//Seconds since last autosave
|
||||
double autosaveTimer = 0.0;
|
||||
extern double autosaveInterval;
|
||||
|
||||
threads::Signal scriptTickSignal;
|
||||
|
||||
extern bool queuedModSwitch;
|
||||
extern std::vector<std::string> modSetup;
|
||||
|
||||
namespace scripts {
|
||||
void logException();
|
||||
};
|
||||
|
||||
class RunScriptTick : public processing::Action {
|
||||
double time;
|
||||
double& logTime;
|
||||
bool gcLock;
|
||||
scripts::Manager* manager;
|
||||
public:
|
||||
RunScriptTick(scripts::Manager* Manager, double Time, bool Gc, double& LogTime)
|
||||
: time(Time), gcLock(Gc), manager(Manager), logTime(LogTime) {
|
||||
}
|
||||
|
||||
bool run() {
|
||||
auto prevSection = enterSection(NS_ScriptTick);
|
||||
double start = devices.driver->getAccurateTime();
|
||||
#ifndef _DEBUG
|
||||
try {
|
||||
#endif
|
||||
#ifdef TRACE_GC_LOCK
|
||||
manager->markGCImpossible();
|
||||
#endif
|
||||
manager->tick(time);
|
||||
devices.network->managerNetworking(manager, time);
|
||||
scriptTickSignal.signalDown();
|
||||
#ifdef TRACE_GC_LOCK
|
||||
manager->markGCPossible();
|
||||
#endif
|
||||
#ifndef _DEBUG
|
||||
}
|
||||
catch(...) {
|
||||
scripts::logException();
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
logTime = devices.driver->getAccurateTime() - start;
|
||||
enterSection(prevSection);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class RunScriptGC : public processing::Action {
|
||||
scripts::Manager* manager;
|
||||
bool full;
|
||||
public:
|
||||
RunScriptGC(scripts::Manager* Manager, bool FullCycle = false)
|
||||
: manager(Manager), full(FullCycle) {
|
||||
}
|
||||
|
||||
bool run() {
|
||||
double start = devices.driver->getAccurateTime();
|
||||
manager->pauseScriptThreads();
|
||||
double gcStart = devices.driver->getAccurateTime();
|
||||
int gcMode = manager->garbageCollect(full);
|
||||
double end = devices.driver->getAccurateTime();
|
||||
manager->resumeScriptThreads();
|
||||
|
||||
#ifdef PROFILE_PROCESSING
|
||||
if(end - start > 0.016) {
|
||||
const char* name = "Server";
|
||||
if(manager == devices.scripts.client)
|
||||
name = "Client";
|
||||
else if(manager == devices.scripts.menu)
|
||||
name = "Menu";
|
||||
error("%s GC took %dms (Mode %d)", name, (int)((end - gcStart) * 1.0e3), gcMode);
|
||||
if(gcStart - start > 0.01)
|
||||
error("%s script pausing took %dms", name, (int)((gcStart - start) * 1.0e3));
|
||||
}
|
||||
#endif
|
||||
|
||||
scriptTickSignal.signalDown();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
unsigned animationThreadCount = 6;
|
||||
threads::Signal animationSignal, activeAnimCount;
|
||||
threads::Mutex animDataLock;
|
||||
double nearestNode = 9.0e35, furthestNode = 0.0;
|
||||
double animation_s = 0.0, render_s = 0.0, present_s = 0.0;
|
||||
threads::atomic_int animIndex(-1), animProcessed;
|
||||
|
||||
//Animates some nodes, returning how long it processed
|
||||
double animateSomeNodes(int maxNodes) {
|
||||
if(animIndex < 0)
|
||||
return 0.0;
|
||||
|
||||
double furthest = 0.0;
|
||||
double start = devices.driver->getAccurateTime();
|
||||
|
||||
auto* nodes = &devices.scene->children.front();
|
||||
int count = (int)devices.scene->children.size();
|
||||
int animCount = count/(animationThreadCount*16);
|
||||
if(animCount == 0)
|
||||
animCount = 1;
|
||||
|
||||
int index = (animIndex -= animCount);
|
||||
while(index >= 0) {
|
||||
int animated = 0;
|
||||
for(int i = index; i > index - animCount && i >= 0; --i) {
|
||||
auto* node = nodes[i];
|
||||
node->animate();
|
||||
++animated;
|
||||
|
||||
double d = node->sortDistance + node->abs_scale;
|
||||
if(d > furthest)
|
||||
furthest = d;
|
||||
}
|
||||
|
||||
if(animated != 0)
|
||||
animProcessed += animated;
|
||||
|
||||
maxNodes -= animated;
|
||||
if(maxNodes < 0)
|
||||
break;
|
||||
|
||||
index = (animIndex -= animCount);
|
||||
}
|
||||
|
||||
animDataLock.lock();
|
||||
//if(nearest < nearestNode)
|
||||
// nearestNode = nearest;
|
||||
if(furthest > furthestNode)
|
||||
furthestNode = furthest;
|
||||
animDataLock.release();
|
||||
|
||||
return devices.driver->getAccurateTime() - start;
|
||||
}
|
||||
|
||||
volatile bool EndAnimationThreads = false;
|
||||
threads::threadreturn threadcall animateNodes(void* arg) {
|
||||
enterSection(NS_Animation);
|
||||
double& logTime = *(double*)arg;
|
||||
initNewThread();
|
||||
activeAnimCount.signalUp();
|
||||
//threads::setThreadPriority(threads::TP_High);
|
||||
|
||||
#ifdef TRACE_GC_LOCK
|
||||
devices.scripts.menu->markGCImpossible();
|
||||
devices.scripts.client->markGCImpossible();
|
||||
#endif
|
||||
while(true) {
|
||||
while(animIndex < 0 && !EndAnimationThreads)
|
||||
threads::sleep(1);
|
||||
if(EndAnimationThreads)
|
||||
break;
|
||||
|
||||
animationSignal.signalUp();
|
||||
|
||||
double /*nearest = 9.0e35,*/ furthest = 0.0;
|
||||
double start = devices.driver->getAccurateTime();
|
||||
|
||||
if(!devices.scene->children.empty()) {
|
||||
auto* nodes = &devices.scene->children.front();
|
||||
int animCount = (int)devices.scene->children.size()/(animationThreadCount*16);
|
||||
if(animCount == 0)
|
||||
animCount = 1;
|
||||
|
||||
int index = (animIndex -= animCount);
|
||||
while(index >= 0) {
|
||||
int animated = 0;
|
||||
for(int i = index; i > index - animCount && i >= 0; --i) {
|
||||
auto* node = nodes[i];
|
||||
node->animate();
|
||||
++animated;
|
||||
|
||||
double d = node->sortDistance + node->abs_scale;
|
||||
if(d > furthest)
|
||||
furthest = d;
|
||||
}
|
||||
|
||||
if(animated != 0)
|
||||
animProcessed += animated;
|
||||
|
||||
index = (animIndex -= animCount);
|
||||
}
|
||||
}
|
||||
|
||||
logTime = devices.driver->getAccurateTime() - start;
|
||||
|
||||
animDataLock.lock();
|
||||
//if(nearest < nearestNode)
|
||||
// nearestNode = nearest;
|
||||
if(furthest > furthestNode)
|
||||
furthestNode = furthest;
|
||||
animDataLock.release();
|
||||
|
||||
animationSignal.signalDown();
|
||||
threads::sleep(1);
|
||||
}
|
||||
#ifdef TRACE_GC_LOCK
|
||||
devices.scripts.menu->markGCPossible();
|
||||
devices.scripts.client->markGCPossible();
|
||||
#endif
|
||||
|
||||
activeAnimCount.signalDown();
|
||||
cleanupThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
double frameLen_s = 0, frameTime_s = 0;
|
||||
double realFrameLen = 0, realFrameTime;
|
||||
int frameTime_ms = 0, frameLen_ms = 0;
|
||||
unsigned frameNumber = 0;
|
||||
|
||||
std::vector<double> frames;
|
||||
unsigned max_frames = 120;
|
||||
|
||||
void shader_gameTime(float* pFloats,unsigned short n,void*) {
|
||||
do {
|
||||
*pFloats = (float)frameTime_s;
|
||||
++pFloats;
|
||||
} while(--n);
|
||||
}
|
||||
|
||||
void shader_frameTime(float* pFloats,unsigned short n,void*) {
|
||||
do {
|
||||
*pFloats = (float)realFrameTime;
|
||||
++pFloats;
|
||||
} while(--n);
|
||||
}
|
||||
|
||||
void shader_gameTime_cycle(float* pFloats,unsigned short n,void* pArgs) {
|
||||
float* period = (float*)pArgs;
|
||||
do {
|
||||
*pFloats = (float)(std::fmod(frameTime_s, double(*period)) / double(*period));
|
||||
++period; ++pFloats;
|
||||
} while(--n);
|
||||
}
|
||||
|
||||
void shader_frameTime_cycle(float* pFloats,unsigned short n,void* pArgs) {
|
||||
float* period = (float*)pArgs;
|
||||
do {
|
||||
*pFloats = (float)(std::fmod(realFrameTime, double(*period)) / double(*period));
|
||||
++period; ++pFloats;
|
||||
} while(--n);
|
||||
}
|
||||
|
||||
void shader_gameTime_cycle_abs(float* pFloats,unsigned short n,void* pArgs) {
|
||||
float* period = (float*)pArgs;
|
||||
do {
|
||||
*pFloats = (float)std::fmod(frameTime_s, double(*period));
|
||||
++period; ++pFloats;
|
||||
} while(--n);
|
||||
}
|
||||
|
||||
void shader_frameTime_cycle_abs(float* pFloats,unsigned short n,void* pArgs) {
|
||||
float* period = (float*)pArgs;
|
||||
do {
|
||||
*pFloats = (float)std::fmod(realFrameTime, double(*period));
|
||||
++period; ++pFloats;
|
||||
} while(--n);
|
||||
}
|
||||
|
||||
void shader_pixelRatio(float* pFloats,unsigned short n,void* pArgs) {
|
||||
*pFloats = (float)pixelSizeRatio;
|
||||
}
|
||||
|
||||
#ifdef PROFILE_LOCKS
|
||||
double lockProfileTimer = 0.0;
|
||||
bool printLockProfile = false;
|
||||
bool requireObserved = false;
|
||||
#endif
|
||||
|
||||
double menuTickTime = 0, serverTickTime = 0, clientTickTime = 0, animationTime = 0;
|
||||
double* animTimes = nullptr;
|
||||
|
||||
extern double lastLockGlobalUpdate, nextTargetUpdateTime, nextScriptGCTime;
|
||||
|
||||
void resetGameTime() {
|
||||
nextTargetUpdateTime = lastLockGlobalUpdate = devices.driver->getGameTime();
|
||||
frameTime_s = devices.driver->getGameTime() - 0.25;
|
||||
nextScriptGCTime = devices.driver->getFrameTime();
|
||||
}
|
||||
|
||||
void endAnimation() {
|
||||
if(animTimes) {
|
||||
EndAnimationThreads = true;
|
||||
activeAnimCount.wait(0);
|
||||
EndAnimationThreads = false;
|
||||
delete[] animTimes;
|
||||
animTimes = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void idleLoadResources(int maxPriority) {
|
||||
if(devices.library.processTextures(maxPriority, true))
|
||||
return;
|
||||
if(devices.library.processMeshes(maxPriority, 1))
|
||||
return;
|
||||
if(devices.library.processTextures(INT_MIN, true))
|
||||
return;
|
||||
if(devices.library.processMeshes(INT_MIN, 1))
|
||||
return;
|
||||
}
|
||||
|
||||
GameState prev_state = GS_Menu;
|
||||
void tickGlobal(bool hasScripts) {
|
||||
#ifdef TRACE_GC_LOCK
|
||||
devices.scripts.menu->markGCImpossible();
|
||||
devices.scripts.client->markGCImpossible();
|
||||
#endif
|
||||
|
||||
//Get frame timings
|
||||
static int lastRender = 0;
|
||||
|
||||
if(queuedModSwitch) {
|
||||
queuedModSwitch = false;
|
||||
destroyMod();
|
||||
initMods(modSetup);
|
||||
game_state = GS_Menu;
|
||||
}
|
||||
|
||||
if(game_state != prev_state) {
|
||||
if(hasScripts) {
|
||||
if(game_running && devices.scripts.client)
|
||||
devices.scripts.client->stateChange();
|
||||
if(devices.scripts.menu)
|
||||
devices.scripts.menu->stateChange();
|
||||
}
|
||||
prev_state = game_state;
|
||||
}
|
||||
|
||||
++frameNumber;
|
||||
|
||||
double frame_time = devices.driver->getGameTime() - 0.25;
|
||||
frameTime_ms = devices.driver->getTime();
|
||||
|
||||
double real_frame = devices.driver->getFrameTime();
|
||||
realFrameLen = real_frame - realFrameTime;
|
||||
realFrameTime = real_frame;
|
||||
|
||||
int frame_ms = frameTime_ms - lastRender;
|
||||
lastRender = frameTime_ms;
|
||||
|
||||
double frame_s = frame_time - frameTime_s;
|
||||
frameTime_s = frame_time;
|
||||
|
||||
if(frame_ms > 250 || frame_s > 0.25) {
|
||||
frame_ms = 250;
|
||||
frame_s = 0.25;
|
||||
}
|
||||
else if(frame_ms < 0 || frame_s < 0.0) {
|
||||
frame_ms = 0;
|
||||
frame_s = 0.0;
|
||||
}
|
||||
|
||||
frameLen_s = frame_s;
|
||||
frameLen_ms = frame_ms;
|
||||
|
||||
frames.push_back(realFrameLen);
|
||||
if(frames.size() > max_frames)
|
||||
frames.erase(frames.begin());
|
||||
|
||||
//Run network manager tick
|
||||
devices.network->tick(realFrameLen);
|
||||
|
||||
//Do lock profiling
|
||||
#ifdef PROFILE_LOCKS
|
||||
lockProfileTimer += frame_s;
|
||||
if(lockProfileTimer >= 1.0) {
|
||||
lockProfileTimer = 0.0;
|
||||
|
||||
if(printLockProfile) {
|
||||
threads::profileMutexCycle([](threads::Mutex* mtx) {
|
||||
if((mtx->observed || !requireObserved) && mtx->profileCount > 0) {
|
||||
print("%s: %d locks", mtx->name.c_str(), mtx->profileCount);
|
||||
}
|
||||
});
|
||||
threads::profileReadWriteMutexCycle([](threads::ReadWriteMutex* mtx) {
|
||||
if((mtx->observed || !requireObserved) && (mtx->profileReadCount > 0 || mtx->profileWriteCount > 0)) {
|
||||
print("%s: %d read, %d write", mtx->name.c_str(), mtx->profileReadCount, mtx->profileWriteCount);
|
||||
}
|
||||
});
|
||||
|
||||
printLockProfile = false;
|
||||
}
|
||||
else {
|
||||
threads::profileMutexCycle(0);
|
||||
threads::profileReadWriteMutexCycle(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
idleLoadResources(isPreloading() ? -10 : INT_MIN);
|
||||
|
||||
//Do script GCs
|
||||
#ifdef TRACE_GC_LOCK
|
||||
devices.scripts.menu->markGCPossible();
|
||||
devices.scripts.client->markGCPossible();
|
||||
#endif
|
||||
|
||||
if(hasScripts) {
|
||||
scriptTickSignal.signal(1);
|
||||
processing::queueAction(new RunScriptGC(devices.scripts.menu, fullGC));
|
||||
|
||||
if(game_running) {
|
||||
scriptTickSignal.signalUp(1);
|
||||
processing::queueAction(new RunScriptGC(devices.scripts.client, fullGC));
|
||||
}
|
||||
|
||||
fullGC = false;
|
||||
|
||||
while(!scriptTickSignal.check(0)) {
|
||||
processing::run();
|
||||
threads::sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TRACE_GC_LOCK
|
||||
devices.scripts.menu->markGCImpossible();
|
||||
devices.scripts.client->markGCImpossible();
|
||||
#endif
|
||||
|
||||
//Run all the script ticks
|
||||
if(hasScripts) {
|
||||
scriptTickSignal.signal(1);
|
||||
processing::queueAction(new RunScriptTick(devices.scripts.menu, realFrameLen, false, menuTickTime));
|
||||
|
||||
if(game_running) {
|
||||
scriptTickSignal.signalUp(2);
|
||||
processing::queueAction(new RunScriptTick(devices.scripts.server, frameLen_s, true, serverTickTime));
|
||||
processing::queueAction(new RunScriptTick(devices.scripts.client, realFrameLen, false, clientTickTime));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TRACE_GC_LOCK
|
||||
devices.scripts.menu->markGCPossible();
|
||||
devices.scripts.client->markGCPossible();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unordered_map<std::string,time_t> guiModuleTimes;
|
||||
void tickGuiReload() {
|
||||
if(!devices.scripts.client)
|
||||
return;
|
||||
for(auto it = devices.scripts.client->modules.begin(); it != devices.scripts.client->modules.end(); ++it) {
|
||||
scripts::Module& mod = *it->second;
|
||||
scripts::File& fl = *mod.file;
|
||||
|
||||
time_t mtime = getModifiedTime(fl.path);
|
||||
auto f = guiModuleTimes.find(mod.name);
|
||||
if(f == guiModuleTimes.end()) {
|
||||
guiModuleTimes[mod.name] = mtime;
|
||||
}
|
||||
else {
|
||||
if(f->second < mtime)
|
||||
devices.scripts.client->reload(mod.name);
|
||||
guiModuleTimes[mod.name] = mtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static render::Texture* renderTarget = nullptr;
|
||||
void getFrameRender(Image& img) {
|
||||
if(!game_running || !devices.scripts.client)
|
||||
return;
|
||||
|
||||
auto prev_state = game_state;
|
||||
game_state = GS_Menu;
|
||||
|
||||
devices.scripts.client->preRender(0.0);
|
||||
devices.scripts.client->render(0.0);
|
||||
|
||||
renderTarget->save(img);
|
||||
devices.render->setRenderTarget(nullptr);
|
||||
|
||||
game_state = prev_state;
|
||||
}
|
||||
|
||||
void renderFrame(scripts::Manager* uiScript, scripts::Manager* renderScript) {
|
||||
drawnSteps = 0;
|
||||
bufferFlushes = 0;
|
||||
for(unsigned i = 0; i < render::FC_COUNT; ++i)
|
||||
render::vbFlushCounts[i] = 0;
|
||||
|
||||
destroyQueuedTextures();
|
||||
|
||||
#ifdef TRACE_GC_LOCK
|
||||
devices.scripts.menu->markGCImpossible();
|
||||
devices.scripts.client->markGCImpossible();
|
||||
#endif
|
||||
|
||||
vec2i screenSize(devices.driver->win_width,devices.driver->win_height);
|
||||
bool render = screenSize.width != 0 && screenSize.height != 0;
|
||||
|
||||
if(render) {
|
||||
if(!renderTarget)
|
||||
renderTarget = devices.render->createRenderTarget(screenSize * scale_3d);
|
||||
if(renderTarget->size != screenSize * scale_3d) {
|
||||
delete renderTarget;
|
||||
renderTarget = devices.render->createRenderTarget(screenSize * scale_3d);
|
||||
}
|
||||
}
|
||||
|
||||
devices.sound->setListenerData(vec3f(devices.render->cam_pos), vec3f(), vec3f(devices.render->cam_pos + devices.render->cam_facing), vec3f(devices.render->cam_up));
|
||||
|
||||
if(!Object::GALAXY_CREATION)
|
||||
scene::processNodeEvents();
|
||||
|
||||
if(renderScript)
|
||||
renderScript->preRender(realFrameLen);
|
||||
|
||||
nearestNode = 9.0e35; furthestNode = 0.0;
|
||||
|
||||
static bool dumpAnimTimes = false;
|
||||
if(!animTimes) {
|
||||
animationThreadCount = std::max(1u,devices.driver->getProcessorCount()-1);
|
||||
animTimes = new double[animationThreadCount]();
|
||||
for(unsigned i = 0; i < animationThreadCount; ++i)
|
||||
threads::createThread(animateNodes, &animTimes[i]);
|
||||
#ifdef PROFILE_ANIMATION
|
||||
console.addCommand("anim_times", [](argList& args) {
|
||||
dumpAnimTimes = true;
|
||||
}, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
render::Texture* rt = nullptr;
|
||||
const render::Shader* shad = nullptr;
|
||||
if(fsShader || scale_3d != 1.0) {
|
||||
rt = renderTarget;
|
||||
shad = fsShader;
|
||||
}
|
||||
|
||||
devices.library.reloadWatchedResources();
|
||||
|
||||
if(render) {
|
||||
devices.render->setScreenSize(screenSize.x, screenSize.y);
|
||||
devices.render->setRenderTarget(rt);
|
||||
}
|
||||
|
||||
if(screenSize.y != 0)
|
||||
pixelSizeRatio = (double)screenSize.y / 1024.0;
|
||||
|
||||
double animTotal = 0.0;
|
||||
{
|
||||
int nodeCount = (int)devices.scene->children.size();
|
||||
animProcessed = 0;
|
||||
|
||||
//Animate nodes, signaling the animation threads to work if they get time
|
||||
animIndex = nodeCount-1;
|
||||
unsigned loops = 0;
|
||||
while(true) {
|
||||
bool waiting = false;
|
||||
|
||||
if(!scriptTickSignal.check(0)) {
|
||||
waiting = true;
|
||||
processing::run(true);
|
||||
}
|
||||
if(animProcessed != nodeCount) {
|
||||
waiting = true;
|
||||
animTotal += animateSomeNodes(128);
|
||||
}
|
||||
|
||||
if(!waiting)
|
||||
break;
|
||||
if(++loops % 100 == 0)
|
||||
threads::sleep(0);
|
||||
}
|
||||
|
||||
//Sort the catch-all parent
|
||||
devices.scene->sortChildren();
|
||||
|
||||
//Wait for all animation threads to report in
|
||||
while(!animationSignal.check(0))
|
||||
threads::sleep(0);
|
||||
|
||||
for(unsigned i = 0; i < animationThreadCount; ++i) {
|
||||
double t = animTimes[i];
|
||||
animTotal += t;
|
||||
}
|
||||
#ifdef PROFILE_ANIMATION
|
||||
if(dumpAnimTimes)
|
||||
scene::dumpAnimationProfile();
|
||||
dumpAnimTimes = false;
|
||||
#else
|
||||
(void)dumpAnimTimes;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef TRACE_GC_LOCK
|
||||
devices.scripts.menu->markGCImpossible();
|
||||
devices.scripts.client->markGCImpossible();
|
||||
#endif
|
||||
|
||||
double animation_end = devices.driver->getAccurateTime();
|
||||
animation_s = animTotal;
|
||||
|
||||
auto prevSection = enterSection(NS_Render);
|
||||
|
||||
if(render) {
|
||||
devices.render->setDefaultRenderState();
|
||||
|
||||
//Animation system tells us the near and far distances of everything that gets rendered
|
||||
devices.render->setNearFarPlanes(1.0, furthestNode * 1.1);
|
||||
|
||||
//3D prepare is done by scripts, since it needs the camera
|
||||
devices.render->clearRenderPrepared();
|
||||
if(renderScript)
|
||||
renderScript->render(realFrameLen);
|
||||
|
||||
if(rt) {
|
||||
devices.render->setRenderTarget(0);
|
||||
|
||||
if(devices.render->isRenderPrepared()) {
|
||||
render::RenderState rs;
|
||||
if(shad)
|
||||
rs.shader = shad;
|
||||
else
|
||||
rs.shader = devices.library["Fullscreen"];
|
||||
rs.lighting = false;
|
||||
rs.culling = render::FC_None;
|
||||
rs.depthWrite = false;
|
||||
rs.depthTest = render::DT_NoDepthTest;
|
||||
rs.constant = false;
|
||||
rs.textures[0] = rt;
|
||||
rs.textures[7] = ((render::glFrameBuffer*)rt)->depthTexture; //fuck you framebuffers aren't textures
|
||||
|
||||
auto* buffer = render::VertexBufferTCV::fetch(&rs);
|
||||
auto* verts = buffer->request(1, render::PT_Quads);
|
||||
|
||||
verts[0].set(vec2f(0,0));
|
||||
verts[1].set(vec2f(1,0));
|
||||
verts[2].set(vec2f(1,1));
|
||||
verts[3].set(vec2f(0,1));
|
||||
|
||||
buffer->draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check if we should scale the 2D interface
|
||||
if(render && !hide_ui) {
|
||||
static render::Texture* uirt = 0;
|
||||
vec2i uiArea;
|
||||
if(ui_scale != 1.0) {
|
||||
uiArea = vec2d(devices.driver->win_width, devices.driver->win_height) / ui_scale;
|
||||
|
||||
if(uirt == 0) {
|
||||
uirt = devices.render->createRenderTarget(uiArea);
|
||||
}
|
||||
else {
|
||||
if(uirt->size != uiArea) {
|
||||
delete uirt;
|
||||
uirt = devices.render->createRenderTarget(uiArea);
|
||||
}
|
||||
}
|
||||
|
||||
devices.render->setScreenSize(uiArea.x, uiArea.y);
|
||||
devices.render->setRenderTarget(uirt, true);
|
||||
}
|
||||
|
||||
//2D prepare is done before scripts
|
||||
devices.render->prepareRender2D();
|
||||
if(uiScript)
|
||||
uiScript->draw();
|
||||
|
||||
//Scale the 2D interface
|
||||
if(uiArea.x != 0) {
|
||||
devices.render->setScreenSize(screenSize.x, screenSize.y);
|
||||
devices.render->setRenderTarget(0);
|
||||
devices.render->prepareRender2D();
|
||||
|
||||
render::RenderState rs;
|
||||
rs.lighting = false;
|
||||
rs.constant = false;
|
||||
rs.culling = render::FC_None;
|
||||
rs.baseMat = render::MAT_Overlay;
|
||||
rs.shader = devices.library["Fullscreen"];
|
||||
rs.depthWrite = false;
|
||||
rs.depthTest = render::DT_NoDepthTest;
|
||||
rs.filterMin = render::TF_Linear;
|
||||
rs.filterMag = render::TF_Linear;
|
||||
rs.textures[0] = uirt;
|
||||
|
||||
auto* buffer = render::VertexBufferTCV::fetch(&rs);
|
||||
auto* verts = buffer->request(1, render::PT_Quads);
|
||||
|
||||
verts[0].set(vec2f(0,0));
|
||||
verts[1].set(vec2f(1,0));
|
||||
verts[2].set(vec2f(1,1));
|
||||
verts[3].set(vec2f(0,1));
|
||||
|
||||
buffer->draw();
|
||||
}
|
||||
}
|
||||
|
||||
if(render) {
|
||||
if(hide_ui)
|
||||
devices.render->prepareRender2D();
|
||||
console.render(*devices.render);
|
||||
render::renderVertexBuffers();
|
||||
}
|
||||
else {
|
||||
threads::sleep(1);
|
||||
}
|
||||
|
||||
double render_end = devices.driver->getAccurateTime();
|
||||
|
||||
devices.driver->swapBuffers(maxfps && *maxfps > 0 ? 1.0 / *maxfps : 0.0);
|
||||
|
||||
double present_end = devices.driver->getAccurateTime();
|
||||
|
||||
render_s = render_end - animation_end;
|
||||
present_s = present_end - render_end;
|
||||
|
||||
#ifdef TRACE_GC_LOCK
|
||||
devices.scripts.menu->markGCPossible();
|
||||
devices.scripts.client->markGCPossible();
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
devices.render->reportErrors();
|
||||
#endif
|
||||
|
||||
enterSection(prevSection);
|
||||
}
|
||||
|
||||
void tickConsole() {
|
||||
tickGlobal(false);
|
||||
renderFrame(0, 0);
|
||||
}
|
||||
|
||||
void tickGame() {
|
||||
//General tick
|
||||
tickGlobal();
|
||||
|
||||
if(devices.driver->getGameSpeed() > 0 && !devices.network->isClient) {
|
||||
autosaveTimer += realFrameLen;
|
||||
|
||||
auto* interval = devices.settings.engine.getSetting("dAutosaveMinutes");
|
||||
if(interval && interval->getDouble() >= 1.0 && autosaveTimer > interval->getDouble() * 60.0) {
|
||||
int maxAutosaves = 1;
|
||||
auto* count = devices.settings.engine.getSetting("iAutosaveCount");
|
||||
if(count)
|
||||
maxAutosaves = count->getInteger();
|
||||
std::string prevName;
|
||||
if(maxAutosaves > 1) {
|
||||
for(int index = maxAutosaves; index > 0; --index) {
|
||||
std::string fname = "autosave";
|
||||
if(index != 1)
|
||||
fname += toString(index);
|
||||
fname += ".sr2";
|
||||
fname = path_join(devices.mods.getGlobalProfile("saves"), fname);
|
||||
|
||||
if(fileExists(fname)) {
|
||||
if(index == maxAutosaves)
|
||||
remove(fname.c_str());
|
||||
else
|
||||
rename(fname.c_str(), prevName.c_str());
|
||||
}
|
||||
prevName = fname;
|
||||
}
|
||||
}
|
||||
|
||||
processing::pause();
|
||||
saveGame(path_join(devices.mods.getGlobalProfile("saves"), "autosave.sr2"));
|
||||
processing::resume();
|
||||
|
||||
autosaveTimer = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if(reload_gui)
|
||||
tickGuiReload();
|
||||
|
||||
renderFrame(devices.scripts.client, devices.scripts.client);
|
||||
inputTick();
|
||||
processing::runIsolation();
|
||||
}
|
||||
|
||||
void tickMenu() {
|
||||
//General tick
|
||||
tickGlobal();
|
||||
renderFrame(devices.scripts.menu, devices.scripts.menu);
|
||||
inputTick();
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, time_t> monitoredFiles;
|
||||
void monitorFile(const std::string& filename) {
|
||||
if(monitor_files)
|
||||
monitoredFiles[filename] = getModifiedTime(filename);
|
||||
}
|
||||
|
||||
bool tickMonitor() {
|
||||
if(isPreloading())
|
||||
return false;
|
||||
|
||||
foreach(it, monitoredFiles) {
|
||||
time_t mtime = getModifiedTime(it->first);
|
||||
if(mtime > it->second) {
|
||||
monitoredFiles.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user