mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-25 11:52:40 +01:00
Rework how exiting and cleanup is handled.
This involves a few changes. The first changes involve allocating GameWindow and WorkContext on the heap, so that RWGame still owns them but chooses when they're freed. The work queue is given a method to stop the worker thread without destroying the work context, so that subsystems relying on the work context may still function to shut down. Then RWGame is rearranged to cleanup separate subsystems in an order that does not conflict (i.e., stop the work queue, shut down other subsystems, then the renderer, *then* the window.) The window needs to be cleaned up *after* the renderer because it owns the OpenGL context.
This commit is contained in:
parent
7c8cdba196
commit
d3084ad721
@ -35,6 +35,7 @@ StdOutReciever logPrinter;
|
||||
RWGame::RWGame(int argc, char* argv[])
|
||||
: config("openrw.ini")
|
||||
, state(nullptr), world(nullptr), renderer(nullptr), script(nullptr),
|
||||
window(nullptr), work(nullptr),
|
||||
debugScript(false), inFocus(true),
|
||||
showDebugStats(false), showDebugPaths(false), showDebugPhysics(false),
|
||||
accum(0.f), timescale(1.f)
|
||||
@ -90,8 +91,11 @@ RWGame::RWGame(int argc, char* argv[])
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
throw std::runtime_error("Failed to initialize SDL2!");
|
||||
|
||||
window.create(w, h, fullscreen);
|
||||
window.hideCursor();
|
||||
window = new GameWindow();
|
||||
window->create(w, h, fullscreen);
|
||||
window->hideCursor();
|
||||
|
||||
work = new WorkContext();
|
||||
|
||||
log.addReciever(&logPrinter);
|
||||
log.info("Game", "Game directory: " + config.getGameDataPath());
|
||||
@ -101,7 +105,7 @@ RWGame::RWGame(int argc, char* argv[])
|
||||
throw std::runtime_error("Invalid game directory path: " + config.getGameDataPath());
|
||||
}
|
||||
|
||||
data = new GameData(&log, &work, config.getGameDataPath());
|
||||
data = new GameData(&log, work, config.getGameDataPath());
|
||||
|
||||
// Initalize all the archives.
|
||||
data->loadIMG("/models/gta3");
|
||||
@ -168,10 +172,30 @@ RWGame::RWGame(int argc, char* argv[])
|
||||
|
||||
RWGame::~RWGame()
|
||||
{
|
||||
log.info("Game", "Beginning cleanup");
|
||||
|
||||
log.info("Game", "Stopping work queue");
|
||||
work->stop();
|
||||
|
||||
log.info("Game", "Cleaning up scripts");
|
||||
delete script;
|
||||
|
||||
log.info("Game", "Cleaning up renderer");
|
||||
delete renderer;
|
||||
|
||||
log.info("Game", "Cleaning up world");
|
||||
delete world;
|
||||
|
||||
log.info("Game", "Cleaning up state");
|
||||
delete state;
|
||||
|
||||
log.info("Game", "Cleaning up window");
|
||||
delete window;
|
||||
|
||||
log.info("Game", "Cleaning up work queue");
|
||||
delete work;
|
||||
|
||||
log.info("Game", "Done cleaning up");
|
||||
}
|
||||
|
||||
void RWGame::newGame()
|
||||
@ -183,7 +207,7 @@ void RWGame::newGame()
|
||||
}
|
||||
|
||||
state = new GameState();
|
||||
world = new GameWorld(&log, &work, data);
|
||||
world = new GameWorld(&log, work, data);
|
||||
world->dynamicsWorld->setDebugDrawer(debug);
|
||||
|
||||
// Associate the new world with the new state and vice versa
|
||||
@ -294,8 +318,8 @@ int RWGame::run()
|
||||
{
|
||||
last_clock_time = clock.now();
|
||||
|
||||
// Loop until the window is closed or we run out of state.
|
||||
while (window.isOpen() && StateManager::get().states.size()) {
|
||||
// Loop until we run out of states.
|
||||
while (StateManager::get().states.size()) {
|
||||
State* state = StateManager::get().states.back();
|
||||
|
||||
RW_PROFILE_FRAME_BOUNDARY();
|
||||
@ -305,7 +329,7 @@ int RWGame::run()
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
window.close();
|
||||
StateManager::get().clear();
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
@ -343,14 +367,14 @@ int RWGame::run()
|
||||
|
||||
RW_PROFILE_BEGIN("Update");
|
||||
if ( accum >= GAME_TIMESTEP ) {
|
||||
RW_PROFILE_BEGIN("state");
|
||||
StateManager::get().tick(GAME_TIMESTEP);
|
||||
RW_PROFILE_END();
|
||||
|
||||
if (StateManager::get().states.size() == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
RW_PROFILE_BEGIN("state");
|
||||
StateManager::get().tick(GAME_TIMESTEP);
|
||||
RW_PROFILE_END();
|
||||
|
||||
RW_PROFILE_BEGIN("engine");
|
||||
tick(GAME_TIMESTEP);
|
||||
RW_PROFILE_END();
|
||||
@ -385,7 +409,7 @@ int RWGame::run()
|
||||
|
||||
renderProfile();
|
||||
|
||||
window.swap();
|
||||
window->swap();
|
||||
}
|
||||
|
||||
if( httpserver_thread )
|
||||
@ -483,7 +507,7 @@ void RWGame::render(float alpha, float time)
|
||||
|
||||
getRenderer()->getRenderer()->swap();
|
||||
|
||||
glm::ivec2 windowSize = window.getSize();
|
||||
glm::ivec2 windowSize = window->getSize();
|
||||
renderer->setViewport(windowSize.x, windowSize.y);
|
||||
|
||||
ViewCamera viewCam;
|
||||
|
@ -28,11 +28,11 @@ class RWGame
|
||||
GameRenderer* renderer;
|
||||
ScriptMachine* script;
|
||||
// Background worker
|
||||
WorkContext work;
|
||||
WorkContext *work;
|
||||
bool debugScript;
|
||||
HttpServer* httpserver = nullptr;
|
||||
std::thread* httpserver_thread = nullptr;
|
||||
GameWindow window;
|
||||
GameWindow *window;
|
||||
std::chrono::steady_clock clock;
|
||||
std::chrono::steady_clock::time_point last_clock_time;
|
||||
|
||||
@ -79,7 +79,7 @@ public:
|
||||
|
||||
GameWindow& getWindow()
|
||||
{
|
||||
return window;
|
||||
return *window;
|
||||
}
|
||||
|
||||
ScriptMachine* getScript() const
|
||||
|
@ -80,6 +80,11 @@ struct StateManager
|
||||
}
|
||||
|
||||
std::deque<State*> states;
|
||||
|
||||
void clear()
|
||||
{
|
||||
states.clear();
|
||||
}
|
||||
|
||||
void enter(State* state)
|
||||
{
|
||||
|
@ -20,7 +20,7 @@ void MenuState::enterMainMenu()
|
||||
m->addEntry(Menu::lambda("Load Game", [=] { enterLoadMenu(); }));
|
||||
m->addEntry(Menu::lambda("Test", [=] { StateManager::get().enter(new IngameState(game, true, "test")); }));
|
||||
m->addEntry(Menu::lambda("Options", [] { RW_UNIMPLEMENTED("Options Menu"); }));
|
||||
m->addEntry(Menu::lambda("Exit", [=] { getWindow().close(); }));
|
||||
m->addEntry(Menu::lambda("Exit", [] { StateManager::get().clear(); }));
|
||||
this->enterMenu(m);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ PauseState::PauseState(RWGame* game)
|
||||
m->offset = glm::vec2( 200.f, 200.f );
|
||||
m->addEntry(Menu::lambda("Continue", [] { StateManager::get().exit(); }));
|
||||
m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; }));
|
||||
m->addEntry(Menu::lambda("Exit", [&] { getWindow().close(); }));
|
||||
m->addEntry(Menu::lambda("Exit", [] { StateManager::get().clear(); }));
|
||||
this->enterMenu(m);
|
||||
}
|
||||
|
||||
|
@ -12,20 +12,21 @@ void WorkContext::workNext()
|
||||
{
|
||||
WorkJob* j = nullptr;
|
||||
|
||||
_inMutex.lock();
|
||||
if( ! _workQueue.empty() ) {
|
||||
j = _workQueue.front();
|
||||
_workQueue.pop();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard( _inMutex );
|
||||
|
||||
if( ! _workQueue.empty() ) {
|
||||
j = _workQueue.front();
|
||||
_workQueue.pop();
|
||||
}
|
||||
}
|
||||
_inMutex.unlock();
|
||||
|
||||
if( j == nullptr ) return;
|
||||
|
||||
j->work();
|
||||
|
||||
_outMutex.lock();
|
||||
|
||||
std::lock_guard<std::mutex> guard( _outMutex );
|
||||
_completeQueue.push(j);
|
||||
_outMutex.unlock();
|
||||
}
|
||||
|
||||
void WorkContext::update()
|
||||
|
@ -5,8 +5,11 @@
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
class WorkContext;
|
||||
|
||||
@ -16,7 +19,7 @@ class LoadWorker
|
||||
|
||||
public:
|
||||
|
||||
bool _running;
|
||||
std::atomic<bool> _running;
|
||||
std::thread _thread;
|
||||
void start();
|
||||
|
||||
@ -66,26 +69,32 @@ class GameWorld;
|
||||
*/
|
||||
class WorkContext
|
||||
{
|
||||
std::unique_ptr<LoadWorker> _worker;
|
||||
|
||||
std::queue<WorkJob*> _workQueue;
|
||||
std::queue<WorkJob*> _completeQueue;
|
||||
|
||||
LoadWorker _worker;
|
||||
|
||||
std::mutex _inMutex;
|
||||
std::mutex _outMutex;
|
||||
|
||||
public:
|
||||
|
||||
WorkContext()
|
||||
: _worker(this) { }
|
||||
: _worker(new LoadWorker(this)) { }
|
||||
|
||||
void queueJob( WorkJob* job )
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_inMutex);
|
||||
std::lock_guard<std::mutex> guard( _inMutex );
|
||||
_workQueue.push( job );
|
||||
}
|
||||
|
||||
// Called by the worker thread - don't touch;
|
||||
void stop()
|
||||
{
|
||||
// Stop serving the queue.
|
||||
_worker.reset(nullptr);
|
||||
}
|
||||
|
||||
// Called by the worker thread - don't touch
|
||||
void workNext();
|
||||
|
||||
const std::queue<WorkJob*> getWorkQueue() const { return _workQueue; }
|
||||
|
Loading…
Reference in New Issue
Block a user