summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Rodrigo Locatti2020-09-21 00:21:32 +0000
committerGravatar GitHub2020-09-21 00:21:32 +0000
commita2eb44db825a892cc2863bd1f5d0352c273ff0f0 (patch)
tree3e75441609f97c8a6ad01e70461ecb218c51d086
parentMerge pull request #4683 from Morph1984/NpadHandheldActivationMode-impl (diff)
parentrenderer_opengl: Remove emulated mailbox presentation (diff)
downloadyuzu-a2eb44db825a892cc2863bd1f5d0352c273ff0f0.tar.gz
yuzu-a2eb44db825a892cc2863bd1f5d0352c273ff0f0.tar.xz
yuzu-a2eb44db825a892cc2863bd1f5d0352c273ff0f0.zip
Merge pull request #4692 from ReinUsesLisp/remove-vsync
renderer_opengl: Remove emulated mailbox presentation
Diffstat (limited to '')
-rw-r--r--src/video_core/renderer_base.h5
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp288
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h16
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp5
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h1
-rw-r--r--src/yuzu/bootmanager.cpp23
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h9
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp15
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp9
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h7
-rw-r--r--src/yuzu_cmd/yuzu.cpp7
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:
46 /// Finalize rendering the guest frame and draw into the presentation texture 46 /// Finalize rendering the guest frame and draw into the presentation texture
47 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; 47 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
48 48
49 /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
50 /// specific implementation)
51 /// Returns true if a frame was drawn
52 virtual bool TryPresent(int timeout_ms) = 0;
53
54 // Getter/setter functions: 49 // Getter/setter functions:
55 // ------------------------ 50 // ------------------------
56 51
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 {
32 32
33namespace { 33namespace {
34 34
35constexpr std::size_t SWAP_CHAIN_SIZE = 3;
36
37struct Frame {
38 u32 width{}; /// Width of the frame (to detect resize)
39 u32 height{}; /// Height of the frame
40 bool color_reloaded{}; /// Texture attachment was recreated (ie: resized)
41 OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO
42 OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread
43 OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
44 GLsync render_fence{}; /// Fence created on the render thread
45 GLsync present_fence{}; /// Fence created on the presentation thread
46 bool is_srgb{}; /// Framebuffer is sRGB or RGB
47};
48
49constexpr GLint PositionLocation = 0; 35constexpr GLint PositionLocation = 0;
50constexpr GLint TexCoordLocation = 1; 36constexpr GLint TexCoordLocation = 1;
51constexpr GLint ModelViewMatrixLocation = 0; 37constexpr GLint ModelViewMatrixLocation = 0;
@@ -58,24 +44,6 @@ struct ScreenRectVertex {
58 std::array<GLfloat, 2> tex_coord; 44 std::array<GLfloat, 2> tex_coord;
59}; 45};
60 46
61/// Returns true if any debug tool is attached
62bool HasDebugTool() {
63 const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
64 if (nsight) {
65 return true;
66 }
67
68 GLint num_extensions;
69 glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
70 for (GLuint index = 0; index < static_cast<GLuint>(num_extensions); ++index) {
71 const auto name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index));
72 if (!std::strcmp(name, "GL_EXT_debug_tool")) {
73 return true;
74 }
75 }
76 return false;
77}
78
79/** 47/**
80 * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left 48 * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
81 * corner and (width, height) on the lower-bottom. 49 * corner and (width, height) on the lower-bottom.
@@ -159,135 +127,15 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
159 127
160} // Anonymous namespace 128} // Anonymous namespace
161 129
162/**
163 * For smooth Vsync rendering, we want to always present the latest frame that the core generates,
164 * but also make sure that rendering happens at the pace that the frontend dictates. This is a
165 * helper class that the renderer uses to sync frames between the render thread and the presentation
166 * thread
167 */
168class FrameMailbox {
169public:
170 std::mutex swap_chain_lock;
171 std::condition_variable present_cv;
172 std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
173 std::queue<Frame*> free_queue;
174 std::deque<Frame*> present_queue;
175 Frame* previous_frame{};
176
177 FrameMailbox() {
178 for (auto& frame : swap_chain) {
179 free_queue.push(&frame);
180 }
181 }
182
183 ~FrameMailbox() {
184 // lock the mutex and clear out the present and free_queues and notify any people who are
185 // blocked to prevent deadlock on shutdown
186 std::scoped_lock lock{swap_chain_lock};
187 std::queue<Frame*>().swap(free_queue);
188 present_queue.clear();
189 present_cv.notify_all();
190 }
191
192 void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
193 frame->present.Release();
194 frame->present.Create();
195 GLint previous_draw_fbo{};
196 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
197 glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
198 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
199 frame->color.handle);
200 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
201 LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
202 }
203 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
204 frame->color_reloaded = false;
205 }
206
207 void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
208 // Recreate the color texture attachment
209 frame->color.Release();
210 frame->color.Create();
211 const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
212 glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
213
214 // Recreate the FBO for the render target
215 frame->render.Release();
216 frame->render.Create();
217 glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
218 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
219 frame->color.handle);
220 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
221 LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
222 }
223
224 frame->width = width;
225 frame->height = height;
226 frame->color_reloaded = true;
227 }
228
229 Frame* GetRenderFrame() {
230 std::unique_lock lock{swap_chain_lock};
231
232 // If theres no free frames, we will reuse the oldest render frame
233 if (free_queue.empty()) {
234 auto frame = present_queue.back();
235 present_queue.pop_back();
236 return frame;
237 }
238
239 Frame* frame = free_queue.front();
240 free_queue.pop();
241 return frame;
242 }
243
244 void ReleaseRenderFrame(Frame* frame) {
245 std::unique_lock lock{swap_chain_lock};
246 present_queue.push_front(frame);
247 present_cv.notify_one();
248 }
249
250 Frame* TryGetPresentFrame(int timeout_ms) {
251 std::unique_lock lock{swap_chain_lock};
252 // wait for new entries in the present_queue
253 present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
254 [&] { return !present_queue.empty(); });
255 if (present_queue.empty()) {
256 // timed out waiting for a frame to draw so return the previous frame
257 return previous_frame;
258 }
259
260 // free the previous frame and add it back to the free queue
261 if (previous_frame) {
262 free_queue.push(previous_frame);
263 }
264
265 // the newest entries are pushed to the front of the queue
266 Frame* frame = present_queue.front();
267 present_queue.pop_front();
268 // remove all old entries from the present queue and move them back to the free_queue
269 for (auto f : present_queue) {
270 free_queue.push(f);
271 }
272 present_queue.clear();
273 previous_frame = frame;
274 return frame;
275 }
276};
277
278RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, 130RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
279 Core::Frontend::EmuWindow& emu_window_, 131 Core::Frontend::EmuWindow& emu_window_,
280 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, 132 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
281 std::unique_ptr<Core::Frontend::GraphicsContext> context) 133 std::unique_ptr<Core::Frontend::GraphicsContext> context)
282 : RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_}, 134 : RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_},
283 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device}, 135 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device} {}
284 has_debug_tool{HasDebugTool()} {}
285 136
286RendererOpenGL::~RendererOpenGL() = default; 137RendererOpenGL::~RendererOpenGL() = default;
287 138
288MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
289MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
290
291void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 139void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
292 if (!framebuffer) { 140 if (!framebuffer) {
293 return; 141 return;
@@ -296,79 +144,34 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
296 PrepareRendertarget(framebuffer); 144 PrepareRendertarget(framebuffer);
297 RenderScreenshot(); 145 RenderScreenshot();
298 146
299 Frame* frame; 147 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
300 { 148 DrawScreen(emu_window.GetFramebufferLayout());
301 MICROPROFILE_SCOPE(OpenGL_WaitPresent);
302
303 frame = frame_mailbox->GetRenderFrame();
304
305 // Clean up sync objects before drawing
306
307 // INTEL driver workaround. We can't delete the previous render sync object until we are
308 // sure that the presentation is done
309 if (frame->present_fence) {
310 glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
311 }
312
313 // delete the draw fence if the frame wasn't presented
314 if (frame->render_fence) {
315 glDeleteSync(frame->render_fence);
316 frame->render_fence = 0;
317 }
318
319 // wait for the presentation to be done
320 if (frame->present_fence) {
321 glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
322 glDeleteSync(frame->present_fence);
323 frame->present_fence = 0;
324 }
325 }
326 149
327 { 150 ++m_current_frame;
328 MICROPROFILE_SCOPE(OpenGL_RenderFrame);
329 const auto& layout = render_window.GetFramebufferLayout();
330 151
331 // Recreate the frame if the size of the window has changed 152 rasterizer->TickFrame();
332 if (layout.width != frame->width || layout.height != frame->height ||
333 screen_info.display_srgb != frame->is_srgb) {
334 LOG_DEBUG(Render_OpenGL, "Reloading render frame");
335 frame->is_srgb = screen_info.display_srgb;
336 frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
337 }
338 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame->render.handle);
339 DrawScreen(layout);
340 // Create a fence for the frontend to wait on and swap this frame to OffTex
341 frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
342 glFlush();
343 frame_mailbox->ReleaseRenderFrame(frame);
344 m_current_frame++;
345 rasterizer->TickFrame();
346 }
347 153
348 render_window.PollEvents(); 154 render_window.PollEvents();
349 if (has_debug_tool) { 155 context->SwapBuffers();
350 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
351 Present(0);
352 context->SwapBuffers();
353 }
354} 156}
355 157
356void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { 158void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
357 if (framebuffer) { 159 if (!framebuffer) {
358 // If framebuffer is provided, reload it from memory to a texture 160 return;
359 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || 161 }
360 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || 162 // If framebuffer is provided, reload it from memory to a texture
361 screen_info.texture.pixel_format != framebuffer->pixel_format || 163 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
362 gl_framebuffer_data.empty()) { 164 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
363 // Reallocate texture if the framebuffer size has changed. 165 screen_info.texture.pixel_format != framebuffer->pixel_format ||
364 // This is expected to not happen very often and hence should not be a 166 gl_framebuffer_data.empty()) {
365 // performance problem. 167 // Reallocate texture if the framebuffer size has changed.
366 ConfigureFramebufferTexture(screen_info.texture, *framebuffer); 168 // This is expected to not happen very often and hence should not be a
367 } 169 // performance problem.
368 170 ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
369 // Load the framebuffer from memory, draw it to the screen, and swap buffers
370 LoadFBToScreenInfo(*framebuffer);
371 } 171 }
172
173 // Load the framebuffer from memory, draw it to the screen, and swap buffers
174 LoadFBToScreenInfo(*framebuffer);
372} 175}
373 176
374void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { 177void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
@@ -418,8 +221,6 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
418} 221}
419 222
420void RendererOpenGL::InitOpenGLObjects() { 223void RendererOpenGL::InitOpenGLObjects() {
421 frame_mailbox = std::make_unique<FrameMailbox>();
422
423 glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), 224 glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
424 Settings::values.bg_blue.GetValue(), 0.0f); 225 Settings::values.bg_blue.GetValue(), 0.0f);
425 226
@@ -647,51 +448,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
647 program_manager.RestoreGuestPipeline(); 448 program_manager.RestoreGuestPipeline();
648} 449}
649 450
650bool RendererOpenGL::TryPresent(int timeout_ms) {
651 if (has_debug_tool) {
652 LOG_DEBUG(Render_OpenGL,
653 "Skipping presentation because we are presenting on the main context");
654 return false;
655 }
656 return Present(timeout_ms);
657}
658
659bool RendererOpenGL::Present(int timeout_ms) {
660 const auto& layout = render_window.GetFramebufferLayout();
661 auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
662 if (!frame) {
663 LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
664 return false;
665 }
666
667 // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
668 // readback since we won't be doing any blending
669 glClear(GL_COLOR_BUFFER_BIT);
670
671 // Recreate the presentation FBO if the color attachment was changed
672 if (frame->color_reloaded) {
673 LOG_DEBUG(Render_OpenGL, "Reloading present frame");
674 frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
675 }
676 glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
677 // INTEL workaround.
678 // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
679 // it on the emulation thread without too much penalty
680 // glDeleteSync(frame.render_sync);
681 // frame.render_sync = 0;
682
683 glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
684 glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
685 GL_COLOR_BUFFER_BIT, GL_LINEAR);
686
687 // Insert fence for the main thread to block on
688 frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
689 glFlush();
690
691 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
692 return true;
693}
694
695void RendererOpenGL::RenderScreenshot() { 451void RendererOpenGL::RenderScreenshot() {
696 if (!renderer_settings.screenshot_requested) { 452 if (!renderer_settings.screenshot_requested) {
697 return; 453 return;
@@ -706,7 +462,7 @@ void RendererOpenGL::RenderScreenshot() {
706 screenshot_framebuffer.Create(); 462 screenshot_framebuffer.Create();
707 glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle); 463 glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
708 464
709 Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; 465 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
710 466
711 GLuint renderbuffer; 467 GLuint renderbuffer;
712 glGenRenderbuffers(1, &renderbuffer); 468 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 {
55 TextureInfo texture; 55 TextureInfo texture;
56}; 56};
57 57
58struct PresentationTexture {
59 u32 width = 0;
60 u32 height = 0;
61 OGLTexture texture;
62};
63
64class FrameMailbox;
65
66class RendererOpenGL final : public VideoCore::RendererBase { 58class RendererOpenGL final : public VideoCore::RendererBase {
67public: 59public:
68 explicit RendererOpenGL(Core::TelemetrySession& telemetry_session, 60 explicit RendererOpenGL(Core::TelemetrySession& telemetry_session,
@@ -74,7 +66,6 @@ public:
74 bool Init() override; 66 bool Init() override;
75 void ShutDown() override; 67 void ShutDown() override;
76 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 68 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
77 bool TryPresent(int timeout_ms) override;
78 69
79private: 70private:
80 /// Initializes the OpenGL state and creates persistent objects. 71 /// Initializes the OpenGL state and creates persistent objects.
@@ -102,8 +93,6 @@ private:
102 93
103 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); 94 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
104 95
105 bool Present(int timeout_ms);
106
107 Core::TelemetrySession& telemetry_session; 96 Core::TelemetrySession& telemetry_session;
108 Core::Frontend::EmuWindow& emu_window; 97 Core::Frontend::EmuWindow& emu_window;
109 Core::Memory::Memory& cpu_memory; 98 Core::Memory::Memory& cpu_memory;
@@ -134,11 +123,6 @@ private:
134 /// Used for transforming the framebuffer orientation 123 /// Used for transforming the framebuffer orientation
135 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{}; 124 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
136 Common::Rectangle<int> framebuffer_crop_rect; 125 Common::Rectangle<int> framebuffer_crop_rect;
137
138 /// Frame presentation mailbox
139 std::unique_ptr<FrameMailbox> frame_mailbox;
140
141 bool has_debug_tool = false;
142}; 126};
143 127
144} // namespace OpenGL 128} // 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) {
283 render_window.PollEvents(); 283 render_window.PollEvents();
284} 284}
285 285
286bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
287 // TODO (bunnei): ImplementMe
288 return true;
289}
290
291bool RendererVulkan::Init() { 286bool RendererVulkan::Init() {
292 library = OpenVulkanLibrary(); 287 library = OpenVulkanLibrary();
293 instance = CreateInstance(library, dld, render_window.GetWindowInfo().type, 288 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:
55 bool Init() override; 55 bool Init() override;
56 void ShutDown() override; 56 void ShutDown() override;
57 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 57 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
58 bool TryPresent(int timeout_ms) override;
59 58
60 static std::vector<std::string> EnumerateDevices(); 59 static std::vector<std::string> EnumerateDevices();
61 60
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:
218 218
219 virtual ~RenderWidget() = default; 219 virtual ~RenderWidget() = default;
220 220
221 /// Called on the UI thread when this Widget is ready to draw
222 /// Dervied classes can override this to draw the latest frame.
223 virtual void Present() {}
224
225 void paintEvent(QPaintEvent* event) override {
226 Present();
227 update();
228 }
229
230 QPaintEngine* paintEngine() const override { 221 QPaintEngine* paintEngine() const override {
231 return nullptr; 222 return nullptr;
232 } 223 }
@@ -245,20 +236,8 @@ public:
245 context = std::move(context_); 236 context = std::move(context_);
246 } 237 }
247 238
248 void Present() override {
249 if (!isVisible()) {
250 return;
251 }
252
253 context->MakeCurrent();
254 if (Core::System::GetInstance().Renderer().TryPresent(100)) {
255 context->SwapBuffers();
256 glFinish();
257 }
258 }
259
260private: 239private:
261 std::unique_ptr<Core::Frontend::GraphicsContext> context{}; 240 std::unique_ptr<Core::Frontend::GraphicsContext> context;
262}; 241};
263 242
264#ifdef HAS_VULKAN 243#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 @@
13#include "input_common/sdl/sdl.h" 13#include "input_common/sdl/sdl.h"
14#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 14#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
15 15
16EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen, 16EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_)
17 InputCommon::InputSubsystem* input_subsystem_) 17 : input_subsystem{input_subsystem_} {
18 : system{system}, input_subsystem{input_subsystem_} {
19 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 18 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
20 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 19 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
21 exit(1); 20 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;
20 20
21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
22public: 22public:
23 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen, 23 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
24 InputCommon::InputSubsystem* input_subsystem);
25 ~EmuWindow_SDL2(); 24 ~EmuWindow_SDL2();
26 25
27 /// Polls window events 26 /// Polls window events
@@ -33,9 +32,6 @@ public:
33 /// Returns if window is shown (not minimized) 32 /// Returns if window is shown (not minimized)
34 bool IsShown() const override; 33 bool IsShown() const override;
35 34
36 /// Presents the next frame
37 virtual void Present() = 0;
38
39protected: 35protected:
40 /// Called by PollEvents when a key is pressed or released. 36 /// Called by PollEvents when a key is pressed or released.
41 void OnKeyEvent(int key, u8 state); 37 void OnKeyEvent(int key, u8 state);
@@ -67,9 +63,6 @@ protected:
67 /// Called when a configuration change affects the minimal size of the window 63 /// Called when a configuration change affects the minimal size of the window
68 void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; 64 void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override;
69 65
70 /// Instance of the system, used to access renderer for the presentation thread
71 Core::System& system;
72
73 /// Is the window still open? 66 /// Is the window still open?
74 bool is_open = true; 67 bool is_open = true;
75 68
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() {
87 return unsupported_ext.empty(); 87 return unsupported_ext.empty();
88} 88}
89 89
90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen, 90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
91 InputCommon::InputSubsystem* input_subsystem) 91 : EmuWindow_SDL2{input_subsystem} {
92 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 92 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
95 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
@@ -163,13 +162,3 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
163std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { 162std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
164 return std::make_unique<SDLGLContext>(); 163 return std::make_unique<SDLGLContext>();
165} 164}
166
167void EmuWindow_SDL2_GL::Present() {
168 SDL_GL_MakeCurrent(render_window, window_context);
169 SDL_GL_SetSwapInterval(Settings::values.use_vsync.GetValue() ? 1 : 0);
170 while (IsOpen()) {
171 system.Renderer().TryPresent(100);
172 SDL_GL_SwapWindow(render_window);
173 }
174 SDL_GL_MakeCurrent(render_window, nullptr);
175}
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;
14 14
15class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 15class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
16public: 16public:
17 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen, 17 explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
18 InputCommon::InputSubsystem* input_subsystem);
19 ~EmuWindow_SDL2_GL(); 18 ~EmuWindow_SDL2_GL();
20 19
21 void Present() override;
22
23 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 20 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
24 21
25private: 22private:
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 @@
19#include <SDL.h> 19#include <SDL.h>
20#include <SDL_syswm.h> 20#include <SDL_syswm.h>
21 21
22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen, 22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem)
23 InputCommon::InputSubsystem* input_subsystem) 23 : EmuWindow_SDL2{input_subsystem} {
24 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
25 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, 24 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
26 Common::g_scm_branch, Common::g_scm_desc); 25 Common::g_scm_branch, Common::g_scm_desc);
27 render_window = 26 render_window =
@@ -74,7 +73,3 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
74std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { 73std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
75 return std::make_unique<DummyContext>(); 74 return std::make_unique<DummyContext>();
76} 75}
77
78void EmuWindow_SDL2_VK::Present() {
79 // TODO (bunnei): ImplementMe
80}
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;
19 19
20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
21public: 21public:
22 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen, 22 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem);
23 InputCommon::InputSubsystem* input_subsystem); 23 ~EmuWindow_SDL2_VK() override;
24 ~EmuWindow_SDL2_VK();
25
26 void Present() override;
27 24
28 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
29}; 26};
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) {
185 std::unique_ptr<EmuWindow_SDL2> emu_window; 185 std::unique_ptr<EmuWindow_SDL2> emu_window;
186 switch (Settings::values.renderer_backend.GetValue()) { 186 switch (Settings::values.renderer_backend.GetValue()) {
187 case Settings::RendererBackend::OpenGL: 187 case Settings::RendererBackend::OpenGL:
188 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, &input_subsystem); 188 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen);
189 break; 189 break;
190 case Settings::RendererBackend::Vulkan: 190 case Settings::RendererBackend::Vulkan:
191#ifdef HAS_VULKAN 191#ifdef HAS_VULKAN
192 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, &input_subsystem); 192 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem);
193 break; 193 break;
194#else 194#else
195 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); 195 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
@@ -240,14 +240,11 @@ int main(int argc, char** argv) {
240 system.CurrentProcess()->GetTitleID(), false, 240 system.CurrentProcess()->GetTitleID(), false,
241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); 241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
242 242
243 std::thread render_thread([&emu_window] { emu_window->Present(); });
244 system.Run(); 243 system.Run();
245 while (emu_window->IsOpen()) { 244 while (emu_window->IsOpen()) {
246 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 245 std::this_thread::sleep_for(std::chrono::milliseconds(1));
247 } 246 }
248 system.Pause(); 247 system.Pause();
249 render_thread.join();
250
251 system.Shutdown(); 248 system.Shutdown();
252 249
253 detached_tasks.WaitForAllTasks(); 250 detached_tasks.WaitForAllTasks();