summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2020-02-17 14:31:14 -0500
committerGravatar bunnei2020-02-25 21:22:59 -0500
commitdc672ca4b39c1ab9c2ee81257b6fb605a23cbcd8 (patch)
tree15415ab06bdba99ca408dad1c526c31313c3e8af /src
parentrenderer_opengl: Add OGLRenderbuffer to resource/state management. (diff)
downloadyuzu-dc672ca4b39c1ab9c2ee81257b6fb605a23cbcd8.tar.gz
yuzu-dc672ca4b39c1ab9c2ee81257b6fb605a23cbcd8.tar.xz
yuzu-dc672ca4b39c1ab9c2ee81257b6fb605a23cbcd8.zip
renderer_opengl: Add texture mailbox support for presenter thread.
Diffstat (limited to 'src')
-rw-r--r--src/core/frontend/framebuffer_layout.h1
-rw-r--r--src/video_core/renderer_base.h10
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp269
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h24
4 files changed, 269 insertions, 35 deletions
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 1d39c1faf..e9d0a40d3 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -29,6 +29,7 @@ enum class AspectRatio {
29struct FramebufferLayout { 29struct FramebufferLayout {
30 u32 width{ScreenUndocked::Width}; 30 u32 width{ScreenUndocked::Width};
31 u32 height{ScreenUndocked::Height}; 31 u32 height{ScreenUndocked::Height};
32 bool is_srgb{};
32 33
33 Common::Rectangle<u32> screen; 34 Common::Rectangle<u32> screen;
34 35
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/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index bba16afaf..ee69caa3a 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"
@@ -22,8 +22,145 @@
22#include "video_core/renderer_opengl/gl_rasterizer.h" 22#include "video_core/renderer_opengl/gl_rasterizer.h"
23#include "video_core/renderer_opengl/renderer_opengl.h" 23#include "video_core/renderer_opengl/renderer_opengl.h"
24 24
25namespace Core::Frontend {
26
27struct Frame {
28 u32 width{}; /// Width of the frame (to detect resize)
29 u32 height{}; /// Height of the frame
30 bool color_reloaded = false; /// Texture attachment was recreated (ie: resized)
31 OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO
32 OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread
33 OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
34 GLsync render_fence{}; /// Fence created on the render thread
35 GLsync present_fence{}; /// Fence created on the presentation thread
36 bool is_srgb{}; /// Framebuffer is sRGB or RGB
37};
38
39} // namespace Core::Frontend
40
25namespace OpenGL { 41namespace OpenGL {
26 42
43// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
44// to wait on available presentation frames. There doesn't seem to be much of a downside to a larger
45// number but 9 swap textures at 60FPS presentation allows for 800% speed so thats probably fine
46constexpr std::size_t SWAP_CHAIN_SIZE = 9;
47
48class OGLTextureMailbox : public Core::Frontend::TextureMailbox {
49public:
50 std::mutex swap_chain_lock;
51 std::condition_variable present_cv;
52 std::array<Core::Frontend::Frame, SWAP_CHAIN_SIZE> swap_chain{};
53 std::queue<Core::Frontend::Frame*> free_queue;
54 std::deque<Core::Frontend::Frame*> present_queue;
55 Core::Frontend::Frame* previous_frame{};
56
57 OGLTextureMailbox() {
58 for (auto& frame : swap_chain) {
59 free_queue.push(&frame);
60 }
61 }
62
63 ~OGLTextureMailbox() override {
64 // lock the mutex and clear out the present and free_queues and notify any people who are
65 // blocked to prevent deadlock on shutdown
66 std::scoped_lock lock(swap_chain_lock);
67 std::queue<Core::Frontend::Frame*>().swap(free_queue);
68 present_queue.clear();
69 present_cv.notify_all();
70 }
71
72 void ReloadPresentFrame(Core::Frontend::Frame* frame, u32 height, u32 width) override {
73 frame->present.Release();
74 frame->present.Create();
75 GLint previous_draw_fbo{};
76 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
77 glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
78 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
79 frame->color.handle);
80 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
81 LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
82 }
83 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
84 frame->color_reloaded = false;
85 }
86
87 void ReloadRenderFrame(Core::Frontend::Frame* frame, u32 width, u32 height) override {
88 OpenGLState prev_state = OpenGLState::GetCurState();
89 OpenGLState state = OpenGLState::GetCurState();
90
91 // Recreate the color texture attachment
92 frame->color.Release();
93 frame->color.Create();
94 state.renderbuffer = frame->color.handle;
95 state.Apply();
96 glRenderbufferStorage(GL_RENDERBUFFER, frame->is_srgb ? GL_SRGB8 : GL_RGB8, width, height);
97
98 // Recreate the FBO for the render target
99 frame->render.Release();
100 frame->render.Create();
101 state.draw.read_framebuffer = frame->render.handle;
102 state.draw.draw_framebuffer = frame->render.handle;
103 state.Apply();
104 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
105 frame->color.handle);
106 if (!glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
107 LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
108 }
109 prev_state.Apply();
110 frame->width = width;
111 frame->height = height;
112 frame->color_reloaded = true;
113 }
114
115 Core::Frontend::Frame* GetRenderFrame() override {
116 std::unique_lock<std::mutex> lock(swap_chain_lock);
117
118 // If theres no free frames, we will reuse the oldest render frame
119 if (free_queue.empty()) {
120 auto frame = present_queue.back();
121 present_queue.pop_back();
122 return frame;
123 }
124
125 Core::Frontend::Frame* frame = free_queue.front();
126 free_queue.pop();
127 return frame;
128 }
129
130 void ReleaseRenderFrame(Core::Frontend::Frame* frame) override {
131 std::unique_lock<std::mutex> lock(swap_chain_lock);
132 present_queue.push_front(frame);
133 present_cv.notify_one();
134 }
135
136 Core::Frontend::Frame* TryGetPresentFrame(int timeout_ms) override {
137 std::unique_lock<std::mutex> lock(swap_chain_lock);
138 // wait for new entries in the present_queue
139 present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
140 [&] { return !present_queue.empty(); });
141 if (present_queue.empty()) {
142 // timed out waiting for a frame to draw so return the previous frame
143 return previous_frame;
144 }
145
146 // free the previous frame and add it back to the free queue
147 if (previous_frame) {
148 free_queue.push(previous_frame);
149 }
150
151 // the newest entries are pushed to the front of the queue
152 Core::Frontend::Frame* frame = present_queue.front();
153 present_queue.pop_front();
154 // remove all old entries from the present queue and move them back to the free_queue
155 for (auto f : present_queue) {
156 free_queue.push(f);
157 }
158 present_queue.clear();
159 previous_frame = frame;
160 return frame;
161 }
162};
163
27namespace { 164namespace {
28 165
29constexpr char vertex_shader[] = R"( 166constexpr char vertex_shader[] = R"(
@@ -158,16 +295,86 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
158} // Anonymous namespace 295} // Anonymous namespace
159 296
160RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) 297RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
161 : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {} 298 : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {
299 emu_window.mailbox = std::make_unique<OGLTextureMailbox>();
300}
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 Core::Frontend::Frame* frame;
323 {
324 MICROPROFILE_SCOPE(OpenGL_WaitPresent);
325
326 frame = render_window.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 render_window.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 render_window.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) ||
@@ -181,22 +388,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
181 388
182 // Load the framebuffer from memory, draw it to the screen, and swap buffers 389 // Load the framebuffer from memory, draw it to the screen, and swap buffers
183 LoadFBToScreenInfo(*framebuffer); 390 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 } 391 }
194
195 render_window.PollEvents();
196
197 // Restore the rasterizer state
198 prev_state.AllDirty();
199 prev_state.Apply();
200} 392}
201 393
202void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { 394void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
@@ -418,13 +610,48 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
418 DrawScreenTriangles(screen_info, static_cast<float>(screen.left), 610 DrawScreenTriangles(screen_info, static_cast<float>(screen.left),
419 static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()), 611 static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()),
420 static_cast<float>(screen.GetHeight())); 612 static_cast<float>(screen.GetHeight()));
613}
421 614
422 m_current_frame++; 615void RendererOpenGL::TryPresent(int timeout_ms) {
616 const auto& layout = render_window.GetFramebufferLayout();
617 auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms);
618 if (!frame) {
619 LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
620 return;
621 }
622
623 // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
624 // readback since we won't be doing any blending
625 glClear(GL_COLOR_BUFFER_BIT);
626
627 // Recreate the presentation FBO if the color attachment was changed
628 if (frame->color_reloaded) {
629 LOG_DEBUG(Render_OpenGL, "Reloading present frame");
630 render_window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
631 }
632 glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
633 // INTEL workaround.
634 // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
635 // it on the emulation thread without too much penalty
636 // glDeleteSync(frame.render_sync);
637 // frame.render_sync = 0;
638
639 glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
640 glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
641 GL_COLOR_BUFFER_BIT, GL_LINEAR);
642
643 // Insert fence for the main thread to block on
644 frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
645 glFlush();
646
647 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
423} 648}
424 649
425void RendererOpenGL::UpdateFramerate() {} 650void RendererOpenGL::RenderScreenshot() {
651 if (!renderer_settings.screenshot_requested) {
652 return;
653 }
426 654
427void RendererOpenGL::CaptureScreenshot() {
428 // Draw the current frame to the screenshot framebuffer 655 // Draw the current frame to the screenshot framebuffer
429 screenshot_framebuffer.Create(); 656 screenshot_framebuffer.Create();
430 GLuint old_read_fb = state.draw.read_framebuffer; 657 GLuint old_read_fb = state.draw.read_framebuffer;
@@ -459,8 +686,6 @@ void RendererOpenGL::CaptureScreenshot() {
459} 686}
460 687
461bool RendererOpenGL::Init() { 688bool RendererOpenGL::Init() {
462 Core::Frontend::ScopeAcquireWindowContext acquire_context{render_window};
463
464 if (GLAD_GL_KHR_debug) { 689 if (GLAD_GL_KHR_debug) {
465 glEnable(GL_DEBUG_OUTPUT); 690 glEnable(GL_DEBUG_OUTPUT);
466 glDebugMessageCallback(DebugHandler, nullptr); 691 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..797965925 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -44,19 +44,21 @@ 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
47class RendererOpenGL final : public VideoCore::RendererBase { 53class RendererOpenGL final : public VideoCore::RendererBase {
48public: 54public:
49 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); 55 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
50 ~RendererOpenGL() override; 56 ~RendererOpenGL() override;
51 57
52 /// Swap buffers (render frame)
53 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
54
55 /// Initialize the renderer
56 bool Init() override; 58 bool Init() override;
57
58 /// Shutdown the renderer
59 void ShutDown() override; 59 void ShutDown() override;
60 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
61 void TryPresent(int timeout_ms) override;
60 62
61private: 63private:
62 /// Initializes the OpenGL state and creates persistent objects. 64 /// Initializes the OpenGL state and creates persistent objects.
@@ -74,10 +76,7 @@ private:
74 76
75 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); 77 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
76 78
77 /// Updates the framerate. 79 void RenderScreenshot();
78 void UpdateFramerate();
79
80 void CaptureScreenshot();
81 80
82 /// Loads framebuffer from emulated memory into the active OpenGL texture. 81 /// Loads framebuffer from emulated memory into the active OpenGL texture.
83 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); 82 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
@@ -87,6 +86,8 @@ private:
87 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, 86 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
88 const TextureInfo& texture); 87 const TextureInfo& texture);
89 88
89 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
90
90 Core::Frontend::EmuWindow& emu_window; 91 Core::Frontend::EmuWindow& emu_window;
91 Core::System& system; 92 Core::System& system;
92 93
@@ -107,6 +108,9 @@ private:
107 /// Used for transforming the framebuffer orientation 108 /// Used for transforming the framebuffer orientation
108 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; 109 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
109 Common::Rectangle<int> framebuffer_crop_rect; 110 Common::Rectangle<int> framebuffer_crop_rect;
111
112 /// Represents if the final render frame is sRGB
113 bool is_srgb{};
110}; 114};
111 115
112} // namespace OpenGL 116} // namespace OpenGL