maxwell_dma.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // Copyright 2018 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/assert.h"
  5. #include "common/logging/log.h"
  6. #include "core/core.h"
  7. #include "core/settings.h"
  8. #include "video_core/engines/maxwell_3d.h"
  9. #include "video_core/engines/maxwell_dma.h"
  10. #include "video_core/memory_manager.h"
  11. #include "video_core/renderer_base.h"
  12. #include "video_core/textures/decoders.h"
  13. namespace Tegra::Engines {
  14. MaxwellDMA::MaxwellDMA(Core::System& system, MemoryManager& memory_manager)
  15. : system{system}, memory_manager{memory_manager} {}
  16. void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
  17. ASSERT_MSG(method < Regs::NUM_REGS,
  18. "Invalid MaxwellDMA register, increase the size of the Regs structure");
  19. regs.reg_array[method] = method_argument;
  20. #define MAXWELLDMA_REG_INDEX(field_name) \
  21. (offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
  22. switch (method) {
  23. case MAXWELLDMA_REG_INDEX(exec): {
  24. HandleCopy();
  25. break;
  26. }
  27. }
  28. #undef MAXWELLDMA_REG_INDEX
  29. }
  30. void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
  31. u32 methods_pending) {
  32. for (std::size_t i = 0; i < amount; i++) {
  33. CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
  34. }
  35. }
  36. void MaxwellDMA::HandleCopy() {
  37. LOG_TRACE(HW_GPU, "Requested a DMA copy");
  38. const GPUVAddr source = regs.src_address.Address();
  39. const GPUVAddr dest = regs.dst_address.Address();
  40. // TODO(Subv): Perform more research and implement all features of this engine.
  41. ASSERT(regs.exec.enable_swizzle == 0);
  42. ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
  43. ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
  44. ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
  45. ASSERT(regs.dst_params.pos_x == 0);
  46. ASSERT(regs.dst_params.pos_y == 0);
  47. if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
  48. // If both the source and the destination are in block layout, assert.
  49. UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
  50. return;
  51. }
  52. // All copies here update the main memory, so mark all rasterizer states as invalid.
  53. system.GPU().Maxwell3D().OnMemoryWrite();
  54. if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
  55. // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
  56. // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
  57. // y_count).
  58. if (!regs.exec.enable_2d) {
  59. memory_manager.CopyBlock(dest, source, regs.x_count);
  60. return;
  61. }
  62. // If both the source and the destination are in linear layout, perform a line-by-line
  63. // copy. We're going to take a subrect of size (x_count, y_count) from the source
  64. // rectangle. There is no need to manually flush/invalidate the regions because
  65. // CopyBlock does that for us.
  66. for (u32 line = 0; line < regs.y_count; ++line) {
  67. const GPUVAddr source_line = source + line * regs.src_pitch;
  68. const GPUVAddr dest_line = dest + line * regs.dst_pitch;
  69. memory_manager.CopyBlock(dest_line, source_line, regs.x_count);
  70. }
  71. return;
  72. }
  73. ASSERT(regs.exec.enable_2d == 1);
  74. if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
  75. ASSERT(regs.src_params.BlockDepth() == 0);
  76. // Optimized path for micro copies.
  77. if (regs.dst_pitch * regs.y_count < Texture::GetGOBSize() && regs.dst_pitch <= 64) {
  78. const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
  79. const std::size_t src_size = Texture::GetGOBSize();
  80. const std::size_t dst_size = regs.dst_pitch * regs.y_count;
  81. u32 pos_x = regs.src_params.pos_x;
  82. u32 pos_y = regs.src_params.pos_y;
  83. const u64 offset =
  84. Texture::GetGOBOffset(regs.src_params.size_x, regs.src_params.size_y, pos_x, pos_y,
  85. regs.src_params.BlockDepth(), bytes_per_pixel);
  86. const u32 x_in_gob = 64 / bytes_per_pixel;
  87. pos_x = pos_x % x_in_gob;
  88. pos_y = pos_y % 8;
  89. if (read_buffer.size() < src_size) {
  90. read_buffer.resize(src_size);
  91. }
  92. if (write_buffer.size() < dst_size) {
  93. write_buffer.resize(dst_size);
  94. }
  95. if (Settings::IsGPULevelExtreme()) {
  96. memory_manager.ReadBlock(source + offset, read_buffer.data(), src_size);
  97. memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
  98. } else {
  99. memory_manager.ReadBlockUnsafe(source + offset, read_buffer.data(), src_size);
  100. memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
  101. }
  102. Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
  103. regs.src_params.size_x, bytes_per_pixel, read_buffer.data(),
  104. write_buffer.data(), regs.src_params.BlockHeight(), pos_x,
  105. pos_y);
  106. memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
  107. return;
  108. }
  109. // If the input is tiled and the output is linear, deswizzle the input and copy it over.
  110. const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
  111. const std::size_t src_size = Texture::CalculateSize(
  112. true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y,
  113. regs.src_params.size_z, regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
  114. const std::size_t src_layer_size = Texture::CalculateSize(
  115. true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y, 1,
  116. regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
  117. const std::size_t dst_size = regs.dst_pitch * regs.y_count;
  118. if (read_buffer.size() < src_size) {
  119. read_buffer.resize(src_size);
  120. }
  121. if (write_buffer.size() < dst_size) {
  122. write_buffer.resize(dst_size);
  123. }
  124. if (Settings::IsGPULevelExtreme()) {
  125. memory_manager.ReadBlock(source, read_buffer.data(), src_size);
  126. memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
  127. } else {
  128. memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
  129. memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
  130. }
  131. Texture::UnswizzleSubrect(
  132. regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel,
  133. read_buffer.data() + src_layer_size * regs.src_params.pos_z, write_buffer.data(),
  134. regs.src_params.BlockHeight(), regs.src_params.pos_x, regs.src_params.pos_y);
  135. memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
  136. } else {
  137. ASSERT(regs.dst_params.BlockDepth() == 0);
  138. const u32 bytes_per_pixel = regs.src_pitch / regs.x_count;
  139. const std::size_t dst_size = Texture::CalculateSize(
  140. true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y,
  141. regs.dst_params.size_z, regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
  142. const std::size_t dst_layer_size = Texture::CalculateSize(
  143. true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, 1,
  144. regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
  145. const std::size_t src_size = regs.src_pitch * regs.y_count;
  146. if (read_buffer.size() < src_size) {
  147. read_buffer.resize(src_size);
  148. }
  149. if (write_buffer.size() < dst_size) {
  150. write_buffer.resize(dst_size);
  151. }
  152. if (Settings::IsGPULevelExtreme()) {
  153. memory_manager.ReadBlock(source, read_buffer.data(), src_size);
  154. memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
  155. } else {
  156. memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
  157. memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
  158. }
  159. // If the input is linear and the output is tiled, swizzle the input and copy it over.
  160. Texture::SwizzleSubrect(
  161. regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, bytes_per_pixel,
  162. write_buffer.data() + dst_layer_size * regs.dst_params.pos_z, read_buffer.data(),
  163. regs.dst_params.BlockHeight(), regs.dst_params.pos_x, regs.dst_params.pos_y);
  164. memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
  165. }
  166. }
  167. } // namespace Tegra::Engines