77#include < facade/engine/scene_renderer.hpp>
88#include < facade/glfw/glfw_wsi.hpp>
99#include < facade/render/renderer.hpp>
10+ #include < facade/scene/gltf_loader.hpp>
1011#include < facade/util/data_provider.hpp>
1112#include < facade/util/env.hpp>
1213#include < facade/util/error.hpp>
1314#include < facade/util/logger.hpp>
15+ #include < facade/util/thread_pool.hpp>
1416#include < facade/vk/cmd.hpp>
1517#include < facade/vk/vk.hpp>
1618#include < glm/gtc/color_space.hpp>
1719#include < glm/mat4x4.hpp>
1820#include < filesystem>
19- #include < future>
2021
2122namespace facade {
2223namespace fs = std::filesystem;
@@ -159,10 +160,10 @@ struct RenderWindow {
159160 renderer (gfx, this ->window, gui.get(), Renderer::CreateInfo{command_buffers_v, msaa}), gui(std::move(gui)) {}
160161};
161162
162- bool load_gltf (Scene& out_scene, char const * path, AtomicLoadStatus* out_status) {
163+ bool load_gltf (Scene& out_scene, char const * path, AtomicLoadStatus& out_status, ThreadPool* thread_pool ) {
163164 auto const provider = FileDataProvider::mount_parent_dir (path);
164165 auto json = dj::Json::from_file (path);
165- return out_scene. load_gltf (json, provider, out_status );
166+ return Scene::GltfLoader{ out_scene, out_status} (json, provider, thread_pool );
166167}
167168
168169template <typename T>
@@ -195,15 +196,17 @@ struct Engine::Impl {
195196
196197 std::uint8_t msaa;
197198
199+ ThreadPool thread_pool{};
198200 std::mutex mutex{};
199201
200202 struct {
201203 LoadRequest request{};
202204 UniqueTask<void ()> callback{};
203205 } load{};
204206
205- Impl (UniqueWin window, std::uint8_t msaa, bool validation)
206- : window(std::move(window), std::make_unique<DearImGui>(), msaa, validation), renderer(this ->window.gfx), scene(this ->window.gfx), msaa(msaa) {
207+ Impl (UniqueWin window, std::uint8_t msaa, bool validation, std::optional<std::uint32_t > thread_count)
208+ : window(std::move(window), std::make_unique<DearImGui>(), msaa, validation), renderer(this ->window.gfx), scene(this ->window.gfx), msaa(msaa),
209+ thread_pool (thread_count) {
207210 s_instance = this ;
208211 load.request .status .reset ();
209212 }
@@ -225,7 +228,8 @@ bool Engine::is_instance_active() { return s_instance != nullptr; }
225228
226229Engine::Engine (CreateInfo const & info) noexcept (false ) {
227230 if (s_instance) { throw Error{" Engine: active instance exists and has not been destroyed" }; }
228- m_impl = std::make_unique<Impl>(make_window (info.extent , info.title ), info.desired_msaa , determine_validation (info.validation ));
231+ if (info.force_thread_count ) { logger::info (" [Engine] Forcing load thread count: [{}]" , *info.force_thread_count ); }
232+ m_impl = std::make_unique<Impl>(make_window (info.extent , info.title ), info.desired_msaa , determine_validation (info.validation ), info.force_thread_count );
229233 if (info.auto_show ) { show (true ); }
230234}
231235
@@ -269,6 +273,18 @@ bool Engine::load_async(std::string gltf_json_path, UniqueTask<void()> on_loaded
269273 logger::error (" [Engine] Invalid GLTF JSON path: [{}]" , gltf_json_path);
270274 return false ;
271275 }
276+
277+ // ensure thread pool queue has at least one worker thread, else load on this thread
278+ if (m_impl->thread_pool .thread_count () == 0 ) {
279+ auto const start = time::since_start ();
280+ if (!load_gltf (m_impl->scene , gltf_json_path.c_str (), m_impl->load .request .status , nullptr )) {
281+ logger::error (" [Engine] Failed to load GLTF: [{}]" , gltf_json_path);
282+ return false ;
283+ }
284+ logger::info (" ...GLTF [{}] loaded in [{:.2f}s]" , env::to_filename (gltf_json_path), time::since_start () - start);
285+ return true ;
286+ }
287+
272288 // shared state will need to be accessed, lock the mutex
273289 auto lock = std::scoped_lock{m_impl->mutex };
274290 if (m_impl->load .request .future .valid ()) {
@@ -284,14 +300,16 @@ bool Engine::load_async(std::string gltf_json_path, UniqueTask<void()> on_loaded
284300 m_impl->load .request .path = std::move (gltf_json_path);
285301 m_impl->load .request .status .reset ();
286302 m_impl->load .request .start_time = time::since_start ();
287- auto func = [path = m_impl->load .request .path , gfx = m_impl->window .gfx , status = &m_impl->load .request .status ] {
303+ // if thread pool queue has only one worker thread, can't dispatch tasks from within a task and then wait for them (deadlock)
304+ auto * tp = m_impl->thread_pool .thread_count () > 1 ? &m_impl->thread_pool : nullptr ;
305+ auto func = [path = m_impl->load .request .path , gfx = m_impl->window .gfx , status = &m_impl->load .request .status , tp] {
288306 auto scene = Scene{gfx};
289- if (!load_gltf (scene, path.c_str (), status)) { logger::error (" [Engine] Failed to load GLTF: [{}]" , path); }
307+ if (!load_gltf (scene, path.c_str (), * status, tp )) { logger::error (" [Engine] Failed to load GLTF: [{}]" , path); }
290308 // return the scene even on failure, it will be empty but valid
291309 return scene;
292310 };
293311 // store future
294- m_impl->load .request .future = std::async (std::launch::async, func);
312+ m_impl->load .request .future = m_impl-> thread_pool . enqueue ( func);
295313 return true ;
296314}
297315
@@ -301,6 +319,8 @@ LoadStatus Engine::load_status() const {
301319 return {.stage = status.stage .load (), .total = status.total , .done = status.done };
302320}
303321
322+ std::size_t Engine::load_thread_count () const { return m_impl->thread_pool .thread_count (); }
323+
304324Scene& Engine::scene () const { return m_impl->scene ; }
305325GLFWwindow* Engine::window () const { return m_impl->window .window .get (); }
306326Glfw::State const & Engine::state () const { return m_impl->window .window .get ().state (); }
0 commit comments