more c++ bootstrap refactoring

This commit is contained in:
Eric Froemling 2022-09-12 10:09:31 -07:00
parent 014a996933
commit 866f9475fb
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
18 changed files with 676 additions and 684 deletions

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@ def bootstrap() -> None:
# Give a soft warning if we're being used with a different binary # Give a soft warning if we're being used with a different binary
# version than we expect. # version than we expect.
expected_build = 20821 expected_build = 20822
running_build: int = env['build_number'] running_build: int = env['build_number']
if running_build != expected_build: if running_build != expected_build:
print( print(

View File

@ -14,6 +14,7 @@
#include "ballistica/core/fatal_error.h" #include "ballistica/core/fatal_error.h"
#include "ballistica/core/logging.h" #include "ballistica/core/logging.h"
#include "ballistica/core/thread.h" #include "ballistica/core/thread.h"
#include "ballistica/dynamics/bg/bg_dynamics.h"
#include "ballistica/dynamics/bg/bg_dynamics_server.h" #include "ballistica/dynamics/bg/bg_dynamics_server.h"
#include "ballistica/game/v1_account.h" #include "ballistica/game/v1_account.h"
#include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/graphics_server.h"
@ -30,7 +31,7 @@
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // These are set automatically via script; don't modify them here.
const int kAppBuildNumber = 20821; const int kAppBuildNumber = 20822;
const char* kAppVersion = "1.7.7"; const char* kAppVersion = "1.7.7";
// Our standalone globals. // Our standalone globals.
@ -55,8 +56,8 @@ Input* g_input{};
Thread* g_main_thread{}; Thread* g_main_thread{};
Assets* g_assets{}; Assets* g_assets{};
AssetsServer* g_assets_server{}; AssetsServer* g_assets_server{};
NetworkReader* g_network_reader{};
Networking* g_networking{}; Networking* g_networking{};
NetworkReader* g_network_reader{};
NetworkWriter* g_network_writer{}; NetworkWriter* g_network_writer{};
Platform* g_platform{}; Platform* g_platform{};
Python* g_python{}; Python* g_python{};
@ -99,16 +100,15 @@ auto BallisticaMain(int argc, char** argv) -> int {
g_app = new App(argc, argv); g_app = new App(argc, argv);
g_platform = Platform::Create(); g_platform = Platform::Create();
// Bootstrap our Python environment as early as we can (depends on
// g_platform for locating OS-specific paths).
g_python = new Python();
// Create a Thread wrapper around the current (main) thread. // Create a Thread wrapper around the current (main) thread.
g_main_thread = new Thread(ThreadIdentifier::kMain, ThreadType::kMain); g_main_thread = new Thread(ThreadIdentifier::kMain, ThreadType::kMain);
Thread::UpdateMainThreadID(); Thread::UpdateMainThreadID();
// Spin up our specific app and graphics variations // Bootstrap our Python environment as early as we can (depends on
// (VR, headless, regular, etc.) // g_platform for locating OS-specific paths).
g_python = new Python();
// Spin up our specific app and graphics variations (VR, headless, etc.)
g_app_flavor = g_platform->CreateAppFlavor(); g_app_flavor = g_platform->CreateAppFlavor();
g_graphics = g_platform->CreateGraphics(); g_graphics = g_platform->CreateGraphics();
@ -125,25 +125,21 @@ auto BallisticaMain(int argc, char** argv) -> int {
g_assets_server = new AssetsServer(); g_assets_server = new AssetsServer();
g_ui = Object::NewUnmanaged<UI>(); g_ui = Object::NewUnmanaged<UI>();
g_networking = new Networking(); g_networking = new Networking();
g_network_writer = new NetworkWriter();
g_input = new Input(); g_input = new Input();
g_app_internal = GetAppInternal(); g_app_internal = CreateAppInternal();
g_game = new Game();
Scene::Init(); Scene::Init();
if (!HeadlessMode()) {
g_bg_dynamics = new BGDynamics();
g_bg_dynamics_server = new BGDynamicsServer();
}
// Spin up our other standard threads. // FIXME - move this later but need to init Python earlier then.
auto* logic_thread{new Thread(ThreadIdentifier::kLogic)}; g_game->Start();
g_app->pausable_threads.push_back(logic_thread);
auto* network_write_thread{new Thread(ThreadIdentifier::kNetworkWrite)};
g_app->pausable_threads.push_back(network_write_thread);
// Spin up our subsystems in those threads. // NOTE TO SELF: this starts reading stdin and py-init hangs if we do
logic_thread->PushCallSynchronous( // it after here.
[logic_thread] { new Game(logic_thread); });
network_write_thread->PushCallSynchronous(
[network_write_thread] { new NetworkWriter(network_write_thread); });
// Now let the platform spin up any other threads/modules it uses.
// (bg-dynamics in non-headless builds, stdin/stdout where applicable,
// etc.)
g_platform->CreateAuxiliaryModules(); g_platform->CreateAuxiliaryModules();
// Ok at this point we can be considered up-and-running. // Ok at this point we can be considered up-and-running.

View File

@ -84,7 +84,7 @@ void Thread::WaitForNextEvent(bool single_cycle) {
} }
// While we're waiting, allow other python threads to run. // While we're waiting, allow other python threads to run.
if (holds_python_gil_) { if (acquires_python_gil_) {
g_python->ReleaseGIL(); g_python->ReleaseGIL();
} }
@ -117,7 +117,7 @@ void Thread::WaitForNextEvent(bool single_cycle) {
} }
} }
if (holds_python_gil_) { if (acquires_python_gil_) {
g_python->AcquireGIL(); g_python->AcquireGIL();
} }
} }
@ -357,8 +357,8 @@ auto Thread::ThreadMain() -> int {
} }
} }
void Thread::SetHoldsPythonGIL() { void Thread::SetAcquiresPythonGIL() {
holds_python_gil_ = true; acquires_python_gil_ = true;
g_python->AcquireGIL(); g_python->AcquireGIL();
} }

View File

@ -44,7 +44,7 @@ class Thread {
// Used to quit the main thread. // Used to quit the main thread.
void Quit(); void Quit();
void SetHoldsPythonGIL(); void SetAcquiresPythonGIL();
void SetPaused(bool paused); void SetPaused(bool paused);
auto thread_id() const -> std::thread::id { return thread_id_; } auto thread_id() const -> std::thread::id { return thread_id_; }
@ -137,7 +137,7 @@ class Thread {
std::thread::id thread_id_{}; std::thread::id thread_id_{};
ThreadIdentifier identifier_{ThreadIdentifier::kInvalid}; ThreadIdentifier identifier_{ThreadIdentifier::kInvalid};
millisecs_t last_complaint_time_{}; millisecs_t last_complaint_time_{};
bool holds_python_gil_{}; bool acquires_python_gil_{};
// FIXME: Should generalize this to some sort of PlatformThreadData class. // FIXME: Should generalize this to some sort of PlatformThreadData class.
#if BA_XCODE_BUILD #if BA_XCODE_BUILD

View File

@ -14,15 +14,9 @@
namespace ballistica { namespace ballistica {
void BGDynamics::Init() {
// Just init our singleton.
new BGDynamics();
}
BGDynamics::BGDynamics() { BGDynamics::BGDynamics() {
assert(InLogicThread()); // We're a singleton; make sure we don't already exist.
assert(g_bg_dynamics == nullptr); assert(g_bg_dynamics == nullptr);
g_bg_dynamics = this;
} }
void BGDynamics::AddTerrain(CollideModelData* o) { void BGDynamics::AddTerrain(CollideModelData* o) {

View File

@ -48,7 +48,7 @@ class BGDynamicsEmission {
// client (game thread) functionality for bg dynamics // client (game thread) functionality for bg dynamics
class BGDynamics { class BGDynamics {
public: public:
static void Init(); BGDynamics();
void Emit(const BGDynamicsEmission& def); void Emit(const BGDynamicsEmission& def);
void Step(const Vector3f& cam_pos); void Step(const Vector3f& cam_pos);
@ -68,7 +68,6 @@ class BGDynamics {
void SetDrawSnapshot(BGDynamicsDrawSnapshot* s); void SetDrawSnapshot(BGDynamicsDrawSnapshot* s);
private: private:
BGDynamics();
void DrawChunks(FrameDef* frame_def, std::vector<Matrix44f>* instances, void DrawChunks(FrameDef* frame_def, std::vector<Matrix44f>* instances,
BGDynamicsChunkType chunk_type); BGDynamicsChunkType chunk_type);
Object::Ref<SpriteMesh> lights_mesh_; Object::Ref<SpriteMesh> lights_mesh_;

View File

@ -660,12 +660,15 @@ void BGDynamicsServer::ParticleSet::UpdateAndCreateSnapshot(
current_set = !current_set; current_set = !current_set;
} }
BGDynamicsServer::BGDynamicsServer(Thread* thread) BGDynamicsServer::BGDynamicsServer()
: thread_(thread), : height_cache_(new BGDynamicsHeightCache()),
height_cache_(new BGDynamicsHeightCache()),
collision_cache_(new CollisionCache) { collision_cache_(new CollisionCache) {
// We're a singleton; make sure we don't already exist.
BA_PRECONDITION(g_bg_dynamics_server == nullptr); BA_PRECONDITION(g_bg_dynamics_server == nullptr);
g_bg_dynamics_server = this;
// Spin up our thread.
thread_ = new Thread(ThreadIdentifier::kBGDynamics);
g_app->pausable_threads.push_back(thread_);
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
ode_world_ = dWorldCreate(); ode_world_ = dWorldCreate();

View File

@ -80,7 +80,7 @@ class BGDynamicsServer {
std::vector<std::pair<BGDynamicsFuseData*, FuseStepData> > fuse_step_data_; std::vector<std::pair<BGDynamicsFuseData*, FuseStepData> > fuse_step_data_;
}; };
explicit BGDynamicsServer(Thread* thread); BGDynamicsServer();
auto time() const -> uint32_t { return time_; } auto time() const -> uint32_t { return time_; }
auto graphics_quality() const -> GraphicsQuality { return graphics_quality_; } auto graphics_quality() const -> GraphicsQuality { return graphics_quality_; }

View File

@ -17,12 +17,12 @@ CollisionCache::~CollisionCache() {
dGeomDestroy(test_box_); dGeomDestroy(test_box_);
} }
void CollisionCache::SetGeoms(const std::vector<dGeomID>& geoms) { auto CollisionCache::SetGeoms(const std::vector<dGeomID>& geoms) -> void {
dirty_ = true; dirty_ = true;
geoms_ = geoms; geoms_ = geoms;
} }
void CollisionCache::Draw(FrameDef* frame_def) { auto CollisionCache::Draw(FrameDef* frame_def) -> void {
if (cells_.empty()) { if (cells_.empty()) {
return; return;
} }
@ -124,7 +124,7 @@ void CollisionCache::Draw(FrameDef* frame_def) {
} }
} }
void CollisionCache::Precalc() { auto CollisionCache::Precalc() -> void {
Update(); Update();
if (precalc_index_ >= cells_.size()) { if (precalc_index_ >= cells_.size()) {
@ -138,8 +138,8 @@ void CollisionCache::Precalc() {
TestCell(precalc_index_++, x, z); TestCell(precalc_index_++, x, z);
} }
void CollisionCache::CollideAgainstGeom(dGeomID g1, void* data, auto CollisionCache::CollideAgainstGeom(dGeomID g1, void* data,
dNearCallback* callback) { dNearCallback* callback) -> void {
// Update bounds, test for quick out against our height map, // Update bounds, test for quick out against our height map,
// and proceed to a full test on a positive result. // and proceed to a full test on a positive result.
g1->recomputeAABB(); g1->recomputeAABB();
@ -204,7 +204,7 @@ void CollisionCache::CollideAgainstGeom(dGeomID g1, void* data,
} }
} }
void CollisionCache::TestCell(size_t cell_index, int x, int z) { auto CollisionCache::TestCell(size_t cell_index, int x, int z) -> void {
int t_count = static_cast<int>(geoms_.size()); int t_count = static_cast<int>(geoms_.size());
float top = cells_[cell_index].height_confirmed_empty_; float top = cells_[cell_index].height_confirmed_empty_;
@ -252,8 +252,8 @@ void CollisionCache::TestCell(size_t cell_index, int x, int z) {
} }
} }
void CollisionCache::CollideAgainstSpace(dSpaceID space, void* data, auto CollisionCache::CollideAgainstSpace(dSpaceID space, void* data,
dNearCallback* callback) { dNearCallback* callback) -> void {
// We handle our own testing against trimeshes, so we can bring our fancy // We handle our own testing against trimeshes, so we can bring our fancy
// caching into play. // caching into play.
if (!geoms_.empty()) { if (!geoms_.empty()) {
@ -264,7 +264,7 @@ void CollisionCache::CollideAgainstSpace(dSpaceID space, void* data,
} }
} }
void CollisionCache::Update() { auto CollisionCache::Update() -> void {
if (!dirty_) { if (!dirty_) {
return; return;
} }

View File

@ -27,7 +27,7 @@ class CollisionCache {
// Call this periodically (once per cycle or so) to slowly fill in // Call this periodically (once per cycle or so) to slowly fill in
// the cache so there's less to do during spurts of activity; // the cache so there's less to do during spurts of activity;
void Precalc(); auto Precalc() -> void;
private: private:
auto TestCell(size_t cell_index, int x, int z) -> void; auto TestCell(size_t cell_index, int x, int z) -> void;

View File

@ -64,29 +64,33 @@ const int kMaxChatMessages = 40;
// Go with 5 minute ban. // Go with 5 minute ban.
const int kKickBanSeconds = 5 * 60; const int kKickBanSeconds = 5 * 60;
Game::Game(Thread* thread) Game::Game()
: thread_(thread), : game_roster_(cJSON_CreateArray()),
game_roster_(cJSON_CreateArray()),
realtimers_(new TimerList()), realtimers_(new TimerList()),
connections_(std::make_unique<ConnectionSet>()) { connections_(std::make_unique<ConnectionSet>()) {
// We're a singleton; make sure we don't already exist.
assert(g_game == nullptr); assert(g_game == nullptr);
g_game = this;
InitSpecialChars();
// Spin up our thread.
thread_ = new Thread(ThreadIdentifier::kLogic);
g_app->pausable_threads.push_back(thread_);
// Our thread should hold the Python GIL by default.
// TODO(ericf): It could be better to have each individual Python call
// we make acquire the GIL. Then we're not holding it during long
// bits of C++ logic.
thread_->SetAcquiresPythonGIL();
}
auto Game::Start() -> void {
thread_->PushCallSynchronous([this] { StartInThread(); });
}
auto Game::StartInThread() -> void {
try { try {
// Our thread should hold the Python GIL by default.
// TODO(ericf): It could be better to have each individual Python call
// we make acquire the GIL. Then we're not holding it during long
// bits of C++ logic.
thread->SetHoldsPythonGIL();
if (!HeadlessMode()) {
BGDynamics::Init();
}
InitSpecialChars();
// We want to be informed when our thread is pausing. // We want to be informed when our thread is pausing.
thread->AddPauseCallback(NewLambdaRunnableRaw([this] { OnThreadPause(); })); thread()->AddPauseCallback(
NewLambdaRunnableRaw([this] { OnThreadPause(); }));
g_ui->LogicThreadInit(); g_ui->LogicThreadInit();

View File

@ -24,7 +24,8 @@ const int kMaxPartyNameCombinedSize = 25;
/// rendering, etc. /// rendering, etc.
class Game { class Game {
public: public:
explicit Game(Thread* thread); Game();
auto Start() -> void;
auto LaunchHostSession(PyObject* session_type_obj, auto LaunchHostSession(PyObject* session_type_obj,
BenchmarkType benchmark_type = BenchmarkType::kNone) BenchmarkType benchmark_type = BenchmarkType::kNone)
@ -244,6 +245,7 @@ class Game {
auto thread() const -> Thread* { return thread_; } auto thread() const -> Thread* { return thread_; }
private: private:
auto StartInThread() -> void;
auto HandleQuitOnIdle() -> void; auto HandleQuitOnIdle() -> void;
auto InitSpecialChars() -> void; auto InitSpecialChars() -> void;
auto Draw() -> void; auto Draw() -> void;

View File

@ -24,7 +24,7 @@ void StdInputModule::PushBeginReadCall() {
while (true) { while (true) {
// Print a prompt if we're a tty. // Print a prompt if we're a tty.
// We send this to the game thread so it happens AFTER the // We send this to the logic thread so it happens AFTER the
// results of the last script-command message we may have just sent. // results of the last script-command message we may have just sent.
if (stdin_is_terminal) { if (stdin_is_terminal) {
g_game->thread()->PushCall([] { g_game->thread()->PushCall([] {

View File

@ -10,7 +10,7 @@
namespace ballistica { namespace ballistica {
auto GetAppInternal() -> AppInternal*; auto CreateAppInternal() -> AppInternal*;
class AppInternal { class AppInternal {
public: public:

View File

@ -8,10 +8,13 @@
namespace ballistica { namespace ballistica {
NetworkWriter::NetworkWriter(Thread* thread) : thread_(thread) { NetworkWriter::NetworkWriter() {
// we're a singleton // We're a singleton; make sure we don't already exist.
assert(g_network_writer == nullptr); assert(g_network_writer == nullptr);
g_network_writer = this;
// Spin up our thread.
thread_ = new Thread(ThreadIdentifier::kNetworkWrite);
g_app->pausable_threads.push_back(thread_);
} }
void NetworkWriter::PushSendToCall(const std::vector<uint8_t>& msg, void NetworkWriter::PushSendToCall(const std::vector<uint8_t>& msg,

View File

@ -12,8 +12,8 @@ namespace ballistica {
// A subsystem handling outbound network traffic. // A subsystem handling outbound network traffic.
class NetworkWriter { class NetworkWriter {
public: public:
NetworkWriter();
void PushSendToCall(const std::vector<uint8_t>& msg, const SockAddr& addr); void PushSendToCall(const std::vector<uint8_t>& msg, const SockAddr& addr);
explicit NetworkWriter(Thread* thread);
auto thread() const -> Thread* { return thread_; } auto thread() const -> Thread* { return thread_; }
private: private:

View File

@ -701,15 +701,6 @@ auto Platform::GetKeyName(int keycode) -> std::string {
} }
void Platform::CreateAuxiliaryModules() { void Platform::CreateAuxiliaryModules() {
#if !BA_HEADLESS_BUILD
auto* bg_dynamics_thread = new Thread(ThreadIdentifier::kBGDynamics);
g_app->pausable_threads.push_back(bg_dynamics_thread);
#endif
#if !BA_HEADLESS_BUILD
bg_dynamics_thread->PushCallSynchronous(
[bg_dynamics_thread] { new BGDynamicsServer(bg_dynamics_thread); });
#endif
if (g_buildconfig.use_stdin_thread()) { if (g_buildconfig.use_stdin_thread()) {
// Start listening for stdin commands (on platforms where that makes sense). // Start listening for stdin commands (on platforms where that makes sense).
// Note: this thread blocks indefinitely for input so we don't add it to the // Note: this thread blocks indefinitely for input so we don't add it to the