OpcodeDecoding.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. // Copyright 2008 Dolphin Emulator Project
  2. // Licensed under GPLv2+
  3. // Refer to the license.txt file included.
  4. //DL facts:
  5. // Ikaruga uses (nearly) NO display lists!
  6. // Zelda WW uses TONS of display lists
  7. // Zelda TP uses almost 100% display lists except menus (we like this!)
  8. // Super Mario Galaxy has nearly all geometry and more than half of the state in DLs (great!)
  9. // Note that it IS NOT GENERALLY POSSIBLE to precompile display lists! You can compile them as they are
  10. // while interpreting them, and hope that the vertex format doesn't change, though, if you do it right
  11. // when they are called. The reason is that the vertex format affects the sizes of the vertices.
  12. #include "Common/CommonTypes.h"
  13. #include "Common/CPUDetect.h"
  14. #include "Core/Core.h"
  15. #include "Core/Host.h"
  16. #include "Core/FifoPlayer/FifoRecorder.h"
  17. #include "Core/HW/Memmap.h"
  18. #include "VideoCommon/BPMemory.h"
  19. #include "VideoCommon/CommandProcessor.h"
  20. #include "VideoCommon/CPMemory.h"
  21. #include "VideoCommon/DataReader.h"
  22. #include "VideoCommon/Fifo.h"
  23. #include "VideoCommon/OpcodeDecoding.h"
  24. #include "VideoCommon/PixelEngine.h"
  25. #include "VideoCommon/Statistics.h"
  26. #include "VideoCommon/VertexLoaderManager.h"
  27. #include "VideoCommon/VideoCommon.h"
  28. #include "VideoCommon/VideoConfig.h"
  29. #include "VideoCommon/XFMemory.h"
  30. bool g_bRecordFifoData = false;
  31. static bool s_bFifoErrorSeen = false;
  32. static u32 InterpretDisplayList(u32 address, u32 size)
  33. {
  34. u8* startAddress;
  35. if (g_use_deterministic_gpu_thread)
  36. startAddress = (u8*)PopFifoAuxBuffer(size);
  37. else
  38. startAddress = Memory::GetPointer(address);
  39. u32 cycles = 0;
  40. // Avoid the crash if Memory::GetPointer failed ..
  41. if (startAddress != nullptr)
  42. {
  43. // temporarily swap dl and non-dl (small "hack" for the stats)
  44. Statistics::SwapDL();
  45. OpcodeDecoder_Run(DataReader(startAddress, startAddress + size), &cycles, true);
  46. INCSTAT(stats.thisFrame.numDListsCalled);
  47. // un-swap
  48. Statistics::SwapDL();
  49. }
  50. return cycles;
  51. }
  52. static void InterpretDisplayListPreprocess(u32 address, u32 size)
  53. {
  54. u8* startAddress = Memory::GetPointer(address);
  55. PushFifoAuxBuffer(startAddress, size);
  56. if (startAddress != nullptr)
  57. {
  58. OpcodeDecoder_Run<true>(DataReader(startAddress, startAddress + size), nullptr, true);
  59. }
  60. }
  61. static void UnknownOpcode(u8 cmd_byte, void *buffer, bool preprocess)
  62. {
  63. // TODO(Omega): Maybe dump FIFO to file on this error
  64. PanicAlert(
  65. "GFX FIFO: Unknown Opcode (0x%02x @ %p, preprocessing=%s).\n"
  66. "This means one of the following:\n"
  67. "* The emulated GPU got desynced, disabling dual core can help\n"
  68. "* Command stream corrupted by some spurious memory bug\n"
  69. "* This really is an unknown opcode (unlikely)\n"
  70. "* Some other sort of bug\n\n"
  71. "Further errors will be sent to the Video Backend log and\n"
  72. "Dolphin will now likely crash or hang. Enjoy." ,
  73. cmd_byte,
  74. buffer,
  75. preprocess ? "yes" : "no");
  76. {
  77. SCPFifoStruct &fifo = CommandProcessor::fifo;
  78. PanicAlert(
  79. "Illegal command %02x\n"
  80. "CPBase: 0x%08x\n"
  81. "CPEnd: 0x%08x\n"
  82. "CPHiWatermark: 0x%08x\n"
  83. "CPLoWatermark: 0x%08x\n"
  84. "CPReadWriteDistance: 0x%08x\n"
  85. "CPWritePointer: 0x%08x\n"
  86. "CPReadPointer: 0x%08x\n"
  87. "CPBreakpoint: 0x%08x\n"
  88. "bFF_GPReadEnable: %s\n"
  89. "bFF_BPEnable: %s\n"
  90. "bFF_BPInt: %s\n"
  91. "bFF_Breakpoint: %s\n"
  92. "bFF_GPLinkEnable: %s\n"
  93. "bFF_HiWatermarkInt: %s\n"
  94. "bFF_LoWatermarkInt: %s\n"
  95. ,cmd_byte, fifo.CPBase, fifo.CPEnd, fifo.CPHiWatermark, fifo.CPLoWatermark, fifo.CPReadWriteDistance
  96. ,fifo.CPWritePointer, fifo.CPReadPointer, fifo.CPBreakpoint
  97. ,fifo.bFF_GPReadEnable ? "true" : "false"
  98. ,fifo.bFF_BPEnable ? "true" : "false"
  99. ,fifo.bFF_BPInt ? "true" : "false"
  100. ,fifo.bFF_Breakpoint ? "true" : "false"
  101. ,fifo.bFF_GPLinkEnable ? "true" : "false"
  102. ,fifo.bFF_HiWatermarkInt ? "true" : "false"
  103. ,fifo.bFF_LoWatermarkInt ? "true" : "false"
  104. );
  105. }
  106. }
  107. void OpcodeDecoder_Init()
  108. {
  109. s_bFifoErrorSeen = false;
  110. }
  111. void OpcodeDecoder_Shutdown()
  112. {
  113. }
  114. template <bool is_preprocess>
  115. u8* OpcodeDecoder_Run(DataReader src, u32* cycles, bool in_display_list)
  116. {
  117. u32 totalCycles = 0;
  118. u8* opcodeStart;
  119. while (true)
  120. {
  121. opcodeStart = src.GetPointer();
  122. if (!src.size())
  123. goto end;
  124. u8 cmd_byte = src.Read<u8>();
  125. int refarray;
  126. switch (cmd_byte)
  127. {
  128. case GX_NOP:
  129. totalCycles += 6; // Hm, this means that we scan over nop streams pretty slowly...
  130. break;
  131. case GX_UNKNOWN_RESET:
  132. totalCycles += 6; // Datel software uses this command
  133. DEBUG_LOG(VIDEO, "GX Reset?: %08x", cmd_byte);
  134. break;
  135. case GX_LOAD_CP_REG:
  136. {
  137. if (src.size() < 1 + 4)
  138. goto end;
  139. totalCycles += 12;
  140. u8 sub_cmd = src.Read<u8>();
  141. u32 value = src.Read<u32>();
  142. LoadCPReg(sub_cmd, value, is_preprocess);
  143. if (!is_preprocess)
  144. INCSTAT(stats.thisFrame.numCPLoads);
  145. }
  146. break;
  147. case GX_LOAD_XF_REG:
  148. {
  149. if (src.size() < 4)
  150. goto end;
  151. u32 Cmd2 = src.Read<u32>();
  152. int transfer_size = ((Cmd2 >> 16) & 15) + 1;
  153. if (src.size() < transfer_size * sizeof(u32))
  154. goto end;
  155. totalCycles += 18 + 6 * transfer_size;
  156. if (!is_preprocess)
  157. {
  158. u32 xf_address = Cmd2 & 0xFFFF;
  159. LoadXFReg(transfer_size, xf_address, src);
  160. INCSTAT(stats.thisFrame.numXFLoads);
  161. }
  162. src.Skip<u32>(transfer_size);
  163. }
  164. break;
  165. case GX_LOAD_INDX_A: //used for position matrices
  166. refarray = 0xC;
  167. goto load_indx;
  168. case GX_LOAD_INDX_B: //used for normal matrices
  169. refarray = 0xD;
  170. goto load_indx;
  171. case GX_LOAD_INDX_C: //used for postmatrices
  172. refarray = 0xE;
  173. goto load_indx;
  174. case GX_LOAD_INDX_D: //used for lights
  175. refarray = 0xF;
  176. goto load_indx;
  177. load_indx:
  178. if (src.size() < 4)
  179. goto end;
  180. totalCycles += 6;
  181. if (is_preprocess)
  182. PreprocessIndexedXF(src.Read<u32>(), refarray);
  183. else
  184. LoadIndexedXF(src.Read<u32>(), refarray);
  185. break;
  186. case GX_CMD_CALL_DL:
  187. {
  188. if (src.size() < 8)
  189. goto end;
  190. u32 address = src.Read<u32>();
  191. u32 count = src.Read<u32>();
  192. if (in_display_list)
  193. {
  194. totalCycles += 6;
  195. WARN_LOG(VIDEO,"recursive display list detected");
  196. }
  197. else
  198. {
  199. if (is_preprocess)
  200. InterpretDisplayListPreprocess(address, count);
  201. else
  202. totalCycles += 6 + InterpretDisplayList(address, count);
  203. }
  204. }
  205. break;
  206. case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that
  207. totalCycles += 6;
  208. DEBUG_LOG(VIDEO, "GX 0x44: %08x", cmd_byte);
  209. break;
  210. case GX_CMD_INVL_VC: // Invalidate Vertex Cache
  211. totalCycles += 6;
  212. DEBUG_LOG(VIDEO, "Invalidate (vertex cache?)");
  213. break;
  214. case GX_LOAD_BP_REG:
  215. // In skipped_frame case: We have to let BP writes through because they set
  216. // tokens and stuff. TODO: Call a much simplified LoadBPReg instead.
  217. {
  218. if (src.size() < 4)
  219. goto end;
  220. totalCycles += 12;
  221. u32 bp_cmd = src.Read<u32>();
  222. if (is_preprocess)
  223. {
  224. LoadBPRegPreprocess(bp_cmd);
  225. }
  226. else
  227. {
  228. LoadBPReg(bp_cmd);
  229. INCSTAT(stats.thisFrame.numBPLoads);
  230. }
  231. }
  232. break;
  233. // draw primitives
  234. default:
  235. if ((cmd_byte & 0xC0) == 0x80)
  236. {
  237. // load vertices
  238. if (src.size() < 2)
  239. goto end;
  240. u16 num_vertices = src.Read<u16>();
  241. int bytes = VertexLoaderManager::RunVertices(
  242. cmd_byte & GX_VAT_MASK, // Vertex loader index (0 - 7)
  243. (cmd_byte & GX_PRIMITIVE_MASK) >> GX_PRIMITIVE_SHIFT,
  244. num_vertices,
  245. src,
  246. g_bSkipCurrentFrame,
  247. is_preprocess);
  248. if (bytes < 0)
  249. goto end;
  250. src.Skip(bytes);
  251. // 4 GPU ticks per vertex, 3 CPU ticks per GPU tick
  252. totalCycles += num_vertices * 4 * 3 + 6;
  253. }
  254. else
  255. {
  256. if (!s_bFifoErrorSeen)
  257. UnknownOpcode(cmd_byte, opcodeStart, is_preprocess);
  258. ERROR_LOG(VIDEO, "FIFO: Unknown Opcode(0x%02x @ %p, preprocessing = %s)", cmd_byte, opcodeStart, is_preprocess ? "yes" : "no");
  259. s_bFifoErrorSeen = true;
  260. totalCycles += 1;
  261. }
  262. break;
  263. }
  264. // Display lists get added directly into the FIFO stream
  265. if (!is_preprocess && g_bRecordFifoData && cmd_byte != GX_CMD_CALL_DL)
  266. {
  267. u8* opcodeEnd;
  268. opcodeEnd = src.GetPointer();
  269. FifoRecorder::GetInstance().WriteGPCommand(opcodeStart, u32(opcodeEnd - opcodeStart));
  270. }
  271. }
  272. end:
  273. if (cycles)
  274. {
  275. *cycles = totalCycles;
  276. }
  277. return opcodeStart;
  278. }
  279. template u8* OpcodeDecoder_Run<true>(DataReader src, u32* cycles, bool in_display_list);
  280. template u8* OpcodeDecoder_Run<false>(DataReader src, u32* cycles, bool in_display_list);