summaryrefslogtreecommitdiff
path: root/src/video_core/engines/puller.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/engines/puller.cpp')
-rw-r--r--src/video_core/engines/puller.cpp306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp
new file mode 100644
index 000000000..cca890792
--- /dev/null
+++ b/src/video_core/engines/puller.cpp
@@ -0,0 +1,306 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/assert.h"
5#include "common/logging/log.h"
6#include "common/settings.h"
7#include "core/core.h"
8#include "video_core/control/channel_state.h"
9#include "video_core/dma_pusher.h"
10#include "video_core/engines/fermi_2d.h"
11#include "video_core/engines/kepler_compute.h"
12#include "video_core/engines/kepler_memory.h"
13#include "video_core/engines/maxwell_3d.h"
14#include "video_core/engines/maxwell_dma.h"
15#include "video_core/engines/puller.h"
16#include "video_core/gpu.h"
17#include "video_core/memory_manager.h"
18#include "video_core/rasterizer_interface.h"
19
20namespace Tegra::Engines {
21
22Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_,
23 Control::ChannelState& channel_state_)
24 : gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, channel_state{
25 channel_state_} {}
26
27Puller::~Puller() = default;
28
29void Puller::ProcessBindMethod(const MethodCall& method_call) {
30 // Bind the current subchannel to the desired engine id.
31 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
32 method_call.argument);
33 const auto engine_id = static_cast<EngineID>(method_call.argument);
34 bound_engines[method_call.subchannel] = static_cast<EngineID>(engine_id);
35 switch (engine_id) {
36 case EngineID::FERMI_TWOD_A:
37 dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel);
38 break;
39 case EngineID::MAXWELL_B:
40 dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel);
41 break;
42 case EngineID::KEPLER_COMPUTE_B:
43 dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel);
44 break;
45 case EngineID::MAXWELL_DMA_COPY_A:
46 dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel);
47 break;
48 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
49 dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel);
50 break;
51 default:
52 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
53 }
54}
55
56void Puller::ProcessFenceActionMethod() {
57 switch (regs.fence_action.op) {
58 case Puller::FenceOperation::Acquire:
59 // UNIMPLEMENTED_MSG("Channel Scheduling pending.");
60 // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
61 rasterizer->ReleaseFences();
62 break;
63 case Puller::FenceOperation::Increment:
64 rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);
65 break;
66 default:
67 UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
68 }
69}
70
71void Puller::ProcessSemaphoreTriggerMethod() {
72 const auto semaphoreOperationMask = 0xF;
73 const auto op =
74 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
75 if (op == GpuSemaphoreOperation::WriteLong) {
76 const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
77 const u32 payload = regs.semaphore_sequence;
78 std::function<void()> operation([this, sequence_address, payload] {
79 memory_manager.Write<u64>(sequence_address + sizeof(u64), gpu.GetTicks());
80 memory_manager.Write<u64>(sequence_address, payload);
81 });
82 rasterizer->SignalFence(std::move(operation));
83 } else {
84 do {
85 const u32 word{memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress())};
86 regs.acquire_source = true;
87 regs.acquire_value = regs.semaphore_sequence;
88 if (op == GpuSemaphoreOperation::AcquireEqual) {
89 regs.acquire_active = true;
90 regs.acquire_mode = false;
91 if (word != regs.acquire_value) {
92 rasterizer->ReleaseFences();
93 continue;
94 }
95 } else if (op == GpuSemaphoreOperation::AcquireGequal) {
96 regs.acquire_active = true;
97 regs.acquire_mode = true;
98 if (word < regs.acquire_value) {
99 rasterizer->ReleaseFences();
100 continue;
101 }
102 } else if (op == GpuSemaphoreOperation::AcquireMask) {
103 if (word && regs.semaphore_sequence == 0) {
104 rasterizer->ReleaseFences();
105 continue;
106 }
107 } else {
108 LOG_ERROR(HW_GPU, "Invalid semaphore operation");
109 }
110 } while (false);
111 }
112}
113
114void Puller::ProcessSemaphoreRelease() {
115 const GPUVAddr sequence_address{regs.semaphore_address.SemaphoreAddress()};
116 const u32 payload = regs.semaphore_release;
117 std::function<void()> operation([this, sequence_address, payload] {
118 memory_manager.Write<u32>(sequence_address, payload);
119 });
120 rasterizer->SyncOperation(std::move(operation));
121}
122
123void Puller::ProcessSemaphoreAcquire() {
124 u32 word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
125 const auto value = regs.semaphore_acquire;
126 while (word != value) {
127 regs.acquire_active = true;
128 regs.acquire_value = value;
129 std::this_thread::sleep_for(std::chrono::milliseconds(1));
130 rasterizer->ReleaseFences();
131 word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
132 // TODO(kemathe73) figure out how to do the acquire_timeout
133 regs.acquire_mode = false;
134 regs.acquire_source = false;
135 }
136}
137
138/// Calls a GPU puller method.
139void Puller::CallPullerMethod(const MethodCall& method_call) {
140 regs.reg_array[method_call.method] = method_call.argument;
141 const auto method = static_cast<BufferMethods>(method_call.method);
142
143 switch (method) {
144 case BufferMethods::BindObject: {
145 ProcessBindMethod(method_call);
146 break;
147 }
148 case BufferMethods::Nop:
149 case BufferMethods::SemaphoreAddressHigh:
150 case BufferMethods::SemaphoreAddressLow:
151 case BufferMethods::SemaphoreSequencePayload:
152 case BufferMethods::SyncpointPayload:
153 break;
154 case BufferMethods::WrcacheFlush:
155 case BufferMethods::RefCnt:
156 rasterizer->SignalReference();
157 break;
158 case BufferMethods::SyncpointOperation:
159 ProcessFenceActionMethod();
160 break;
161 case BufferMethods::WaitForIdle:
162 rasterizer->WaitForIdle();
163 break;
164 case BufferMethods::SemaphoreOperation: {
165 ProcessSemaphoreTriggerMethod();
166 break;
167 }
168 case BufferMethods::NonStallInterrupt: {
169 LOG_ERROR(HW_GPU, "Special puller engine method NonStallInterrupt not implemented");
170 break;
171 }
172 case BufferMethods::MemOpA: {
173 LOG_ERROR(HW_GPU, "Memory Operation A");
174 break;
175 }
176 case BufferMethods::MemOpB: {
177 // Implement this better.
178 rasterizer->InvalidateGPUCache();
179 break;
180 }
181 case BufferMethods::MemOpC:
182 case BufferMethods::MemOpD: {
183 LOG_ERROR(HW_GPU, "Memory Operation C,D");
184 break;
185 }
186 case BufferMethods::SemaphoreAcquire: {
187 ProcessSemaphoreAcquire();
188 break;
189 }
190 case BufferMethods::SemaphoreRelease: {
191 ProcessSemaphoreRelease();
192 break;
193 }
194 case BufferMethods::Yield: {
195 // TODO(Kmather73): Research and implement this method.
196 LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented");
197 break;
198 }
199 default:
200 LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method);
201 break;
202 }
203}
204
205/// Calls a GPU engine method.
206void Puller::CallEngineMethod(const MethodCall& method_call) {
207 const EngineID engine = bound_engines[method_call.subchannel];
208
209 switch (engine) {
210 case EngineID::FERMI_TWOD_A:
211 channel_state.fermi_2d->CallMethod(method_call.method, method_call.argument,
212 method_call.IsLastCall());
213 break;
214 case EngineID::MAXWELL_B:
215 channel_state.maxwell_3d->CallMethod(method_call.method, method_call.argument,
216 method_call.IsLastCall());
217 break;
218 case EngineID::KEPLER_COMPUTE_B:
219 channel_state.kepler_compute->CallMethod(method_call.method, method_call.argument,
220 method_call.IsLastCall());
221 break;
222 case EngineID::MAXWELL_DMA_COPY_A:
223 channel_state.maxwell_dma->CallMethod(method_call.method, method_call.argument,
224 method_call.IsLastCall());
225 break;
226 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
227 channel_state.kepler_memory->CallMethod(method_call.method, method_call.argument,
228 method_call.IsLastCall());
229 break;
230 default:
231 UNIMPLEMENTED_MSG("Unimplemented engine");
232 }
233}
234
235/// Calls a GPU engine multivalue method.
236void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
237 u32 methods_pending) {
238 const EngineID engine = bound_engines[subchannel];
239
240 switch (engine) {
241 case EngineID::FERMI_TWOD_A:
242 channel_state.fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending);
243 break;
244 case EngineID::MAXWELL_B:
245 channel_state.maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending);
246 break;
247 case EngineID::KEPLER_COMPUTE_B:
248 channel_state.kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending);
249 break;
250 case EngineID::MAXWELL_DMA_COPY_A:
251 channel_state.maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending);
252 break;
253 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
254 channel_state.kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending);
255 break;
256 default:
257 UNIMPLEMENTED_MSG("Unimplemented engine");
258 }
259}
260
261/// Calls a GPU method.
262void Puller::CallMethod(const MethodCall& method_call) {
263 LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method,
264 method_call.subchannel);
265
266 ASSERT(method_call.subchannel < bound_engines.size());
267
268 if (ExecuteMethodOnEngine(method_call.method)) {
269 CallEngineMethod(method_call);
270 } else {
271 CallPullerMethod(method_call);
272 }
273}
274
275/// Calls a GPU multivalue method.
276void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
277 u32 methods_pending) {
278 LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel);
279
280 ASSERT(subchannel < bound_engines.size());
281
282 if (ExecuteMethodOnEngine(method)) {
283 CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending);
284 } else {
285 for (std::size_t i = 0; i < amount; i++) {
286 CallPullerMethod(MethodCall{
287 method,
288 base_start[i],
289 subchannel,
290 methods_pending - static_cast<u32>(i),
291 });
292 }
293 }
294}
295
296void Puller::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
297 rasterizer = rasterizer_;
298}
299
300/// Determines where the method should be executed.
301[[nodiscard]] bool Puller::ExecuteMethodOnEngine(u32 method) {
302 const auto buffer_method = static_cast<BufferMethods>(method);
303 return buffer_method >= BufferMethods::NonPullerMethods;
304}
305
306} // namespace Tegra::Engines