diff --git a/src/xenia/debug/ui/debug_window.cc b/src/xenia/debug/ui/debug_window.cc index 5711de711..c87378606 100644 --- a/src/xenia/debug/ui/debug_window.cc +++ b/src/xenia/debug/ui/debug_window.cc @@ -31,7 +31,7 @@ #include "xenia/gpu/graphics_system.h" #include "xenia/kernel/xmodule.h" #include "xenia/kernel/xthread.h" -#include "xenia/ui/gl/gl_context.h" +#include "xenia/ui/graphics_provider.h" #include "xenia/ui/imgui_drawer.h" DEFINE_bool(imgui_debug, false, "Show ImGui debugging tools."); @@ -108,16 +108,9 @@ bool DebugWindow::Initialize() { window_->Resize(1500, 1000); - // If there exists a display window we need to share resources with it. - xe::ui::gl::GLContext* parent_context = nullptr; - if (emulator_->display_window()) { - parent_context = reinterpret_cast( - emulator_->display_window()->context()); - } - - // Create the GL context used for drawing. - auto context = xe::ui::gl::GLContext::Create(window_.get(), parent_context); - window_->set_context(std::move(context)); + // Create the graphics context used for drawing. + auto provider = emulator_->display_window()->context()->provider(); + window_->set_context(provider->CreateContext(window_.get())); // Setup ImGui. imgui_drawer_ = std::make_unique(window_.get()); @@ -133,8 +126,6 @@ bool DebugWindow::Initialize() { void DebugWindow::DrawFrame() { xe::ui::GraphicsContextLock lock(window_->context()); - glClearColor(0.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); auto& io = ImGui::GetIO(); auto current_tick_count = Clock::QueryHostTickCount(); @@ -310,7 +301,8 @@ void DebugWindow::DrawToolbar() { if (thread_info == state_.thread_info) { current_thread_index = i; } - thread_combo.Append(thread_info->thread->name()); + thread_combo.Append(thread_info->thread ? thread_info->thread->name() + : "(zombie)"); thread_combo.Append(static_cast(0)); ++i; } @@ -1021,6 +1013,7 @@ void DebugWindow::DrawThreadsPane() { auto thread_info = cache_.thread_execution_infos[i]; auto thread = thread_info->thread; bool is_current_thread = thread == state_.thread; + assert_not_null(thread); // TODO(benvanik): zombie thread states. if (is_current_thread && state_.has_changed_thread) { ImGui::SetScrollHere(); state_.has_changed_thread = false; diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 10029702a..79a7f1a1b 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -131,10 +131,6 @@ X_STATUS Emulator::Setup( if (!graphics_system_) { return X_STATUS_NOT_IMPLEMENTED; } - display_window_->loop()->PostSynchronous([this]() { - display_window_->set_context( - graphics_system_->CreateContext(display_window_)); - }); // Initialize the HID. input_system_ = std::make_unique(display_window_); diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.cc b/src/xenia/gpu/gl4/gl4_graphics_system.cc index 0279541fb..8632f5729 100644 --- a/src/xenia/gpu/gl4/gl4_graphics_system.cc +++ b/src/xenia/gpu/gl4/gl4_graphics_system.cc @@ -18,27 +18,23 @@ #include "xenia/gpu/gl4/gl4_command_processor.h" #include "xenia/gpu/gl4/gl4_gpu_flags.h" #include "xenia/gpu/gpu_flags.h" +#include "xenia/ui/gl/gl_provider.h" #include "xenia/ui/window.h" namespace xe { namespace gpu { namespace gl4 { -std::unique_ptr GL4GraphicsSystem::CreateContext( - ui::Window* target_window) { - // Setup the GL control that actually does the drawing. - // We run here in the loop and only touch it (and its context) on this - // thread. That means some sync-fu when we want to swap. - return xe::ui::gl::GLContext::Create(target_window); -} - -GL4GraphicsSystem::GL4GraphicsSystem() : GraphicsSystem() {} +GL4GraphicsSystem::GL4GraphicsSystem() = default; GL4GraphicsSystem::~GL4GraphicsSystem() = default; X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, ui::Window* target_window) { + // Must create the provider so we can create contexts. + provider_ = xe::ui::gl::GLProvider::Create(target_window); + auto result = GraphicsSystem::Setup(processor, kernel_state, target_window); if (result) { return result; diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.h b/src/xenia/gpu/gl4/gl4_graphics_system.h index cd712040d..3050cc4b4 100644 --- a/src/xenia/gpu/gl4/gl4_graphics_system.h +++ b/src/xenia/gpu/gl4/gl4_graphics_system.h @@ -24,9 +24,6 @@ class GL4GraphicsSystem : public GraphicsSystem { GL4GraphicsSystem(); ~GL4GraphicsSystem() override; - std::unique_ptr CreateContext( - ui::Window* target_window) override; - X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, ui::Window* target_window) override; void Shutdown() override; diff --git a/src/xenia/gpu/graphics_system.cc b/src/xenia/gpu/graphics_system.cc index a78560ecd..25b38dced 100644 --- a/src/xenia/gpu/graphics_system.cc +++ b/src/xenia/gpu/graphics_system.cc @@ -16,6 +16,7 @@ #include "xenia/base/threading.h" #include "xenia/gpu/command_processor.h" #include "xenia/gpu/gpu_flags.h" +#include "xenia/ui/graphics_provider.h" #include "xenia/ui/loop.h" namespace xe { @@ -33,14 +34,18 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor, kernel_state_ = kernel_state; target_window_ = target_window; - // Initialize rendering context. + // Initialize display and rendering context. // This must happen on the UI thread. std::unique_ptr processor_context; target_window_->loop()->PostSynchronous([&]() { + // Create the context used for presentation. + assert_null(target_window->context()); + target_window_->set_context(provider_->CreateContext(target_window_)); + // Setup the GL context the command processor will do all its drawing in. // It's shared with the display context so that we can resolve framebuffers // from it. - processor_context = target_window->context()->CreateShared(); + processor_context = provider()->CreateOffscreenContext(); processor_context->ClearCurrent(); }); if (!processor_context) { diff --git a/src/xenia/gpu/graphics_system.h b/src/xenia/gpu/graphics_system.h index 659eed964..19c93aa93 100644 --- a/src/xenia/gpu/graphics_system.h +++ b/src/xenia/gpu/graphics_system.h @@ -34,12 +34,10 @@ class GraphicsSystem { public: virtual ~GraphicsSystem(); - virtual std::unique_ptr CreateContext( - ui::Window* target_window) = 0; - Memory* memory() const { return memory_; } cpu::Processor* processor() const { return processor_; } kernel::KernelState* kernel_state() const { return kernel_state_; } + ui::GraphicsProvider* provider() const { return provider_.get(); } virtual X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, @@ -82,6 +80,7 @@ class GraphicsSystem { cpu::Processor* processor_ = nullptr; kernel::KernelState* kernel_state_ = nullptr; ui::Window* target_window_ = nullptr; + std::unique_ptr provider_; uint32_t interrupt_callback_ = 0; uint32_t interrupt_callback_data_ = 0; diff --git a/src/xenia/hid/hid_demo.cc b/src/xenia/hid/hid_demo.cc index 5cb7e4f9f..04c8455a7 100644 --- a/src/xenia/hid/hid_demo.cc +++ b/src/xenia/hid/hid_demo.cc @@ -18,7 +18,7 @@ #include "xenia/base/platform_win.h" #include "xenia/base/threading.h" #include "xenia/hid/input_system.h" -#include "xenia/ui/gl/gl_context.h" +#include "xenia/ui/gl/gl_provider.h" #include "xenia/ui/imgui_drawer.h" #include "xenia/ui/window.h" @@ -67,9 +67,9 @@ std::vector> CreateInputDrivers( return drivers; } -std::unique_ptr CreateDemoContext( +std::unique_ptr CreateDemoGraphicsProvider( xe::ui::Window* window) { - return xe::ui::gl::GLContext::Create(window); + return xe::ui::gl::GLProvider::Create(window); } void DrawInputStatus(); @@ -97,12 +97,13 @@ int hid_demo_main(const std::vector& args) { window->Resize(600, 500); // Create the graphics context used for drawing and setup the window. - loop->PostSynchronous([&window]() { + std::unique_ptr graphics_provider; + loop->PostSynchronous([&window, &graphics_provider]() { // Create context and give it to the window. // The window will finish initialization wtih the context (loading // resources, etc). - auto context = CreateDemoContext(window.get()); - window->set_context(std::move(context)); + graphics_provider = CreateDemoGraphicsProvider(window.get()); + window->set_context(graphics_provider->CreateContext(window.get())); // Initialize the ImGui renderer we'll use. imgui_drawer_ = std::make_unique(window.get()); @@ -157,6 +158,7 @@ int hid_demo_main(const std::vector& args) { imgui_drawer_.reset(); input_system_.reset(); + loop->PostSynchronous([&graphics_provider]() { graphics_provider.reset(); }); window.reset(); loop.reset(); return 0; diff --git a/src/xenia/ui/gl/gl_context.cc b/src/xenia/ui/gl/gl_context.cc index 4b089c0a9..e59177020 100644 --- a/src/xenia/ui/gl/gl_context.cc +++ b/src/xenia/ui/gl/gl_context.cc @@ -58,24 +58,20 @@ void FatalGLError(std::string error) { "of supported GPUs."); } -std::unique_ptr GLContext::Create(Window* target_window, +std::unique_ptr GLContext::Create(GraphicsProvider* provider, + Window* target_window, GLContext* share_context) { - auto context = std::unique_ptr(new GLContext(target_window)); - if (!context->Initialize(target_window, share_context)) { + auto context = + std::unique_ptr(new GLContext(provider, target_window)); + if (!context->Initialize(share_context)) { return nullptr; } context->AssertExtensionsPresent(); return context; } -GLContext::GLContext(Window* target_window) : GraphicsContext(target_window) { - glew_context_.reset(new GLEWContext()); - wglew_context_.reset(new WGLEWContext()); -} - -GLContext::GLContext(Window* target_window, HGLRC glrc) - : GraphicsContext(target_window), glrc_(glrc) { - dc_ = GetDC(HWND(target_window_->native_handle())); +GLContext::GLContext(GraphicsProvider* provider, Window* target_window) + : GraphicsContext(provider, target_window) { glew_context_.reset(new GLEWContext()); wglew_context_.reset(new WGLEWContext()); } @@ -93,8 +89,7 @@ GLContext::~GLContext() { } } -bool GLContext::Initialize(Window* target_window, GLContext* share_context) { - target_window_ = target_window; +bool GLContext::Initialize(GLContext* share_context) { dc_ = GetDC(HWND(target_window_->native_handle())); PIXELFORMATDESCRIPTOR pfd = {0}; @@ -193,12 +188,13 @@ bool GLContext::Initialize(Window* target_window, GLContext* share_context) { return true; } -std::unique_ptr GLContext::CreateShared() { - assert_not_null(glrc_); +std::unique_ptr GLContext::CreateOffscreen( + GraphicsProvider* provider, GLContext* parent_context) { + assert_not_null(parent_context->glrc_); HGLRC new_glrc = nullptr; { - GraphicsContextLock context_lock(this); + GraphicsContextLock context_lock(parent_context); int context_flags = 0; if (FLAGS_gl_debug) { @@ -214,15 +210,19 @@ std::unique_ptr GLContext::CreateShared() { WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, 0}; - new_glrc = wglCreateContextAttribsARB(dc_, glrc_, attrib_list); + new_glrc = wglCreateContextAttribsARB(parent_context->dc_, + parent_context->glrc_, attrib_list); if (!new_glrc) { FatalGLError("Could not create shared context."); return nullptr; } } - auto new_context = - std::unique_ptr(new GLContext(target_window_, new_glrc)); + auto new_context = std::unique_ptr( + new GLContext(provider, parent_context->target_window_)); + new_context->glrc_ = new_glrc; + new_context->dc_ = + GetDC(HWND(parent_context->target_window_->native_handle())); if (!new_context->MakeCurrent()) { FatalGLError("Could not make new GL context current."); return nullptr; @@ -244,18 +244,16 @@ std::unique_ptr GLContext::CreateShared() { return nullptr; } - SetupDebugging(); + new_context->SetupDebugging(); if (!new_context->blitter_.Initialize()) { FatalGLError("Unable to initialize blitter on shared context."); return nullptr; } - new_context->immediate_drawer_ = std::make_unique(this); - new_context->ClearCurrent(); - return std::unique_ptr(new_context.release()); + return new_context; } void GLContext::AssertExtensionsPresent() { diff --git a/src/xenia/ui/gl/gl_context.h b/src/xenia/ui/gl/gl_context.h index a1596053d..81ff5903f 100644 --- a/src/xenia/ui/gl/gl_context.h +++ b/src/xenia/ui/gl/gl_context.h @@ -29,18 +29,14 @@ namespace ui { namespace gl { class GLImmediateDrawer; +class GLProvider; class GLContext : public GraphicsContext { public: - static std::unique_ptr Create(Window* target_window, - GLContext* share_context = nullptr); - ~GLContext() override; HDC dc() const { return dc_; } - std::unique_ptr CreateShared() override; - ImmediateDrawer* immediate_drawer() override; bool is_current() override; @@ -53,10 +49,18 @@ class GLContext : public GraphicsContext { Blitter* blitter() { return &blitter_; } private: - explicit GLContext(Window* target_window); - GLContext(Window* target_window, HGLRC glrc); + friend class GLProvider; - bool Initialize(Window* target_window, GLContext* share_context); + static std::unique_ptr Create(GraphicsProvider* provider, + Window* target_window, + GLContext* share_context = nullptr); + static std::unique_ptr CreateOffscreen(GraphicsProvider* provider, + GLContext* parent_context); + + private: + GLContext(GraphicsProvider* provider, Window* target_window); + + bool Initialize(GLContext* share_context); void AssertExtensionsPresent(); void SetupDebugging(); diff --git a/src/xenia/ui/gl/gl_provider.cc b/src/xenia/ui/gl/gl_provider.cc new file mode 100644 index 000000000..b53bc9696 --- /dev/null +++ b/src/xenia/ui/gl/gl_provider.cc @@ -0,0 +1,49 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/gl/gl_provider.h" + +#include "xenia/ui/gl/gl_context.h" +#include "xenia/ui/window.h" + +namespace xe { +namespace ui { +namespace gl { + +std::unique_ptr GLProvider::Create(Window* main_window) { + std::unique_ptr provider(new GLProvider(main_window)); + + // + + return std::unique_ptr(provider.release()); +} + +GLProvider::GLProvider(Window* main_window) : GraphicsProvider(main_window) {} + +GLProvider::~GLProvider() = default; + +std::unique_ptr GLProvider::CreateContext( + Window* target_window) { + auto share_context = main_window_->context(); + return std::unique_ptr( + GLContext::Create(this, target_window, + static_cast(share_context)) + .release()); +} + +std::unique_ptr GLProvider::CreateOffscreenContext() { + auto share_context = main_window_->context(); + return std::unique_ptr( + GLContext::CreateOffscreen(this, static_cast(share_context)) + .release()); +} + +} // namespace gl +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/gl/gl_provider.h b/src/xenia/ui/gl/gl_provider.h new file mode 100644 index 000000000..5c3303f32 --- /dev/null +++ b/src/xenia/ui/gl/gl_provider.h @@ -0,0 +1,40 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_GL_GL_PROVIDER_H_ +#define XENIA_UI_GL_GL_PROVIDER_H_ + +#include + +#include "xenia/ui/graphics_provider.h" + +namespace xe { +namespace ui { +namespace gl { + +class GLProvider : public GraphicsProvider { + public: + ~GLProvider() override; + + static std::unique_ptr Create(Window* main_window); + + std::unique_ptr CreateContext( + Window* target_window) override; + + std::unique_ptr CreateOffscreenContext() override; + + protected: + explicit GLProvider(Window* main_window); +}; + +} // namespace gl +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_GL_GL_PROVIDER_H_ diff --git a/src/xenia/ui/gl/gl_window_demo.cc b/src/xenia/ui/gl/gl_window_demo.cc index 7db76602a..6b39ab40f 100644 --- a/src/xenia/ui/gl/gl_window_demo.cc +++ b/src/xenia/ui/gl/gl_window_demo.cc @@ -11,7 +11,7 @@ #include #include "xenia/base/main.h" -#include "xenia/ui/gl/gl_context.h" +#include "xenia/ui/gl/gl_provider.h" #include "xenia/ui/window.h" namespace xe { @@ -19,8 +19,8 @@ namespace ui { int window_demo_main(const std::vector& args); -std::unique_ptr CreateDemoContext(Window* window) { - return xe::ui::gl::GLContext::Create(window); +std::unique_ptr CreateDemoGraphicsProvider(Window* window) { + return xe::ui::gl::GLProvider::Create(window); } } // namespace ui diff --git a/src/xenia/ui/graphics_context.cc b/src/xenia/ui/graphics_context.cc index 99fe9d91f..6fff1f7cb 100644 --- a/src/xenia/ui/graphics_context.cc +++ b/src/xenia/ui/graphics_context.cc @@ -9,11 +9,14 @@ #include "xenia/ui/graphics_context.h" +#include "xenia/ui/graphics_provider.h" + namespace xe { namespace ui { -GraphicsContext::GraphicsContext(Window* target_window) - : target_window_(target_window) {} +GraphicsContext::GraphicsContext(GraphicsProvider* provider, + Window* target_window) + : provider_(provider), target_window_(target_window) {} GraphicsContext::~GraphicsContext() = default; diff --git a/src/xenia/ui/graphics_context.h b/src/xenia/ui/graphics_context.h index 87d89d3cd..8c02a7b65 100644 --- a/src/xenia/ui/graphics_context.h +++ b/src/xenia/ui/graphics_context.h @@ -15,6 +15,7 @@ namespace xe { namespace ui { +class GraphicsProvider; class ImmediateDrawer; class Window; @@ -22,9 +23,9 @@ class GraphicsContext { public: virtual ~GraphicsContext(); + GraphicsProvider* provider() const { return provider_; } Window* target_window() const { return target_window_; } - - virtual std::unique_ptr CreateShared() = 0; + bool is_offscreen() { return immediate_drawer() == nullptr; } virtual ImmediateDrawer* immediate_drawer() = 0; @@ -36,8 +37,9 @@ class GraphicsContext { virtual void EndSwap() = 0; protected: - explicit GraphicsContext(Window* target_window); + explicit GraphicsContext(GraphicsProvider* provider, Window* target_window); + GraphicsProvider* provider_ = nullptr; Window* target_window_ = nullptr; }; diff --git a/src/xenia/ui/graphics_provider.h b/src/xenia/ui/graphics_provider.h new file mode 100644 index 000000000..b0ebc9d3a --- /dev/null +++ b/src/xenia/ui/graphics_provider.h @@ -0,0 +1,48 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_GRAPHICS_PROVIDER_H_ +#define XENIA_UI_GRAPHICS_PROVIDER_H_ + +#include + +namespace xe { +namespace ui { + +class GraphicsContext; +class Window; + +// Factory for graphics contexts. +// All contexts created by the same provider will be able to share resources +// according to the rules of the backing graphics API. +class GraphicsProvider { + public: + virtual ~GraphicsProvider() = default; + + // The 'main' window of an application, used to query provider information. + Window* main_window() const { return main_window_; } + + // Creates a new graphics context and swapchain for presenting to a window. + virtual std::unique_ptr CreateContext( + Window* target_window) = 0; + + // Creates a new offscreen graphics context without a swapchain or immediate + // drawer. + virtual std::unique_ptr CreateOffscreenContext() = 0; + + protected: + explicit GraphicsProvider(Window* main_window) : main_window_(main_window) {} + + Window* main_window_ = nullptr; +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_GRAPHICS_PROVIDER_H_ diff --git a/src/xenia/ui/window_demo.cc b/src/xenia/ui/window_demo.cc index 61f07d0bf..b81fabbe1 100644 --- a/src/xenia/ui/window_demo.cc +++ b/src/xenia/ui/window_demo.cc @@ -19,6 +19,7 @@ #include "xenia/base/platform_win.h" #include "xenia/base/profiling.h" #include "xenia/base/threading.h" +#include "xenia/ui/graphics_provider.h" #include "xenia/ui/imgui_drawer.h" #include "xenia/ui/window.h" @@ -26,9 +27,7 @@ namespace xe { namespace ui { // Implemented in one of the window_*_demo.cc files under a subdir. -std::unique_ptr CreateDemoContext(Window* window); - -std::unique_ptr imgui_drawer_; +std::unique_ptr CreateDemoGraphicsProvider(Window* window); int window_demo_main(const std::vector& args) { Profiler::Initialize(); @@ -45,13 +44,6 @@ int window_demo_main(const std::vector& args) { return; } }); - window->on_closed.AddListener([&loop](xe::ui::UIEvent* e) { - loop->Quit(); - XELOGI("User-initiated death!"); - imgui_drawer_.reset(); - exit(1); - }); - loop->on_quit.AddListener([&window](xe::ui::UIEvent* e) { window.reset(); }); // Main menu. auto main_menu = MenuItem::Create(MenuItem::Type::kNormal); @@ -78,25 +70,37 @@ int window_demo_main(const std::vector& args) { window->Resize(1920, 1200); // Create the graphics context used for drawing and setup the window. - loop->PostSynchronous([&window]() { - // Create context and give it to the window. + std::unique_ptr graphics_provider; + std::unique_ptr imgui_drawer; + loop->PostSynchronous([&window, &graphics_provider, &imgui_drawer]() { + // Create graphics provider and an initial context for the window. // The window will finish initialization wtih the context (loading // resources, etc). - auto context = CreateDemoContext(window.get()); - window->set_context(std::move(context)); + graphics_provider = CreateDemoGraphicsProvider(window.get()); + window->set_context(graphics_provider->CreateContext(window.get())); // Setup the profiler display. GraphicsContextLock context_lock(window->context()); Profiler::set_window(window.get()); // Initialize the ImGui renderer we'll use. - imgui_drawer_ = std::make_unique(window.get()); - imgui_drawer_->SetupDefaultInput(); + imgui_drawer = std::make_unique(window.get()); + imgui_drawer->SetupDefaultInput(); // Show the elemental-forms debug UI so we can see it working. el::util::ShowDebugInfoSettingsForm(window->root_element()); }); + window->on_closed.AddListener( + [&loop, &graphics_provider, &imgui_drawer](xe::ui::UIEvent* e) { + loop->Quit(); + XELOGI("User-initiated death!"); + imgui_drawer.reset(); + graphics_provider.reset(); + exit(1); + }); + loop->on_quit.AddListener([&window](xe::ui::UIEvent* e) { window.reset(); }); + window->on_key_down.AddListener([](xe::ui::KeyEvent* e) { switch (e->key_code()) { case 0x72: { // F3 @@ -130,8 +134,9 @@ int window_demo_main(const std::vector& args) { // Wait until we are exited. loop->AwaitQuit(); - imgui_drawer_.reset(); + imgui_drawer.reset(); + loop->PostSynchronous([&graphics_provider]() { graphics_provider.reset(); }); window.reset(); loop.reset(); Profiler::Dump();