diff --git a/src/core/core.cpp b/src/core/core.cpp
index 2b8ec3ca791cc0d2afe4a72b09fa5d5d4fdeaa66..eb300eef73f49b40fbe59bdbefcf25f53fbc941a 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -3,9 +3,7 @@
 // Refer to the license.txt file included.
 
 #include <array>
-#include <map>
 #include <memory>
-#include <thread>
 #include <utility>
 
 #include "common/file_util.h"
@@ -38,8 +36,6 @@
 #include "frontend/applets/software_keyboard.h"
 #include "frontend/applets/web_browser.h"
 #include "video_core/debug_utils/debug_utils.h"
-#include "video_core/gpu_asynch.h"
-#include "video_core/gpu_synch.h"
 #include "video_core/renderer_base.h"
 #include "video_core/video_core.h"
 
@@ -135,13 +131,9 @@ struct System::Impl {
             return ResultStatus::ErrorVideoCore;
         }
 
-        is_powered_on = true;
+        gpu_core = VideoCore::CreateGPU(system);
 
-        if (Settings::values.use_asynchronous_gpu_emulation) {
-            gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
-        } else {
-            gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
-        }
+        is_powered_on = true;
 
         LOG_DEBUG(Core, "Initialized OK");
 
@@ -188,7 +180,8 @@ struct System::Impl {
         }
 
         // Main process has been loaded and been made current.
-        // Begin CPU execution.
+        // Begin GPU and CPU execution.
+        gpu_core->Start();
         cpu_core_manager.StartThreads();
 
         status = ResultStatus::Success;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index de30ea3546537189f74734f20e8a106951a05570..fe662892303f30f7be860f1b528e079f74f03b62 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -207,6 +207,11 @@ public:
         };
     } regs{};
 
+    /// Performs any additional setup necessary in order to begin GPU emulation.
+    /// This can be used to launch any necessary threads and register any necessary
+    /// core timing events.
+    virtual void Start() = 0;
+
     /// Push GPU command entries to be processed
     virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
 
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index db507cf04f790103d4443c20faa21df5f92bc074..d4e2553a954e80735dbf46cfbd1bfa8c98977ea2 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -9,10 +9,14 @@
 namespace VideoCommon {
 
 GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
-    : Tegra::GPU(system, renderer), gpu_thread{system, renderer, *dma_pusher} {}
+    : GPU(system, renderer), gpu_thread{system} {}
 
 GPUAsynch::~GPUAsynch() = default;
 
+void GPUAsynch::Start() {
+    gpu_thread.StartThread(renderer, *dma_pusher);
+}
+
 void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
     gpu_thread.SubmitList(std::move(entries));
 }
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 1dcc61a6cb0e3b54a360b7cfb225d60c192e2599..30be74cba875f23777e1fb723f62b381643e9fcc 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -13,16 +13,13 @@ class RendererBase;
 
 namespace VideoCommon {
 
-namespace GPUThread {
-class ThreadManager;
-} // namespace GPUThread
-
 /// Implementation of GPU interface that runs the GPU asynchronously
 class GPUAsynch : public Tegra::GPU {
 public:
     explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
     ~GPUAsynch() override;
 
+    void Start() override;
     void PushGPUEntries(Tegra::CommandList&& entries) override;
     void SwapBuffers(
         std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index 2cfc900ed7ad220d7b4304991289022eff8f156e..45e43b1dc54bcd8f20fa48bdf62203bca2e667e6 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -8,10 +8,12 @@
 namespace VideoCommon {
 
 GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
-    : Tegra::GPU(system, renderer) {}
+    : GPU(system, renderer) {}
 
 GPUSynch::~GPUSynch() = default;
 
+void GPUSynch::Start() {}
+
 void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
     dma_pusher->Push(std::move(entries));
     dma_pusher->DispatchCalls();
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 766b5631c7852802fd67b74b6dc7b50069573574..3031fcf725bff9275dcbefa57b85fd81bcb81ffc 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -18,6 +18,7 @@ public:
     explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
     ~GPUSynch() override;
 
+    void Start() override;
     void PushGPUEntries(Tegra::CommandList&& entries) override;
     void SwapBuffers(
         std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index cc56cf4673be605bbe0f70e7380b1fbbf79492e3..c9a2077de4548885827f04aa108efd143c965dfb 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -55,19 +55,24 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
     }
 }
 
-ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
-                             Tegra::DmaPusher& dma_pusher)
-    : system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} {
-    synchronization_event = system.CoreTiming().RegisterEvent(
-        "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
-}
+ThreadManager::ThreadManager(Core::System& system) : system{system} {}
 
 ThreadManager::~ThreadManager() {
+    if (!thread.joinable()) {
+        return;
+    }
+
     // Notify GPU thread that a shutdown is pending
     PushCommand(EndProcessingCommand());
     thread.join();
 }
 
+void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) {
+    thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)};
+    synchronization_event = system.CoreTiming().RegisterEvent(
+        "GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
+}
+
 void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
     const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
     const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 62bcea5bb2c97443dcd091626535ec98629b5849..cc14527c79c8ff260f77708831d25a04f80d1182 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -138,10 +138,12 @@ struct SynchState final {
 /// Class used to manage the GPU thread
 class ThreadManager final {
 public:
-    explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
-                           Tegra::DmaPusher& dma_pusher);
+    explicit ThreadManager(Core::System& system);
     ~ThreadManager();
 
+    /// Creates and starts the GPU thread.
+    void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
+
     /// Push GPU command entries to be processed
     void SubmitList(Tegra::CommandList&& entries);
 
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index cb82ecf3f6b61b64dba4f4303b5daf37f7df5479..60cda0ca3e387f0bbe717499917bb576f9fdf153 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -5,6 +5,8 @@
 #include <memory>
 #include "core/core.h"
 #include "core/settings.h"
+#include "video_core/gpu_asynch.h"
+#include "video_core/gpu_synch.h"
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_opengl/renderer_opengl.h"
 #include "video_core/video_core.h"
@@ -16,6 +18,14 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
     return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
 }
 
+std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
+    if (Settings::values.use_asynchronous_gpu_emulation) {
+        return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer());
+    }
+
+    return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer());
+}
+
 u16 GetResolutionScaleFactor(const RendererBase& renderer) {
     return static_cast<u16>(
         Settings::values.resolution_factor
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 3c583f1954d1962c6bc9fdb34797db509bebcaf0..b8e0ac3728f7b5145f35ff1eae5bd7775dc0e658 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -14,6 +14,10 @@ namespace Core::Frontend {
 class EmuWindow;
 }
 
+namespace Tegra {
+class GPU;
+}
+
 namespace VideoCore {
 
 class RendererBase;
@@ -27,6 +31,9 @@ class RendererBase;
 std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
                                              Core::System& system);
 
+/// Creates an emulated GPU instance using the given system context.
+std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system);
+
 u16 GetResolutionScaleFactor(const RendererBase& renderer);
 
 } // namespace VideoCore