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.cpp297
1 files changed, 297 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..37f2ced18
--- /dev/null
+++ b/src/video_core/engines/puller.cpp
@@ -0,0 +1,297 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "common/settings.h"
8#include "core/core.h"
9#include "video_core/control/channel_state.h"
10#include "video_core/dma_pusher.h"
11#include "video_core/engines/fermi_2d.h"
12#include "video_core/engines/kepler_compute.h"
13#include "video_core/engines/kepler_memory.h"
14#include "video_core/engines/maxwell_3d.h"
15#include "video_core/engines/maxwell_dma.h"
16#include "video_core/engines/puller.h"
17#include "video_core/gpu.h"
18#include "video_core/memory_manager.h"
19#include "video_core/rasterizer_interface.h"
20
21namespace Tegra::Engines {
22
23Puller::Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher_,
24 Control::ChannelState& channel_state_)
25 : gpu{gpu_}, memory_manager{memory_manager_}, dma_pusher{dma_pusher_}, channel_state{
26 channel_state_} {}
27
28Puller::~Puller() = default;
29
30void Puller::ProcessBindMethod(const MethodCall& method_call) {
31 // Bind the current subchannel to the desired engine id.
32 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
33 method_call.argument);
34 const auto engine_id = static_cast<EngineID>(method_call.argument);
35 bound_engines[method_call.subchannel] = static_cast<EngineID>(engine_id);
36 switch (engine_id) {
37 case EngineID::FERMI_TWOD_A:
38 dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel);
39 break;
40 case EngineID::MAXWELL_B:
41 dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel);
42 break;
43 case EngineID::KEPLER_COMPUTE_B:
44 dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel);
45 break;
46 case EngineID::MAXWELL_DMA_COPY_A:
47 dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel);
48 break;
49 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
50 dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel);
51 break;
52 default:
53 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
54 }
55}
56
57void Puller::ProcessFenceActionMethod() {
58 switch (regs.fence_action.op) {
59 case Puller::FenceOperation::Acquire:
60 // UNIMPLEMENTED_MSG("Channel Scheduling pending.");
61 // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
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::ProcessWaitForInterruptMethod() {
72 // TODO(bunnei) ImplementMe
73 LOG_WARNING(HW_GPU, "(STUBBED) called");
74}
75
76void Puller::ProcessSemaphoreTriggerMethod() {
77 const auto semaphoreOperationMask = 0xF;
78 const auto op =
79 static_cast<GpuSemaphoreOperation>(regs.semaphore_trigger & semaphoreOperationMask);
80 if (op == GpuSemaphoreOperation::WriteLong) {
81 struct Block {
82 u32 sequence;
83 u32 zeros = 0;
84 u64 timestamp;
85 };
86
87 Block block{};
88 block.sequence = regs.semaphore_sequence;
89 // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
90 // CoreTiming
91 block.timestamp = gpu.GetTicks();
92 memory_manager.WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, sizeof(block));
93 } else {
94 const u32 word{memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress())};
95 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) ||
96 (op == GpuSemaphoreOperation::AcquireGequal &&
97 static_cast<s32>(word - regs.semaphore_sequence) > 0) ||
98 (op == GpuSemaphoreOperation::AcquireMask && (word & regs.semaphore_sequence))) {
99 // Nothing to do in this case
100 } else {
101 regs.acquire_source = true;
102 regs.acquire_value = regs.semaphore_sequence;
103 if (op == GpuSemaphoreOperation::AcquireEqual) {
104 regs.acquire_active = true;
105 regs.acquire_mode = false;
106 } else if (op == GpuSemaphoreOperation::AcquireGequal) {
107 regs.acquire_active = true;
108 regs.acquire_mode = true;
109 } else if (op == GpuSemaphoreOperation::AcquireMask) {
110 // TODO(kemathe) The acquire mask operation waits for a value that, ANDed with
111 // semaphore_sequence, gives a non-0 result
112 LOG_ERROR(HW_GPU, "Invalid semaphore operation AcquireMask not implemented");
113 } else {
114 LOG_ERROR(HW_GPU, "Invalid semaphore operation");
115 }
116 }
117 }
118}
119
120void Puller::ProcessSemaphoreRelease() {
121 memory_manager.Write<u32>(regs.semaphore_address.SemaphoreAddress(), regs.semaphore_release);
122}
123
124void Puller::ProcessSemaphoreAcquire() {
125 const u32 word = memory_manager.Read<u32>(regs.semaphore_address.SemaphoreAddress());
126 const auto value = regs.semaphore_acquire;
127 if (word != value) {
128 regs.acquire_active = true;
129 regs.acquire_value = value;
130 // TODO(kemathe73) figure out how to do the acquire_timeout
131 regs.acquire_mode = false;
132 regs.acquire_source = false;
133 }
134}
135
136/// Calls a GPU puller method.
137void Puller::CallPullerMethod(const MethodCall& method_call) {
138 regs.reg_array[method_call.method] = method_call.argument;
139 const auto method = static_cast<BufferMethods>(method_call.method);
140
141 switch (method) {
142 case BufferMethods::BindObject: {
143 ProcessBindMethod(method_call);
144 break;
145 }
146 case BufferMethods::Nop:
147 case BufferMethods::SemaphoreAddressHigh:
148 case BufferMethods::SemaphoreAddressLow:
149 case BufferMethods::SemaphoreSequence:
150 case BufferMethods::UnkCacheFlush:
151 case BufferMethods::WrcacheFlush:
152 case BufferMethods::FenceValue:
153 break;
154 case BufferMethods::RefCnt:
155 rasterizer->SignalReference();
156 break;
157 case BufferMethods::FenceAction:
158 ProcessFenceActionMethod();
159 break;
160 case BufferMethods::WaitForInterrupt:
161 ProcessWaitForInterruptMethod();
162 break;
163 case BufferMethods::SemaphoreTrigger: {
164 ProcessSemaphoreTriggerMethod();
165 break;
166 }
167 case BufferMethods::NotifyIntr: {
168 // TODO(Kmather73): Research and implement this method.
169 LOG_ERROR(HW_GPU, "Special puller engine method NotifyIntr not implemented");
170 break;
171 }
172 case BufferMethods::Unk28: {
173 // TODO(Kmather73): Research and implement this method.
174 LOG_ERROR(HW_GPU, "Special puller engine method Unk28 not implemented");
175 break;
176 }
177 case BufferMethods::SemaphoreAcquire: {
178 ProcessSemaphoreAcquire();
179 break;
180 }
181 case BufferMethods::SemaphoreRelease: {
182 ProcessSemaphoreRelease();
183 break;
184 }
185 case BufferMethods::Yield: {
186 // TODO(Kmather73): Research and implement this method.
187 LOG_ERROR(HW_GPU, "Special puller engine method Yield not implemented");
188 break;
189 }
190 default:
191 LOG_ERROR(HW_GPU, "Special puller engine method {:X} not implemented", method);
192 break;
193 }
194}
195
196/// Calls a GPU engine method.
197void Puller::CallEngineMethod(const MethodCall& method_call) {
198 const EngineID engine = bound_engines[method_call.subchannel];
199
200 switch (engine) {
201 case EngineID::FERMI_TWOD_A:
202 channel_state.fermi_2d->CallMethod(method_call.method, method_call.argument,
203 method_call.IsLastCall());
204 break;
205 case EngineID::MAXWELL_B:
206 channel_state.maxwell_3d->CallMethod(method_call.method, method_call.argument,
207 method_call.IsLastCall());
208 break;
209 case EngineID::KEPLER_COMPUTE_B:
210 channel_state.kepler_compute->CallMethod(method_call.method, method_call.argument,
211 method_call.IsLastCall());
212 break;
213 case EngineID::MAXWELL_DMA_COPY_A:
214 channel_state.maxwell_dma->CallMethod(method_call.method, method_call.argument,
215 method_call.IsLastCall());
216 break;
217 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
218 channel_state.kepler_memory->CallMethod(method_call.method, method_call.argument,
219 method_call.IsLastCall());
220 break;
221 default:
222 UNIMPLEMENTED_MSG("Unimplemented engine");
223 }
224}
225
226/// Calls a GPU engine multivalue method.
227void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
228 u32 methods_pending) {
229 const EngineID engine = bound_engines[subchannel];
230
231 switch (engine) {
232 case EngineID::FERMI_TWOD_A:
233 channel_state.fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending);
234 break;
235 case EngineID::MAXWELL_B:
236 channel_state.maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending);
237 break;
238 case EngineID::KEPLER_COMPUTE_B:
239 channel_state.kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending);
240 break;
241 case EngineID::MAXWELL_DMA_COPY_A:
242 channel_state.maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending);
243 break;
244 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
245 channel_state.kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending);
246 break;
247 default:
248 UNIMPLEMENTED_MSG("Unimplemented engine");
249 }
250}
251
252/// Calls a GPU method.
253void Puller::CallMethod(const MethodCall& method_call) {
254 LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method,
255 method_call.subchannel);
256
257 ASSERT(method_call.subchannel < bound_engines.size());
258
259 if (ExecuteMethodOnEngine(method_call.method)) {
260 CallEngineMethod(method_call);
261 } else {
262 CallPullerMethod(method_call);
263 }
264}
265
266/// Calls a GPU multivalue method.
267void Puller::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
268 u32 methods_pending) {
269 LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel);
270
271 ASSERT(subchannel < bound_engines.size());
272
273 if (ExecuteMethodOnEngine(method)) {
274 CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending);
275 } else {
276 for (std::size_t i = 0; i < amount; i++) {
277 CallPullerMethod(MethodCall{
278 method,
279 base_start[i],
280 subchannel,
281 methods_pending - static_cast<u32>(i),
282 });
283 }
284 }
285}
286
287void Puller::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
288 rasterizer = rasterizer_;
289}
290
291/// Determines where the method should be executed.
292[[nodiscard]] bool Puller::ExecuteMethodOnEngine(u32 method) {
293 const auto buffer_method = static_cast<BufferMethods>(method);
294 return buffer_method >= BufferMethods::NonPullerMethods;
295}
296
297} // namespace Tegra::Engines