summaryrefslogtreecommitdiff
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/gpu_thread.cpp4
-rw-r--r--src/video_core/renderer_base.h10
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp18
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h25
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_state.h4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp272
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h29
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp15
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h8
10 files changed, 353 insertions, 47 deletions
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 2cdf1aa7f..b1088af3d 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -5,7 +5,7 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/scope_acquire_window_context.h" 8#include "core/frontend/scope_acquire_context.h"
9#include "video_core/dma_pusher.h" 9#include "video_core/dma_pusher.h"
10#include "video_core/gpu.h" 10#include "video_core/gpu.h"
11#include "video_core/gpu_thread.h" 11#include "video_core/gpu_thread.h"
@@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
27 return; 27 return;
28 } 28 }
29 29
30 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()}; 30 Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()};
31 31
32 CommandDataContainer next; 32 CommandDataContainer next;
33 while (state.is_running) { 33 while (state.is_running) {
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index af1bebc4f..5ec99a126 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -35,15 +35,19 @@ public:
35 explicit RendererBase(Core::Frontend::EmuWindow& window); 35 explicit RendererBase(Core::Frontend::EmuWindow& window);
36 virtual ~RendererBase(); 36 virtual ~RendererBase();
37 37
38 /// Swap buffers (render frame)
39 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
40
41 /// Initialize the renderer 38 /// Initialize the renderer
42 virtual bool Init() = 0; 39 virtual bool Init() = 0;
43 40
44 /// Shutdown the renderer 41 /// Shutdown the renderer
45 virtual void ShutDown() = 0; 42 virtual void ShutDown() = 0;
46 43
44 /// Finalize rendering the guest frame and draw into the presentation texture
45 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
46
47 /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
48 /// specific implementation)
49 virtual void TryPresent(int timeout_ms) = 0;
50
47 // Getter/setter functions: 51 // Getter/setter functions:
48 // ------------------------ 52 // ------------------------
49 53
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index f0ddfb276..c0aee770f 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -15,6 +15,24 @@ MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_R
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18void OGLRenderbuffer::Create() {
19 if (handle != 0)
20 return;
21
22 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
23 glGenRenderbuffers(1, &handle);
24}
25
26void OGLRenderbuffer::Release() {
27 if (handle == 0)
28 return;
29
30 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
31 glDeleteRenderbuffers(1, &handle);
32 OpenGLState::GetCurState().ResetRenderbuffer(handle).Apply();
33 handle = 0;
34}
35
18void OGLTexture::Create(GLenum target) { 36void OGLTexture::Create(GLenum target) {
19 if (handle != 0) 37 if (handle != 0)
20 return; 38 return;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 514d1d165..995a4e45e 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -11,6 +11,31 @@
11 11
12namespace OpenGL { 12namespace OpenGL {
13 13
14class OGLRenderbuffer : private NonCopyable {
15public:
16 OGLRenderbuffer() = default;
17
18 OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
19
20 ~OGLRenderbuffer() {
21 Release();
22 }
23
24 OGLRenderbuffer& operator=(OGLRenderbuffer&& o) noexcept {
25 Release();
26 handle = std::exchange(o.handle, 0);
27 return *this;
28 }
29
30 /// Creates a new internal OpenGL resource and stores the handle
31 void Create();
32
33 /// Deletes the internal OpenGL resource
34 void Release();
35
36 GLuint handle = 0;
37};
38
14class OGLTexture : private NonCopyable { 39class OGLTexture : private NonCopyable {
15public: 40public:
16 OGLTexture() = default; 41 OGLTexture() = default;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index ab1f7983c..7d3bc1a1f 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -423,6 +423,13 @@ void OpenGLState::ApplyClipControl() {
423 } 423 }
424} 424}
425 425
426void OpenGLState::ApplyRenderBuffer() {
427 if (cur_state.renderbuffer != renderbuffer) {
428 cur_state.renderbuffer = renderbuffer;
429 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
430 }
431}
432
426void OpenGLState::ApplyTextures() { 433void OpenGLState::ApplyTextures() {
427 const std::size_t size = std::size(textures); 434 const std::size_t size = std::size(textures);
428 for (std::size_t i = 0; i < size; ++i) { 435 for (std::size_t i = 0; i < size; ++i) {
@@ -478,6 +485,7 @@ void OpenGLState::Apply() {
478 ApplyPolygonOffset(); 485 ApplyPolygonOffset();
479 ApplyAlphaTest(); 486 ApplyAlphaTest();
480 ApplyClipControl(); 487 ApplyClipControl();
488 ApplyRenderBuffer();
481} 489}
482 490
483void OpenGLState::EmulateViewportWithScissor() { 491void OpenGLState::EmulateViewportWithScissor() {
@@ -551,4 +559,11 @@ OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
551 return *this; 559 return *this;
552} 560}
553 561
562OpenGLState& OpenGLState::ResetRenderbuffer(GLuint handle) {
563 if (renderbuffer == handle) {
564 renderbuffer = 0;
565 }
566 return *this;
567}
568
554} // namespace OpenGL 569} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 4953eeda2..bce662f2c 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -158,6 +158,8 @@ public:
158 GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE; 158 GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE;
159 } clip_control; 159 } clip_control;
160 160
161 GLuint renderbuffer{}; // GL_RENDERBUFFER_BINDING
162
161 OpenGLState(); 163 OpenGLState();
162 164
163 /// Get the currently active OpenGL state 165 /// Get the currently active OpenGL state
@@ -196,6 +198,7 @@ public:
196 void ApplyPolygonOffset(); 198 void ApplyPolygonOffset();
197 void ApplyAlphaTest(); 199 void ApplyAlphaTest();
198 void ApplyClipControl(); 200 void ApplyClipControl();
201 void ApplyRenderBuffer();
199 202
200 /// Resets any references to the given resource 203 /// Resets any references to the given resource
201 OpenGLState& UnbindTexture(GLuint handle); 204 OpenGLState& UnbindTexture(GLuint handle);
@@ -204,6 +207,7 @@ public:
204 OpenGLState& ResetPipeline(GLuint handle); 207 OpenGLState& ResetPipeline(GLuint handle);
205 OpenGLState& ResetVertexArray(GLuint handle); 208 OpenGLState& ResetVertexArray(GLuint handle);
206 OpenGLState& ResetFramebuffer(GLuint handle); 209 OpenGLState& ResetFramebuffer(GLuint handle);
210 OpenGLState& ResetRenderbuffer(GLuint handle);
207 211
208 /// Viewport does not affects glClearBuffer so emulate viewport using scissor test 212 /// Viewport does not affects glClearBuffer so emulate viewport using scissor test
209 void EmulateViewportWithScissor(); 213 void EmulateViewportWithScissor();
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index bba16afaf..447f69d4d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -9,11 +9,11 @@
9#include <glad/glad.h> 9#include <glad/glad.h>
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/microprofile.h"
12#include "common/telemetry.h" 13#include "common/telemetry.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/core_timing.h" 15#include "core/core_timing.h"
15#include "core/frontend/emu_window.h" 16#include "core/frontend/emu_window.h"
16#include "core/frontend/scope_acquire_window_context.h"
17#include "core/memory.h" 17#include "core/memory.h"
18#include "core/perf_stats.h" 18#include "core/perf_stats.h"
19#include "core/settings.h" 19#include "core/settings.h"
@@ -24,6 +24,144 @@
24 24
25namespace OpenGL { 25namespace OpenGL {
26 26
27// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
28// to wait on available presentation frames.
29constexpr std::size_t SWAP_CHAIN_SIZE = 3;
30
31struct Frame {
32 u32 width{}; /// Width of the frame (to detect resize)
33 u32 height{}; /// Height of the frame
34 bool color_reloaded{}; /// Texture attachment was recreated (ie: resized)
35 OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO
36 OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread
37 OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
38 GLsync render_fence{}; /// Fence created on the render thread
39 GLsync present_fence{}; /// Fence created on the presentation thread
40 bool is_srgb{}; /// Framebuffer is sRGB or RGB
41};
42
43/**
44 * For smooth Vsync rendering, we want to always present the latest frame that the core generates,
45 * but also make sure that rendering happens at the pace that the frontend dictates. This is a
46 * helper class that the renderer uses to sync frames between the render thread and the presentation
47 * thread
48 */
49class FrameMailbox {
50public:
51 std::mutex swap_chain_lock;
52 std::condition_variable present_cv;
53 std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
54 std::queue<Frame*> free_queue;
55 std::deque<Frame*> present_queue;
56 Frame* previous_frame{};
57
58 FrameMailbox() {
59 for (auto& frame : swap_chain) {
60 free_queue.push(&frame);
61 }
62 }
63
64 ~FrameMailbox() {
65 // lock the mutex and clear out the present and free_queues and notify any people who are
66 // blocked to prevent deadlock on shutdown
67 std::scoped_lock lock{swap_chain_lock};
68 std::queue<Frame*>().swap(free_queue);
69 present_queue.clear();
70 present_cv.notify_all();
71 }
72
73 void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
74 frame->present.Release();
75 frame->present.Create();
76 GLint previous_draw_fbo{};
77 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
78 glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
79 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
80 frame->color.handle);
81 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
82 LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
83 }
84 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
85 frame->color_reloaded = false;
86 }
87
88 void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
89 OpenGLState prev_state = OpenGLState::GetCurState();
90 OpenGLState state = OpenGLState::GetCurState();
91
92 // Recreate the color texture attachment
93 frame->color.Release();
94 frame->color.Create();
95 state.renderbuffer = frame->color.handle;
96 state.Apply();
97 glRenderbufferStorage(GL_RENDERBUFFER, frame->is_srgb ? GL_SRGB8 : GL_RGB8, width, height);
98
99 // Recreate the FBO for the render target
100 frame->render.Release();
101 frame->render.Create();
102 state.draw.read_framebuffer = frame->render.handle;
103 state.draw.draw_framebuffer = frame->render.handle;
104 state.Apply();
105 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
106 frame->color.handle);
107 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
108 LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
109 }
110 prev_state.Apply();
111 frame->width = width;
112 frame->height = height;
113 frame->color_reloaded = true;
114 }
115
116 Frame* GetRenderFrame() {
117 std::unique_lock lock{swap_chain_lock};
118
119 // If theres no free frames, we will reuse the oldest render frame
120 if (free_queue.empty()) {
121 auto frame = present_queue.back();
122 present_queue.pop_back();
123 return frame;
124 }
125
126 Frame* frame = free_queue.front();
127 free_queue.pop();
128 return frame;
129 }
130
131 void ReleaseRenderFrame(Frame* frame) {
132 std::unique_lock lock{swap_chain_lock};
133 present_queue.push_front(frame);
134 present_cv.notify_one();
135 }
136
137 Frame* TryGetPresentFrame(int timeout_ms) {
138 std::unique_lock lock{swap_chain_lock};
139 // wait for new entries in the present_queue
140 present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
141 [&] { return !present_queue.empty(); });
142 if (present_queue.empty()) {
143 // timed out waiting for a frame to draw so return the previous frame
144 return previous_frame;
145 }
146
147 // free the previous frame and add it back to the free queue
148 if (previous_frame) {
149 free_queue.push(previous_frame);
150 }
151
152 // the newest entries are pushed to the front of the queue
153 Frame* frame = present_queue.front();
154 present_queue.pop_front();
155 // remove all old entries from the present queue and move them back to the free_queue
156 for (auto f : present_queue) {
157 free_queue.push(f);
158 }
159 present_queue.clear();
160 previous_frame = frame;
161 return frame;
162 }
163};
164
27namespace { 165namespace {
28 166
29constexpr char vertex_shader[] = R"( 167constexpr char vertex_shader[] = R"(
@@ -158,21 +296,91 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
158} // Anonymous namespace 296} // Anonymous namespace
159 297
160RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) 298RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
161 : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {} 299 : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
300 frame_mailbox{std::make_unique<FrameMailbox>()} {}
162 301
163RendererOpenGL::~RendererOpenGL() = default; 302RendererOpenGL::~RendererOpenGL() = default;
164 303
304MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
305MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
306
165void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 307void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
308 render_window.PollEvents();
309
310 if (!framebuffer) {
311 return;
312 }
313
166 // Maintain the rasterizer's state as a priority 314 // Maintain the rasterizer's state as a priority
167 OpenGLState prev_state = OpenGLState::GetCurState(); 315 OpenGLState prev_state = OpenGLState::GetCurState();
168 state.AllDirty(); 316 state.AllDirty();
169 state.Apply(); 317 state.Apply();
170 318
319 PrepareRendertarget(framebuffer);
320 RenderScreenshot();
321
322 Frame* frame;
323 {
324 MICROPROFILE_SCOPE(OpenGL_WaitPresent);
325
326 frame = frame_mailbox->GetRenderFrame();
327
328 // Clean up sync objects before drawing
329
330 // INTEL driver workaround. We can't delete the previous render sync object until we are
331 // sure that the presentation is done
332 if (frame->present_fence) {
333 glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
334 }
335
336 // delete the draw fence if the frame wasn't presented
337 if (frame->render_fence) {
338 glDeleteSync(frame->render_fence);
339 frame->render_fence = 0;
340 }
341
342 // wait for the presentation to be done
343 if (frame->present_fence) {
344 glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
345 glDeleteSync(frame->present_fence);
346 frame->present_fence = 0;
347 }
348 }
349
350 {
351 MICROPROFILE_SCOPE(OpenGL_RenderFrame);
352 const auto& layout = render_window.GetFramebufferLayout();
353
354 // Recreate the frame if the size of the window has changed
355 if (layout.width != frame->width || layout.height != frame->height ||
356 is_srgb != frame->is_srgb) {
357 LOG_DEBUG(Render_OpenGL, "Reloading render frame");
358 is_srgb = frame->is_srgb = screen_info.display_srgb;
359 frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
360 }
361 state.draw.draw_framebuffer = frame->render.handle;
362 state.Apply();
363 DrawScreen(layout);
364 // Create a fence for the frontend to wait on and swap this frame to OffTex
365 frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
366 glFlush();
367 frame_mailbox->ReleaseRenderFrame(frame);
368 m_current_frame++;
369 rasterizer->TickFrame();
370 }
371
372 // Restore the rasterizer state
373 prev_state.AllDirty();
374 prev_state.Apply();
375}
376
377void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
171 if (framebuffer) { 378 if (framebuffer) {
172 // If framebuffer is provided, reload it from memory to a texture 379 // If framebuffer is provided, reload it from memory to a texture
173 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || 380 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
174 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || 381 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
175 screen_info.texture.pixel_format != framebuffer->pixel_format) { 382 screen_info.texture.pixel_format != framebuffer->pixel_format ||
383 gl_framebuffer_data.empty()) {
176 // Reallocate texture if the framebuffer size has changed. 384 // Reallocate texture if the framebuffer size has changed.
177 // This is expected to not happen very often and hence should not be a 385 // This is expected to not happen very often and hence should not be a
178 // performance problem. 386 // performance problem.
@@ -181,22 +389,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
181 389
182 // Load the framebuffer from memory, draw it to the screen, and swap buffers 390 // Load the framebuffer from memory, draw it to the screen, and swap buffers
183 LoadFBToScreenInfo(*framebuffer); 391 LoadFBToScreenInfo(*framebuffer);
184
185 if (renderer_settings.screenshot_requested)
186 CaptureScreenshot();
187
188 DrawScreen(render_window.GetFramebufferLayout());
189
190 rasterizer->TickFrame();
191
192 render_window.SwapBuffers();
193 } 392 }
194
195 render_window.PollEvents();
196
197 // Restore the rasterizer state
198 prev_state.AllDirty();
199 prev_state.Apply();
200} 393}
201 394
202void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { 395void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
@@ -418,13 +611,48 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
418 DrawScreenTriangles(screen_info, static_cast<float>(screen.left), 611 DrawScreenTriangles(screen_info, static_cast<float>(screen.left),
419 static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()), 612 static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()),
420 static_cast<float>(screen.GetHeight())); 613 static_cast<float>(screen.GetHeight()));
614}
421 615
422 m_current_frame++; 616void RendererOpenGL::TryPresent(int timeout_ms) {
617 const auto& layout = render_window.GetFramebufferLayout();
618 auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
619 if (!frame) {
620 LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
621 return;
622 }
623
624 // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
625 // readback since we won't be doing any blending
626 glClear(GL_COLOR_BUFFER_BIT);
627
628 // Recreate the presentation FBO if the color attachment was changed
629 if (frame->color_reloaded) {
630 LOG_DEBUG(Render_OpenGL, "Reloading present frame");
631 frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
632 }
633 glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
634 // INTEL workaround.
635 // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
636 // it on the emulation thread without too much penalty
637 // glDeleteSync(frame.render_sync);
638 // frame.render_sync = 0;
639
640 glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
641 glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
642 GL_COLOR_BUFFER_BIT, GL_LINEAR);
643
644 // Insert fence for the main thread to block on
645 frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
646 glFlush();
647
648 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
423} 649}
424 650
425void RendererOpenGL::UpdateFramerate() {} 651void RendererOpenGL::RenderScreenshot() {
652 if (!renderer_settings.screenshot_requested) {
653 return;
654 }
426 655
427void RendererOpenGL::CaptureScreenshot() {
428 // Draw the current frame to the screenshot framebuffer 656 // Draw the current frame to the screenshot framebuffer
429 screenshot_framebuffer.Create(); 657 screenshot_framebuffer.Create();
430 GLuint old_read_fb = state.draw.read_framebuffer; 658 GLuint old_read_fb = state.draw.read_framebuffer;
@@ -459,8 +687,6 @@ void RendererOpenGL::CaptureScreenshot() {
459} 687}
460 688
461bool RendererOpenGL::Init() { 689bool RendererOpenGL::Init() {
462 Core::Frontend::ScopeAcquireWindowContext acquire_context{render_window};
463
464 if (GLAD_GL_KHR_debug) { 690 if (GLAD_GL_KHR_debug) {
465 glEnable(GL_DEBUG_OUTPUT); 691 glEnable(GL_DEBUG_OUTPUT);
466 glDebugMessageCallback(DebugHandler, nullptr); 692 glDebugMessageCallback(DebugHandler, nullptr);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index b56328a7f..4107e10a9 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -44,19 +44,23 @@ struct ScreenInfo {
44 TextureInfo texture; 44 TextureInfo texture;
45}; 45};
46 46
47struct PresentationTexture {
48 u32 width = 0;
49 u32 height = 0;
50 OGLTexture texture;
51};
52
53class FrameMailbox;
54
47class RendererOpenGL final : public VideoCore::RendererBase { 55class RendererOpenGL final : public VideoCore::RendererBase {
48public: 56public:
49 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); 57 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
50 ~RendererOpenGL() override; 58 ~RendererOpenGL() override;
51 59
52 /// Swap buffers (render frame)
53 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
54
55 /// Initialize the renderer
56 bool Init() override; 60 bool Init() override;
57
58 /// Shutdown the renderer
59 void ShutDown() override; 61 void ShutDown() override;
62 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
63 void TryPresent(int timeout_ms) override;
60 64
61private: 65private:
62 /// Initializes the OpenGL state and creates persistent objects. 66 /// Initializes the OpenGL state and creates persistent objects.
@@ -74,10 +78,7 @@ private:
74 78
75 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); 79 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
76 80
77 /// Updates the framerate. 81 void RenderScreenshot();
78 void UpdateFramerate();
79
80 void CaptureScreenshot();
81 82
82 /// Loads framebuffer from emulated memory into the active OpenGL texture. 83 /// Loads framebuffer from emulated memory into the active OpenGL texture.
83 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); 84 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
@@ -87,6 +88,8 @@ private:
87 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, 88 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
88 const TextureInfo& texture); 89 const TextureInfo& texture);
89 90
91 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
92
90 Core::Frontend::EmuWindow& emu_window; 93 Core::Frontend::EmuWindow& emu_window;
91 Core::System& system; 94 Core::System& system;
92 95
@@ -107,6 +110,12 @@ private:
107 /// Used for transforming the framebuffer orientation 110 /// Used for transforming the framebuffer orientation
108 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; 111 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
109 Common::Rectangle<int> framebuffer_crop_rect; 112 Common::Rectangle<int> framebuffer_crop_rect;
113
114 /// Represents if the final render frame is sRGB
115 bool is_srgb{};
116
117 /// Frame presentation mailbox
118 std::unique_ptr<FrameMailbox> frame_mailbox;
110}; 119};
111 120
112} // namespace OpenGL 121} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index d5032b432..ddc62bc97 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() {
106} 106}
107 107
108void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 108void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
109 render_window.PollEvents();
110
111 if (!framebuffer) {
112 return;
113 }
114
109 const auto& layout = render_window.GetFramebufferLayout(); 115 const auto& layout = render_window.GetFramebufferLayout();
110 if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) { 116 if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
111 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; 117 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
112 const bool use_accelerated = 118 const bool use_accelerated =
113 rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 119 rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
@@ -128,13 +134,16 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
128 blit_screen->Recreate(); 134 blit_screen->Recreate();
129 } 135 }
130 136
131 render_window.SwapBuffers();
132 rasterizer->TickFrame(); 137 rasterizer->TickFrame();
133 } 138 }
134 139
135 render_window.PollEvents(); 140 render_window.PollEvents();
136} 141}
137 142
143void RendererVulkan::TryPresent(int /*timeout_ms*/) {
144 // TODO (bunnei): ImplementMe
145}
146
138bool RendererVulkan::Init() { 147bool RendererVulkan::Init() {
139 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; 148 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
140 render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); 149 render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
@@ -262,4 +271,4 @@ void RendererVulkan::Report() const {
262 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); 271 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
263} 272}
264 273
265} // namespace Vulkan \ No newline at end of file 274} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index a472c5dc9..f513397f0 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -36,14 +36,10 @@ public:
36 explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system); 36 explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system);
37 ~RendererVulkan() override; 37 ~RendererVulkan() override;
38 38
39 /// Swap buffers (render frame)
40 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
41
42 /// Initialize the renderer
43 bool Init() override; 39 bool Init() override;
44
45 /// Shutdown the renderer
46 void ShutDown() override; 40 void ShutDown() override;
41 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
42 void TryPresent(int timeout_ms) override;
47 43
48private: 44private:
49 std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( 45 std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(