diff options
38 files changed, 787 insertions, 378 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 88c06b2ce..54be7dc0c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -131,8 +131,8 @@ add_library(core STATIC | |||
| 131 | frontend/framebuffer_layout.cpp | 131 | frontend/framebuffer_layout.cpp |
| 132 | frontend/framebuffer_layout.h | 132 | frontend/framebuffer_layout.h |
| 133 | frontend/input.h | 133 | frontend/input.h |
| 134 | frontend/scope_acquire_window_context.cpp | 134 | frontend/scope_acquire_context.cpp |
| 135 | frontend/scope_acquire_window_context.h | 135 | frontend/scope_acquire_context.h |
| 136 | gdbstub/gdbstub.cpp | 136 | gdbstub/gdbstub.cpp |
| 137 | gdbstub/gdbstub.h | 137 | gdbstub/gdbstub.h |
| 138 | hardware_interrupt_manager.cpp | 138 | hardware_interrupt_manager.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 86e314c94..a82faf127 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include "core/file_sys/sdmc_factory.h" | 24 | #include "core/file_sys/sdmc_factory.h" |
| 25 | #include "core/file_sys/vfs_concat.h" | 25 | #include "core/file_sys/vfs_concat.h" |
| 26 | #include "core/file_sys/vfs_real.h" | 26 | #include "core/file_sys/vfs_real.h" |
| 27 | #include "core/frontend/scope_acquire_context.h" | ||
| 27 | #include "core/gdbstub/gdbstub.h" | 28 | #include "core/gdbstub/gdbstub.h" |
| 28 | #include "core/hardware_interrupt_manager.h" | 29 | #include "core/hardware_interrupt_manager.h" |
| 29 | #include "core/hle/kernel/client_port.h" | 30 | #include "core/hle/kernel/client_port.h" |
| @@ -184,6 +185,8 @@ struct System::Impl { | |||
| 184 | 185 | ||
| 185 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, | 186 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, |
| 186 | const std::string& filepath) { | 187 | const std::string& filepath) { |
| 188 | Core::Frontend::ScopeAcquireContext acquire_context{emu_window}; | ||
| 189 | |||
| 187 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); | 190 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); |
| 188 | if (!app_loader) { | 191 | if (!app_loader) { |
| 189 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); | 192 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 3376eedc5..5eb87fb63 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -26,9 +26,6 @@ public: | |||
| 26 | 26 | ||
| 27 | /// Releases (dunno if this is the "right" word) the context from the caller thread | 27 | /// Releases (dunno if this is the "right" word) the context from the caller thread |
| 28 | virtual void DoneCurrent() = 0; | 28 | virtual void DoneCurrent() = 0; |
| 29 | |||
| 30 | /// Swap buffers to display the next frame | ||
| 31 | virtual void SwapBuffers() = 0; | ||
| 32 | }; | 29 | }; |
| 33 | 30 | ||
| 34 | /** | 31 | /** |
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 { | |||
| 29 | struct FramebufferLayout { | 29 | struct 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/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp new file mode 100644 index 000000000..878c3157c --- /dev/null +++ b/src/core/frontend/scope_acquire_context.cpp | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/frontend/emu_window.h" | ||
| 6 | #include "core/frontend/scope_acquire_context.h" | ||
| 7 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context) | ||
| 11 | : context{context} { | ||
| 12 | context.MakeCurrent(); | ||
| 13 | } | ||
| 14 | ScopeAcquireContext::~ScopeAcquireContext() { | ||
| 15 | context.DoneCurrent(); | ||
| 16 | } | ||
| 17 | |||
| 18 | } // namespace Core::Frontend | ||
diff --git a/src/core/frontend/scope_acquire_window_context.h b/src/core/frontend/scope_acquire_context.h index 2d9f6e825..7a65c0623 100644 --- a/src/core/frontend/scope_acquire_window_context.h +++ b/src/core/frontend/scope_acquire_context.h | |||
| @@ -8,16 +8,16 @@ | |||
| 8 | 8 | ||
| 9 | namespace Core::Frontend { | 9 | namespace Core::Frontend { |
| 10 | 10 | ||
| 11 | class EmuWindow; | 11 | class GraphicsContext; |
| 12 | 12 | ||
| 13 | /// Helper class to acquire/release window context within a given scope | 13 | /// Helper class to acquire/release window context within a given scope |
| 14 | class ScopeAcquireWindowContext : NonCopyable { | 14 | class ScopeAcquireContext : NonCopyable { |
| 15 | public: | 15 | public: |
| 16 | explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window); | 16 | explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context); |
| 17 | ~ScopeAcquireWindowContext(); | 17 | ~ScopeAcquireContext(); |
| 18 | 18 | ||
| 19 | private: | 19 | private: |
| 20 | Core::Frontend::EmuWindow& emu_window; | 20 | Core::Frontend::GraphicsContext& context; |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | } // namespace Core::Frontend | 23 | } // namespace Core::Frontend |
diff --git a/src/core/frontend/scope_acquire_window_context.cpp b/src/core/frontend/scope_acquire_window_context.cpp deleted file mode 100644 index 3663dad17..000000000 --- a/src/core/frontend/scope_acquire_window_context.cpp +++ /dev/null | |||
| @@ -1,18 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/frontend/emu_window.h" | ||
| 6 | #include "core/frontend/scope_acquire_window_context.h" | ||
| 7 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_) | ||
| 11 | : emu_window{emu_window_} { | ||
| 12 | emu_window.MakeCurrent(); | ||
| 13 | } | ||
| 14 | ScopeAcquireWindowContext::~ScopeAcquireWindowContext() { | ||
| 15 | emu_window.DoneCurrent(); | ||
| 16 | } | ||
| 17 | |||
| 18 | } // namespace Core::Frontend | ||
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d1fc94060..7c0303684 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -94,6 +94,7 @@ void LogSettings() { | |||
| 94 | LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); | 94 | LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); |
| 95 | LogSetting("Renderer_UseAsynchronousGpuEmulation", | 95 | LogSetting("Renderer_UseAsynchronousGpuEmulation", |
| 96 | Settings::values.use_asynchronous_gpu_emulation); | 96 | Settings::values.use_asynchronous_gpu_emulation); |
| 97 | LogSetting("Renderer_UseVsync", Settings::values.use_vsync); | ||
| 97 | LogSetting("Audio_OutputEngine", Settings::values.sink_id); | 98 | LogSetting("Audio_OutputEngine", Settings::values.sink_id); |
| 98 | LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); | 99 | LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); |
| 99 | LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); | 100 | LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); |
diff --git a/src/core/settings.h b/src/core/settings.h index f837d3fbc..15b691342 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -435,6 +435,7 @@ struct Values { | |||
| 435 | bool use_disk_shader_cache; | 435 | bool use_disk_shader_cache; |
| 436 | bool use_accurate_gpu_emulation; | 436 | bool use_accurate_gpu_emulation; |
| 437 | bool use_asynchronous_gpu_emulation; | 437 | bool use_asynchronous_gpu_emulation; |
| 438 | bool use_vsync; | ||
| 438 | bool force_30fps_mode; | 439 | bool force_30fps_mode; |
| 439 | 440 | ||
| 440 | float bg_red; | 441 | float bg_red; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 0e72d31cd..0f3685d1c 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -188,6 +188,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { | |||
| 188 | Settings::values.use_accurate_gpu_emulation); | 188 | Settings::values.use_accurate_gpu_emulation); |
| 189 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", | 189 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", |
| 190 | Settings::values.use_asynchronous_gpu_emulation); | 190 | Settings::values.use_asynchronous_gpu_emulation); |
| 191 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync); | ||
| 191 | AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); | 192 | AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); |
| 192 | } | 193 | } |
| 193 | 194 | ||
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 | ||
| 16 | namespace OpenGL { | 16 | namespace OpenGL { |
| 17 | 17 | ||
| 18 | void OGLRenderbuffer::Create() { | ||
| 19 | if (handle != 0) | ||
| 20 | return; | ||
| 21 | |||
| 22 | MICROPROFILE_SCOPE(OpenGL_ResourceCreation); | ||
| 23 | glGenRenderbuffers(1, &handle); | ||
| 24 | } | ||
| 25 | |||
| 26 | void 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 | |||
| 18 | void OGLTexture::Create(GLenum target) { | 36 | void 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 | ||
| 12 | namespace OpenGL { | 12 | namespace OpenGL { |
| 13 | 13 | ||
| 14 | class OGLRenderbuffer : private NonCopyable { | ||
| 15 | public: | ||
| 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 | |||
| 14 | class OGLTexture : private NonCopyable { | 39 | class OGLTexture : private NonCopyable { |
| 15 | public: | 40 | public: |
| 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 | ||
| 426 | void OpenGLState::ApplyRenderBuffer() { | ||
| 427 | if (cur_state.renderbuffer != renderbuffer) { | ||
| 428 | cur_state.renderbuffer = renderbuffer; | ||
| 429 | glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 426 | void OpenGLState::ApplyTextures() { | 433 | void 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 | ||
| 483 | void OpenGLState::EmulateViewportWithScissor() { | 491 | void OpenGLState::EmulateViewportWithScissor() { |
| @@ -551,4 +559,11 @@ OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) { | |||
| 551 | return *this; | 559 | return *this; |
| 552 | } | 560 | } |
| 553 | 561 | ||
| 562 | OpenGLState& 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 | ||
| 25 | namespace OpenGL { | 25 | namespace 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. | ||
| 29 | constexpr std::size_t SWAP_CHAIN_SIZE = 3; | ||
| 30 | |||
| 31 | struct 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 | */ | ||
| 49 | class FrameMailbox { | ||
| 50 | public: | ||
| 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 | |||
| 27 | namespace { | 165 | namespace { |
| 28 | 166 | ||
| 29 | constexpr char vertex_shader[] = R"( | 167 | constexpr 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 | ||
| 160 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) | 298 | RendererOpenGL::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 | ||
| 163 | RendererOpenGL::~RendererOpenGL() = default; | 302 | RendererOpenGL::~RendererOpenGL() = default; |
| 164 | 303 | ||
| 304 | MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64)); | ||
| 305 | MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); | ||
| 306 | |||
| 165 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 307 | void 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 | |||
| 377 | void 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 | ||
| 202 | void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { | 395 | void 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++; | 616 | void 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 | ||
| 425 | void RendererOpenGL::UpdateFramerate() {} | 651 | void RendererOpenGL::RenderScreenshot() { |
| 652 | if (!renderer_settings.screenshot_requested) { | ||
| 653 | return; | ||
| 654 | } | ||
| 426 | 655 | ||
| 427 | void 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 | ||
| 461 | bool RendererOpenGL::Init() { | 689 | bool 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 | ||
| 47 | struct PresentationTexture { | ||
| 48 | u32 width = 0; | ||
| 49 | u32 height = 0; | ||
| 50 | OGLTexture texture; | ||
| 51 | }; | ||
| 52 | |||
| 53 | class FrameMailbox; | ||
| 54 | |||
| 47 | class RendererOpenGL final : public VideoCore::RendererBase { | 55 | class RendererOpenGL final : public VideoCore::RendererBase { |
| 48 | public: | 56 | public: |
| 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 | ||
| 61 | private: | 65 | private: |
| 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 | ||
| 108 | void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 108 | void 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 | ||
| 143 | void RendererVulkan::TryPresent(int /*timeout_ms*/) { | ||
| 144 | // TODO (bunnei): ImplementMe | ||
| 145 | } | ||
| 146 | |||
| 138 | bool RendererVulkan::Init() { | 147 | bool 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 | ||
| 48 | private: | 44 | private: |
| 49 | std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( | 45 | std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 55a37fffa..c3dbb1a88 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -9,6 +9,9 @@ | |||
| 9 | #include <QKeyEvent> | 9 | #include <QKeyEvent> |
| 10 | #include <QMessageBox> | 10 | #include <QMessageBox> |
| 11 | #include <QOffscreenSurface> | 11 | #include <QOffscreenSurface> |
| 12 | #include <QOpenGLContext> | ||
| 13 | #include <QOpenGLFunctions> | ||
| 14 | #include <QOpenGLFunctions_4_3_Core> | ||
| 12 | #include <QOpenGLWindow> | 15 | #include <QOpenGLWindow> |
| 13 | #include <QPainter> | 16 | #include <QPainter> |
| 14 | #include <QScreen> | 17 | #include <QScreen> |
| @@ -23,9 +26,10 @@ | |||
| 23 | #include "common/assert.h" | 26 | #include "common/assert.h" |
| 24 | #include "common/microprofile.h" | 27 | #include "common/microprofile.h" |
| 25 | #include "common/scm_rev.h" | 28 | #include "common/scm_rev.h" |
| 29 | #include "common/scope_exit.h" | ||
| 26 | #include "core/core.h" | 30 | #include "core/core.h" |
| 27 | #include "core/frontend/framebuffer_layout.h" | 31 | #include "core/frontend/framebuffer_layout.h" |
| 28 | #include "core/frontend/scope_acquire_window_context.h" | 32 | #include "core/frontend/scope_acquire_context.h" |
| 29 | #include "core/settings.h" | 33 | #include "core/settings.h" |
| 30 | #include "input_common/keyboard.h" | 34 | #include "input_common/keyboard.h" |
| 31 | #include "input_common/main.h" | 35 | #include "input_common/main.h" |
| @@ -35,15 +39,27 @@ | |||
| 35 | #include "yuzu/bootmanager.h" | 39 | #include "yuzu/bootmanager.h" |
| 36 | #include "yuzu/main.h" | 40 | #include "yuzu/main.h" |
| 37 | 41 | ||
| 38 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} | 42 | EmuThread::EmuThread(GRenderWindow& window) |
| 43 | : shared_context{window.CreateSharedContext()}, | ||
| 44 | context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context | ||
| 45 | : window} {} | ||
| 39 | 46 | ||
| 40 | EmuThread::~EmuThread() = default; | 47 | EmuThread::~EmuThread() = default; |
| 41 | 48 | ||
| 42 | void EmuThread::run() { | 49 | static GMainWindow* GetMainWindow() { |
| 43 | render_window->MakeCurrent(); | 50 | for (QWidget* w : qApp->topLevelWidgets()) { |
| 51 | if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) { | ||
| 52 | return main; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | return nullptr; | ||
| 56 | } | ||
| 44 | 57 | ||
| 58 | void EmuThread::run() { | ||
| 45 | MicroProfileOnThreadCreate("EmuThread"); | 59 | MicroProfileOnThreadCreate("EmuThread"); |
| 46 | 60 | ||
| 61 | Core::Frontend::ScopeAcquireContext acquire_context{context}; | ||
| 62 | |||
| 47 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 63 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 48 | 64 | ||
| 49 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( | 65 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( |
| @@ -53,11 +69,6 @@ void EmuThread::run() { | |||
| 53 | 69 | ||
| 54 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | 70 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); |
| 55 | 71 | ||
| 56 | if (Settings::values.use_asynchronous_gpu_emulation) { | ||
| 57 | // Release OpenGL context for the GPU thread | ||
| 58 | render_window->DoneCurrent(); | ||
| 59 | } | ||
| 60 | |||
| 61 | // Holds whether the cpu was running during the last iteration, | 72 | // Holds whether the cpu was running during the last iteration, |
| 62 | // so that the DebugModeLeft signal can be emitted before the | 73 | // so that the DebugModeLeft signal can be emitted before the |
| 63 | // next execution step | 74 | // next execution step |
| @@ -98,190 +109,202 @@ void EmuThread::run() { | |||
| 98 | #if MICROPROFILE_ENABLED | 109 | #if MICROPROFILE_ENABLED |
| 99 | MicroProfileOnThreadExit(); | 110 | MicroProfileOnThreadExit(); |
| 100 | #endif | 111 | #endif |
| 101 | |||
| 102 | render_window->moveContext(); | ||
| 103 | } | 112 | } |
| 104 | 113 | ||
| 105 | class GGLContext : public Core::Frontend::GraphicsContext { | 114 | class GGLContext : public Core::Frontend::GraphicsContext { |
| 106 | public: | 115 | public: |
| 107 | explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} { | 116 | explicit GGLContext(QOpenGLContext* shared_context) |
| 108 | context.setFormat(shared_context->format()); | 117 | : context(new QOpenGLContext(shared_context->parent())), |
| 109 | context.setShareContext(shared_context); | 118 | surface(new QOffscreenSurface(nullptr)) { |
| 110 | context.create(); | 119 | |
| 120 | // disable vsync for any shared contexts | ||
| 121 | auto format = shared_context->format(); | ||
| 122 | format.setSwapInterval(0); | ||
| 123 | |||
| 124 | context->setShareContext(shared_context); | ||
| 125 | context->setFormat(format); | ||
| 126 | context->create(); | ||
| 127 | surface->setParent(shared_context->parent()); | ||
| 128 | surface->setFormat(format); | ||
| 129 | surface->create(); | ||
| 111 | } | 130 | } |
| 112 | 131 | ||
| 113 | void MakeCurrent() override { | 132 | void MakeCurrent() override { |
| 114 | context.makeCurrent(shared_context->surface()); | 133 | context->makeCurrent(surface); |
| 115 | } | 134 | } |
| 116 | 135 | ||
| 117 | void DoneCurrent() override { | 136 | void DoneCurrent() override { |
| 118 | context.doneCurrent(); | 137 | context->doneCurrent(); |
| 119 | } | 138 | } |
| 120 | 139 | ||
| 121 | void SwapBuffers() override {} | ||
| 122 | |||
| 123 | private: | 140 | private: |
| 124 | QOpenGLContext* shared_context; | 141 | QOpenGLContext* context; |
| 125 | QOpenGLContext context; | 142 | QOffscreenSurface* surface; |
| 126 | }; | 143 | }; |
| 127 | 144 | ||
| 128 | class GWidgetInternal : public QWindow { | 145 | class ChildRenderWindow : public QWindow { |
| 129 | public: | 146 | public: |
| 130 | GWidgetInternal(GRenderWindow* parent) : parent(parent) {} | 147 | ChildRenderWindow(QWindow* parent, QWidget* event_handler) |
| 131 | virtual ~GWidgetInternal() = default; | 148 | : QWindow{parent}, event_handler{event_handler} {} |
| 132 | 149 | ||
| 133 | void resizeEvent(QResizeEvent* ev) override { | 150 | virtual ~ChildRenderWindow() = default; |
| 134 | parent->OnClientAreaResized(ev->size().width(), ev->size().height()); | ||
| 135 | parent->OnFramebufferSizeChanged(); | ||
| 136 | } | ||
| 137 | 151 | ||
| 138 | void keyPressEvent(QKeyEvent* event) override { | 152 | virtual void Present() = 0; |
| 139 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 140 | } | ||
| 141 | 153 | ||
| 142 | void keyReleaseEvent(QKeyEvent* event) override { | 154 | protected: |
| 143 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | 155 | bool event(QEvent* event) override { |
| 156 | switch (event->type()) { | ||
| 157 | case QEvent::UpdateRequest: | ||
| 158 | Present(); | ||
| 159 | return true; | ||
| 160 | case QEvent::MouseButtonPress: | ||
| 161 | case QEvent::MouseButtonRelease: | ||
| 162 | case QEvent::MouseButtonDblClick: | ||
| 163 | case QEvent::MouseMove: | ||
| 164 | case QEvent::KeyPress: | ||
| 165 | case QEvent::KeyRelease: | ||
| 166 | case QEvent::FocusIn: | ||
| 167 | case QEvent::FocusOut: | ||
| 168 | case QEvent::FocusAboutToChange: | ||
| 169 | case QEvent::Enter: | ||
| 170 | case QEvent::Leave: | ||
| 171 | case QEvent::Wheel: | ||
| 172 | case QEvent::TabletMove: | ||
| 173 | case QEvent::TabletPress: | ||
| 174 | case QEvent::TabletRelease: | ||
| 175 | case QEvent::TabletEnterProximity: | ||
| 176 | case QEvent::TabletLeaveProximity: | ||
| 177 | case QEvent::TouchBegin: | ||
| 178 | case QEvent::TouchUpdate: | ||
| 179 | case QEvent::TouchEnd: | ||
| 180 | case QEvent::InputMethodQuery: | ||
| 181 | case QEvent::TouchCancel: | ||
| 182 | return QCoreApplication::sendEvent(event_handler, event); | ||
| 183 | case QEvent::Drop: | ||
| 184 | GetMainWindow()->DropAction(static_cast<QDropEvent*>(event)); | ||
| 185 | return true; | ||
| 186 | case QEvent::DragResponse: | ||
| 187 | case QEvent::DragEnter: | ||
| 188 | case QEvent::DragLeave: | ||
| 189 | case QEvent::DragMove: | ||
| 190 | GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event)); | ||
| 191 | return true; | ||
| 192 | default: | ||
| 193 | return QWindow::event(event); | ||
| 194 | } | ||
| 144 | } | 195 | } |
| 145 | 196 | ||
| 146 | void mousePressEvent(QMouseEvent* event) override { | 197 | void exposeEvent(QExposeEvent* event) override { |
| 147 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 198 | QWindow::requestUpdate(); |
| 148 | return; // touch input is handled in TouchBeginEvent | 199 | QWindow::exposeEvent(event); |
| 149 | |||
| 150 | const auto pos{event->pos()}; | ||
| 151 | if (event->button() == Qt::LeftButton) { | ||
| 152 | const auto [x, y] = parent->ScaleTouch(pos); | ||
| 153 | parent->TouchPressed(x, y); | ||
| 154 | } else if (event->button() == Qt::RightButton) { | ||
| 155 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 156 | } | ||
| 157 | } | 200 | } |
| 158 | 201 | ||
| 159 | void mouseMoveEvent(QMouseEvent* event) override { | 202 | private: |
| 160 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 203 | QWidget* event_handler{}; |
| 161 | return; // touch input is handled in TouchUpdateEvent | 204 | }; |
| 162 | 205 | ||
| 163 | const auto pos{event->pos()}; | 206 | class OpenGLWindow final : public ChildRenderWindow { |
| 164 | const auto [x, y] = parent->ScaleTouch(pos); | 207 | public: |
| 165 | parent->TouchMoved(x, y); | 208 | OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) |
| 166 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | 209 | : ChildRenderWindow{parent, event_handler}, |
| 167 | } | 210 | context(new QOpenGLContext(shared_context->parent())) { |
| 168 | 211 | ||
| 169 | void mouseReleaseEvent(QMouseEvent* event) override { | 212 | // disable vsync for any shared contexts |
| 170 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 213 | auto format = shared_context->format(); |
| 171 | return; // touch input is handled in TouchEndEvent | 214 | format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); |
| 215 | this->setFormat(format); | ||
| 172 | 216 | ||
| 173 | if (event->button() == Qt::LeftButton) | 217 | context->setShareContext(shared_context); |
| 174 | parent->TouchReleased(); | 218 | context->setScreen(this->screen()); |
| 175 | else if (event->button() == Qt::RightButton) | 219 | context->setFormat(format); |
| 176 | InputCommon::GetMotionEmu()->EndTilt(); | 220 | context->create(); |
| 177 | } | ||
| 178 | 221 | ||
| 179 | void DisablePainting() { | 222 | setSurfaceType(QWindow::OpenGLSurface); |
| 180 | do_painting = false; | ||
| 181 | } | ||
| 182 | 223 | ||
| 183 | void EnablePainting() { | 224 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 184 | do_painting = true; | 225 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 185 | } | 226 | } |
| 186 | 227 | ||
| 187 | std::pair<unsigned, unsigned> GetSize() const { | 228 | ~OpenGLWindow() override { |
| 188 | return std::make_pair(width(), height()); | 229 | context->doneCurrent(); |
| 189 | } | 230 | } |
| 190 | 231 | ||
| 191 | protected: | 232 | void Present() override { |
| 192 | bool IsPaintingEnabled() const { | 233 | if (!isExposed()) { |
| 193 | return do_painting; | 234 | return; |
| 235 | } | ||
| 236 | |||
| 237 | context->makeCurrent(this); | ||
| 238 | Core::System::GetInstance().Renderer().TryPresent(100); | ||
| 239 | context->swapBuffers(this); | ||
| 240 | auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); | ||
| 241 | f->glFinish(); | ||
| 242 | QWindow::requestUpdate(); | ||
| 194 | } | 243 | } |
| 195 | 244 | ||
| 196 | private: | 245 | private: |
| 197 | GRenderWindow* parent; | 246 | QOpenGLContext* context{}; |
| 198 | bool do_painting = false; | ||
| 199 | }; | ||
| 200 | |||
| 201 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL | ||
| 202 | // context. | ||
| 203 | // The corresponding functionality is handled in EmuThread instead | ||
| 204 | class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow { | ||
| 205 | public: | ||
| 206 | GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) | ||
| 207 | : GWidgetInternal(parent), QOpenGLWindow(shared_context) {} | ||
| 208 | ~GGLWidgetInternal() override = default; | ||
| 209 | |||
| 210 | void paintEvent(QPaintEvent* ev) override { | ||
| 211 | if (IsPaintingEnabled()) { | ||
| 212 | QPainter painter(this); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | }; | 247 | }; |
| 216 | 248 | ||
| 217 | #ifdef HAS_VULKAN | 249 | #ifdef HAS_VULKAN |
| 218 | class GVKWidgetInternal final : public GWidgetInternal { | 250 | class VulkanWindow final : public ChildRenderWindow { |
| 219 | public: | 251 | public: |
| 220 | GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { | 252 | VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) |
| 253 | : ChildRenderWindow{parent, event_handler} { | ||
| 221 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); | 254 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); |
| 222 | setVulkanInstance(instance); | 255 | setVulkanInstance(instance); |
| 223 | } | 256 | } |
| 224 | ~GVKWidgetInternal() override = default; | 257 | |
| 258 | ~VulkanWindow() override = default; | ||
| 259 | |||
| 260 | void Present() override { | ||
| 261 | // TODO(bunnei): ImplementMe | ||
| 262 | } | ||
| 263 | |||
| 264 | private: | ||
| 265 | QWidget* event_handler{}; | ||
| 225 | }; | 266 | }; |
| 226 | #endif | 267 | #endif |
| 227 | 268 | ||
| 228 | GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) | 269 | GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) |
| 229 | : QWidget(parent), emu_thread(emu_thread) { | 270 | : QWidget(parent_), emu_thread(emu_thread) { |
| 230 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 271 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 231 | .arg(QString::fromUtf8(Common::g_build_name), | 272 | .arg(QString::fromUtf8(Common::g_build_name), |
| 232 | QString::fromUtf8(Common::g_scm_branch), | 273 | QString::fromUtf8(Common::g_scm_branch), |
| 233 | QString::fromUtf8(Common::g_scm_desc))); | 274 | QString::fromUtf8(Common::g_scm_desc))); |
| 234 | setAttribute(Qt::WA_AcceptTouchEvents); | 275 | setAttribute(Qt::WA_AcceptTouchEvents); |
| 235 | 276 | auto layout = new QHBoxLayout(this); | |
| 277 | layout->setMargin(0); | ||
| 278 | setLayout(layout); | ||
| 236 | InputCommon::Init(); | 279 | InputCommon::Init(); |
| 280 | |||
| 281 | GMainWindow* parent = GetMainWindow(); | ||
| 237 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | 282 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); |
| 238 | } | 283 | } |
| 239 | 284 | ||
| 240 | GRenderWindow::~GRenderWindow() { | 285 | GRenderWindow::~GRenderWindow() { |
| 241 | InputCommon::Shutdown(); | 286 | InputCommon::Shutdown(); |
| 242 | |||
| 243 | // Avoid an unordered destruction that generates a segfault | ||
| 244 | delete child; | ||
| 245 | } | 287 | } |
| 246 | 288 | ||
| 247 | void GRenderWindow::moveContext() { | 289 | void GRenderWindow::MakeCurrent() { |
| 248 | if (!context) { | 290 | if (core_context) { |
| 249 | return; | 291 | core_context->MakeCurrent(); |
| 250 | } | 292 | } |
| 251 | DoneCurrent(); | ||
| 252 | |||
| 253 | // If the thread started running, move the GL Context to the new thread. Otherwise, move it | ||
| 254 | // back. | ||
| 255 | auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) | ||
| 256 | ? emu_thread | ||
| 257 | : qApp->thread(); | ||
| 258 | context->moveToThread(thread); | ||
| 259 | } | 293 | } |
| 260 | 294 | ||
| 261 | void GRenderWindow::SwapBuffers() { | 295 | void GRenderWindow::DoneCurrent() { |
| 262 | if (context) { | 296 | if (core_context) { |
| 263 | context->swapBuffers(child); | 297 | core_context->DoneCurrent(); |
| 264 | } | 298 | } |
| 299 | } | ||
| 300 | |||
| 301 | void GRenderWindow::PollEvents() { | ||
| 265 | if (!first_frame) { | 302 | if (!first_frame) { |
| 266 | first_frame = true; | 303 | first_frame = true; |
| 267 | emit FirstFrameDisplayed(); | 304 | emit FirstFrameDisplayed(); |
| 268 | } | 305 | } |
| 269 | } | 306 | } |
| 270 | 307 | ||
| 271 | void GRenderWindow::MakeCurrent() { | ||
| 272 | if (context) { | ||
| 273 | context->makeCurrent(child); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | void GRenderWindow::DoneCurrent() { | ||
| 278 | if (context) { | ||
| 279 | context->doneCurrent(); | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | void GRenderWindow::PollEvents() {} | ||
| 284 | |||
| 285 | bool GRenderWindow::IsShown() const { | 308 | bool GRenderWindow::IsShown() const { |
| 286 | return !isMinimized(); | 309 | return !isMinimized(); |
| 287 | } | 310 | } |
| @@ -291,7 +314,7 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i | |||
| 291 | #ifdef HAS_VULKAN | 314 | #ifdef HAS_VULKAN |
| 292 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); | 315 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); |
| 293 | const VkInstance instance_copy = vk_instance->vkInstance(); | 316 | const VkInstance instance_copy = vk_instance->vkInstance(); |
| 294 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child); | 317 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window); |
| 295 | 318 | ||
| 296 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | 319 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); |
| 297 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); | 320 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); |
| @@ -309,21 +332,10 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i | |||
| 309 | void GRenderWindow::OnFramebufferSizeChanged() { | 332 | void GRenderWindow::OnFramebufferSizeChanged() { |
| 310 | // Screen changes potentially incur a change in screen DPI, hence we should update the | 333 | // Screen changes potentially incur a change in screen DPI, hence we should update the |
| 311 | // framebuffer size | 334 | // framebuffer size |
| 312 | const qreal pixelRatio{GetWindowPixelRatio()}; | 335 | const qreal pixel_ratio = windowPixelRatio(); |
| 313 | const auto size{child->GetSize()}; | 336 | const u32 width = this->width() * pixel_ratio; |
| 314 | UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); | 337 | const u32 height = this->height() * pixel_ratio; |
| 315 | } | 338 | UpdateCurrentFramebufferLayout(width, height); |
| 316 | |||
| 317 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { | ||
| 318 | if (child) { | ||
| 319 | child->keyPressEvent(event); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { | ||
| 324 | if (child) { | ||
| 325 | child->keyReleaseEvent(event); | ||
| 326 | } | ||
| 327 | } | 339 | } |
| 328 | 340 | ||
| 329 | void GRenderWindow::BackupGeometry() { | 341 | void GRenderWindow::BackupGeometry() { |
| @@ -351,13 +363,12 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 351 | return geometry; | 363 | return geometry; |
| 352 | } | 364 | } |
| 353 | 365 | ||
| 354 | qreal GRenderWindow::GetWindowPixelRatio() const { | 366 | qreal GRenderWindow::windowPixelRatio() const { |
| 355 | // windowHandle() might not be accessible until the window is displayed to screen. | 367 | return devicePixelRatio(); |
| 356 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; | ||
| 357 | } | 368 | } |
| 358 | 369 | ||
| 359 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { | 370 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { |
| 360 | const qreal pixel_ratio{GetWindowPixelRatio()}; | 371 | const qreal pixel_ratio = windowPixelRatio(); |
| 361 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 372 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), |
| 362 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | 373 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| 363 | } | 374 | } |
| @@ -367,6 +378,47 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { | |||
| 367 | QWidget::closeEvent(event); | 378 | QWidget::closeEvent(event); |
| 368 | } | 379 | } |
| 369 | 380 | ||
| 381 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | ||
| 382 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 383 | } | ||
| 384 | |||
| 385 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 386 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 387 | } | ||
| 388 | |||
| 389 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||
| 390 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 391 | return; // touch input is handled in TouchBeginEvent | ||
| 392 | |||
| 393 | auto pos = event->pos(); | ||
| 394 | if (event->button() == Qt::LeftButton) { | ||
| 395 | const auto [x, y] = ScaleTouch(pos); | ||
| 396 | this->TouchPressed(x, y); | ||
| 397 | } else if (event->button() == Qt::RightButton) { | ||
| 398 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||
| 403 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 404 | return; // touch input is handled in TouchUpdateEvent | ||
| 405 | |||
| 406 | auto pos = event->pos(); | ||
| 407 | const auto [x, y] = ScaleTouch(pos); | ||
| 408 | this->TouchMoved(x, y); | ||
| 409 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||
| 410 | } | ||
| 411 | |||
| 412 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | ||
| 413 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 414 | return; // touch input is handled in TouchEndEvent | ||
| 415 | |||
| 416 | if (event->button() == Qt::LeftButton) | ||
| 417 | this->TouchReleased(); | ||
| 418 | else if (event->button() == Qt::RightButton) | ||
| 419 | InputCommon::GetMotionEmu()->EndTilt(); | ||
| 420 | } | ||
| 421 | |||
| 370 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 422 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { |
| 371 | // TouchBegin always has exactly one touch point, so take the .first() | 423 | // TouchBegin always has exactly one touch point, so take the .first() |
| 372 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); | 424 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); |
| @@ -415,26 +467,20 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) { | |||
| 415 | InputCommon::GetKeyboard()->ReleaseAllKeys(); | 467 | InputCommon::GetKeyboard()->ReleaseAllKeys(); |
| 416 | } | 468 | } |
| 417 | 469 | ||
| 418 | void GRenderWindow::OnClientAreaResized(u32 width, u32 height) { | 470 | void GRenderWindow::resizeEvent(QResizeEvent* event) { |
| 419 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); | 471 | QWidget::resizeEvent(event); |
| 472 | OnFramebufferSizeChanged(); | ||
| 420 | } | 473 | } |
| 421 | 474 | ||
| 422 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | 475 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { |
| 423 | return std::make_unique<GGLContext>(context.get()); | 476 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| 477 | return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); | ||
| 478 | } | ||
| 479 | return {}; | ||
| 424 | } | 480 | } |
| 425 | 481 | ||
| 426 | bool GRenderWindow::InitRenderTarget() { | 482 | bool GRenderWindow::InitRenderTarget() { |
| 427 | shared_context.reset(); | 483 | ReleaseRenderTarget(); |
| 428 | context.reset(); | ||
| 429 | if (child) { | ||
| 430 | delete child; | ||
| 431 | } | ||
| 432 | if (container) { | ||
| 433 | delete container; | ||
| 434 | } | ||
| 435 | if (layout()) { | ||
| 436 | delete layout(); | ||
| 437 | } | ||
| 438 | 484 | ||
| 439 | first_frame = false; | 485 | first_frame = false; |
| 440 | 486 | ||
| @@ -451,13 +497,6 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 451 | break; | 497 | break; |
| 452 | } | 498 | } |
| 453 | 499 | ||
| 454 | container = QWidget::createWindowContainer(child, this); | ||
| 455 | QBoxLayout* layout = new QHBoxLayout(this); | ||
| 456 | |||
| 457 | layout->addWidget(container); | ||
| 458 | layout->setMargin(0); | ||
| 459 | setLayout(layout); | ||
| 460 | |||
| 461 | // Reset minimum required size to avoid resizing issues on the main window after restarting. | 500 | // Reset minimum required size to avoid resizing issues on the main window after restarting. |
| 462 | setMinimumSize(1, 1); | 501 | setMinimumSize(1, 1); |
| 463 | 502 | ||
| @@ -467,14 +506,9 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 467 | hide(); | 506 | hide(); |
| 468 | 507 | ||
| 469 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | 508 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); |
| 470 | child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 471 | container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 472 | 509 | ||
| 473 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 510 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 474 | |||
| 475 | OnFramebufferSizeChanged(); | 511 | OnFramebufferSizeChanged(); |
| 476 | NotifyClientAreaSizeChanged(child->GetSize()); | ||
| 477 | |||
| 478 | BackupGeometry(); | 512 | BackupGeometry(); |
| 479 | 513 | ||
| 480 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | 514 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| @@ -486,6 +520,14 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 486 | return true; | 520 | return true; |
| 487 | } | 521 | } |
| 488 | 522 | ||
| 523 | void GRenderWindow::ReleaseRenderTarget() { | ||
| 524 | if (child_widget) { | ||
| 525 | layout()->removeWidget(child_widget); | ||
| 526 | delete child_widget; | ||
| 527 | child_widget = nullptr; | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 489 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | 531 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { |
| 490 | auto& renderer = Core::System::GetInstance().Renderer(); | 532 | auto& renderer = Core::System::GetInstance().Renderer(); |
| 491 | 533 | ||
| @@ -521,16 +563,19 @@ bool GRenderWindow::InitializeOpenGL() { | |||
| 521 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | 563 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); |
| 522 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 564 | // TODO: expose a setting for buffer value (ie default/single/double/triple) |
| 523 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 565 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); |
| 524 | shared_context = std::make_unique<QOpenGLContext>(); | 566 | fmt.setSwapInterval(0); |
| 525 | shared_context->setFormat(fmt); | 567 | QSurfaceFormat::setDefaultFormat(fmt); |
| 526 | shared_context->create(); | 568 | |
| 527 | context = std::make_unique<QOpenGLContext>(); | 569 | GMainWindow* parent = GetMainWindow(); |
| 528 | context->setShareContext(shared_context.get()); | 570 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; |
| 529 | context->setFormat(fmt); | 571 | child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); |
| 530 | context->create(); | 572 | child_window->create(); |
| 531 | fmt.setSwapInterval(false); | 573 | child_widget = createWindowContainer(child_window, this); |
| 532 | 574 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | |
| 533 | child = new GGLWidgetInternal(this, shared_context.get()); | 575 | layout()->addWidget(child_widget); |
| 576 | |||
| 577 | core_context = CreateSharedContext(); | ||
| 578 | |||
| 534 | return true; | 579 | return true; |
| 535 | } | 580 | } |
| 536 | 581 | ||
| @@ -559,7 +604,14 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 559 | return false; | 604 | return false; |
| 560 | } | 605 | } |
| 561 | 606 | ||
| 562 | child = new GVKWidgetInternal(this, vk_instance.get()); | 607 | GMainWindow* parent = GetMainWindow(); |
| 608 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | ||
| 609 | child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); | ||
| 610 | child_window->create(); | ||
| 611 | child_widget = createWindowContainer(child_window, this); | ||
| 612 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 613 | layout()->addWidget(child_widget); | ||
| 614 | |||
| 563 | return true; | 615 | return true; |
| 564 | #else | 616 | #else |
| 565 | QMessageBox::critical(this, tr("Vulkan not available!"), | 617 | QMessageBox::critical(this, tr("Vulkan not available!"), |
| @@ -569,7 +621,7 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 569 | } | 621 | } |
| 570 | 622 | ||
| 571 | bool GRenderWindow::LoadOpenGL() { | 623 | bool GRenderWindow::LoadOpenGL() { |
| 572 | Core::Frontend::ScopeAcquireWindowContext acquire_context{*this}; | 624 | Core::Frontend::ScopeAcquireContext acquire_context{*this}; |
| 573 | if (!gladLoadGL()) { | 625 | if (!gladLoadGL()) { |
| 574 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), | 626 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), |
| 575 | tr("Your GPU may not support OpenGL 4.3, or you do not have the " | 627 | tr("Your GPU may not support OpenGL 4.3, or you do not have the " |
| @@ -621,12 +673,10 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const { | |||
| 621 | 673 | ||
| 622 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { | 674 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { |
| 623 | this->emu_thread = emu_thread; | 675 | this->emu_thread = emu_thread; |
| 624 | child->DisablePainting(); | ||
| 625 | } | 676 | } |
| 626 | 677 | ||
| 627 | void GRenderWindow::OnEmulationStopping() { | 678 | void GRenderWindow::OnEmulationStopping() { |
| 628 | emu_thread = nullptr; | 679 | emu_thread = nullptr; |
| 629 | child->EnablePainting(); | ||
| 630 | } | 680 | } |
| 631 | 681 | ||
| 632 | void GRenderWindow::showEvent(QShowEvent* event) { | 682 | void GRenderWindow::showEvent(QShowEvent* event) { |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 71a2fa321..79b030304 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -11,11 +11,13 @@ | |||
| 11 | #include <QImage> | 11 | #include <QImage> |
| 12 | #include <QThread> | 12 | #include <QThread> |
| 13 | #include <QWidget> | 13 | #include <QWidget> |
| 14 | #include <QWindow> | ||
| 14 | 15 | ||
| 15 | #include "common/thread.h" | 16 | #include "common/thread.h" |
| 16 | #include "core/core.h" | 17 | #include "core/core.h" |
| 17 | #include "core/frontend/emu_window.h" | 18 | #include "core/frontend/emu_window.h" |
| 18 | 19 | ||
| 20 | class GRenderWindow; | ||
| 19 | class QKeyEvent; | 21 | class QKeyEvent; |
| 20 | class QScreen; | 22 | class QScreen; |
| 21 | class QTouchEvent; | 23 | class QTouchEvent; |
| @@ -26,14 +28,6 @@ class QOpenGLContext; | |||
| 26 | class QVulkanInstance; | 28 | class QVulkanInstance; |
| 27 | #endif | 29 | #endif |
| 28 | 30 | ||
| 29 | class GWidgetInternal; | ||
| 30 | class GGLWidgetInternal; | ||
| 31 | class GVKWidgetInternal; | ||
| 32 | class GMainWindow; | ||
| 33 | class GRenderWindow; | ||
| 34 | class QSurface; | ||
| 35 | class QOpenGLContext; | ||
| 36 | |||
| 37 | namespace VideoCore { | 31 | namespace VideoCore { |
| 38 | enum class LoadCallbackStage; | 32 | enum class LoadCallbackStage; |
| 39 | } | 33 | } |
| @@ -42,7 +36,7 @@ class EmuThread final : public QThread { | |||
| 42 | Q_OBJECT | 36 | Q_OBJECT |
| 43 | 37 | ||
| 44 | public: | 38 | public: |
| 45 | explicit EmuThread(GRenderWindow* render_window); | 39 | explicit EmuThread(GRenderWindow& window); |
| 46 | ~EmuThread() override; | 40 | ~EmuThread() override; |
| 47 | 41 | ||
| 48 | /** | 42 | /** |
| @@ -96,7 +90,11 @@ private: | |||
| 96 | std::mutex running_mutex; | 90 | std::mutex running_mutex; |
| 97 | std::condition_variable running_cv; | 91 | std::condition_variable running_cv; |
| 98 | 92 | ||
| 99 | GRenderWindow* render_window; | 93 | /// Only used in asynchronous GPU mode |
| 94 | std::unique_ptr<Core::Frontend::GraphicsContext> shared_context; | ||
| 95 | |||
| 96 | /// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode | ||
| 97 | Core::Frontend::GraphicsContext& context; | ||
| 100 | 98 | ||
| 101 | signals: | 99 | signals: |
| 102 | /** | 100 | /** |
| @@ -126,11 +124,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { | |||
| 126 | Q_OBJECT | 124 | Q_OBJECT |
| 127 | 125 | ||
| 128 | public: | 126 | public: |
| 129 | GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); | 127 | GRenderWindow(QWidget* parent, EmuThread* emu_thread); |
| 130 | ~GRenderWindow() override; | 128 | ~GRenderWindow() override; |
| 131 | 129 | ||
| 132 | // EmuWindow implementation | 130 | // EmuWindow implementation. |
| 133 | void SwapBuffers() override; | ||
| 134 | void MakeCurrent() override; | 131 | void MakeCurrent() override; |
| 135 | void DoneCurrent() override; | 132 | void DoneCurrent() override; |
| 136 | void PollEvents() override; | 133 | void PollEvents() override; |
| @@ -139,30 +136,36 @@ public: | |||
| 139 | void* surface) const override; | 136 | void* surface) const override; |
| 140 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 137 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 141 | 138 | ||
| 142 | void ForwardKeyPressEvent(QKeyEvent* event); | ||
| 143 | void ForwardKeyReleaseEvent(QKeyEvent* event); | ||
| 144 | |||
| 145 | void BackupGeometry(); | 139 | void BackupGeometry(); |
| 146 | void RestoreGeometry(); | 140 | void RestoreGeometry(); |
| 147 | void restoreGeometry(const QByteArray& geometry); // overridden | 141 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 148 | QByteArray saveGeometry(); // overridden | 142 | QByteArray saveGeometry(); // overridden |
| 149 | 143 | ||
| 150 | qreal GetWindowPixelRatio() const; | 144 | qreal windowPixelRatio() const; |
| 151 | std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||
| 152 | 145 | ||
| 153 | void closeEvent(QCloseEvent* event) override; | 146 | void closeEvent(QCloseEvent* event) override; |
| 147 | |||
| 148 | void resizeEvent(QResizeEvent* event) override; | ||
| 149 | |||
| 150 | void keyPressEvent(QKeyEvent* event) override; | ||
| 151 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 152 | |||
| 153 | void mousePressEvent(QMouseEvent* event) override; | ||
| 154 | void mouseMoveEvent(QMouseEvent* event) override; | ||
| 155 | void mouseReleaseEvent(QMouseEvent* event) override; | ||
| 156 | |||
| 154 | bool event(QEvent* event) override; | 157 | bool event(QEvent* event) override; |
| 155 | void focusOutEvent(QFocusEvent* event) override; | ||
| 156 | 158 | ||
| 157 | void OnClientAreaResized(u32 width, u32 height); | 159 | void focusOutEvent(QFocusEvent* event) override; |
| 158 | 160 | ||
| 159 | bool InitRenderTarget(); | 161 | bool InitRenderTarget(); |
| 160 | 162 | ||
| 163 | /// Destroy the previous run's child_widget which should also destroy the child_window | ||
| 164 | void ReleaseRenderTarget(); | ||
| 165 | |||
| 161 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); | 166 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); |
| 162 | 167 | ||
| 163 | public slots: | 168 | public slots: |
| 164 | void moveContext(); // overridden | ||
| 165 | |||
| 166 | void OnEmulationStarting(EmuThread* emu_thread); | 169 | void OnEmulationStarting(EmuThread* emu_thread); |
| 167 | void OnEmulationStopping(); | 170 | void OnEmulationStopping(); |
| 168 | void OnFramebufferSizeChanged(); | 171 | void OnFramebufferSizeChanged(); |
| @@ -173,6 +176,7 @@ signals: | |||
| 173 | void FirstFrameDisplayed(); | 176 | void FirstFrameDisplayed(); |
| 174 | 177 | ||
| 175 | private: | 178 | private: |
| 179 | std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||
| 176 | void TouchBeginEvent(const QTouchEvent* event); | 180 | void TouchBeginEvent(const QTouchEvent* event); |
| 177 | void TouchUpdateEvent(const QTouchEvent* event); | 181 | void TouchUpdateEvent(const QTouchEvent* event); |
| 178 | void TouchEndEvent(); | 182 | void TouchEndEvent(); |
| @@ -184,15 +188,9 @@ private: | |||
| 184 | bool LoadOpenGL(); | 188 | bool LoadOpenGL(); |
| 185 | QStringList GetUnsupportedGLExtensions() const; | 189 | QStringList GetUnsupportedGLExtensions() const; |
| 186 | 190 | ||
| 187 | QWidget* container = nullptr; | ||
| 188 | GWidgetInternal* child = nullptr; | ||
| 189 | |||
| 190 | EmuThread* emu_thread; | 191 | EmuThread* emu_thread; |
| 191 | // Context that backs the GGLWidgetInternal (and will be used by core to render) | 192 | |
| 192 | std::unique_ptr<QOpenGLContext> context; | 193 | std::unique_ptr<GraphicsContext> core_context; |
| 193 | // Context that will be shared between all newly created contexts. This should never be made | ||
| 194 | // current | ||
| 195 | std::unique_ptr<QOpenGLContext> shared_context; | ||
| 196 | 194 | ||
| 197 | #ifdef HAS_VULKAN | 195 | #ifdef HAS_VULKAN |
| 198 | std::unique_ptr<QVulkanInstance> vk_instance; | 196 | std::unique_ptr<QVulkanInstance> vk_instance; |
| @@ -202,6 +200,15 @@ private: | |||
| 202 | QImage screenshot_image; | 200 | QImage screenshot_image; |
| 203 | 201 | ||
| 204 | QByteArray geometry; | 202 | QByteArray geometry; |
| 203 | |||
| 204 | /// Native window handle that backs this presentation widget | ||
| 205 | QWindow* child_window = nullptr; | ||
| 206 | |||
| 207 | /// In order to embed the window into GRenderWindow, you need to use createWindowContainer to | ||
| 208 | /// put the child_window into a widget then add it to the layout. This child_widget can be | ||
| 209 | /// parented to GRenderWindow and use Qt's lifetime system | ||
| 210 | QWidget* child_widget = nullptr; | ||
| 211 | |||
| 205 | bool first_frame = false; | 212 | bool first_frame = false; |
| 206 | 213 | ||
| 207 | protected: | 214 | protected: |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 6209fff75..d0f574147 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -640,6 +640,7 @@ void Config::ReadRendererValues() { | |||
| 640 | ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); | 640 | ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); |
| 641 | Settings::values.use_asynchronous_gpu_emulation = | 641 | Settings::values.use_asynchronous_gpu_emulation = |
| 642 | ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); | 642 | ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); |
| 643 | Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool(); | ||
| 643 | Settings::values.force_30fps_mode = | 644 | Settings::values.force_30fps_mode = |
| 644 | ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); | 645 | ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); |
| 645 | 646 | ||
| @@ -1074,6 +1075,7 @@ void Config::SaveRendererValues() { | |||
| 1074 | Settings::values.use_accurate_gpu_emulation, false); | 1075 | Settings::values.use_accurate_gpu_emulation, false); |
| 1075 | WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), | 1076 | WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), |
| 1076 | Settings::values.use_asynchronous_gpu_emulation, false); | 1077 | Settings::values.use_asynchronous_gpu_emulation, false); |
| 1078 | WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); | ||
| 1077 | WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); | 1079 | WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); |
| 1078 | 1080 | ||
| 1079 | // Cast to double because Qt's written float values are not human-readable | 1081 | // Cast to double because Qt's written float values are not human-readable |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index ea899c080..fe64c7d81 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -103,6 +103,8 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 103 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 103 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| 104 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); | 104 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); |
| 105 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 105 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); |
| 106 | ui->use_vsync->setEnabled(runtime_lock); | ||
| 107 | ui->use_vsync->setChecked(Settings::values.use_vsync); | ||
| 106 | ui->force_30fps_mode->setEnabled(runtime_lock); | 108 | ui->force_30fps_mode->setEnabled(runtime_lock); |
| 107 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | 109 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); |
| 108 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 110 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| @@ -120,6 +122,7 @@ void ConfigureGraphics::ApplyConfiguration() { | |||
| 120 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 122 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); |
| 121 | Settings::values.use_asynchronous_gpu_emulation = | 123 | Settings::values.use_asynchronous_gpu_emulation = |
| 122 | ui->use_asynchronous_gpu_emulation->isChecked(); | 124 | ui->use_asynchronous_gpu_emulation->isChecked(); |
| 125 | Settings::values.use_vsync = ui->use_vsync->isChecked(); | ||
| 123 | Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); | 126 | Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); |
| 124 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); | 127 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); |
| 125 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); | 128 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index db60426ab..9acc7dd93 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -85,6 +85,16 @@ | |||
| 85 | </widget> | 85 | </widget> |
| 86 | </item> | 86 | </item> |
| 87 | <item> | 87 | <item> |
| 88 | <widget class="QCheckBox" name="use_vsync"> | ||
| 89 | <property name="toolTip"> | ||
| 90 | <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string> | ||
| 91 | </property> | ||
| 92 | <property name="text"> | ||
| 93 | <string>Use VSync (OpenGL only)</string> | ||
| 94 | </property> | ||
| 95 | </widget> | ||
| 96 | </item> | ||
| 97 | <item> | ||
| 88 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> | 98 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> |
| 89 | <property name="text"> | 99 | <property name="text"> |
| 90 | <string>Use accurate GPU emulation (slow)</string> | 100 | <string>Use accurate GPU emulation (slow)</string> |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 54ca2dc1d..47615adfe 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -20,7 +20,6 @@ | |||
| 20 | #include "core/file_sys/vfs.h" | 20 | #include "core/file_sys/vfs.h" |
| 21 | #include "core/file_sys/vfs_real.h" | 21 | #include "core/file_sys/vfs_real.h" |
| 22 | #include "core/frontend/applets/general_frontend.h" | 22 | #include "core/frontend/applets/general_frontend.h" |
| 23 | #include "core/frontend/scope_acquire_window_context.h" | ||
| 24 | #include "core/hle/service/acc/profile_manager.h" | 23 | #include "core/hle/service/acc/profile_manager.h" |
| 25 | #include "core/hle/service/am/applet_ae.h" | 24 | #include "core/hle/service/am/applet_ae.h" |
| 26 | #include "core/hle/service/am/applet_oe.h" | 25 | #include "core/hle/service/am/applet_oe.h" |
| @@ -985,11 +984,8 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 985 | return; | 984 | return; |
| 986 | 985 | ||
| 987 | // Create and start the emulation thread | 986 | // Create and start the emulation thread |
| 988 | emu_thread = std::make_unique<EmuThread>(render_window); | 987 | emu_thread = std::make_unique<EmuThread>(*render_window); |
| 989 | emit EmulationStarting(emu_thread.get()); | 988 | emit EmulationStarting(emu_thread.get()); |
| 990 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | ||
| 991 | render_window->moveContext(); | ||
| 992 | } | ||
| 993 | emu_thread->start(); | 989 | emu_thread->start(); |
| 994 | 990 | ||
| 995 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 991 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| @@ -1087,6 +1083,9 @@ void GMainWindow::ShutdownGame() { | |||
| 1087 | emulation_running = false; | 1083 | emulation_running = false; |
| 1088 | 1084 | ||
| 1089 | game_path.clear(); | 1085 | game_path.clear(); |
| 1086 | |||
| 1087 | // When closing the game, destroy the GLWindow to clear the context after the game is closed | ||
| 1088 | render_window->ReleaseRenderTarget(); | ||
| 1090 | } | 1089 | } |
| 1091 | 1090 | ||
| 1092 | void GMainWindow::StoreRecentFile(const QString& filename) { | 1091 | void GMainWindow::StoreRecentFile(const QString& filename) { |
| @@ -2215,48 +2214,47 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 2215 | QWidget::closeEvent(event); | 2214 | QWidget::closeEvent(event); |
| 2216 | } | 2215 | } |
| 2217 | 2216 | ||
| 2218 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | 2217 | static bool IsSingleFileDropEvent(const QMimeData* mime) { |
| 2219 | if (render_window) { | 2218 | return mime->hasUrls() && mime->urls().length() == 1; |
| 2220 | render_window->ForwardKeyPressEvent(event); | ||
| 2221 | } | ||
| 2222 | } | 2219 | } |
| 2223 | 2220 | ||
| 2224 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | 2221 | void GMainWindow::AcceptDropEvent(QDropEvent* event) { |
| 2225 | if (render_window) { | 2222 | if (IsSingleFileDropEvent(event->mimeData())) { |
| 2226 | render_window->ForwardKeyReleaseEvent(event); | 2223 | event->setDropAction(Qt::DropAction::LinkAction); |
| 2224 | event->accept(); | ||
| 2227 | } | 2225 | } |
| 2228 | } | 2226 | } |
| 2229 | 2227 | ||
| 2230 | static bool IsSingleFileDropEvent(QDropEvent* event) { | 2228 | bool GMainWindow::DropAction(QDropEvent* event) { |
| 2231 | const QMimeData* mimeData = event->mimeData(); | 2229 | if (!IsSingleFileDropEvent(event->mimeData())) { |
| 2232 | return mimeData->hasUrls() && mimeData->urls().length() == 1; | 2230 | return false; |
| 2233 | } | ||
| 2234 | |||
| 2235 | void GMainWindow::dropEvent(QDropEvent* event) { | ||
| 2236 | if (!IsSingleFileDropEvent(event)) { | ||
| 2237 | return; | ||
| 2238 | } | 2231 | } |
| 2239 | 2232 | ||
| 2240 | const QMimeData* mime_data = event->mimeData(); | 2233 | const QMimeData* mime_data = event->mimeData(); |
| 2241 | const QString filename = mime_data->urls().at(0).toLocalFile(); | 2234 | const QString& filename = mime_data->urls().at(0).toLocalFile(); |
| 2242 | 2235 | ||
| 2243 | if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) { | 2236 | if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) { |
| 2237 | // Amiibo | ||
| 2244 | LoadAmiibo(filename); | 2238 | LoadAmiibo(filename); |
| 2245 | } else { | 2239 | } else { |
| 2240 | // Game | ||
| 2246 | if (ConfirmChangeGame()) { | 2241 | if (ConfirmChangeGame()) { |
| 2247 | BootGame(filename); | 2242 | BootGame(filename); |
| 2248 | } | 2243 | } |
| 2249 | } | 2244 | } |
| 2245 | return true; | ||
| 2246 | } | ||
| 2247 | |||
| 2248 | void GMainWindow::dropEvent(QDropEvent* event) { | ||
| 2249 | DropAction(event); | ||
| 2250 | } | 2250 | } |
| 2251 | 2251 | ||
| 2252 | void GMainWindow::dragEnterEvent(QDragEnterEvent* event) { | 2252 | void GMainWindow::dragEnterEvent(QDragEnterEvent* event) { |
| 2253 | if (IsSingleFileDropEvent(event)) { | 2253 | AcceptDropEvent(event); |
| 2254 | event->acceptProposedAction(); | ||
| 2255 | } | ||
| 2256 | } | 2254 | } |
| 2257 | 2255 | ||
| 2258 | void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { | 2256 | void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { |
| 2259 | event->acceptProposedAction(); | 2257 | AcceptDropEvent(event); |
| 2260 | } | 2258 | } |
| 2261 | 2259 | ||
| 2262 | bool GMainWindow::ConfirmChangeGame() { | 2260 | bool GMainWindow::ConfirmChangeGame() { |
| @@ -2377,6 +2375,7 @@ int main(int argc, char* argv[]) { | |||
| 2377 | 2375 | ||
| 2378 | // Enables the core to make the qt created contexts current on std::threads | 2376 | // Enables the core to make the qt created contexts current on std::threads |
| 2379 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2377 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
| 2378 | QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); | ||
| 2380 | QApplication app(argc, argv); | 2379 | QApplication app(argc, argv); |
| 2381 | 2380 | ||
| 2382 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 2381 | // Qt changes the locale and causes issues in float conversion using std::to_string() when |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8eba2172c..a67125567 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -78,6 +78,9 @@ public: | |||
| 78 | 78 | ||
| 79 | std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; | 79 | std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; |
| 80 | 80 | ||
| 81 | bool DropAction(QDropEvent* event); | ||
| 82 | void AcceptDropEvent(QDropEvent* event); | ||
| 83 | |||
| 81 | signals: | 84 | signals: |
| 82 | 85 | ||
| 83 | /** | 86 | /** |
| @@ -264,8 +267,4 @@ protected: | |||
| 264 | void dropEvent(QDropEvent* event) override; | 267 | void dropEvent(QDropEvent* event) override; |
| 265 | void dragEnterEvent(QDragEnterEvent* event) override; | 268 | void dragEnterEvent(QDragEnterEvent* event) override; |
| 266 | void dragMoveEvent(QDragMoveEvent* event) override; | 269 | void dragMoveEvent(QDragMoveEvent* event) override; |
| 267 | |||
| 268 | // Overrides used to forward signals to the render window when the focus moves out. | ||
| 269 | void keyPressEvent(QKeyEvent* event) override; | ||
| 270 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 271 | }; | 270 | }; |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 96f1ce3af..b77c12baf 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -390,6 +390,8 @@ void Config::ReadValues() { | |||
| 390 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); | 390 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); |
| 391 | Settings::values.use_asynchronous_gpu_emulation = | 391 | Settings::values.use_asynchronous_gpu_emulation = |
| 392 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); | 392 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); |
| 393 | Settings::values.use_vsync = | ||
| 394 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)); | ||
| 393 | 395 | ||
| 394 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); | 396 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); |
| 395 | Settings::values.bg_green = | 397 | Settings::values.bg_green = |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 8a2b658cd..df7473858 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -150,6 +150,11 @@ use_accurate_gpu_emulation = | |||
| 150 | # 0 : Off (slow), 1 (default): On (fast) | 150 | # 0 : Off (slow), 1 (default): On (fast) |
| 151 | use_asynchronous_gpu_emulation = | 151 | use_asynchronous_gpu_emulation = |
| 152 | 152 | ||
| 153 | # Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can | ||
| 154 | # so only turn this off if you notice a speed difference. | ||
| 155 | # 0: Off, 1 (default): On | ||
| 156 | use_vsync = | ||
| 157 | |||
| 153 | # The clear color for the renderer. What shows up on the sides of the bottom screen. | 158 | # The clear color for the renderer. What shows up on the sides of the bottom screen. |
| 154 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. | 159 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. |
| 155 | bg_red = | 160 | bg_red = |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index e96139885..19584360c 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -13,7 +13,7 @@ | |||
| 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 | ||
| 16 | EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | 16 | EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen) : system{system} { |
| 17 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { | 17 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { |
| 18 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); | 18 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); |
| 19 | exit(1); | 19 | 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 b38f56661..fffac4252 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -10,9 +10,13 @@ | |||
| 10 | 10 | ||
| 11 | struct SDL_Window; | 11 | struct SDL_Window; |
| 12 | 12 | ||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 13 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { | 17 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { |
| 14 | public: | 18 | public: |
| 15 | explicit EmuWindow_SDL2(bool fullscreen); | 19 | explicit EmuWindow_SDL2(Core::System& system, bool fullscreen); |
| 16 | ~EmuWindow_SDL2(); | 20 | ~EmuWindow_SDL2(); |
| 17 | 21 | ||
| 18 | /// Polls window events | 22 | /// Polls window events |
| @@ -24,6 +28,9 @@ public: | |||
| 24 | /// Returns if window is shown (not minimized) | 28 | /// Returns if window is shown (not minimized) |
| 25 | bool IsShown() const override; | 29 | bool IsShown() const override; |
| 26 | 30 | ||
| 31 | /// Presents the next frame | ||
| 32 | virtual void Present() = 0; | ||
| 33 | |||
| 27 | protected: | 34 | protected: |
| 28 | /// Called by PollEvents when a key is pressed or released. | 35 | /// Called by PollEvents when a key is pressed or released. |
| 29 | void OnKeyEvent(int key, u8 state); | 36 | void OnKeyEvent(int key, u8 state); |
| @@ -55,6 +62,9 @@ protected: | |||
| 55 | /// Called when a configuration change affects the minimal size of the window | 62 | /// Called when a configuration change affects the minimal size of the window |
| 56 | void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; | 63 | void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; |
| 57 | 64 | ||
| 65 | /// Instance of the system, used to access renderer for the presentation thread | ||
| 66 | Core::System& system; | ||
| 67 | |||
| 58 | /// Is the window still open? | 68 | /// Is the window still open? |
| 59 | bool is_open = true; | 69 | bool is_open = true; |
| 60 | 70 | ||
| @@ -62,7 +72,7 @@ protected: | |||
| 62 | bool is_shown = true; | 72 | bool is_shown = true; |
| 63 | 73 | ||
| 64 | /// Internal SDL2 render window | 74 | /// Internal SDL2 render window |
| 65 | SDL_Window* render_window; | 75 | SDL_Window* render_window{}; |
| 66 | 76 | ||
| 67 | /// Keeps track of how often to update the title bar during gameplay | 77 | /// Keeps track of how often to update the title bar during gameplay |
| 68 | u32 last_time = 0; | 78 | u32 last_time = 0; |
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 7ffa0ac09..c0d373477 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -13,24 +13,25 @@ | |||
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | #include "common/scm_rev.h" | 14 | #include "common/scm_rev.h" |
| 15 | #include "common/string_util.h" | 15 | #include "common/string_util.h" |
| 16 | #include "core/core.h" | ||
| 16 | #include "core/settings.h" | 17 | #include "core/settings.h" |
| 17 | #include "input_common/keyboard.h" | 18 | #include "input_common/keyboard.h" |
| 18 | #include "input_common/main.h" | 19 | #include "input_common/main.h" |
| 19 | #include "input_common/motion_emu.h" | 20 | #include "input_common/motion_emu.h" |
| 21 | #include "video_core/renderer_base.h" | ||
| 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" | 22 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" |
| 21 | 23 | ||
| 22 | class SDLGLContext : public Core::Frontend::GraphicsContext { | 24 | class SDLGLContext : public Core::Frontend::GraphicsContext { |
| 23 | public: | 25 | public: |
| 24 | explicit SDLGLContext() { | 26 | explicit SDLGLContext() { |
| 25 | // create a hidden window to make the shared context against | 27 | // create a hidden window to make the shared context against |
| 26 | window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position | 28 | window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, |
| 27 | SDL_WINDOWPOS_UNDEFINED, // y position | 29 | SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); |
| 28 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 29 | SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); | ||
| 30 | context = SDL_GL_CreateContext(window); | 30 | context = SDL_GL_CreateContext(window); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | ~SDLGLContext() { | 33 | ~SDLGLContext() { |
| 34 | DoneCurrent(); | ||
| 34 | SDL_GL_DeleteContext(context); | 35 | SDL_GL_DeleteContext(context); |
| 35 | SDL_DestroyWindow(window); | 36 | SDL_DestroyWindow(window); |
| 36 | } | 37 | } |
| @@ -43,8 +44,6 @@ public: | |||
| 43 | SDL_GL_MakeCurrent(window, nullptr); | 44 | SDL_GL_MakeCurrent(window, nullptr); |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | void SwapBuffers() override {} | ||
| 47 | |||
| 48 | private: | 47 | private: |
| 49 | SDL_Window* window; | 48 | SDL_Window* window; |
| 50 | SDL_GLContext context; | 49 | SDL_GLContext context; |
| @@ -80,7 +79,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { | |||
| 80 | return unsupported_ext.empty(); | 79 | return unsupported_ext.empty(); |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscreen) { | 82 | EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen) |
| 83 | : EmuWindow_SDL2{system, fullscreen} { | ||
| 84 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); | 84 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); |
| 85 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | 85 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); |
| 86 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); | 86 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); |
| @@ -90,6 +90,7 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree | |||
| 90 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | 90 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |
| 91 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | 91 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); |
| 92 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); | 92 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); |
| 93 | SDL_GL_SetSwapInterval(0); | ||
| 93 | 94 | ||
| 94 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, | 95 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, |
| 95 | Common::g_scm_branch, Common::g_scm_desc); | 96 | Common::g_scm_branch, Common::g_scm_desc); |
| @@ -105,13 +106,22 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree | |||
| 105 | exit(1); | 106 | exit(1); |
| 106 | } | 107 | } |
| 107 | 108 | ||
| 109 | dummy_window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, | ||
| 110 | SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); | ||
| 111 | |||
| 108 | if (fullscreen) { | 112 | if (fullscreen) { |
| 109 | Fullscreen(); | 113 | Fullscreen(); |
| 110 | } | 114 | } |
| 111 | gl_context = SDL_GL_CreateContext(render_window); | ||
| 112 | 115 | ||
| 113 | if (gl_context == nullptr) { | 116 | window_context = SDL_GL_CreateContext(render_window); |
| 114 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); | 117 | core_context = CreateSharedContext(); |
| 118 | |||
| 119 | if (window_context == nullptr) { | ||
| 120 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError()); | ||
| 121 | exit(1); | ||
| 122 | } | ||
| 123 | if (core_context == nullptr) { | ||
| 124 | LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError()); | ||
| 115 | exit(1); | 125 | exit(1); |
| 116 | } | 126 | } |
| 117 | 127 | ||
| @@ -128,28 +138,22 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree | |||
| 128 | OnResize(); | 138 | OnResize(); |
| 129 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 139 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 130 | SDL_PumpEvents(); | 140 | SDL_PumpEvents(); |
| 131 | SDL_GL_SetSwapInterval(false); | ||
| 132 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, | 141 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, |
| 133 | Common::g_scm_desc); | 142 | Common::g_scm_desc); |
| 134 | Settings::LogSettings(); | 143 | Settings::LogSettings(); |
| 135 | |||
| 136 | DoneCurrent(); | ||
| 137 | } | 144 | } |
| 138 | 145 | ||
| 139 | EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { | 146 | EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { |
| 140 | SDL_GL_DeleteContext(gl_context); | 147 | core_context.reset(); |
| 141 | } | 148 | SDL_GL_DeleteContext(window_context); |
| 142 | |||
| 143 | void EmuWindow_SDL2_GL::SwapBuffers() { | ||
| 144 | SDL_GL_SwapWindow(render_window); | ||
| 145 | } | 149 | } |
| 146 | 150 | ||
| 147 | void EmuWindow_SDL2_GL::MakeCurrent() { | 151 | void EmuWindow_SDL2_GL::MakeCurrent() { |
| 148 | SDL_GL_MakeCurrent(render_window, gl_context); | 152 | core_context->MakeCurrent(); |
| 149 | } | 153 | } |
| 150 | 154 | ||
| 151 | void EmuWindow_SDL2_GL::DoneCurrent() { | 155 | void EmuWindow_SDL2_GL::DoneCurrent() { |
| 152 | SDL_GL_MakeCurrent(render_window, nullptr); | 156 | core_context->DoneCurrent(); |
| 153 | } | 157 | } |
| 154 | 158 | ||
| 155 | void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 159 | void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| @@ -161,3 +165,13 @@ void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, voi | |||
| 161 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { | 165 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { |
| 162 | return std::make_unique<SDLGLContext>(); | 166 | return std::make_unique<SDLGLContext>(); |
| 163 | } | 167 | } |
| 168 | |||
| 169 | void EmuWindow_SDL2_GL::Present() { | ||
| 170 | SDL_GL_MakeCurrent(render_window, window_context); | ||
| 171 | SDL_GL_SetSwapInterval(Settings::values.use_vsync ? 1 : 0); | ||
| 172 | while (IsOpen()) { | ||
| 173 | system.Renderer().TryPresent(100); | ||
| 174 | SDL_GL_SwapWindow(render_window); | ||
| 175 | } | ||
| 176 | SDL_GL_MakeCurrent(render_window, nullptr); | ||
| 177 | } | ||
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 c753085a8..b80669ff0 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | |||
| @@ -10,17 +10,12 @@ | |||
| 10 | 10 | ||
| 11 | class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { | 11 | class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { |
| 12 | public: | 12 | public: |
| 13 | explicit EmuWindow_SDL2_GL(bool fullscreen); | 13 | explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); |
| 14 | ~EmuWindow_SDL2_GL(); | 14 | ~EmuWindow_SDL2_GL(); |
| 15 | 15 | ||
| 16 | /// Swap buffers to display the next frame | ||
| 17 | void SwapBuffers() override; | ||
| 18 | |||
| 19 | /// Makes the graphics context current for the caller thread | ||
| 20 | void MakeCurrent() override; | 16 | void MakeCurrent() override; |
| 21 | |||
| 22 | /// Releases the GL context from the caller thread | ||
| 23 | void DoneCurrent() override; | 17 | void DoneCurrent() override; |
| 18 | void Present() override; | ||
| 24 | 19 | ||
| 25 | /// Ignored in OpenGL | 20 | /// Ignored in OpenGL |
| 26 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 21 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| @@ -29,10 +24,17 @@ public: | |||
| 29 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 24 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 30 | 25 | ||
| 31 | private: | 26 | private: |
| 27 | /// Fake hidden window for the core context | ||
| 28 | SDL_Window* dummy_window{}; | ||
| 29 | |||
| 32 | /// Whether the GPU and driver supports the OpenGL extension required | 30 | /// Whether the GPU and driver supports the OpenGL extension required |
| 33 | bool SupportsRequiredGLExtensions(); | 31 | bool SupportsRequiredGLExtensions(); |
| 34 | 32 | ||
| 35 | using SDL_GLContext = void*; | 33 | using SDL_GLContext = void*; |
| 34 | |||
| 36 | /// The OpenGL context associated with the window | 35 | /// The OpenGL context associated with the window |
| 37 | SDL_GLContext gl_context; | 36 | SDL_GLContext window_context; |
| 37 | |||
| 38 | /// The OpenGL context associated with the core | ||
| 39 | std::unique_ptr<Core::Frontend::GraphicsContext> core_context; | ||
| 38 | }; | 40 | }; |
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 a203f0da9..abcc58165 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -15,7 +15,8 @@ | |||
| 15 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" | 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" |
| 17 | 17 | ||
| 18 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) { | 18 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) |
| 19 | : EmuWindow_SDL2{system, fullscreen} { | ||
| 19 | if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { | 20 | if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { |
| 20 | LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); | 21 | LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); |
| 21 | exit(EXIT_FAILURE); | 22 | exit(EXIT_FAILURE); |
| @@ -110,8 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { | |||
| 110 | vkDestroyInstance(vk_instance, nullptr); | 111 | vkDestroyInstance(vk_instance, nullptr); |
| 111 | } | 112 | } |
| 112 | 113 | ||
| 113 | void EmuWindow_SDL2_VK::SwapBuffers() {} | ||
| 114 | |||
| 115 | void EmuWindow_SDL2_VK::MakeCurrent() { | 114 | void EmuWindow_SDL2_VK::MakeCurrent() { |
| 116 | // Unused on Vulkan | 115 | // Unused on Vulkan |
| 117 | } | 116 | } |
| @@ -160,3 +159,7 @@ bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanc | |||
| 160 | return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); | 159 | return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); |
| 161 | }) != layers.end(); | 160 | }) != layers.end(); |
| 162 | } | 161 | } |
| 162 | |||
| 163 | void EmuWindow_SDL2_VK::Present() { | ||
| 164 | // TODO (bunnei): ImplementMe | ||
| 165 | } | ||
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 2a7c06a24..1eb8c0868 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | |||
| @@ -10,19 +10,12 @@ | |||
| 10 | 10 | ||
| 11 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { | 11 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { |
| 12 | public: | 12 | public: |
| 13 | explicit EmuWindow_SDL2_VK(bool fullscreen); | 13 | explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); |
| 14 | ~EmuWindow_SDL2_VK(); | 14 | ~EmuWindow_SDL2_VK(); |
| 15 | 15 | ||
| 16 | /// Swap buffers to display the next frame | ||
| 17 | void SwapBuffers() override; | ||
| 18 | |||
| 19 | /// Makes the graphics context current for the caller thread | ||
| 20 | void MakeCurrent() override; | 16 | void MakeCurrent() override; |
| 21 | |||
| 22 | /// Releases the GL context from the caller thread | ||
| 23 | void DoneCurrent() override; | 17 | void DoneCurrent() override; |
| 24 | 18 | void Present() override; | |
| 25 | /// Retrieves Vulkan specific handlers from the window | ||
| 26 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 19 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 27 | void* surface) const override; | 20 | void* surface) const override; |
| 28 | 21 | ||
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 325795321..babf4c3a4 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -177,14 +177,16 @@ int main(int argc, char** argv) { | |||
| 177 | Settings::values.use_gdbstub = use_gdbstub; | 177 | Settings::values.use_gdbstub = use_gdbstub; |
| 178 | Settings::Apply(); | 178 | Settings::Apply(); |
| 179 | 179 | ||
| 180 | Core::System& system{Core::System::GetInstance()}; | ||
| 181 | |||
| 180 | std::unique_ptr<EmuWindow_SDL2> emu_window; | 182 | std::unique_ptr<EmuWindow_SDL2> emu_window; |
| 181 | switch (Settings::values.renderer_backend) { | 183 | switch (Settings::values.renderer_backend) { |
| 182 | case Settings::RendererBackend::OpenGL: | 184 | case Settings::RendererBackend::OpenGL: |
| 183 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen); | 185 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen); |
| 184 | break; | 186 | break; |
| 185 | case Settings::RendererBackend::Vulkan: | 187 | case Settings::RendererBackend::Vulkan: |
| 186 | #ifdef HAS_VULKAN | 188 | #ifdef HAS_VULKAN |
| 187 | emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen); | 189 | emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen); |
| 188 | break; | 190 | break; |
| 189 | #else | 191 | #else |
| 190 | LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); | 192 | LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); |
| @@ -192,12 +194,6 @@ int main(int argc, char** argv) { | |||
| 192 | #endif | 194 | #endif |
| 193 | } | 195 | } |
| 194 | 196 | ||
| 195 | if (!Settings::values.use_multi_core) { | ||
| 196 | // Single core mode must acquire OpenGL context for entire emulation session | ||
| 197 | emu_window->MakeCurrent(); | ||
| 198 | } | ||
| 199 | |||
| 200 | Core::System& system{Core::System::GetInstance()}; | ||
| 201 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | 197 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); |
| 202 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 198 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 203 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | 199 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); |
| @@ -234,12 +230,23 @@ int main(int argc, char** argv) { | |||
| 234 | 230 | ||
| 235 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); | 231 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); |
| 236 | 232 | ||
| 237 | emu_window->MakeCurrent(); | ||
| 238 | system.Renderer().Rasterizer().LoadDiskResources(); | 233 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 239 | 234 | ||
| 235 | // Acquire render context for duration of the thread if this is the rendering thread | ||
| 236 | if (!Settings::values.use_asynchronous_gpu_emulation) { | ||
| 237 | emu_window->MakeCurrent(); | ||
| 238 | } | ||
| 239 | SCOPE_EXIT({ | ||
| 240 | if (!Settings::values.use_asynchronous_gpu_emulation) { | ||
| 241 | emu_window->DoneCurrent(); | ||
| 242 | } | ||
| 243 | }); | ||
| 244 | |||
| 245 | std::thread render_thread([&emu_window] { emu_window->Present(); }); | ||
| 240 | while (emu_window->IsOpen()) { | 246 | while (emu_window->IsOpen()) { |
| 241 | system.RunLoop(); | 247 | system.RunLoop(); |
| 242 | } | 248 | } |
| 249 | render_thread.join(); | ||
| 243 | 250 | ||
| 244 | system.Shutdown(); | 251 | system.Shutdown(); |
| 245 | 252 | ||
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp index f2cc4a797..a1bdb1a12 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp | |||
| @@ -112,10 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { | |||
| 112 | SDL_Quit(); | 112 | SDL_Quit(); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | void EmuWindow_SDL2_Hide::SwapBuffers() { | ||
| 116 | SDL_GL_SwapWindow(render_window); | ||
| 117 | } | ||
| 118 | |||
| 119 | void EmuWindow_SDL2_Hide::PollEvents() {} | 115 | void EmuWindow_SDL2_Hide::PollEvents() {} |
| 120 | 116 | ||
| 121 | void EmuWindow_SDL2_Hide::MakeCurrent() { | 117 | void EmuWindow_SDL2_Hide::MakeCurrent() { |
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h index c7fccc002..b13e15309 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | |||
| @@ -13,9 +13,6 @@ public: | |||
| 13 | explicit EmuWindow_SDL2_Hide(); | 13 | explicit EmuWindow_SDL2_Hide(); |
| 14 | ~EmuWindow_SDL2_Hide(); | 14 | ~EmuWindow_SDL2_Hide(); |
| 15 | 15 | ||
| 16 | /// Swap buffers to display the next frame | ||
| 17 | void SwapBuffers() override; | ||
| 18 | |||
| 19 | /// Polls window events | 16 | /// Polls window events |
| 20 | void PollEvents() override; | 17 | void PollEvents() override; |
| 21 | 18 | ||