gpu.cpp 10 KB

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