From 7003090187e02c8625f4eb7a024ac97f9b0159aa Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sat, 19 Sep 2020 17:15:02 -0300
Subject: [PATCH] renderer_opengl: Remove emulated mailbox presentation

Emulated mailbox presentation was causing performance issues on
Nvidia's OpenGL driver. Remove it.
---
 src/video_core/renderer_base.h                |   5 -
 .../renderer_opengl/renderer_opengl.cpp       | 288 ++----------------
 .../renderer_opengl/renderer_opengl.h         |  16 -
 .../renderer_vulkan/renderer_vulkan.cpp       |   5 -
 .../renderer_vulkan/renderer_vulkan.h         |   1 -
 src/yuzu/bootmanager.cpp                      |  23 +-
 src/yuzu_cmd/emu_window/emu_window_sdl2.cpp   |   5 +-
 src/yuzu_cmd/emu_window/emu_window_sdl2.h     |   9 +-
 .../emu_window/emu_window_sdl2_gl.cpp         |  15 +-
 src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h  |   5 +-
 .../emu_window/emu_window_sdl2_vk.cpp         |   9 +-
 src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h  |   7 +-
 src/yuzu_cmd/yuzu.cpp                         |   7 +-
 13 files changed, 35 insertions(+), 360 deletions(-)

diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 649074acd..5c650808b 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -46,11 +46,6 @@ public:
     /// Finalize rendering the guest frame and draw into the presentation texture
     virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
 
-    /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
-    /// specific implementation)
-    /// Returns true if a frame was drawn
-    virtual bool TryPresent(int timeout_ms) = 0;
-
     // Getter/setter functions:
     // ------------------------
 
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index a4c5b8f74..2ccca1993 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -32,20 +32,6 @@ namespace OpenGL {
 
 namespace {
 
-constexpr std::size_t SWAP_CHAIN_SIZE = 3;
-
-struct Frame {
-    u32 width{};                      /// Width of the frame (to detect resize)
-    u32 height{};                     /// Height of the frame
-    bool color_reloaded{};            /// Texture attachment was recreated (ie: resized)
-    OpenGL::OGLRenderbuffer color{};  /// Buffer shared between the render/present FBO
-    OpenGL::OGLFramebuffer render{};  /// FBO created on the render thread
-    OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
-    GLsync render_fence{};            /// Fence created on the render thread
-    GLsync present_fence{};           /// Fence created on the presentation thread
-    bool is_srgb{};                   /// Framebuffer is sRGB or RGB
-};
-
 constexpr GLint PositionLocation = 0;
 constexpr GLint TexCoordLocation = 1;
 constexpr GLint ModelViewMatrixLocation = 0;
@@ -58,24 +44,6 @@ struct ScreenRectVertex {
     std::array<GLfloat, 2> tex_coord;
 };
 
-/// Returns true if any debug tool is attached
-bool HasDebugTool() {
-    const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
-    if (nsight) {
-        return true;
-    }
-
-    GLint num_extensions;
-    glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
-    for (GLuint index = 0; index < static_cast<GLuint>(num_extensions); ++index) {
-        const auto name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index));
-        if (!std::strcmp(name, "GL_EXT_debug_tool")) {
-            return true;
-        }
-    }
-    return false;
-}
-
 /**
  * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
  * corner and (width, height) on the lower-bottom.
@@ -159,135 +127,15 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
 
 } // Anonymous namespace
 
-/**
- * For smooth Vsync rendering, we want to always present the latest frame that the core generates,
- * but also make sure that rendering happens at the pace that the frontend dictates. This is a
- * helper class that the renderer uses to sync frames between the render thread and the presentation
- * thread
- */
-class FrameMailbox {
-public:
-    std::mutex swap_chain_lock;
-    std::condition_variable present_cv;
-    std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
-    std::queue<Frame*> free_queue;
-    std::deque<Frame*> present_queue;
-    Frame* previous_frame{};
-
-    FrameMailbox() {
-        for (auto& frame : swap_chain) {
-            free_queue.push(&frame);
-        }
-    }
-
-    ~FrameMailbox() {
-        // lock the mutex and clear out the present and free_queues and notify any people who are
-        // blocked to prevent deadlock on shutdown
-        std::scoped_lock lock{swap_chain_lock};
-        std::queue<Frame*>().swap(free_queue);
-        present_queue.clear();
-        present_cv.notify_all();
-    }
-
-    void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
-        frame->present.Release();
-        frame->present.Create();
-        GLint previous_draw_fbo{};
-        glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
-        glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
-        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
-                                  frame->color.handle);
-        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
-            LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
-        }
-        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
-        frame->color_reloaded = false;
-    }
-
-    void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
-        // Recreate the color texture attachment
-        frame->color.Release();
-        frame->color.Create();
-        const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
-        glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
-
-        // Recreate the FBO for the render target
-        frame->render.Release();
-        frame->render.Create();
-        glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
-        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
-                                  frame->color.handle);
-        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
-            LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
-        }
-
-        frame->width = width;
-        frame->height = height;
-        frame->color_reloaded = true;
-    }
-
-    Frame* GetRenderFrame() {
-        std::unique_lock lock{swap_chain_lock};
-
-        // If theres no free frames, we will reuse the oldest render frame
-        if (free_queue.empty()) {
-            auto frame = present_queue.back();
-            present_queue.pop_back();
-            return frame;
-        }
-
-        Frame* frame = free_queue.front();
-        free_queue.pop();
-        return frame;
-    }
-
-    void ReleaseRenderFrame(Frame* frame) {
-        std::unique_lock lock{swap_chain_lock};
-        present_queue.push_front(frame);
-        present_cv.notify_one();
-    }
-
-    Frame* TryGetPresentFrame(int timeout_ms) {
-        std::unique_lock lock{swap_chain_lock};
-        // wait for new entries in the present_queue
-        present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
-                            [&] { return !present_queue.empty(); });
-        if (present_queue.empty()) {
-            // timed out waiting for a frame to draw so return the previous frame
-            return previous_frame;
-        }
-
-        // free the previous frame and add it back to the free queue
-        if (previous_frame) {
-            free_queue.push(previous_frame);
-        }
-
-        // the newest entries are pushed to the front of the queue
-        Frame* frame = present_queue.front();
-        present_queue.pop_front();
-        // remove all old entries from the present queue and move them back to the free_queue
-        for (auto f : present_queue) {
-            free_queue.push(f);
-        }
-        present_queue.clear();
-        previous_frame = frame;
-        return frame;
-    }
-};
-
 RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
                                Core::Frontend::EmuWindow& emu_window_,
                                Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
                                std::unique_ptr<Core::Frontend::GraphicsContext> context)
     : RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_},
-      emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device},
-      has_debug_tool{HasDebugTool()} {}
+      emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device} {}
 
 RendererOpenGL::~RendererOpenGL() = default;
 
-MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
-MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
-
 void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
     if (!framebuffer) {
         return;
@@ -296,79 +144,34 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
     PrepareRendertarget(framebuffer);
     RenderScreenshot();
 
-    Frame* frame;
-    {
-        MICROPROFILE_SCOPE(OpenGL_WaitPresent);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+    DrawScreen(emu_window.GetFramebufferLayout());
 
-        frame = frame_mailbox->GetRenderFrame();
+    ++m_current_frame;
 
-        // Clean up sync objects before drawing
-
-        // INTEL driver workaround. We can't delete the previous render sync object until we are
-        // sure that the presentation is done
-        if (frame->present_fence) {
-            glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
-        }
-
-        // delete the draw fence if the frame wasn't presented
-        if (frame->render_fence) {
-            glDeleteSync(frame->render_fence);
-            frame->render_fence = 0;
-        }
-
-        // wait for the presentation to be done
-        if (frame->present_fence) {
-            glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
-            glDeleteSync(frame->present_fence);
-            frame->present_fence = 0;
-        }
-    }
-
-    {
-        MICROPROFILE_SCOPE(OpenGL_RenderFrame);
-        const auto& layout = render_window.GetFramebufferLayout();
-
-        // Recreate the frame if the size of the window has changed
-        if (layout.width != frame->width || layout.height != frame->height ||
-            screen_info.display_srgb != frame->is_srgb) {
-            LOG_DEBUG(Render_OpenGL, "Reloading render frame");
-            frame->is_srgb = screen_info.display_srgb;
-            frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
-        }
-        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame->render.handle);
-        DrawScreen(layout);
-        // Create a fence for the frontend to wait on and swap this frame to OffTex
-        frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
-        glFlush();
-        frame_mailbox->ReleaseRenderFrame(frame);
-        m_current_frame++;
-        rasterizer->TickFrame();
-    }
+    rasterizer->TickFrame();
 
     render_window.PollEvents();
-    if (has_debug_tool) {
-        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
-        Present(0);
-        context->SwapBuffers();
-    }
+    context->SwapBuffers();
 }
 
 void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
-    if (framebuffer) {
-        // If framebuffer is provided, reload it from memory to a texture
-        if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
-            screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
-            screen_info.texture.pixel_format != framebuffer->pixel_format ||
-            gl_framebuffer_data.empty()) {
-            // Reallocate texture if the framebuffer size has changed.
-            // This is expected to not happen very often and hence should not be a
-            // performance problem.
-            ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
-        }
-
-        // Load the framebuffer from memory, draw it to the screen, and swap buffers
-        LoadFBToScreenInfo(*framebuffer);
+    if (!framebuffer) {
+        return;
     }
+    // If framebuffer is provided, reload it from memory to a texture
+    if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
+        screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
+        screen_info.texture.pixel_format != framebuffer->pixel_format ||
+        gl_framebuffer_data.empty()) {
+        // Reallocate texture if the framebuffer size has changed.
+        // This is expected to not happen very often and hence should not be a
+        // performance problem.
+        ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
+    }
+
+    // Load the framebuffer from memory, draw it to the screen, and swap buffers
+    LoadFBToScreenInfo(*framebuffer);
 }
 
 void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
@@ -418,8 +221,6 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
 }
 
 void RendererOpenGL::InitOpenGLObjects() {
-    frame_mailbox = std::make_unique<FrameMailbox>();
-
     glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
                  Settings::values.bg_blue.GetValue(), 0.0f);
 
@@ -647,51 +448,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     program_manager.RestoreGuestPipeline();
 }
 
-bool RendererOpenGL::TryPresent(int timeout_ms) {
-    if (has_debug_tool) {
-        LOG_DEBUG(Render_OpenGL,
-                  "Skipping presentation because we are presenting on the main context");
-        return false;
-    }
-    return Present(timeout_ms);
-}
-
-bool RendererOpenGL::Present(int timeout_ms) {
-    const auto& layout = render_window.GetFramebufferLayout();
-    auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
-    if (!frame) {
-        LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
-        return false;
-    }
-
-    // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
-    // readback since we won't be doing any blending
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    // Recreate the presentation FBO if the color attachment was changed
-    if (frame->color_reloaded) {
-        LOG_DEBUG(Render_OpenGL, "Reloading present frame");
-        frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
-    }
-    glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
-    // INTEL workaround.
-    // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
-    // it on the emulation thread without too much penalty
-    // glDeleteSync(frame.render_sync);
-    // frame.render_sync = 0;
-
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
-    glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
-                      GL_COLOR_BUFFER_BIT, GL_LINEAR);
-
-    // Insert fence for the main thread to block on
-    frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
-    glFlush();
-
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
-    return true;
-}
-
 void RendererOpenGL::RenderScreenshot() {
     if (!renderer_settings.screenshot_requested) {
         return;
@@ -706,7 +462,7 @@ void RendererOpenGL::RenderScreenshot() {
     screenshot_framebuffer.Create();
     glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
 
-    Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
+    const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
 
     GLuint renderbuffer;
     glGenRenderbuffers(1, &renderbuffer);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 5329577fb..9ef181f95 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -55,14 +55,6 @@ struct ScreenInfo {
     TextureInfo texture;
 };
 
-struct PresentationTexture {
-    u32 width = 0;
-    u32 height = 0;
-    OGLTexture texture;
-};
-
-class FrameMailbox;
-
 class RendererOpenGL final : public VideoCore::RendererBase {
 public:
     explicit RendererOpenGL(Core::TelemetrySession& telemetry_session,
@@ -74,7 +66,6 @@ public:
     bool Init() override;
     void ShutDown() override;
     void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
-    bool TryPresent(int timeout_ms) override;
 
 private:
     /// Initializes the OpenGL state and creates persistent objects.
@@ -102,8 +93,6 @@ private:
 
     void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
 
-    bool Present(int timeout_ms);
-
     Core::TelemetrySession& telemetry_session;
     Core::Frontend::EmuWindow& emu_window;
     Core::Memory::Memory& cpu_memory;
@@ -134,11 +123,6 @@ private:
     /// Used for transforming the framebuffer orientation
     Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
     Common::Rectangle<int> framebuffer_crop_rect;
-
-    /// Frame presentation mailbox
-    std::unique_ptr<FrameMailbox> frame_mailbox;
-
-    bool has_debug_tool = false;
 };
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 0e4583986..d38e797a4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -283,11 +283,6 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
     render_window.PollEvents();
 }
 
-bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
-    // TODO (bunnei): ImplementMe
-    return true;
-}
-
 bool RendererVulkan::Init() {
     library = OpenVulkanLibrary();
     instance = CreateInstance(library, dld, render_window.GetWindowInfo().type,
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index ddff77942..5085310d0 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -55,7 +55,6 @@ public:
     bool Init() override;
     void ShutDown() override;
     void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
-    bool TryPresent(int timeout_ms) override;
 
     static std::vector<std::string> EnumerateDevices();
 
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index caa2d06d3..408eac2b7 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -218,15 +218,6 @@ public:
 
     virtual ~RenderWidget() = default;
 
-    /// Called on the UI thread when this Widget is ready to draw
-    /// Dervied classes can override this to draw the latest frame.
-    virtual void Present() {}
-
-    void paintEvent(QPaintEvent* event) override {
-        Present();
-        update();
-    }
-
     QPaintEngine* paintEngine() const override {
         return nullptr;
     }
@@ -245,20 +236,8 @@ public:
         context = std::move(context_);
     }
 
-    void Present() override {
-        if (!isVisible()) {
-            return;
-        }
-
-        context->MakeCurrent();
-        if (Core::System::GetInstance().Renderer().TryPresent(100)) {
-            context->SwapBuffers();
-            glFinish();
-        }
-    }
-
 private:
-    std::unique_ptr<Core::Frontend::GraphicsContext> context{};
+    std::unique_ptr<Core::Frontend::GraphicsContext> context;
 };
 
 #ifdef HAS_VULKAN
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index a804d5185..521209622 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -13,9 +13,8 @@
 #include "input_common/sdl/sdl.h"
 #include "yuzu_cmd/emu_window/emu_window_sdl2.h"
 
-EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen,
-                               InputCommon::InputSubsystem* input_subsystem_)
-    : system{system}, input_subsystem{input_subsystem_} {
+EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_)
+    : input_subsystem{input_subsystem_} {
     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
         LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
         exit(1);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 82750ffec..53d756c3c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -20,8 +20,7 @@ class InputSubsystem;
 
 class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
 public:
-    explicit EmuWindow_SDL2(Core::System& system, bool fullscreen,
-                            InputCommon::InputSubsystem* input_subsystem);
+    explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
     ~EmuWindow_SDL2();
 
     /// Polls window events
@@ -33,9 +32,6 @@ public:
     /// Returns if window is shown (not minimized)
     bool IsShown() const override;
 
-    /// Presents the next frame
-    virtual void Present() = 0;
-
 protected:
     /// Called by PollEvents when a key is pressed or released.
     void OnKeyEvent(int key, u8 state);
@@ -67,9 +63,6 @@ protected:
     /// Called when a configuration change affects the minimal size of the window
     void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override;
 
-    /// Instance of the system, used to access renderer for the presentation thread
-    Core::System& system;
-
     /// Is the window still open?
     bool is_open = true;
 
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 881b67a76..5f35233b5 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -87,9 +87,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
     return unsupported_ext.empty();
 }
 
-EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
-                                     InputCommon::InputSubsystem* input_subsystem)
-    : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
+EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
+    : EmuWindow_SDL2{input_subsystem} {
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
@@ -163,13 +162,3 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
 std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
     return std::make_unique<SDLGLContext>();
 }
-
-void EmuWindow_SDL2_GL::Present() {
-    SDL_GL_MakeCurrent(render_window, window_context);
-    SDL_GL_SetSwapInterval(Settings::values.use_vsync.GetValue() ? 1 : 0);
-    while (IsOpen()) {
-        system.Renderer().TryPresent(100);
-        SDL_GL_SwapWindow(render_window);
-    }
-    SDL_GL_MakeCurrent(render_window, nullptr);
-}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index 732a64edd..dba5c293c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -14,12 +14,9 @@ class InputSubsystem;
 
 class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
 public:
-    explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
-                               InputCommon::InputSubsystem* input_subsystem);
+    explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
     ~EmuWindow_SDL2_GL();
 
-    void Present() override;
-
     std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
 
 private:
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 53491f86e..3ba657c00 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -19,9 +19,8 @@
 #include <SDL.h>
 #include <SDL_syswm.h>
 
-EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
-                                     InputCommon::InputSubsystem* input_subsystem)
-    : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
+EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem)
+    : EmuWindow_SDL2{input_subsystem} {
     const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
                                                  Common::g_scm_branch, Common::g_scm_desc);
     render_window =
@@ -74,7 +73,3 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
 std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
     return std::make_unique<DummyContext>();
 }
-
-void EmuWindow_SDL2_VK::Present() {
-    // TODO (bunnei): ImplementMe
-}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index f99704d4c..bdfdc3c6f 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -19,11 +19,8 @@ class InputSubsystem;
 
 class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
 public:
-    explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
-                               InputCommon::InputSubsystem* input_subsystem);
-    ~EmuWindow_SDL2_VK();
-
-    void Present() override;
+    explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem);
+    ~EmuWindow_SDL2_VK() override;
 
     std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
 };
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index e960b5413..3a76c785f 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -185,11 +185,11 @@ int main(int argc, char** argv) {
     std::unique_ptr<EmuWindow_SDL2> emu_window;
     switch (Settings::values.renderer_backend.GetValue()) {
     case Settings::RendererBackend::OpenGL:
-        emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, &input_subsystem);
+        emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen);
         break;
     case Settings::RendererBackend::Vulkan:
 #ifdef HAS_VULKAN
-        emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, &input_subsystem);
+        emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem);
         break;
 #else
         LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
@@ -240,14 +240,11 @@ int main(int argc, char** argv) {
         system.CurrentProcess()->GetTitleID(), false,
         [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
 
-    std::thread render_thread([&emu_window] { emu_window->Present(); });
     system.Run();
     while (emu_window->IsOpen()) {
         std::this_thread::sleep_for(std::chrono::milliseconds(1));
     }
     system.Pause();
-    render_thread.join();
-
     system.Shutdown();
 
     detached_tasks.WaitForAllTasks();