diff options
32 files changed, 381 insertions, 446 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e836bf396..66497a386 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -131,8 +131,6 @@ 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_context.cpp | ||
| 135 | frontend/scope_acquire_context.h | ||
| 136 | gdbstub/gdbstub.cpp | 134 | gdbstub/gdbstub.cpp |
| 137 | gdbstub/gdbstub.h | 135 | gdbstub/gdbstub.h |
| 138 | hardware_interrupt_manager.cpp | 136 | hardware_interrupt_manager.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index d1bc9340d..3bd90d79f 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -24,7 +24,6 @@ | |||
| 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" | ||
| 28 | #include "core/gdbstub/gdbstub.h" | 27 | #include "core/gdbstub/gdbstub.h" |
| 29 | #include "core/hardware_interrupt_manager.h" | 28 | #include "core/hardware_interrupt_manager.h" |
| 30 | #include "core/hle/kernel/client_port.h" | 29 | #include "core/hle/kernel/client_port.h" |
| @@ -168,13 +167,12 @@ struct System::Impl { | |||
| 168 | Service::Init(service_manager, system); | 167 | Service::Init(service_manager, system); |
| 169 | GDBStub::DeferStart(); | 168 | GDBStub::DeferStart(); |
| 170 | 169 | ||
| 171 | renderer = VideoCore::CreateRenderer(emu_window, system); | 170 | interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); |
| 172 | if (!renderer->Init()) { | 171 | gpu_core = VideoCore::CreateGPU(emu_window, system); |
| 172 | if (!gpu_core) { | ||
| 173 | return ResultStatus::ErrorVideoCore; | 173 | return ResultStatus::ErrorVideoCore; |
| 174 | } | 174 | } |
| 175 | interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); | 175 | gpu_core->Renderer().Rasterizer().SetupDirtyFlags(); |
| 176 | gpu_core = VideoCore::CreateGPU(system); | ||
| 177 | renderer->Rasterizer().SetupDirtyFlags(); | ||
| 178 | 176 | ||
| 179 | is_powered_on = true; | 177 | is_powered_on = true; |
| 180 | exit_lock = false; | 178 | exit_lock = false; |
| @@ -186,8 +184,6 @@ struct System::Impl { | |||
| 186 | 184 | ||
| 187 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, | 185 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, |
| 188 | const std::string& filepath) { | 186 | const std::string& filepath) { |
| 189 | Core::Frontend::ScopeAcquireContext acquire_context{emu_window}; | ||
| 190 | |||
| 191 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); | 187 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); |
| 192 | if (!app_loader) { | 188 | if (!app_loader) { |
| 193 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); | 189 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); |
| @@ -216,10 +212,6 @@ struct System::Impl { | |||
| 216 | AddGlueRegistrationForProcess(*app_loader, *main_process); | 212 | AddGlueRegistrationForProcess(*app_loader, *main_process); |
| 217 | kernel.MakeCurrentProcess(main_process.get()); | 213 | kernel.MakeCurrentProcess(main_process.get()); |
| 218 | 214 | ||
| 219 | // Main process has been loaded and been made current. | ||
| 220 | // Begin GPU and CPU execution. | ||
| 221 | gpu_core->Start(); | ||
| 222 | |||
| 223 | // Initialize cheat engine | 215 | // Initialize cheat engine |
| 224 | if (cheat_engine) { | 216 | if (cheat_engine) { |
| 225 | cheat_engine->Initialize(); | 217 | cheat_engine->Initialize(); |
| @@ -277,7 +269,6 @@ struct System::Impl { | |||
| 277 | } | 269 | } |
| 278 | 270 | ||
| 279 | // Shutdown emulation session | 271 | // Shutdown emulation session |
| 280 | renderer.reset(); | ||
| 281 | GDBStub::Shutdown(); | 272 | GDBStub::Shutdown(); |
| 282 | Service::Shutdown(); | 273 | Service::Shutdown(); |
| 283 | service_manager.reset(); | 274 | service_manager.reset(); |
| @@ -353,7 +344,6 @@ struct System::Impl { | |||
| 353 | Service::FileSystem::FileSystemController fs_controller; | 344 | Service::FileSystem::FileSystemController fs_controller; |
| 354 | /// AppLoader used to load the current executing application | 345 | /// AppLoader used to load the current executing application |
| 355 | std::unique_ptr<Loader::AppLoader> app_loader; | 346 | std::unique_ptr<Loader::AppLoader> app_loader; |
| 356 | std::unique_ptr<VideoCore::RendererBase> renderer; | ||
| 357 | std::unique_ptr<Tegra::GPU> gpu_core; | 347 | std::unique_ptr<Tegra::GPU> gpu_core; |
| 358 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | 348 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; |
| 359 | Memory::Memory memory; | 349 | Memory::Memory memory; |
| @@ -536,11 +526,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const { | |||
| 536 | } | 526 | } |
| 537 | 527 | ||
| 538 | VideoCore::RendererBase& System::Renderer() { | 528 | VideoCore::RendererBase& System::Renderer() { |
| 539 | return *impl->renderer; | 529 | return impl->gpu_core->Renderer(); |
| 540 | } | 530 | } |
| 541 | 531 | ||
| 542 | const VideoCore::RendererBase& System::Renderer() const { | 532 | const VideoCore::RendererBase& System::Renderer() const { |
| 543 | return *impl->renderer; | 533 | return impl->gpu_core->Renderer(); |
| 544 | } | 534 | } |
| 545 | 535 | ||
| 546 | Kernel::KernelCore& System::Kernel() { | 536 | Kernel::KernelCore& System::Kernel() { |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 5eb87fb63..72294d4d8 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -13,19 +13,39 @@ | |||
| 13 | namespace Core::Frontend { | 13 | namespace Core::Frontend { |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * Represents a graphics context that can be used for background computation or drawing. If the | 16 | * Represents a drawing context that supports graphics operations. |
| 17 | * graphics backend doesn't require the context, then the implementation of these methods can be | ||
| 18 | * stubs | ||
| 19 | */ | 17 | */ |
| 20 | class GraphicsContext { | 18 | class GraphicsContext { |
| 21 | public: | 19 | public: |
| 22 | virtual ~GraphicsContext(); | 20 | virtual ~GraphicsContext(); |
| 23 | 21 | ||
| 22 | /// Inform the driver to swap the front/back buffers and present the current image | ||
| 23 | virtual void SwapBuffers() {} | ||
| 24 | |||
| 24 | /// Makes the graphics context current for the caller thread | 25 | /// Makes the graphics context current for the caller thread |
| 25 | virtual void MakeCurrent() = 0; | 26 | virtual void MakeCurrent() {} |
| 26 | 27 | ||
| 27 | /// Releases (dunno if this is the "right" word) the context from the caller thread | 28 | /// Releases (dunno if this is the "right" word) the context from the caller thread |
| 28 | virtual void DoneCurrent() = 0; | 29 | virtual void DoneCurrent() {} |
| 30 | |||
| 31 | class Scoped { | ||
| 32 | public: | ||
| 33 | explicit Scoped(GraphicsContext& context_) : context(context_) { | ||
| 34 | context.MakeCurrent(); | ||
| 35 | } | ||
| 36 | ~Scoped() { | ||
| 37 | context.DoneCurrent(); | ||
| 38 | } | ||
| 39 | |||
| 40 | private: | ||
| 41 | GraphicsContext& context; | ||
| 42 | }; | ||
| 43 | |||
| 44 | /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value | ||
| 45 | /// ends | ||
| 46 | Scoped Acquire() { | ||
| 47 | return Scoped{*this}; | ||
| 48 | } | ||
| 29 | }; | 49 | }; |
| 30 | 50 | ||
| 31 | /** | 51 | /** |
| @@ -46,7 +66,7 @@ public: | |||
| 46 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please | 66 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please |
| 47 | * re-read the upper points again and think about it if you don't see this. | 67 | * re-read the upper points again and think about it if you don't see this. |
| 48 | */ | 68 | */ |
| 49 | class EmuWindow : public GraphicsContext { | 69 | class EmuWindow { |
| 50 | public: | 70 | public: |
| 51 | /// Data structure to store emuwindow configuration | 71 | /// Data structure to store emuwindow configuration |
| 52 | struct WindowConfig { | 72 | struct WindowConfig { |
| @@ -60,17 +80,9 @@ public: | |||
| 60 | virtual void PollEvents() = 0; | 80 | virtual void PollEvents() = 0; |
| 61 | 81 | ||
| 62 | /** | 82 | /** |
| 63 | * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This | 83 | * Returns a GraphicsContext that the frontend provides to be used for rendering. |
| 64 | * context can be used from other threads for background graphics computation. If the frontend | ||
| 65 | * is using a graphics backend that doesn't need anything specific to run on a different thread, | ||
| 66 | * then it can use a stubbed implemenation for GraphicsContext. | ||
| 67 | * | ||
| 68 | * If the return value is null, then the core should assume that the frontend cannot provide a | ||
| 69 | * Shared Context | ||
| 70 | */ | 84 | */ |
| 71 | virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const { | 85 | virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0; |
| 72 | return nullptr; | ||
| 73 | } | ||
| 74 | 86 | ||
| 75 | /// Returns if window is shown (not minimized) | 87 | /// Returns if window is shown (not minimized) |
| 76 | virtual bool IsShown() const = 0; | 88 | virtual bool IsShown() const = 0; |
diff --git a/src/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp deleted file mode 100644 index 878c3157c..000000000 --- a/src/core/frontend/scope_acquire_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_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_context.h b/src/core/frontend/scope_acquire_context.h deleted file mode 100644 index 7a65c0623..000000000 --- a/src/core/frontend/scope_acquire_context.h +++ /dev/null | |||
| @@ -1,23 +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 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Core::Frontend { | ||
| 10 | |||
| 11 | class GraphicsContext; | ||
| 12 | |||
| 13 | /// Helper class to acquire/release window context within a given scope | ||
| 14 | class ScopeAcquireContext : NonCopyable { | ||
| 15 | public: | ||
| 16 | explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context); | ||
| 17 | ~ScopeAcquireContext(); | ||
| 18 | |||
| 19 | private: | ||
| 20 | Core::Frontend::GraphicsContext& context; | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace Core::Frontend | ||
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index e8f763ce9..8acf2eda2 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/core_timing_util.h" | 9 | #include "core/core_timing_util.h" |
| 10 | #include "core/frontend/emu_window.h" | ||
| 10 | #include "core/memory.h" | 11 | #include "core/memory.h" |
| 11 | #include "video_core/engines/fermi_2d.h" | 12 | #include "video_core/engines/fermi_2d.h" |
| 12 | #include "video_core/engines/kepler_compute.h" | 13 | #include "video_core/engines/kepler_compute.h" |
| @@ -16,14 +17,15 @@ | |||
| 16 | #include "video_core/gpu.h" | 17 | #include "video_core/gpu.h" |
| 17 | #include "video_core/memory_manager.h" | 18 | #include "video_core/memory_manager.h" |
| 18 | #include "video_core/renderer_base.h" | 19 | #include "video_core/renderer_base.h" |
| 20 | #include "video_core/video_core.h" | ||
| 19 | 21 | ||
| 20 | namespace Tegra { | 22 | namespace Tegra { |
| 21 | 23 | ||
| 22 | MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); | 24 | MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); |
| 23 | 25 | ||
| 24 | GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async) | 26 | GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async) |
| 25 | : system{system}, renderer{renderer}, is_async{is_async} { | 27 | : system{system}, renderer{std::move(renderer_)}, is_async{is_async} { |
| 26 | auto& rasterizer{renderer.Rasterizer()}; | 28 | auto& rasterizer{renderer->Rasterizer()}; |
| 27 | memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); | 29 | memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); |
| 28 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); | 30 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); |
| 29 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); | 31 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); |
| @@ -137,7 +139,7 @@ u64 GPU::GetTicks() const { | |||
| 137 | } | 139 | } |
| 138 | 140 | ||
| 139 | void GPU::FlushCommands() { | 141 | void GPU::FlushCommands() { |
| 140 | renderer.Rasterizer().FlushCommands(); | 142 | renderer->Rasterizer().FlushCommands(); |
| 141 | } | 143 | } |
| 142 | 144 | ||
| 143 | // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence | 145 | // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 64acb17df..ced9d7e28 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -25,8 +25,11 @@ inline u8* FromCacheAddr(CacheAddr cache_addr) { | |||
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | namespace Core { | 27 | namespace Core { |
| 28 | class System; | 28 | namespace Frontend { |
| 29 | class EmuWindow; | ||
| 29 | } | 30 | } |
| 31 | class System; | ||
| 32 | } // namespace Core | ||
| 30 | 33 | ||
| 31 | namespace VideoCore { | 34 | namespace VideoCore { |
| 32 | class RendererBase; | 35 | class RendererBase; |
| @@ -129,7 +132,8 @@ class MemoryManager; | |||
| 129 | 132 | ||
| 130 | class GPU { | 133 | class GPU { |
| 131 | public: | 134 | public: |
| 132 | explicit GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async); | 135 | explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, |
| 136 | bool is_async); | ||
| 133 | 137 | ||
| 134 | virtual ~GPU(); | 138 | virtual ~GPU(); |
| 135 | 139 | ||
| @@ -174,6 +178,14 @@ public: | |||
| 174 | /// Returns a reference to the GPU DMA pusher. | 178 | /// Returns a reference to the GPU DMA pusher. |
| 175 | Tegra::DmaPusher& DmaPusher(); | 179 | Tegra::DmaPusher& DmaPusher(); |
| 176 | 180 | ||
| 181 | VideoCore::RendererBase& Renderer() { | ||
| 182 | return *renderer; | ||
| 183 | } | ||
| 184 | |||
| 185 | const VideoCore::RendererBase& Renderer() const { | ||
| 186 | return *renderer; | ||
| 187 | } | ||
| 188 | |||
| 177 | // Waits for the GPU to finish working | 189 | // Waits for the GPU to finish working |
| 178 | virtual void WaitIdle() const = 0; | 190 | virtual void WaitIdle() const = 0; |
| 179 | 191 | ||
| @@ -287,7 +299,7 @@ private: | |||
| 287 | protected: | 299 | protected: |
| 288 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; | 300 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; |
| 289 | Core::System& system; | 301 | Core::System& system; |
| 290 | VideoCore::RendererBase& renderer; | 302 | std::unique_ptr<VideoCore::RendererBase> renderer; |
| 291 | 303 | ||
| 292 | private: | 304 | private: |
| 293 | std::unique_ptr<Tegra::MemoryManager> memory_manager; | 305 | std::unique_ptr<Tegra::MemoryManager> memory_manager; |
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 04222d060..925be8d7b 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp | |||
| @@ -10,13 +10,16 @@ | |||
| 10 | 10 | ||
| 11 | namespace VideoCommon { | 11 | namespace VideoCommon { |
| 12 | 12 | ||
| 13 | GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) | 13 | GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, |
| 14 | : GPU(system, renderer, true), gpu_thread{system} {} | 14 | std::unique_ptr<Core::Frontend::GraphicsContext>&& context) |
| 15 | : GPU(system, std::move(renderer_), true), gpu_thread{system}, gpu_context(std::move(context)), | ||
| 16 | cpu_context(renderer->GetRenderWindow().CreateSharedContext()) {} | ||
| 15 | 17 | ||
| 16 | GPUAsynch::~GPUAsynch() = default; | 18 | GPUAsynch::~GPUAsynch() = default; |
| 17 | 19 | ||
| 18 | void GPUAsynch::Start() { | 20 | void GPUAsynch::Start() { |
| 19 | gpu_thread.StartThread(renderer, *dma_pusher); | 21 | cpu_context->MakeCurrent(); |
| 22 | gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); | ||
| 20 | } | 23 | } |
| 21 | 24 | ||
| 22 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { | 25 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { |
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 1241ade1d..265c62758 100644 --- a/src/video_core/gpu_asynch.h +++ b/src/video_core/gpu_asynch.h | |||
| @@ -7,6 +7,10 @@ | |||
| 7 | #include "video_core/gpu.h" | 7 | #include "video_core/gpu.h" |
| 8 | #include "video_core/gpu_thread.h" | 8 | #include "video_core/gpu_thread.h" |
| 9 | 9 | ||
| 10 | namespace Core::Frontend { | ||
| 11 | class GraphicsContext; | ||
| 12 | } | ||
| 13 | |||
| 10 | namespace VideoCore { | 14 | namespace VideoCore { |
| 11 | class RendererBase; | 15 | class RendererBase; |
| 12 | } // namespace VideoCore | 16 | } // namespace VideoCore |
| @@ -16,7 +20,8 @@ namespace VideoCommon { | |||
| 16 | /// Implementation of GPU interface that runs the GPU asynchronously | 20 | /// Implementation of GPU interface that runs the GPU asynchronously |
| 17 | class GPUAsynch final : public Tegra::GPU { | 21 | class GPUAsynch final : public Tegra::GPU { |
| 18 | public: | 22 | public: |
| 19 | explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer); | 23 | explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, |
| 24 | std::unique_ptr<Core::Frontend::GraphicsContext>&& context); | ||
| 20 | ~GPUAsynch() override; | 25 | ~GPUAsynch() override; |
| 21 | 26 | ||
| 22 | void Start() override; | 27 | void Start() override; |
| @@ -32,6 +37,8 @@ protected: | |||
| 32 | 37 | ||
| 33 | private: | 38 | private: |
| 34 | GPUThread::ThreadManager gpu_thread; | 39 | GPUThread::ThreadManager gpu_thread; |
| 40 | std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; | ||
| 41 | std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context; | ||
| 35 | }; | 42 | }; |
| 36 | 43 | ||
| 37 | } // namespace VideoCommon | 44 | } // namespace VideoCommon |
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp index d48221077..bd5278a5c 100644 --- a/src/video_core/gpu_synch.cpp +++ b/src/video_core/gpu_synch.cpp | |||
| @@ -7,12 +7,15 @@ | |||
| 7 | 7 | ||
| 8 | namespace VideoCommon { | 8 | namespace VideoCommon { |
| 9 | 9 | ||
| 10 | GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer) | 10 | GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, |
| 11 | : GPU(system, renderer, false) {} | 11 | std::unique_ptr<Core::Frontend::GraphicsContext>&& context) |
| 12 | : GPU(system, std::move(renderer), false), context{std::move(context)} {} | ||
| 12 | 13 | ||
| 13 | GPUSynch::~GPUSynch() = default; | 14 | GPUSynch::~GPUSynch() = default; |
| 14 | 15 | ||
| 15 | void GPUSynch::Start() {} | 16 | void GPUSynch::Start() { |
| 17 | context->MakeCurrent(); | ||
| 18 | } | ||
| 16 | 19 | ||
| 17 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { | 20 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { |
| 18 | dma_pusher->Push(std::move(entries)); | 21 | dma_pusher->Push(std::move(entries)); |
| @@ -20,19 +23,19 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { | |||
| 20 | } | 23 | } |
| 21 | 24 | ||
| 22 | void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 25 | void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |
| 23 | renderer.SwapBuffers(framebuffer); | 26 | renderer->SwapBuffers(framebuffer); |
| 24 | } | 27 | } |
| 25 | 28 | ||
| 26 | void GPUSynch::FlushRegion(CacheAddr addr, u64 size) { | 29 | void GPUSynch::FlushRegion(CacheAddr addr, u64 size) { |
| 27 | renderer.Rasterizer().FlushRegion(addr, size); | 30 | renderer->Rasterizer().FlushRegion(addr, size); |
| 28 | } | 31 | } |
| 29 | 32 | ||
| 30 | void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) { | 33 | void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) { |
| 31 | renderer.Rasterizer().InvalidateRegion(addr, size); | 34 | renderer->Rasterizer().InvalidateRegion(addr, size); |
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | 37 | void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { |
| 35 | renderer.Rasterizer().FlushAndInvalidateRegion(addr, size); | 38 | renderer->Rasterizer().FlushAndInvalidateRegion(addr, size); |
| 36 | } | 39 | } |
| 37 | 40 | ||
| 38 | } // namespace VideoCommon | 41 | } // namespace VideoCommon |
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h index c71baee89..866a94c8c 100644 --- a/src/video_core/gpu_synch.h +++ b/src/video_core/gpu_synch.h | |||
| @@ -6,6 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include "video_core/gpu.h" | 7 | #include "video_core/gpu.h" |
| 8 | 8 | ||
| 9 | namespace Core::Frontend { | ||
| 10 | class GraphicsContext; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace VideoCore { | 13 | namespace VideoCore { |
| 10 | class RendererBase; | 14 | class RendererBase; |
| 11 | } // namespace VideoCore | 15 | } // namespace VideoCore |
| @@ -15,7 +19,8 @@ namespace VideoCommon { | |||
| 15 | /// Implementation of GPU interface that runs the GPU synchronously | 19 | /// Implementation of GPU interface that runs the GPU synchronously |
| 16 | class GPUSynch final : public Tegra::GPU { | 20 | class GPUSynch final : public Tegra::GPU { |
| 17 | public: | 21 | public: |
| 18 | explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer); | 22 | explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, |
| 23 | std::unique_ptr<Core::Frontend::GraphicsContext>&& context); | ||
| 19 | ~GPUSynch() override; | 24 | ~GPUSynch() override; |
| 20 | 25 | ||
| 21 | void Start() override; | 26 | void Start() override; |
| @@ -29,6 +34,9 @@ public: | |||
| 29 | protected: | 34 | protected: |
| 30 | void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, | 35 | void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, |
| 31 | [[maybe_unused]] u32 value) const override {} | 36 | [[maybe_unused]] u32 value) const override {} |
| 37 | |||
| 38 | private: | ||
| 39 | std::unique_ptr<Core::Frontend::GraphicsContext> context; | ||
| 32 | }; | 40 | }; |
| 33 | 41 | ||
| 34 | } // namespace VideoCommon | 42 | } // namespace VideoCommon |
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index b1088af3d..270c7ae0d 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_context.h" | 8 | #include "core/frontend/emu_window.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" |
| @@ -14,8 +14,8 @@ | |||
| 14 | namespace VideoCommon::GPUThread { | 14 | namespace VideoCommon::GPUThread { |
| 15 | 15 | ||
| 16 | /// Runs the GPU thread | 16 | /// Runs the GPU thread |
| 17 | static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher, | 17 | static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, |
| 18 | SynchState& state) { | 18 | Tegra::DmaPusher& dma_pusher, SynchState& state) { |
| 19 | MicroProfileOnThreadCreate("GpuThread"); | 19 | MicroProfileOnThreadCreate("GpuThread"); |
| 20 | 20 | ||
| 21 | // Wait for first GPU command before acquiring the window context | 21 | // Wait for first GPU command before acquiring the window context |
| @@ -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::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()}; | 30 | auto current_context = context.Acquire(); |
| 31 | 31 | ||
| 32 | CommandDataContainer next; | 32 | CommandDataContainer next; |
| 33 | while (state.is_running) { | 33 | while (state.is_running) { |
| @@ -62,8 +62,11 @@ ThreadManager::~ThreadManager() { | |||
| 62 | thread.join(); | 62 | thread.join(); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) { | 65 | void ThreadManager::StartThread(VideoCore::RendererBase& renderer, |
| 66 | thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)}; | 66 | Core::Frontend::GraphicsContext& context, |
| 67 | Tegra::DmaPusher& dma_pusher) { | ||
| 68 | thread = std::thread{RunThread, std::ref(renderer), std::ref(context), std::ref(dma_pusher), | ||
| 69 | std::ref(state)}; | ||
| 67 | } | 70 | } |
| 68 | 71 | ||
| 69 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { | 72 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 882e2d9c7..be36c580e 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include <optional> | 10 | #include <optional> |
| 11 | #include <thread> | 11 | #include <thread> |
| 12 | #include <variant> | 12 | #include <variant> |
| 13 | |||
| 14 | #include "common/threadsafe_queue.h" | 13 | #include "common/threadsafe_queue.h" |
| 15 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| 16 | 15 | ||
| @@ -20,6 +19,9 @@ class DmaPusher; | |||
| 20 | } // namespace Tegra | 19 | } // namespace Tegra |
| 21 | 20 | ||
| 22 | namespace Core { | 21 | namespace Core { |
| 22 | namespace Frontend { | ||
| 23 | class GraphicsContext; | ||
| 24 | } | ||
| 23 | class System; | 25 | class System; |
| 24 | } // namespace Core | 26 | } // namespace Core |
| 25 | 27 | ||
| @@ -99,7 +101,8 @@ public: | |||
| 99 | ~ThreadManager(); | 101 | ~ThreadManager(); |
| 100 | 102 | ||
| 101 | /// Creates and starts the GPU thread. | 103 | /// Creates and starts the GPU thread. |
| 102 | void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); | 104 | void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, |
| 105 | Tegra::DmaPusher& dma_pusher); | ||
| 103 | 106 | ||
| 104 | /// Push GPU command entries to be processed | 107 | /// Push GPU command entries to be processed |
| 105 | void SubmitList(Tegra::CommandList&& entries); | 108 | void SubmitList(Tegra::CommandList&& entries); |
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 5ec99a126..1d85219b6 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -46,7 +46,8 @@ public: | |||
| 46 | 46 | ||
| 47 | /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer | 47 | /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer |
| 48 | /// specific implementation) | 48 | /// specific implementation) |
| 49 | virtual void TryPresent(int timeout_ms) = 0; | 49 | /// Returns true if a frame was drawn |
| 50 | virtual bool TryPresent(int timeout_ms) = 0; | ||
| 50 | 51 | ||
| 51 | // Getter/setter functions: | 52 | // Getter/setter functions: |
| 52 | // ------------------------ | 53 | // ------------------------ |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index e3d31c3eb..046ee55a5 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -327,8 +327,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 327 | 327 | ||
| 328 | const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, | 328 | const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, |
| 329 | std::size_t end) { | 329 | std::size_t end) { |
| 330 | context->MakeCurrent(); | 330 | const auto scope = context->Acquire(); |
| 331 | SCOPE_EXIT({ return context->DoneCurrent(); }); | ||
| 332 | 331 | ||
| 333 | for (std::size_t i = begin; i < end; ++i) { | 332 | for (std::size_t i = begin; i < end; ++i) { |
| 334 | if (stop_loading) { | 333 | if (stop_loading) { |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index fca5e3ec0..f1a28cc21 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -30,8 +30,6 @@ namespace OpenGL { | |||
| 30 | 30 | ||
| 31 | namespace { | 31 | namespace { |
| 32 | 32 | ||
| 33 | // If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have | ||
| 34 | // to wait on available presentation frames. | ||
| 35 | constexpr std::size_t SWAP_CHAIN_SIZE = 3; | 33 | constexpr std::size_t SWAP_CHAIN_SIZE = 3; |
| 36 | 34 | ||
| 37 | struct Frame { | 35 | struct Frame { |
| @@ -214,7 +212,7 @@ public: | |||
| 214 | std::deque<Frame*> present_queue; | 212 | std::deque<Frame*> present_queue; |
| 215 | Frame* previous_frame{}; | 213 | Frame* previous_frame{}; |
| 216 | 214 | ||
| 217 | FrameMailbox() : has_debug_tool{HasDebugTool()} { | 215 | FrameMailbox() { |
| 218 | for (auto& frame : swap_chain) { | 216 | for (auto& frame : swap_chain) { |
| 219 | free_queue.push(&frame); | 217 | free_queue.push(&frame); |
| 220 | } | 218 | } |
| @@ -285,13 +283,9 @@ public: | |||
| 285 | std::unique_lock lock{swap_chain_lock}; | 283 | std::unique_lock lock{swap_chain_lock}; |
| 286 | present_queue.push_front(frame); | 284 | present_queue.push_front(frame); |
| 287 | present_cv.notify_one(); | 285 | present_cv.notify_one(); |
| 288 | |||
| 289 | DebugNotifyNextFrame(); | ||
| 290 | } | 286 | } |
| 291 | 287 | ||
| 292 | Frame* TryGetPresentFrame(int timeout_ms) { | 288 | Frame* TryGetPresentFrame(int timeout_ms) { |
| 293 | DebugWaitForNextFrame(); | ||
| 294 | |||
| 295 | std::unique_lock lock{swap_chain_lock}; | 289 | std::unique_lock lock{swap_chain_lock}; |
| 296 | // wait for new entries in the present_queue | 290 | // wait for new entries in the present_queue |
| 297 | present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), | 291 | present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), |
| @@ -317,38 +311,12 @@ public: | |||
| 317 | previous_frame = frame; | 311 | previous_frame = frame; |
| 318 | return frame; | 312 | return frame; |
| 319 | } | 313 | } |
| 320 | |||
| 321 | private: | ||
| 322 | std::mutex debug_synch_mutex; | ||
| 323 | std::condition_variable debug_synch_condition; | ||
| 324 | std::atomic_int frame_for_debug{}; | ||
| 325 | const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step | ||
| 326 | |||
| 327 | /// Signal that a new frame is available (called from GPU thread) | ||
| 328 | void DebugNotifyNextFrame() { | ||
| 329 | if (!has_debug_tool) { | ||
| 330 | return; | ||
| 331 | } | ||
| 332 | frame_for_debug++; | ||
| 333 | std::lock_guard lock{debug_synch_mutex}; | ||
| 334 | debug_synch_condition.notify_one(); | ||
| 335 | } | ||
| 336 | |||
| 337 | /// Wait for a new frame to be available (called from presentation thread) | ||
| 338 | void DebugWaitForNextFrame() { | ||
| 339 | if (!has_debug_tool) { | ||
| 340 | return; | ||
| 341 | } | ||
| 342 | const int last_frame = frame_for_debug; | ||
| 343 | std::unique_lock lock{debug_synch_mutex}; | ||
| 344 | debug_synch_condition.wait(lock, | ||
| 345 | [this, last_frame] { return frame_for_debug > last_frame; }); | ||
| 346 | } | ||
| 347 | }; | 314 | }; |
| 348 | 315 | ||
| 349 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) | 316 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, |
| 317 | Core::Frontend::GraphicsContext& context) | ||
| 350 | : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, | 318 | : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, |
| 351 | frame_mailbox{std::make_unique<FrameMailbox>()} {} | 319 | frame_mailbox{}, context{context}, has_debug_tool{HasDebugTool()} {} |
| 352 | 320 | ||
| 353 | RendererOpenGL::~RendererOpenGL() = default; | 321 | RendererOpenGL::~RendererOpenGL() = default; |
| 354 | 322 | ||
| @@ -356,8 +324,6 @@ MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 12 | |||
| 356 | MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); | 324 | MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); |
| 357 | 325 | ||
| 358 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 326 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |
| 359 | render_window.PollEvents(); | ||
| 360 | |||
| 361 | if (!framebuffer) { | 327 | if (!framebuffer) { |
| 362 | return; | 328 | return; |
| 363 | } | 329 | } |
| @@ -413,6 +379,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 413 | m_current_frame++; | 379 | m_current_frame++; |
| 414 | rasterizer->TickFrame(); | 380 | rasterizer->TickFrame(); |
| 415 | } | 381 | } |
| 382 | |||
| 383 | render_window.PollEvents(); | ||
| 384 | if (has_debug_tool) { | ||
| 385 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||
| 386 | Present(0); | ||
| 387 | context.SwapBuffers(); | ||
| 388 | } | ||
| 416 | } | 389 | } |
| 417 | 390 | ||
| 418 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { | 391 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { |
| @@ -480,6 +453,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color | |||
| 480 | } | 453 | } |
| 481 | 454 | ||
| 482 | void RendererOpenGL::InitOpenGLObjects() { | 455 | void RendererOpenGL::InitOpenGLObjects() { |
| 456 | frame_mailbox = std::make_unique<FrameMailbox>(); | ||
| 457 | |||
| 483 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, | 458 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, |
| 484 | 0.0f); | 459 | 0.0f); |
| 485 | 460 | ||
| @@ -692,12 +667,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 692 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | 667 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| 693 | } | 668 | } |
| 694 | 669 | ||
| 695 | void RendererOpenGL::TryPresent(int timeout_ms) { | 670 | bool RendererOpenGL::TryPresent(int timeout_ms) { |
| 671 | if (has_debug_tool) { | ||
| 672 | LOG_DEBUG(Render_OpenGL, | ||
| 673 | "Skipping presentation because we are presenting on the main context"); | ||
| 674 | return false; | ||
| 675 | } | ||
| 676 | return Present(timeout_ms); | ||
| 677 | } | ||
| 678 | |||
| 679 | bool RendererOpenGL::Present(int timeout_ms) { | ||
| 696 | const auto& layout = render_window.GetFramebufferLayout(); | 680 | const auto& layout = render_window.GetFramebufferLayout(); |
| 697 | auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms); | 681 | auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms); |
| 698 | if (!frame) { | 682 | if (!frame) { |
| 699 | LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); | 683 | LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); |
| 700 | return; | 684 | return false; |
| 701 | } | 685 | } |
| 702 | 686 | ||
| 703 | // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a | 687 | // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a |
| @@ -725,6 +709,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) { | |||
| 725 | glFlush(); | 709 | glFlush(); |
| 726 | 710 | ||
| 727 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | 711 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); |
| 712 | return true; | ||
| 728 | } | 713 | } |
| 729 | 714 | ||
| 730 | void RendererOpenGL::RenderScreenshot() { | 715 | void RendererOpenGL::RenderScreenshot() { |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 33073ce5b..50b647661 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -55,13 +55,14 @@ class FrameMailbox; | |||
| 55 | 55 | ||
| 56 | class RendererOpenGL final : public VideoCore::RendererBase { | 56 | class RendererOpenGL final : public VideoCore::RendererBase { |
| 57 | public: | 57 | public: |
| 58 | explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); | 58 | explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, |
| 59 | Core::Frontend::GraphicsContext& context); | ||
| 59 | ~RendererOpenGL() override; | 60 | ~RendererOpenGL() override; |
| 60 | 61 | ||
| 61 | bool Init() override; | 62 | bool Init() override; |
| 62 | void ShutDown() override; | 63 | void ShutDown() override; |
| 63 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 64 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 64 | void TryPresent(int timeout_ms) override; | 65 | bool TryPresent(int timeout_ms) override; |
| 65 | 66 | ||
| 66 | private: | 67 | private: |
| 67 | /// Initializes the OpenGL state and creates persistent objects. | 68 | /// Initializes the OpenGL state and creates persistent objects. |
| @@ -89,8 +90,11 @@ private: | |||
| 89 | 90 | ||
| 90 | void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); | 91 | void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); |
| 91 | 92 | ||
| 93 | bool Present(int timeout_ms); | ||
| 94 | |||
| 92 | Core::Frontend::EmuWindow& emu_window; | 95 | Core::Frontend::EmuWindow& emu_window; |
| 93 | Core::System& system; | 96 | Core::System& system; |
| 97 | Core::Frontend::GraphicsContext& context; | ||
| 94 | 98 | ||
| 95 | StateTracker state_tracker{system}; | 99 | StateTracker state_tracker{system}; |
| 96 | 100 | ||
| @@ -115,6 +119,8 @@ private: | |||
| 115 | 119 | ||
| 116 | /// Frame presentation mailbox | 120 | /// Frame presentation mailbox |
| 117 | std::unique_ptr<FrameMailbox> frame_mailbox; | 121 | std::unique_ptr<FrameMailbox> frame_mailbox; |
| 122 | |||
| 123 | bool has_debug_tool = false; | ||
| 118 | }; | 124 | }; |
| 119 | 125 | ||
| 120 | } // namespace OpenGL | 126 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 42bb01418..6953aaafe 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -141,8 +141,9 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 141 | render_window.PollEvents(); | 141 | render_window.PollEvents(); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | void RendererVulkan::TryPresent(int /*timeout_ms*/) { | 144 | bool RendererVulkan::TryPresent(int /*timeout_ms*/) { |
| 145 | // TODO (bunnei): ImplementMe | 145 | // TODO (bunnei): ImplementMe |
| 146 | return true; | ||
| 146 | } | 147 | } |
| 147 | 148 | ||
| 148 | bool RendererVulkan::Init() { | 149 | bool RendererVulkan::Init() { |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 3da08d2e4..d14384e79 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -42,7 +42,7 @@ public: | |||
| 42 | bool Init() override; | 42 | bool Init() override; |
| 43 | void ShutDown() override; | 43 | void ShutDown() override; |
| 44 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 44 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 45 | void TryPresent(int timeout_ms) override; | 45 | bool TryPresent(int timeout_ms) override; |
| 46 | 46 | ||
| 47 | private: | 47 | private: |
| 48 | std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( | 48 | std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( |
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index a5f81a8a0..f60bdc60a 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -15,13 +15,13 @@ | |||
| 15 | #endif | 15 | #endif |
| 16 | #include "video_core/video_core.h" | 16 | #include "video_core/video_core.h" |
| 17 | 17 | ||
| 18 | namespace VideoCore { | 18 | namespace { |
| 19 | 19 | std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, | |
| 20 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, | 20 | Core::System& system, |
| 21 | Core::System& system) { | 21 | Core::Frontend::GraphicsContext& context) { |
| 22 | switch (Settings::values.renderer_backend) { | 22 | switch (Settings::values.renderer_backend) { |
| 23 | case Settings::RendererBackend::OpenGL: | 23 | case Settings::RendererBackend::OpenGL: |
| 24 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); | 24 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context); |
| 25 | #ifdef HAS_VULKAN | 25 | #ifdef HAS_VULKAN |
| 26 | case Settings::RendererBackend::Vulkan: | 26 | case Settings::RendererBackend::Vulkan: |
| 27 | return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); | 27 | return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); |
| @@ -30,13 +30,23 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind | |||
| 30 | return nullptr; | 30 | return nullptr; |
| 31 | } | 31 | } |
| 32 | } | 32 | } |
| 33 | } // Anonymous namespace | ||
| 33 | 34 | ||
| 34 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { | 35 | namespace VideoCore { |
| 35 | if (Settings::values.use_asynchronous_gpu_emulation) { | 36 | |
| 36 | return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer()); | 37 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { |
| 38 | auto context = emu_window.CreateSharedContext(); | ||
| 39 | const auto scope = context->Acquire(); | ||
| 40 | auto renderer = CreateRenderer(emu_window, system, *context); | ||
| 41 | if (!renderer->Init()) { | ||
| 42 | return nullptr; | ||
| 37 | } | 43 | } |
| 38 | 44 | ||
| 39 | return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer()); | 45 | if (Settings::values.use_asynchronous_gpu_emulation) { |
| 46 | return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), | ||
| 47 | std::move(context)); | ||
| 48 | } | ||
| 49 | return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context)); | ||
| 40 | } | 50 | } |
| 41 | 51 | ||
| 42 | u16 GetResolutionScaleFactor(const RendererBase& renderer) { | 52 | u16 GetResolutionScaleFactor(const RendererBase& renderer) { |
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index b8e0ac372..f5c27125d 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h | |||
| @@ -22,17 +22,8 @@ namespace VideoCore { | |||
| 22 | 22 | ||
| 23 | class RendererBase; | 23 | class RendererBase; |
| 24 | 24 | ||
| 25 | /** | ||
| 26 | * Creates a renderer instance. | ||
| 27 | * | ||
| 28 | * @note The returned renderer instance is simply allocated. Its Init() | ||
| 29 | * function still needs to be called to fully complete its setup. | ||
| 30 | */ | ||
| 31 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, | ||
| 32 | Core::System& system); | ||
| 33 | |||
| 34 | /// Creates an emulated GPU instance using the given system context. | 25 | /// Creates an emulated GPU instance using the given system context. |
| 35 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system); | 26 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system); |
| 36 | 27 | ||
| 37 | u16 GetResolutionScaleFactor(const RendererBase& renderer); | 28 | u16 GetResolutionScaleFactor(const RendererBase& renderer); |
| 38 | 29 | ||
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index c3dbb1a88..eaded2640 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -10,9 +10,6 @@ | |||
| 10 | #include <QMessageBox> | 10 | #include <QMessageBox> |
| 11 | #include <QOffscreenSurface> | 11 | #include <QOffscreenSurface> |
| 12 | #include <QOpenGLContext> | 12 | #include <QOpenGLContext> |
| 13 | #include <QOpenGLFunctions> | ||
| 14 | #include <QOpenGLFunctions_4_3_Core> | ||
| 15 | #include <QOpenGLWindow> | ||
| 16 | #include <QPainter> | 13 | #include <QPainter> |
| 17 | #include <QScreen> | 14 | #include <QScreen> |
| 18 | #include <QStringList> | 15 | #include <QStringList> |
| @@ -29,7 +26,6 @@ | |||
| 29 | #include "common/scope_exit.h" | 26 | #include "common/scope_exit.h" |
| 30 | #include "core/core.h" | 27 | #include "core/core.h" |
| 31 | #include "core/frontend/framebuffer_layout.h" | 28 | #include "core/frontend/framebuffer_layout.h" |
| 32 | #include "core/frontend/scope_acquire_context.h" | ||
| 33 | #include "core/settings.h" | 29 | #include "core/settings.h" |
| 34 | #include "input_common/keyboard.h" | 30 | #include "input_common/keyboard.h" |
| 35 | #include "input_common/main.h" | 31 | #include "input_common/main.h" |
| @@ -39,26 +35,16 @@ | |||
| 39 | #include "yuzu/bootmanager.h" | 35 | #include "yuzu/bootmanager.h" |
| 40 | #include "yuzu/main.h" | 36 | #include "yuzu/main.h" |
| 41 | 37 | ||
| 42 | EmuThread::EmuThread(GRenderWindow& window) | 38 | EmuThread::EmuThread() = default; |
| 43 | : shared_context{window.CreateSharedContext()}, | ||
| 44 | context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context | ||
| 45 | : window} {} | ||
| 46 | 39 | ||
| 47 | EmuThread::~EmuThread() = default; | 40 | EmuThread::~EmuThread() = default; |
| 48 | 41 | ||
| 49 | static GMainWindow* GetMainWindow() { | ||
| 50 | for (QWidget* w : qApp->topLevelWidgets()) { | ||
| 51 | if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) { | ||
| 52 | return main; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | return nullptr; | ||
| 56 | } | ||
| 57 | |||
| 58 | void EmuThread::run() { | 42 | void EmuThread::run() { |
| 59 | MicroProfileOnThreadCreate("EmuThread"); | 43 | MicroProfileOnThreadCreate("EmuThread"); |
| 60 | 44 | ||
| 61 | Core::Frontend::ScopeAcquireContext acquire_context{context}; | 45 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU |
| 46 | // execution. | ||
| 47 | Core::System::GetInstance().GPU().Start(); | ||
| 62 | 48 | ||
| 63 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 49 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 64 | 50 | ||
| @@ -111,162 +97,156 @@ void EmuThread::run() { | |||
| 111 | #endif | 97 | #endif |
| 112 | } | 98 | } |
| 113 | 99 | ||
| 114 | class GGLContext : public Core::Frontend::GraphicsContext { | 100 | class OpenGLSharedContext : public Core::Frontend::GraphicsContext { |
| 115 | public: | 101 | public: |
| 116 | explicit GGLContext(QOpenGLContext* shared_context) | 102 | /// Create the original context that should be shared from |
| 117 | : context(new QOpenGLContext(shared_context->parent())), | 103 | explicit OpenGLSharedContext(QSurface* surface) : surface(surface) { |
| 118 | surface(new QOffscreenSurface(nullptr)) { | 104 | QSurfaceFormat format; |
| 105 | format.setVersion(4, 3); | ||
| 106 | format.setProfile(QSurfaceFormat::CompatibilityProfile); | ||
| 107 | format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | ||
| 108 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | ||
| 109 | format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||
| 110 | format.setSwapInterval(0); | ||
| 111 | |||
| 112 | context = std::make_unique<QOpenGLContext>(); | ||
| 113 | context->setFormat(format); | ||
| 114 | if (!context->create()) { | ||
| 115 | LOG_ERROR(Frontend, "Unable to create main openGL context"); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Create the shared contexts for rendering and presentation | ||
| 120 | explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) { | ||
| 119 | 121 | ||
| 120 | // disable vsync for any shared contexts | 122 | // disable vsync for any shared contexts |
| 121 | auto format = shared_context->format(); | 123 | auto format = share_context->format(); |
| 122 | format.setSwapInterval(0); | 124 | format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0); |
| 123 | 125 | ||
| 124 | context->setShareContext(shared_context); | 126 | context = std::make_unique<QOpenGLContext>(); |
| 127 | context->setShareContext(share_context); | ||
| 125 | context->setFormat(format); | 128 | context->setFormat(format); |
| 126 | context->create(); | 129 | if (!context->create()) { |
| 127 | surface->setParent(shared_context->parent()); | 130 | LOG_ERROR(Frontend, "Unable to create shared openGL context"); |
| 128 | surface->setFormat(format); | 131 | } |
| 129 | surface->create(); | 132 | |
| 133 | if (!main_surface) { | ||
| 134 | offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr); | ||
| 135 | offscreen_surface->setFormat(format); | ||
| 136 | offscreen_surface->create(); | ||
| 137 | surface = offscreen_surface.get(); | ||
| 138 | } else { | ||
| 139 | surface = main_surface; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | ~OpenGLSharedContext() { | ||
| 144 | DoneCurrent(); | ||
| 145 | } | ||
| 146 | |||
| 147 | void SwapBuffers() override { | ||
| 148 | context->swapBuffers(surface); | ||
| 130 | } | 149 | } |
| 131 | 150 | ||
| 132 | void MakeCurrent() override { | 151 | void MakeCurrent() override { |
| 133 | context->makeCurrent(surface); | 152 | if (is_current) { |
| 153 | return; | ||
| 154 | } | ||
| 155 | is_current = context->makeCurrent(surface); | ||
| 134 | } | 156 | } |
| 135 | 157 | ||
| 136 | void DoneCurrent() override { | 158 | void DoneCurrent() override { |
| 159 | if (!is_current) { | ||
| 160 | return; | ||
| 161 | } | ||
| 137 | context->doneCurrent(); | 162 | context->doneCurrent(); |
| 163 | is_current = false; | ||
| 138 | } | 164 | } |
| 139 | 165 | ||
| 140 | private: | 166 | QOpenGLContext* GetShareContext() { |
| 141 | QOpenGLContext* context; | 167 | return context.get(); |
| 142 | QOffscreenSurface* surface; | ||
| 143 | }; | ||
| 144 | |||
| 145 | class ChildRenderWindow : public QWindow { | ||
| 146 | public: | ||
| 147 | ChildRenderWindow(QWindow* parent, QWidget* event_handler) | ||
| 148 | : QWindow{parent}, event_handler{event_handler} {} | ||
| 149 | |||
| 150 | virtual ~ChildRenderWindow() = default; | ||
| 151 | |||
| 152 | virtual void Present() = 0; | ||
| 153 | |||
| 154 | protected: | ||
| 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 | } | ||
| 195 | } | 168 | } |
| 196 | 169 | ||
| 197 | void exposeEvent(QExposeEvent* event) override { | 170 | const QOpenGLContext* GetShareContext() const { |
| 198 | QWindow::requestUpdate(); | 171 | return context.get(); |
| 199 | QWindow::exposeEvent(event); | ||
| 200 | } | 172 | } |
| 201 | 173 | ||
| 202 | private: | 174 | private: |
| 203 | QWidget* event_handler{}; | 175 | // Avoid using Qt parent system here since we might move the QObjects to new threads |
| 176 | // As a note, this means we should avoid using slots/signals with the objects too | ||
| 177 | std::unique_ptr<QOpenGLContext> context; | ||
| 178 | std::unique_ptr<QOffscreenSurface> offscreen_surface{}; | ||
| 179 | QSurface* surface; | ||
| 180 | bool is_current = false; | ||
| 204 | }; | 181 | }; |
| 205 | 182 | ||
| 206 | class OpenGLWindow final : public ChildRenderWindow { | 183 | class DummyContext : public Core::Frontend::GraphicsContext {}; |
| 184 | |||
| 185 | class RenderWidget : public QWidget { | ||
| 207 | public: | 186 | public: |
| 208 | OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) | 187 | explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { |
| 209 | : ChildRenderWindow{parent, event_handler}, | 188 | setAttribute(Qt::WA_NativeWindow); |
| 210 | context(new QOpenGLContext(shared_context->parent())) { | 189 | setAttribute(Qt::WA_PaintOnScreen); |
| 190 | } | ||
| 211 | 191 | ||
| 212 | // disable vsync for any shared contexts | 192 | virtual ~RenderWidget() = default; |
| 213 | auto format = shared_context->format(); | ||
| 214 | format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); | ||
| 215 | this->setFormat(format); | ||
| 216 | 193 | ||
| 217 | context->setShareContext(shared_context); | 194 | /// Called on the UI thread when this Widget is ready to draw |
| 218 | context->setScreen(this->screen()); | 195 | /// Dervied classes can override this to draw the latest frame. |
| 219 | context->setFormat(format); | 196 | virtual void Present() {} |
| 220 | context->create(); | ||
| 221 | 197 | ||
| 222 | setSurfaceType(QWindow::OpenGLSurface); | 198 | void paintEvent(QPaintEvent* event) override { |
| 199 | Present(); | ||
| 200 | update(); | ||
| 201 | } | ||
| 223 | 202 | ||
| 224 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 203 | QPaintEngine* paintEngine() const override { |
| 225 | // WA_DontShowOnScreen, WA_DeleteOnClose | 204 | return nullptr; |
| 226 | } | 205 | } |
| 227 | 206 | ||
| 228 | ~OpenGLWindow() override { | 207 | private: |
| 229 | context->doneCurrent(); | 208 | GRenderWindow* render_window; |
| 209 | }; | ||
| 210 | |||
| 211 | class OpenGLRenderWidget : public RenderWidget { | ||
| 212 | public: | ||
| 213 | explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { | ||
| 214 | windowHandle()->setSurfaceType(QWindow::OpenGLSurface); | ||
| 215 | } | ||
| 216 | |||
| 217 | void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) { | ||
| 218 | context = std::move(context_); | ||
| 230 | } | 219 | } |
| 231 | 220 | ||
| 232 | void Present() override { | 221 | void Present() override { |
| 233 | if (!isExposed()) { | 222 | if (!isVisible()) { |
| 234 | return; | 223 | return; |
| 235 | } | 224 | } |
| 236 | 225 | ||
| 237 | context->makeCurrent(this); | 226 | context->MakeCurrent(); |
| 238 | Core::System::GetInstance().Renderer().TryPresent(100); | 227 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| 239 | context->swapBuffers(this); | 228 | if (Core::System::GetInstance().Renderer().TryPresent(100)) { |
| 240 | auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); | 229 | context->SwapBuffers(); |
| 241 | f->glFinish(); | 230 | glFinish(); |
| 242 | QWindow::requestUpdate(); | 231 | } |
| 243 | } | 232 | } |
| 244 | 233 | ||
| 245 | private: | 234 | private: |
| 246 | QOpenGLContext* context{}; | 235 | std::unique_ptr<Core::Frontend::GraphicsContext> context{}; |
| 247 | }; | 236 | }; |
| 248 | 237 | ||
| 249 | #ifdef HAS_VULKAN | 238 | #ifdef HAS_VULKAN |
| 250 | class VulkanWindow final : public ChildRenderWindow { | 239 | class VulkanRenderWidget : public RenderWidget { |
| 251 | public: | 240 | public: |
| 252 | VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) | 241 | explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance) |
| 253 | : ChildRenderWindow{parent, event_handler} { | 242 | : RenderWidget(parent) { |
| 254 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); | 243 | windowHandle()->setSurfaceType(QWindow::VulkanSurface); |
| 255 | setVulkanInstance(instance); | 244 | windowHandle()->setVulkanInstance(instance); |
| 256 | } | ||
| 257 | |||
| 258 | ~VulkanWindow() override = default; | ||
| 259 | |||
| 260 | void Present() override { | ||
| 261 | // TODO(bunnei): ImplementMe | ||
| 262 | } | 245 | } |
| 263 | |||
| 264 | private: | ||
| 265 | QWidget* event_handler{}; | ||
| 266 | }; | 246 | }; |
| 267 | #endif | 247 | #endif |
| 268 | 248 | ||
| 269 | GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | 249 | GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread) |
| 270 | : QWidget(parent_), emu_thread(emu_thread) { | 250 | : QWidget(parent_), emu_thread(emu_thread) { |
| 271 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 251 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 272 | .arg(QString::fromUtf8(Common::g_build_name), | 252 | .arg(QString::fromUtf8(Common::g_build_name), |
| @@ -278,26 +258,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | |||
| 278 | setLayout(layout); | 258 | setLayout(layout); |
| 279 | InputCommon::Init(); | 259 | InputCommon::Init(); |
| 280 | 260 | ||
| 281 | GMainWindow* parent = GetMainWindow(); | 261 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete); |
| 282 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | ||
| 283 | } | 262 | } |
| 284 | 263 | ||
| 285 | GRenderWindow::~GRenderWindow() { | 264 | GRenderWindow::~GRenderWindow() { |
| 286 | InputCommon::Shutdown(); | 265 | InputCommon::Shutdown(); |
| 287 | } | 266 | } |
| 288 | 267 | ||
| 289 | void GRenderWindow::MakeCurrent() { | ||
| 290 | if (core_context) { | ||
| 291 | core_context->MakeCurrent(); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | void GRenderWindow::DoneCurrent() { | ||
| 296 | if (core_context) { | ||
| 297 | core_context->DoneCurrent(); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | void GRenderWindow::PollEvents() { | 268 | void GRenderWindow::PollEvents() { |
| 302 | if (!first_frame) { | 269 | if (!first_frame) { |
| 303 | first_frame = true; | 270 | first_frame = true; |
| @@ -309,21 +276,6 @@ bool GRenderWindow::IsShown() const { | |||
| 309 | return !isMinimized(); | 276 | return !isMinimized(); |
| 310 | } | 277 | } |
| 311 | 278 | ||
| 312 | void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 313 | void* surface) const { | ||
| 314 | #ifdef HAS_VULKAN | ||
| 315 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); | ||
| 316 | const VkInstance instance_copy = vk_instance->vkInstance(); | ||
| 317 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window); | ||
| 318 | |||
| 319 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 320 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); | ||
| 321 | std::memcpy(surface, &surface_copy, sizeof(surface_copy)); | ||
| 322 | #else | ||
| 323 | UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); | ||
| 324 | #endif | ||
| 325 | } | ||
| 326 | |||
| 327 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). | 279 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). |
| 328 | // | 280 | // |
| 329 | // Older versions get the window size (density independent pixels), | 281 | // Older versions get the window size (density independent pixels), |
| @@ -367,7 +319,7 @@ qreal GRenderWindow::windowPixelRatio() const { | |||
| 367 | return devicePixelRatio(); | 319 | return devicePixelRatio(); |
| 368 | } | 320 | } |
| 369 | 321 | ||
| 370 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { | 322 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const { |
| 371 | const qreal pixel_ratio = windowPixelRatio(); | 323 | const qreal pixel_ratio = windowPixelRatio(); |
| 372 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 324 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), |
| 373 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | 325 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| @@ -387,8 +339,10 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | |||
| 387 | } | 339 | } |
| 388 | 340 | ||
| 389 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | 341 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { |
| 390 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 342 | // touch input is handled in TouchBeginEvent |
| 391 | return; // touch input is handled in TouchBeginEvent | 343 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 344 | return; | ||
| 345 | } | ||
| 392 | 346 | ||
| 393 | auto pos = event->pos(); | 347 | auto pos = event->pos(); |
| 394 | if (event->button() == Qt::LeftButton) { | 348 | if (event->button() == Qt::LeftButton) { |
| @@ -400,8 +354,10 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { | |||
| 400 | } | 354 | } |
| 401 | 355 | ||
| 402 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | 356 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { |
| 403 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 357 | // touch input is handled in TouchUpdateEvent |
| 404 | return; // touch input is handled in TouchUpdateEvent | 358 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 359 | return; | ||
| 360 | } | ||
| 405 | 361 | ||
| 406 | auto pos = event->pos(); | 362 | auto pos = event->pos(); |
| 407 | const auto [x, y] = ScaleTouch(pos); | 363 | const auto [x, y] = ScaleTouch(pos); |
| @@ -410,13 +366,16 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | |||
| 410 | } | 366 | } |
| 411 | 367 | ||
| 412 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | 368 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { |
| 413 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 369 | // touch input is handled in TouchEndEvent |
| 414 | return; // touch input is handled in TouchEndEvent | 370 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 371 | return; | ||
| 372 | } | ||
| 415 | 373 | ||
| 416 | if (event->button() == Qt::LeftButton) | 374 | if (event->button() == Qt::LeftButton) { |
| 417 | this->TouchReleased(); | 375 | this->TouchReleased(); |
| 418 | else if (event->button() == Qt::RightButton) | 376 | } else if (event->button() == Qt::RightButton) { |
| 419 | InputCommon::GetMotionEmu()->EndTilt(); | 377 | InputCommon::GetMotionEmu()->EndTilt(); |
| 378 | } | ||
| 420 | } | 379 | } |
| 421 | 380 | ||
| 422 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 381 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { |
| @@ -474,9 +433,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { | |||
| 474 | 433 | ||
| 475 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | 434 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { |
| 476 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | 435 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| 477 | return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); | 436 | auto c = static_cast<OpenGLSharedContext*>(main_context.get()); |
| 437 | // Bind the shared contexts to the main surface in case the backend wants to take over | ||
| 438 | // presentation | ||
| 439 | return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), | ||
| 440 | child_widget->windowHandle()); | ||
| 478 | } | 441 | } |
| 479 | return {}; | 442 | return std::make_unique<DummyContext>(); |
| 480 | } | 443 | } |
| 481 | 444 | ||
| 482 | bool GRenderWindow::InitRenderTarget() { | 445 | bool GRenderWindow::InitRenderTarget() { |
| @@ -497,14 +460,11 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 497 | break; | 460 | break; |
| 498 | } | 461 | } |
| 499 | 462 | ||
| 463 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 464 | layout()->addWidget(child_widget); | ||
| 500 | // Reset minimum required size to avoid resizing issues on the main window after restarting. | 465 | // Reset minimum required size to avoid resizing issues on the main window after restarting. |
| 501 | setMinimumSize(1, 1); | 466 | setMinimumSize(1, 1); |
| 502 | 467 | ||
| 503 | // Show causes the window to actually be created and the gl context as well, but we don't want | ||
| 504 | // the widget to be shown yet, so immediately hide it. | ||
| 505 | show(); | ||
| 506 | hide(); | ||
| 507 | |||
| 508 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | 468 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); |
| 509 | 469 | ||
| 510 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 470 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| @@ -523,9 +483,10 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 523 | void GRenderWindow::ReleaseRenderTarget() { | 483 | void GRenderWindow::ReleaseRenderTarget() { |
| 524 | if (child_widget) { | 484 | if (child_widget) { |
| 525 | layout()->removeWidget(child_widget); | 485 | layout()->removeWidget(child_widget); |
| 526 | delete child_widget; | 486 | child_widget->deleteLater(); |
| 527 | child_widget = nullptr; | 487 | child_widget = nullptr; |
| 528 | } | 488 | } |
| 489 | main_context.reset(); | ||
| 529 | } | 490 | } |
| 530 | 491 | ||
| 531 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | 492 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { |
| @@ -557,24 +518,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
| 557 | bool GRenderWindow::InitializeOpenGL() { | 518 | bool GRenderWindow::InitializeOpenGL() { |
| 558 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 519 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 559 | // WA_DontShowOnScreen, WA_DeleteOnClose | 520 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 560 | QSurfaceFormat fmt; | 521 | auto child = new OpenGLRenderWidget(this); |
| 561 | fmt.setVersion(4, 3); | 522 | child_widget = child; |
| 562 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | 523 | child_widget->windowHandle()->create(); |
| 563 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | 524 | auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle()); |
| 564 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 525 | main_context = context; |
| 565 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 526 | child->SetContext( |
| 566 | fmt.setSwapInterval(0); | 527 | std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle())); |
| 567 | QSurfaceFormat::setDefaultFormat(fmt); | ||
| 568 | |||
| 569 | GMainWindow* parent = GetMainWindow(); | ||
| 570 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | ||
| 571 | child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); | ||
| 572 | child_window->create(); | ||
| 573 | child_widget = createWindowContainer(child_window, this); | ||
| 574 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 575 | layout()->addWidget(child_widget); | ||
| 576 | |||
| 577 | core_context = CreateSharedContext(); | ||
| 578 | 528 | ||
| 579 | return true; | 529 | return true; |
| 580 | } | 530 | } |
| @@ -604,13 +554,10 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 604 | return false; | 554 | return false; |
| 605 | } | 555 | } |
| 606 | 556 | ||
| 607 | GMainWindow* parent = GetMainWindow(); | 557 | auto child = new VulkanRenderWidget(this, vk_instance.get()); |
| 608 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | 558 | child_widget = child; |
| 609 | child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); | 559 | child_widget->windowHandle()->create(); |
| 610 | child_window->create(); | 560 | main_context = std::make_unique<DummyContext>(); |
| 611 | child_widget = createWindowContainer(child_window, this); | ||
| 612 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 613 | layout()->addWidget(child_widget); | ||
| 614 | 561 | ||
| 615 | return true; | 562 | return true; |
| 616 | #else | 563 | #else |
| @@ -620,8 +567,24 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 620 | #endif | 567 | #endif |
| 621 | } | 568 | } |
| 622 | 569 | ||
| 570 | void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 571 | void* surface) const { | ||
| 572 | #ifdef HAS_VULKAN | ||
| 573 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); | ||
| 574 | const VkInstance instance_copy = vk_instance->vkInstance(); | ||
| 575 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle()); | ||
| 576 | |||
| 577 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 578 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); | ||
| 579 | std::memcpy(surface, &surface_copy, sizeof(surface_copy)); | ||
| 580 | #else | ||
| 581 | UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); | ||
| 582 | #endif | ||
| 583 | } | ||
| 584 | |||
| 623 | bool GRenderWindow::LoadOpenGL() { | 585 | bool GRenderWindow::LoadOpenGL() { |
| 624 | Core::Frontend::ScopeAcquireContext acquire_context{*this}; | 586 | auto context = CreateSharedContext(); |
| 587 | auto scope = context->Acquire(); | ||
| 625 | if (!gladLoadGL()) { | 588 | if (!gladLoadGL()) { |
| 626 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), | 589 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), |
| 627 | tr("Your GPU may not support OpenGL 4.3, or you do not have the " | 590 | tr("Your GPU may not support OpenGL 4.3, or you do not have the " |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 79b030304..d69078df1 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -18,12 +18,10 @@ | |||
| 18 | #include "core/frontend/emu_window.h" | 18 | #include "core/frontend/emu_window.h" |
| 19 | 19 | ||
| 20 | class GRenderWindow; | 20 | class GRenderWindow; |
| 21 | class GMainWindow; | ||
| 21 | class QKeyEvent; | 22 | class QKeyEvent; |
| 22 | class QScreen; | ||
| 23 | class QTouchEvent; | 23 | class QTouchEvent; |
| 24 | class QStringList; | 24 | class QStringList; |
| 25 | class QSurface; | ||
| 26 | class QOpenGLContext; | ||
| 27 | #ifdef HAS_VULKAN | 25 | #ifdef HAS_VULKAN |
| 28 | class QVulkanInstance; | 26 | class QVulkanInstance; |
| 29 | #endif | 27 | #endif |
| @@ -36,7 +34,7 @@ class EmuThread final : public QThread { | |||
| 36 | Q_OBJECT | 34 | Q_OBJECT |
| 37 | 35 | ||
| 38 | public: | 36 | public: |
| 39 | explicit EmuThread(GRenderWindow& window); | 37 | explicit EmuThread(); |
| 40 | ~EmuThread() override; | 38 | ~EmuThread() override; |
| 41 | 39 | ||
| 42 | /** | 40 | /** |
| @@ -90,12 +88,6 @@ private: | |||
| 90 | std::mutex running_mutex; | 88 | std::mutex running_mutex; |
| 91 | std::condition_variable running_cv; | 89 | std::condition_variable running_cv; |
| 92 | 90 | ||
| 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; | ||
| 98 | |||
| 99 | signals: | 91 | signals: |
| 100 | /** | 92 | /** |
| 101 | * Emitted when the CPU has halted execution | 93 | * Emitted when the CPU has halted execution |
| @@ -124,12 +116,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { | |||
| 124 | Q_OBJECT | 116 | Q_OBJECT |
| 125 | 117 | ||
| 126 | public: | 118 | public: |
| 127 | GRenderWindow(QWidget* parent, EmuThread* emu_thread); | 119 | GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); |
| 128 | ~GRenderWindow() override; | 120 | ~GRenderWindow() override; |
| 129 | 121 | ||
| 130 | // EmuWindow implementation. | 122 | // EmuWindow implementation. |
| 131 | void MakeCurrent() override; | ||
| 132 | void DoneCurrent() override; | ||
| 133 | void PollEvents() override; | 123 | void PollEvents() override; |
| 134 | bool IsShown() const override; | 124 | bool IsShown() const override; |
| 135 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 125 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| @@ -165,6 +155,8 @@ public: | |||
| 165 | 155 | ||
| 166 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); | 156 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); |
| 167 | 157 | ||
| 158 | std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; | ||
| 159 | |||
| 168 | public slots: | 160 | public slots: |
| 169 | void OnEmulationStarting(EmuThread* emu_thread); | 161 | void OnEmulationStarting(EmuThread* emu_thread); |
| 170 | void OnEmulationStopping(); | 162 | void OnEmulationStopping(); |
| @@ -176,7 +168,6 @@ signals: | |||
| 176 | void FirstFrameDisplayed(); | 168 | void FirstFrameDisplayed(); |
| 177 | 169 | ||
| 178 | private: | 170 | private: |
| 179 | std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||
| 180 | void TouchBeginEvent(const QTouchEvent* event); | 171 | void TouchBeginEvent(const QTouchEvent* event); |
| 181 | void TouchUpdateEvent(const QTouchEvent* event); | 172 | void TouchUpdateEvent(const QTouchEvent* event); |
| 182 | void TouchEndEvent(); | 173 | void TouchEndEvent(); |
| @@ -190,7 +181,10 @@ private: | |||
| 190 | 181 | ||
| 191 | EmuThread* emu_thread; | 182 | EmuThread* emu_thread; |
| 192 | 183 | ||
| 193 | std::unique_ptr<GraphicsContext> core_context; | 184 | // Main context that will be shared with all other contexts that are requested. |
| 185 | // If this is used in a shared context setting, then this should not be used directly, but | ||
| 186 | // should instead be shared from | ||
| 187 | std::shared_ptr<Core::Frontend::GraphicsContext> main_context; | ||
| 194 | 188 | ||
| 195 | #ifdef HAS_VULKAN | 189 | #ifdef HAS_VULKAN |
| 196 | std::unique_ptr<QVulkanInstance> vk_instance; | 190 | std::unique_ptr<QVulkanInstance> vk_instance; |
| @@ -201,12 +195,6 @@ private: | |||
| 201 | 195 | ||
| 202 | QByteArray geometry; | 196 | QByteArray geometry; |
| 203 | 197 | ||
| 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; | 198 | QWidget* child_widget = nullptr; |
| 211 | 199 | ||
| 212 | bool first_frame = false; | 200 | bool first_frame = false; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d7e59d0cd..940f24dc8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -984,7 +984,7 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 984 | return; | 984 | return; |
| 985 | 985 | ||
| 986 | // Create and start the emulation thread | 986 | // Create and start the emulation thread |
| 987 | emu_thread = std::make_unique<EmuThread>(*render_window); | 987 | emu_thread = std::make_unique<EmuThread>(); |
| 988 | emit EmulationStarting(emu_thread.get()); | 988 | emit EmulationStarting(emu_thread.get()); |
| 989 | emu_thread->start(); | 989 | emu_thread->start(); |
| 990 | 990 | ||
| @@ -2378,7 +2378,6 @@ int main(int argc, char* argv[]) { | |||
| 2378 | 2378 | ||
| 2379 | // Enables the core to make the qt created contexts current on std::threads | 2379 | // Enables the core to make the qt created contexts current on std::threads |
| 2380 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2380 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
| 2381 | QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); | ||
| 2382 | QApplication app(argc, argv); | 2381 | QApplication app(argc, argv); |
| 2383 | 2382 | ||
| 2384 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 2383 | // Qt changes the locale and causes issues in float conversion using std::to_string() when |
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 c0d373477..3522dcf6d 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -37,16 +37,24 @@ public: | |||
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void MakeCurrent() override { | 39 | void MakeCurrent() override { |
| 40 | SDL_GL_MakeCurrent(window, context); | 40 | if (is_current) { |
| 41 | return; | ||
| 42 | } | ||
| 43 | is_current = SDL_GL_MakeCurrent(window, context) == 0; | ||
| 41 | } | 44 | } |
| 42 | 45 | ||
| 43 | void DoneCurrent() override { | 46 | void DoneCurrent() override { |
| 47 | if (!is_current) { | ||
| 48 | return; | ||
| 49 | } | ||
| 44 | SDL_GL_MakeCurrent(window, nullptr); | 50 | SDL_GL_MakeCurrent(window, nullptr); |
| 51 | is_current = false; | ||
| 45 | } | 52 | } |
| 46 | 53 | ||
| 47 | private: | 54 | private: |
| 48 | SDL_Window* window; | 55 | SDL_Window* window; |
| 49 | SDL_GLContext context; | 56 | SDL_GLContext context; |
| 57 | bool is_current = false; | ||
| 50 | }; | 58 | }; |
| 51 | 59 | ||
| 52 | bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { | 60 | bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { |
| @@ -148,14 +156,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { | |||
| 148 | SDL_GL_DeleteContext(window_context); | 156 | SDL_GL_DeleteContext(window_context); |
| 149 | } | 157 | } |
| 150 | 158 | ||
| 151 | void EmuWindow_SDL2_GL::MakeCurrent() { | ||
| 152 | core_context->MakeCurrent(); | ||
| 153 | } | ||
| 154 | |||
| 155 | void EmuWindow_SDL2_GL::DoneCurrent() { | ||
| 156 | core_context->DoneCurrent(); | ||
| 157 | } | ||
| 158 | |||
| 159 | 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, |
| 160 | void* surface) const { | 160 | void* surface) const { |
| 161 | // Should not have been called from OpenGL | 161 | // Should not have been called from OpenGL |
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 b80669ff0..e092021d7 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | |||
| @@ -13,8 +13,6 @@ public: | |||
| 13 | explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); | 13 | explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); |
| 14 | ~EmuWindow_SDL2_GL(); | 14 | ~EmuWindow_SDL2_GL(); |
| 15 | 15 | ||
| 16 | void MakeCurrent() override; | ||
| 17 | void DoneCurrent() override; | ||
| 18 | void Present() override; | 16 | void Present() override; |
| 19 | 17 | ||
| 20 | /// Ignored in OpenGL | 18 | /// Ignored in OpenGL |
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 abcc58165..46d053f04 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -111,14 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { | |||
| 111 | vkDestroyInstance(vk_instance, nullptr); | 111 | vkDestroyInstance(vk_instance, nullptr); |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | void EmuWindow_SDL2_VK::MakeCurrent() { | ||
| 115 | // Unused on Vulkan | ||
| 116 | } | ||
| 117 | |||
| 118 | void EmuWindow_SDL2_VK::DoneCurrent() { | ||
| 119 | // Unused on Vulkan | ||
| 120 | } | ||
| 121 | |||
| 122 | void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 114 | void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 123 | void* surface) const { | 115 | void* surface) const { |
| 124 | const auto instance_proc_addr = vkGetInstanceProcAddr; | 116 | const auto instance_proc_addr = vkGetInstanceProcAddr; |
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 1eb8c0868..3dd1f3f61 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | |||
| @@ -13,8 +13,6 @@ public: | |||
| 13 | explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); | 13 | explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); |
| 14 | ~EmuWindow_SDL2_VK(); | 14 | ~EmuWindow_SDL2_VK(); |
| 15 | 15 | ||
| 16 | void MakeCurrent() override; | ||
| 17 | void DoneCurrent() override; | ||
| 18 | void Present() override; | 16 | void Present() override; |
| 19 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 17 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 20 | void* surface) const override; | 18 | void* surface) const override; |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index babf4c3a4..4d2ea7e9e 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -230,17 +230,10 @@ int main(int argc, char** argv) { | |||
| 230 | 230 | ||
| 231 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); | 231 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); |
| 232 | 232 | ||
| 233 | system.Renderer().Rasterizer().LoadDiskResources(); | 233 | // Core is loaded, start the GPU (makes the GPU contexts current to this thread) |
| 234 | system.GPU().Start(); | ||
| 234 | 235 | ||
| 235 | // Acquire render context for duration of the thread if this is the rendering thread | 236 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 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 | 237 | ||
| 245 | std::thread render_thread([&emu_window] { emu_window->Present(); }); | 238 | std::thread render_thread([&emu_window] { emu_window->Present(); }); |
| 246 | while (emu_window->IsOpen()) { | 239 | while (emu_window->IsOpen()) { |
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 a1bdb1a12..a837430cc 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp | |||
| @@ -102,8 +102,6 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() { | |||
| 102 | LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname, | 102 | LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname, |
| 103 | Common::g_scm_branch, Common::g_scm_desc); | 103 | Common::g_scm_branch, Common::g_scm_desc); |
| 104 | Settings::LogSettings(); | 104 | Settings::LogSettings(); |
| 105 | |||
| 106 | DoneCurrent(); | ||
| 107 | } | 105 | } |
| 108 | 106 | ||
| 109 | EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { | 107 | EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { |
| @@ -114,14 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { | |||
| 114 | 112 | ||
| 115 | void EmuWindow_SDL2_Hide::PollEvents() {} | 113 | void EmuWindow_SDL2_Hide::PollEvents() {} |
| 116 | 114 | ||
| 117 | void EmuWindow_SDL2_Hide::MakeCurrent() { | ||
| 118 | SDL_GL_MakeCurrent(render_window, gl_context); | ||
| 119 | } | ||
| 120 | |||
| 121 | void EmuWindow_SDL2_Hide::DoneCurrent() { | ||
| 122 | SDL_GL_MakeCurrent(render_window, nullptr); | ||
| 123 | } | ||
| 124 | |||
| 125 | bool EmuWindow_SDL2_Hide::IsShown() const { | 115 | bool EmuWindow_SDL2_Hide::IsShown() const { |
| 126 | return false; | 116 | return false; |
| 127 | } | 117 | } |
| @@ -129,3 +119,35 @@ bool EmuWindow_SDL2_Hide::IsShown() const { | |||
| 129 | void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { | 119 | void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { |
| 130 | UNREACHABLE(); | 120 | UNREACHABLE(); |
| 131 | } | 121 | } |
| 122 | |||
| 123 | class SDLGLContext : public Core::Frontend::GraphicsContext { | ||
| 124 | public: | ||
| 125 | explicit SDLGLContext() { | ||
| 126 | // create a hidden window to make the shared context against | ||
| 127 | window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, | ||
| 128 | SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); | ||
| 129 | context = SDL_GL_CreateContext(window); | ||
| 130 | } | ||
| 131 | |||
| 132 | ~SDLGLContext() { | ||
| 133 | DoneCurrent(); | ||
| 134 | SDL_GL_DeleteContext(context); | ||
| 135 | SDL_DestroyWindow(window); | ||
| 136 | } | ||
| 137 | |||
| 138 | void MakeCurrent() override { | ||
| 139 | SDL_GL_MakeCurrent(window, context); | ||
| 140 | } | ||
| 141 | |||
| 142 | void DoneCurrent() override { | ||
| 143 | SDL_GL_MakeCurrent(window, nullptr); | ||
| 144 | } | ||
| 145 | |||
| 146 | private: | ||
| 147 | SDL_Window* window; | ||
| 148 | SDL_GLContext context; | ||
| 149 | }; | ||
| 150 | |||
| 151 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const { | ||
| 152 | return std::make_unique<SDLGLContext>(); | ||
| 153 | } | ||
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 b13e15309..9f5d04fca 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | |||
| @@ -16,12 +16,6 @@ public: | |||
| 16 | /// Polls window events | 16 | /// Polls window events |
| 17 | void PollEvents() override; | 17 | void PollEvents() override; |
| 18 | 18 | ||
| 19 | /// Makes the graphics context current for the caller thread | ||
| 20 | void MakeCurrent() override; | ||
| 21 | |||
| 22 | /// Releases the GL context from the caller thread | ||
| 23 | void DoneCurrent() override; | ||
| 24 | |||
| 25 | /// Whether the screen is being shown or not. | 19 | /// Whether the screen is being shown or not. |
| 26 | bool IsShown() const override; | 20 | bool IsShown() const override; |
| 27 | 21 | ||
| @@ -29,8 +23,7 @@ public: | |||
| 29 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 23 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 30 | void* surface) const override; | 24 | void* surface) const override; |
| 31 | 25 | ||
| 32 | /// Whether the window is still open, and a close request hasn't yet been sent | 26 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 33 | bool IsOpen() const; | ||
| 34 | 27 | ||
| 35 | private: | 28 | private: |
| 36 | /// Whether the GPU and driver supports the OpenGL extension required | 29 | /// Whether the GPU and driver supports the OpenGL extension required |
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 94ad50cb3..676e70ebd 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp | |||
| @@ -164,11 +164,6 @@ int main(int argc, char** argv) { | |||
| 164 | 164 | ||
| 165 | std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; | 165 | std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; |
| 166 | 166 | ||
| 167 | if (!Settings::values.use_multi_core) { | ||
| 168 | // Single core mode must acquire OpenGL context for entire emulation session | ||
| 169 | emu_window->MakeCurrent(); | ||
| 170 | } | ||
| 171 | |||
| 172 | bool finished = false; | 167 | bool finished = false; |
| 173 | int return_value = 0; | 168 | int return_value = 0; |
| 174 | const auto callback = [&finished, | 169 | const auto callback = [&finished, |
| @@ -257,6 +252,7 @@ int main(int argc, char** argv) { | |||
| 257 | 252 | ||
| 258 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester"); | 253 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester"); |
| 259 | 254 | ||
| 255 | system.GPU().Start(); | ||
| 260 | system.Renderer().Rasterizer().LoadDiskResources(); | 256 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 261 | 257 | ||
| 262 | while (!finished) { | 258 | while (!finished) { |