summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp15
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/command_processor.h53
-rw-r--r--src/video_core/dma_pusher.cpp123
-rw-r--r--src/video_core/dma_pusher.h99
-rw-r--r--src/video_core/engines/fermi_2d.cpp8
-rw-r--r--src/video_core/engines/fermi_2d.h2
-rw-r--r--src/video_core/engines/kepler_memory.cpp10
-rw-r--r--src/video_core/engines/kepler_memory.h3
-rw-r--r--src/video_core/engines/maxwell_3d.cpp53
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/video_core/engines/maxwell_compute.cpp8
-rw-r--r--src/video_core/engines/maxwell_compute.h3
-rw-r--r--src/video_core/engines/maxwell_dma.cpp8
-rw-r--r--src/video_core/engines/maxwell_dma.h2
-rw-r--r--src/video_core/gpu.cpp53
-rw-r--r--src/video_core/gpu.h27
-rw-r--r--src/video_core/macro_interpreter.cpp2
18 files changed, 365 insertions, 110 deletions
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 874d5e1c3..2e2b0ae1c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -8,7 +8,6 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
10#include "core/memory.h" 10#include "core/memory.h"
11#include "video_core/command_processor.h"
12#include "video_core/gpu.h" 11#include "video_core/gpu.h"
13#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
14 13
@@ -129,6 +128,12 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
129 return 0; 128 return 0;
130} 129}
131 130
131static void PushGPUEntries(Tegra::CommandList&& entries) {
132 auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()};
133 dma_pusher.Push(std::move(entries));
134 dma_pusher.DispatchCalls();
135}
136
132u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { 137u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
133 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 138 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
134 UNIMPLEMENTED(); 139 UNIMPLEMENTED();
@@ -142,11 +147,11 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
142 params.num_entries * sizeof(Tegra::CommandListHeader), 147 params.num_entries * sizeof(Tegra::CommandListHeader),
143 "Incorrect input size"); 148 "Incorrect input size");
144 149
145 std::vector<Tegra::CommandListHeader> entries(params.num_entries); 150 Tegra::CommandList entries(params.num_entries);
146 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], 151 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
147 params.num_entries * sizeof(Tegra::CommandListHeader)); 152 params.num_entries * sizeof(Tegra::CommandListHeader));
148 153
149 Core::System::GetInstance().GPU().ProcessCommandLists(entries); 154 PushGPUEntries(std::move(entries));
150 155
151 params.fence_out.id = 0; 156 params.fence_out.id = 0;
152 params.fence_out.value = 0; 157 params.fence_out.value = 0;
@@ -163,11 +168,11 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
163 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 168 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
164 params.address, params.num_entries, params.flags); 169 params.address, params.num_entries, params.flags);
165 170
166 std::vector<Tegra::CommandListHeader> entries(params.num_entries); 171 Tegra::CommandList entries(params.num_entries);
167 Memory::ReadBlock(params.address, entries.data(), 172 Memory::ReadBlock(params.address, entries.data(),
168 params.num_entries * sizeof(Tegra::CommandListHeader)); 173 params.num_entries * sizeof(Tegra::CommandListHeader));
169 174
170 Core::System::GetInstance().GPU().ProcessCommandLists(entries); 175 PushGPUEntries(std::move(entries));
171 176
172 params.fence_out.id = 0; 177 params.fence_out.id = 0;
173 params.fence_out.value = 0; 178 params.fence_out.value = 0;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3f906a517..0406fbcd9 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,6 +1,6 @@
1add_library(video_core STATIC 1add_library(video_core STATIC
2 command_processor.cpp 2 dma_pusher.cpp
3 command_processor.h 3 dma_pusher.h
4 debug_utils/debug_utils.cpp 4 debug_utils/debug_utils.cpp
5 debug_utils/debug_utils.h 5 debug_utils/debug_utils.h
6 engines/fermi_2d.cpp 6 engines/fermi_2d.cpp
diff --git a/src/video_core/command_processor.h b/src/video_core/command_processor.h
deleted file mode 100644
index bd766e77a..000000000
--- a/src/video_core/command_processor.h
+++ /dev/null
@@ -1,53 +0,0 @@
1// Copyright 2018 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 <type_traits>
8#include "common/bit_field.h"
9#include "common/common_types.h"
10#include "video_core/memory_manager.h"
11
12namespace Tegra {
13
14enum class SubmissionMode : u32 {
15 IncreasingOld = 0,
16 Increasing = 1,
17 NonIncreasingOld = 2,
18 NonIncreasing = 3,
19 Inline = 4,
20 IncreaseOnce = 5
21};
22
23struct CommandListHeader {
24 u32 entry0; // gpu_va_lo
25 union {
26 u32 entry1; // gpu_va_hi | (unk_0x02 << 0x08) | (size << 0x0A) | (unk_0x01 << 0x1F)
27 BitField<0, 8, u32> gpu_va_hi;
28 BitField<8, 2, u32> unk1;
29 BitField<10, 21, u32> sz;
30 BitField<31, 1, u32> unk2;
31 };
32
33 GPUVAddr Address() const {
34 return (static_cast<GPUVAddr>(gpu_va_hi) << 32) | entry0;
35 }
36};
37static_assert(sizeof(CommandListHeader) == 8, "CommandListHeader is incorrect size");
38
39union CommandHeader {
40 u32 hex;
41
42 BitField<0, 13, u32> method;
43 BitField<13, 3, u32> subchannel;
44
45 BitField<16, 13, u32> arg_count;
46 BitField<16, 13, u32> inline_data;
47
48 BitField<29, 3, SubmissionMode> mode;
49};
50static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout");
51static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
52
53} // namespace Tegra
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
new file mode 100644
index 000000000..63a958f11
--- /dev/null
+++ b/src/video_core/dma_pusher.cpp
@@ -0,0 +1,123 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/microprofile.h"
6#include "core/core.h"
7#include "core/memory.h"
8#include "video_core/dma_pusher.h"
9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/gpu.h"
11
12namespace Tegra {
13
14DmaPusher::DmaPusher(GPU& gpu) : gpu(gpu) {}
15
16DmaPusher::~DmaPusher() = default;
17
18MICROPROFILE_DEFINE(DispatchCalls, "GPU", "Execute command buffer", MP_RGB(128, 128, 192));
19
20void DmaPusher::DispatchCalls() {
21 MICROPROFILE_SCOPE(DispatchCalls);
22
23 // On entering GPU code, assume all memory may be touched by the ARM core.
24 gpu.Maxwell3D().dirty_flags.OnMemoryWrite();
25
26 dma_pushbuffer_subindex = 0;
27
28 while (Core::System::GetInstance().IsPoweredOn()) {
29 if (!Step()) {
30 break;
31 }
32 }
33}
34
35bool DmaPusher::Step() {
36 if (dma_get != dma_put) {
37 // Push buffer non-empty, read a word
38 const CommandHeader command_header{
39 Memory::Read32(*gpu.MemoryManager().GpuToCpuAddress(dma_get))};
40
41 dma_get += sizeof(u32);
42
43 if (!non_main) {
44 dma_mget = dma_get;
45 }
46
47 // now, see if we're in the middle of a command
48 if (dma_state.length_pending) {
49 // Second word of long non-inc methods command - method count
50 dma_state.length_pending = 0;
51 dma_state.method_count = command_header.method_count_;
52 } else if (dma_state.method_count) {
53 // Data word of methods command
54 CallMethod(command_header.argument);
55
56 if (!dma_state.non_incrementing) {
57 dma_state.method++;
58 }
59
60 if (dma_increment_once) {
61 dma_state.non_incrementing = true;
62 }
63
64 dma_state.method_count--;
65 } else {
66 // No command active - this is the first word of a new one
67 switch (command_header.mode) {
68 case SubmissionMode::Increasing:
69 SetState(command_header);
70 dma_state.non_incrementing = false;
71 dma_increment_once = false;
72 break;
73 case SubmissionMode::NonIncreasing:
74 SetState(command_header);
75 dma_state.non_incrementing = true;
76 dma_increment_once = false;
77 break;
78 case SubmissionMode::Inline:
79 dma_state.method = command_header.method;
80 dma_state.subchannel = command_header.subchannel;
81 CallMethod(command_header.arg_count);
82 dma_state.non_incrementing = true;
83 dma_increment_once = false;
84 break;
85 case SubmissionMode::IncreaseOnce:
86 SetState(command_header);
87 dma_state.non_incrementing = false;
88 dma_increment_once = true;
89 break;
90 }
91 }
92 } else if (ib_enable && !dma_pushbuffer.empty()) {
93 // Current pushbuffer empty, but we have more IB entries to read
94 const CommandList& command_list{dma_pushbuffer.front()};
95 const CommandListHeader& command_list_header{command_list[dma_pushbuffer_subindex++]};
96 dma_get = command_list_header.addr;
97 dma_put = dma_get + command_list_header.size * sizeof(u32);
98 non_main = command_list_header.is_non_main;
99
100 if (dma_pushbuffer_subindex >= command_list.size()) {
101 // We've gone through the current list, remove it from the queue
102 dma_pushbuffer.pop();
103 dma_pushbuffer_subindex = 0;
104 }
105 } else {
106 // Otherwise, pushbuffer empty and IB empty or nonexistent - nothing to do
107 return {};
108 }
109
110 return true;
111}
112
113void DmaPusher::SetState(const CommandHeader& command_header) {
114 dma_state.method = command_header.method;
115 dma_state.subchannel = command_header.subchannel;
116 dma_state.method_count = command_header.method_count;
117}
118
119void DmaPusher::CallMethod(u32 argument) const {
120 gpu.CallMethod({dma_state.method, argument, dma_state.subchannel, dma_state.method_count});
121}
122
123} // namespace Tegra
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
new file mode 100644
index 000000000..16e0697c4
--- /dev/null
+++ b/src/video_core/dma_pusher.h
@@ -0,0 +1,99 @@
1// Copyright 2018 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 <vector>
8#include <queue>
9
10#include "common/bit_field.h"
11#include "common/common_types.h"
12#include "video_core/memory_manager.h"
13
14namespace Tegra {
15
16enum class SubmissionMode : u32 {
17 IncreasingOld = 0,
18 Increasing = 1,
19 NonIncreasingOld = 2,
20 NonIncreasing = 3,
21 Inline = 4,
22 IncreaseOnce = 5
23};
24
25struct CommandListHeader {
26 union {
27 u64 raw;
28 BitField<0, 40, GPUVAddr> addr;
29 BitField<41, 1, u64> is_non_main;
30 BitField<42, 21, u64> size;
31 };
32};
33static_assert(sizeof(CommandListHeader) == sizeof(u64), "CommandListHeader is incorrect size");
34
35union CommandHeader {
36 u32 argument;
37 BitField<0, 13, u32> method;
38 BitField<0, 24, u32> method_count_;
39 BitField<13, 3, u32> subchannel;
40 BitField<16, 13, u32> arg_count;
41 BitField<16, 13, u32> method_count;
42 BitField<29, 3, SubmissionMode> mode;
43};
44static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout");
45static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
46
47class GPU;
48
49using CommandList = std::vector<Tegra::CommandListHeader>;
50
51/**
52 * The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the
53 * emulated app fills with commands and tells PFIFO to process. The pushbuffers are then assembled
54 * into a "command stream" consisting of 32-bit words that make up "commands".
55 * See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for
56 * details on this implementation.
57 */
58class DmaPusher {
59public:
60 explicit DmaPusher(GPU& gpu);
61 ~DmaPusher();
62
63 void Push(CommandList&& entries) {
64 dma_pushbuffer.push(std::move(entries));
65 }
66
67 void DispatchCalls();
68
69private:
70 bool Step();
71
72 void SetState(const CommandHeader& command_header);
73
74 void CallMethod(u32 argument) const;
75
76 GPU& gpu;
77
78 std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed
79 std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer
80
81 struct DmaState {
82 u32 method; ///< Current method
83 u32 subchannel; ///< Current subchannel
84 u32 method_count; ///< Current method count
85 u32 length_pending; ///< Large NI command length pending
86 bool non_incrementing; ///< Current command’s NI flag
87 };
88
89 DmaState dma_state{};
90 bool dma_increment_once{};
91
92 GPUVAddr dma_put{}; ///< pushbuffer current end address
93 GPUVAddr dma_get{}; ///< pushbuffer current read address
94 GPUVAddr dma_mget{}; ///< main pushbuffer last read address
95 bool ib_enable{true}; ///< IB mode enabled
96 bool non_main{}; ///< non-main pushbuffer active
97};
98
99} // namespace Tegra
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index e7721a2be..80f70e332 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -14,13 +14,13 @@ namespace Tegra::Engines {
14Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 14Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
15 : memory_manager(memory_manager), rasterizer{rasterizer} {} 15 : memory_manager(memory_manager), rasterizer{rasterizer} {}
16 16
17void Fermi2D::WriteReg(u32 method, u32 value) { 17void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
18 ASSERT_MSG(method < Regs::NUM_REGS, 18 ASSERT_MSG(method_call.method < Regs::NUM_REGS,
19 "Invalid Fermi2D register, increase the size of the Regs structure"); 19 "Invalid Fermi2D register, increase the size of the Regs structure");
20 20
21 regs.reg_array[method] = value; 21 regs.reg_array[method_call.method] = method_call.argument;
22 22
23 switch (method) { 23 switch (method_call.method) {
24 case FERMI2D_REG_INDEX(trigger): { 24 case FERMI2D_REG_INDEX(trigger): {
25 HandleSurfaceCopy(); 25 HandleSurfaceCopy();
26 break; 26 break;
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 2a6e8bbbb..50009bf75 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -27,7 +27,7 @@ public:
27 ~Fermi2D() = default; 27 ~Fermi2D() = default;
28 28
29 /// Write the value to the register identified by method. 29 /// Write the value to the register identified by method.
30 void WriteReg(u32 method, u32 value); 30 void CallMethod(const GPU::MethodCall& method_call);
31 31
32 struct Regs { 32 struct Regs {
33 static constexpr std::size_t NUM_REGS = 0x258; 33 static constexpr std::size_t NUM_REGS = 0x258;
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 2adbc9eaf..4880191fc 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -17,19 +17,19 @@ KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer,
17 17
18KeplerMemory::~KeplerMemory() = default; 18KeplerMemory::~KeplerMemory() = default;
19 19
20void KeplerMemory::WriteReg(u32 method, u32 value) { 20void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
21 ASSERT_MSG(method < Regs::NUM_REGS, 21 ASSERT_MSG(method_call.method < Regs::NUM_REGS,
22 "Invalid KeplerMemory register, increase the size of the Regs structure"); 22 "Invalid KeplerMemory register, increase the size of the Regs structure");
23 23
24 regs.reg_array[method] = value; 24 regs.reg_array[method_call.method] = method_call.argument;
25 25
26 switch (method) { 26 switch (method_call.method) {
27 case KEPLERMEMORY_REG_INDEX(exec): { 27 case KEPLERMEMORY_REG_INDEX(exec): {
28 state.write_offset = 0; 28 state.write_offset = 0;
29 break; 29 break;
30 } 30 }
31 case KEPLERMEMORY_REG_INDEX(data): { 31 case KEPLERMEMORY_REG_INDEX(data): {
32 ProcessData(value); 32 ProcessData(method_call.argument);
33 break; 33 break;
34 } 34 }
35 } 35 }
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index bf4a13cff..fe9ebc5b9 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -9,6 +9,7 @@
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
13 14
14namespace VideoCore { 15namespace VideoCore {
@@ -26,7 +27,7 @@ public:
26 ~KeplerMemory(); 27 ~KeplerMemory();
27 28
28 /// Write the value to the register identified by method. 29 /// Write the value to the register identified by method.
29 void WriteReg(u32 method, u32 value); 30 void CallMethod(const GPU::MethodCall& method_call);
30 31
31 struct Regs { 32 struct Regs {
32 static constexpr size_t NUM_REGS = 0x7F; 33 static constexpr size_t NUM_REGS = 0x7F;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index f0a5470b9..b19b3a75a 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -97,71 +97,74 @@ void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
97 macro_interpreter.Execute(search->second, std::move(parameters)); 97 macro_interpreter.Execute(search->second, std::move(parameters));
98} 98}
99 99
100void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { 100void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
101 auto debug_context = Core::System::GetInstance().GetGPUDebugContext(); 101 auto debug_context = Core::System::GetInstance().GetGPUDebugContext();
102 102
103 // It is an error to write to a register other than the current macro's ARG register before it 103 // It is an error to write to a register other than the current macro's ARG register before it
104 // has finished execution. 104 // has finished execution.
105 if (executing_macro != 0) { 105 if (executing_macro != 0) {
106 ASSERT(method == executing_macro + 1); 106 ASSERT(method_call.method == executing_macro + 1);
107 } 107 }
108 108
109 // Methods after 0xE00 are special, they're actually triggers for some microcode that was 109 // Methods after 0xE00 are special, they're actually triggers for some microcode that was
110 // uploaded to the GPU during initialization. 110 // uploaded to the GPU during initialization.
111 if (method >= MacroRegistersStart) { 111 if (method_call.method >= MacroRegistersStart) {
112 // We're trying to execute a macro 112 // We're trying to execute a macro
113 if (executing_macro == 0) { 113 if (executing_macro == 0) {
114 // A macro call must begin by writing the macro method's register, not its argument. 114 // A macro call must begin by writing the macro method's register, not its argument.
115 ASSERT_MSG((method % 2) == 0, 115 ASSERT_MSG((method_call.method % 2) == 0,
116 "Can't start macro execution by writing to the ARGS register"); 116 "Can't start macro execution by writing to the ARGS register");
117 executing_macro = method; 117 executing_macro = method_call.method;
118 } 118 }
119 119
120 macro_params.push_back(value); 120 macro_params.push_back(method_call.argument);
121 121
122 // Call the macro when there are no more parameters in the command buffer 122 // Call the macro when there are no more parameters in the command buffer
123 if (remaining_params == 0) { 123 if (method_call.IsLastCall()) {
124 CallMacroMethod(executing_macro, std::move(macro_params)); 124 CallMacroMethod(executing_macro, std::move(macro_params));
125 } 125 }
126 return; 126 return;
127 } 127 }
128 128
129 ASSERT_MSG(method < Regs::NUM_REGS, 129 ASSERT_MSG(method_call.method < Regs::NUM_REGS,
130 "Invalid Maxwell3D register, increase the size of the Regs structure"); 130 "Invalid Maxwell3D register, increase the size of the Regs structure");
131 131
132 if (debug_context) { 132 if (debug_context) {
133 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr); 133 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
134 } 134 }
135 135
136 if (regs.reg_array[method] != value) { 136 if (regs.reg_array[method_call.method] != method_call.argument) {
137 regs.reg_array[method] = value; 137 regs.reg_array[method_call.method] = method_call.argument;
138 // Vertex format 138 // Vertex format
139 if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) && 139 if (method_call.method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
140 method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) { 140 method_call.method <
141 MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) {
141 dirty_flags.vertex_attrib_format = true; 142 dirty_flags.vertex_attrib_format = true;
142 } 143 }
143 144
144 // Vertex buffer 145 // Vertex buffer
145 if (method >= MAXWELL3D_REG_INDEX(vertex_array) && 146 if (method_call.method >= MAXWELL3D_REG_INDEX(vertex_array) &&
146 method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * 32) { 147 method_call.method < MAXWELL3D_REG_INDEX(vertex_array) + 4 * 32) {
147 dirty_flags.vertex_array |= 1u << ((method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2);
148 } else if (method >= MAXWELL3D_REG_INDEX(vertex_array_limit) &&
149 method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * 32) {
150 dirty_flags.vertex_array |= 148 dirty_flags.vertex_array |=
151 1u << ((method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1); 149 1u << ((method_call.method - MAXWELL3D_REG_INDEX(vertex_array)) >> 2);
152 } else if (method >= MAXWELL3D_REG_INDEX(instanced_arrays) && 150 } else if (method_call.method >= MAXWELL3D_REG_INDEX(vertex_array_limit) &&
153 method < MAXWELL3D_REG_INDEX(instanced_arrays) + 32) { 151 method_call.method < MAXWELL3D_REG_INDEX(vertex_array_limit) + 2 * 32) {
154 dirty_flags.vertex_array |= 1u << (method - MAXWELL3D_REG_INDEX(instanced_arrays)); 152 dirty_flags.vertex_array |=
153 1u << ((method_call.method - MAXWELL3D_REG_INDEX(vertex_array_limit)) >> 1);
154 } else if (method_call.method >= MAXWELL3D_REG_INDEX(instanced_arrays) &&
155 method_call.method < MAXWELL3D_REG_INDEX(instanced_arrays) + 32) {
156 dirty_flags.vertex_array |=
157 1u << (method_call.method - MAXWELL3D_REG_INDEX(instanced_arrays));
155 } 158 }
156 } 159 }
157 160
158 switch (method) { 161 switch (method_call.method) {
159 case MAXWELL3D_REG_INDEX(macros.data): { 162 case MAXWELL3D_REG_INDEX(macros.data): {
160 ProcessMacroUpload(value); 163 ProcessMacroUpload(method_call.argument);
161 break; 164 break;
162 } 165 }
163 case MAXWELL3D_REG_INDEX(macros.bind): { 166 case MAXWELL3D_REG_INDEX(macros.bind): {
164 ProcessMacroBind(value); 167 ProcessMacroBind(method_call.argument);
165 break; 168 break;
166 } 169 }
167 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): 170 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
@@ -180,7 +183,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
180 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]): 183 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
181 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]): 184 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
182 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): { 185 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): {
183 ProcessCBData(value); 186 ProcessCBData(method_call.argument);
184 break; 187 break;
185 } 188 }
186 case MAXWELL3D_REG_INDEX(cb_bind[0].raw_config): { 189 case MAXWELL3D_REG_INDEX(cb_bind[0].raw_config): {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 9324d9710..84471f181 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1080,7 +1080,7 @@ public:
1080 u32 GetRegisterValue(u32 method) const; 1080 u32 GetRegisterValue(u32 method) const;
1081 1081
1082 /// Write the value to the register identified by method. 1082 /// Write the value to the register identified by method.
1083 void WriteReg(u32 method, u32 value, u32 remaining_params); 1083 void CallMethod(const GPU::MethodCall& method_call);
1084 1084
1085 /// Returns a list of enabled textures for the specified shader stage. 1085 /// Returns a list of enabled textures for the specified shader stage.
1086 std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; 1086 std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const;
diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp
index 8b5f08351..656db6a61 100644
--- a/src/video_core/engines/maxwell_compute.cpp
+++ b/src/video_core/engines/maxwell_compute.cpp
@@ -8,13 +8,13 @@
8 8
9namespace Tegra::Engines { 9namespace Tegra::Engines {
10 10
11void MaxwellCompute::WriteReg(u32 method, u32 value) { 11void MaxwellCompute::CallMethod(const GPU::MethodCall& method_call) {
12 ASSERT_MSG(method < Regs::NUM_REGS, 12 ASSERT_MSG(method_call.method < Regs::NUM_REGS,
13 "Invalid MaxwellCompute register, increase the size of the Regs structure"); 13 "Invalid MaxwellCompute register, increase the size of the Regs structure");
14 14
15 regs.reg_array[method] = value; 15 regs.reg_array[method_call.method] = method_call.argument;
16 16
17 switch (method) { 17 switch (method_call.method) {
18 case MAXWELL_COMPUTE_REG_INDEX(compute): { 18 case MAXWELL_COMPUTE_REG_INDEX(compute): {
19 LOG_CRITICAL(HW_GPU, "Compute shaders are not implemented"); 19 LOG_CRITICAL(HW_GPU, "Compute shaders are not implemented");
20 UNREACHABLE(); 20 UNREACHABLE();
diff --git a/src/video_core/engines/maxwell_compute.h b/src/video_core/engines/maxwell_compute.h
index 6ea934fb9..1d71f11bd 100644
--- a/src/video_core/engines/maxwell_compute.h
+++ b/src/video_core/engines/maxwell_compute.h
@@ -9,6 +9,7 @@
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/gpu.h"
12 13
13namespace Tegra::Engines { 14namespace Tegra::Engines {
14 15
@@ -42,7 +43,7 @@ public:
42 "MaxwellCompute Regs has wrong size"); 43 "MaxwellCompute Regs has wrong size");
43 44
44 /// Write the value to the register identified by method. 45 /// Write the value to the register identified by method.
45 void WriteReg(u32 method, u32 value); 46 void CallMethod(const GPU::MethodCall& method_call);
46}; 47};
47 48
48#define ASSERT_REG_POSITION(field_name, position) \ 49#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index a34e884fe..06462f570 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -14,16 +14,16 @@ namespace Tegra::Engines {
14MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 14MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
15 : memory_manager(memory_manager), rasterizer{rasterizer} {} 15 : memory_manager(memory_manager), rasterizer{rasterizer} {}
16 16
17void MaxwellDMA::WriteReg(u32 method, u32 value) { 17void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) {
18 ASSERT_MSG(method < Regs::NUM_REGS, 18 ASSERT_MSG(method_call.method < Regs::NUM_REGS,
19 "Invalid MaxwellDMA register, increase the size of the Regs structure"); 19 "Invalid MaxwellDMA register, increase the size of the Regs structure");
20 20
21 regs.reg_array[method] = value; 21 regs.reg_array[method_call.method] = method_call.argument;
22 22
23#define MAXWELLDMA_REG_INDEX(field_name) \ 23#define MAXWELLDMA_REG_INDEX(field_name) \
24 (offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32)) 24 (offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
25 25
26 switch (method) { 26 switch (method_call.method) {
27 case MAXWELLDMA_REG_INDEX(exec): { 27 case MAXWELLDMA_REG_INDEX(exec): {
28 HandleCopy(); 28 HandleCopy();
29 break; 29 break;
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 5f3704f05..1f8cd65d2 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -24,7 +24,7 @@ public:
24 ~MaxwellDMA() = default; 24 ~MaxwellDMA() = default;
25 25
26 /// Write the value to the register identified by method. 26 /// Write the value to the register identified by method.
27 void WriteReg(u32 method, u32 value); 27 void CallMethod(const GPU::MethodCall& method_call);
28 28
29 struct Regs { 29 struct Regs {
30 static constexpr std::size_t NUM_REGS = 0x1D6; 30 static constexpr std::size_t NUM_REGS = 0x1D6;
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 51b3904f6..6c81dee64 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -26,6 +26,7 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
26 26
27GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { 27GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
28 memory_manager = std::make_unique<Tegra::MemoryManager>(); 28 memory_manager = std::make_unique<Tegra::MemoryManager>();
29 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
29 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); 30 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
30 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); 31 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
31 maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); 32 maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
@@ -51,6 +52,14 @@ const MemoryManager& GPU::MemoryManager() const {
51 return *memory_manager; 52 return *memory_manager;
52} 53}
53 54
55DmaPusher& GPU::DmaPusher() {
56 return *dma_pusher;
57}
58
59const DmaPusher& GPU::DmaPusher() const {
60 return *dma_pusher;
61}
62
54u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { 63u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
55 ASSERT(format != RenderTargetFormat::NONE); 64 ASSERT(format != RenderTargetFormat::NONE);
56 65
@@ -113,4 +122,48 @@ u32 DepthFormatBytesPerPixel(DepthFormat format) {
113 } 122 }
114} 123}
115 124
125enum class BufferMethods {
126 BindObject = 0,
127 CountBufferMethods = 0x40,
128};
129
130void GPU::CallMethod(const MethodCall& method_call) {
131 LOG_TRACE(HW_GPU,
132 "Processing method {:08X} on subchannel {} value "
133 "{:08X} remaining params {}",
134 MethCall.method, MethCall.subchannel, value, remaining_params);
135
136 ASSERT(method_call.subchannel < bound_engines.size());
137
138 if (method_call.method == static_cast<u32>(BufferMethods::BindObject)) {
139 // Bind the current subchannel to the desired engine id.
140 LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel,
141 method_call.argument);
142 bound_engines[method_call.subchannel] = static_cast<EngineID>(method_call.argument);
143 return;
144 }
145
146 const EngineID engine = bound_engines[method_call.subchannel];
147
148 switch (engine) {
149 case EngineID::FERMI_TWOD_A:
150 fermi_2d->CallMethod(method_call);
151 break;
152 case EngineID::MAXWELL_B:
153 maxwell_3d->CallMethod(method_call);
154 break;
155 case EngineID::MAXWELL_COMPUTE_B:
156 maxwell_compute->CallMethod(method_call);
157 break;
158 case EngineID::MAXWELL_DMA_COPY_A:
159 maxwell_dma->CallMethod(method_call);
160 break;
161 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
162 kepler_memory->CallMethod(method_call);
163 break;
164 default:
165 UNIMPLEMENTED_MSG("Unimplemented engine");
166 }
167}
168
116} // namespace Tegra 169} // namespace Tegra
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 5cc1e19ca..af5ccd1e9 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/service/nvflinger/buffer_queue.h" 11#include "core/hle/service/nvflinger/buffer_queue.h"
12#include "video_core/dma_pusher.h"
12#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
13 14
14namespace VideoCore { 15namespace VideoCore {
@@ -119,8 +120,23 @@ public:
119 explicit GPU(VideoCore::RasterizerInterface& rasterizer); 120 explicit GPU(VideoCore::RasterizerInterface& rasterizer);
120 ~GPU(); 121 ~GPU();
121 122
122 /// Processes a command list stored at the specified address in GPU memory. 123 struct MethodCall {
123 void ProcessCommandLists(const std::vector<CommandListHeader>& commands); 124 u32 method{};
125 u32 argument{};
126 u32 subchannel{};
127 u32 method_count{};
128
129 bool IsLastCall() const {
130 return method_count <= 1;
131 }
132
133 MethodCall(u32 method, u32 argument, u32 subchannel = 0, u32 method_count = 0)
134 : method(method), argument(argument), subchannel(subchannel),
135 method_count(method_count) {}
136 };
137
138 /// Calls a GPU method.
139 void CallMethod(const MethodCall& method_call);
124 140
125 /// Returns a reference to the Maxwell3D GPU engine. 141 /// Returns a reference to the Maxwell3D GPU engine.
126 Engines::Maxwell3D& Maxwell3D(); 142 Engines::Maxwell3D& Maxwell3D();
@@ -134,7 +150,14 @@ public:
134 /// Returns a const reference to the GPU memory manager. 150 /// Returns a const reference to the GPU memory manager.
135 const Tegra::MemoryManager& MemoryManager() const; 151 const Tegra::MemoryManager& MemoryManager() const;
136 152
153 /// Returns a reference to the GPU DMA pusher.
154 Tegra::DmaPusher& DmaPusher();
155
156 /// Returns a const reference to the GPU DMA pusher.
157 const Tegra::DmaPusher& DmaPusher() const;
158
137private: 159private:
160 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
138 std::unique_ptr<Tegra::MemoryManager> memory_manager; 161 std::unique_ptr<Tegra::MemoryManager> memory_manager;
139 162
140 /// Mapping of command subchannels to their bound engine ids. 163 /// Mapping of command subchannels to their bound engine ids.
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 2b0dea5cd..9c55e9f1e 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -250,7 +250,7 @@ void MacroInterpreter::SetMethodAddress(u32 address) {
250} 250}
251 251
252void MacroInterpreter::Send(u32 value) { 252void MacroInterpreter::Send(u32 value) {
253 maxwell3d.WriteReg(method_address.address, value, 0); 253 maxwell3d.CallMethod({method_address.address, value});
254 // Increment the method address by the method increment. 254 // Increment the method address by the method increment.
255 method_address.address.Assign(method_address.address.Value() + 255 method_address.address.Assign(method_address.address.Value() +
256 method_address.increment.Value()); 256 method_address.increment.Value());