From ceb5f5079c3efd8046ccf35bbc3507a4c190f355 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Tue, 4 Jun 2019 16:10:07 -0400
Subject: [PATCH 01/25] nvflinger: Implement swap intervals

---
 src/core/hle/service/nvflinger/buffer_queue.cpp |  3 ++-
 src/core/hle/service/nvflinger/buffer_queue.h   |  3 ++-
 src/core/hle/service/nvflinger/nvflinger.cpp    | 14 ++++++++++----
 src/core/hle/service/nvflinger/nvflinger.h      |  4 ++++
 src/core/hle/service/vi/vi.cpp                  |  5 +++--
 5 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 5731e815f1..dca75c35e6 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -63,7 +63,7 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
 }
 
 void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
-                              const Common::Rectangle<int>& crop_rect) {
+                              const Common::Rectangle<int>& crop_rect, u32 swap_interval) {
     auto itr = std::find_if(queue.begin(), queue.end(),
                             [&](const Buffer& buffer) { return buffer.slot == slot; });
     ASSERT(itr != queue.end());
@@ -71,6 +71,7 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
     itr->status = Buffer::Status::Queued;
     itr->transform = transform;
     itr->crop_rect = crop_rect;
+    itr->swap_interval = swap_interval;
 }
 
 std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index e1ccb61714..139b98b9f8 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -68,13 +68,14 @@ public:
         IGBPBuffer igbp_buffer;
         BufferTransformFlags transform;
         Common::Rectangle<int> crop_rect;
+        u32 swap_interval;
     };
 
     void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
     std::optional<u32> DequeueBuffer(u32 width, u32 height);
     const IGBPBuffer& RequestBuffer(u32 slot) const;
     void QueueBuffer(u32 slot, BufferTransformFlags transform,
-                     const Common::Rectangle<int>& crop_rect);
+                     const Common::Rectangle<int>& crop_rect, u32 swap_interval);
     std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
     void ReleaseBuffer(u32 slot);
     u32 Query(QueryType type);
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 3c5c53e246..6d83535e77 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -37,15 +37,16 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
     displays.emplace_back(4, "Null");
 
     // Schedule the screen composition events
-    const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
+    //const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
 
     composition_event = core_timing.RegisterEvent(
-        "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) {
+        "ScreenComposition", [this](u64 userdata, s64 cycles_late) {
             Compose();
-            this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event);
+            const auto ticks = GetNextTicks();
+            this->core_timing.ScheduleEvent(std::max(0LL,ticks - cycles_late), composition_event);
         });
 
-    core_timing.ScheduleEvent(ticks, composition_event);
+    core_timing.ScheduleEvent(frame_ticks, composition_event);
 }
 
 NVFlinger::~NVFlinger() {
@@ -206,8 +207,13 @@ void NVFlinger::Compose() {
                      igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
                      buffer->get().transform, buffer->get().crop_rect);
 
+        swap_interval = buffer->get().swap_interval;
         buffer_queue.ReleaseBuffer(buffer->get().slot);
     }
 }
 
+s64 NVFlinger::GetNextTicks() {
+    return (Core::Timing::BASE_CLOCK_RATE * (1LL << swap_interval)) / 120;
+}
+
 } // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index c0a83fffb8..86b94302ce 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -74,6 +74,8 @@ public:
     /// finished.
     void Compose();
 
+    s64 GetNextTicks();
+
 private:
     /// Finds the display identified by the specified ID.
     VI::Display* FindDisplay(u64 display_id);
@@ -98,6 +100,8 @@ private:
     /// layers.
     u32 next_buffer_queue_id = 1;
 
+    u32 swap_interval = 1;
+
     /// Event that handles screen composition.
     Core::Timing::EventType* composition_event;
 
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index f1fa6ccd10..55bd252c23 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -418,7 +418,8 @@ public:
         s32_le scaling_mode;
         NVFlinger::BufferQueue::BufferTransformFlags transform;
         u32_le sticky_transform;
-        INSERT_PADDING_WORDS(2);
+        INSERT_PADDING_WORDS(1);
+        u32_le swap_interval;
         u32_le fence_is_valid;
         std::array<Fence, 2> fences;
 
@@ -582,7 +583,7 @@ private:
             IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
 
             buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
-                                     request.data.GetCropRect());
+                                     request.data.GetCropRect(), request.data.swap_interval);
 
             IGBPQueueBufferResponseParcel response{1280, 720};
             ctx.WriteBuffer(response.Serialize());

From 737e978f5b1440a044ef90f346c8616c2de49a81 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Fri, 7 Jun 2019 11:34:55 -0400
Subject: [PATCH 02/25] nv_services: Correct buffer queue fencing and GPFifo
 fencing

---
 src/core/CMakeLists.txt                       |  1 +
 .../hle/service/nvdrv/devices/nvhost_gpu.cpp  |  8 ++--
 .../hle/service/nvdrv/devices/nvhost_gpu.h    | 22 ++++-----
 src/core/hle/service/nvdrv/nvdata.h           | 25 ++++++++++
 src/core/hle/service/nvdrv/nvdrv.h            |  8 +---
 .../hle/service/nvflinger/buffer_queue.cpp    |  9 ++--
 src/core/hle/service/nvflinger/buffer_queue.h |  8 +++-
 src/core/hle/service/vi/vi.cpp                | 46 +++++++------------
 8 files changed, 70 insertions(+), 57 deletions(-)
 create mode 100644 src/core/hle/service/nvdrv/nvdata.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 30eb9d82e3..c22585bfb4 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -369,6 +369,7 @@ add_library(core STATIC
     hle/service/nvdrv/devices/nvmap.h
     hle/service/nvdrv/interface.cpp
     hle/service/nvdrv/interface.h
+    hle/service/nvdrv/nvdata.h
     hle/service/nvdrv/nvdrv.cpp
     hle/service/nvdrv/nvdrv.h
     hle/service/nvdrv/nvmemp.cpp
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 8ce7bc7a5e..8a53eddb13 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -155,8 +155,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
 
     Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
 
-    params.fence_out.id = 0;
-    params.fence_out.value = 0;
+    // TODO(Blinkhawk): Figure how thoios fence is set
+    // params.fence_out.value = 0;
     std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
     return 0;
 }
@@ -176,8 +176,8 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
 
     Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
 
-    params.fence_out.id = 0;
-    params.fence_out.value = 0;
+    // TODO(Blinkhawk): Figure how thoios fence is set
+    // params.fence_out.value = 0;
     std::memcpy(output.data(), &params, output.size());
     return 0;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 62beb5c0c0..d95cedb091 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -113,11 +113,11 @@ private:
     static_assert(sizeof(IoctlGetErrorNotification) == 16,
                   "IoctlGetErrorNotification is incorrect size");
 
-    struct IoctlFence {
+    struct Fence {
         u32_le id;
         u32_le value;
     };
-    static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size");
+    static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
 
     struct IoctlAllocGpfifoEx {
         u32_le num_entries;
@@ -132,13 +132,13 @@ private:
     static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
 
     struct IoctlAllocGpfifoEx2 {
-        u32_le num_entries;   // in
-        u32_le flags;         // in
-        u32_le unk0;          // in (1 works)
-        IoctlFence fence_out; // out
-        u32_le unk1;          // in
-        u32_le unk2;          // in
-        u32_le unk3;          // in
+        u32_le num_entries; // in
+        u32_le flags;       // in
+        u32_le unk0;        // in (1 works)
+        Fence fence_out;    // out
+        u32_le unk1;        // in
+        u32_le unk2;        // in
+        u32_le unk3;        // in
     };
     static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
 
@@ -154,9 +154,9 @@ private:
         u64_le address;     // pointer to gpfifo entry structs
         u32_le num_entries; // number of fence objects being submitted
         u32_le flags;
-        IoctlFence fence_out; // returned new fence object for others to wait on
+        Fence fence_out; // returned new fence object for others to wait on
     };
-    static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence),
+    static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
                   "IoctlSubmitGpfifo is incorrect size");
 
     struct IoctlGetWaitbase {
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
new file mode 100644
index 0000000000..7e1dce232d
--- /dev/null
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <array>
+#include "common/common_types.h"
+
+namespace Service::Nvidia {
+
+struct Fence {
+    s32 id;
+    u32 value;
+};
+
+static_assert(sizeof(Fence) == 8, "Fence has wrong size");
+
+struct MultiFence {
+    u32 num_fences;
+    std::array<Fence, 4> fences;
+};
+
+enum class NvResult : u32 {
+    Success = 0,
+    TryAgain = 11,
+};
+
+} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 53564f6964..bacd7cdb78 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -8,6 +8,7 @@
 #include <unordered_map>
 #include <vector>
 #include "common/common_types.h"
+#include "core/hle/service/nvdrv/nvdata.h"
 #include "core/hle/service/service.h"
 
 namespace Service::NVFlinger {
@@ -20,13 +21,6 @@ namespace Devices {
 class nvdevice;
 }
 
-struct IoctlFence {
-    u32 id;
-    u32 value;
-};
-
-static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size");
-
 class Module final {
 public:
     Module();
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index dca75c35e6..75e47b8c77 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -34,7 +34,8 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
     buffer_wait_event.writable->Signal();
 }
 
-std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
+std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
+                                                                                       u32 height) {
     auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
         // Only consider free buffers. Buffers become free once again after they've been Acquired
         // and Released by the compositor, see the NVFlinger::Compose method.
@@ -51,7 +52,7 @@ std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
     }
 
     itr->status = Buffer::Status::Dequeued;
-    return itr->slot;
+    return {{itr->slot, &itr->multi_fence}};
 }
 
 const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
@@ -63,7 +64,8 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
 }
 
 void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
-                              const Common::Rectangle<int>& crop_rect, u32 swap_interval) {
+                              const Common::Rectangle<int>& crop_rect, u32 swap_interval,
+                              Service::Nvidia::MultiFence& multi_fence) {
     auto itr = std::find_if(queue.begin(), queue.end(),
                             [&](const Buffer& buffer) { return buffer.slot == slot; });
     ASSERT(itr != queue.end());
@@ -72,6 +74,7 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
     itr->transform = transform;
     itr->crop_rect = crop_rect;
     itr->swap_interval = swap_interval;
+    itr->multi_fence = multi_fence;
 }
 
 std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 139b98b9f8..c163e565c5 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -12,6 +12,7 @@
 #include "common/swap.h"
 #include "core/hle/kernel/object.h"
 #include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/nvdrv/nvdata.h"
 
 namespace Service::NVFlinger {
 
@@ -69,13 +70,16 @@ public:
         BufferTransformFlags transform;
         Common::Rectangle<int> crop_rect;
         u32 swap_interval;
+        Service::Nvidia::MultiFence multi_fence;
     };
 
     void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
-    std::optional<u32> DequeueBuffer(u32 width, u32 height);
+    std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
+                                                                              u32 height);
     const IGBPBuffer& RequestBuffer(u32 slot) const;
     void QueueBuffer(u32 slot, BufferTransformFlags transform,
-                     const Common::Rectangle<int>& crop_rect, u32 swap_interval);
+                     const Common::Rectangle<int>& crop_rect, u32 swap_interval,
+                     Service::Nvidia::MultiFence& multi_fence);
     std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
     void ReleaseBuffer(u32 slot);
     u32 Query(QueryType type);
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 55bd252c23..894bcdc042 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -21,6 +21,7 @@
 #include "core/hle/kernel/readable_event.h"
 #include "core/hle/kernel/thread.h"
 #include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/nvdrv/nvdata.h"
 #include "core/hle/service/nvdrv/nvdrv.h"
 #include "core/hle/service/nvflinger/buffer_queue.h"
 #include "core/hle/service/nvflinger/nvflinger.h"
@@ -328,32 +329,22 @@ public:
     Data data;
 };
 
-struct BufferProducerFence {
-    u32 is_valid;
-    std::array<Nvidia::IoctlFence, 4> fences;
-};
-static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size");
-
 class IGBPDequeueBufferResponseParcel : public Parcel {
 public:
-    explicit IGBPDequeueBufferResponseParcel(u32 slot) : slot(slot) {}
+    explicit IGBPDequeueBufferResponseParcel(u32 slot, Service::Nvidia::MultiFence& multi_fence)
+        : slot(slot), multi_fence(multi_fence) {}
     ~IGBPDequeueBufferResponseParcel() override = default;
 
 protected:
     void SerializeData() override {
-        // TODO(Subv): Find out how this Fence is used.
-        BufferProducerFence fence = {};
-        fence.is_valid = 1;
-        for (auto& fence_ : fence.fences)
-            fence_.id = -1;
-
         Write(slot);
         Write<u32_le>(1);
-        WriteObject(fence);
+        WriteObject(multi_fence);
         Write<u32_le>(0);
     }
 
     u32_le slot;
+    Service::Nvidia::MultiFence multi_fence;
 };
 
 class IGBPRequestBufferRequestParcel : public Parcel {
@@ -400,12 +391,6 @@ public:
         data = Read<Data>();
     }
 
-    struct Fence {
-        u32_le id;
-        u32_le value;
-    };
-    static_assert(sizeof(Fence) == 8, "Fence has wrong size");
-
     struct Data {
         u32_le slot;
         INSERT_PADDING_WORDS(3);
@@ -420,14 +405,13 @@ public:
         u32_le sticky_transform;
         INSERT_PADDING_WORDS(1);
         u32_le swap_interval;
-        u32_le fence_is_valid;
-        std::array<Fence, 2> fences;
+        Service::Nvidia::MultiFence multi_fence;
 
         Common::Rectangle<int> GetCropRect() const {
             return {crop_left, crop_top, crop_right, crop_bottom};
         }
     };
-    static_assert(sizeof(Data) == 80, "ParcelData has wrong size");
+    static_assert(sizeof(Data) == 96, "ParcelData has wrong size");
 
     Data data;
 };
@@ -548,11 +532,11 @@ private:
             IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
             const u32 width{request.data.width};
             const u32 height{request.data.height};
-            std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height);
+            auto result = buffer_queue.DequeueBuffer(width, height);
 
-            if (slot) {
+            if (result) {
                 // Buffer is available
-                IGBPDequeueBufferResponseParcel response{*slot};
+                IGBPDequeueBufferResponseParcel response{(*result).first, *(*result).second};
                 ctx.WriteBuffer(response.Serialize());
             } else {
                 // Wait the current thread until a buffer becomes available
@@ -562,10 +546,11 @@ private:
                         Kernel::ThreadWakeupReason reason) {
                         // Repeat TransactParcel DequeueBuffer when a buffer is available
                         auto& buffer_queue = nv_flinger->FindBufferQueue(id);
-                        std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height);
-                        ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer.");
+                        auto result = buffer_queue.DequeueBuffer(width, height);
+                        ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
 
-                        IGBPDequeueBufferResponseParcel response{*slot};
+                        IGBPDequeueBufferResponseParcel response{(*result).first,
+                                                                 *(*result).second};
                         ctx.WriteBuffer(response.Serialize());
                         IPC::ResponseBuilder rb{ctx, 2};
                         rb.Push(RESULT_SUCCESS);
@@ -583,7 +568,8 @@ private:
             IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
 
             buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
-                                     request.data.GetCropRect(), request.data.swap_interval);
+                                     request.data.GetCropRect(), request.data.swap_interval,
+                                     request.data.multi_fence);
 
             IGBPQueueBufferResponseParcel response{1280, 720};
             ctx.WriteBuffer(response.Serialize());

From 82b829625b89a706dd0d867c529f533fe928710c Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Fri, 7 Jun 2019 12:56:30 -0400
Subject: [PATCH 03/25] video_core: Implement GPU side Syncpoints

---
 .../hle/service/nvdrv/devices/nvhost_gpu.cpp  | 30 +++++++++++++++----
 .../hle/service/nvdrv/devices/nvhost_gpu.h    |  8 ++++-
 src/core/hle/service/nvdrv/nvdata.h           |  2 ++
 src/video_core/engines/maxwell_3d.cpp         |  5 ++--
 src/video_core/gpu.cpp                        | 24 +++++++++++++++
 src/video_core/gpu.h                          | 24 +++++++++++++++
 6 files changed, 84 insertions(+), 9 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 8a53eddb13..9d11075946 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -143,7 +143,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
     IoctlSubmitGpfifo params{};
     std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
-                params.address, params.num_entries, params.flags);
+                params.address, params.num_entries, params.flags.raw);
 
     ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
                                    params.num_entries * sizeof(Tegra::CommandListHeader),
@@ -153,7 +153,17 @@ 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));
 
-    Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
+    UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
+    UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
+
+    auto& gpu = Core::System::GetInstance().GPU();
+    u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
+    if (params.flags.increment.Value()) {
+        params.fence_out.value += current_syncpoint_value;
+    } else {
+        params.fence_out.value = current_syncpoint_value;
+    }
+    gpu.PushGPUEntries(std::move(entries));
 
     // TODO(Blinkhawk): Figure how thoios fence is set
     // params.fence_out.value = 0;
@@ -168,16 +178,24 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
     IoctlSubmitGpfifo params{};
     std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
-                params.address, params.num_entries, params.flags);
+                params.address, params.num_entries, params.flags.raw);
 
     Tegra::CommandList entries(params.num_entries);
     Memory::ReadBlock(params.address, entries.data(),
                       params.num_entries * sizeof(Tegra::CommandListHeader));
 
-    Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
+    UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
+    UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
+
+    auto& gpu = Core::System::GetInstance().GPU();
+    u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
+    if (params.flags.increment.Value()) {
+        params.fence_out.value += current_syncpoint_value;
+    } else {
+        params.fence_out.value = current_syncpoint_value;
+    }
+    gpu.PushGPUEntries(std::move(entries));
 
-    // TODO(Blinkhawk): Figure how thoios fence is set
-    // params.fence_out.value = 0;
     std::memcpy(output.data(), &params, output.size());
     return 0;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index d95cedb091..0729eeb8db 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -153,7 +153,13 @@ private:
     struct IoctlSubmitGpfifo {
         u64_le address;     // pointer to gpfifo entry structs
         u32_le num_entries; // number of fence objects being submitted
-        u32_le flags;
+        union {
+            u32_le raw;
+            BitField<0, 1, u32_le> add_wait;      // append a wait sync_point to the list
+            BitField<1, 1, u32_le> add_increment; // append an increment to the list
+            BitField<2, 1, u32_le> new_hw_format; // Mostly ignored
+            BitField<8, 1, u32_le> increment;     // increment the returned fence
+        } flags;
         Fence fence_out; // returned new fence object for others to wait on
     };
     static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 7e1dce232d..fd5f79f366 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -5,6 +5,8 @@
 
 namespace Service::Nvidia {
 
+constexpr u32 MaxSyncPoints = 192;
+
 struct Fence {
     s32 id;
     u32 value;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 8755b8af40..224c27bd2c 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -346,8 +346,9 @@ void Maxwell3D::ProcessSyncPoint() {
     const u32 sync_point = regs.sync_info.sync_point.Value();
     const u32 increment = regs.sync_info.increment.Value();
     const u32 cache_flush = regs.sync_info.unknown.Value();
-    LOG_DEBUG(HW_GPU, "Syncpoint set {}, increment: {}, unk: {}", sync_point, increment,
-              cache_flush);
+    if (increment) {
+        system.GPU().IncrementSyncPoint(sync_point);
+    }
 }
 
 void Maxwell3D::DrawArrays() {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 52706505b0..1d12f04930 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -66,6 +66,30 @@ const DmaPusher& GPU::DmaPusher() const {
     return *dma_pusher;
 }
 
+void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
+    syncpoints[syncpoint_id]++;
+    if (!events[syncpoint_id].empty()) {
+        u32 value = syncpoints[syncpoint_id].load();
+        auto it = events[syncpoint_id].begin();
+        while (it != events[syncpoint_id].end()) {
+            if (value >= it->value) {
+                TriggerCpuInterrupt(it->event_id);
+                it = events[syncpoint_id].erase(it);
+                continue;
+            }
+            it++;
+        }
+    }
+}
+
+u32 GPU::GetSyncpointValue(const u32 syncpoint_id) const {
+    return syncpoints[syncpoint_id].load();
+}
+
+void GPU::RegisterEvent(const u32 event_id, const u32 syncpoint_id, const u32 value) {
+    events[syncpoint_id].emplace_back(event_id, value);
+}
+
 u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
     ASSERT(format != RenderTargetFormat::NONE);
 
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index fe66289230..4c97d6c6f4 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -5,8 +5,11 @@
 #pragma once
 
 #include <array>
+#include <atomic>
+#include <list>
 #include <memory>
 #include "common/common_types.h"
+#include "core/hle/service/nvdrv/nvdata.h"
 #include "core/hle/service/nvflinger/buffer_queue.h"
 #include "video_core/dma_pusher.h"
 
@@ -164,6 +167,12 @@ public:
     /// Returns a reference to the GPU DMA pusher.
     Tegra::DmaPusher& DmaPusher();
 
+    void IncrementSyncPoint(const u32 syncpoint_id);
+
+    u32 GetSyncpointValue(const u32 syncpoint_id) const;
+
+    void RegisterEvent(const u32 event_id, const u32 sync_point_id, const u32 value);
+
     /// Returns a const reference to the GPU DMA pusher.
     const Tegra::DmaPusher& DmaPusher() const;
 
@@ -228,6 +237,11 @@ public:
     /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
     virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
 
+protected:
+    virtual void TriggerCpuInterrupt(const u32 event_id) const {
+        // Todo implement this
+    }
+
 private:
     void ProcessBindMethod(const MethodCall& method_call);
     void ProcessSemaphoreTriggerMethod();
@@ -262,6 +276,16 @@ private:
     std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
     /// Inline memory engine
     std::unique_ptr<Engines::KeplerMemory> kepler_memory;
+
+    std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{};
+
+    struct Event {
+        Event(const u32 event_id, const u32 value) : event_id(event_id), value(value) {}
+        u32 event_id;
+        u32 value;
+    };
+
+    std::array<std::list<Event>, Service::Nvidia::MaxSyncPoints> events;
 };
 
 #define ASSERT_REG_POSITION(field_name, position)                                                  \

From 7039ece0a0effbb62c18fba3ac57bdb3d89b27c8 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Fri, 7 Jun 2019 15:16:54 -0400
Subject: [PATCH 04/25] nv_services: Create GPU channels correctly

---
 src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | 6 ++++--
 src/core/hle/service/nvdrv/devices/nvhost_gpu.h   | 1 +
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 9d11075946..8083f5a87a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -119,8 +119,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
                 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
                 params.unk3);
 
-    params.fence_out.id = 0;
-    params.fence_out.value = 0;
+    auto& gpu = Core::System::GetInstance().GPU();
+    params.fence_out.id = channels;
+    params.fence_out.value = gpu.GetSyncpointValue(channels);
+    channels++;
     std::memcpy(output.data(), &params, output.size());
     return 0;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 0729eeb8db..54378cb5d2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -190,6 +190,7 @@ private:
     u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
 
     std::shared_ptr<nvmap> nvmap_dev;
+    u32 channels{};
 };
 
 } // namespace Service::Nvidia::Devices

From e0027eba854b9cf097360e898457e164e6ae0b4d Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Fri, 7 Jun 2019 18:41:55 -0400
Subject: [PATCH 05/25] nv_services: Implement NvQueryEvent, NvCtrlEventWait,
 NvEventRegister, NvEventUnregister

---
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 91 +++++++++++++++++--
 .../hle/service/nvdrv/devices/nvhost_ctrl.h   |  7 +-
 src/core/hle/service/nvdrv/interface.cpp      | 17 +++-
 src/core/hle/service/nvdrv/interface.h        |  2 +
 src/core/hle/service/nvdrv/nvdata.h           | 14 ++-
 src/core/hle/service/nvdrv/nvdrv.cpp          | 26 +++++-
 src/core/hle/service/nvdrv/nvdrv.h            | 52 +++++++++++
 7 files changed, 192 insertions(+), 17 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index b39fb9ef90..ef6731a8f2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -7,11 +7,15 @@
 
 #include "common/assert.h"
 #include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
 #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
+#include "video_core/gpu.h"
 
 namespace Service::Nvidia::Devices {
 
-nvhost_ctrl::nvhost_ctrl() = default;
+nvhost_ctrl::nvhost_ctrl(EventsInterface& events_interface) : events_interface{events_interface} {}
 nvhost_ctrl::~nvhost_ctrl() = default;
 
 u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
@@ -27,6 +31,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
         return IocCtrlEventWait(input, output, true);
     case IoctlCommand::IocCtrlEventRegisterCommand:
         return IocCtrlEventRegister(input, output);
+    case IoctlCommand::IocCtrlEventUnregisterCommand:
+        return IocCtrlEventUnregister(input, output);
     }
     UNIMPLEMENTED_MSG("Unimplemented ioctl");
     return 0;
@@ -44,20 +50,85 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
                                   bool is_async) {
     IocCtrlEventWaitParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
-    LOG_WARNING(Service_NVDRV,
-                "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}",
-                params.syncpt_id, params.threshold, params.timeout, is_async);
+    LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
+              params.syncpt_id, params.threshold, params.timeout, is_async);
 
-    // TODO(Subv): Implement actual syncpt waiting.
-    params.value = 0;
+    if (params.syncpt_id >= MaxSyncPoints) {
+        return NvResult::BadParameter;
+    }
+
+    auto& gpu = Core::System::GetInstance().GPU();
+    u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
+    if (current_syncpoint_value >= params.threshold) {
+        params.value = current_syncpoint_value;
+        std::memcpy(output.data(), &params, sizeof(params));
+        return NvResult::Success;
+    }
+
+    if (!is_async) {
+        params.value = 0;
+    }
+
+    if (params.timeout == 0) {
+        std::memcpy(output.data(), &params, sizeof(params));
+        return NvResult::Timeout;
+    }
+
+    u32 event_index;
+    if (is_async) {
+        event_index = params.value;
+        if (event_index >= 64) {
+            std::memcpy(output.data(), &params, sizeof(params));
+            return NvResult::BadParameter;
+        }
+    } else {
+        event_index = events_interface.GetFreeEvent();
+    }
+
+    EventState status = events_interface.status[event_index];
+    if (event_index < MaxNvEvents || status == EventState::Free ||
+        status == EventState::Registered) {
+        events_interface.SetEventStatus(event_index, EventState::Waiting);
+        events_interface.assigned_syncpt[event_index] = params.syncpt_id;
+        events_interface.assigned_value[event_index] = params.threshold;
+        if (is_async) {
+            params.value = params.syncpt_id << 4;
+        } else {
+            params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
+        }
+        params.value |= event_index;
+        gpu.RegisterEvent(event_index, params.syncpt_id, params.threshold);
+        std::memcpy(output.data(), &params, sizeof(params));
+        return NvResult::Timeout;
+    }
     std::memcpy(output.data(), &params, sizeof(params));
-    return 0;
+    return NvResult::BadParameter;
 }
 
 u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
-    LOG_WARNING(Service_NVDRV, "(STUBBED) called");
-    // TODO(bunnei): Implement this.
-    return 0;
+    IocCtrlEventRegisterParams params{};
+    std::memcpy(&params, input.data(), sizeof(params));
+    if (params.user_event_id >= MaxNvEvents) {
+        return NvResult::BadParameter;
+    }
+    if (events_interface.registered[params.user_event_id]) {
+        return NvResult::BadParameter;
+    }
+    events_interface.RegisterEvent(params.user_event_id);
+    return NvResult::Success;
+}
+
+u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) {
+    IocCtrlEventUnregisterParams params{};
+    std::memcpy(&params, input.data(), sizeof(params));
+    if (params.user_event_id >= MaxNvEvents) {
+        return NvResult::BadParameter;
+    }
+    if (!events_interface.registered[params.user_event_id]) {
+        return NvResult::BadParameter;
+    }
+    events_interface.UnregisterEvent(params.user_event_id);
+    return NvResult::Success;
 }
 
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 6d0de2212a..2985e7f75c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -8,12 +8,13 @@
 #include <vector>
 #include "common/common_types.h"
 #include "core/hle/service/nvdrv/devices/nvdevice.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
 
 namespace Service::Nvidia::Devices {
 
 class nvhost_ctrl final : public nvdevice {
 public:
-    nvhost_ctrl();
+    nvhost_ctrl(EventsInterface& events_interface);
     ~nvhost_ctrl() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
@@ -135,6 +136,10 @@ private:
     u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
 
     u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
+
+    u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
+
+    EventsInterface& events_interface;
 };
 
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index b60fc748b8..76482d16e4 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -10,10 +10,15 @@
 #include "core/hle/kernel/readable_event.h"
 #include "core/hle/kernel/writable_event.h"
 #include "core/hle/service/nvdrv/interface.h"
+#include "core/hle/service/nvdrv/nvdata.h"
 #include "core/hle/service/nvdrv/nvdrv.h"
 
 namespace Service::Nvidia {
 
+void NVDRV::SignalGPUInterrupt(const u32 event_id) {
+    nvdrv->SignalEvent(event_id);
+}
+
 void NVDRV::Open(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_NVDRV, "called");
 
@@ -66,13 +71,19 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
 void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     u32 fd = rp.Pop<u32>();
-    u32 event_id = rp.Pop<u32>();
+    // TODO(Blinkhawk): Figure the meaning of the flag at bit 16
+    u32 event_id = rp.Pop<u32>() & 0x000000FF;
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
 
     IPC::ResponseBuilder rb{ctx, 3, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(query_event.readable);
-    rb.Push<u32>(0);
+    if (event_id < 64) {
+        rb.PushCopyObjects(nvdrv->GetEvent(event_id));
+        rb.Push<u32>(NvResult::Success);
+    } else {
+        rb.Push<u32>(0);
+        rb.Push<u32>(NvResult::BadParameter);
+    }
 }
 
 void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 5b48899108..421b010174 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -19,6 +19,8 @@ public:
     NVDRV(std::shared_ptr<Module> nvdrv, const char* name);
     ~NVDRV() override;
 
+    void SignalGPUInterrupt(const u32 event_id);
+
 private:
     void Open(Kernel::HLERequestContext& ctx);
     void Ioctl(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index fd5f79f366..6dbc90e4c7 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -6,6 +6,7 @@
 namespace Service::Nvidia {
 
 constexpr u32 MaxSyncPoints = 192;
+constexpr u32 MaxNvEvents = 64;
 
 struct Fence {
     s32 id;
@@ -19,9 +20,18 @@ struct MultiFence {
     std::array<Fence, 4> fences;
 };
 
-enum class NvResult : u32 {
+enum NvResult : u32 {
     Success = 0,
-    TryAgain = 11,
+    BadParameter = 4,
+    Timeout = 5,
+    ResourceError = 15,
+};
+
+enum class EventState {
+    Free = 0,
+    Registered = 1,
+    Waiting = 2,
+    Busy = 3,
 };
 
 } // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 6e4b8f2c66..618bcbc7c1 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -4,7 +4,10 @@
 
 #include <utility>
 
+#include <fmt/format.h>
 #include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
 #include "core/hle/service/nvdrv/devices/nvdevice.h"
 #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
 #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
@@ -33,13 +36,21 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
 }
 
 Module::Module() {
+    auto& kernel = Core::System::GetInstance().Kernel();
+    for (u32 i = 0; i < MaxNvEvents; i++) {
+        std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
+        events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(
+            kernel, Kernel::ResetType::Automatic, event_label);
+        events_interface.status[i] = EventState::Free;
+        events_interface.registered[i] = false;
+    }
     auto nvmap_dev = std::make_shared<Devices::nvmap>();
     devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev);
     devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev);
     devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>();
     devices["/dev/nvmap"] = nvmap_dev;
     devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev);
-    devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>();
+    devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(events_interface);
     devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>();
     devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>();
     devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>();
@@ -77,4 +88,17 @@ ResultCode Module::Close(u32 fd) {
     return RESULT_SUCCESS;
 }
 
+void Module::SignalEvent(const u32 event_id) {
+    if (event_id >= 64) {
+        LOG_ERROR(Service_NVDRV, "Unexpected Event signalled!");
+        return;
+    }
+    events_interface.LiberateEvent(event_id);
+    events_interface.events[event_id].writable->Signal();
+}
+
+Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) {
+    return events_interface.events[event_id].readable;
+}
+
 } // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index bacd7cdb78..9a4cdc60ff 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -15,12 +15,58 @@ namespace Service::NVFlinger {
 class NVFlinger;
 }
 
+namespace Kernel {
+class WritableEvent;
+}
+
 namespace Service::Nvidia {
 
 namespace Devices {
 class nvdevice;
 }
 
+struct EventsInterface {
+    u64 events_mask;
+    std::array<Kernel::EventPair, MaxNvEvents> events;
+    std::array<EventState, MaxNvEvents> status;
+    std::array<bool, MaxNvEvents> registered;
+    std::array<u32, MaxNvEvents> assigned_syncpt;
+    std::array<u32, MaxNvEvents> assigned_value;
+    u32 GetFreeEvent() {
+        u64 mask = events_mask;
+        for (u32 i = 0; i < MaxNvEvents; i++) {
+            if (mask & 0x1) {
+                if (status[i] == EventState::Registered || status[i] == EventState::Free) {
+                    return i;
+                }
+            }
+            mask = mask >> 1;
+        }
+        return 0xFFFFFFFF;
+    }
+    void SetEventStatus(const u32 event_id, EventState new_status) {
+        status[event_id] = new_status;
+        if (new_status == EventState::Registered) {
+            registered[event_id] = true;
+        }
+    }
+    void RegisterEvent(const u32 event_id) {
+        registered[event_id] = true;
+        if (status[event_id] == EventState::Free) {
+            status[event_id] = EventState::Registered;
+        }
+    }
+    void UnregisterEvent(const u32 event_id) {
+        registered[event_id] = false;
+        if (status[event_id] == EventState::Registered) {
+            status[event_id] = EventState::Free;
+        }
+    }
+    void LiberateEvent(const u32 event_id) {
+        status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
+    }
+};
+
 class Module final {
 public:
     Module();
@@ -42,6 +88,10 @@ public:
     /// Closes a device file descriptor and returns operation success.
     ResultCode Close(u32 fd);
 
+    void SignalEvent(const u32 event_id);
+
+    Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(const u32 event_id);
+
 private:
     /// Id to use for the next open file descriptor.
     u32 next_fd = 1;
@@ -51,6 +101,8 @@ private:
 
     /// Mapping of device node names to their implementation.
     std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
+
+    EventsInterface events_interface;
 };
 
 /// Registers all NVDRV services with the specified service manager.

From 8942047d419f6d2d0c56adad689fbf3bcd4d2961 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Fri, 7 Jun 2019 20:41:06 -0400
Subject: [PATCH 06/25] Gpu: Implement Hardware Interrupt Manager and manage
 GPU interrupts

---
 src/core/CMakeLists.txt                  |  2 ++
 src/core/core.cpp                        | 12 +++++++++++-
 src/core/core.h                          | 10 ++++++++++
 src/core/hardware_interrupt_manager.cpp  | 21 +++++++++++++++++++++
 src/core/hardware_interrupt_manager.h    | 24 ++++++++++++++++++++++++
 src/core/hle/service/nvdrv/interface.cpp |  2 --
 src/core/hle/service/nvdrv/interface.h   |  2 --
 src/core/hle/service/nvdrv/nvdrv.h       |  5 +----
 src/video_core/gpu.cpp                   |  7 ++++++-
 src/video_core/gpu.h                     |  5 ++---
 src/video_core/gpu_asynch.cpp            |  7 +++++++
 src/video_core/gpu_asynch.h              |  3 +++
 src/video_core/gpu_synch.h               |  3 +++
 13 files changed, 90 insertions(+), 13 deletions(-)
 create mode 100644 src/core/hardware_interrupt_manager.cpp
 create mode 100644 src/core/hardware_interrupt_manager.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c22585bfb4..12f06a1899 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -111,6 +111,8 @@ add_library(core STATIC
     frontend/scope_acquire_window_context.h
     gdbstub/gdbstub.cpp
     gdbstub/gdbstub.h
+    hardware_interrupt_manager.cpp
+    hardware_interrupt_manager.h
     hle/ipc.h
     hle/ipc_helpers.h
     hle/kernel/address_arbiter.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 262411db85..d7f43f5ec5 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -19,6 +19,7 @@
 #include "core/file_sys/vfs_concat.h"
 #include "core/file_sys/vfs_real.h"
 #include "core/gdbstub/gdbstub.h"
+#include "core/hardware_interrupt_manager.h"
 #include "core/hle/kernel/client_port.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/process.h"
@@ -150,7 +151,7 @@ struct System::Impl {
         if (!renderer->Init()) {
             return ResultStatus::ErrorVideoCore;
         }
-
+        interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
         gpu_core = VideoCore::CreateGPU(system);
 
         is_powered_on = true;
@@ -297,6 +298,7 @@ struct System::Impl {
     std::unique_ptr<VideoCore::RendererBase> renderer;
     std::unique_ptr<Tegra::GPU> gpu_core;
     std::shared_ptr<Tegra::DebugContext> debug_context;
+    std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
     CpuCoreManager cpu_core_manager;
     bool is_powered_on = false;
 
@@ -440,6 +442,14 @@ const Tegra::GPU& System::GPU() const {
     return *impl->gpu_core;
 }
 
+Core::Hardware::InterruptManager& System::InterruptManager() {
+    return *impl->interrupt_manager;
+}
+
+const Core::Hardware::InterruptManager& System::InterruptManager() const {
+    return *impl->interrupt_manager;
+}
+
 VideoCore::RendererBase& System::Renderer() {
     return *impl->renderer;
 }
diff --git a/src/core/core.h b/src/core/core.h
index 70adb7af96..53e6fdb7b9 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -66,6 +66,10 @@ namespace Core::Timing {
 class CoreTiming;
 }
 
+namespace Core::Hardware {
+class InterruptManager;
+}
+
 namespace Core {
 
 class ARM_Interface;
@@ -230,6 +234,12 @@ public:
     /// Provides a constant reference to the core timing instance.
     const Timing::CoreTiming& CoreTiming() const;
 
+    /// Provides a reference to the interrupt manager instance.
+    Core::Hardware::InterruptManager& InterruptManager();
+
+    /// Provides a constant reference to the interrupt manager instance.
+    const Core::Hardware::InterruptManager& InterruptManager() const;
+
     /// Provides a reference to the kernel instance.
     Kernel::KernelCore& Kernel();
 
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
new file mode 100644
index 0000000000..463d2916c5
--- /dev/null
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -0,0 +1,21 @@
+
+#include "core/core.h"
+#include "core/hardware_interrupt_manager.h"
+#include "core/hle/service/nvdrv/interface.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Core::Hardware {
+
+InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
+    gpu_interrupt_event =
+        system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 event_index, s64) {
+            auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
+            nvdrv->SignalGPUInterrupt(static_cast<u32>(event_index));
+        });
+}
+
+void InterruptManager::InterruptGPU(const u32 event_index) {
+    system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, static_cast<u64>(event_index));
+}
+
+} // namespace Core::Hardware
diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h
new file mode 100644
index 0000000000..fc565c88bf
--- /dev/null
+++ b/src/core/hardware_interrupt_manager.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "common/common_types.h"
+#include "core/core_timing.h"
+
+namespace Core {
+class System;
+}
+
+namespace Core::Hardware {
+
+class InterruptManager {
+public:
+    InterruptManager(Core::System& system);
+    ~InterruptManager() = default;
+
+    void InterruptGPU(const u32 event_index);
+
+private:
+    Core::System& system;
+    Core::Timing::EventType* gpu_interrupt_event{};
+};
+
+} // namespace Core::Hardware
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 76482d16e4..d95ba18cb2 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -140,8 +140,6 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
     RegisterHandlers(functions);
 
     auto& kernel = Core::System::GetInstance().Kernel();
-    query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
-                                                         "NVDRV::query_event");
 }
 
 NVDRV::~NVDRV() = default;
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 421b010174..09cf4bb121 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -35,8 +35,6 @@ private:
     std::shared_ptr<Module> nvdrv;
 
     u64 pid{};
-
-    Kernel::EventPair query_event;
 };
 
 } // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 9a4cdc60ff..d299f28772 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -8,6 +8,7 @@
 #include <unordered_map>
 #include <vector>
 #include "common/common_types.h"
+#include "core/hle/kernel/writable_event.h"
 #include "core/hle/service/nvdrv/nvdata.h"
 #include "core/hle/service/service.h"
 
@@ -15,10 +16,6 @@ namespace Service::NVFlinger {
 class NVFlinger;
 }
 
-namespace Kernel {
-class WritableEvent;
-}
-
 namespace Service::Nvidia {
 
 namespace Devices {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 1d12f04930..06eb570ab7 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -29,7 +29,8 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
     UNREACHABLE();
 }
 
-GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} {
+GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer)
+    : system{system}, renderer{renderer} {
     auto& rasterizer{renderer.Rasterizer()};
     memory_manager = std::make_unique<Tegra::MemoryManager>(rasterizer);
     dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
@@ -87,6 +88,10 @@ u32 GPU::GetSyncpointValue(const u32 syncpoint_id) const {
 }
 
 void GPU::RegisterEvent(const u32 event_id, const u32 syncpoint_id, const u32 value) {
+    for (auto& ev : events[syncpoint_id]) {
+        if (ev.event_id == event_id && ev.value == value)
+            return;
+    }
     events[syncpoint_id].emplace_back(event_id, value);
 }
 
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 4c97d6c6f4..c3e5311fac 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -238,9 +238,7 @@ public:
     virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
 
 protected:
-    virtual void TriggerCpuInterrupt(const u32 event_id) const {
-        // Todo implement this
-    }
+    virtual void TriggerCpuInterrupt(const u32 event_id) const = 0;
 
 private:
     void ProcessBindMethod(const MethodCall& method_call);
@@ -260,6 +258,7 @@ private:
 protected:
     std::unique_ptr<Tegra::DmaPusher> dma_pusher;
     VideoCore::RendererBase& renderer;
+    Core::System& system;
 
 private:
     std::unique_ptr<Tegra::MemoryManager> memory_manager;
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index d4e2553a95..7060f9a89b 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -2,6 +2,8 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include "core/core.h"
+#include "core/hardware_interrupt_manager.h"
 #include "video_core/gpu_asynch.h"
 #include "video_core/gpu_thread.h"
 #include "video_core/renderer_base.h"
@@ -38,4 +40,9 @@ void GPUAsynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
     gpu_thread.FlushAndInvalidateRegion(addr, size);
 }
 
+void GPUAsynch::TriggerCpuInterrupt(const u32 event_id) const {
+    auto& interrupt_manager = system.InterruptManager();
+    interrupt_manager.InterruptGPU(event_id);
+}
+
 } // namespace VideoCommon
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 30be74cba8..d49e9b96e7 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -27,6 +27,9 @@ public:
     void InvalidateRegion(CacheAddr addr, u64 size) override;
     void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
 
+protected:
+    void TriggerCpuInterrupt(const u32 event_id) const override;
+
 private:
     GPUThread::ThreadManager gpu_thread;
 };
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 3031fcf725..09bda854a2 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -25,6 +25,9 @@ public:
     void FlushRegion(CacheAddr addr, u64 size) override;
     void InvalidateRegion(CacheAddr addr, u64 size) override;
     void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
+
+protected:
+    void TriggerCpuInterrupt(const u32 event_id) const override {}
 };
 
 } // namespace VideoCommon

From a45643cb3b07e76e73814baf1d472d636dd2cd91 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Fri, 7 Jun 2019 21:13:20 -0400
Subject: [PATCH 07/25] nv_services: Stub CtrlEventSignal

---
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 44 ++++++++++++++-----
 .../hle/service/nvdrv/devices/nvhost_ctrl.h   |  2 +
 src/video_core/gpu.cpp                        | 11 +++++
 src/video_core/gpu.h                          |  4 +-
 4 files changed, 48 insertions(+), 13 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index ef6731a8f2..8f47d63e33 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -33,6 +33,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
         return IocCtrlEventRegister(input, output);
     case IoctlCommand::IocCtrlEventUnregisterCommand:
         return IocCtrlEventUnregister(input, output);
+    case IoctlCommand::IocCtrlEventSignalCommand:
+        return IocCtrlEventSignal(input, output);
     }
     UNIMPLEMENTED_MSG("Unimplemented ioctl");
     return 0;
@@ -74,30 +76,29 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         return NvResult::Timeout;
     }
 
-    u32 event_index;
+    u32 event_id;
     if (is_async) {
-        event_index = params.value;
-        if (event_index >= 64) {
+        event_id = params.value & 0x00FF;
+        if (event_id >= 64) {
             std::memcpy(output.data(), &params, sizeof(params));
             return NvResult::BadParameter;
         }
     } else {
-        event_index = events_interface.GetFreeEvent();
+        event_id = events_interface.GetFreeEvent();
     }
 
-    EventState status = events_interface.status[event_index];
-    if (event_index < MaxNvEvents || status == EventState::Free ||
-        status == EventState::Registered) {
-        events_interface.SetEventStatus(event_index, EventState::Waiting);
-        events_interface.assigned_syncpt[event_index] = params.syncpt_id;
-        events_interface.assigned_value[event_index] = params.threshold;
+    EventState status = events_interface.status[event_id];
+    if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) {
+        events_interface.SetEventStatus(event_id, EventState::Waiting);
+        events_interface.assigned_syncpt[event_id] = params.syncpt_id;
+        events_interface.assigned_value[event_id] = params.threshold;
         if (is_async) {
             params.value = params.syncpt_id << 4;
         } else {
             params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
         }
-        params.value |= event_index;
-        gpu.RegisterEvent(event_index, params.syncpt_id, params.threshold);
+        params.value |= event_id;
+        gpu.RegisterEvent(event_id, params.syncpt_id, params.threshold);
         std::memcpy(output.data(), &params, sizeof(params));
         return NvResult::Timeout;
     }
@@ -131,4 +132,23 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
     return NvResult::Success;
 }
 
+u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) {
+    IocCtrlEventSignalParams params{};
+    std::memcpy(&params, input.data(), sizeof(params));
+    // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
+    // It is believed to cancel the GPU Event. However, better research is required
+    u32 event_id = params.user_event_id & 0x00FF;
+    LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
+    if (event_id >= MaxNvEvents) {
+        return NvResult::BadParameter;
+    }
+    if (events_interface.status[event_id] == EventState::Waiting) {
+        auto& gpu = Core::System::GetInstance().GPU();
+        gpu.CancelEvent(event_id, events_interface.assigned_syncpt[event_id],
+                        events_interface.assigned_value[event_id]);
+        events_interface.LiberateEvent(event_id);
+    }
+    return NvResult::Success;
+}
+
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 2985e7f75c..b5bc9337b2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -139,6 +139,8 @@ private:
 
     u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
 
+    u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output);
+
     EventsInterface& events_interface;
 };
 
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 06eb570ab7..1fa6770ca8 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -95,6 +95,17 @@ void GPU::RegisterEvent(const u32 event_id, const u32 syncpoint_id, const u32 va
     events[syncpoint_id].emplace_back(event_id, value);
 }
 
+void GPU::CancelEvent(const u32 event_id, const u32 syncpoint_id, const u32 value) {
+    auto it = events[syncpoint_id].begin();
+    while (it != events[syncpoint_id].end()) {
+        if (value == it->value) {
+            it = events[syncpoint_id].erase(it);
+            return;
+        }
+        it++;
+    }
+}
+
 u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
     ASSERT(format != RenderTargetFormat::NONE);
 
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index c3e5311fac..4805a5fbcb 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -171,7 +171,9 @@ public:
 
     u32 GetSyncpointValue(const u32 syncpoint_id) const;
 
-    void RegisterEvent(const u32 event_id, const u32 sync_point_id, const u32 value);
+    void RegisterEvent(const u32 event_id, const u32 syncpoint_id, const u32 value);
+
+    void CancelEvent(const u32 event_id, const u32 syncpoint_id, const u32 value);
 
     /// Returns a const reference to the GPU DMA pusher.
     const Tegra::DmaPusher& DmaPusher() const;

From eef55f493b636bfc57389e9c541ddf2c39f6f826 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Fri, 7 Jun 2019 22:13:40 -0400
Subject: [PATCH 08/25] Gpu: Mark areas as protected.

---
 src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp |  6 ++++++
 src/video_core/gpu.cpp                             |  2 ++
 src/video_core/gpu.h                               | 11 +++++++++++
 3 files changed, 19 insertions(+)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 8f47d63e33..8e28c2fa42 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -60,10 +60,12 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
     }
 
     auto& gpu = Core::System::GetInstance().GPU();
+    gpu.Guard(true);
     u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
     if (current_syncpoint_value >= params.threshold) {
         params.value = current_syncpoint_value;
         std::memcpy(output.data(), &params, sizeof(params));
+        gpu.Guard(false);
         return NvResult::Success;
     }
 
@@ -73,6 +75,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
 
     if (params.timeout == 0) {
         std::memcpy(output.data(), &params, sizeof(params));
+        gpu.Guard(false);
         return NvResult::Timeout;
     }
 
@@ -81,6 +84,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         event_id = params.value & 0x00FF;
         if (event_id >= 64) {
             std::memcpy(output.data(), &params, sizeof(params));
+            gpu.Guard(false);
             return NvResult::BadParameter;
         }
     } else {
@@ -100,9 +104,11 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         params.value |= event_id;
         gpu.RegisterEvent(event_id, params.syncpt_id, params.threshold);
         std::memcpy(output.data(), &params, sizeof(params));
+        gpu.Guard(false);
         return NvResult::Timeout;
     }
     std::memcpy(output.data(), &params, sizeof(params));
+    gpu.Guard(false);
     return NvResult::BadParameter;
 }
 
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 1fa6770ca8..ee976f81f9 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -69,6 +69,7 @@ const DmaPusher& GPU::DmaPusher() const {
 
 void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
     syncpoints[syncpoint_id]++;
+    sync_guard.lock();
     if (!events[syncpoint_id].empty()) {
         u32 value = syncpoints[syncpoint_id].load();
         auto it = events[syncpoint_id].begin();
@@ -81,6 +82,7 @@ void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
             it++;
         }
     }
+    sync_guard.unlock();
 }
 
 u32 GPU::GetSyncpointValue(const u32 syncpoint_id) const {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 4805a5fbcb..bc63920f2e 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -12,6 +12,7 @@
 #include "core/hle/service/nvdrv/nvdata.h"
 #include "core/hle/service/nvflinger/buffer_queue.h"
 #include "video_core/dma_pusher.h"
+#include "common/spin_lock.h"
 
 using CacheAddr = std::uintptr_t;
 inline CacheAddr ToCacheAddr(const void* host_ptr) {
@@ -175,6 +176,14 @@ public:
 
     void CancelEvent(const u32 event_id, const u32 syncpoint_id, const u32 value);
 
+    void Guard(bool guard_set) {
+        if (guard_set) {
+            sync_guard.lock();
+        } else {
+            sync_guard.unlock();
+        }
+    }
+
     /// Returns a const reference to the GPU DMA pusher.
     const Tegra::DmaPusher& DmaPusher() const;
 
@@ -287,6 +296,8 @@ private:
     };
 
     std::array<std::list<Event>, Service::Nvidia::MaxSyncPoints> events;
+
+    Common::SpinLock sync_guard{};
 };
 
 #define ASSERT_REG_POSITION(field_name, position)                                                  \

From 78add28aabf0d9835336e5b4369b11308ab362e3 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Sat, 8 Jun 2019 15:13:26 -0400
Subject: [PATCH 09/25] nvhost_ctrl: Corrections to event handling

---
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp  | 18 +++++++++++-------
 src/core/hle/service/nvdrv/nvdrv.cpp           |  2 +-
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 8e28c2fa42..5b1253f6ba 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -102,6 +102,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
             params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
         }
         params.value |= event_id;
+        events_interface.events[event_id].writable->Clear();
         gpu.RegisterEvent(event_id, params.syncpt_id, params.threshold);
         std::memcpy(output.data(), &params, sizeof(params));
         gpu.Guard(false);
@@ -115,26 +116,29 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
 u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
     IocCtrlEventRegisterParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
-    if (params.user_event_id >= MaxNvEvents) {
+    const u32 event_id = params.user_event_id & 0x00FF;
+    if (event_id >= MaxNvEvents) {
         return NvResult::BadParameter;
     }
-    if (events_interface.registered[params.user_event_id]) {
+    if (events_interface.registered[event_id]) {
         return NvResult::BadParameter;
     }
-    events_interface.RegisterEvent(params.user_event_id);
+    events_interface.RegisterEvent(event_id);
+    events_interface.events[event_id].writable->Signal();
     return NvResult::Success;
 }
 
 u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) {
     IocCtrlEventUnregisterParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
-    if (params.user_event_id >= MaxNvEvents) {
+    const u32 event_id = params.user_event_id & 0x00FF;
+    if (event_id >= MaxNvEvents) {
         return NvResult::BadParameter;
     }
-    if (!events_interface.registered[params.user_event_id]) {
+    if (!events_interface.registered[event_id]) {
         return NvResult::BadParameter;
     }
-    events_interface.UnregisterEvent(params.user_event_id);
+    events_interface.UnregisterEvent(event_id);
     return NvResult::Success;
 }
 
@@ -142,7 +146,7 @@ u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8
     IocCtrlEventSignalParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
     // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
-    // It is believed to cancel the GPU Event. However, better research is required
+    // It is believed from RE to cancel the GPU Event. However, better research is required
     u32 event_id = params.user_event_id & 0x00FF;
     LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
     if (event_id >= MaxNvEvents) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 618bcbc7c1..3a716e7348 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -40,7 +40,7 @@ Module::Module() {
     for (u32 i = 0; i < MaxNvEvents; i++) {
         std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
         events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(
-            kernel, Kernel::ResetType::Automatic, event_label);
+            kernel, Kernel::ResetType::Manual, event_label);
         events_interface.status[i] = EventState::Free;
         events_interface.registered[i] = false;
     }

From c13433aee4032ce654de1db31a93e4aed578596f Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Sat, 8 Jun 2019 16:45:25 -0400
Subject: [PATCH 10/25] Gpu: use an std mutex instead of a spin_lock to guard
 syncpoints

---
 src/video_core/gpu.cpp | 4 ++--
 src/video_core/gpu.h   | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index ee976f81f9..c71f0f9bf4 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -69,7 +69,7 @@ const DmaPusher& GPU::DmaPusher() const {
 
 void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
     syncpoints[syncpoint_id]++;
-    sync_guard.lock();
+    sync_mutex.lock();
     if (!events[syncpoint_id].empty()) {
         u32 value = syncpoints[syncpoint_id].load();
         auto it = events[syncpoint_id].begin();
@@ -82,7 +82,7 @@ void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
             it++;
         }
     }
-    sync_guard.unlock();
+    sync_mutex.unlock();
 }
 
 u32 GPU::GetSyncpointValue(const u32 syncpoint_id) const {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index bc63920f2e..ab1a4bdd45 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -8,11 +8,11 @@
 #include <atomic>
 #include <list>
 #include <memory>
+#include <mutex>
 #include "common/common_types.h"
 #include "core/hle/service/nvdrv/nvdata.h"
 #include "core/hle/service/nvflinger/buffer_queue.h"
 #include "video_core/dma_pusher.h"
-#include "common/spin_lock.h"
 
 using CacheAddr = std::uintptr_t;
 inline CacheAddr ToCacheAddr(const void* host_ptr) {
@@ -178,9 +178,9 @@ public:
 
     void Guard(bool guard_set) {
         if (guard_set) {
-            sync_guard.lock();
+            sync_mutex.lock();
         } else {
-            sync_guard.unlock();
+            sync_mutex.unlock();
         }
     }
 
@@ -297,7 +297,7 @@ private:
 
     std::array<std::list<Event>, Service::Nvidia::MaxSyncPoints> events;
 
-    Common::SpinLock sync_guard{};
+    std::mutex sync_mutex;
 };
 
 #define ASSERT_REG_POSITION(field_name, position)                                                  \

From 600dddf88db0a786b945c65d27da05105410bfe6 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Sat, 8 Jun 2019 17:04:41 -0400
Subject: [PATCH 11/25] Async GPU: do invalidate as synced operation

Async GPU: Always invalidate synced.
---
 src/video_core/gpu_thread.cpp | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 3f0939ec9e..6926553957 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -89,12 +89,7 @@ void ThreadManager::FlushRegion(CacheAddr addr, u64 size) {
 }
 
 void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) {
-    if (state.queue.Empty()) {
-        // It's quicker to invalidate a single region on the CPU if the queue is already empty
-        system.Renderer().Rasterizer().InvalidateRegion(addr, size);
-    } else {
-        PushCommand(InvalidateRegionCommand(addr, size));
-    }
+    system.Renderer().Rasterizer().InvalidateRegion(addr, size);
 }
 
 void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {

From 0706d633bf7764455082cfdfdc35c14507cb6897 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Mon, 10 Jun 2019 08:19:27 -0400
Subject: [PATCH 12/25] nv_host_ctrl: Make Sync GPU variant always return
 synced result.

---
 src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 5 +++++
 src/video_core/gpu.cpp                             | 4 ++--
 src/video_core/gpu.h                               | 8 +++++++-
 src/video_core/gpu_asynch.cpp                      | 2 +-
 src/video_core/gpu_synch.cpp                       | 2 +-
 5 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 5b1253f6ba..96310ed83f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -60,6 +60,11 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
     }
 
     auto& gpu = Core::System::GetInstance().GPU();
+    // This is mostly to take into account unimplemented features. As synced
+    // gpu is always synced.
+    if (!gpu.IsAsync()) {
+        return NvResult::Success;
+    }
     gpu.Guard(true);
     u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
     if (current_syncpoint_value >= params.threshold) {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index c71f0f9bf4..086db0e695 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -29,8 +29,8 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
     UNREACHABLE();
 }
 
-GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer)
-    : system{system}, renderer{renderer} {
+GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
+    : system{system}, renderer{renderer}, is_async{is_async} {
     auto& rasterizer{renderer.Rasterizer()};
     memory_manager = std::make_unique<Tegra::MemoryManager>(rasterizer);
     dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index ab1a4bdd45..18ac3237ed 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -131,7 +131,7 @@ class MemoryManager;
 
 class GPU {
 public:
-    explicit GPU(Core::System& system, VideoCore::RendererBase& renderer);
+    explicit GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async);
 
     virtual ~GPU();
 
@@ -184,6 +184,10 @@ public:
         }
     }
 
+    bool IsAsync() const {
+        return is_async;
+    }
+
     /// Returns a const reference to the GPU DMA pusher.
     const Tegra::DmaPusher& DmaPusher() const;
 
@@ -298,6 +302,8 @@ private:
     std::array<std::list<Event>, Service::Nvidia::MaxSyncPoints> events;
 
     std::mutex sync_mutex;
+
+    const bool is_async;
 };
 
 #define ASSERT_REG_POSITION(field_name, position)                                                  \
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 7060f9a89b..6b6f0f6ec9 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -11,7 +11,7 @@
 namespace VideoCommon {
 
 GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
-    : GPU(system, renderer), gpu_thread{system} {}
+    : GPU(system, renderer, true), gpu_thread{system} {}
 
 GPUAsynch::~GPUAsynch() = default;
 
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index 45e43b1dc5..d4ead9c47c 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -8,7 +8,7 @@
 namespace VideoCommon {
 
 GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
-    : GPU(system, renderer) {}
+    : GPU(system, renderer, false) {}
 
 GPUSynch::~GPUSynch() = default;
 

From f2e026a1d8fb2384c1ece24e6dd32062b4f390a2 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Mon, 10 Jun 2019 08:36:22 -0400
Subject: [PATCH 13/25] gpu_asynch: Simplify synchronization to a simpler
 consumer->producer scheme.

---
 src/video_core/gpu_thread.cpp | 18 +++---------------
 src/video_core/gpu_thread.h   | 32 --------------------------------
 2 files changed, 3 insertions(+), 47 deletions(-)

diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 6926553957..b87938fdd5 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -21,7 +21,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
     MicroProfileOnThreadCreate("GpuThread");
 
     // Wait for first GPU command before acquiring the window context
-    state.WaitForCommands();
+    while (state.queue.Empty());
 
     // If emulation was stopped during disk shader loading, abort before trying to acquire context
     if (!state.is_running) {
@@ -32,7 +32,6 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
 
     CommandDataContainer next;
     while (state.is_running) {
-        state.WaitForCommands();
         while (!state.queue.Empty()) {
             state.queue.Pop(next);
             if (const auto submit_list = std::get_if<SubmitListCommand>(&next.data)) {
@@ -49,8 +48,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
             } else {
                 UNREACHABLE();
             }
-            state.signaled_fence = next.fence;
-            state.TrySynchronize();
+            state.signaled_fence.store(next.fence);
         }
     }
 }
@@ -100,22 +98,12 @@ void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
 u64 ThreadManager::PushCommand(CommandData&& command_data) {
     const u64 fence{++state.last_fence};
     state.queue.Push(CommandDataContainer(std::move(command_data), fence));
-    state.SignalCommands();
     return fence;
 }
 
 MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
 void SynchState::WaitForSynchronization(u64 fence) {
-    if (signaled_fence >= fence) {
-        return;
-    }
-
-    // Wait for the GPU to be idle (all commands to be executed)
-    {
-        MICROPROFILE_SCOPE(GPU_wait);
-        std::unique_lock lock{synchronization_mutex};
-        synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; });
-    }
+    while (signaled_fence.load() < fence);
 }
 
 } // namespace VideoCommon::GPUThread
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 05a168a726..1d9d0c39e3 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -88,41 +88,9 @@ struct CommandDataContainer {
 /// Struct used to synchronize the GPU thread
 struct SynchState final {
     std::atomic_bool is_running{true};
-    std::atomic_int queued_frame_count{};
-    std::mutex synchronization_mutex;
-    std::mutex commands_mutex;
-    std::condition_variable commands_condition;
-    std::condition_variable synchronization_condition;
-
-    /// Returns true if the gap in GPU commands is small enough that we can consider the CPU and GPU
-    /// synchronized. This is entirely empirical.
-    bool IsSynchronized() const {
-        constexpr std::size_t max_queue_gap{5};
-        return queue.Size() <= max_queue_gap;
-    }
-
-    void TrySynchronize() {
-        if (IsSynchronized()) {
-            std::lock_guard lock{synchronization_mutex};
-            synchronization_condition.notify_one();
-        }
-    }
 
     void WaitForSynchronization(u64 fence);
 
-    void SignalCommands() {
-        if (queue.Empty()) {
-            return;
-        }
-
-        commands_condition.notify_one();
-    }
-
-    void WaitForCommands() {
-        std::unique_lock lock{commands_mutex};
-        commands_condition.wait(lock, [this] { return !queue.Empty(); });
-    }
-
     using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
     CommandQueue queue;
     u64 last_fence{};

From 24408cce9bd899a6709c03b25e318123f4de7371 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Mon, 10 Jun 2019 12:03:30 -0400
Subject: [PATCH 14/25] nv_services: Deglobalize NvServices

---
 src/core/hle/service/nvdrv/devices/nvdevice.h |  9 +++++-
 .../service/nvdrv/devices/nvdisp_disp0.cpp    |  5 +--
 .../hle/service/nvdrv/devices/nvdisp_disp0.h  |  2 +-
 .../service/nvdrv/devices/nvhost_as_gpu.cpp   | 12 +++----
 .../hle/service/nvdrv/devices/nvhost_as_gpu.h |  2 +-
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp |  7 +++--
 .../hle/service/nvdrv/devices/nvhost_ctrl.h   |  2 +-
 .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp |  4 +--
 .../service/nvdrv/devices/nvhost_ctrl_gpu.h   |  2 +-
 .../hle/service/nvdrv/devices/nvhost_gpu.cpp  |  9 +++---
 .../hle/service/nvdrv/devices/nvhost_gpu.h    |  2 +-
 .../service/nvdrv/devices/nvhost_nvdec.cpp    |  2 +-
 .../hle/service/nvdrv/devices/nvhost_nvdec.h  |  2 +-
 .../service/nvdrv/devices/nvhost_nvjpg.cpp    |  2 +-
 .../hle/service/nvdrv/devices/nvhost_nvjpg.h  |  2 +-
 .../hle/service/nvdrv/devices/nvhost_vic.cpp  |  2 +-
 .../hle/service/nvdrv/devices/nvhost_vic.h    |  2 +-
 src/core/hle/service/nvdrv/devices/nvmap.cpp  |  2 +-
 src/core/hle/service/nvdrv/devices/nvmap.h    |  2 +-
 src/core/hle/service/nvdrv/interface.cpp      |  2 --
 src/core/hle/service/nvdrv/nvdrv.cpp          | 31 ++++++++++---------
 src/core/hle/service/nvdrv/nvdrv.h            |  9 ++++--
 src/core/hle/service/service.cpp              |  2 +-
 23 files changed, 65 insertions(+), 51 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 4f6042b00a..ed606cd15d 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -9,13 +9,17 @@
 #include "common/common_types.h"
 #include "common/swap.h"
 
+namespace Core {
+class System;
+}
+
 namespace Service::Nvidia::Devices {
 
 /// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to
 /// implement the ioctl interface.
 class nvdevice {
 public:
-    nvdevice() = default;
+    nvdevice(Core::System& system) : system{system} {};
     virtual ~nvdevice() = default;
     union Ioctl {
         u32_le raw;
@@ -34,6 +38,9 @@ public:
      * @returns The result code of the ioctl.
      */
     virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0;
+
+protected:
+    Core::System& system;
 };
 
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 20c7c39aaf..3336b2080f 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -13,7 +13,8 @@
 
 namespace Service::Nvidia::Devices {
 
-nvdisp_disp0::nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
+nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+    : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvdisp_disp0 ::~nvdisp_disp0() = default;
 
 u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
@@ -34,7 +35,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
         addr,      offset,   width, height, stride, static_cast<PixelFormat>(format),
         transform, crop_rect};
 
-    auto& instance = Core::System::GetInstance();
+    auto& instance = system;
     instance.GetPerfStats().EndGameFrame();
     instance.GPU().SwapBuffers(framebuffer);
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 12f3ef8254..812967bdf9 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -17,7 +17,7 @@ class nvmap;
 
 class nvdisp_disp0 final : public nvdevice {
 public:
-    explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev);
+    explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvdisp_disp0() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
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 af62d33d21..eccc3f1d95 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -22,7 +22,8 @@ enum {
 };
 }
 
-nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
+nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+    : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvhost_as_gpu::~nvhost_as_gpu() = default;
 
 u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
@@ -65,7 +66,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
     LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
               params.page_size, params.flags);
 
-    auto& gpu = Core::System::GetInstance().GPU();
+    auto& gpu = system.GPU();
     const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
     if (params.flags & 1) {
         params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1);
@@ -85,7 +86,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
     std::vector<IoctlRemapEntry> entries(num_entries);
     std::memcpy(entries.data(), input.data(), input.size());
 
-    auto& gpu = Core::System::GetInstance().GPU();
+    auto& gpu = system.GPU();
     for (const auto& entry : entries) {
         LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
                     entry.offset, entry.nvmap_handle, entry.pages);
@@ -136,7 +137,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
     // case to prevent unexpected behavior.
     ASSERT(object->id == params.nvmap_handle);
 
-    auto& gpu = Core::System::GetInstance().GPU();
+    auto& gpu = system.GPU();
 
     if (params.flags & 1) {
         params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size);
@@ -173,8 +174,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
         return 0;
     }
 
-    params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset,
-                                                                                  itr->second.size);
+    params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size);
     buffer_mappings.erase(itr->second.offset);
 
     std::memcpy(output.data(), &params, output.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index eb14b1da8e..e45d4f1ff4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -17,7 +17,7 @@ class nvmap;
 
 class nvhost_as_gpu final : public nvdevice {
 public:
-    explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev);
+    explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_as_gpu() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 96310ed83f..02b078c2f6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -15,7 +15,8 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_ctrl::nvhost_ctrl(EventsInterface& events_interface) : events_interface{events_interface} {}
+nvhost_ctrl::nvhost_ctrl(Core::System& system, EventsInterface& events_interface)
+    : nvdevice(system), events_interface{events_interface} {}
 nvhost_ctrl::~nvhost_ctrl() = default;
 
 u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
@@ -59,7 +60,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         return NvResult::BadParameter;
     }
 
-    auto& gpu = Core::System::GetInstance().GPU();
+    auto& gpu = system.GPU();
     // This is mostly to take into account unimplemented features. As synced
     // gpu is always synced.
     if (!gpu.IsAsync()) {
@@ -158,7 +159,7 @@ u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8
         return NvResult::BadParameter;
     }
     if (events_interface.status[event_id] == EventState::Waiting) {
-        auto& gpu = Core::System::GetInstance().GPU();
+        auto& gpu = system.GPU();
         gpu.CancelEvent(event_id, events_interface.assigned_syncpt[event_id],
                         events_interface.assigned_value[event_id]);
         events_interface.LiberateEvent(event_id);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index b5bc9337b2..6cbf75f89c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -14,7 +14,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_ctrl final : public nvdevice {
 public:
-    nvhost_ctrl(EventsInterface& events_interface);
+    nvhost_ctrl(Core::System& system, EventsInterface& events_interface);
     ~nvhost_ctrl() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 0e28755bdd..c139f2ceba 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -12,7 +12,7 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_ctrl_gpu::nvhost_ctrl_gpu() = default;
+nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system){};
 nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
 
 u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
@@ -185,7 +185,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
 
     IoctlGetGpuTime params{};
     std::memcpy(&params, input.data(), input.size());
-    const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
+    const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks());
     params.gpu_time = static_cast<u64_le>(ns.count());
     std::memcpy(output.data(), &params, output.size());
     return 0;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 240435eea5..65eac47d14 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_ctrl_gpu final : public nvdevice {
 public:
-    nvhost_ctrl_gpu();
+    nvhost_ctrl_gpu(Core::System& system);
     ~nvhost_ctrl_gpu() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 8083f5a87a..13e80bfad5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -13,7 +13,8 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_gpu::nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
+nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+    : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvhost_gpu::~nvhost_gpu() = default;
 
 u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
@@ -119,7 +120,7 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
                 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
                 params.unk3);
 
-    auto& gpu = Core::System::GetInstance().GPU();
+    auto& gpu = system.GPU();
     params.fence_out.id = channels;
     params.fence_out.value = gpu.GetSyncpointValue(channels);
     channels++;
@@ -158,7 +159,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
     UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
     UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
 
-    auto& gpu = Core::System::GetInstance().GPU();
+    auto& gpu = system.GPU();
     u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
     if (params.flags.increment.Value()) {
         params.fence_out.value += current_syncpoint_value;
@@ -189,7 +190,7 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
     UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
     UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
 
-    auto& gpu = Core::System::GetInstance().GPU();
+    auto& gpu = system.GPU();
     u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
     if (params.flags.increment.Value()) {
         params.fence_out.value += current_syncpoint_value;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 54378cb5d2..106359f876 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -20,7 +20,7 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
 
 class nvhost_gpu final : public nvdevice {
 public:
-    explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev);
+    explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_gpu() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index f5e8ea7c3a..3bfcb84230 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -10,7 +10,7 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_nvdec::nvhost_nvdec() = default;
+nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system){};
 nvhost_nvdec::~nvhost_nvdec() = default;
 
 u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 0e7b284f87..febfd4cc4c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_nvdec final : public nvdevice {
 public:
-    nvhost_nvdec();
+    nvhost_nvdec(Core::System& system);
     ~nvhost_nvdec() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 3e0951ab04..16c683b476 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -10,7 +10,7 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_nvjpg::nvhost_nvjpg() = default;
+nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system){};
 nvhost_nvjpg::~nvhost_nvjpg() = default;
 
 u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 89fd5e95ed..33b149bb73 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_nvjpg final : public nvdevice {
 public:
-    nvhost_nvjpg();
+    nvhost_nvjpg(Core::System& system);
     ~nvhost_nvjpg() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index d544f0f314..853136dea1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -10,7 +10,7 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_vic::nvhost_vic() = default;
+nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system){};
 nvhost_vic::~nvhost_vic() = default;
 
 u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index fc24c3f9c0..b71816ba17 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_vic final : public nvdevice {
 public:
-    nvhost_vic();
+    nvhost_vic(Core::System& system);
     ~nvhost_vic() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 1ec796fc6d..a75ff334b5 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -18,7 +18,7 @@ enum {
 };
 }
 
-nvmap::nvmap() = default;
+nvmap::nvmap(Core::System& system) : nvdevice(system){};
 nvmap::~nvmap() = default;
 
 VAddr nvmap::GetObjectAddress(u32 handle) const {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 396230c191..623c9b232d 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -16,7 +16,7 @@ namespace Service::Nvidia::Devices {
 
 class nvmap final : public nvdevice {
 public:
-    nvmap();
+    nvmap(Core::System& system);
     ~nvmap() override;
 
     /// Returns the allocated address of an nvmap object given its handle.
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index d95ba18cb2..c548f1223d 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -138,8 +138,6 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
         {13, &NVDRV::FinishInitialize, "FinishInitialize"},
     };
     RegisterHandlers(functions);
-
-    auto& kernel = Core::System::GetInstance().Kernel();
 }
 
 NVDRV::~NVDRV() = default;
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 3a716e7348..c68d291776 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -25,8 +25,9 @@
 
 namespace Service::Nvidia {
 
-void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) {
-    auto module_ = std::make_shared<Module>();
+void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
+                       Core::System& system) {
+    auto module_ = std::make_shared<Module>(system);
     std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager);
     std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager);
     std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager);
@@ -35,25 +36,25 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
     nvflinger.SetNVDrvInstance(module_);
 }
 
-Module::Module() {
-    auto& kernel = Core::System::GetInstance().Kernel();
+Module::Module(Core::System& system) {
+    auto& kernel = system.Kernel();
     for (u32 i = 0; i < MaxNvEvents; i++) {
         std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
-        events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(
-            kernel, Kernel::ResetType::Manual, event_label);
+        events_interface.events[i] =
+            Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, event_label);
         events_interface.status[i] = EventState::Free;
         events_interface.registered[i] = false;
     }
-    auto nvmap_dev = std::make_shared<Devices::nvmap>();
-    devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev);
-    devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev);
-    devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>();
+    auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
+    devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
+    devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev);
+    devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
     devices["/dev/nvmap"] = nvmap_dev;
-    devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev);
-    devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(events_interface);
-    devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>();
-    devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>();
-    devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>();
+    devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
+    devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface);
+    devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system);
+    devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
+    devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system);
 }
 
 Module::~Module() = default;
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index d299f28772..0e8eed113f 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -12,6 +12,10 @@
 #include "core/hle/service/nvdrv/nvdata.h"
 #include "core/hle/service/service.h"
 
+namespace Core {
+class System;
+}
+
 namespace Service::NVFlinger {
 class NVFlinger;
 }
@@ -66,7 +70,7 @@ struct EventsInterface {
 
 class Module final {
 public:
-    Module();
+    Module(Core::System& system);
     ~Module();
 
     /// Returns a pointer to one of the available devices, identified by its name.
@@ -103,6 +107,7 @@ private:
 };
 
 /// Registers all NVDRV services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger);
+void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
+                       Core::System& system);
 
 } // namespace Service::Nvidia
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 5fc7d3cab7..07b1f4d436 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -237,7 +237,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
     NIM::InstallInterfaces(*sm);
     NPNS::InstallInterfaces(*sm);
     NS::InstallInterfaces(*sm);
-    Nvidia::InstallInterfaces(*sm, *nv_flinger);
+    Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
     PCIe::InstallInterfaces(*sm);
     PCTL::InstallInterfaces(*sm);
     PCV::InstallInterfaces(*sm);

From ea975896242fba4a92d68c8fb45751e60ed826cc Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Mon, 10 Jun 2019 22:58:20 -0400
Subject: [PATCH 15/25] nvflinger: Acquire buffers in the same order as they
 were queued.

---
 src/core/hle/service/nvflinger/buffer_queue.cpp | 12 +++++++++---
 src/core/hle/service/nvflinger/buffer_queue.h   |  2 ++
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 75e47b8c77..d8aa3f1c0a 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -75,12 +75,18 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
     itr->crop_rect = crop_rect;
     itr->swap_interval = swap_interval;
     itr->multi_fence = multi_fence;
+    queue_sequence.push_back(slot);
 }
 
 std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
-    auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
-        return buffer.status == Buffer::Status::Queued;
-    });
+    std::vector<Buffer>::iterator itr = queue.end();
+    while (itr == queue.end() && !queue_sequence.empty()) {
+        u32 slot = queue_sequence.front();
+        itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
+            return buffer.status == Buffer::Status::Queued && buffer.slot == slot;
+        });
+        queue_sequence.pop_front();
+    }
     if (itr == queue.end())
         return {};
     itr->status = Buffer::Status::Acquired;
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index c163e565c5..be993ee617 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -6,6 +6,7 @@
 
 #include <optional>
 #include <vector>
+#include <list>
 
 #include "common/common_funcs.h"
 #include "common/math_util.h"
@@ -97,6 +98,7 @@ private:
     u64 layer_id;
 
     std::vector<Buffer> queue;
+    std::list<u32> queue_sequence;
     Kernel::EventPair buffer_wait_event;
 };
 

From efdeab3a1d5735a8b000241f09ba57e9b981204b Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Tue, 11 Jun 2019 17:04:24 -0400
Subject: [PATCH 16/25] nv_services: Fixes to event liberation.

---
 src/core/hle/service/nvdrv/nvdrv.h | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 0e8eed113f..597acc9c6f 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -27,16 +27,17 @@ class nvdevice;
 }
 
 struct EventsInterface {
-    u64 events_mask;
+    u64 events_mask{};
     std::array<Kernel::EventPair, MaxNvEvents> events;
-    std::array<EventState, MaxNvEvents> status;
-    std::array<bool, MaxNvEvents> registered;
-    std::array<u32, MaxNvEvents> assigned_syncpt;
-    std::array<u32, MaxNvEvents> assigned_value;
+    std::array<EventState, MaxNvEvents> status{};
+    std::array<bool, MaxNvEvents> registered{};
+    std::array<u32, MaxNvEvents> assigned_syncpt{};
+    std::array<u32, MaxNvEvents> assigned_value{};
     u32 GetFreeEvent() {
         u64 mask = events_mask;
         for (u32 i = 0; i < MaxNvEvents; i++) {
-            if (mask & 0x1) {
+            const bool is_free = (mask & 0x1) == 0;
+            if (is_free) {
                 if (status[i] == EventState::Registered || status[i] == EventState::Free) {
                     return i;
                 }
@@ -46,10 +47,16 @@ struct EventsInterface {
         return 0xFFFFFFFF;
     }
     void SetEventStatus(const u32 event_id, EventState new_status) {
+        EventState old_status = status[event_id];
+        if (old_status == new_status)
+            return;
         status[event_id] = new_status;
         if (new_status == EventState::Registered) {
             registered[event_id] = true;
         }
+        if (new_status == EventState::Waiting || new_status == EventState::Busy) {
+            events_mask |= (1 << event_id);
+        }
     }
     void RegisterEvent(const u32 event_id) {
         registered[event_id] = true;
@@ -65,6 +72,7 @@ struct EventsInterface {
     }
     void LiberateEvent(const u32 event_id) {
         status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
+        events_mask &= ~(1 << event_id);
     }
 };
 

From 61697864c3c8abc35ce957f5f0a0dacd7a8e96f9 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Tue, 11 Jun 2019 17:56:36 -0400
Subject: [PATCH 17/25] nvflinger: Make the force 30 fps still force 30 fps

---
 src/core/hle/service/nvflinger/nvflinger.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 6d83535e77..a7937b4904 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -42,7 +42,7 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
     composition_event = core_timing.RegisterEvent(
         "ScreenComposition", [this](u64 userdata, s64 cycles_late) {
             Compose();
-            const auto ticks = GetNextTicks();
+            const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
             this->core_timing.ScheduleEvent(std::max(0LL,ticks - cycles_late), composition_event);
         });
 

From 7d1b974bcaf72c32910dcf4ff2d435f91cf40609 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Wed, 12 Jun 2019 07:52:49 -0400
Subject: [PATCH 18/25] GPU: Correct Interrupts to interrupt on syncpt/value
 instead of event, mirroring hardware

---
 src/core/hardware_interrupt_manager.cpp       | 11 ++++---
 src/core/hardware_interrupt_manager.h         |  2 +-
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp |  5 +---
 src/core/hle/service/nvdrv/interface.cpp      |  4 +--
 src/core/hle/service/nvdrv/interface.h        |  2 +-
 src/core/hle/service/nvdrv/nvdrv.cpp          | 13 ++++----
 src/core/hle/service/nvdrv/nvdrv.h            |  4 ++-
 src/video_core/gpu.cpp                        | 30 +++++++++----------
 src/video_core/gpu.h                          | 14 +++------
 src/video_core/gpu_asynch.cpp                 |  4 +--
 src/video_core/gpu_asynch.h                   |  2 +-
 src/video_core/gpu_synch.h                    |  2 +-
 12 files changed, 45 insertions(+), 48 deletions(-)

diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
index 463d2916c5..c3fffa8949 100644
--- a/src/core/hardware_interrupt_manager.cpp
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -8,14 +8,17 @@ namespace Core::Hardware {
 
 InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
     gpu_interrupt_event =
-        system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 event_index, s64) {
+        system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) {
             auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
-            nvdrv->SignalGPUInterrupt(static_cast<u32>(event_index));
+            const u32 syncpt = static_cast<u32>(message >> 32);
+            const u32 value = static_cast<u32>(message & 0x00000000FFFFFFFFULL);
+            nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
         });
 }
 
-void InterruptManager::InterruptGPU(const u32 event_index) {
-    system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, static_cast<u64>(event_index));
+void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
+    const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
+    system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg);
 }
 
 } // namespace Core::Hardware
diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h
index fc565c88bf..590392f75f 100644
--- a/src/core/hardware_interrupt_manager.h
+++ b/src/core/hardware_interrupt_manager.h
@@ -14,7 +14,7 @@ public:
     InterruptManager(Core::System& system);
     ~InterruptManager() = default;
 
-    void InterruptGPU(const u32 event_index);
+    void GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value);
 
 private:
     Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 02b078c2f6..d412746168 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -109,7 +109,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         }
         params.value |= event_id;
         events_interface.events[event_id].writable->Clear();
-        gpu.RegisterEvent(event_id, params.syncpt_id, params.threshold);
+        gpu.RegisterSyncptInterrupt(params.syncpt_id, params.threshold);
         std::memcpy(output.data(), &params, sizeof(params));
         gpu.Guard(false);
         return NvResult::Timeout;
@@ -159,9 +159,6 @@ u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8
         return NvResult::BadParameter;
     }
     if (events_interface.status[event_id] == EventState::Waiting) {
-        auto& gpu = system.GPU();
-        gpu.CancelEvent(event_id, events_interface.assigned_syncpt[event_id],
-                        events_interface.assigned_value[event_id]);
         events_interface.LiberateEvent(event_id);
     }
     return NvResult::Success;
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index c548f1223d..a4cb8cb81d 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -15,8 +15,8 @@
 
 namespace Service::Nvidia {
 
-void NVDRV::SignalGPUInterrupt(const u32 event_id) {
-    nvdrv->SignalEvent(event_id);
+void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
+    nvdrv->SignalSyncpt(syncpoint_id, value);
 }
 
 void NVDRV::Open(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 09cf4bb121..10a0ecd52d 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -19,7 +19,7 @@ public:
     NVDRV(std::shared_ptr<Module> nvdrv, const char* name);
     ~NVDRV() override;
 
-    void SignalGPUInterrupt(const u32 event_id);
+    void SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value);
 
 private:
     void Open(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index c68d291776..b87c228bd5 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -89,13 +89,14 @@ ResultCode Module::Close(u32 fd) {
     return RESULT_SUCCESS;
 }
 
-void Module::SignalEvent(const u32 event_id) {
-    if (event_id >= 64) {
-        LOG_ERROR(Service_NVDRV, "Unexpected Event signalled!");
-        return;
+void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
+    for (u32 i = 0; i < MaxNvEvents; i++) {
+        if (events_interface.assigned_syncpt[i] == syncpoint_id &&
+            events_interface.assigned_value[i] == value) {
+            events_interface.LiberateEvent(i);
+            events_interface.events[i].writable->Signal();
+        }
     }
-    events_interface.LiberateEvent(event_id);
-    events_interface.events[event_id].writable->Signal();
 }
 
 Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 597acc9c6f..97b48d150d 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -73,6 +73,8 @@ struct EventsInterface {
     void LiberateEvent(const u32 event_id) {
         status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
         events_mask &= ~(1 << event_id);
+        assigned_syncpt[event_id] = 0xFFFFFFFF;
+        assigned_value[event_id] = 0;
     }
 };
 
@@ -97,7 +99,7 @@ public:
     /// Closes a device file descriptor and returns operation success.
     ResultCode Close(u32 fd);
 
-    void SignalEvent(const u32 event_id);
+    void SignalSyncpt(const u32 syncpoint_id, const u32 value);
 
     Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(const u32 event_id);
 
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 086db0e695..efea23bf26 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -70,13 +70,13 @@ const DmaPusher& GPU::DmaPusher() const {
 void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
     syncpoints[syncpoint_id]++;
     sync_mutex.lock();
-    if (!events[syncpoint_id].empty()) {
+    if (!syncpt_interrupts[syncpoint_id].empty()) {
         u32 value = syncpoints[syncpoint_id].load();
-        auto it = events[syncpoint_id].begin();
-        while (it != events[syncpoint_id].end()) {
-            if (value >= it->value) {
-                TriggerCpuInterrupt(it->event_id);
-                it = events[syncpoint_id].erase(it);
+        auto it = syncpt_interrupts[syncpoint_id].begin();
+        while (it != syncpt_interrupts[syncpoint_id].end()) {
+            if (value >= *it) {
+                TriggerCpuInterrupt(syncpoint_id, *it);
+                it = syncpt_interrupts[syncpoint_id].erase(it);
                 continue;
             }
             it++;
@@ -89,19 +89,19 @@ u32 GPU::GetSyncpointValue(const u32 syncpoint_id) const {
     return syncpoints[syncpoint_id].load();
 }
 
-void GPU::RegisterEvent(const u32 event_id, const u32 syncpoint_id, const u32 value) {
-    for (auto& ev : events[syncpoint_id]) {
-        if (ev.event_id == event_id && ev.value == value)
+void GPU::RegisterSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
+    for (u32 in_value : syncpt_interrupts[syncpoint_id]) {
+        if (in_value == value)
             return;
     }
-    events[syncpoint_id].emplace_back(event_id, value);
+    syncpt_interrupts[syncpoint_id].emplace_back(value);
 }
 
-void GPU::CancelEvent(const u32 event_id, const u32 syncpoint_id, const u32 value) {
-    auto it = events[syncpoint_id].begin();
-    while (it != events[syncpoint_id].end()) {
-        if (value == it->value) {
-            it = events[syncpoint_id].erase(it);
+void GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
+    auto it = syncpt_interrupts[syncpoint_id].begin();
+    while (it != syncpt_interrupts[syncpoint_id].end()) {
+        if (value == *it) {
+            it = syncpt_interrupts[syncpoint_id].erase(it);
             return;
         }
         it++;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 18ac3237ed..9bd618941c 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -172,9 +172,9 @@ public:
 
     u32 GetSyncpointValue(const u32 syncpoint_id) const;
 
-    void RegisterEvent(const u32 event_id, const u32 syncpoint_id, const u32 value);
+    void RegisterSyncptInterrupt(const u32 syncpoint_id, const u32 value);
 
-    void CancelEvent(const u32 event_id, const u32 syncpoint_id, const u32 value);
+    void CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value);
 
     void Guard(bool guard_set) {
         if (guard_set) {
@@ -253,7 +253,7 @@ public:
     virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
 
 protected:
-    virtual void TriggerCpuInterrupt(const u32 event_id) const = 0;
+    virtual void TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const = 0;
 
 private:
     void ProcessBindMethod(const MethodCall& method_call);
@@ -293,13 +293,7 @@ private:
 
     std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{};
 
-    struct Event {
-        Event(const u32 event_id, const u32 value) : event_id(event_id), value(value) {}
-        u32 event_id;
-        u32 value;
-    };
-
-    std::array<std::list<Event>, Service::Nvidia::MaxSyncPoints> events;
+    std::array<std::list<u32>, Service::Nvidia::MaxSyncPoints> syncpt_interrupts;
 
     std::mutex sync_mutex;
 
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 6b6f0f6ec9..ea67be831f 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -40,9 +40,9 @@ void GPUAsynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
     gpu_thread.FlushAndInvalidateRegion(addr, size);
 }
 
-void GPUAsynch::TriggerCpuInterrupt(const u32 event_id) const {
+void GPUAsynch::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
     auto& interrupt_manager = system.InterruptManager();
-    interrupt_manager.InterruptGPU(event_id);
+    interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
 }
 
 } // namespace VideoCommon
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index d49e9b96e7..5f1b2fb7d8 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -28,7 +28,7 @@ public:
     void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
 
 protected:
-    void TriggerCpuInterrupt(const u32 event_id) const override;
+    void TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const override;
 
 private:
     GPUThread::ThreadManager gpu_thread;
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 09bda854a2..58d258503a 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -27,7 +27,7 @@ public:
     void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
 
 protected:
-    void TriggerCpuInterrupt(const u32 event_id) const override {}
+    void TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const override {}
 };
 
 } // namespace VideoCommon

From b6844bec608ed82511738e9f3911e72aeb05243a Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Sun, 16 Jun 2019 11:43:41 -0400
Subject: [PATCH 19/25] NVServices: Correct CtrlEventWaitSync to block the ipc
 until timeout.

---
 src/core/hle/service/nvdrv/devices/nvdevice.h |  4 ++-
 .../service/nvdrv/devices/nvdisp_disp0.cpp    |  3 +-
 .../hle/service/nvdrv/devices/nvdisp_disp0.h  |  3 +-
 .../service/nvdrv/devices/nvhost_as_gpu.cpp   |  3 +-
 .../hle/service/nvdrv/devices/nvhost_as_gpu.h |  3 +-
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 22 +++++++++---
 .../hle/service/nvdrv/devices/nvhost_ctrl.h   |  6 ++--
 .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp |  3 +-
 .../service/nvdrv/devices/nvhost_ctrl_gpu.h   |  3 +-
 .../hle/service/nvdrv/devices/nvhost_gpu.cpp  |  3 +-
 .../hle/service/nvdrv/devices/nvhost_gpu.h    |  3 +-
 .../service/nvdrv/devices/nvhost_nvdec.cpp    |  3 +-
 .../hle/service/nvdrv/devices/nvhost_nvdec.h  |  3 +-
 .../service/nvdrv/devices/nvhost_nvjpg.cpp    |  3 +-
 .../hle/service/nvdrv/devices/nvhost_nvjpg.h  |  3 +-
 .../hle/service/nvdrv/devices/nvhost_vic.cpp  |  3 +-
 .../hle/service/nvdrv/devices/nvhost_vic.h    |  3 +-
 src/core/hle/service/nvdrv/devices/nvmap.cpp  |  3 +-
 src/core/hle/service/nvdrv/devices/nvmap.h    |  3 +-
 src/core/hle/service/nvdrv/interface.cpp      | 34 ++++++++++++++++---
 src/core/hle/service/nvdrv/nvdata.h           |  7 ++++
 src/core/hle/service/nvdrv/nvdrv.cpp          |  9 +++--
 src/core/hle/service/nvdrv/nvdrv.h            |  5 ++-
 23 files changed, 104 insertions(+), 31 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index ed606cd15d..fae69eb199 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -8,6 +8,7 @@
 #include "common/bit_field.h"
 #include "common/common_types.h"
 #include "common/swap.h"
+#include "core/hle/service/nvdrv/nvdata.h"
 
 namespace Core {
 class System;
@@ -37,7 +38,8 @@ public:
      * @param output A buffer where the output data will be written to.
      * @returns The result code of the ioctl.
      */
-    virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0;
+    virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                      IoctlCtrl& ctrl) = 0;
 
 protected:
     Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 3336b2080f..c918b42250 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -17,7 +17,8 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvdisp_disp0 ::~nvdisp_disp0() = default;
 
-u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                        IoctlCtrl& ctrl) {
     UNIMPLEMENTED_MSG("Unimplemented ioctl");
     return 0;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 812967bdf9..e79e490ff0 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -20,7 +20,8 @@ public:
     explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvdisp_disp0() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl) override;
 
     /// Performs a screen flip, drawing the buffer pointed to by the handle.
     void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
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 eccc3f1d95..24ab3f2e9f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -26,7 +26,8 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_
     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvhost_as_gpu::~nvhost_as_gpu() = default;
 
-u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                         IoctlCtrl& ctrl) {
     LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
               command.raw, input.size(), output.size());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index e45d4f1ff4..30ca5f4c3b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -20,7 +20,8 @@ public:
     explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_as_gpu() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl) override;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index d412746168..e46e6b94ca 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -19,7 +19,8 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventsInterface& events_interface
     : nvdevice(system), events_interface{events_interface} {}
 nvhost_ctrl::~nvhost_ctrl() = default;
 
-u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                       IoctlCtrl& ctrl) {
     LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
               command.raw, input.size(), output.size());
 
@@ -27,9 +28,9 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
     case IoctlCommand::IocGetConfigCommand:
         return NvOsGetConfigU32(input, output);
     case IoctlCommand::IocCtrlEventWaitCommand:
-        return IocCtrlEventWait(input, output, false);
+        return IocCtrlEventWait(input, output, false, ctrl);
     case IoctlCommand::IocCtrlEventWaitAsyncCommand:
-        return IocCtrlEventWait(input, output, true);
+        return IocCtrlEventWait(input, output, true, ctrl);
     case IoctlCommand::IocCtrlEventRegisterCommand:
         return IocCtrlEventRegister(input, output);
     case IoctlCommand::IocCtrlEventUnregisterCommand:
@@ -50,7 +51,7 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
 }
 
 u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
-                                  bool is_async) {
+                                  bool is_async, IoctlCtrl& ctrl) {
     IocCtrlEventWaitParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
     LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
@@ -94,7 +95,11 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
             return NvResult::BadParameter;
         }
     } else {
-        event_id = events_interface.GetFreeEvent();
+        if (ctrl.fresh_call) {
+            event_id = events_interface.GetFreeEvent();
+        } else {
+            event_id = ctrl.event_id;
+        }
     }
 
     EventState status = events_interface.status[event_id];
@@ -110,6 +115,13 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         params.value |= event_id;
         events_interface.events[event_id].writable->Clear();
         gpu.RegisterSyncptInterrupt(params.syncpt_id, params.threshold);
+        if (!is_async && ctrl.fresh_call) {
+            ctrl.must_delay = true;
+            ctrl.timeout = params.timeout;
+            ctrl.event_id = event_id;
+            gpu.Guard(false);
+            return NvResult::Timeout;
+        }
         std::memcpy(output.data(), &params, sizeof(params));
         gpu.Guard(false);
         return NvResult::Timeout;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 6cbf75f89c..7cb41aa547 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -17,7 +17,8 @@ public:
     nvhost_ctrl(Core::System& system, EventsInterface& events_interface);
     ~nvhost_ctrl() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl) override;
 
 private:
     enum class IoctlCommand : u32_le {
@@ -133,7 +134,8 @@ private:
 
     u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
 
-    u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
+    u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
+                         IoctlCtrl& ctrl);
 
     u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index c139f2ceba..e3d2b4470e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -15,7 +15,8 @@ namespace Service::Nvidia::Devices {
 nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system){};
 nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
 
-u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                           IoctlCtrl& ctrl) {
     LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
               command.raw, input.size(), output.size());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 65eac47d14..de36cb014d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -16,7 +16,8 @@ public:
     nvhost_ctrl_gpu(Core::System& system);
     ~nvhost_ctrl_gpu() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl) override;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 13e80bfad5..b9e13fae94 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -17,7 +17,8 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
 nvhost_gpu::~nvhost_gpu() = default;
 
-u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                      IoctlCtrl& ctrl) {
     LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
               command.raw, input.size(), output.size());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 106359f876..edc37ff3a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -23,7 +23,8 @@ public:
     explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
     ~nvhost_gpu() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl) override;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 3bfcb84230..f464328f3b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -13,7 +13,8 @@ namespace Service::Nvidia::Devices {
 nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system){};
 nvhost_nvdec::~nvhost_nvdec() = default;
 
-u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                        IoctlCtrl& ctrl) {
     LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
               command.raw, input.size(), output.size());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index febfd4cc4c..c2b7a22f6a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -16,7 +16,8 @@ public:
     nvhost_nvdec(Core::System& system);
     ~nvhost_nvdec() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl) override;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 16c683b476..d4d67fc72d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,7 +13,8 @@ namespace Service::Nvidia::Devices {
 nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system){};
 nvhost_nvjpg::~nvhost_nvjpg() = default;
 
-u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                        IoctlCtrl& ctrl) {
     LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
               command.raw, input.size(), output.size());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 33b149bb73..4bf280d67f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,7 +16,8 @@ public:
     nvhost_nvjpg(Core::System& system);
     ~nvhost_nvjpg() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl) override;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 853136dea1..24e38d31a5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -13,7 +13,8 @@ namespace Service::Nvidia::Devices {
 nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system){};
 nvhost_vic::~nvhost_vic() = default;
 
-u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                      IoctlCtrl& ctrl) {
     LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
               command.raw, input.size(), output.size());
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index b71816ba17..3d0934a788 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -16,7 +16,8 @@ public:
     nvhost_vic(Core::System& system);
     ~nvhost_vic() override;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl) override;
 
 private:
     enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index a75ff334b5..349454685b 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -28,7 +28,8 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
     return object->addr;
 }
 
-u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+                 IoctlCtrl& ctrl) {
     switch (static_cast<IoctlCommand>(command.raw)) {
     case IoctlCommand::Create:
         return IocCreate(input, output);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 623c9b232d..b79ed736c1 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -22,7 +22,8 @@ public:
     /// Returns the allocated address of an nvmap object given its handle.
     VAddr GetObjectAddress(u32 handle) const;
 
-    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+    u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl) override;
 
     /// Represents an nvmap object.
     struct Object {
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index a4cb8cb81d..45912153dc 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -8,6 +8,7 @@
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/thread.h"
 #include "core/hle/kernel/writable_event.h"
 #include "core/hle/service/nvdrv/interface.h"
 #include "core/hle/service/nvdrv/nvdata.h"
@@ -41,11 +42,36 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
 
     std::vector<u8> output(ctx.GetWriteBufferSize());
 
-    IPC::ResponseBuilder rb{ctx, 3};
-    rb.Push(RESULT_SUCCESS);
-    rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output));
+    IoctlCtrl ctrl{};
 
-    ctx.WriteBuffer(output);
+    u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl);
+
+    if (!ctrl.must_delay) {
+        IPC::ResponseBuilder rb{ctx, 3};
+        rb.Push(RESULT_SUCCESS);
+        rb.Push(result);
+
+        ctx.WriteBuffer(output);
+        return;
+    }
+    ctrl.fresh_call = false;
+    ctx.SleepClientThread(
+        "NVServices::DelayedResponse", ctrl.timeout,
+        [this, ctrl = ctrl](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
+                      Kernel::ThreadWakeupReason reason) {
+            IPC::RequestParser rp{ctx};
+            u32 fd = rp.Pop<u32>();
+            u32 command = rp.Pop<u32>();
+            std::vector<u8> output(ctx.GetWriteBufferSize());
+            IoctlCtrl ctrl2{ctrl};
+            u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl2);
+            IPC::ResponseBuilder rb{ctx, 3};
+            rb.Push(RESULT_SUCCESS);
+            rb.Push(result);
+
+            ctx.WriteBuffer(output);
+        },
+        nvdrv->GetEventWriteable(ctrl.event_id));
 }
 
 void NVDRV::Close(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 6dbc90e4c7..22b1dc79b6 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -34,4 +34,11 @@ enum class EventState {
     Busy = 3,
 };
 
+struct IoctlCtrl {
+    bool fresh_call{true};
+    bool must_delay{};
+    s64 timeout{};
+    s32 event_id{-1};
+};
+
 } // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index b87c228bd5..598a1123b1 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -71,12 +71,13 @@ u32 Module::Open(const std::string& device_name) {
     return fd;
 }
 
-u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
+u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
+                  IoctlCtrl& ctrl) {
     auto itr = open_files.find(fd);
     ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
 
     auto& device = itr->second;
-    return device->ioctl({command}, input, output);
+    return device->ioctl({command}, input, output, ctrl);
 }
 
 ResultCode Module::Close(u32 fd) {
@@ -103,4 +104,8 @@ Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) {
     return events_interface.events[event_id].readable;
 }
 
+Kernel::SharedPtr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) {
+    return events_interface.events[event_id].writable;
+}
+
 } // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 97b48d150d..b7f6929624 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -95,7 +95,8 @@ public:
     /// Opens a device node and returns a file descriptor to it.
     u32 Open(const std::string& device_name);
     /// Sends an ioctl command to the specified file descriptor.
-    u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output);
+    u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
+              IoctlCtrl& ctrl);
     /// Closes a device file descriptor and returns operation success.
     ResultCode Close(u32 fd);
 
@@ -103,6 +104,8 @@ public:
 
     Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(const u32 event_id);
 
+    Kernel::SharedPtr<Kernel::WritableEvent> GetEventWriteable(const u32 event_id);
+
 private:
     /// Id to use for the next open file descriptor.
     u32 next_fd = 1;

From 0335a25d1fcca5328ef79b3c62edb679df63ffba Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Mon, 17 Jun 2019 15:27:42 -0400
Subject: [PATCH 20/25] NVServices: Make NVEvents Automatic according to
 documentation.

---
 src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 7 +++++--
 src/core/hle/service/nvdrv/nvdrv.cpp               | 4 ++--
 src/video_core/gpu.cpp                             | 7 +++++--
 src/video_core/gpu.h                               | 2 +-
 4 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index e46e6b94ca..ffa6e75c76 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -142,7 +142,6 @@ u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<
         return NvResult::BadParameter;
     }
     events_interface.RegisterEvent(event_id);
-    events_interface.events[event_id].writable->Signal();
     return NvResult::Success;
 }
 
@@ -171,7 +170,11 @@ u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8
         return NvResult::BadParameter;
     }
     if (events_interface.status[event_id] == EventState::Waiting) {
-        events_interface.LiberateEvent(event_id);
+        auto& gpu = system.GPU();
+        if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
+                                      events_interface.assigned_value[event_id])) {
+            events_interface.LiberateEvent(event_id);
+        }
     }
     return NvResult::Success;
 }
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 598a1123b1..8958e21e38 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -40,8 +40,8 @@ Module::Module(Core::System& system) {
     auto& kernel = system.Kernel();
     for (u32 i = 0; i < MaxNvEvents; i++) {
         std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
-        events_interface.events[i] =
-            Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, event_label);
+        events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(
+            kernel, Kernel::ResetType::Automatic, event_label);
         events_interface.status[i] = EventState::Free;
         events_interface.registered[i] = false;
     }
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index efea23bf26..cdb2f804ef 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -97,15 +97,18 @@ void GPU::RegisterSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
     syncpt_interrupts[syncpoint_id].emplace_back(value);
 }
 
-void GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
+bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
+    sync_mutex.lock();
     auto it = syncpt_interrupts[syncpoint_id].begin();
     while (it != syncpt_interrupts[syncpoint_id].end()) {
         if (value == *it) {
             it = syncpt_interrupts[syncpoint_id].erase(it);
-            return;
+            return true;
         }
         it++;
     }
+    return false;
+    sync_mutex.unlock();
 }
 
 u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 9bd618941c..94afc91f85 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -174,7 +174,7 @@ public:
 
     void RegisterSyncptInterrupt(const u32 syncpoint_id, const u32 value);
 
-    void CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value);
+    bool CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value);
 
     void Guard(bool guard_set) {
         if (guard_set) {

From b391e5f6386eecf6170b544245e3e4e31427913c Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Tue, 18 Jun 2019 16:58:29 -0400
Subject: [PATCH 21/25] NVFlinger: Correct GCC compile error

---
 src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp |  1 +
 src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp  |  8 +++-----
 src/core/hle/service/nvdrv/devices/nvhost_gpu.h    |  2 +-
 src/core/hle/service/nvdrv/interface.cpp           |  4 ++--
 src/core/hle/service/nvflinger/buffer_queue.h      |  2 +-
 src/core/hle/service/nvflinger/nvflinger.cpp       | 14 +++++++-------
 src/video_core/gpu.cpp                             |  6 ++----
 src/video_core/gpu_thread.cpp                      |  6 ++++--
 8 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index ffa6e75c76..a5a4f8c7b8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -174,6 +174,7 @@ u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8
         if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
                                       events_interface.assigned_value[event_id])) {
             events_interface.LiberateEvent(event_id);
+            events_interface.events[event_id].writable->Signal();
         }
     }
     return NvResult::Success;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index b9e13fae94..241dac8817 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -122,9 +122,9 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
                 params.unk3);
 
     auto& gpu = system.GPU();
-    params.fence_out.id = channels;
-    params.fence_out.value = gpu.GetSyncpointValue(channels);
-    channels++;
+    params.fence_out.id = assigned_syncpoints;
+    params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints);
+    assigned_syncpoints++;
     std::memcpy(output.data(), &params, output.size());
     return 0;
 }
@@ -169,8 +169,6 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
     }
     gpu.PushGPUEntries(std::move(entries));
 
-    // TODO(Blinkhawk): Figure how thoios fence is set
-    // params.fence_out.value = 0;
     std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
     return 0;
 }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index edc37ff3a6..3ad8e1db10 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -191,7 +191,7 @@ private:
     u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
 
     std::shared_ptr<nvmap> nvmap_dev;
-    u32 channels{};
+    u32 assigned_syncpoints{};
 };
 
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 45912153dc..6f7c7502a3 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -57,8 +57,8 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
     ctrl.fresh_call = false;
     ctx.SleepClientThread(
         "NVServices::DelayedResponse", ctrl.timeout,
-        [this, ctrl = ctrl](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
-                      Kernel::ThreadWakeupReason reason) {
+        [this, ctrl = ctrl](Kernel::SharedPtr<Kernel::Thread> thread,
+                            Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
             IPC::RequestParser rp{ctx};
             u32 fd = rp.Pop<u32>();
             u32 command = rp.Pop<u32>();
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index be993ee617..356bedb814 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -4,9 +4,9 @@
 
 #pragma once
 
+#include <list>
 #include <optional>
 #include <vector>
-#include <list>
 
 #include "common/common_funcs.h"
 #include "common/math_util.h"
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index a7937b4904..70441f6a29 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -37,14 +37,14 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
     displays.emplace_back(4, "Null");
 
     // Schedule the screen composition events
-    //const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
+    // const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
 
-    composition_event = core_timing.RegisterEvent(
-        "ScreenComposition", [this](u64 userdata, s64 cycles_late) {
-            Compose();
-            const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
-            this->core_timing.ScheduleEvent(std::max(0LL,ticks - cycles_late), composition_event);
-        });
+    composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata,
+                                                                              s64 cycles_late) {
+        Compose();
+        const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
+        this->core_timing.ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), composition_event);
+    });
 
     core_timing.ScheduleEvent(frame_ticks, composition_event);
 }
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index cdb2f804ef..278528618a 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -69,7 +69,7 @@ const DmaPusher& GPU::DmaPusher() const {
 
 void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
     syncpoints[syncpoint_id]++;
-    sync_mutex.lock();
+    std::lock_guard lock{sync_mutex};
     if (!syncpt_interrupts[syncpoint_id].empty()) {
         u32 value = syncpoints[syncpoint_id].load();
         auto it = syncpt_interrupts[syncpoint_id].begin();
@@ -82,7 +82,6 @@ void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
             it++;
         }
     }
-    sync_mutex.unlock();
 }
 
 u32 GPU::GetSyncpointValue(const u32 syncpoint_id) const {
@@ -98,7 +97,7 @@ void GPU::RegisterSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
 }
 
 bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
-    sync_mutex.lock();
+    std::lock_guard lock{sync_mutex};
     auto it = syncpt_interrupts[syncpoint_id].begin();
     while (it != syncpt_interrupts[syncpoint_id].end()) {
         if (value == *it) {
@@ -108,7 +107,6 @@ bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
         it++;
     }
     return false;
-    sync_mutex.unlock();
 }
 
 u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index b87938fdd5..b441e92b0a 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -21,7 +21,8 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
     MicroProfileOnThreadCreate("GpuThread");
 
     // Wait for first GPU command before acquiring the window context
-    while (state.queue.Empty());
+    while (state.queue.Empty())
+        ;
 
     // If emulation was stopped during disk shader loading, abort before trying to acquire context
     if (!state.is_running) {
@@ -103,7 +104,8 @@ u64 ThreadManager::PushCommand(CommandData&& command_data) {
 
 MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
 void SynchState::WaitForSynchronization(u64 fence) {
-    while (signaled_fence.load() < fence);
+    while (signaled_fence.load() < fence)
+        ;
 }
 
 } // namespace VideoCommon::GPUThread

From d20ede40b1e9cd0539982fb1feb3b13af3501ea2 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Tue, 18 Jun 2019 20:53:21 -0400
Subject: [PATCH 22/25] NVServices: Styling, define constructors as explicit
 and corrections

---
 src/core/hardware_interrupt_manager.cpp       |  8 +++++-
 src/core/hardware_interrupt_manager.h         | 15 ++++++++---
 src/core/hle/service/nvdrv/devices/nvdevice.h |  2 +-
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 10 ++------
 .../hle/service/nvdrv/devices/nvhost_ctrl.h   |  4 +--
 .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp |  2 +-
 .../service/nvdrv/devices/nvhost_ctrl_gpu.h   |  2 +-
 .../service/nvdrv/devices/nvhost_nvdec.cpp    |  2 +-
 .../hle/service/nvdrv/devices/nvhost_nvdec.h  |  2 +-
 .../service/nvdrv/devices/nvhost_nvjpg.cpp    |  2 +-
 .../hle/service/nvdrv/devices/nvhost_nvjpg.h  |  2 +-
 .../hle/service/nvdrv/devices/nvhost_vic.cpp  |  2 +-
 .../hle/service/nvdrv/devices/nvhost_vic.h    |  2 +-
 src/core/hle/service/nvdrv/devices/nvmap.cpp  |  2 +-
 src/core/hle/service/nvdrv/devices/nvmap.h    |  2 +-
 src/core/hle/service/nvdrv/nvdrv.cpp          |  4 +--
 src/core/hle/service/nvdrv/nvdrv.h            | 16 ++++++------
 .../hle/service/nvflinger/buffer_queue.cpp    |  2 +-
 src/core/hle/service/nvflinger/nvflinger.cpp  |  7 +++---
 src/core/hle/service/nvflinger/nvflinger.h    |  2 +-
 src/video_core/gpu.cpp                        | 25 +++++++++++--------
 src/video_core/gpu.h                          | 18 ++++++-------
 src/video_core/gpu_asynch.h                   |  2 +-
 src/video_core/gpu_synch.h                    |  3 ++-
 24 files changed, 73 insertions(+), 65 deletions(-)

diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
index c3fffa8949..c2115db2de 100644
--- a/src/core/hardware_interrupt_manager.cpp
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -1,5 +1,9 @@
+// Copyright 2019 Yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
 
 #include "core/core.h"
+#include "core/core_timing.h"
 #include "core/hardware_interrupt_manager.h"
 #include "core/hle/service/nvdrv/interface.h"
 #include "core/hle/service/sm/sm.h"
@@ -11,11 +15,13 @@ InterruptManager::InterruptManager(Core::System& system_in) : system(system_in)
         system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) {
             auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
             const u32 syncpt = static_cast<u32>(message >> 32);
-            const u32 value = static_cast<u32>(message & 0x00000000FFFFFFFFULL);
+            const u32 value = static_cast<u32>(message);
             nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
         });
 }
 
+InterruptManager::~InterruptManager() = default;
+
 void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
     const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
     system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg);
diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h
index 590392f75f..494db883ad 100644
--- a/src/core/hardware_interrupt_manager.h
+++ b/src/core/hardware_interrupt_manager.h
@@ -1,20 +1,27 @@
+// Copyright 2019 Yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
 #pragma once
 
 #include "common/common_types.h"
-#include "core/core_timing.h"
 
 namespace Core {
 class System;
 }
 
+namespace Core::Timing {
+struct EventType;
+}
+
 namespace Core::Hardware {
 
 class InterruptManager {
 public:
-    InterruptManager(Core::System& system);
-    ~InterruptManager() = default;
+    explicit InterruptManager(Core::System& system);
+    ~InterruptManager();
 
-    void GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value);
+    void GPUInterruptSyncpt(u32 syncpoint_id, u32 value);
 
 private:
     Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index fae69eb199..5b8248433e 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -20,7 +20,7 @@ namespace Service::Nvidia::Devices {
 /// implement the ioctl interface.
 class nvdevice {
 public:
-    nvdevice(Core::System& system) : system{system} {};
+    explicit nvdevice(Core::System& system) : system{system} {};
     virtual ~nvdevice() = default;
     union Ioctl {
         u32_le raw;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index a5a4f8c7b8..749aa71d46 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -15,7 +15,7 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_ctrl::nvhost_ctrl(Core::System& system, EventsInterface& events_interface)
+nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
     : nvdevice(system), events_interface{events_interface} {}
 nvhost_ctrl::~nvhost_ctrl() = default;
 
@@ -67,12 +67,11 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
     if (!gpu.IsAsync()) {
         return NvResult::Success;
     }
-    gpu.Guard(true);
+    auto lock = gpu.LockSync();
     u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
     if (current_syncpoint_value >= params.threshold) {
         params.value = current_syncpoint_value;
         std::memcpy(output.data(), &params, sizeof(params));
-        gpu.Guard(false);
         return NvResult::Success;
     }
 
@@ -82,7 +81,6 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
 
     if (params.timeout == 0) {
         std::memcpy(output.data(), &params, sizeof(params));
-        gpu.Guard(false);
         return NvResult::Timeout;
     }
 
@@ -91,7 +89,6 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         event_id = params.value & 0x00FF;
         if (event_id >= 64) {
             std::memcpy(output.data(), &params, sizeof(params));
-            gpu.Guard(false);
             return NvResult::BadParameter;
         }
     } else {
@@ -119,15 +116,12 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
             ctrl.must_delay = true;
             ctrl.timeout = params.timeout;
             ctrl.event_id = event_id;
-            gpu.Guard(false);
             return NvResult::Timeout;
         }
         std::memcpy(output.data(), &params, sizeof(params));
-        gpu.Guard(false);
         return NvResult::Timeout;
     }
     std::memcpy(output.data(), &params, sizeof(params));
-    gpu.Guard(false);
     return NvResult::BadParameter;
 }
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 7cb41aa547..14e6e7e57a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -14,7 +14,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_ctrl final : public nvdevice {
 public:
-    nvhost_ctrl(Core::System& system, EventsInterface& events_interface);
+    explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
     ~nvhost_ctrl() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
@@ -143,7 +143,7 @@ private:
 
     u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output);
 
-    EventsInterface& events_interface;
+    EventInterface& events_interface;
 };
 
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index e3d2b4470e..988effd90b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -12,7 +12,7 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system){};
+nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
 nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
 
 u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index de36cb014d..2b035ae3f9 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_ctrl_gpu final : public nvdevice {
 public:
-    nvhost_ctrl_gpu(Core::System& system);
+    explicit nvhost_ctrl_gpu(Core::System& system);
     ~nvhost_ctrl_gpu() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index f464328f3b..f572ad30f3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -10,7 +10,7 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system){};
+nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
 nvhost_nvdec::~nvhost_nvdec() = default;
 
 u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index c2b7a22f6a..2710f05113 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_nvdec final : public nvdevice {
 public:
-    nvhost_nvdec(Core::System& system);
+    explicit nvhost_nvdec(Core::System& system);
     ~nvhost_nvdec() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index d4d67fc72d..38282956fb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -10,7 +10,7 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system){};
+nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
 nvhost_nvjpg::~nvhost_nvjpg() = default;
 
 u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 4bf280d67f..3797666936 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_nvjpg final : public nvdevice {
 public:
-    nvhost_nvjpg(Core::System& system);
+    explicit nvhost_nvjpg(Core::System& system);
     ~nvhost_nvjpg() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 24e38d31a5..70e8091dbc 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -10,7 +10,7 @@
 
 namespace Service::Nvidia::Devices {
 
-nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system){};
+nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
 nvhost_vic::~nvhost_vic() = default;
 
 u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 3d0934a788..7d111977e3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices {
 
 class nvhost_vic final : public nvdevice {
 public:
-    nvhost_vic(Core::System& system);
+    explicit nvhost_vic(Core::System& system);
     ~nvhost_vic() override;
 
     u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 349454685b..223b496b74 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -18,7 +18,7 @@ enum {
 };
 }
 
-nvmap::nvmap(Core::System& system) : nvdevice(system){};
+nvmap::nvmap(Core::System& system) : nvdevice(system) {}
 nvmap::~nvmap() = default;
 
 VAddr nvmap::GetObjectAddress(u32 handle) const {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index b79ed736c1..bf4a101c22 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -16,7 +16,7 @@ namespace Service::Nvidia::Devices {
 
 class nvmap final : public nvdevice {
 public:
-    nvmap(Core::System& system);
+    explicit nvmap(Core::System& system);
     ~nvmap() override;
 
     /// Returns the allocated address of an nvmap object given its handle.
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 8958e21e38..2011a226af 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -100,11 +100,11 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
     }
 }
 
-Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) {
+Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
     return events_interface.events[event_id].readable;
 }
 
-Kernel::SharedPtr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) {
+Kernel::SharedPtr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
     return events_interface.events[event_id].writable;
 }
 
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index b7f6929624..8f7c59a21f 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -26,14 +26,15 @@ namespace Devices {
 class nvdevice;
 }
 
-struct EventsInterface {
+struct EventInterface {
     u64 events_mask{};
     std::array<Kernel::EventPair, MaxNvEvents> events;
     std::array<EventState, MaxNvEvents> status{};
     std::array<bool, MaxNvEvents> registered{};
     std::array<u32, MaxNvEvents> assigned_syncpt{};
     std::array<u32, MaxNvEvents> assigned_value{};
-    u32 GetFreeEvent() {
+    static constexpr u32 null_event = 0xFFFFFFFF;
+    u32 GetFreeEvent() const {
         u64 mask = events_mask;
         for (u32 i = 0; i < MaxNvEvents; i++) {
             const bool is_free = (mask & 0x1) == 0;
@@ -44,12 +45,13 @@ struct EventsInterface {
             }
             mask = mask >> 1;
         }
-        return 0xFFFFFFFF;
+        return null_event;
     }
     void SetEventStatus(const u32 event_id, EventState new_status) {
         EventState old_status = status[event_id];
-        if (old_status == new_status)
+        if (old_status == new_status) {
             return;
+        }
         status[event_id] = new_status;
         if (new_status == EventState::Registered) {
             registered[event_id] = true;
@@ -102,9 +104,9 @@ public:
 
     void SignalSyncpt(const u32 syncpoint_id, const u32 value);
 
-    Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(const u32 event_id);
+    Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(u32 event_id) const;
 
-    Kernel::SharedPtr<Kernel::WritableEvent> GetEventWriteable(const u32 event_id);
+    Kernel::SharedPtr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
 
 private:
     /// Id to use for the next open file descriptor.
@@ -116,7 +118,7 @@ private:
     /// Mapping of device node names to their implementation.
     std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
 
-    EventsInterface events_interface;
+    EventInterface events_interface;
 };
 
 /// Registers all NVDRV services with the specified service manager.
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index d8aa3f1c0a..ddc224f2c3 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -79,7 +79,7 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
 }
 
 std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
-    std::vector<Buffer>::iterator itr = queue.end();
+    auto itr = queue.end();
     while (itr == queue.end() && !queue_sequence.empty()) {
         u32 slot = queue_sequence.front();
         itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 70441f6a29..f9db79370c 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -37,8 +37,6 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t
     displays.emplace_back(4, "Null");
 
     // Schedule the screen composition events
-    // const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks;
-
     composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata,
                                                                               s64 cycles_late) {
         Compose();
@@ -212,8 +210,9 @@ void NVFlinger::Compose() {
     }
 }
 
-s64 NVFlinger::GetNextTicks() {
-    return (Core::Timing::BASE_CLOCK_RATE * (1LL << swap_interval)) / 120;
+s64 NVFlinger::GetNextTicks() const {
+    constexpr s64 max_hertz = 120LL;
+    return (Core::Timing::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz;
 }
 
 } // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 86b94302ce..988be87264 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -74,7 +74,7 @@ public:
     /// finished.
     void Compose();
 
-    s64 GetNextTicks();
+    s64 GetNextTicks() const;
 
 private:
     /// Finds the display identified by the specified ID.
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 278528618a..da8c715b66 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -89,24 +89,27 @@ u32 GPU::GetSyncpointValue(const u32 syncpoint_id) const {
 }
 
 void GPU::RegisterSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
-    for (u32 in_value : syncpt_interrupts[syncpoint_id]) {
-        if (in_value == value)
-            return;
+    auto& interrupt = syncpt_interrupts[syncpoint_id];
+    bool contains = std::any_of(interrupt.begin(), interrupt.end(),
+                                [value](u32 in_value) { return in_value == value; });
+    if (contains) {
+        return;
     }
     syncpt_interrupts[syncpoint_id].emplace_back(value);
 }
 
 bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) {
     std::lock_guard lock{sync_mutex};
-    auto it = syncpt_interrupts[syncpoint_id].begin();
-    while (it != syncpt_interrupts[syncpoint_id].end()) {
-        if (value == *it) {
-            it = syncpt_interrupts[syncpoint_id].erase(it);
-            return true;
-        }
-        it++;
+    auto& interrupt = syncpt_interrupts[syncpoint_id];
+    const auto iter =
+        std::find_if(interrupt.begin(), interrupt.end(),
+                     [value](u32 interrupt_value) { return value == interrupt_value; });
+
+    if (iter == interrupt.end()) {
+        return false;
     }
-    return false;
+    interrupt.erase(iter);
+    return true;
 }
 
 u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 94afc91f85..334dec48cd 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -168,20 +168,16 @@ public:
     /// Returns a reference to the GPU DMA pusher.
     Tegra::DmaPusher& DmaPusher();
 
-    void IncrementSyncPoint(const u32 syncpoint_id);
+    void IncrementSyncPoint(u32 syncpoint_id);
 
-    u32 GetSyncpointValue(const u32 syncpoint_id) const;
+    u32 GetSyncpointValue(u32 syncpoint_id) const;
 
-    void RegisterSyncptInterrupt(const u32 syncpoint_id, const u32 value);
+    void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value);
 
-    bool CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value);
+    bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value);
 
-    void Guard(bool guard_set) {
-        if (guard_set) {
-            sync_mutex.lock();
-        } else {
-            sync_mutex.unlock();
-        }
+    std::unique_lock<std::mutex> LockSync() {
+        return std::unique_lock{sync_mutex};
     }
 
     bool IsAsync() const {
@@ -253,7 +249,7 @@ public:
     virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0;
 
 protected:
-    virtual void TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const = 0;
+    virtual void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const = 0;
 
 private:
     void ProcessBindMethod(const MethodCall& method_call);
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 5f1b2fb7d8..36377d6777 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -28,7 +28,7 @@ public:
     void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
 
 protected:
-    void TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const override;
+    void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override;
 
 private:
     GPUThread::ThreadManager gpu_thread;
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 58d258503a..07bcc47f1c 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -27,7 +27,8 @@ public:
     void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
 
 protected:
-    void TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const override {}
+    void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
+                             [[maybe_unused]] u32 value) const override {}
 };
 
 } // namespace VideoCommon

From f3a39e0c9ce8468859da12e35a52fa088e264d28 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Mon, 1 Jul 2019 11:10:27 -0400
Subject: [PATCH 23/25] NVServices: Address Feedback

---
 .../service/nvdrv/devices/nvdisp_disp0.cpp    |  5 ++--
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 14 ++++++++---
 .../hle/service/nvdrv/devices/nvhost_gpu.h    |  5 +---
 src/core/hle/service/nvdrv/interface.cpp      |  2 +-
 src/core/hle/service/nvdrv/nvdata.h           |  4 ++++
 src/core/hle/service/nvdrv/nvdrv.h            | 23 +++++++++++++------
 .../hle/service/nvflinger/buffer_queue.cpp    |  1 +
 src/core/hle/service/vi/vi.cpp                |  5 ++--
 8 files changed, 38 insertions(+), 21 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index c918b42250..76494f0b7f 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -36,9 +36,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
         addr,      offset,   width, height, stride, static_cast<PixelFormat>(format),
         transform, crop_rect};
 
-    auto& instance = system;
-    instance.GetPerfStats().EndGameFrame();
-    instance.GPU().SwapBuffers(framebuffer);
+    system.GetPerfStats().EndGameFrame();
+    system.GPU().SwapBuffers(framebuffer);
 }
 
 } // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 749aa71d46..1f178d17d4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -87,13 +87,19 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
     u32 event_id;
     if (is_async) {
         event_id = params.value & 0x00FF;
-        if (event_id >= 64) {
+        if (event_id >= MaxNvEvents) {
             std::memcpy(output.data(), &params, sizeof(params));
             return NvResult::BadParameter;
         }
     } else {
         if (ctrl.fresh_call) {
-            event_id = events_interface.GetFreeEvent();
+            const auto result = events_interface.GetFreeEvent();
+            if (result) {
+                event_id = *result;
+            } else {
+                LOG_CRITICAL(Service_NVDRV, "No Free Events available!");
+                event_id = params.value & 0x00FF;
+            }
         } else {
             event_id = ctrl.event_id;
         }
@@ -129,6 +135,7 @@ u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<
     IocCtrlEventRegisterParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
     const u32 event_id = params.user_event_id & 0x00FF;
+    LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
     if (event_id >= MaxNvEvents) {
         return NvResult::BadParameter;
     }
@@ -143,6 +150,7 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
     IocCtrlEventUnregisterParams params{};
     std::memcpy(&params, input.data(), sizeof(params));
     const u32 event_id = params.user_event_id & 0x00FF;
+    LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
     if (event_id >= MaxNvEvents) {
         return NvResult::BadParameter;
     }
@@ -159,7 +167,7 @@ u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8
     // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
     // It is believed from RE to cancel the GPU Event. However, better research is required
     u32 event_id = params.user_event_id & 0x00FF;
-    LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
+    LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
     if (event_id >= MaxNvEvents) {
         return NvResult::BadParameter;
     }
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 3ad8e1db10..d2e8fbae9a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -10,6 +10,7 @@
 #include "common/common_types.h"
 #include "common/swap.h"
 #include "core/hle/service/nvdrv/devices/nvdevice.h"
+#include "core/hle/service/nvdrv/nvdata.h"
 
 namespace Service::Nvidia::Devices {
 
@@ -114,10 +115,6 @@ private:
     static_assert(sizeof(IoctlGetErrorNotification) == 16,
                   "IoctlGetErrorNotification is incorrect size");
 
-    struct Fence {
-        u32_le id;
-        u32_le value;
-    };
     static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
 
     struct IoctlAllocGpfifoEx {
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 6f7c7502a3..b8877b6cb8 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -103,7 +103,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 3, 1};
     rb.Push(RESULT_SUCCESS);
-    if (event_id < 64) {
+    if (event_id < MaxNvEvents) {
         rb.PushCopyObjects(nvdrv->GetEvent(event_id));
         rb.Push<u32>(NvResult::Success);
     } else {
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 22b1dc79b6..ac03cbc232 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -35,9 +35,13 @@ enum class EventState {
 };
 
 struct IoctlCtrl {
+    // First call done to the servioce for services that call itself again after a call.
     bool fresh_call{true};
+    // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
     bool must_delay{};
+    // Timeout for the delay
     s64 timeout{};
+    // NV Event Id
     s32 event_id{-1};
 };
 
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 8f7c59a21f..a339ab672b 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -27,25 +27,34 @@ class nvdevice;
 }
 
 struct EventInterface {
+    // Mask representing currently busy events
     u64 events_mask{};
+    // Each kernel event associated to an NV event
     std::array<Kernel::EventPair, MaxNvEvents> events;
+    // The status of the current NVEvent
     std::array<EventState, MaxNvEvents> status{};
+    // Tells if an NVEvent is registered or not
     std::array<bool, MaxNvEvents> registered{};
+    // When an NVEvent is waiting on GPU interrupt, this is the sync_point
+    // associated with it.
     std::array<u32, MaxNvEvents> assigned_syncpt{};
+    // This is the value of the GPU interrupt for which the NVEvent is waiting
+    // for.
     std::array<u32, MaxNvEvents> assigned_value{};
-    static constexpr u32 null_event = 0xFFFFFFFF;
-    u32 GetFreeEvent() const {
+    // Constant to denote an unasigned syncpoint.
+    static constexpr u32 unassigned_syncpt = 0xFFFFFFFF;
+    std::optional<u32> GetFreeEvent() const {
         u64 mask = events_mask;
         for (u32 i = 0; i < MaxNvEvents; i++) {
             const bool is_free = (mask & 0x1) == 0;
             if (is_free) {
                 if (status[i] == EventState::Registered || status[i] == EventState::Free) {
-                    return i;
+                    return {i};
                 }
             }
             mask = mask >> 1;
         }
-        return null_event;
+        return {};
     }
     void SetEventStatus(const u32 event_id, EventState new_status) {
         EventState old_status = status[event_id];
@@ -57,7 +66,7 @@ struct EventInterface {
             registered[event_id] = true;
         }
         if (new_status == EventState::Waiting || new_status == EventState::Busy) {
-            events_mask |= (1 << event_id);
+            events_mask |= (1ULL << event_id);
         }
     }
     void RegisterEvent(const u32 event_id) {
@@ -74,8 +83,8 @@ struct EventInterface {
     }
     void LiberateEvent(const u32 event_id) {
         status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
-        events_mask &= ~(1 << event_id);
-        assigned_syncpt[event_id] = 0xFFFFFFFF;
+        events_mask &= ~(1ULL << event_id);
+        assigned_syncpt[event_id] = unassigned_syncpt;
         assigned_value[event_id] = 0;
     }
 };
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index ddc224f2c3..e1a07d3eef 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -80,6 +80,7 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
 
 std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
     auto itr = queue.end();
+    // Iterate to find a queued buffer matching the requested slot.
     while (itr == queue.end() && !queue_sequence.empty()) {
         u32 slot = queue_sequence.front();
         itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 894bcdc042..199b306357 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -536,7 +536,7 @@ private:
 
             if (result) {
                 // Buffer is available
-                IGBPDequeueBufferResponseParcel response{(*result).first, *(*result).second};
+                IGBPDequeueBufferResponseParcel response{result->first, *result->second};
                 ctx.WriteBuffer(response.Serialize());
             } else {
                 // Wait the current thread until a buffer becomes available
@@ -549,8 +549,7 @@ private:
                         auto result = buffer_queue.DequeueBuffer(width, height);
                         ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
 
-                        IGBPDequeueBufferResponseParcel response{(*result).first,
-                                                                 *(*result).second};
+                        IGBPDequeueBufferResponseParcel response{result->first, *result->second};
                         ctx.WriteBuffer(response.Serialize());
                         IPC::ResponseBuilder rb{ctx, 2};
                         rb.Push(RESULT_SUCCESS);

From 8c91d5c16614ac42efee7c1858cb338d65861154 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Thu, 4 Jul 2019 09:55:50 -0400
Subject: [PATCH 24/25] Nv_Host_Ctrl: Correct difference calculation

---
 src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 1f178d17d4..9a66a5f880 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -68,12 +68,14 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         return NvResult::Success;
     }
     auto lock = gpu.LockSync();
-    u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
-    if (current_syncpoint_value >= params.threshold) {
+    const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
+    const s32 diff = current_syncpoint_value - params.threshold;
+    if (diff >= 0) {
         params.value = current_syncpoint_value;
         std::memcpy(output.data(), &params, sizeof(params));
         return NvResult::Success;
     }
+    const u32 target_value = current_syncpoint_value - diff;
 
     if (!is_async) {
         params.value = 0;
@@ -109,7 +111,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
     if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) {
         events_interface.SetEventStatus(event_id, EventState::Waiting);
         events_interface.assigned_syncpt[event_id] = params.syncpt_id;
-        events_interface.assigned_value[event_id] = params.threshold;
+        events_interface.assigned_value[event_id] = target_value;
         if (is_async) {
             params.value = params.syncpt_id << 4;
         } else {
@@ -117,7 +119,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
         }
         params.value |= event_id;
         events_interface.events[event_id].writable->Clear();
-        gpu.RegisterSyncptInterrupt(params.syncpt_id, params.threshold);
+        gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
         if (!is_async && ctrl.fresh_call) {
             ctrl.must_delay = true;
             ctrl.timeout = params.timeout;
@@ -167,7 +169,7 @@ u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8
     // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
     // It is believed from RE to cancel the GPU Event. However, better research is required
     u32 event_id = params.user_event_id & 0x00FF;
-    LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
+    LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
     if (event_id >= MaxNvEvents) {
         return NvResult::BadParameter;
     }

From 0fc98958a3efdc30e2d6ece5a2654df0987ce7ac Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Thu, 4 Jul 2019 10:19:25 -0400
Subject: [PATCH 25/25] NVServices: Correct delayed responses.

---
 src/core/hle/service/nvdrv/interface.cpp | 43 +++++++++++-------------
 1 file changed, 19 insertions(+), 24 deletions(-)

diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index b8877b6cb8..d5be64ed28 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -46,32 +46,27 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
 
     u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl);
 
-    if (!ctrl.must_delay) {
-        IPC::ResponseBuilder rb{ctx, 3};
-        rb.Push(RESULT_SUCCESS);
-        rb.Push(result);
-
+    if (ctrl.must_delay) {
+        ctrl.fresh_call = false;
+        ctx.SleepClientThread(
+            "NVServices::DelayedResponse", ctrl.timeout,
+            [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
+                Kernel::ThreadWakeupReason reason) {
+                IoctlCtrl ctrl2{ctrl};
+                std::vector<u8> output2 = output;
+                u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2);
+                ctx.WriteBuffer(output2);
+                IPC::ResponseBuilder rb{ctx, 3};
+                rb.Push(RESULT_SUCCESS);
+                rb.Push(result);
+            },
+            nvdrv->GetEventWriteable(ctrl.event_id));
+    } else {
         ctx.WriteBuffer(output);
-        return;
     }
-    ctrl.fresh_call = false;
-    ctx.SleepClientThread(
-        "NVServices::DelayedResponse", ctrl.timeout,
-        [this, ctrl = ctrl](Kernel::SharedPtr<Kernel::Thread> thread,
-                            Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
-            IPC::RequestParser rp{ctx};
-            u32 fd = rp.Pop<u32>();
-            u32 command = rp.Pop<u32>();
-            std::vector<u8> output(ctx.GetWriteBufferSize());
-            IoctlCtrl ctrl2{ctrl};
-            u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl2);
-            IPC::ResponseBuilder rb{ctx, 3};
-            rb.Push(RESULT_SUCCESS);
-            rb.Push(result);
-
-            ctx.WriteBuffer(output);
-        },
-        nvdrv->GetEventWriteable(ctrl.event_id));
+    IPC::ResponseBuilder rb{ctx, 3};
+    rb.Push(RESULT_SUCCESS);
+    rb.Push(result);
 }
 
 void NVDRV::Close(Kernel::HLERequestContext& ctx) {