summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar James Rowe2020-03-24 20:58:49 -0600
committerGravatar James Rowe2020-03-24 21:03:42 -0600
commit282adfc70b5d7d958d564bfda0227bb3fbd8d110 (patch)
tree2a98e3bedec2e7fdb33478814a73be664661aecc /src
parentUse the correct directory for Qt Plugins (diff)
downloadyuzu-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')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp20
-rw-r--r--src/core/frontend/emu_window.h44
-rw-r--r--src/core/frontend/scope_acquire_context.cpp18
-rw-r--r--src/core/frontend/scope_acquire_context.h23
-rw-r--r--src/video_core/gpu.cpp10
-rw-r--r--src/video_core/gpu.h18
-rw-r--r--src/video_core/gpu_asynch.cpp9
-rw-r--r--src/video_core/gpu_asynch.h9
-rw-r--r--src/video_core/gpu_synch.cpp17
-rw-r--r--src/video_core/gpu_synch.h10
-rw-r--r--src/video_core/gpu_thread.cpp15
-rw-r--r--src/video_core/gpu_thread.h7
-rw-r--r--src/video_core/renderer_base.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp5
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp69
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h10
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp3
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h2
-rw-r--r--src/video_core/video_core.cpp28
-rw-r--r--src/video_core/video_core.h11
-rw-r--r--src/yuzu/bootmanager.cpp375
-rw-r--r--src/yuzu/bootmanager.h37
-rw-r--r--src/yuzu/main.cpp3
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h2
-rw-r--r--src/yuzu_cmd/yuzu.cpp11
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
538VideoCore::RendererBase& System::Renderer() { 526VideoCore::RendererBase& System::Renderer() {
539 return *impl->renderer; 527 return impl->gpu_core->Renderer();
540} 528}
541 529
542const VideoCore::RendererBase& System::Renderer() const { 530const VideoCore::RendererBase& System::Renderer() const {
543 return *impl->renderer; 531 return impl->gpu_core->Renderer();
544} 532}
545 533
546Kernel::KernelCore& System::Kernel() { 534Kernel::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 @@
13namespace Core::Frontend { 13namespace 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 */
20class GraphicsContext { 18class GraphicsContext {
21public: 19public:
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 */
49class EmuWindow : public GraphicsContext { 69class EmuWindow {
50public: 70public:
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
8namespace Core::Frontend {
9
10ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context)
11 : context{context} {
12 context.MakeCurrent();
13}
14ScopeAcquireContext::~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
9namespace Core::Frontend {
10
11class GraphicsContext;
12
13/// Helper class to acquire/release window context within a given scope
14class ScopeAcquireContext : NonCopyable {
15public:
16 explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context);
17 ~ScopeAcquireContext();
18
19private:
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
20namespace Tegra { 22namespace Tegra {
21 23
22MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); 24MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
23 25
24GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async) 26GPU::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
139void GPU::FlushCommands() { 141void 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
27namespace Core { 27namespace Core {
28class System; 28namespace Frontend {
29class EmuWindow;
29} 30}
31class System;
32} // namespace Core
30 33
31namespace VideoCore { 34namespace VideoCore {
32class RendererBase; 35class RendererBase;
@@ -129,7 +132,8 @@ class MemoryManager;
129 132
130class GPU { 133class GPU {
131public: 134public:
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:
287protected: 299protected:
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
292private: 304private:
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
11namespace VideoCommon { 11namespace VideoCommon {
12 12
13GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) 13GPUAsynch::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
16GPUAsynch::~GPUAsynch() = default; 18GPUAsynch::~GPUAsynch() = default;
17 19
18void GPUAsynch::Start() { 20void 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
22void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { 25void 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
10namespace Core::Frontend {
11class GraphicsContext;
12}
13
10namespace VideoCore { 14namespace VideoCore {
11class RendererBase; 15class 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
17class GPUAsynch final : public Tegra::GPU { 21class GPUAsynch final : public Tegra::GPU {
18public: 22public:
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
33private: 38private:
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
8namespace VideoCommon { 8namespace VideoCommon {
9 9
10GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer) 10GPUSynch::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
13GPUSynch::~GPUSynch() = default; 14GPUSynch::~GPUSynch() = default;
14 15
15void GPUSynch::Start() {} 16void GPUSynch::Start() {
17 context->MakeCurrent();
18}
16 19
17void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { 20void 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
22void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 25void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
23 renderer.SwapBuffers(framebuffer); 26 renderer->SwapBuffers(framebuffer);
24} 27}
25 28
26void GPUSynch::FlushRegion(CacheAddr addr, u64 size) { 29void GPUSynch::FlushRegion(CacheAddr addr, u64 size) {
27 renderer.Rasterizer().FlushRegion(addr, size); 30 renderer->Rasterizer().FlushRegion(addr, size);
28} 31}
29 32
30void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) { 33void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) {
31 renderer.Rasterizer().InvalidateRegion(addr, size); 34 renderer->Rasterizer().InvalidateRegion(addr, size);
32} 35}
33 36
34void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { 37void 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
9namespace Core::Frontend {
10class GraphicsContext;
11}
12
9namespace VideoCore { 13namespace VideoCore {
10class RendererBase; 14class 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
16class GPUSynch final : public Tegra::GPU { 20class GPUSynch final : public Tegra::GPU {
17public: 21public:
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:
29protected: 34protected:
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
38private:
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 @@
14namespace VideoCommon::GPUThread { 14namespace VideoCommon::GPUThread {
15 15
16/// Runs the GPU thread 16/// Runs the GPU thread
17static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher, 17static 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
65void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) { 65void 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
69void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 72void 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
22namespace Core { 21namespace Core {
22namespace Frontend {
23class GraphicsContext;
24}
23class System; 25class 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
31namespace { 29namespace {
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.
35constexpr std::size_t SWAP_CHAIN_SIZE = 3; 31constexpr std::size_t SWAP_CHAIN_SIZE = 3;
36 32
37struct Frame { 33struct 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
321private:
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
349RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) 314RendererOpenGL::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
353RendererOpenGL::~RendererOpenGL() = default; 319RendererOpenGL::~RendererOpenGL() = default;
354 320
@@ -356,8 +322,6 @@ MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 12
356MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); 322MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
357 323
358void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 324void 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
418void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { 389void 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
482void RendererOpenGL::InitOpenGLObjects() { 453void 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
695void RendererOpenGL::TryPresent(int timeout_ms) { 668bool 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
677bool 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
730void RendererOpenGL::RenderScreenshot() { 713void 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
56class RendererOpenGL final : public VideoCore::RendererBase { 56class RendererOpenGL final : public VideoCore::RendererBase {
57public: 57public:
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
66private: 67private:
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
144void RendererVulkan::TryPresent(int /*timeout_ms*/) { 144bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
145 // TODO (bunnei): ImplementMe 145 // TODO (bunnei): ImplementMe
146 return true;
146} 147}
147 148
148bool RendererVulkan::Init() { 149bool 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
47private: 47private:
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
18namespace VideoCore { 18namespace {
19 19std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
20std::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
34std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { 35namespace VideoCore {
35 if (Settings::values.use_asynchronous_gpu_emulation) { 36
36 return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer()); 37std::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
42u16 GetResolutionScaleFactor(const RendererBase& renderer) { 52u16 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
23class RendererBase; 23class 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 */
31std::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.
35std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system); 26std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
36 27
37u16 GetResolutionScaleFactor(const RendererBase& renderer); 28u16 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
42EmuThread::EmuThread(GRenderWindow& window) 38EmuThread::EmuThread() = default;
43 : shared_context{window.CreateSharedContext()},
44 context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context
45 : window} {}
46 39
47EmuThread::~EmuThread() = default; 40EmuThread::~EmuThread() = default;
48 41
49static 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
58void EmuThread::run() { 42void 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
114class GGLContext : public Core::Frontend::GraphicsContext { 100class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
115public: 101public:
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
140private: 167private:
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
145class ChildRenderWindow : public QWindow { 176class DummyContext : public Core::Frontend::GraphicsContext {};
177
178class RenderWidget : public QWidget {
146public: 179public:
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
154protected:
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
202private: 194 void resizeEvent(QResizeEvent* ev) override {
203 QWidget* event_handler{}; 195 render_window->resize(ev->size());
204}; 196 render_window->OnFramebufferSizeChanged();
197 }
205 198
206class OpenGLWindow final : public ChildRenderWindow { 199 void keyPressEvent(QKeyEvent* event) override {
207public: 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
245private: 248private:
246 QOpenGLContext* context{}; 249 GRenderWindow* render_window;
247}; 250};
248 251
249#ifdef HAS_VULKAN 252class OpenGLRenderWidget : public RenderWidget {
250class VulkanWindow final : public ChildRenderWindow {
251public: 253public:
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
264private: 275private:
265 QWidget* event_handler{}; 276 std::unique_ptr<Core::Frontend::GraphicsContext> context{};
266}; 277};
267#endif
268 278
269GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) 279class VulkanRenderWidget : public RenderWidget {
280public:
281 explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance)
282 : RenderWidget(parent) {
283 windowHandle()->setSurfaceType(QWindow::VulkanSurface);
284 windowHandle()->setVulkanInstance(instance);
285 }
286};
287
288GRenderWindow::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
285GRenderWindow::~GRenderWindow() { 303GRenderWindow::~GRenderWindow() {
286 InputCommon::Shutdown(); 304 InputCommon::Shutdown();
287} 305}
288 306
289void GRenderWindow::MakeCurrent() {
290 if (core_context) {
291 core_context->MakeCurrent();
292 }
293}
294
295void GRenderWindow::DoneCurrent() {
296 if (core_context) {
297 core_context->DoneCurrent();
298 }
299}
300
301void GRenderWindow::PollEvents() { 307void 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
312void 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
475std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { 466std::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
482bool GRenderWindow::InitRenderTarget() { 477bool 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() {
523void GRenderWindow::ReleaseRenderTarget() { 515void 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
531void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { 524void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
@@ -557,24 +550,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
557bool GRenderWindow::InitializeOpenGL() { 550bool 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
602void 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
623bool GRenderWindow::LoadOpenGL() { 617bool 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
20class GRenderWindow; 19class GRenderWindow;
20class GMainWindow;
21class QKeyEvent; 21class QKeyEvent;
22class QScreen;
23class QTouchEvent; 22class QTouchEvent;
24class QStringList; 23class QStringList;
25class QSurface;
26class QOpenGLContext;
27#ifdef HAS_VULKAN 24#ifdef HAS_VULKAN
28class QVulkanInstance; 25class QVulkanInstance;
29#endif 26#endif
@@ -36,7 +33,7 @@ class EmuThread final : public QThread {
36 Q_OBJECT 33 Q_OBJECT
37 34
38public: 35public:
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
99signals: 90signals:
100 /** 91 /**
@@ -124,12 +115,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
124 Q_OBJECT 115 Q_OBJECT
125 116
126public: 117public:
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
168public slots: 159public 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
178private: 169private:
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
151void EmuWindow_SDL2_GL::MakeCurrent() {
152 core_context->MakeCurrent();
153}
154
155void EmuWindow_SDL2_GL::DoneCurrent() {
156 core_context->DoneCurrent();
157}
158
159void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, 151void 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
114void EmuWindow_SDL2_VK::MakeCurrent() {
115 // Unused on Vulkan
116}
117
118void EmuWindow_SDL2_VK::DoneCurrent() {
119 // Unused on Vulkan
120}
121
122void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, 114void 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()) {