diff --git a/destrum/include/destrum/App.h b/destrum/include/destrum/App.h index 32f2c0f..a9c03e1 100644 --- a/destrum/include/destrum/App.h +++ b/destrum/include/destrum/App.h @@ -89,6 +89,7 @@ protected: bool cleanedUp{false}; private: + void initDevices(); void processEvents(); void runFixedSteps(); void handleSwapchainResize(bool& skipFrame); diff --git a/destrum/src/App.cpp b/destrum/src/App.cpp index 47ea3e3..01125ba 100644 --- a/destrum/src/App.cpp +++ b/destrum/src/App.cpp @@ -70,13 +70,11 @@ void App::init(const AppParams& params) .maximized = params.startMaximized, }); - gfxDevice.init(window.GetHandle(), params.appName, false); + initDevices(); imguiPass.init(window.GetHandle(), gfxDevice); resources.init(gfxDevice); renderer.init(gfxDevice, resources, params.renderSize); - InputManager::GetInstance().Init(); - AudioSystem::GetInstance().Init(); Time::GetInstance().Update(); customInitStarted = true; @@ -90,6 +88,31 @@ void App::init(const AppParams& params) } } +void App::initDevices() +{ + ZoneScopedN("App::initDevices"); + auto& jobs = JobSystem::GetInstance(); + auto gfxInit = jobs.Async([this] { gfxDevice.init(window.GetHandle(), m_params.appName, false); }); + auto audioInit = jobs.Async([] { AudioSystem::GetInstance().Init(); }); + + // SDL wants its subsystems initialised on the main thread + std::exception_ptr inputError; + try { + InputManager::GetInstance().Init(); + } catch (...) { + inputError = std::current_exception(); + } + + // Wait for both before rethrowing anything, cleanup must not race a running init + gfxInit.wait(); + audioInit.wait(); + if (inputError) { + std::rethrow_exception(inputError); + } + gfxInit.get(); + audioInit.get(); +} + void App::attachLayer(std::unique_ptr newLayer) { if (layer) { diff --git a/destrum/src/Serialization/SceneSerializer.cpp b/destrum/src/Serialization/SceneSerializer.cpp index f0cbc45..03db435 100644 --- a/destrum/src/Serialization/SceneSerializer.cpp +++ b/destrum/src/Serialization/SceneSerializer.cpp @@ -15,6 +15,7 @@ #include #include #include +#include using json = nlohmann::json; @@ -349,7 +350,5 @@ bool SceneSerializer::LoadFromJson(Scene& scene, const json& root) { std::future SceneSerializer::LoadSceneFileAsync( const std::filesystem::path& path) { - return std::async(std::launch::async, [path]() { - return LoadSceneFile(path); - }); + return JobSystem::GetInstance().Async([path] { return LoadSceneFile(path); }); } diff --git a/editor/src/AssetBrowser.cpp b/editor/src/AssetBrowser.cpp index 5f11cc6..c38627a 100644 --- a/editor/src/AssetBrowser.cpp +++ b/editor/src/AssetBrowser.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace { @@ -219,7 +220,7 @@ void AssetBrowser::StartCook() return; } - m_Cook = std::async(std::launch::async, [commands = std::move(commands)] { + m_Cook = JobSystem::GetInstance().Async([commands = std::move(commands)] { bool succeeded = true; for (const std::string& command : commands) { succeeded = std::system(command.c_str()) == 0 && succeeded; diff --git a/editor/src/ShaderHotReload.cpp b/editor/src/ShaderHotReload.cpp index 82988e8..261935f 100644 --- a/editor/src/ShaderHotReload.cpp +++ b/editor/src/ShaderHotReload.cpp @@ -6,6 +6,8 @@ #include +#include + namespace { constexpr float PollInterval = 0.5f; @@ -48,7 +50,7 @@ void ShaderHotReload::StartCook() // cmd.exe strips the outer quotes, the inner ones keep paths with spaces intact const std::string command = "\"\"" + m_Cooker.string() + "\" --input \"" + m_SourceRoot.string() + "\" --output \"" + m_RuntimeRoot.string() + "\"\""; - m_Cook = std::async(std::launch::async, [command] { return std::system(command.c_str()); }); + m_Cook = JobSystem::GetInstance().Async([command] { return std::system(command.c_str()); }); spdlog::info("Shader sources changed, cooking engine assets"); } diff --git a/tests/jobs_tests.cpp b/tests/jobs_tests.cpp index e11b775..91256e7 100644 --- a/tests/jobs_tests.cpp +++ b/tests/jobs_tests.cpp @@ -1,9 +1,12 @@ #include #include +#include +#include #include #include #include +#include #include #include "TestUtils.h" @@ -143,6 +146,21 @@ namespace { check(!Jobs().IsRunning(), "job system should be stopped"); } + void testLoadSceneFileAsync() + { + Jobs().Init(2); + const auto path = std::filesystem::temp_directory_path() / "destrum_async_scene.json"; + std::ofstream{path} << R"({"version":2,"name":"async","objects":[]})"; + + auto good = SceneSerializer::LoadSceneFileAsync(path); + auto missing = SceneSerializer::LoadSceneFileAsync(path.parent_path() / "destrum_no_such_scene.json"); + check(good.get().success, "async scene load should parse a valid file"); + check(!missing.get().success, "async scene load should report a missing file"); + + std::filesystem::remove(path); + Jobs().Shutdown(); + } + void testRunsInlineWhenStopped() { check(!Jobs().IsRunning(), "job system should start stopped"); @@ -164,4 +182,5 @@ void RunJobsTests() testAsyncRethrows(); testMainThreadQueue(); testShutdownDrainsPendingJobs(); + testLoadSceneFileAsync(); }