From 81e086b5ac490f41498030ee3127c086116b4af2 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Mon, 7 Jan 2019 21:42:32 -0500
Subject: core: Set is_powered_on before GPU is initialized.

---
 src/core/core.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 6dda20faa7..11094a87a5 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -129,10 +129,12 @@ struct System::Impl {
             return ResultStatus::ErrorVideoCore;
         }
 
+        is_powered_on = true;
+
         gpu_core = std::make_unique<Tegra::GPU>(system, renderer->Rasterizer());
 
         cpu_core_manager.Initialize(system);
-        is_powered_on = true;
+
         LOG_DEBUG(Core, "Initialized OK");
 
         // Reset counters and set time origin to current frame
-- 
cgit v1.2.3-70-g09d2


From d6015ee211b04ac948a30abbaf2f623e1d903945 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Mon, 7 Jan 2019 21:46:33 -0500
Subject: settings: Add new graphics setting for
 use_asynchronous_gpu_emulation.

---
 src/core/settings.h                           | 1 +
 src/core/telemetry_session.cpp                | 2 ++
 src/yuzu/configuration/config.cpp             | 4 ++++
 src/yuzu/configuration/configure_graphics.cpp | 4 ++++
 src/yuzu/configuration/configure_graphics.ui  | 7 +++++++
 src/yuzu_cmd/config.cpp                       | 2 ++
 src/yuzu_cmd/default_ini.h                    | 4 ++++
 7 files changed, 24 insertions(+)

(limited to 'src')

diff --git a/src/core/settings.h b/src/core/settings.h
index 7e76e0466f..cdfb2f742d 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -393,6 +393,7 @@ struct Values {
     u16 frame_limit;
     bool use_disk_shader_cache;
     bool use_accurate_gpu_emulation;
+    bool use_asynchronous_gpu_emulation;
 
     float bg_red;
     float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 58dfcc4dfc..e1db068115 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -162,6 +162,8 @@ TelemetrySession::TelemetrySession() {
              Settings::values.use_disk_shader_cache);
     AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
              Settings::values.use_accurate_gpu_emulation);
+    AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAsynchronousGpuEmulation",
+             Settings::values.use_asynchronous_gpu_emulation);
     AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
              Settings::values.use_docked_mode);
 }
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index e9546dadf3..74dc6bb286 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -374,6 +374,8 @@ void Config::ReadValues() {
         qt_config->value("use_disk_shader_cache", false).toBool();
     Settings::values.use_accurate_gpu_emulation =
         qt_config->value("use_accurate_gpu_emulation", false).toBool();
+    Settings::values.use_asynchronous_gpu_emulation =
+        qt_config->value("use_asynchronous_gpu_emulation", false).toBool();
 
     Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
     Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
@@ -633,6 +635,8 @@ void Config::SaveValues() {
     qt_config->setValue("frame_limit", Settings::values.frame_limit);
     qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache);
     qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
+    qt_config->setValue("use_asynchronous_gpu_emulation",
+                        Settings::values.use_asynchronous_gpu_emulation);
 
     // Cast to double because Qt's written float values are not human-readable
     qt_config->setValue("bg_red", (double)Settings::values.bg_red);
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 0f5dd534b1..dd1d674882 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -75,6 +75,8 @@ void ConfigureGraphics::setConfiguration() {
     ui->frame_limit->setValue(Settings::values.frame_limit);
     ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
     ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
+    ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn());
+    ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
     UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
                                                  Settings::values.bg_blue));
 }
@@ -86,6 +88,8 @@ void ConfigureGraphics::applyConfiguration() {
     Settings::values.frame_limit = ui->frame_limit->value();
     Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
     Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
+    Settings::values.use_asynchronous_gpu_emulation =
+        ui->use_asynchronous_gpu_emulation->isChecked();
     Settings::values.bg_red = static_cast<float>(bg_color.redF());
     Settings::values.bg_green = static_cast<float>(bg_color.greenF());
     Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 824f5810a1..c6767e0cac 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -63,6 +63,13 @@
           </property>
          </widget>
         </item>
+        <item>
+         <widget class="QCheckBox" name="use_asynchronous_gpu_emulation">
+          <property name="text">
+           <string>Use asynchronous GPU emulation</string>
+          </property>
+         </widget>
+        </item>
         <item>
          <layout class="QHBoxLayout" name="horizontalLayout">
           <item>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index ff05b31791..ca880dc658 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -354,6 +354,8 @@ void Config::ReadValues() {
         sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false);
     Settings::values.use_accurate_gpu_emulation =
         sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
+    Settings::values.use_asynchronous_gpu_emulation =
+        sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false);
 
     Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
     Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index a81986f8e6..6538af0985 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -118,6 +118,10 @@ use_disk_shader_cache =
 # 0 (default): Off (fast), 1 : On (slow)
 use_accurate_gpu_emulation =
 
+# Whether to use asynchronous GPU emulation
+# 0 : Off (slow), 1 (default): On (fast)
+use_asynchronous_gpu_emulation =
+
 # The clear color for the renderer. What shows up on the sides of the bottom screen.
 # Must be in range of 0.0-1.0. Defaults to 1.0 for all.
 bg_red =
-- 
cgit v1.2.3-70-g09d2


From 4483089d704cd4913a748d2198359cc0cf7b32c5 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Mon, 7 Jan 2019 23:32:02 -0500
Subject: gpu: Refactor to take RendererBase instead of RasterizerInterface.

---
 src/core/core.cpp      |  2 +-
 src/video_core/gpu.cpp |  5 +++--
 src/video_core/gpu.h   | 34 +++++++++++++++++++---------------
 3 files changed, 23 insertions(+), 18 deletions(-)

(limited to 'src')

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 11094a87a5..9e5d167c34 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -131,7 +131,7 @@ struct System::Impl {
 
         is_powered_on = true;
 
-        gpu_core = std::make_unique<Tegra::GPU>(system, renderer->Rasterizer());
+        gpu_core = std::make_unique<Tegra::GPU>(system, *renderer);
 
         cpu_core_manager.Initialize(system);
 
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index ac30d1a89c..08abf8ac9d 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -12,7 +12,7 @@
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/engines/maxwell_dma.h"
 #include "video_core/gpu.h"
-#include "video_core/rasterizer_interface.h"
+#include "video_core/renderer_base.h"
 
 namespace Tegra {
 
@@ -28,7 +28,8 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
     UNREACHABLE();
 }
 
-GPU::GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer) {
+GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} {
+    auto& rasterizer{renderer.Rasterizer()};
     memory_manager = std::make_unique<Tegra::MemoryManager>();
     dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
     maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 6313702f27..ac7aec6a43 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -16,8 +16,8 @@ class System;
 }
 
 namespace VideoCore {
-class RasterizerInterface;
-}
+class RendererBase;
+} // namespace VideoCore
 
 namespace Tegra {
 
@@ -121,7 +121,8 @@ enum class EngineID {
 
 class GPU final {
 public:
-    explicit GPU(Core::System& system, VideoCore::RasterizerInterface& rasterizer);
+    explicit GPU(Core::System& system, VideoCore::RendererBase& renderer);
+
     ~GPU();
 
     struct MethodCall {
@@ -200,9 +201,24 @@ public:
         };
     } regs{};
 
+private:
+    void ProcessBindMethod(const MethodCall& method_call);
+    void ProcessSemaphoreTriggerMethod();
+    void ProcessSemaphoreRelease();
+    void ProcessSemaphoreAcquire();
+
+    // Calls a GPU puller method.
+    void CallPullerMethod(const MethodCall& method_call);
+    // Calls a GPU engine method.
+    void CallEngineMethod(const MethodCall& method_call);
+    // Determines where the method should be executed.
+    bool ExecuteMethodOnEngine(const MethodCall& method_call);
+
 private:
     std::unique_ptr<Tegra::DmaPusher> dma_pusher;
     std::unique_ptr<Tegra::MemoryManager> memory_manager;
+    
+    VideoCore::RendererBase& renderer;
 
     /// Mapping of command subchannels to their bound engine ids.
     std::array<EngineID, 8> bound_engines = {};
@@ -217,18 +233,6 @@ private:
     std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
     /// Inline memory engine
     std::unique_ptr<Engines::KeplerMemory> kepler_memory;
-
-    void ProcessBindMethod(const MethodCall& method_call);
-    void ProcessSemaphoreTriggerMethod();
-    void ProcessSemaphoreRelease();
-    void ProcessSemaphoreAcquire();
-
-    // Calls a GPU puller method.
-    void CallPullerMethod(const MethodCall& method_call);
-    // Calls a GPU engine method.
-    void CallEngineMethod(const MethodCall& method_call);
-    // Determines where the method should be executed.
-    bool ExecuteMethodOnEngine(const MethodCall& method_call);
 };
 
 #define ASSERT_REG_POSITION(field_name, position)                                                  \
-- 
cgit v1.2.3-70-g09d2


From ac51d048a91593a3da124aeea32dc5b0898f1dd6 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Mon, 21 Jan 2019 15:18:09 -0500
Subject: gpu: Refactor command and swap buffers interface for asynch.

---
 src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp |  2 +-
 src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp   | 14 ++------------
 src/core/hle/service/nvflinger/nvflinger.cpp        |  2 +-
 src/video_core/gpu.cpp                              | 10 ++++++++++
 src/video_core/gpu.h                                | 15 ++++++++++++---
 5 files changed, 26 insertions(+), 17 deletions(-)

(limited to 'src')

diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index dbe7ee6e8c..20c7c39aaf 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -36,7 +36,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
 
     auto& instance = Core::System::GetInstance();
     instance.GetPerfStats().EndGameFrame();
-    instance.Renderer().SwapBuffers(framebuffer);
+    instance.GPU().SwapBuffers(framebuffer);
 }
 
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 0a650f36c6..8ce7bc7a5e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -136,16 +136,6 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
     return 0;
 }
 
-static void PushGPUEntries(Tegra::CommandList&& entries) {
-    if (entries.empty()) {
-        return;
-    }
-
-    auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()};
-    dma_pusher.Push(std::move(entries));
-    dma_pusher.DispatchCalls();
-}
-
 u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
     if (input.size() < sizeof(IoctlSubmitGpfifo)) {
         UNIMPLEMENTED();
@@ -163,7 +153,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
     std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
                 params.num_entries * sizeof(Tegra::CommandListHeader));
 
-    PushGPUEntries(std::move(entries));
+    Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
 
     params.fence_out.id = 0;
     params.fence_out.value = 0;
@@ -184,7 +174,7 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
     Memory::ReadBlock(params.address, entries.data(),
                       params.num_entries * sizeof(Tegra::CommandListHeader));
 
-    PushGPUEntries(std::move(entries));
+    Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
 
     params.fence_out.id = 0;
     params.fence_out.value = 0;
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 56f31e2ac4..fc496b6540 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -186,7 +186,7 @@ void NVFlinger::Compose() {
 
             // There was no queued buffer to draw, render previous frame
             system_instance.GetPerfStats().EndGameFrame();
-            system_instance.Renderer().SwapBuffers({});
+            system_instance.GPU().SwapBuffers({});
             continue;
         }
 
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 08abf8ac9d..b0f3310e5f 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -65,6 +65,16 @@ const DmaPusher& GPU::DmaPusher() const {
     return *dma_pusher;
 }
 
+void GPU::PushGPUEntries(Tegra::CommandList&& entries) {
+    dma_pusher->Push(std::move(entries));
+    dma_pusher->DispatchCalls();
+}
+
+void GPU::SwapBuffers(
+    std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
+    renderer.SwapBuffers(std::move(framebuffer));
+}
+
 u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
     ASSERT(format != RenderTargetFormat::NONE);
 
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index ac7aec6a43..62649bd6ed 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -200,6 +200,13 @@ public:
             std::array<u32, NUM_REGS> reg_array;
         };
     } regs{};
+    
+    /// Push GPU command entries to be processed
+    void PushGPUEntries(Tegra::CommandList&& entries);
+
+    /// Swap buffers (render frame)
+    void SwapBuffers(
+        std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);
 
 private:
     void ProcessBindMethod(const MethodCall& method_call);
@@ -207,11 +214,13 @@ private:
     void ProcessSemaphoreRelease();
     void ProcessSemaphoreAcquire();
 
-    // Calls a GPU puller method.
+    /// Calls a GPU puller method.
     void CallPullerMethod(const MethodCall& method_call);
-    // Calls a GPU engine method.
+    
+    /// Calls a GPU engine method.
     void CallEngineMethod(const MethodCall& method_call);
-    // Determines where the method should be executed.
+    
+    /// Determines where the method should be executed.
     bool ExecuteMethodOnEngine(const MethodCall& method_call);
 
 private:
-- 
cgit v1.2.3-70-g09d2


From 65651078e5ae60ff14bf6d6e8e367ec7262c4120 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Wed, 23 Jan 2019 22:09:22 -0500
Subject: bootmanager: Ensure that we have a context for shader loading.

---
 src/yuzu/bootmanager.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 73b04b7492..3b070bfbb8 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -20,10 +20,7 @@
 EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
 
 void EmuThread::run() {
-    if (!Settings::values.use_multi_core) {
-        // Single core mode must acquire OpenGL context for entire emulation session
-        render_window->MakeCurrent();
-    }
+    render_window->MakeCurrent();
 
     MicroProfileOnThreadCreate("EmuThread");
 
@@ -38,6 +35,11 @@ void EmuThread::run() {
 
     emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
 
+    if (Settings::values.use_asynchronous_gpu_emulation) {
+        // Release OpenGL context for the GPU thread
+        render_window->DoneCurrent();
+    }
+
     // holds whether the cpu was running during the last iteration,
     // so that the DebugModeLeft signal can be emitted before the
     // next execution step
-- 
cgit v1.2.3-70-g09d2


From 7b574f406b25c02a0e0efd8b7ec13d68ecb55497 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Wed, 23 Jan 2019 22:17:55 -0500
Subject: gpu: Move command processing to another thread.

---
 .../hle/service/nvdrv/devices/nvhost_as_gpu.cpp    |   2 +-
 src/core/memory.cpp                                |   8 +-
 src/video_core/CMakeLists.txt                      |   2 +
 src/video_core/engines/kepler_memory.cpp           |   2 +-
 src/video_core/engines/maxwell_dma.cpp             |   4 +-
 src/video_core/gpu.cpp                             |  44 +++++-
 src/video_core/gpu.h                               |  22 ++-
 src/video_core/gpu_thread.cpp                      | 154 +++++++++++++++++++++
 src/video_core/gpu_thread.h                        | 135 ++++++++++++++++++
 9 files changed, 358 insertions(+), 15 deletions(-)
 create mode 100644 src/video_core/gpu_thread.cpp
 create mode 100644 src/video_core/gpu_thread.h

(limited to 'src')

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 466db7ccd3..a34b9e753f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -178,7 +178,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
     auto& gpu = system_instance.GPU();
     auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
     ASSERT(cpu_addr);
-    system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
+    gpu.FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
 
     params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
 
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index ec279cef83..6591c45d27 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -356,16 +356,16 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
         const VAddr overlap_end = std::min(end, region_end);
         const VAddr overlap_size = overlap_end - overlap_start;
 
-        auto& rasterizer = system_instance.Renderer().Rasterizer();
+        auto& gpu = system_instance.GPU();
         switch (mode) {
         case FlushMode::Flush:
-            rasterizer.FlushRegion(overlap_start, overlap_size);
+            gpu.FlushRegion(overlap_start, overlap_size);
             break;
         case FlushMode::Invalidate:
-            rasterizer.InvalidateRegion(overlap_start, overlap_size);
+            gpu.InvalidateRegion(overlap_start, overlap_size);
             break;
         case FlushMode::FlushAndInvalidate:
-            rasterizer.FlushAndInvalidateRegion(overlap_start, overlap_size);
+            gpu.FlushAndInvalidateRegion(overlap_start, overlap_size);
             break;
         }
     };
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3e9d2b3be7..3bb5d0ed7a 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -17,6 +17,8 @@ add_library(video_core STATIC
     engines/shader_header.h
     gpu.cpp
     gpu.h
+    gpu_thread.cpp
+    gpu_thread.h
     macro_interpreter.cpp
     macro_interpreter.h
     memory_manager.cpp
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 4f6126116e..aae2a40199 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -48,7 +48,7 @@ void KeplerMemory::ProcessData(u32 data) {
     // We have to invalidate the destination region to evict any outdated surfaces from the cache.
     // We do this before actually writing the new data because the destination address might contain
     // a dirty surface that will have to be written back to memory.
-    rasterizer.InvalidateRegion(*dest_address, sizeof(u32));
+    Core::System::GetInstance().GPU().InvalidateRegion(*dest_address, sizeof(u32));
 
     Memory::Write32(*dest_address, data);
     system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 0474c7ba3f..9dfea59990 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -92,12 +92,12 @@ void MaxwellDMA::HandleCopy() {
     const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
         // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
         // copying.
-        rasterizer.FlushRegion(*source_cpu, src_size);
+        Core::System::GetInstance().GPU().FlushRegion(*source_cpu, src_size);
 
         // We have to invalidate the destination region to evict any outdated surfaces from the
         // cache. We do this before actually writing the new data because the destination address
         // might contain a dirty surface that will have to be written back to memory.
-        rasterizer.InvalidateRegion(*dest_cpu, dst_size);
+        Core::System::GetInstance().GPU().InvalidateRegion(*dest_cpu, dst_size);
     };
 
     if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index b0f3310e5f..0d7a052ddd 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -6,12 +6,14 @@
 #include "core/core.h"
 #include "core/core_timing.h"
 #include "core/memory.h"
+#include "core/settings.h"
 #include "video_core/engines/fermi_2d.h"
 #include "video_core/engines/kepler_compute.h"
 #include "video_core/engines/kepler_memory.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/engines/maxwell_dma.h"
 #include "video_core/gpu.h"
+#include "video_core/gpu_thread.h"
 #include "video_core/renderer_base.h"
 
 namespace Tegra {
@@ -37,6 +39,10 @@ GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{ren
     kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager);
     maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, rasterizer, *memory_manager);
     kepler_memory = std::make_unique<Engines::KeplerMemory>(system, rasterizer, *memory_manager);
+
+    if (Settings::values.use_asynchronous_gpu_emulation) {
+        gpu_thread = std::make_unique<VideoCommon::GPUThread::ThreadManager>(renderer, *dma_pusher);
+    }
 }
 
 GPU::~GPU() = default;
@@ -66,13 +72,45 @@ const DmaPusher& GPU::DmaPusher() const {
 }
 
 void GPU::PushGPUEntries(Tegra::CommandList&& entries) {
-    dma_pusher->Push(std::move(entries));
-    dma_pusher->DispatchCalls();
+    if (Settings::values.use_asynchronous_gpu_emulation) {
+        gpu_thread->SubmitList(std::move(entries));
+    } else {
+        dma_pusher->Push(std::move(entries));
+        dma_pusher->DispatchCalls();
+    }
 }
 
 void GPU::SwapBuffers(
     std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
-    renderer.SwapBuffers(std::move(framebuffer));
+    if (Settings::values.use_asynchronous_gpu_emulation) {
+        gpu_thread->SwapBuffers(std::move(framebuffer));
+    } else {
+        renderer.SwapBuffers(std::move(framebuffer));
+    }
+}
+
+void GPU::FlushRegion(VAddr addr, u64 size) {
+    if (Settings::values.use_asynchronous_gpu_emulation) {
+        gpu_thread->FlushRegion(addr, size);
+    } else {
+        renderer.Rasterizer().FlushRegion(addr, size);
+    }
+}
+
+void GPU::InvalidateRegion(VAddr addr, u64 size) {
+    if (Settings::values.use_asynchronous_gpu_emulation) {
+        gpu_thread->InvalidateRegion(addr, size);
+    } else {
+        renderer.Rasterizer().InvalidateRegion(addr, size);
+    }
+}
+
+void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) {
+    if (Settings::values.use_asynchronous_gpu_emulation) {
+        gpu_thread->FlushAndInvalidateRegion(addr, size);
+    } else {
+        renderer.Rasterizer().FlushAndInvalidateRegion(addr, size);
+    }
 }
 
 u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 62649bd6ed..3f3098bf16 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -19,6 +19,10 @@ namespace VideoCore {
 class RendererBase;
 } // namespace VideoCore
 
+namespace VideoCommon::GPUThread {
+class ThreadManager;
+} // namespace VideoCommon::GPUThread
+
 namespace Tegra {
 
 enum class RenderTargetFormat : u32 {
@@ -200,7 +204,7 @@ public:
             std::array<u32, NUM_REGS> reg_array;
         };
     } regs{};
-    
+
     /// Push GPU command entries to be processed
     void PushGPUEntries(Tegra::CommandList&& entries);
 
@@ -208,6 +212,15 @@ public:
     void SwapBuffers(
         std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);
 
+    /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
+    void FlushRegion(VAddr addr, u64 size);
+
+    /// Notify rasterizer that any caches of the specified region should be invalidated
+    void InvalidateRegion(VAddr addr, u64 size);
+
+    /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
+    void FlushAndInvalidateRegion(VAddr addr, u64 size);
+
 private:
     void ProcessBindMethod(const MethodCall& method_call);
     void ProcessSemaphoreTriggerMethod();
@@ -216,17 +229,18 @@ private:
 
     /// Calls a GPU puller method.
     void CallPullerMethod(const MethodCall& method_call);
-    
+
     /// Calls a GPU engine method.
     void CallEngineMethod(const MethodCall& method_call);
-    
+
     /// Determines where the method should be executed.
     bool ExecuteMethodOnEngine(const MethodCall& method_call);
 
 private:
     std::unique_ptr<Tegra::DmaPusher> dma_pusher;
     std::unique_ptr<Tegra::MemoryManager> memory_manager;
-    
+    std::unique_ptr<VideoCommon::GPUThread::ThreadManager> gpu_thread;
+
     VideoCore::RendererBase& renderer;
 
     /// Mapping of command subchannels to their bound engine ids.
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
new file mode 100644
index 0000000000..22c4cca4df
--- /dev/null
+++ b/src/video_core/gpu_thread.cpp
@@ -0,0 +1,154 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/microprofile.h"
+#include "core/frontend/scope_acquire_window_context.h"
+#include "core/settings.h"
+#include "video_core/dma_pusher.h"
+#include "video_core/gpu.h"
+#include "video_core/gpu_thread.h"
+#include "video_core/renderer_base.h"
+
+namespace VideoCommon::GPUThread {
+
+/// Executes a single GPU thread command
+static void ExecuteCommand(CommandData* command, VideoCore::RendererBase& renderer,
+                           Tegra::DmaPusher& dma_pusher) {
+    if (const auto submit_list = std::get_if<SubmitListCommand>(command)) {
+        dma_pusher.Push(std::move(submit_list->entries));
+        dma_pusher.DispatchCalls();
+    } else if (const auto data = std::get_if<SwapBuffersCommand>(command)) {
+        renderer.SwapBuffers(data->framebuffer);
+    } else if (const auto data = std::get_if<FlushRegionCommand>(command)) {
+        renderer.Rasterizer().FlushRegion(data->addr, data->size);
+    } else if (const auto data = std::get_if<InvalidateRegionCommand>(command)) {
+        renderer.Rasterizer().InvalidateRegion(data->addr, data->size);
+    } else if (const auto data = std::get_if<FlushAndInvalidateRegionCommand>(command)) {
+        renderer.Rasterizer().FlushAndInvalidateRegion(data->addr, data->size);
+    } else {
+        UNREACHABLE();
+    }
+}
+
+/// Runs the GPU thread
+static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher,
+                      SynchState& state) {
+
+    MicroProfileOnThreadCreate("GpuThread");
+
+    auto WaitForWakeup = [&]() {
+        std::unique_lock<std::mutex> lock{state.signal_mutex};
+        state.signal_condition.wait(lock, [&] { return !state.IsIdle() || !state.is_running; });
+    };
+
+    // Wait for first GPU command before acquiring the window context
+    WaitForWakeup();
+
+    // If emulation was stopped during disk shader loading, abort before trying to acquire context
+    if (!state.is_running) {
+        return;
+    }
+
+    Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()};
+
+    while (state.is_running) {
+        if (!state.is_running) {
+            return;
+        }
+
+        {
+            // Thread has been woken up, so make the previous write queue the next read queue
+            std::lock_guard<std::mutex> lock{state.signal_mutex};
+            std::swap(state.push_queue, state.pop_queue);
+        }
+
+        // Execute all of the GPU commands
+        while (!state.pop_queue->empty()) {
+            ExecuteCommand(&state.pop_queue->front(), renderer, dma_pusher);
+            state.pop_queue->pop();
+        }
+
+        // Signal that the GPU thread has finished processing commands
+        if (state.IsIdle()) {
+            state.idle_condition.notify_one();
+        }
+
+        // Wait for CPU thread to send more GPU commands
+        WaitForWakeup();
+    }
+}
+
+ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher)
+    : renderer{renderer}, dma_pusher{dma_pusher}, thread{RunThread, std::ref(renderer),
+                                                         std::ref(dma_pusher), std::ref(state)},
+      thread_id{thread.get_id()} {}
+
+ThreadManager::~ThreadManager() {
+    {
+        // Notify GPU thread that a shutdown is pending
+        std::lock_guard<std::mutex> lock{state.signal_mutex};
+        state.is_running = false;
+    }
+
+    state.signal_condition.notify_one();
+    thread.join();
+}
+
+void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
+    if (entries.empty()) {
+        return;
+    }
+
+    PushCommand(SubmitListCommand(std::move(entries)), false, false);
+}
+
+void ThreadManager::SwapBuffers(
+    std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
+    PushCommand(SwapBuffersCommand(std::move(framebuffer)), true, false);
+}
+
+void ThreadManager::FlushRegion(VAddr addr, u64 size) {
+    if (Settings::values.use_accurate_gpu_emulation) {
+        PushCommand(FlushRegionCommand(addr, size), true, false);
+    }
+}
+
+void ThreadManager::InvalidateRegion(VAddr addr, u64 size) {
+    PushCommand(InvalidateRegionCommand(addr, size), true, true);
+}
+
+void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
+    if (Settings::values.use_accurate_gpu_emulation) {
+        PushCommand(FlushAndInvalidateRegionCommand(addr, size), true, false);
+    } else {
+        InvalidateRegion(addr, size);
+    }
+}
+
+void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu) {
+    {
+        std::lock_guard<std::mutex> lock{state.signal_mutex};
+
+        if ((allow_on_cpu && state.IsIdle()) || IsGpuThread()) {
+            // Execute the command synchronously on the current thread
+            ExecuteCommand(&command_data, renderer, dma_pusher);
+            return;
+        }
+
+        // Push the command to the GPU thread
+        state.push_queue->emplace(command_data);
+    }
+
+    // Signal the GPU thread that commands are pending
+    state.signal_condition.notify_one();
+
+    if (wait_for_idle) {
+        // Wait for the GPU to be idle (all commands to be executed)
+        std::unique_lock<std::mutex> lock{state.idle_mutex};
+        state.idle_condition.wait(lock, [this] { return state.IsIdle(); });
+    }
+}
+
+} // namespace VideoCommon::GPUThread
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
new file mode 100644
index 0000000000..ad9f9462b9
--- /dev/null
+++ b/src/video_core/gpu_thread.h
@@ -0,0 +1,135 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <atomic>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <variant>
+
+namespace Tegra {
+struct FramebufferConfig;
+class DmaPusher;
+} // namespace Tegra
+
+namespace VideoCore {
+class RendererBase;
+} // namespace VideoCore
+
+namespace VideoCommon::GPUThread {
+
+/// Command to signal to the GPU thread that a command list is ready for processing
+struct SubmitListCommand final {
+    explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {}
+
+    Tegra::CommandList entries;
+};
+
+/// Command to signal to the GPU thread that a swap buffers is pending
+struct SwapBuffersCommand final {
+    explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer)
+        : framebuffer{std::move(framebuffer)} {}
+
+    std::optional<const Tegra::FramebufferConfig> framebuffer;
+};
+
+/// Command to signal to the GPU thread to flush a region
+struct FlushRegionCommand final {
+    explicit constexpr FlushRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {}
+
+    const VAddr addr;
+    const u64 size;
+};
+
+/// Command to signal to the GPU thread to invalidate a region
+struct InvalidateRegionCommand final {
+    explicit constexpr InvalidateRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {}
+
+    const VAddr addr;
+    const u64 size;
+};
+
+/// Command to signal to the GPU thread to flush and invalidate a region
+struct FlushAndInvalidateRegionCommand final {
+    explicit constexpr FlushAndInvalidateRegionCommand(VAddr addr, u64 size)
+        : addr{addr}, size{size} {}
+
+    const VAddr addr;
+    const u64 size;
+};
+
+using CommandData = std::variant<SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
+                                 InvalidateRegionCommand, FlushAndInvalidateRegionCommand>;
+
+/// Struct used to synchronize the GPU thread
+struct SynchState final {
+    std::atomic<bool> is_running{true};
+    std::condition_variable signal_condition;
+    std::mutex signal_mutex;
+    std::condition_variable idle_condition;
+    std::mutex idle_mutex;
+
+    // We use two queues for sending commands to the GPU thread, one for writing (push_queue) to and
+    // one for reading from (pop_queue). These are swapped whenever the current pop_queue becomes
+    // empty. This allows for efficient thread-safe access, as it does not require any copies.
+
+    using CommandQueue = std::queue<CommandData>;
+    std::array<CommandQueue, 2> command_queues;
+    CommandQueue* push_queue{&command_queues[0]};
+    CommandQueue* pop_queue{&command_queues[1]};
+
+    /// Returns true if the GPU thread should be idle, meaning there are no commands to process
+    bool IsIdle() const {
+        return command_queues[0].empty() && command_queues[1].empty();
+    }
+};
+
+/// Class used to manage the GPU thread
+class ThreadManager final {
+public:
+    explicit ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
+    ~ThreadManager();
+
+    /// Push GPU command entries to be processed
+    void SubmitList(Tegra::CommandList&& entries);
+
+    /// Swap buffers (render frame)
+    void SwapBuffers(
+        std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);
+
+    /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
+    void FlushRegion(VAddr addr, u64 size);
+
+    /// Notify rasterizer that any caches of the specified region should be invalidated
+    void InvalidateRegion(VAddr addr, u64 size);
+
+    /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
+    void FlushAndInvalidateRegion(VAddr addr, u64 size);
+
+    /// Waits the caller until the GPU thread is idle, used for synchronization
+    void WaitForIdle();
+
+private:
+    /// Pushes a command to be executed by the GPU thread
+    void PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu);
+
+    /// Returns true if this is called by the GPU thread
+    bool IsGpuThread() const {
+        return std::this_thread::get_id() == thread_id;
+    }
+
+private:
+    SynchState state;
+    std::thread thread;
+    std::thread::id thread_id;
+    VideoCore::RendererBase& renderer;
+    Tegra::DmaPusher& dma_pusher;
+};
+
+} // namespace VideoCommon::GPUThread
-- 
cgit v1.2.3-70-g09d2


From aaa373585cd55bd03fcc589d2ad9f749e2cb99d4 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 8 Feb 2019 23:21:53 -0500
Subject: gpu: Refactor a/synchronous implementations into their own classes.

---
 src/core/core.cpp             |  9 ++++++--
 src/video_core/CMakeLists.txt |  4 ++++
 src/video_core/gpu.cpp        | 48 -------------------------------------------
 src/video_core/gpu.h          | 26 ++++++++++-------------
 src/video_core/gpu_asynch.cpp | 37 +++++++++++++++++++++++++++++++++
 src/video_core/gpu_asynch.h   | 37 +++++++++++++++++++++++++++++++++
 src/video_core/gpu_synch.cpp  | 37 +++++++++++++++++++++++++++++++++
 src/video_core/gpu_synch.h    | 29 ++++++++++++++++++++++++++
 8 files changed, 162 insertions(+), 65 deletions(-)
 create mode 100644 src/video_core/gpu_asynch.cpp
 create mode 100644 src/video_core/gpu_asynch.h
 create mode 100644 src/video_core/gpu_synch.cpp
 create mode 100644 src/video_core/gpu_synch.h

(limited to 'src')

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9e5d167c34..1d83e9e118 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -36,7 +36,8 @@
 #include "frontend/applets/software_keyboard.h"
 #include "frontend/applets/web_browser.h"
 #include "video_core/debug_utils/debug_utils.h"
-#include "video_core/gpu.h"
+#include "video_core/gpu_asynch.h"
+#include "video_core/gpu_synch.h"
 #include "video_core/renderer_base.h"
 #include "video_core/video_core.h"
 
@@ -131,7 +132,11 @@ struct System::Impl {
 
         is_powered_on = true;
 
-        gpu_core = std::make_unique<Tegra::GPU>(system, *renderer);
+        if (Settings::values.use_asynchronous_gpu_emulation) {
+            gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
+        } else {
+            gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
+        }
 
         cpu_core_manager.Initialize(system);
 
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3bb5d0ed7a..a4cb33c171 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -17,6 +17,10 @@ add_library(video_core STATIC
     engines/shader_header.h
     gpu.cpp
     gpu.h
+    gpu_asynch.cpp
+    gpu_asynch.h
+    gpu_synch.cpp
+    gpu_synch.h
     gpu_thread.cpp
     gpu_thread.h
     macro_interpreter.cpp
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 0d7a052ddd..08abf8ac9d 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -6,14 +6,12 @@
 #include "core/core.h"
 #include "core/core_timing.h"
 #include "core/memory.h"
-#include "core/settings.h"
 #include "video_core/engines/fermi_2d.h"
 #include "video_core/engines/kepler_compute.h"
 #include "video_core/engines/kepler_memory.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/engines/maxwell_dma.h"
 #include "video_core/gpu.h"
-#include "video_core/gpu_thread.h"
 #include "video_core/renderer_base.h"
 
 namespace Tegra {
@@ -39,10 +37,6 @@ GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{ren
     kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager);
     maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, rasterizer, *memory_manager);
     kepler_memory = std::make_unique<Engines::KeplerMemory>(system, rasterizer, *memory_manager);
-
-    if (Settings::values.use_asynchronous_gpu_emulation) {
-        gpu_thread = std::make_unique<VideoCommon::GPUThread::ThreadManager>(renderer, *dma_pusher);
-    }
 }
 
 GPU::~GPU() = default;
@@ -71,48 +65,6 @@ const DmaPusher& GPU::DmaPusher() const {
     return *dma_pusher;
 }
 
-void GPU::PushGPUEntries(Tegra::CommandList&& entries) {
-    if (Settings::values.use_asynchronous_gpu_emulation) {
-        gpu_thread->SubmitList(std::move(entries));
-    } else {
-        dma_pusher->Push(std::move(entries));
-        dma_pusher->DispatchCalls();
-    }
-}
-
-void GPU::SwapBuffers(
-    std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
-    if (Settings::values.use_asynchronous_gpu_emulation) {
-        gpu_thread->SwapBuffers(std::move(framebuffer));
-    } else {
-        renderer.SwapBuffers(std::move(framebuffer));
-    }
-}
-
-void GPU::FlushRegion(VAddr addr, u64 size) {
-    if (Settings::values.use_asynchronous_gpu_emulation) {
-        gpu_thread->FlushRegion(addr, size);
-    } else {
-        renderer.Rasterizer().FlushRegion(addr, size);
-    }
-}
-
-void GPU::InvalidateRegion(VAddr addr, u64 size) {
-    if (Settings::values.use_asynchronous_gpu_emulation) {
-        gpu_thread->InvalidateRegion(addr, size);
-    } else {
-        renderer.Rasterizer().InvalidateRegion(addr, size);
-    }
-}
-
-void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) {
-    if (Settings::values.use_asynchronous_gpu_emulation) {
-        gpu_thread->FlushAndInvalidateRegion(addr, size);
-    } else {
-        renderer.Rasterizer().FlushAndInvalidateRegion(addr, size);
-    }
-}
-
 u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
     ASSERT(format != RenderTargetFormat::NONE);
 
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 3f3098bf16..14a421cc1e 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -19,10 +19,6 @@ namespace VideoCore {
 class RendererBase;
 } // namespace VideoCore
 
-namespace VideoCommon::GPUThread {
-class ThreadManager;
-} // namespace VideoCommon::GPUThread
-
 namespace Tegra {
 
 enum class RenderTargetFormat : u32 {
@@ -123,7 +119,7 @@ enum class EngineID {
     MAXWELL_DMA_COPY_A = 0xB0B5,
 };
 
-class GPU final {
+class GPU {
 public:
     explicit GPU(Core::System& system, VideoCore::RendererBase& renderer);
 
@@ -206,20 +202,20 @@ public:
     } regs{};
 
     /// Push GPU command entries to be processed
-    void PushGPUEntries(Tegra::CommandList&& entries);
+    virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
 
     /// Swap buffers (render frame)
-    void SwapBuffers(
-        std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer);
+    virtual void SwapBuffers(
+        std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0;
 
     /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
-    void FlushRegion(VAddr addr, u64 size);
+    virtual void FlushRegion(VAddr addr, u64 size) = 0;
 
     /// Notify rasterizer that any caches of the specified region should be invalidated
-    void InvalidateRegion(VAddr addr, u64 size);
+    virtual void InvalidateRegion(VAddr addr, u64 size) = 0;
 
     /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
-    void FlushAndInvalidateRegion(VAddr addr, u64 size);
+    virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
 
 private:
     void ProcessBindMethod(const MethodCall& method_call);
@@ -236,13 +232,13 @@ private:
     /// Determines where the method should be executed.
     bool ExecuteMethodOnEngine(const MethodCall& method_call);
 
-private:
+protected:
     std::unique_ptr<Tegra::DmaPusher> dma_pusher;
-    std::unique_ptr<Tegra::MemoryManager> memory_manager;
-    std::unique_ptr<VideoCommon::GPUThread::ThreadManager> gpu_thread;
-
     VideoCore::RendererBase& renderer;
 
+private:
+    std::unique_ptr<Tegra::MemoryManager> memory_manager;
+
     /// Mapping of command subchannels to their bound engine ids.
     std::array<EngineID, 8> bound_engines = {};
 
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
new file mode 100644
index 0000000000..ad0a747e3b
--- /dev/null
+++ b/src/video_core/gpu_asynch.cpp
@@ -0,0 +1,37 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "video_core/gpu_asynch.h"
+#include "video_core/gpu_thread.h"
+#include "video_core/renderer_base.h"
+
+namespace VideoCommon {
+
+GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
+    : Tegra::GPU(system, renderer), gpu_thread{renderer, *dma_pusher} {}
+
+GPUAsynch::~GPUAsynch() = default;
+
+void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
+    gpu_thread.SubmitList(std::move(entries));
+}
+
+void GPUAsynch::SwapBuffers(
+    std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
+    gpu_thread.SwapBuffers(std::move(framebuffer));
+}
+
+void GPUAsynch::FlushRegion(VAddr addr, u64 size) {
+    gpu_thread.FlushRegion(addr, size);
+}
+
+void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) {
+    gpu_thread.InvalidateRegion(addr, size);
+}
+
+void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
+    gpu_thread.FlushAndInvalidateRegion(addr, size);
+}
+
+} // namespace VideoCommon
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
new file mode 100644
index 0000000000..58046f3e96
--- /dev/null
+++ b/src/video_core/gpu_asynch.h
@@ -0,0 +1,37 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "video_core/gpu.h"
+#include "video_core/gpu_thread.h"
+
+namespace VideoCore {
+class RendererBase;
+} // namespace VideoCore
+
+namespace VideoCommon {
+
+namespace GPUThread {
+class ThreadManager;
+} // namespace GPUThread
+
+/// Implementation of GPU interface that runs the GPU asynchronously
+class GPUAsynch : public Tegra::GPU {
+public:
+    explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
+    ~GPUAsynch();
+
+    void PushGPUEntries(Tegra::CommandList&& entries) override;
+    void SwapBuffers(
+        std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
+    void FlushRegion(VAddr addr, u64 size) override;
+    void InvalidateRegion(VAddr addr, u64 size) override;
+    void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
+
+private:
+    GPUThread::ThreadManager gpu_thread;
+};
+
+} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
new file mode 100644
index 0000000000..4c00b96c71
--- /dev/null
+++ b/src/video_core/gpu_synch.cpp
@@ -0,0 +1,37 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "video_core/gpu_synch.h"
+#include "video_core/renderer_base.h"
+
+namespace VideoCommon {
+
+GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
+    : Tegra::GPU(system, renderer) {}
+
+GPUSynch::~GPUSynch() = default;
+
+void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
+    dma_pusher->Push(std::move(entries));
+    dma_pusher->DispatchCalls();
+}
+
+void GPUSynch::SwapBuffers(
+    std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
+    renderer.SwapBuffers(std::move(framebuffer));
+}
+
+void GPUSynch::FlushRegion(VAddr addr, u64 size) {
+    renderer.Rasterizer().FlushRegion(addr, size);
+}
+
+void GPUSynch::InvalidateRegion(VAddr addr, u64 size) {
+    renderer.Rasterizer().InvalidateRegion(addr, size);
+}
+
+void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) {
+    renderer.Rasterizer().FlushAndInvalidateRegion(addr, size);
+}
+
+} // namespace VideoCommon
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
new file mode 100644
index 0000000000..658f683e2b
--- /dev/null
+++ b/src/video_core/gpu_synch.h
@@ -0,0 +1,29 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "video_core/gpu.h"
+
+namespace VideoCore {
+class RendererBase;
+} // namespace VideoCore
+
+namespace VideoCommon {
+
+/// Implementation of GPU interface that runs the GPU synchronously
+class GPUSynch : public Tegra::GPU {
+public:
+    explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
+    ~GPUSynch();
+
+    void PushGPUEntries(Tegra::CommandList&& entries) override;
+    void SwapBuffers(
+        std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
+    void FlushRegion(VAddr addr, u64 size) override;
+    void InvalidateRegion(VAddr addr, u64 size) override;
+    void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
+};
+
+} // namespace VideoCommon
-- 
cgit v1.2.3-70-g09d2


From 3f1b4fb23ad7e689941b5a01afa15780bc50b77b Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 9 Feb 2019 00:06:35 -0500
Subject: gpu: Always flush.

---
 src/video_core/gpu_thread.cpp                    | 13 +++++--------
 src/video_core/renderer_opengl/gl_rasterizer.cpp |  6 +-----
 2 files changed, 6 insertions(+), 13 deletions(-)

(limited to 'src')

diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 22c4cca4df..7640da6c09 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -110,9 +110,8 @@ void ThreadManager::SwapBuffers(
 }
 
 void ThreadManager::FlushRegion(VAddr addr, u64 size) {
-    if (Settings::values.use_accurate_gpu_emulation) {
-        PushCommand(FlushRegionCommand(addr, size), true, false);
-    }
+    // Block the CPU when using accurate emulation
+    PushCommand(FlushRegionCommand(addr, size), Settings::values.use_accurate_gpu_emulation, false);
 }
 
 void ThreadManager::InvalidateRegion(VAddr addr, u64 size) {
@@ -120,11 +119,9 @@ void ThreadManager::InvalidateRegion(VAddr addr, u64 size) {
 }
 
 void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
-    if (Settings::values.use_accurate_gpu_emulation) {
-        PushCommand(FlushAndInvalidateRegionCommand(addr, size), true, false);
-    } else {
-        InvalidateRegion(addr, size);
-    }
+    // Block the CPU when using accurate emulation
+    PushCommand(FlushAndInvalidateRegionCommand(addr, size),
+                Settings::values.use_accurate_gpu_emulation, false);
 }
 
 void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 321d9dd3da..1682880886 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -749,11 +749,7 @@ void RasterizerOpenGL::FlushAll() {}
 
 void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
     MICROPROFILE_SCOPE(OpenGL_CacheManagement);
-
-    if (Settings::values.use_accurate_gpu_emulation) {
-        // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
-        res_cache.FlushRegion(addr, size);
-    }
+    res_cache.FlushRegion(addr, size);
 }
 
 void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
-- 
cgit v1.2.3-70-g09d2


From 63aa08acbe1da5b78d9f0b37088081a24591849f Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 9 Feb 2019 02:55:45 -0500
Subject: gpu_thread: (HACK) Ignore flush on FlushAndInvalidateRegion.

---
 src/video_core/gpu_thread.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

(limited to 'src')

diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 7640da6c09..4c25380ebe 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -119,9 +119,7 @@ void ThreadManager::InvalidateRegion(VAddr addr, u64 size) {
 }
 
 void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
-    // Block the CPU when using accurate emulation
-    PushCommand(FlushAndInvalidateRegionCommand(addr, size),
-                Settings::values.use_accurate_gpu_emulation, false);
+    InvalidateRegion(addr, size);
 }
 
 void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle, bool allow_on_cpu) {
-- 
cgit v1.2.3-70-g09d2


From 84ad81ee6798ece6c66016c4581b5fe57ce7b20e Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 9 Feb 2019 11:37:11 -0500
Subject: gpu_thread: Fix deadlock with threading idle state check.

---
 src/video_core/gpu_thread.cpp | 11 +++++++----
 src/video_core/gpu_thread.h   |  7 ++++---
 2 files changed, 11 insertions(+), 7 deletions(-)

(limited to 'src')

diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 4c25380ebe..c5bdd2a178 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -40,7 +40,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
 
     auto WaitForWakeup = [&]() {
         std::unique_lock<std::mutex> lock{state.signal_mutex};
-        state.signal_condition.wait(lock, [&] { return !state.IsIdle() || !state.is_running; });
+        state.signal_condition.wait(lock, [&] { return !state.is_idle || !state.is_running; });
     };
 
     // Wait for first GPU command before acquiring the window context
@@ -70,8 +70,10 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
             state.pop_queue->pop();
         }
 
+        state.UpdateIdleState();
+
         // Signal that the GPU thread has finished processing commands
-        if (state.IsIdle()) {
+        if (state.is_idle) {
             state.idle_condition.notify_one();
         }
 
@@ -126,13 +128,14 @@ void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle,
     {
         std::lock_guard<std::mutex> lock{state.signal_mutex};
 
-        if ((allow_on_cpu && state.IsIdle()) || IsGpuThread()) {
+        if ((allow_on_cpu && state.is_idle) || IsGpuThread()) {
             // Execute the command synchronously on the current thread
             ExecuteCommand(&command_data, renderer, dma_pusher);
             return;
         }
 
         // Push the command to the GPU thread
+        state.UpdateIdleState();
         state.push_queue->emplace(command_data);
     }
 
@@ -142,7 +145,7 @@ void ThreadManager::PushCommand(CommandData&& command_data, bool wait_for_idle,
     if (wait_for_idle) {
         // Wait for the GPU to be idle (all commands to be executed)
         std::unique_lock<std::mutex> lock{state.idle_mutex};
-        state.idle_condition.wait(lock, [this] { return state.IsIdle(); });
+        state.idle_condition.wait(lock, [this] { return static_cast<bool>(state.is_idle); });
     }
 }
 
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index ad9f9462b9..2ad8214cce 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -70,6 +70,7 @@ using CommandData = std::variant<SubmitListCommand, SwapBuffersCommand, FlushReg
 /// Struct used to synchronize the GPU thread
 struct SynchState final {
     std::atomic<bool> is_running{true};
+    std::atomic<bool> is_idle{true};
     std::condition_variable signal_condition;
     std::mutex signal_mutex;
     std::condition_variable idle_condition;
@@ -84,9 +85,9 @@ struct SynchState final {
     CommandQueue* push_queue{&command_queues[0]};
     CommandQueue* pop_queue{&command_queues[1]};
 
-    /// Returns true if the GPU thread should be idle, meaning there are no commands to process
-    bool IsIdle() const {
-        return command_queues[0].empty() && command_queues[1].empty();
+    void UpdateIdleState() {
+        std::lock_guard<std::mutex> lock{idle_mutex};
+        is_idle = command_queues[0].empty() && command_queues[1].empty();
     }
 };
 
-- 
cgit v1.2.3-70-g09d2