diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index d5032b4328..ddc62bc975 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -106,8 +106,14 @@ RendererVulkan::~RendererVulkan() {
 }
 
 void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
+    render_window.PollEvents();
+
+    if (!framebuffer) {
+        return;
+    }
+
     const auto& layout = render_window.GetFramebufferLayout();
-    if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
+    if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
         const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
         const bool use_accelerated =
             rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
@@ -128,13 +134,16 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
             blit_screen->Recreate();
         }
 
-        render_window.SwapBuffers();
         rasterizer->TickFrame();
     }
 
     render_window.PollEvents();
 }
 
+void RendererVulkan::TryPresent(int /*timeout_ms*/) {
+    // TODO (bunnei): ImplementMe
+}
+
 bool RendererVulkan::Init() {
     PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
     render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
@@ -262,4 +271,4 @@ void RendererVulkan::Report() const {
     telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
 }
 
-} // namespace Vulkan
\ No newline at end of file
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index a472c5dc93..f513397f0e 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -36,14 +36,10 @@ public:
     explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system);
     ~RendererVulkan() override;
 
-    /// Swap buffers (render frame)
-    void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
-
-    /// Initialize the renderer
     bool Init() override;
-
-    /// Shutdown the renderer
     void ShutDown() override;
+    void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
+    void TryPresent(int timeout_ms) override;
 
 private:
     std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 99e0ac61e4..704c5ecdd6 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -147,88 +147,129 @@ private:
     QOffscreenSurface* surface;
 };
 
-OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
-    : QWindow(parent), event_handler(event_handler),
-      context(new QOpenGLContext(shared_context->parent())) {
+class ChildRenderWindow : public QWindow {
+public:
+    ChildRenderWindow(QWindow* parent, QWidget* event_handler)
+        : QWindow{parent}, event_handler{event_handler} {}
 
-    // disable vsync for any shared contexts
-    auto format = shared_context->format();
-    format.setSwapInterval(Settings::values.use_vsync ? 1 : 0);
-    this->setFormat(format);
+    virtual ~ChildRenderWindow() = default;
 
-    context->setShareContext(shared_context);
-    context->setScreen(this->screen());
-    context->setFormat(format);
-    context->create();
+    virtual void Present() = 0;
 
-    setSurfaceType(QWindow::OpenGLSurface);
-
-    // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
-    // WA_DontShowOnScreen, WA_DeleteOnClose
-}
-
-OpenGLWindow::~OpenGLWindow() {
-    context->doneCurrent();
-}
-
-void OpenGLWindow::Present() {
-    if (!isExposed())
-        return;
-
-    context->makeCurrent(this);
-    Core::System::GetInstance().Renderer().TryPresent(100);
-    context->swapBuffers(this);
-    auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
-    f->glFinish();
-    QWindow::requestUpdate();
-}
-
-bool OpenGLWindow::event(QEvent* event) {
-    switch (event->type()) {
-    case QEvent::UpdateRequest:
-        Present();
-        return true;
-    case QEvent::MouseButtonPress:
-    case QEvent::MouseButtonRelease:
-    case QEvent::MouseButtonDblClick:
-    case QEvent::MouseMove:
-    case QEvent::KeyPress:
-    case QEvent::KeyRelease:
-    case QEvent::FocusIn:
-    case QEvent::FocusOut:
-    case QEvent::FocusAboutToChange:
-    case QEvent::Enter:
-    case QEvent::Leave:
-    case QEvent::Wheel:
-    case QEvent::TabletMove:
-    case QEvent::TabletPress:
-    case QEvent::TabletRelease:
-    case QEvent::TabletEnterProximity:
-    case QEvent::TabletLeaveProximity:
-    case QEvent::TouchBegin:
-    case QEvent::TouchUpdate:
-    case QEvent::TouchEnd:
-    case QEvent::InputMethodQuery:
-    case QEvent::TouchCancel:
-        return QCoreApplication::sendEvent(event_handler, event);
-    case QEvent::Drop:
-        GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
-        return true;
-    case QEvent::DragResponse:
-    case QEvent::DragEnter:
-    case QEvent::DragLeave:
-    case QEvent::DragMove:
-        GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
-        return true;
-    default:
-        return QWindow::event(event);
+protected:
+    bool event(QEvent* event) override {
+        switch (event->type()) {
+        case QEvent::UpdateRequest:
+            Present();
+            return true;
+        case QEvent::MouseButtonPress:
+        case QEvent::MouseButtonRelease:
+        case QEvent::MouseButtonDblClick:
+        case QEvent::MouseMove:
+        case QEvent::KeyPress:
+        case QEvent::KeyRelease:
+        case QEvent::FocusIn:
+        case QEvent::FocusOut:
+        case QEvent::FocusAboutToChange:
+        case QEvent::Enter:
+        case QEvent::Leave:
+        case QEvent::Wheel:
+        case QEvent::TabletMove:
+        case QEvent::TabletPress:
+        case QEvent::TabletRelease:
+        case QEvent::TabletEnterProximity:
+        case QEvent::TabletLeaveProximity:
+        case QEvent::TouchBegin:
+        case QEvent::TouchUpdate:
+        case QEvent::TouchEnd:
+        case QEvent::InputMethodQuery:
+        case QEvent::TouchCancel:
+            return QCoreApplication::sendEvent(event_handler, event);
+        case QEvent::Drop:
+            GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
+            return true;
+        case QEvent::DragResponse:
+        case QEvent::DragEnter:
+        case QEvent::DragLeave:
+        case QEvent::DragMove:
+            GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
+            return true;
+        default:
+            return QWindow::event(event);
+        }
     }
-}
 
-void OpenGLWindow::exposeEvent(QExposeEvent* event) {
-    QWindow::requestUpdate();
-    QWindow::exposeEvent(event);
-}
+    void exposeEvent(QExposeEvent* event) override {
+        QWindow::requestUpdate();
+        QWindow::exposeEvent(event);
+    }
+
+private:
+    QWidget* event_handler{};
+};
+
+class OpenGLWindow final : public ChildRenderWindow {
+public:
+    OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
+        : ChildRenderWindow{parent, event_handler},
+          context(new QOpenGLContext(shared_context->parent())) {
+
+        // disable vsync for any shared contexts
+        auto format = shared_context->format();
+        format.setSwapInterval(Settings::values.use_vsync ? 1 : 0);
+        this->setFormat(format);
+
+        context->setShareContext(shared_context);
+        context->setScreen(this->screen());
+        context->setFormat(format);
+        context->create();
+
+        setSurfaceType(QWindow::OpenGLSurface);
+
+        // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
+        // WA_DontShowOnScreen, WA_DeleteOnClose
+    }
+
+    ~OpenGLWindow() override {
+        context->doneCurrent();
+    }
+
+    void Present() override {
+        if (!isExposed()) {
+            return;
+        }
+
+        context->makeCurrent(this);
+        Core::System::GetInstance().Renderer().TryPresent(100);
+        context->swapBuffers(this);
+        auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
+        f->glFinish();
+        QWindow::requestUpdate();
+    }
+
+private:
+    QOpenGLContext* context{};
+};
+
+#ifdef HAS_VULKAN
+class VulkanWindow final : public ChildRenderWindow {
+public:
+    VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance)
+        : ChildRenderWindow{parent, event_handler} {
+        setSurfaceType(QSurface::SurfaceType::VulkanSurface);
+        setVulkanInstance(instance);
+    }
+
+    ~VulkanWindow() override = default;
+
+    void Present() override {
+        // TODO(bunnei): ImplementMe
+    }
+
+private:
+    QWidget* event_handler{};
+};
+#endif
 
 GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
     : QWidget(parent_), emu_thread(emu_thread) {
@@ -251,11 +292,15 @@ GRenderWindow::~GRenderWindow() {
 }
 
 void GRenderWindow::MakeCurrent() {
-    core_context->MakeCurrent();
+    if (core_context) {
+        core_context->MakeCurrent();
+    }
 }
 
 void GRenderWindow::DoneCurrent() {
-    core_context->DoneCurrent();
+    if (core_context) {
+        core_context->DoneCurrent();
+    }
 }
 
 void GRenderWindow::PollEvents() {
@@ -274,7 +319,7 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i
 #ifdef HAS_VULKAN
     const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
     const VkInstance instance_copy = vk_instance->vkInstance();
-    const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child);
+    const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window);
 
     std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
     std::memcpy(instance, &instance_copy, sizeof(instance_copy));
@@ -535,7 +580,6 @@ bool GRenderWindow::InitializeOpenGL() {
     layout()->addWidget(child_widget);
 
     core_context = CreateSharedContext();
-    resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
 
     return true;
 }
@@ -565,7 +609,14 @@ bool GRenderWindow::InitializeVulkan() {
         return false;
     }
 
-    child = new GVKWidgetInternal(this, vk_instance.get());
+    GMainWindow* parent = GetMainWindow();
+    QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
+    child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get());
+    child_window->create();
+    child_widget = createWindowContainer(child_window, this);
+    child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
+    layout()->addWidget(child_widget);
+
     return true;
 #else
     QMessageBox::critical(this, tr("Vulkan not available!"),
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 37bc4f0437..6710a6e7f2 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -27,14 +27,6 @@ class QOpenGLContext;
 class QVulkanInstance;
 #endif
 
-class GWidgetInternal;
-class GGLWidgetInternal;
-class GVKWidgetInternal;
-class GMainWindow;
-class GRenderWindow;
-class QSurface;
-class QOpenGLContext;
-
 namespace VideoCore {
 enum class LoadCallbackStage;
 }
@@ -123,24 +115,6 @@ signals:
     void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
 };
 
-class OpenGLWindow : public QWindow {
-    Q_OBJECT
-public:
-    explicit OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context);
-
-    ~OpenGLWindow();
-
-    void Present();
-
-protected:
-    bool event(QEvent* event) override;
-    void exposeEvent(QExposeEvent* event) override;
-
-private:
-    QOpenGLContext* context;
-    QWidget* event_handler;
-};
-
 class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
     Q_OBJECT
 
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index f5f6675b58..abcc581651 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -160,4 +160,6 @@ bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanc
            }) != layers.end();
 }
 
-void EmuWindow_SDL2_VK::Present() {}
+void EmuWindow_SDL2_VK::Present() {
+    // TODO (bunnei): ImplementMe
+}