diff options
| author | 2020-03-24 20:58:49 -0600 | |
|---|---|---|
| committer | 2020-03-24 21:03:42 -0600 | |
| commit | 282adfc70b5d7d958d564bfda0227bb3fbd8d110 (patch) | |
| tree | 2a98e3bedec2e7fdb33478814a73be664661aecc /src | |
| parent | Use the correct directory for Qt Plugins (diff) | |
| download | yuzu-282adfc70b5d7d958d564bfda0227bb3fbd8d110.tar.gz yuzu-282adfc70b5d7d958d564bfda0227bb3fbd8d110.tar.xz yuzu-282adfc70b5d7d958d564bfda0227bb3fbd8d110.zip | |
Frontend/GPU: Refactor context management
Changes the GraphicsContext to be managed by the GPU core. This
eliminates the need for the frontends to fool around with tricky
MakeCurrent/DoneCurrent calls that are dependent on the settings (such
as async gpu option).
This also refactors out the need to use QWidget::fromWindowContainer as
that caused issues with focus and input handling. Now we use a regular
QWidget and just access the native windowHandle() directly.
Another change is removing the debug tool setting in FrameMailbox.
Instead of trying to block the frontend until a new frame is ready, the
core will now take over presentation and draw directly to the window if
the renderer detects that its hooked by NSight or RenderDoc
Lastly, since it was in the way, I removed ScopeAcquireWindowContext and
replaced it with a simple subclass in GraphicsContext that achieves the
same result
Diffstat (limited to 'src')
29 files changed, 361 insertions, 418 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b31a0328c..29a267957 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 218508126..6cc4a0812 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,9 @@ struct System::Impl { | |||
| 168 | Service::Init(service_manager, system); | 167 | Service::Init(service_manager, system); |
| 169 | GDBStub::Init(); | 168 | GDBStub::Init(); |
| 170 | 169 | ||
| 171 | renderer = VideoCore::CreateRenderer(emu_window, system); | ||
| 172 | if (!renderer->Init()) { | ||
| 173 | return ResultStatus::ErrorVideoCore; | ||
| 174 | } | ||
| 175 | interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); | 170 | interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); |
| 176 | gpu_core = VideoCore::CreateGPU(system); | 171 | gpu_core = VideoCore::CreateGPU(emu_window, system); |
| 177 | renderer->Rasterizer().SetupDirtyFlags(); | 172 | gpu_core->Renderer().Rasterizer().SetupDirtyFlags(); |
| 178 | 173 | ||
| 179 | is_powered_on = true; | 174 | is_powered_on = true; |
| 180 | exit_lock = false; | 175 | exit_lock = false; |
| @@ -186,7 +181,6 @@ struct System::Impl { | |||
| 186 | 181 | ||
| 187 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, | 182 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, |
| 188 | const std::string& filepath) { | 183 | const std::string& filepath) { |
| 189 | Core::Frontend::ScopeAcquireContext acquire_context{emu_window}; | ||
| 190 | 184 | ||
| 191 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); | 185 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); |
| 192 | if (!app_loader) { | 186 | if (!app_loader) { |
| @@ -216,10 +210,6 @@ struct System::Impl { | |||
| 216 | AddGlueRegistrationForProcess(*app_loader, *main_process); | 210 | AddGlueRegistrationForProcess(*app_loader, *main_process); |
| 217 | kernel.MakeCurrentProcess(main_process.get()); | 211 | kernel.MakeCurrentProcess(main_process.get()); |
| 218 | 212 | ||
| 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 | 213 | // Initialize cheat engine |
| 224 | if (cheat_engine) { | 214 | if (cheat_engine) { |
| 225 | cheat_engine->Initialize(); | 215 | cheat_engine->Initialize(); |
| @@ -277,7 +267,6 @@ struct System::Impl { | |||
| 277 | } | 267 | } |
| 278 | 268 | ||
| 279 | // Shutdown emulation session | 269 | // Shutdown emulation session |
| 280 | renderer.reset(); | ||
| 281 | GDBStub::Shutdown(); | 270 | GDBStub::Shutdown(); |
| 282 | Service::Shutdown(); | 271 | Service::Shutdown(); |
| 283 | service_manager.reset(); | 272 | service_manager.reset(); |
| @@ -353,7 +342,6 @@ struct System::Impl { | |||
| 353 | Service::FileSystem::FileSystemController fs_controller; | 342 | Service::FileSystem::FileSystemController fs_controller; |
| 354 | /// AppLoader used to load the current executing application | 343 | /// AppLoader used to load the current executing application |
| 355 | std::unique_ptr<Loader::AppLoader> app_loader; | 344 | std::unique_ptr<Loader::AppLoader> app_loader; |
| 356 | std::unique_ptr<VideoCore::RendererBase> renderer; | ||
| 357 | std::unique_ptr<Tegra::GPU> gpu_core; | 345 | std::unique_ptr<Tegra::GPU> gpu_core; |
| 358 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | 346 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; |
| 359 | Memory::Memory memory; | 347 | Memory::Memory memory; |
| @@ -536,11 +524,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const { | |||
| 536 | } | 524 | } |
| 537 | 525 | ||
| 538 | VideoCore::RendererBase& System::Renderer() { | 526 | VideoCore::RendererBase& System::Renderer() { |
| 539 | return *impl->renderer; | 527 | return impl->gpu_core->Renderer(); |
| 540 | } | 528 | } |
| 541 | 529 | ||
| 542 | const VideoCore::RendererBase& System::Renderer() const { | 530 | const VideoCore::RendererBase& System::Renderer() const { |
| 543 | return *impl->renderer; | 531 | return impl->gpu_core->Renderer(); |
| 544 | } | 532 | } |
| 545 | 533 | ||
| 546 | Kernel::KernelCore& System::Kernel() { | 534 | Kernel::KernelCore& System::Kernel() { |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 5eb87fb63..bb283d844 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 | 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..8f59e0442 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -305,7 +305,6 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | const std::vector gl_cache = disk_cache.LoadPrecompiled(); | 307 | const std::vector gl_cache = disk_cache.LoadPrecompiled(); |
| 308 | const auto supported_formats = GetSupportedFormats(); | ||
| 309 | 308 | ||
| 310 | // Track if precompiled cache was altered during loading to know if we have to | 309 | // Track if precompiled cache was altered during loading to know if we have to |
| 311 | // serialize the virtual precompiled cache file back to the hard drive | 310 | // serialize the virtual precompiled cache file back to the hard drive |
| @@ -327,8 +326,8 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 327 | 326 | ||
| 328 | const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, | 327 | const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, |
| 329 | std::size_t end) { | 328 | std::size_t end) { |
| 330 | context->MakeCurrent(); | 329 | const auto scope = context->Acquire(); |
| 331 | SCOPE_EXIT({ return context->DoneCurrent(); }); | 330 | const auto supported_formats = GetSupportedFormats(); |
| 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..6f08803c1 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -7,9 +7,7 @@ | |||
| 7 | #include <cstdlib> | 7 | #include <cstdlib> |
| 8 | #include <cstring> | 8 | #include <cstring> |
| 9 | #include <memory> | 9 | #include <memory> |
| 10 | |||
| 11 | #include <glad/glad.h> | 10 | #include <glad/glad.h> |
| 12 | |||
| 13 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 14 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 15 | #include "common/microprofile.h" | 13 | #include "common/microprofile.h" |
| @@ -30,8 +28,6 @@ namespace OpenGL { | |||
| 30 | 28 | ||
| 31 | namespace { | 29 | namespace { |
| 32 | 30 | ||
| 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; | 31 | constexpr std::size_t SWAP_CHAIN_SIZE = 3; |
| 36 | 32 | ||
| 37 | struct Frame { | 33 | struct Frame { |
| @@ -214,7 +210,7 @@ public: | |||
| 214 | std::deque<Frame*> present_queue; | 210 | std::deque<Frame*> present_queue; |
| 215 | Frame* previous_frame{}; | 211 | Frame* previous_frame{}; |
| 216 | 212 | ||
| 217 | FrameMailbox() : has_debug_tool{HasDebugTool()} { | 213 | FrameMailbox() { |
| 218 | for (auto& frame : swap_chain) { | 214 | for (auto& frame : swap_chain) { |
| 219 | free_queue.push(&frame); | 215 | free_queue.push(&frame); |
| 220 | } | 216 | } |
| @@ -285,13 +281,9 @@ public: | |||
| 285 | std::unique_lock lock{swap_chain_lock}; | 281 | std::unique_lock lock{swap_chain_lock}; |
| 286 | present_queue.push_front(frame); | 282 | present_queue.push_front(frame); |
| 287 | present_cv.notify_one(); | 283 | present_cv.notify_one(); |
| 288 | |||
| 289 | DebugNotifyNextFrame(); | ||
| 290 | } | 284 | } |
| 291 | 285 | ||
| 292 | Frame* TryGetPresentFrame(int timeout_ms) { | 286 | Frame* TryGetPresentFrame(int timeout_ms) { |
| 293 | DebugWaitForNextFrame(); | ||
| 294 | |||
| 295 | std::unique_lock lock{swap_chain_lock}; | 287 | std::unique_lock lock{swap_chain_lock}; |
| 296 | // wait for new entries in the present_queue | 288 | // wait for new entries in the present_queue |
| 297 | present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), | 289 | present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), |
| @@ -317,38 +309,12 @@ public: | |||
| 317 | previous_frame = frame; | 309 | previous_frame = frame; |
| 318 | return frame; | 310 | return frame; |
| 319 | } | 311 | } |
| 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 | }; | 312 | }; |
| 348 | 313 | ||
| 349 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) | 314 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, |
| 350 | : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, | 315 | Core::Frontend::GraphicsContext& context) |
| 351 | frame_mailbox{std::make_unique<FrameMailbox>()} {} | 316 | : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, frame_mailbox{}, |
| 317 | has_debug_tool{HasDebugTool()}, context{context} {} | ||
| 352 | 318 | ||
| 353 | RendererOpenGL::~RendererOpenGL() = default; | 319 | RendererOpenGL::~RendererOpenGL() = default; |
| 354 | 320 | ||
| @@ -356,8 +322,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)); | 322 | MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); |
| 357 | 323 | ||
| 358 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 324 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |
| 359 | render_window.PollEvents(); | ||
| 360 | |||
| 361 | if (!framebuffer) { | 325 | if (!framebuffer) { |
| 362 | return; | 326 | return; |
| 363 | } | 327 | } |
| @@ -413,6 +377,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 413 | m_current_frame++; | 377 | m_current_frame++; |
| 414 | rasterizer->TickFrame(); | 378 | rasterizer->TickFrame(); |
| 415 | } | 379 | } |
| 380 | |||
| 381 | render_window.PollEvents(); | ||
| 382 | if (has_debug_tool) { | ||
| 383 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||
| 384 | Present(0); | ||
| 385 | context.SwapBuffers(); | ||
| 386 | } | ||
| 416 | } | 387 | } |
| 417 | 388 | ||
| 418 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { | 389 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { |
| @@ -480,6 +451,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color | |||
| 480 | } | 451 | } |
| 481 | 452 | ||
| 482 | void RendererOpenGL::InitOpenGLObjects() { | 453 | void RendererOpenGL::InitOpenGLObjects() { |
| 454 | frame_mailbox = std::make_unique<FrameMailbox>(); | ||
| 455 | |||
| 483 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, | 456 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, |
| 484 | 0.0f); | 457 | 0.0f); |
| 485 | 458 | ||
| @@ -692,12 +665,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 692 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | 665 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| 693 | } | 666 | } |
| 694 | 667 | ||
| 695 | void RendererOpenGL::TryPresent(int timeout_ms) { | 668 | bool RendererOpenGL::TryPresent(int timeout_ms) { |
| 669 | if (has_debug_tool) { | ||
| 670 | LOG_DEBUG(Render_OpenGL, | ||
| 671 | "Skipping presentation because we are presenting on the main context"); | ||
| 672 | return false; | ||
| 673 | } | ||
| 674 | return Present(timeout_ms); | ||
| 675 | } | ||
| 676 | |||
| 677 | bool RendererOpenGL::Present(int timeout_ms) { | ||
| 696 | const auto& layout = render_window.GetFramebufferLayout(); | 678 | const auto& layout = render_window.GetFramebufferLayout(); |
| 697 | auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms); | 679 | auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms); |
| 698 | if (!frame) { | 680 | if (!frame) { |
| 699 | LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); | 681 | LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); |
| 700 | return; | 682 | return false; |
| 701 | } | 683 | } |
| 702 | 684 | ||
| 703 | // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a | 685 | // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a |
| @@ -725,6 +707,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) { | |||
| 725 | glFlush(); | 707 | glFlush(); |
| 726 | 708 | ||
| 727 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | 709 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); |
| 710 | return true; | ||
| 728 | } | 711 | } |
| 729 | 712 | ||
| 730 | void RendererOpenGL::RenderScreenshot() { | 713 | 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..fd9fec018 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 | } // 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 {}; | ||
| 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..d120ee818 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,27 +35,13 @@ | |||
| 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}; | ||
| 62 | |||
| 63 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 45 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 64 | 46 | ||
| 65 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( | 47 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( |
| @@ -69,6 +51,10 @@ void EmuThread::run() { | |||
| 69 | 51 | ||
| 70 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | 52 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); |
| 71 | 53 | ||
| 54 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU | ||
| 55 | // execution. | ||
| 56 | Core::System::GetInstance().GPU().Start(); | ||
| 57 | |||
| 72 | // Holds whether the cpu was running during the last iteration, | 58 | // Holds whether the cpu was running during the last iteration, |
| 73 | // so that the DebugModeLeft signal can be emitted before the | 59 | // so that the DebugModeLeft signal can be emitted before the |
| 74 | // next execution step | 60 | // next execution step |
| @@ -111,162 +97,195 @@ 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 | context->doneCurrent(); | ||
| 145 | } | ||
| 146 | |||
| 147 | void SwapBuffers() override { | ||
| 148 | context->swapBuffers(surface); | ||
| 130 | } | 149 | } |
| 131 | 150 | ||
| 132 | void MakeCurrent() override { | 151 | void MakeCurrent() override { |
| 152 | if (is_current) { | ||
| 153 | return; | ||
| 154 | } | ||
| 133 | context->makeCurrent(surface); | 155 | context->makeCurrent(surface); |
| 134 | } | 156 | } |
| 135 | 157 | ||
| 136 | void DoneCurrent() override { | 158 | void DoneCurrent() override { |
| 137 | context->doneCurrent(); | 159 | context->doneCurrent(); |
| 160 | is_current = false; | ||
| 161 | } | ||
| 162 | |||
| 163 | QOpenGLContext* GetShareContext() const { | ||
| 164 | return context.get(); | ||
| 138 | } | 165 | } |
| 139 | 166 | ||
| 140 | private: | 167 | private: |
| 141 | QOpenGLContext* context; | 168 | // Avoid using Qt parent system here since we might move the QObjects to new threads |
| 142 | QOffscreenSurface* surface; | 169 | // As a note, this means we should avoid using slots/signals with the objects too |
| 170 | std::unique_ptr<QOpenGLContext> context; | ||
| 171 | std::unique_ptr<QOffscreenSurface> offscreen_surface{}; | ||
| 172 | QSurface* surface; | ||
| 173 | bool is_current = false; | ||
| 143 | }; | 174 | }; |
| 144 | 175 | ||
| 145 | class ChildRenderWindow : public QWindow { | 176 | class DummyContext : public Core::Frontend::GraphicsContext {}; |
| 177 | |||
| 178 | class RenderWidget : public QWidget { | ||
| 146 | public: | 179 | public: |
| 147 | ChildRenderWindow(QWindow* parent, QWidget* event_handler) | 180 | RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { |
| 148 | : QWindow{parent}, event_handler{event_handler} {} | 181 | setAttribute(Qt::WA_NativeWindow); |
| 149 | 182 | setAttribute(Qt::WA_PaintOnScreen); | |
| 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 | } | 183 | } |
| 196 | 184 | ||
| 197 | void exposeEvent(QExposeEvent* event) override { | 185 | virtual ~RenderWidget() = default; |
| 198 | QWindow::requestUpdate(); | 186 | |
| 199 | QWindow::exposeEvent(event); | 187 | virtual void Present() {} |
| 188 | |||
| 189 | void paintEvent(QPaintEvent* event) override { | ||
| 190 | Present(); | ||
| 191 | update(); | ||
| 200 | } | 192 | } |
| 201 | 193 | ||
| 202 | private: | 194 | void resizeEvent(QResizeEvent* ev) override { |
| 203 | QWidget* event_handler{}; | 195 | render_window->resize(ev->size()); |
| 204 | }; | 196 | render_window->OnFramebufferSizeChanged(); |
| 197 | } | ||
| 205 | 198 | ||
| 206 | class OpenGLWindow final : public ChildRenderWindow { | 199 | void keyPressEvent(QKeyEvent* event) override { |
| 207 | public: | 200 | InputCommon::GetKeyboard()->PressKey(event->key()); |
| 208 | OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) | 201 | } |
| 209 | : ChildRenderWindow{parent, event_handler}, | ||
| 210 | context(new QOpenGLContext(shared_context->parent())) { | ||
| 211 | 202 | ||
| 212 | // disable vsync for any shared contexts | 203 | void keyReleaseEvent(QKeyEvent* event) override { |
| 213 | auto format = shared_context->format(); | 204 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); |
| 214 | format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); | 205 | } |
| 215 | this->setFormat(format); | ||
| 216 | 206 | ||
| 217 | context->setShareContext(shared_context); | 207 | void mousePressEvent(QMouseEvent* event) override { |
| 218 | context->setScreen(this->screen()); | 208 | if (event->source() == Qt::MouseEventSynthesizedBySystem) |
| 219 | context->setFormat(format); | 209 | return; // touch input is handled in TouchBeginEvent |
| 220 | context->create(); | ||
| 221 | 210 | ||
| 222 | setSurfaceType(QWindow::OpenGLSurface); | 211 | const auto pos{event->pos()}; |
| 212 | if (event->button() == Qt::LeftButton) { | ||
| 213 | const auto [x, y] = render_window->ScaleTouch(pos); | ||
| 214 | render_window->TouchPressed(x, y); | ||
| 215 | } else if (event->button() == Qt::RightButton) { | ||
| 216 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | void mouseMoveEvent(QMouseEvent* event) override { | ||
| 221 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 222 | return; // touch input is handled in TouchUpdateEvent | ||
| 223 | 223 | ||
| 224 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 224 | const auto pos{event->pos()}; |
| 225 | // WA_DontShowOnScreen, WA_DeleteOnClose | 225 | const auto [x, y] = render_window->ScaleTouch(pos); |
| 226 | render_window->TouchMoved(x, y); | ||
| 227 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||
| 226 | } | 228 | } |
| 227 | 229 | ||
| 228 | ~OpenGLWindow() override { | 230 | void mouseReleaseEvent(QMouseEvent* event) override { |
| 229 | context->doneCurrent(); | 231 | if (event->source() == Qt::MouseEventSynthesizedBySystem) |
| 232 | return; // touch input is handled in TouchEndEvent | ||
| 233 | |||
| 234 | if (event->button() == Qt::LeftButton) | ||
| 235 | render_window->TouchReleased(); | ||
| 236 | else if (event->button() == Qt::RightButton) | ||
| 237 | InputCommon::GetMotionEmu()->EndTilt(); | ||
| 230 | } | 238 | } |
| 231 | 239 | ||
| 232 | void Present() override { | 240 | std::pair<unsigned, unsigned> GetSize() const { |
| 233 | if (!isExposed()) { | 241 | return std::make_pair(width(), height()); |
| 234 | return; | 242 | } |
| 235 | } | ||
| 236 | 243 | ||
| 237 | context->makeCurrent(this); | 244 | QPaintEngine* paintEngine() const override { |
| 238 | Core::System::GetInstance().Renderer().TryPresent(100); | 245 | return nullptr; |
| 239 | context->swapBuffers(this); | ||
| 240 | auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); | ||
| 241 | f->glFinish(); | ||
| 242 | QWindow::requestUpdate(); | ||
| 243 | } | 246 | } |
| 244 | 247 | ||
| 245 | private: | 248 | private: |
| 246 | QOpenGLContext* context{}; | 249 | GRenderWindow* render_window; |
| 247 | }; | 250 | }; |
| 248 | 251 | ||
| 249 | #ifdef HAS_VULKAN | 252 | class OpenGLRenderWidget : public RenderWidget { |
| 250 | class VulkanWindow final : public ChildRenderWindow { | ||
| 251 | public: | 253 | public: |
| 252 | VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) | 254 | explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { |
| 253 | : ChildRenderWindow{parent, event_handler} { | 255 | windowHandle()->setSurfaceType(QWindow::OpenGLSurface); |
| 254 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); | ||
| 255 | setVulkanInstance(instance); | ||
| 256 | } | 256 | } |
| 257 | 257 | ||
| 258 | ~VulkanWindow() override = default; | 258 | void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) { |
| 259 | context = std::move(context_); | ||
| 260 | } | ||
| 259 | 261 | ||
| 260 | void Present() override { | 262 | void Present() override { |
| 261 | // TODO(bunnei): ImplementMe | 263 | if (!isVisible()) { |
| 264 | return; | ||
| 265 | } | ||
| 266 | |||
| 267 | context->MakeCurrent(); | ||
| 268 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||
| 269 | if (Core::System::GetInstance().Renderer().TryPresent(100)) { | ||
| 270 | context->SwapBuffers(); | ||
| 271 | glFinish(); | ||
| 272 | } | ||
| 262 | } | 273 | } |
| 263 | 274 | ||
| 264 | private: | 275 | private: |
| 265 | QWidget* event_handler{}; | 276 | std::unique_ptr<Core::Frontend::GraphicsContext> context{}; |
| 266 | }; | 277 | }; |
| 267 | #endif | ||
| 268 | 278 | ||
| 269 | GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | 279 | class VulkanRenderWidget : public RenderWidget { |
| 280 | public: | ||
| 281 | explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance) | ||
| 282 | : RenderWidget(parent) { | ||
| 283 | windowHandle()->setSurfaceType(QWindow::VulkanSurface); | ||
| 284 | windowHandle()->setVulkanInstance(instance); | ||
| 285 | } | ||
| 286 | }; | ||
| 287 | |||
| 288 | GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread) | ||
| 270 | : QWidget(parent_), emu_thread(emu_thread) { | 289 | : QWidget(parent_), emu_thread(emu_thread) { |
| 271 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 290 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 272 | .arg(QString::fromUtf8(Common::g_build_name), | 291 | .arg(QString::fromUtf8(Common::g_build_name), |
| @@ -278,26 +297,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | |||
| 278 | setLayout(layout); | 297 | setLayout(layout); |
| 279 | InputCommon::Init(); | 298 | InputCommon::Init(); |
| 280 | 299 | ||
| 281 | GMainWindow* parent = GetMainWindow(); | 300 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete); |
| 282 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | ||
| 283 | } | 301 | } |
| 284 | 302 | ||
| 285 | GRenderWindow::~GRenderWindow() { | 303 | GRenderWindow::~GRenderWindow() { |
| 286 | InputCommon::Shutdown(); | 304 | InputCommon::Shutdown(); |
| 287 | } | 305 | } |
| 288 | 306 | ||
| 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() { | 307 | void GRenderWindow::PollEvents() { |
| 302 | if (!first_frame) { | 308 | if (!first_frame) { |
| 303 | first_frame = true; | 309 | first_frame = true; |
| @@ -309,21 +315,6 @@ bool GRenderWindow::IsShown() const { | |||
| 309 | return !isMinimized(); | 315 | return !isMinimized(); |
| 310 | } | 316 | } |
| 311 | 317 | ||
| 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). | 318 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). |
| 328 | // | 319 | // |
| 329 | // Older versions get the window size (density independent pixels), | 320 | // Older versions get the window size (density independent pixels), |
| @@ -474,9 +465,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { | |||
| 474 | 465 | ||
| 475 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | 466 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { |
| 476 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | 467 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| 477 | return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); | 468 | auto c = static_cast<OpenGLSharedContext*>(main_context.get()); |
| 469 | // Bind the shared contexts to the main surface in case the backend wants to take over | ||
| 470 | // presentation | ||
| 471 | return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), | ||
| 472 | child_widget->windowHandle()); | ||
| 478 | } | 473 | } |
| 479 | return {}; | 474 | return std::make_unique<DummyContext>(); |
| 480 | } | 475 | } |
| 481 | 476 | ||
| 482 | bool GRenderWindow::InitRenderTarget() { | 477 | bool GRenderWindow::InitRenderTarget() { |
| @@ -497,14 +492,11 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 497 | break; | 492 | break; |
| 498 | } | 493 | } |
| 499 | 494 | ||
| 495 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 496 | layout()->addWidget(child_widget); | ||
| 500 | // Reset minimum required size to avoid resizing issues on the main window after restarting. | 497 | // Reset minimum required size to avoid resizing issues on the main window after restarting. |
| 501 | setMinimumSize(1, 1); | 498 | setMinimumSize(1, 1); |
| 502 | 499 | ||
| 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); | 500 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); |
| 509 | 501 | ||
| 510 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 502 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| @@ -523,9 +515,10 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 523 | void GRenderWindow::ReleaseRenderTarget() { | 515 | void GRenderWindow::ReleaseRenderTarget() { |
| 524 | if (child_widget) { | 516 | if (child_widget) { |
| 525 | layout()->removeWidget(child_widget); | 517 | layout()->removeWidget(child_widget); |
| 526 | delete child_widget; | 518 | child_widget->deleteLater(); |
| 527 | child_widget = nullptr; | 519 | child_widget = nullptr; |
| 528 | } | 520 | } |
| 521 | main_context.reset(); | ||
| 529 | } | 522 | } |
| 530 | 523 | ||
| 531 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | 524 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { |
| @@ -557,24 +550,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
| 557 | bool GRenderWindow::InitializeOpenGL() { | 550 | bool GRenderWindow::InitializeOpenGL() { |
| 558 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 551 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 559 | // WA_DontShowOnScreen, WA_DeleteOnClose | 552 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 560 | QSurfaceFormat fmt; | 553 | auto child = new OpenGLRenderWidget(this); |
| 561 | fmt.setVersion(4, 3); | 554 | child_widget = child; |
| 562 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | 555 | child_widget->windowHandle()->create(); |
| 563 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | 556 | auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle()); |
| 564 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 557 | main_context = context; |
| 565 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 558 | child->SetContext( |
| 566 | fmt.setSwapInterval(0); | 559 | 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 | 560 | ||
| 579 | return true; | 561 | return true; |
| 580 | } | 562 | } |
| @@ -604,13 +586,10 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 604 | return false; | 586 | return false; |
| 605 | } | 587 | } |
| 606 | 588 | ||
| 607 | GMainWindow* parent = GetMainWindow(); | 589 | auto child = new VulkanRenderWidget(this, vk_instance.get()); |
| 608 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | 590 | child_widget = child; |
| 609 | child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); | 591 | child_widget->windowHandle()->create(); |
| 610 | child_window->create(); | 592 | 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 | 593 | ||
| 615 | return true; | 594 | return true; |
| 616 | #else | 595 | #else |
| @@ -620,8 +599,24 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 620 | #endif | 599 | #endif |
| 621 | } | 600 | } |
| 622 | 601 | ||
| 602 | void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 603 | void* surface) const { | ||
| 604 | #ifdef HAS_VULKAN | ||
| 605 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); | ||
| 606 | const VkInstance instance_copy = vk_instance->vkInstance(); | ||
| 607 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle()); | ||
| 608 | |||
| 609 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 610 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); | ||
| 611 | std::memcpy(surface, &surface_copy, sizeof(surface_copy)); | ||
| 612 | #else | ||
| 613 | UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); | ||
| 614 | #endif | ||
| 615 | } | ||
| 616 | |||
| 623 | bool GRenderWindow::LoadOpenGL() { | 617 | bool GRenderWindow::LoadOpenGL() { |
| 624 | Core::Frontend::ScopeAcquireContext acquire_context{*this}; | 618 | auto context = CreateSharedContext(); |
| 619 | auto scope = context->Acquire(); | ||
| 625 | if (!gladLoadGL()) { | 620 | if (!gladLoadGL()) { |
| 626 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), | 621 | 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 " | 622 | 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..3739ec7ed 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -7,23 +7,20 @@ | |||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <condition_variable> | 8 | #include <condition_variable> |
| 9 | #include <mutex> | 9 | #include <mutex> |
| 10 | 10 | #include <thread> | |
| 11 | #include <QImage> | 11 | #include <QImage> |
| 12 | #include <QThread> | 12 | #include <QThread> |
| 13 | #include <QWidget> | 13 | #include <QWidget> |
| 14 | #include <QWindow> | 14 | #include <QWindow> |
| 15 | |||
| 16 | #include "common/thread.h" | 15 | #include "common/thread.h" |
| 17 | #include "core/core.h" | 16 | #include "core/core.h" |
| 18 | #include "core/frontend/emu_window.h" | 17 | #include "core/frontend/emu_window.h" |
| 19 | 18 | ||
| 20 | class GRenderWindow; | 19 | class GRenderWindow; |
| 20 | class GMainWindow; | ||
| 21 | class QKeyEvent; | 21 | class QKeyEvent; |
| 22 | class QScreen; | ||
| 23 | class QTouchEvent; | 22 | class QTouchEvent; |
| 24 | class QStringList; | 23 | class QStringList; |
| 25 | class QSurface; | ||
| 26 | class QOpenGLContext; | ||
| 27 | #ifdef HAS_VULKAN | 24 | #ifdef HAS_VULKAN |
| 28 | class QVulkanInstance; | 25 | class QVulkanInstance; |
| 29 | #endif | 26 | #endif |
| @@ -36,7 +33,7 @@ class EmuThread final : public QThread { | |||
| 36 | Q_OBJECT | 33 | Q_OBJECT |
| 37 | 34 | ||
| 38 | public: | 35 | public: |
| 39 | explicit EmuThread(GRenderWindow& window); | 36 | explicit EmuThread(); |
| 40 | ~EmuThread() override; | 37 | ~EmuThread() override; |
| 41 | 38 | ||
| 42 | /** | 39 | /** |
| @@ -87,14 +84,8 @@ private: | |||
| 87 | bool exec_step = false; | 84 | bool exec_step = false; |
| 88 | bool running = false; | 85 | bool running = false; |
| 89 | std::atomic_bool stop_run{false}; | 86 | std::atomic_bool stop_run{false}; |
| 90 | std::mutex running_mutex; | 87 | std::mutex running_mutex = {}; |
| 91 | std::condition_variable running_cv; | 88 | std::condition_variable running_cv = {}; |
| 92 | |||
| 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 | 89 | ||
| 99 | signals: | 90 | signals: |
| 100 | /** | 91 | /** |
| @@ -124,12 +115,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { | |||
| 124 | Q_OBJECT | 115 | Q_OBJECT |
| 125 | 116 | ||
| 126 | public: | 117 | public: |
| 127 | GRenderWindow(QWidget* parent, EmuThread* emu_thread); | 118 | GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); |
| 128 | ~GRenderWindow() override; | 119 | ~GRenderWindow() override; |
| 129 | 120 | ||
| 130 | // EmuWindow implementation. | 121 | // EmuWindow implementation. |
| 131 | void MakeCurrent() override; | ||
| 132 | void DoneCurrent() override; | ||
| 133 | void PollEvents() override; | 122 | void PollEvents() override; |
| 134 | bool IsShown() const override; | 123 | bool IsShown() const override; |
| 135 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 124 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| @@ -165,6 +154,8 @@ public: | |||
| 165 | 154 | ||
| 166 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); | 155 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); |
| 167 | 156 | ||
| 157 | std::pair<u32, u32> ScaleTouch(const QPointF pos) const; | ||
| 158 | |||
| 168 | public slots: | 159 | public slots: |
| 169 | void OnEmulationStarting(EmuThread* emu_thread); | 160 | void OnEmulationStarting(EmuThread* emu_thread); |
| 170 | void OnEmulationStopping(); | 161 | void OnEmulationStopping(); |
| @@ -176,7 +167,6 @@ signals: | |||
| 176 | void FirstFrameDisplayed(); | 167 | void FirstFrameDisplayed(); |
| 177 | 168 | ||
| 178 | private: | 169 | private: |
| 179 | std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||
| 180 | void TouchBeginEvent(const QTouchEvent* event); | 170 | void TouchBeginEvent(const QTouchEvent* event); |
| 181 | void TouchUpdateEvent(const QTouchEvent* event); | 171 | void TouchUpdateEvent(const QTouchEvent* event); |
| 182 | void TouchEndEvent(); | 172 | void TouchEndEvent(); |
| @@ -190,7 +180,10 @@ private: | |||
| 190 | 180 | ||
| 191 | EmuThread* emu_thread; | 181 | EmuThread* emu_thread; |
| 192 | 182 | ||
| 193 | std::unique_ptr<GraphicsContext> core_context; | 183 | // Main context that will be shared with all other contexts that are requested. |
| 184 | // If this is used in a shared context setting, then this should not be used directly, but | ||
| 185 | // should instead be shared from | ||
| 186 | std::shared_ptr<Core::Frontend::GraphicsContext> main_context; | ||
| 194 | 187 | ||
| 195 | #ifdef HAS_VULKAN | 188 | #ifdef HAS_VULKAN |
| 196 | std::unique_ptr<QVulkanInstance> vk_instance; | 189 | std::unique_ptr<QVulkanInstance> vk_instance; |
| @@ -201,12 +194,6 @@ private: | |||
| 201 | 194 | ||
| 202 | QByteArray geometry; | 195 | QByteArray geometry; |
| 203 | 196 | ||
| 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; | 197 | QWidget* child_widget = nullptr; |
| 211 | 198 | ||
| 212 | bool first_frame = false; | 199 | bool first_frame = false; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 47615adfe..d7684e241 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 | ||
| @@ -2375,7 +2375,6 @@ int main(int argc, char* argv[]) { | |||
| 2375 | 2375 | ||
| 2376 | // Enables the core to make the qt created contexts current on std::threads | 2376 | // Enables the core to make the qt created contexts current on std::threads |
| 2377 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2377 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
| 2378 | QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); | ||
| 2379 | QApplication app(argc, argv); | 2378 | QApplication app(argc, argv); |
| 2380 | 2379 | ||
| 2381 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 2380 | // 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..ee61179a0 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -148,14 +148,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { | |||
| 148 | SDL_GL_DeleteContext(window_context); | 148 | SDL_GL_DeleteContext(window_context); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 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, | 151 | void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 160 | void* surface) const { | 152 | void* surface) const { |
| 161 | // Should not have been called from OpenGL | 153 | // 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..e5db7d819 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -232,15 +232,8 @@ int main(int argc, char** argv) { | |||
| 232 | 232 | ||
| 233 | system.Renderer().Rasterizer().LoadDiskResources(); | 233 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 234 | 234 | ||
| 235 | // Acquire render context for duration of the thread if this is the rendering thread | 235 | // Core is loaded, start the GPU (makes the GPU contexts current to this thread) |
| 236 | if (!Settings::values.use_asynchronous_gpu_emulation) { | 236 | system.GPU().Start(); |
| 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()) { |