summaryrefslogtreecommitdiff
path: root/src/video_core/engines
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/engines')
-rw-r--r--src/video_core/engines/puller.cpp297
-rw-r--r--src/video_core/engines/puller.h179
2 files changed, 476 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
diff --git a/src/video_core/engines/puller.h b/src/video_core/engines/puller.h
new file mode 100644
index 000000000..d948ec790
--- /dev/null
+++ b/src/video_core/engines/puller.h
@@ -0,0 +1,179 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <cstddef>
9#include <vector>
10#include "common/bit_field.h"
11#include "common/common_funcs.h"
12#include "common/common_types.h"
13#include "video_core/engines/engine_interface.h"
14
15namespace Core {
16class System;
17}
18
19namespace Tegra {
20class MemoryManager;
21class DmaPusher;
22
23enum class EngineID {
24 FERMI_TWOD_A = 0x902D, // 2D Engine
25 MAXWELL_B = 0xB197, // 3D Engine
26 KEPLER_COMPUTE_B = 0xB1C0,
27 KEPLER_INLINE_TO_MEMORY_B = 0xA140,
28 MAXWELL_DMA_COPY_A = 0xB0B5,
29};
30
31namespace Control {
32struct ChannelState;
33}
34} // namespace Tegra
35
36namespace VideoCore {
37class RasterizerInterface;
38}
39
40namespace Tegra::Engines {
41
42class Puller final {
43public:
44 struct MethodCall {
45 u32 method{};
46 u32 argument{};
47 u32 subchannel{};
48 u32 method_count{};
49
50 explicit MethodCall(u32 method_, u32 argument_, u32 subchannel_ = 0, u32 method_count_ = 0)
51 : method(method_), argument(argument_), subchannel(subchannel_),
52 method_count(method_count_) {}
53
54 [[nodiscard]] bool IsLastCall() const {
55 return method_count <= 1;
56 }
57 };
58
59 enum class FenceOperation : u32 {
60 Acquire = 0,
61 Increment = 1,
62 };
63
64 union FenceAction {
65 u32 raw;
66 BitField<0, 1, FenceOperation> op;
67 BitField<8, 24, u32> syncpoint_id;
68 };
69
70 explicit Puller(GPU& gpu_, MemoryManager& memory_manager_, DmaPusher& dma_pusher,
71 Control::ChannelState& channel_state);
72 ~Puller();
73
74 void CallMethod(const MethodCall& method_call);
75
76 void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
77 u32 methods_pending);
78
79 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
80
81 void CallPullerMethod(const MethodCall& method_call);
82
83 void CallEngineMethod(const MethodCall& method_call);
84
85 void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount,
86 u32 methods_pending);
87
88private:
89 Tegra::GPU& gpu;
90
91 MemoryManager& memory_manager;
92 DmaPusher& dma_pusher;
93 Control::ChannelState& channel_state;
94 VideoCore::RasterizerInterface* rasterizer = nullptr;
95
96 static constexpr std::size_t NUM_REGS = 0x800;
97 struct Regs {
98 static constexpr size_t NUM_REGS = 0x40;
99
100 union {
101 struct {
102 INSERT_PADDING_WORDS_NOINIT(0x4);
103 struct {
104 u32 address_high;
105 u32 address_low;
106
107 [[nodiscard]] GPUVAddr SemaphoreAddress() const {
108 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
109 address_low);
110 }
111 } semaphore_address;
112
113 u32 semaphore_sequence;
114 u32 semaphore_trigger;
115 INSERT_PADDING_WORDS_NOINIT(0xC);
116
117 // The pusher and the puller share the reference counter, the pusher only has read
118 // access
119 u32 reference_count;
120 INSERT_PADDING_WORDS_NOINIT(0x5);
121
122 u32 semaphore_acquire;
123 u32 semaphore_release;
124 u32 fence_value;
125 FenceAction fence_action;
126 INSERT_PADDING_WORDS_NOINIT(0xE2);
127
128 // Puller state
129 u32 acquire_mode;
130 u32 acquire_source;
131 u32 acquire_active;
132 u32 acquire_timeout;
133 u32 acquire_value;
134 };
135 std::array<u32, NUM_REGS> reg_array;
136 };
137 } regs{};
138
139 void ProcessBindMethod(const MethodCall& method_call);
140 void ProcessFenceActionMethod();
141 void ProcessSemaphoreAcquire();
142 void ProcessSemaphoreRelease();
143 void ProcessSemaphoreTriggerMethod();
144 void ProcessWaitForInterruptMethod();
145 [[nodiscard]] bool ExecuteMethodOnEngine(u32 method);
146
147 /// Mapping of command subchannels to their bound engine ids
148 std::array<EngineID, 8> bound_engines{};
149
150 enum class GpuSemaphoreOperation {
151 AcquireEqual = 0x1,
152 WriteLong = 0x2,
153 AcquireGequal = 0x4,
154 AcquireMask = 0x8,
155 };
156
157#define ASSERT_REG_POSITION(field_name, position) \
158 static_assert(offsetof(Regs, field_name) == position * 4, \
159 "Field " #field_name " has invalid position")
160
161 ASSERT_REG_POSITION(semaphore_address, 0x4);
162 ASSERT_REG_POSITION(semaphore_sequence, 0x6);
163 ASSERT_REG_POSITION(semaphore_trigger, 0x7);
164 ASSERT_REG_POSITION(reference_count, 0x14);
165 ASSERT_REG_POSITION(semaphore_acquire, 0x1A);
166 ASSERT_REG_POSITION(semaphore_release, 0x1B);
167 ASSERT_REG_POSITION(fence_value, 0x1C);
168 ASSERT_REG_POSITION(fence_action, 0x1D);
169
170 ASSERT_REG_POSITION(acquire_mode, 0x100);
171 ASSERT_REG_POSITION(acquire_source, 0x101);
172 ASSERT_REG_POSITION(acquire_active, 0x102);
173 ASSERT_REG_POSITION(acquire_timeout, 0x103);
174 ASSERT_REG_POSITION(acquire_value, 0x104);
175
176#undef ASSERT_REG_POSITION
177};
178
179} // namespace Tegra::Engines