summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2021-11-01 00:51:29 +0100
committerGravatar Fernando Sahmkow2022-10-06 21:00:51 +0200
commit39a5ce4e696716e48bdd1c980abc792827c68184 (patch)
tree9db2419586537a30374a86e99402e2eeced4cd20 /src
parentNvHost: Try a different approach to blocking. (diff)
downloadyuzu-39a5ce4e696716e48bdd1c980abc792827c68184.tar.gz
yuzu-39a5ce4e696716e48bdd1c980abc792827c68184.tar.xz
yuzu-39a5ce4e696716e48bdd1c980abc792827c68184.zip
NvHost: Remake Ctrl Implementation.
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp178
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h47
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h18
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp128
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h96
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp13
-rw-r--r--src/video_core/gpu.h2
7 files changed, 312 insertions, 170 deletions
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index bfe1faf48..f2b015c8f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -1,11 +1,14 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-2.0-or-later 2// (https://github.com/skyline-emu/)
3// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3
4// or any later version Refer to the license.txt file included.
3 5
4#include <cstdlib> 6#include <cstdlib>
5#include <cstring> 7#include <cstring>
6 8
7#include "common/assert.h" 9#include "common/assert.h"
8#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/scope_exit.h"
9#include "core/core.h" 12#include "core/core.h"
10#include "core/hle/kernel/k_event.h" 13#include "core/hle/kernel/k_event.h"
11#include "core/hle/kernel/k_writable_event.h" 14#include "core/hle/kernel/k_writable_event.h"
@@ -30,9 +33,9 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
30 case 0x1c: 33 case 0x1c:
31 return IocCtrlClearEventWait(input, output); 34 return IocCtrlClearEventWait(input, output);
32 case 0x1d: 35 case 0x1d:
33 return IocCtrlEventWait(input, output, false);
34 case 0x1e:
35 return IocCtrlEventWait(input, output, true); 36 return IocCtrlEventWait(input, output, true);
37 case 0x1e:
38 return IocCtrlEventWait(input, output, false);
36 case 0x1f: 39 case 0x1f:
37 return IocCtrlEventRegister(input, output); 40 return IocCtrlEventRegister(input, output);
38 case 0x20: 41 case 0x20:
@@ -71,54 +74,65 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
71} 74}
72 75
73NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 76NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
74 bool is_async) { 77 bool is_allocation) {
75 IocCtrlEventWaitParams params{}; 78 IocCtrlEventWaitParams params{};
76 std::memcpy(&params, input.data(), sizeof(params)); 79 std::memcpy(&params, input.data(), sizeof(params));
77 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", 80 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
78 params.syncpt_id, params.threshold, params.timeout, is_async); 81 params.fence.id, params.fence.value, params.timeout, is_allocation);
79 82
80 if (params.syncpt_id >= MaxSyncPoints) { 83 bool must_unmark_fail = !is_allocation;
81 return NvResult::BadParameter; 84 const u32 event_id = params.value.raw;
82 } 85 SCOPE_EXIT({
86 std::memcpy(output.data(), &params, sizeof(params));
87 if (must_unmark_fail) {
88 events_interface.fails[event_id] = 0;
89 }
90 });
83 91
84 u32 event_id = params.value & 0x00FF; 92 const u32 fence_id = static_cast<u32>(params.fence.id);
85 93
86 if (event_id >= MaxNvEvents) { 94 if (fence_id >= MaxSyncPoints) {
87 std::memcpy(output.data(), &params, sizeof(params));
88 return NvResult::BadParameter; 95 return NvResult::BadParameter;
89 } 96 }
90 97
91 if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { 98 if (params.fence.value == 0) {
92 params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); 99 params.value.raw = syncpoint_manager.GetSyncpointMin(fence_id);
93 std::memcpy(output.data(), &params, sizeof(params));
94 events_interface.fails[event_id] = 0;
95 return NvResult::Success; 100 return NvResult::Success;
96 } 101 }
97 102
98 if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id); 103 if (syncpoint_manager.IsSyncpointExpired(fence_id, params.fence.value)) {
99 syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { 104 params.value.raw = syncpoint_manager.GetSyncpointMin(fence_id);
100 params.value = new_value; 105 return NvResult::Success;
101 std::memcpy(output.data(), &params, sizeof(params)); 106 }
102 events_interface.fails[event_id] = 0; 107
108 if (const auto new_value = syncpoint_manager.RefreshSyncpoint(fence_id);
109 syncpoint_manager.IsSyncpointExpired(fence_id, params.fence.value)) {
110 params.value.raw = new_value;
103 return NvResult::Success; 111 return NvResult::Success;
104 } 112 }
105 113
106 auto& gpu = system.GPU(); 114 auto& gpu = system.GPU();
107 const u32 target_value = syncpoint_manager.GetSyncpointMax(params.syncpt_id); 115 const u32 target_value = params.fence.value;
108 116
109 if (!is_async) { 117 auto lock = events_interface.Lock();
110 params.value = 0; 118
111 } 119 u32 slot = [&]() {
120 if (is_allocation) {
121 params.value.raw = 0;
122 return events_interface.FindFreeEvent(fence_id);
123 } else {
124 return params.value.raw;
125 }
126 }();
112 127
113 const auto check_failing = [&]() { 128 const auto check_failing = [&]() {
114 if (events_interface.fails[event_id] > 1) { 129 if (events_interface.fails[slot] > 1) {
115 { 130 {
116 auto lk = system.StallProcesses(); 131 auto lk = system.StallProcesses();
117 gpu.WaitFence(params.syncpt_id, target_value); 132 gpu.WaitFence(fence_id, target_value);
118 system.UnstallProcesses(); 133 system.UnstallProcesses();
119 } 134 }
120 std::memcpy(output.data(), &params, sizeof(params)); 135 params.value.raw = target_value;
121 events_interface.fails[event_id] = 0;
122 return true; 136 return true;
123 } 137 }
124 return false; 138 return false;
@@ -131,47 +145,76 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
131 return NvResult::Timeout; 145 return NvResult::Timeout;
132 } 146 }
133 147
134 EventState status = events_interface.status[event_id]; 148 if (slot >= MaxNvEvents) {
135 const bool bad_parameter = status == EventState::Busy;
136 if (bad_parameter) {
137 std::memcpy(output.data(), &params, sizeof(params));
138 return NvResult::BadParameter; 149 return NvResult::BadParameter;
139 } 150 }
140 events_interface.SetEventStatus(event_id, EventState::Waiting); 151
141 events_interface.assigned_syncpt[event_id] = params.syncpt_id; 152 auto* event = events_interface.events[slot];
142 events_interface.assigned_value[event_id] = target_value; 153
143 if (is_async) { 154 if (!event) {
144 params.value = params.syncpt_id << 4; 155 return NvResult::BadParameter;
145 } else {
146 params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
147 } 156 }
148 params.value |= event_id; 157
158 if (events_interface.IsBeingUsed(slot)) {
159 return NvResult::BadParameter;
160 }
161
149 if (check_failing()) { 162 if (check_failing()) {
150 return NvResult::Success; 163 return NvResult::Success;
151 } 164 }
152 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); 165
153 std::memcpy(output.data(), &params, sizeof(params)); 166 params.value.raw = 0;
167
168 events_interface.status[slot].store(EventState::Waiting, std::memory_order_release);
169 events_interface.assigned_syncpt[slot] = fence_id;
170 events_interface.assigned_value[slot] = target_value;
171 if (is_allocation) {
172 params.value.syncpoint_id_for_allocation.Assign(static_cast<u16>(fence_id));
173 params.value.event_allocated.Assign(1);
174 } else {
175 params.value.syncpoint_id.Assign(fence_id);
176 }
177 params.value.raw |= slot;
178
179 gpu.RegisterSyncptInterrupt(fence_id, target_value);
154 return NvResult::Timeout; 180 return NvResult::Timeout;
155} 181}
156 182
183NvResult nvhost_ctrl::FreeEvent(u32 slot) {
184 if (slot >= MaxNvEvents) {
185 return NvResult::BadParameter;
186 }
187
188 if (!events_interface.registered[slot]) {
189 return NvResult::Success;
190 }
191
192 if (events_interface.IsBeingUsed(slot)) {
193 return NvResult::Busy;
194 }
195
196 events_interface.Free(slot);
197 return NvResult::Success;
198}
199
157NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { 200NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
158 IocCtrlEventRegisterParams params{}; 201 IocCtrlEventRegisterParams params{};
159 std::memcpy(&params, input.data(), sizeof(params)); 202 std::memcpy(&params, input.data(), sizeof(params));
160 const u32 event_id = params.user_event_id & 0x00FF; 203 const u32 event_id = params.user_event_id;
161 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 204 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
162 if (event_id >= MaxNvEvents) { 205 if (event_id >= MaxNvEvents) {
163 return NvResult::BadParameter; 206 return NvResult::BadParameter;
164 } 207 }
208
209 auto lock = events_interface.Lock();
210
165 if (events_interface.registered[event_id]) { 211 if (events_interface.registered[event_id]) {
166 const auto event_state = events_interface.status[event_id]; 212 const auto result = FreeEvent(event_id);
167 if (event_state != EventState::Free) { 213 if (result != NvResult::Success) {
168 LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event"); 214 return result;
169 events_interface.UnregisterEvent(event_id);
170 } else {
171 return NvResult::BadParameter;
172 } 215 }
173 } 216 }
174 events_interface.RegisterEvent(event_id); 217 events_interface.Create(event_id);
175 return NvResult::Success; 218 return NvResult::Success;
176} 219}
177 220
@@ -181,32 +224,33 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
181 std::memcpy(&params, input.data(), sizeof(params)); 224 std::memcpy(&params, input.data(), sizeof(params));
182 const u32 event_id = params.user_event_id & 0x00FF; 225 const u32 event_id = params.user_event_id & 0x00FF;
183 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 226 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
184 if (event_id >= MaxNvEvents) { 227
185 return NvResult::BadParameter; 228 auto lock = events_interface.Lock();
186 } 229 return FreeEvent(event_id);
187 if (!events_interface.registered[event_id]) {
188 return NvResult::BadParameter;
189 }
190 events_interface.UnregisterEvent(event_id);
191 return NvResult::Success;
192} 230}
193 231
194NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { 232NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
195 IocCtrlEventSignalParams params{}; 233 IocCtrlEventClearParams params{};
196 std::memcpy(&params, input.data(), sizeof(params)); 234 std::memcpy(&params, input.data(), sizeof(params));
197 235
198 u32 event_id = params.event_id & 0x00FF; 236 u32 event_id = params.event_id.slot;
199 LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); 237 LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id);
200 238
201 if (event_id >= MaxNvEvents) { 239 if (event_id >= MaxNvEvents) {
202 return NvResult::BadParameter; 240 return NvResult::BadParameter;
203 } 241 }
204 if (events_interface.status[event_id] == EventState::Waiting) { 242
205 events_interface.LiberateEvent(event_id); 243 auto lock = events_interface.Lock();
244
245 if (events_interface.status[event_id].exchange(
246 EventState::Cancelling, std::memory_order_acq_rel) == EventState::Waiting) {
247 system.GPU().CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
248 events_interface.assigned_value[event_id]);
249 syncpoint_manager.RefreshSyncpoint(events_interface.assigned_syncpt[event_id]);
206 } 250 }
207 events_interface.fails[event_id]++; 251 events_interface.fails[event_id]++;
208 252 events_interface.status[event_id].store(EventState::Cancelled, std::memory_order_release);
209 syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); 253 events_interface.events[event_id]->GetWritableEvent().Clear();
210 254
211 return NvResult::Success; 255 return NvResult::Success;
212} 256}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 4fbb89b15..0471c09b2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <vector> 7#include <vector>
8#include "common/bit_field.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "core/hle/service/nvdrv/devices/nvdevice.h" 10#include "core/hle/service/nvdrv/devices/nvdevice.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 11#include "core/hle/service/nvdrv/nvdrv.h"
@@ -27,6 +28,24 @@ public:
27 void OnOpen(DeviceFD fd) override; 28 void OnOpen(DeviceFD fd) override;
28 void OnClose(DeviceFD fd) override; 29 void OnClose(DeviceFD fd) override;
29 30
31 union SyncpointEventValue {
32 u32 raw;
33
34 union {
35 BitField<0, 4, u32> partial_slot;
36 BitField<4, 28, u32> syncpoint_id;
37 };
38
39 struct {
40 u16 slot;
41 union {
42 BitField<0, 12, u16> syncpoint_id_for_allocation;
43 BitField<12, 1, u16> event_allocated;
44 };
45 };
46 };
47 static_assert(sizeof(SyncpointEventValue) == sizeof(u32));
48
30private: 49private:
31 struct IocSyncptReadParams { 50 struct IocSyncptReadParams {
32 u32_le id{}; 51 u32_le id{};
@@ -83,27 +102,18 @@ private:
83 }; 102 };
84 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); 103 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
85 104
86 struct IocCtrlEventSignalParams { 105 struct IocCtrlEventClearParams {
87 u32_le event_id{}; 106 SyncpointEventValue event_id{};
88 }; 107 };
89 static_assert(sizeof(IocCtrlEventSignalParams) == 4, 108 static_assert(sizeof(IocCtrlEventClearParams) == 4,
90 "IocCtrlEventSignalParams is incorrect size"); 109 "IocCtrlEventClearParams is incorrect size");
91 110
92 struct IocCtrlEventWaitParams { 111 struct IocCtrlEventWaitParams {
93 u32_le syncpt_id{}; 112 NvFence fence{};
94 u32_le threshold{};
95 s32_le timeout{};
96 u32_le value{};
97 };
98 static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
99
100 struct IocCtrlEventWaitAsyncParams {
101 u32_le syncpt_id{};
102 u32_le threshold{};
103 u32_le timeout{}; 113 u32_le timeout{};
104 u32_le value{}; 114 SyncpointEventValue value{};
105 }; 115 };
106 static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, 116 static_assert(sizeof(IocCtrlEventWaitParams) == 16,
107 "IocCtrlEventWaitAsyncParams is incorrect size"); 117 "IocCtrlEventWaitAsyncParams is incorrect size");
108 118
109 struct IocCtrlEventRegisterParams { 119 struct IocCtrlEventRegisterParams {
@@ -124,11 +134,14 @@ private:
124 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); 134 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
125 135
126 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); 136 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
127 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); 137 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
138 bool is_allocation);
128 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); 139 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
129 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); 140 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
130 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); 141 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
131 142
143 NvResult FreeEvent(u32 slot);
144
132 EventInterface& events_interface; 145 EventInterface& events_interface;
133 SyncpointManager& syncpoint_manager; 146 SyncpointManager& syncpoint_manager;
134}; 147};
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 1d00394c8..2ee91f9c4 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,5 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-2.0-or-later 2// (https://github.com/skyline-emu/)
3// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3
4// or any later version Refer to the license.txt file included.
3 5
4#pragma once 6#pragma once
5 7
@@ -78,11 +80,15 @@ enum class NvResult : u32 {
78 ModuleNotPresent = 0xA000E, 80 ModuleNotPresent = 0xA000E,
79}; 81};
80 82
83// obtained from
84// https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47
81enum class EventState { 85enum class EventState {
82 Free = 0, 86 Available = 0,
83 Registered = 1, 87 Waiting = 1,
84 Waiting = 2, 88 Cancelling = 2,
85 Busy = 3, 89 Signalling = 3,
90 Signalled = 4,
91 Cancelled = 5,
86}; 92};
87 93
88union Ioctl { 94union Ioctl {
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 756eb7453..fa259abed 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -1,6 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-2.0-or-later 2// (https://github.com/skyline-emu/)
3// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3
4// or any later version Refer to the license.txt file included.
3 5
6#include <bit>
4#include <utility> 7#include <utility>
5 8
6#include <fmt/format.h> 9#include <fmt/format.h>
@@ -26,6 +29,73 @@
26 29
27namespace Service::Nvidia { 30namespace Service::Nvidia {
28 31
32std::unique_lock<std::mutex> EventInterface::Lock() {
33 return std::unique_lock<std::mutex>(events_mutex);
34}
35
36void EventInterface::Signal(u32 event_id) {
37 if (status[event_id].exchange(EventState::Signalling, std::memory_order_acq_rel) ==
38 EventState::Waiting) {
39 events[event_id]->GetWritableEvent().Signal();
40 }
41 status[event_id].store(EventState::Signalled, std::memory_order_release);
42}
43
44void EventInterface::Create(u32 event_id) {
45 ASSERT(!events[event_id]);
46 ASSERT(!registered[event_id]);
47 ASSERT(!IsBeingUsed(event_id));
48 events[event_id] = backup[event_id];
49 status[event_id] = EventState::Available;
50 registered[event_id] = true;
51 const u64 mask = 1ULL << event_id;
52 fails[event_id] = 0;
53 events_mask |= mask;
54 LOG_CRITICAL(Service_NVDRV, "Created Event {}", event_id);
55}
56
57void EventInterface::Free(u32 event_id) {
58 ASSERT(events[event_id]);
59 ASSERT(registered[event_id]);
60 ASSERT(!IsBeingUsed(event_id));
61
62 backup[event_id]->GetWritableEvent().Clear();
63 events[event_id] = nullptr;
64 status[event_id] = EventState::Available;
65 registered[event_id] = false;
66 const u64 mask = ~(1ULL << event_id);
67 events_mask &= mask;
68 LOG_CRITICAL(Service_NVDRV, "Freed Event {}", event_id);
69}
70
71u32 EventInterface::FindFreeEvent(u32 syncpoint_id) {
72 u32 slot{MaxNvEvents};
73 u32 free_slot{MaxNvEvents};
74 for (u32 i = 0; i < MaxNvEvents; i++) {
75 if (registered[i]) {
76 if (!IsBeingUsed(i)) {
77 slot = i;
78 if (assigned_syncpt[i] == syncpoint_id) {
79 return slot;
80 }
81 }
82 } else if (free_slot == MaxNvEvents) {
83 free_slot = i;
84 }
85 }
86 if (free_slot < MaxNvEvents) {
87 Create(free_slot);
88 return free_slot;
89 }
90
91 if (slot < MaxNvEvents) {
92 return slot;
93 }
94
95 LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event");
96 return 0;
97}
98
29void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, 99void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
30 Core::System& system) { 100 Core::System& system) {
31 auto module_ = std::make_shared<Module>(system); 101 auto module_ = std::make_shared<Module>(system);
@@ -38,12 +108,14 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
38} 108}
39 109
40Module::Module(Core::System& system) 110Module::Module(Core::System& system)
41 : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { 111 : syncpoint_manager{system.GPU()}, events_interface{*this}, service_context{system, "nvdrv"} {
112 events_interface.events_mask = 0;
42 for (u32 i = 0; i < MaxNvEvents; i++) { 113 for (u32 i = 0; i < MaxNvEvents; i++) {
43 events_interface.events[i].event = 114 events_interface.status[i] = EventState::Available;
44 service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); 115 events_interface.events[i] = nullptr;
45 events_interface.status[i] = EventState::Free;
46 events_interface.registered[i] = false; 116 events_interface.registered[i] = false;
117 events_interface.backup[i] =
118 service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i));
47 } 119 }
48 auto nvmap_dev = std::make_shared<Devices::nvmap>(system); 120 auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
49 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); 121 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
@@ -62,8 +134,12 @@ Module::Module(Core::System& system)
62} 134}
63 135
64Module::~Module() { 136Module::~Module() {
137 auto lock = events_interface.Lock();
65 for (u32 i = 0; i < MaxNvEvents; i++) { 138 for (u32 i = 0; i < MaxNvEvents; i++) {
66 service_context.CloseEvent(events_interface.events[i].event); 139 if (events_interface.registered[i]) {
140 events_interface.Free(i);
141 }
142 service_context.CloseEvent(events_interface.backup[i]);
67 } 143 }
68} 144}
69 145
@@ -169,21 +245,41 @@ NvResult Module::Close(DeviceFD fd) {
169} 245}
170 246
171void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { 247void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
172 for (u32 i = 0; i < MaxNvEvents; i++) { 248 const u32 max = MaxNvEvents - std::countl_zero(events_interface.events_mask);
173 if (events_interface.assigned_syncpt[i] == syncpoint_id && 249 const u32 min = std::countr_zero(events_interface.events_mask);
250 for (u32 i = min; i < max; i++) {
251 if (events_interface.registered[i] && events_interface.assigned_syncpt[i] == syncpoint_id &&
174 events_interface.assigned_value[i] == value) { 252 events_interface.assigned_value[i] == value) {
175 events_interface.LiberateEvent(i); 253 events_interface.Signal(i);
176 events_interface.events[i].event->GetWritableEvent().Signal();
177 } 254 }
178 } 255 }
179} 256}
180 257
181Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) { 258Kernel::KEvent* Module::GetEvent(u32 event_id) {
182 return events_interface.events[event_id].event->GetReadableEvent(); 259 const auto event = Devices::nvhost_ctrl::SyncpointEventValue{.raw = event_id};
183} 260
261 const bool allocated = event.event_allocated.Value() != 0;
262 const u32 slot{allocated ? event.partial_slot.Value() : static_cast<u32>(event.slot)};
263 if (slot >= MaxNvEvents) {
264 ASSERT(false);
265 return nullptr;
266 }
184 267
185Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) { 268 const u32 syncpoint_id{allocated ? event.syncpoint_id_for_allocation.Value()
186 return events_interface.events[event_id].event->GetWritableEvent(); 269 : event.syncpoint_id.Value()};
270
271 auto lock = events_interface.Lock();
272
273 if (events_interface.registered[slot] &&
274 events_interface.assigned_syncpt[slot] == syncpoint_id) {
275 ASSERT(events_interface.events[slot]);
276 return events_interface.events[slot];
277 }
278 // Temporary hack.
279 events_interface.Create(slot);
280 events_interface.assigned_syncpt[slot] = syncpoint_id;
281 ASSERT(false);
282 return events_interface.events[slot];
187} 283}
188 284
189} // namespace Service::Nvidia 285} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index 4c4aa7dab..8ce036508 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -1,5 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu emulator team and Skyline Team and Contributors
2// SPDX-License-Identifier: GPL-2.0-or-later 2// (https://github.com/skyline-emu/)
3// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3
4// or any later version Refer to the license.txt file included.
3 5
4#pragma once 6#pragma once
5 7
@@ -34,19 +36,20 @@ namespace Devices {
34class nvdevice; 36class nvdevice;
35} 37}
36 38
37/// Represents an Nvidia event 39class Module;
38struct NvEvent { 40
39 Kernel::KEvent* event{}; 41class EventInterface {
40 NvFence fence{}; 42public:
41}; 43 EventInterface(Module& module_) : module{module_} {}
42 44
43struct EventInterface { 45 // Mask representing registered events
44 // Mask representing currently busy events
45 u64 events_mask{}; 46 u64 events_mask{};
46 // Each kernel event associated to an NV event 47 // Each kernel event associated to an NV event
47 std::array<NvEvent, MaxNvEvents> events; 48 std::array<Kernel::KEvent*, MaxNvEvents> events{};
49 // Backup NV event
50 std::array<Kernel::KEvent*, MaxNvEvents> backup{};
48 // The status of the current NVEvent 51 // The status of the current NVEvent
49 std::array<EventState, MaxNvEvents> status{}; 52 std::array<std::atomic<EventState>, MaxNvEvents> status{};
50 // Tells if an NVEvent is registered or not 53 // Tells if an NVEvent is registered or not
51 std::array<bool, MaxNvEvents> registered{}; 54 std::array<bool, MaxNvEvents> registered{};
52 // Tells the NVEvent that it has failed. 55 // Tells the NVEvent that it has failed.
@@ -59,50 +62,26 @@ struct EventInterface {
59 std::array<u32, MaxNvEvents> assigned_value{}; 62 std::array<u32, MaxNvEvents> assigned_value{};
60 // Constant to denote an unasigned syncpoint. 63 // Constant to denote an unasigned syncpoint.
61 static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; 64 static constexpr u32 unassigned_syncpt = 0xFFFFFFFF;
62 std::optional<u32> GetFreeEvent() const { 65
63 u64 mask = events_mask; 66 bool IsBeingUsed(u32 event_id) {
64 for (u32 i = 0; i < MaxNvEvents; i++) { 67 const auto current_status = status[event_id].load(std::memory_order_acquire);
65 const bool is_free = (mask & 0x1) == 0; 68 return current_status == EventState::Waiting || current_status == EventState::Cancelling ||
66 if (is_free) { 69 current_status == EventState::Signalling;
67 if (status[i] == EventState::Registered || status[i] == EventState::Free) {
68 return {i};
69 }
70 }
71 mask = mask >> 1;
72 }
73 return std::nullopt;
74 }
75 void SetEventStatus(const u32 event_id, EventState new_status) {
76 EventState old_status = status[event_id];
77 if (old_status == new_status) {
78 return;
79 }
80 status[event_id] = new_status;
81 if (new_status == EventState::Registered) {
82 registered[event_id] = true;
83 }
84 if (new_status == EventState::Waiting || new_status == EventState::Busy) {
85 events_mask |= (1ULL << event_id);
86 }
87 }
88 void RegisterEvent(const u32 event_id) {
89 registered[event_id] = true;
90 if (status[event_id] == EventState::Free) {
91 status[event_id] = EventState::Registered;
92 }
93 }
94 void UnregisterEvent(const u32 event_id) {
95 registered[event_id] = false;
96 if (status[event_id] == EventState::Registered) {
97 status[event_id] = EventState::Free;
98 }
99 }
100 void LiberateEvent(const u32 event_id) {
101 status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
102 events_mask &= ~(1ULL << event_id);
103 assigned_syncpt[event_id] = unassigned_syncpt;
104 assigned_value[event_id] = 0;
105 } 70 }
71
72 std::unique_lock<std::mutex> Lock();
73
74 void Signal(u32 event_id);
75
76 void Create(u32 event_id);
77
78 void Free(u32 event_id);
79
80 u32 FindFreeEvent(u32 syncpoint_id);
81
82private:
83 std::mutex events_mutex;
84 Module& module;
106}; 85};
107 86
108class Module final { 87class Module final {
@@ -139,11 +118,11 @@ public:
139 118
140 void SignalSyncpt(const u32 syncpoint_id, const u32 value); 119 void SignalSyncpt(const u32 syncpoint_id, const u32 value);
141 120
142 Kernel::KReadableEvent& GetEvent(u32 event_id); 121 Kernel::KEvent* GetEvent(u32 event_id);
143
144 Kernel::KWritableEvent& GetEventWriteable(u32 event_id);
145 122
146private: 123private:
124 friend class EventInterface;
125
147 /// Manages syncpoints on the host 126 /// Manages syncpoints on the host
148 SyncpointManager syncpoint_manager; 127 SyncpointManager syncpoint_manager;
149 128
@@ -159,6 +138,9 @@ private:
159 EventInterface events_interface; 138 EventInterface events_interface;
160 139
161 KernelHelpers::ServiceContext service_context; 140 KernelHelpers::ServiceContext service_context;
141
142 void CreateEvent(u32 event_id);
143 void FreeEvent(u32 event_id);
162}; 144};
163 145
164/// Registers all NVDRV services with the specified service manager. 146/// Registers all NVDRV services with the specified service manager.
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index b5a980384..07883feb2 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -5,6 +5,7 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/ipc_helpers.h" 7#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/k_readable_event.h" 9#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/service/nvdrv/nvdata.h" 10#include "core/hle/service/nvdrv/nvdata.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 11#include "core/hle/service/nvdrv/nvdrv.h"
@@ -164,8 +165,7 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
164void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { 165void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
165 IPC::RequestParser rp{ctx}; 166 IPC::RequestParser rp{ctx};
166 const auto fd = rp.Pop<DeviceFD>(); 167 const auto fd = rp.Pop<DeviceFD>();
167 const auto event_id = rp.Pop<u32>() & 0x00FF; 168 const auto event_id = rp.Pop<u32>();
168 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
169 169
170 if (!is_initialized) { 170 if (!is_initialized) {
171 ServiceError(ctx, NvResult::NotInitialized); 171 ServiceError(ctx, NvResult::NotInitialized);
@@ -180,12 +180,13 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
180 return; 180 return;
181 } 181 }
182 182
183 if (event_id < MaxNvEvents) { 183 auto* event = nvdrv->GetEvent(event_id);
184
185 if (event) {
184 IPC::ResponseBuilder rb{ctx, 3, 1}; 186 IPC::ResponseBuilder rb{ctx, 3, 1};
185 rb.Push(ResultSuccess); 187 rb.Push(ResultSuccess);
186 auto& event = nvdrv->GetEvent(event_id); 188 auto& readable_event = event->GetReadableEvent();
187 event.Clear(); 189 rb.PushCopyObjects(readable_event);
188 rb.PushCopyObjects(event);
189 rb.PushEnum(NvResult::Success); 190 rb.PushEnum(NvResult::Success);
190 } else { 191 } else {
191 IPC::ResponseBuilder rb{ctx, 3}; 192 IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index b939ba315..42c91954f 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -201,7 +201,7 @@ public:
201 201
202 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value); 202 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value);
203 203
204 [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); 204 bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value);
205 205
206 [[nodiscard]] u64 GetTicks() const; 206 [[nodiscard]] u64 GetTicks() const;
207 207