maxwell_dma.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "common/algorithm.h"
  4. #include "common/assert.h"
  5. #include "common/logging/log.h"
  6. #include "common/microprofile.h"
  7. #include "common/settings.h"
  8. #include "core/core.h"
  9. #include "video_core/engines/maxwell_3d.h"
  10. #include "video_core/engines/maxwell_dma.h"
  11. #include "video_core/memory_manager.h"
  12. #include "video_core/renderer_base.h"
  13. #include "video_core/textures/decoders.h"
  14. MICROPROFILE_DECLARE(GPU_DMAEngine);
  15. MICROPROFILE_DECLARE(GPU_DMAEngineBL);
  16. MICROPROFILE_DECLARE(GPU_DMAEngineLB);
  17. MICROPROFILE_DECLARE(GPU_DMAEngineBB);
  18. MICROPROFILE_DEFINE(GPU_DMAEngine, "GPU", "DMA Engine", MP_RGB(224, 224, 128));
  19. MICROPROFILE_DEFINE(GPU_DMAEngineBL, "GPU", "DMA Engine Block - Linear", MP_RGB(224, 224, 128));
  20. MICROPROFILE_DEFINE(GPU_DMAEngineLB, "GPU", "DMA Engine Linear - Block", MP_RGB(224, 224, 128));
  21. MICROPROFILE_DEFINE(GPU_DMAEngineBB, "GPU", "DMA Engine Block - Block", MP_RGB(224, 224, 128));
  22. namespace Tegra::Engines {
  23. using namespace Texture;
  24. MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_)
  25. : system{system_}, memory_manager{memory_manager_} {
  26. execution_mask.reset();
  27. execution_mask[offsetof(Regs, launch_dma) / sizeof(u32)] = true;
  28. }
  29. MaxwellDMA::~MaxwellDMA() = default;
  30. void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
  31. rasterizer = rasterizer_;
  32. }
  33. void MaxwellDMA::ConsumeSinkImpl() {
  34. for (auto [method, value] : method_sink) {
  35. regs.reg_array[method] = value;
  36. }
  37. method_sink.clear();
  38. }
  39. void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
  40. ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
  41. regs.reg_array[method] = method_argument;
  42. if (method == offsetof(Regs, launch_dma) / sizeof(u32)) {
  43. Launch();
  44. }
  45. }
  46. void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
  47. u32 methods_pending) {
  48. for (u32 i = 0; i < amount; ++i) {
  49. CallMethod(method, base_start[i], methods_pending - i <= 1);
  50. }
  51. }
  52. void MaxwellDMA::Launch() {
  53. MICROPROFILE_SCOPE(GPU_DMAEngine);
  54. LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
  55. static_cast<GPUVAddr>(regs.offset_out));
  56. // TODO(Subv): Perform more research and implement all features of this engine.
  57. const LaunchDMA& launch = regs.launch_dma;
  58. ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
  59. ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
  60. if (launch.multi_line_enable) {
  61. const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH;
  62. const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH;
  63. memory_manager.FlushCaching();
  64. if (!is_src_pitch && !is_dst_pitch) {
  65. // If both the source and the destination are in block layout, assert.
  66. MICROPROFILE_SCOPE(GPU_DMAEngineBB);
  67. CopyBlockLinearToBlockLinear();
  68. ReleaseSemaphore();
  69. return;
  70. }
  71. if (is_src_pitch && is_dst_pitch) {
  72. for (u32 line = 0; line < regs.line_count; ++line) {
  73. const GPUVAddr source_line =
  74. regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
  75. const GPUVAddr dest_line =
  76. regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
  77. memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
  78. }
  79. } else {
  80. if (!is_src_pitch && is_dst_pitch) {
  81. MICROPROFILE_SCOPE(GPU_DMAEngineBL);
  82. CopyBlockLinearToPitch();
  83. } else {
  84. MICROPROFILE_SCOPE(GPU_DMAEngineLB);
  85. CopyPitchToBlockLinear();
  86. }
  87. }
  88. } else {
  89. // TODO: allow multisized components.
  90. auto& accelerate = rasterizer->AccessAccelerateDMA();
  91. const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A;
  92. if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) {
  93. ASSERT(regs.remap_const.component_size_minus_one == 3);
  94. accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
  95. std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value);
  96. memory_manager.WriteBlockUnsafe(regs.offset_out,
  97. reinterpret_cast<u8*>(tmp_buffer.data()),
  98. regs.line_length_in * sizeof(u32));
  99. } else {
  100. memory_manager.FlushCaching();
  101. const auto convert_linear_2_blocklinear_addr = [](u64 address) {
  102. return (address & ~0x1f0ULL) | ((address & 0x40) >> 2) | ((address & 0x10) << 1) |
  103. ((address & 0x180) >> 1) | ((address & 0x20) << 3);
  104. };
  105. const auto src_kind = memory_manager.GetPageKind(regs.offset_in);
  106. const auto dst_kind = memory_manager.GetPageKind(regs.offset_out);
  107. const bool is_src_pitch = IsPitchKind(src_kind);
  108. const bool is_dst_pitch = IsPitchKind(dst_kind);
  109. if (!is_src_pitch && is_dst_pitch) {
  110. UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
  111. UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
  112. UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
  113. std::vector<u8> tmp_buffer(16);
  114. for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
  115. memory_manager.ReadBlockUnsafe(
  116. convert_linear_2_blocklinear_addr(regs.offset_in + offset),
  117. tmp_buffer.data(), tmp_buffer.size());
  118. memory_manager.WriteBlockCached(regs.offset_out + offset, tmp_buffer.data(),
  119. tmp_buffer.size());
  120. }
  121. } else if (is_src_pitch && !is_dst_pitch) {
  122. UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0);
  123. UNIMPLEMENTED_IF(regs.offset_in % 16 != 0);
  124. UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
  125. std::vector<u8> tmp_buffer(16);
  126. for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
  127. memory_manager.ReadBlockUnsafe(regs.offset_in + offset, tmp_buffer.data(),
  128. tmp_buffer.size());
  129. memory_manager.WriteBlockCached(
  130. convert_linear_2_blocklinear_addr(regs.offset_out + offset),
  131. tmp_buffer.data(), tmp_buffer.size());
  132. }
  133. } else {
  134. if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
  135. std::vector<u8> tmp_buffer(regs.line_length_in);
  136. memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(),
  137. regs.line_length_in);
  138. memory_manager.WriteBlockCached(regs.offset_out, tmp_buffer.data(),
  139. regs.line_length_in);
  140. }
  141. }
  142. }
  143. }
  144. ReleaseSemaphore();
  145. }
  146. void MaxwellDMA::CopyBlockLinearToPitch() {
  147. UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
  148. u32 bytes_per_pixel = 1;
  149. DMA::ImageOperand src_operand;
  150. src_operand.bytes_per_pixel = bytes_per_pixel;
  151. src_operand.params = regs.src_params;
  152. src_operand.address = regs.offset_in;
  153. DMA::BufferOperand dst_operand;
  154. dst_operand.pitch = regs.pitch_out;
  155. dst_operand.width = regs.line_length_in;
  156. dst_operand.height = regs.line_count;
  157. dst_operand.address = regs.offset_out;
  158. DMA::ImageCopy copy_info{};
  159. copy_info.length_x = regs.line_length_in;
  160. copy_info.length_y = regs.line_count;
  161. auto& accelerate = rasterizer->AccessAccelerateDMA();
  162. if (accelerate.ImageToBuffer(copy_info, src_operand, dst_operand)) {
  163. return;
  164. }
  165. UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0);
  166. UNIMPLEMENTED_IF(regs.src_params.block_size.depth != 0);
  167. UNIMPLEMENTED_IF(regs.src_params.block_size.depth == 0 && regs.src_params.depth != 1);
  168. // Deswizzle the input and copy it over.
  169. const DMA::Parameters& src_params = regs.src_params;
  170. const bool is_remapping = regs.launch_dma.remap_enable != 0;
  171. const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1;
  172. const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1;
  173. const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size;
  174. u32 width = src_params.width;
  175. u32 x_elements = regs.line_length_in;
  176. u32 x_offset = src_params.origin.x;
  177. u32 bpp_shift = 0U;
  178. if (!is_remapping) {
  179. bpp_shift = Common::FoldRight(
  180. 4U, [](u32 x, u32 y) { return std::min(x, static_cast<u32>(std::countr_zero(y))); },
  181. width, x_elements, x_offset, static_cast<u32>(regs.offset_in));
  182. width >>= bpp_shift;
  183. x_elements >>= bpp_shift;
  184. x_offset >>= bpp_shift;
  185. }
  186. bytes_per_pixel = base_bpp << bpp_shift;
  187. const u32 height = src_params.height;
  188. const u32 depth = src_params.depth;
  189. const u32 block_height = src_params.block_size.height;
  190. const u32 block_depth = src_params.block_size.depth;
  191. const size_t src_size =
  192. CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
  193. const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
  194. read_buffer.resize_destructive(src_size);
  195. write_buffer.resize_destructive(dst_size);
  196. memory_manager.ReadBlock(src_operand.address, read_buffer.data(), src_size);
  197. memory_manager.ReadBlock(dst_operand.address, write_buffer.data(), dst_size);
  198. UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
  199. src_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
  200. regs.pitch_out);
  201. memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
  202. }
  203. void MaxwellDMA::CopyPitchToBlockLinear() {
  204. UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one");
  205. UNIMPLEMENTED_IF(regs.dst_params.layer != 0);
  206. const bool is_remapping = regs.launch_dma.remap_enable != 0;
  207. const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1;
  208. const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1;
  209. u32 bytes_per_pixel = 1;
  210. DMA::ImageOperand dst_operand;
  211. dst_operand.bytes_per_pixel = bytes_per_pixel;
  212. dst_operand.params = regs.dst_params;
  213. dst_operand.address = regs.offset_out;
  214. DMA::BufferOperand src_operand;
  215. src_operand.pitch = regs.pitch_in;
  216. src_operand.width = regs.line_length_in;
  217. src_operand.height = regs.line_count;
  218. src_operand.address = regs.offset_in;
  219. DMA::ImageCopy copy_info{};
  220. copy_info.length_x = regs.line_length_in;
  221. copy_info.length_y = regs.line_count;
  222. auto& accelerate = rasterizer->AccessAccelerateDMA();
  223. if (accelerate.BufferToImage(copy_info, src_operand, dst_operand)) {
  224. return;
  225. }
  226. const auto& dst_params = regs.dst_params;
  227. const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size;
  228. u32 width = dst_params.width;
  229. u32 x_elements = regs.line_length_in;
  230. u32 x_offset = dst_params.origin.x;
  231. u32 bpp_shift = 0U;
  232. if (!is_remapping) {
  233. bpp_shift = Common::FoldRight(
  234. 4U, [](u32 x, u32 y) { return std::min(x, static_cast<u32>(std::countr_zero(y))); },
  235. width, x_elements, x_offset, static_cast<u32>(regs.offset_out));
  236. width >>= bpp_shift;
  237. x_elements >>= bpp_shift;
  238. x_offset >>= bpp_shift;
  239. }
  240. bytes_per_pixel = base_bpp << bpp_shift;
  241. const u32 height = dst_params.height;
  242. const u32 depth = dst_params.depth;
  243. const u32 block_height = dst_params.block_size.height;
  244. const u32 block_depth = dst_params.block_size.depth;
  245. const size_t dst_size =
  246. CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
  247. const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
  248. read_buffer.resize_destructive(src_size);
  249. write_buffer.resize_destructive(dst_size);
  250. memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
  251. memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
  252. // If the input is linear and the output is tiled, swizzle the input and copy it over.
  253. SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset,
  254. dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth,
  255. regs.pitch_in);
  256. memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
  257. }
  258. void MaxwellDMA::CopyBlockLinearToBlockLinear() {
  259. UNIMPLEMENTED_IF(regs.src_params.block_size.width != 0);
  260. const bool is_remapping = regs.launch_dma.remap_enable != 0;
  261. // Deswizzle the input and copy it over.
  262. const DMA::Parameters& src = regs.src_params;
  263. const DMA::Parameters& dst = regs.dst_params;
  264. const u32 num_remap_components = regs.remap_const.num_dst_components_minus_one + 1;
  265. const u32 remap_components_size = regs.remap_const.component_size_minus_one + 1;
  266. const u32 base_bpp = !is_remapping ? 1U : num_remap_components * remap_components_size;
  267. u32 src_width = src.width;
  268. u32 dst_width = dst.width;
  269. u32 x_elements = regs.line_length_in;
  270. u32 src_x_offset = src.origin.x;
  271. u32 dst_x_offset = dst.origin.x;
  272. u32 bpp_shift = 0U;
  273. if (!is_remapping) {
  274. bpp_shift = Common::FoldRight(
  275. 4U, [](u32 x, u32 y) { return std::min(x, static_cast<u32>(std::countr_zero(y))); },
  276. src_width, dst_width, x_elements, src_x_offset, dst_x_offset,
  277. static_cast<u32>(regs.offset_in), static_cast<u32>(regs.offset_out));
  278. src_width >>= bpp_shift;
  279. dst_width >>= bpp_shift;
  280. x_elements >>= bpp_shift;
  281. src_x_offset >>= bpp_shift;
  282. dst_x_offset >>= bpp_shift;
  283. }
  284. const u32 bytes_per_pixel = base_bpp << bpp_shift;
  285. const size_t src_size = CalculateSize(true, bytes_per_pixel, src_width, src.height, src.depth,
  286. src.block_size.height, src.block_size.depth);
  287. const size_t dst_size = CalculateSize(true, bytes_per_pixel, dst_width, dst.height, dst.depth,
  288. dst.block_size.height, dst.block_size.depth);
  289. const u32 pitch = x_elements * bytes_per_pixel;
  290. const size_t mid_buffer_size = pitch * regs.line_count;
  291. read_buffer.resize_destructive(src_size);
  292. write_buffer.resize_destructive(dst_size);
  293. intermediate_buffer.resize_destructive(mid_buffer_size);
  294. memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
  295. memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
  296. UnswizzleSubrect(intermediate_buffer, read_buffer, bytes_per_pixel, src_width, src.height,
  297. src.depth, src_x_offset, src.origin.y, x_elements, regs.line_count,
  298. src.block_size.height, src.block_size.depth, pitch);
  299. SwizzleSubrect(write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height,
  300. dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count,
  301. dst.block_size.height, dst.block_size.depth, pitch);
  302. memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size);
  303. }
  304. void MaxwellDMA::ReleaseSemaphore() {
  305. const auto type = regs.launch_dma.semaphore_type;
  306. const GPUVAddr address = regs.semaphore.address;
  307. const u32 payload = regs.semaphore.payload;
  308. switch (type) {
  309. case LaunchDMA::SemaphoreType::NONE:
  310. break;
  311. case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE: {
  312. std::function<void()> operation(
  313. [this, address, payload] { memory_manager.Write<u32>(address, payload); });
  314. rasterizer->SignalFence(std::move(operation));
  315. break;
  316. }
  317. case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE: {
  318. std::function<void()> operation([this, address, payload] {
  319. memory_manager.Write<u64>(address + sizeof(u64), system.GPU().GetTicks());
  320. memory_manager.Write<u64>(address, payload);
  321. });
  322. rasterizer->SignalFence(std::move(operation));
  323. break;
  324. }
  325. default:
  326. ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
  327. break;
  328. }
  329. }
  330. } // namespace Tegra::Engines