gpu.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #include "common/common_types.h"
  5. #include "core/settings.h"
  6. #include "core/core.h"
  7. #include "core/mem_map.h"
  8. #include "core/hle/hle.h"
  9. #include "core/hle/service/gsp_gpu.h"
  10. #include "core/hw/gpu.h"
  11. #include "video_core/command_processor.h"
  12. #include "video_core/video_core.h"
  13. namespace GPU {
  14. Regs g_regs;
  15. u32 g_cur_line = 0; ///< Current vertical screen line
  16. u64 g_last_line_ticks = 0; ///< CPU tick count from last vertical screen line
  17. u64 g_last_frame_ticks = 0; ///< CPU tick count from last frame
  18. static u32 kFrameCycles = 0; ///< 268MHz / 60 frames per second
  19. static u32 kFrameTicks = 0; ///< Approximate number of instructions/frame
  20. template <typename T>
  21. inline void Read(T &var, const u32 raw_addr) {
  22. u32 addr = raw_addr - 0x1EF00000;
  23. u32 index = addr / 4;
  24. // Reads other than u32 are untested, so I'd rather have them abort than silently fail
  25. if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
  26. ERROR_LOG(GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
  27. return;
  28. }
  29. var = g_regs[addr / 4];
  30. }
  31. template <typename T>
  32. inline void Write(u32 addr, const T data) {
  33. addr -= 0x1EF00000;
  34. u32 index = addr / 4;
  35. // Writes other than u32 are untested, so I'd rather have them abort than silently fail
  36. if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
  37. ERROR_LOG(GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
  38. return;
  39. }
  40. g_regs[index] = static_cast<u32>(data);
  41. switch (index) {
  42. // Memory fills are triggered once the fill value is written.
  43. // NOTE: This is not verified.
  44. case GPU_REG_INDEX_WORKAROUND(memory_fill_config[0].value, 0x00004 + 0x3):
  45. case GPU_REG_INDEX_WORKAROUND(memory_fill_config[1].value, 0x00008 + 0x3):
  46. {
  47. const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].value));
  48. const auto& config = g_regs.memory_fill_config[is_second_filler];
  49. // TODO: Not sure if this check should be done at GSP level instead
  50. if (config.address_start) {
  51. // TODO: Not sure if this algorithm is correct, particularly because it doesn't use the size member at all
  52. u32* start = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetStartAddress()));
  53. u32* end = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetEndAddress()));
  54. for (u32* ptr = start; ptr < end; ++ptr)
  55. *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
  56. DEBUG_LOG(GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
  57. }
  58. break;
  59. }
  60. case GPU_REG_INDEX(display_transfer_config.trigger):
  61. {
  62. const auto& config = g_regs.display_transfer_config;
  63. if (config.trigger & 1) {
  64. u8* source_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress()));
  65. u8* dest_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress()));
  66. for (u32 y = 0; y < config.output_height; ++y) {
  67. // TODO: Why does the register seem to hold twice the framebuffer width?
  68. for (u32 x = 0; x < config.output_width; ++x) {
  69. struct {
  70. int r, g, b, a;
  71. } source_color = { 0, 0, 0, 0 };
  72. switch (config.input_format) {
  73. case Regs::PixelFormat::RGBA8:
  74. {
  75. // TODO: Most likely got the component order messed up.
  76. u8* srcptr = source_pointer + x * 4 + y * config.input_width * 4;
  77. source_color.r = srcptr[0]; // blue
  78. source_color.g = srcptr[1]; // green
  79. source_color.b = srcptr[2]; // red
  80. source_color.a = srcptr[3]; // alpha
  81. break;
  82. }
  83. default:
  84. ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.input_format.Value());
  85. break;
  86. }
  87. switch (config.output_format) {
  88. /*case Regs::PixelFormat::RGBA8:
  89. {
  90. // TODO: Untested
  91. u8* dstptr = (u32*)(dest_pointer + x * 4 + y * config.output_width * 4);
  92. dstptr[0] = source_color.r;
  93. dstptr[1] = source_color.g;
  94. dstptr[2] = source_color.b;
  95. dstptr[3] = source_color.a;
  96. break;
  97. }*/
  98. case Regs::PixelFormat::RGB8:
  99. {
  100. // TODO: Most likely got the component order messed up.
  101. u8* dstptr = dest_pointer + x * 3 + y * config.output_width * 3;
  102. dstptr[0] = source_color.r; // blue
  103. dstptr[1] = source_color.g; // green
  104. dstptr[2] = source_color.b; // red
  105. break;
  106. }
  107. default:
  108. ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
  109. break;
  110. }
  111. }
  112. }
  113. DEBUG_LOG(GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
  114. config.output_height * config.output_width * 4,
  115. config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height,
  116. config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height,
  117. config.output_format.Value());
  118. }
  119. break;
  120. }
  121. // Seems like writing to this register triggers processing
  122. case GPU_REG_INDEX(command_processor_config.trigger):
  123. {
  124. const auto& config = g_regs.command_processor_config;
  125. if (config.trigger & 1)
  126. {
  127. u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress()));
  128. u32 size = config.size << 3;
  129. Pica::CommandProcessor::ProcessCommandList(buffer, size);
  130. }
  131. break;
  132. }
  133. default:
  134. break;
  135. }
  136. }
  137. // Explicitly instantiate template functions because we aren't defining this in the header:
  138. template void Read<u64>(u64 &var, const u32 addr);
  139. template void Read<u32>(u32 &var, const u32 addr);
  140. template void Read<u16>(u16 &var, const u32 addr);
  141. template void Read<u8>(u8 &var, const u32 addr);
  142. template void Write<u64>(u32 addr, const u64 data);
  143. template void Write<u32>(u32 addr, const u32 data);
  144. template void Write<u16>(u32 addr, const u16 data);
  145. template void Write<u8>(u32 addr, const u8 data);
  146. /// Update hardware
  147. void Update() {
  148. auto& framebuffer_top = g_regs.framebuffer_config[0];
  149. u64 current_ticks = Core::g_app_core->GetTicks();
  150. // Update the frame after a certain number of CPU ticks have elapsed. This assumes that the
  151. // active frame in memory is always complete to render. There also may be issues with this
  152. // becoming out-of-synch with GSP synchrinization code (as follows). At this time, this seems to
  153. // be the most effective solution for both homebrew and retail applications. With retail, this
  154. // could be moved below (and probably would guarantee more accurate synchronization). However,
  155. // primitive homebrew relies on a vertical blank interrupt to happen inevitably (regardless of a
  156. // threading reschedule).
  157. if ((current_ticks - g_last_frame_ticks) > GPU::kFrameTicks) {
  158. VideoCore::g_renderer->SwapBuffers();
  159. g_last_frame_ticks = current_ticks;
  160. }
  161. // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical
  162. // blank, we need to simulate it. Based on testing, it seems that retail applications work more
  163. // accurately when this is signalled between thread switches.
  164. if (HLE::g_reschedule) {
  165. // Synchronize line...
  166. if ((current_ticks - g_last_line_ticks) >= GPU::kFrameTicks / framebuffer_top.height) {
  167. GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
  168. g_cur_line++;
  169. g_last_line_ticks = current_ticks;
  170. }
  171. // Synchronize frame...
  172. if (g_cur_line >= framebuffer_top.height) {
  173. g_cur_line = 0;
  174. GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
  175. }
  176. }
  177. }
  178. /// Initialize hardware
  179. void Init() {
  180. kFrameCycles = 268123480 / Settings::values.gpu_refresh_rate;
  181. kFrameTicks = kFrameCycles / 3;
  182. g_cur_line = 0;
  183. g_last_frame_ticks = g_last_line_ticks = Core::g_app_core->GetTicks();
  184. auto& framebuffer_top = g_regs.framebuffer_config[0];
  185. auto& framebuffer_sub = g_regs.framebuffer_config[1];
  186. // Setup default framebuffer addresses (located in VRAM)
  187. // .. or at least these are the ones used by system applets.
  188. // There's probably a smarter way to come up with addresses
  189. // like this which does not require hardcoding.
  190. framebuffer_top.address_left1 = 0x181E6000;
  191. framebuffer_top.address_left2 = 0x1822C800;
  192. framebuffer_top.address_right1 = 0x18273000;
  193. framebuffer_top.address_right2 = 0x182B9800;
  194. framebuffer_sub.address_left1 = 0x1848F000;
  195. //framebuffer_sub.address_left2 = unknown;
  196. framebuffer_sub.address_right1 = 0x184C7800;
  197. //framebuffer_sub.address_right2 = unknown;
  198. framebuffer_top.width = 240;
  199. framebuffer_top.height = 400;
  200. framebuffer_top.stride = 3 * 240;
  201. framebuffer_top.color_format = Regs::PixelFormat::RGB8;
  202. framebuffer_top.active_fb = 0;
  203. framebuffer_sub.width = 240;
  204. framebuffer_sub.height = 320;
  205. framebuffer_sub.stride = 3 * 240;
  206. framebuffer_sub.color_format = Regs::PixelFormat::RGB8;
  207. framebuffer_sub.active_fb = 0;
  208. NOTICE_LOG(GPU, "initialized OK");
  209. }
  210. /// Shutdown hardware
  211. void Shutdown() {
  212. NOTICE_LOG(GPU, "shutdown OK");
  213. }
  214. } // namespace