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());