XFStructs.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. // Copyright 2008 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoCommon/XFStructs.h"
  4. #include <bit>
  5. #include "Common/CommonTypes.h"
  6. #include "Common/Logging/Log.h"
  7. #include "Common/Swap.h"
  8. #include "Core/DolphinAnalytics.h"
  9. #include "Core/HW/Memmap.h"
  10. #include "Core/System.h"
  11. #include "VideoCommon/CPMemory.h"
  12. #include "VideoCommon/Fifo.h"
  13. #include "VideoCommon/GeometryShaderManager.h"
  14. #include "VideoCommon/PixelShaderManager.h"
  15. #include "VideoCommon/VertexLoaderManager.h"
  16. #include "VideoCommon/VertexManagerBase.h"
  17. #include "VideoCommon/XFMemory.h"
  18. #include "VideoCommon/XFStateManager.h"
  19. static void XFMemWritten(XFStateManager& xf_state_manager, u32 transferSize, u32 baseAddress)
  20. {
  21. g_vertex_manager->Flush();
  22. xf_state_manager.InvalidateXFRange(baseAddress, baseAddress + transferSize);
  23. }
  24. static void XFRegWritten(Core::System& system, XFStateManager& xf_state_manager, u32 address,
  25. u32 value)
  26. {
  27. if (address >= XFMEM_REGISTERS_START && address < XFMEM_REGISTERS_END)
  28. {
  29. switch (address)
  30. {
  31. case XFMEM_ERROR:
  32. case XFMEM_DIAG:
  33. case XFMEM_STATE0: // internal state 0
  34. case XFMEM_STATE1: // internal state 1
  35. case XFMEM_CLOCK:
  36. case XFMEM_SETGPMETRIC:
  37. // Not implemented
  38. break;
  39. case XFMEM_CLIPDISABLE:
  40. {
  41. ClipDisable setting{.hex = value};
  42. if (setting.disable_clipping_detection)
  43. DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::SETS_XF_CLIPDISABLE_BIT_0);
  44. if (setting.disable_trivial_rejection)
  45. DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::SETS_XF_CLIPDISABLE_BIT_1);
  46. if (setting.disable_cpoly_clipping_acceleration)
  47. DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::SETS_XF_CLIPDISABLE_BIT_2);
  48. break;
  49. }
  50. case XFMEM_VTXSPECS: //__GXXfVtxSpecs, wrote 0004
  51. VertexLoaderManager::g_needs_cp_xf_consistency_check = true;
  52. break;
  53. case XFMEM_SETNUMCHAN:
  54. if (xfmem.numChan.numColorChans != (value & 3))
  55. g_vertex_manager->Flush();
  56. xf_state_manager.SetLightingConfigChanged();
  57. break;
  58. case XFMEM_SETCHAN0_AMBCOLOR: // Channel Ambient Color
  59. case XFMEM_SETCHAN1_AMBCOLOR:
  60. {
  61. u8 chan = address - XFMEM_SETCHAN0_AMBCOLOR;
  62. if (xfmem.ambColor[chan] != value)
  63. {
  64. g_vertex_manager->Flush();
  65. xf_state_manager.SetMaterialColorChanged(chan);
  66. }
  67. break;
  68. }
  69. case XFMEM_SETCHAN0_MATCOLOR: // Channel Material Color
  70. case XFMEM_SETCHAN1_MATCOLOR:
  71. {
  72. u8 chan = address - XFMEM_SETCHAN0_MATCOLOR;
  73. if (xfmem.matColor[chan] != value)
  74. {
  75. g_vertex_manager->Flush();
  76. xf_state_manager.SetMaterialColorChanged(chan + 2);
  77. }
  78. break;
  79. }
  80. case XFMEM_SETCHAN0_COLOR: // Channel Color
  81. case XFMEM_SETCHAN1_COLOR:
  82. case XFMEM_SETCHAN0_ALPHA: // Channel Alpha
  83. case XFMEM_SETCHAN1_ALPHA:
  84. if (((u32*)&xfmem)[address] != (value & 0x7fff))
  85. g_vertex_manager->Flush();
  86. xf_state_manager.SetLightingConfigChanged();
  87. break;
  88. case XFMEM_DUALTEX:
  89. if (xfmem.dualTexTrans.enabled != bool(value & 1))
  90. g_vertex_manager->Flush();
  91. xf_state_manager.SetTexMatrixInfoChanged(-1);
  92. break;
  93. case XFMEM_SETMATRIXINDA:
  94. xf_state_manager.SetTexMatrixChangedA(value);
  95. VertexLoaderManager::g_needs_cp_xf_consistency_check = true;
  96. break;
  97. case XFMEM_SETMATRIXINDB:
  98. xf_state_manager.SetTexMatrixChangedB(value);
  99. VertexLoaderManager::g_needs_cp_xf_consistency_check = true;
  100. break;
  101. case XFMEM_SETVIEWPORT:
  102. case XFMEM_SETVIEWPORT + 1:
  103. case XFMEM_SETVIEWPORT + 2:
  104. case XFMEM_SETVIEWPORT + 3:
  105. case XFMEM_SETVIEWPORT + 4:
  106. case XFMEM_SETVIEWPORT + 5:
  107. g_vertex_manager->Flush();
  108. xf_state_manager.SetViewportChanged();
  109. system.GetPixelShaderManager().SetViewportChanged();
  110. system.GetGeometryShaderManager().SetViewportChanged();
  111. break;
  112. case XFMEM_SETPROJECTION:
  113. case XFMEM_SETPROJECTION + 1:
  114. case XFMEM_SETPROJECTION + 2:
  115. case XFMEM_SETPROJECTION + 3:
  116. case XFMEM_SETPROJECTION + 4:
  117. case XFMEM_SETPROJECTION + 5:
  118. case XFMEM_SETPROJECTION + 6:
  119. g_vertex_manager->Flush();
  120. xf_state_manager.SetProjectionChanged();
  121. system.GetGeometryShaderManager().SetProjectionChanged();
  122. break;
  123. case XFMEM_SETNUMTEXGENS: // GXSetNumTexGens
  124. if (xfmem.numTexGen.numTexGens != (value & 15))
  125. g_vertex_manager->Flush();
  126. break;
  127. case XFMEM_SETTEXMTXINFO:
  128. case XFMEM_SETTEXMTXINFO + 1:
  129. case XFMEM_SETTEXMTXINFO + 2:
  130. case XFMEM_SETTEXMTXINFO + 3:
  131. case XFMEM_SETTEXMTXINFO + 4:
  132. case XFMEM_SETTEXMTXINFO + 5:
  133. case XFMEM_SETTEXMTXINFO + 6:
  134. case XFMEM_SETTEXMTXINFO + 7:
  135. g_vertex_manager->Flush();
  136. xf_state_manager.SetTexMatrixInfoChanged(address - XFMEM_SETTEXMTXINFO);
  137. break;
  138. case XFMEM_SETPOSTMTXINFO:
  139. case XFMEM_SETPOSTMTXINFO + 1:
  140. case XFMEM_SETPOSTMTXINFO + 2:
  141. case XFMEM_SETPOSTMTXINFO + 3:
  142. case XFMEM_SETPOSTMTXINFO + 4:
  143. case XFMEM_SETPOSTMTXINFO + 5:
  144. case XFMEM_SETPOSTMTXINFO + 6:
  145. case XFMEM_SETPOSTMTXINFO + 7:
  146. g_vertex_manager->Flush();
  147. xf_state_manager.SetTexMatrixInfoChanged(address - XFMEM_SETPOSTMTXINFO);
  148. break;
  149. // --------------
  150. // Unknown Regs
  151. // --------------
  152. // Maybe these are for Normals?
  153. case 0x1048: // xfmem.texcoords[0].nrmmtxinfo.hex = data; break; ??
  154. case 0x1049:
  155. case 0x104a:
  156. case 0x104b:
  157. case 0x104c:
  158. case 0x104d:
  159. case 0x104e:
  160. case 0x104f:
  161. DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNKNOWN_XF_COMMAND);
  162. DEBUG_LOG_FMT(VIDEO, "Possible Normal Mtx XF reg?: {:x}={:x}", address, value);
  163. break;
  164. case 0x1013:
  165. case 0x1014:
  166. case 0x1015:
  167. case 0x1016:
  168. case 0x1017:
  169. default:
  170. DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_UNKNOWN_XF_COMMAND);
  171. WARN_LOG_FMT(VIDEO, "Unknown XF Reg: {:x}={:x}", address, value);
  172. break;
  173. }
  174. }
  175. }
  176. void LoadXFReg(u16 base_address, u8 transfer_size, const u8* data)
  177. {
  178. if (base_address > XFMEM_REGISTERS_END)
  179. {
  180. WARN_LOG_FMT(VIDEO, "XF load base address past end of address space: {:x} {} bytes",
  181. base_address, transfer_size);
  182. return;
  183. }
  184. u32 end_address = base_address + transfer_size; // exclusive
  185. // do not allow writes past registers
  186. if (end_address > XFMEM_REGISTERS_END)
  187. {
  188. WARN_LOG_FMT(VIDEO, "XF load ends past end of address space: {:x} {} bytes", base_address,
  189. transfer_size);
  190. end_address = XFMEM_REGISTERS_END;
  191. }
  192. auto& system = Core::System::GetInstance();
  193. auto& xf_state_manager = system.GetXFStateManager();
  194. // write to XF mem
  195. if (base_address < XFMEM_REGISTERS_START)
  196. {
  197. const u32 xf_mem_base = base_address;
  198. u32 xf_mem_transfer_size = transfer_size;
  199. if (end_address > XFMEM_REGISTERS_START)
  200. {
  201. xf_mem_transfer_size = XFMEM_REGISTERS_START - base_address;
  202. base_address = XFMEM_REGISTERS_START;
  203. }
  204. XFMemWritten(xf_state_manager, xf_mem_transfer_size, xf_mem_base);
  205. for (u32 i = 0; i < xf_mem_transfer_size; i++)
  206. {
  207. ((u32*)&xfmem)[xf_mem_base + i] = Common::swap32(data);
  208. data += 4;
  209. }
  210. }
  211. // write to XF regs
  212. if (base_address >= XFMEM_REGISTERS_START)
  213. {
  214. for (u32 address = base_address; address < end_address; address++)
  215. {
  216. const u32 value = Common::swap32(data);
  217. XFRegWritten(system, xf_state_manager, address, value);
  218. ((u32*)&xfmem)[address] = value;
  219. data += 4;
  220. }
  221. }
  222. }
  223. // TODO - verify that it is correct. Seems to work, though.
  224. void LoadIndexedXF(CPArray array, u32 index, u16 address, u8 size)
  225. {
  226. // load stuff from array to address in xf mem
  227. const u32 buf_size = size * sizeof(u32);
  228. u32* currData = reinterpret_cast<u32*>(&xfmem) + address;
  229. u32* newData;
  230. auto& system = Core::System::GetInstance();
  231. auto& fifo = system.GetFifo();
  232. if (fifo.UseDeterministicGPUThread())
  233. {
  234. newData = reinterpret_cast<u32*>(fifo.PopFifoAuxBuffer(buf_size));
  235. }
  236. else
  237. {
  238. auto& memory = system.GetMemory();
  239. newData = reinterpret_cast<u32*>(memory.GetPointerForRange(
  240. g_main_cp_state.array_bases[array] + g_main_cp_state.array_strides[array] * index,
  241. buf_size));
  242. }
  243. auto& xf_state_manager = system.GetXFStateManager();
  244. bool changed = false;
  245. for (u32 i = 0; i < size; ++i)
  246. {
  247. if (currData[i] != Common::swap32(newData[i]))
  248. {
  249. changed = true;
  250. XFMemWritten(xf_state_manager, size, address);
  251. break;
  252. }
  253. }
  254. if (changed)
  255. {
  256. for (u32 i = 0; i < size; ++i)
  257. currData[i] = Common::swap32(newData[i]);
  258. }
  259. }
  260. void PreprocessIndexedXF(CPArray array, u32 index, u16 address, u8 size)
  261. {
  262. const size_t buf_size = size * sizeof(u32);
  263. auto& system = Core::System::GetInstance();
  264. auto& memory = system.GetMemory();
  265. const u8* new_data = memory.GetPointerForRange(
  266. g_preprocess_cp_state.array_bases[array] + g_preprocess_cp_state.array_strides[array] * index,
  267. buf_size);
  268. system.GetFifo().PushFifoAuxBuffer(new_data, buf_size);
  269. }
  270. std::pair<std::string, std::string> GetXFRegInfo(u32 address, u32 value)
  271. {
  272. // Macro to set the register name and make sure it was written correctly via compile time assertion
  273. #define RegName(reg) ((void)(reg), #reg)
  274. #define DescriptionlessReg(reg) std::make_pair(RegName(reg), "");
  275. switch (address)
  276. {
  277. case XFMEM_ERROR:
  278. return DescriptionlessReg(XFMEM_ERROR);
  279. case XFMEM_DIAG:
  280. return DescriptionlessReg(XFMEM_DIAG);
  281. case XFMEM_STATE0: // internal state 0
  282. return std::make_pair(RegName(XFMEM_STATE0), "internal state 0");
  283. case XFMEM_STATE1: // internal state 1
  284. return std::make_pair(RegName(XFMEM_STATE1), "internal state 1");
  285. case XFMEM_CLOCK:
  286. return DescriptionlessReg(XFMEM_CLOCK);
  287. case XFMEM_SETGPMETRIC:
  288. return DescriptionlessReg(XFMEM_SETGPMETRIC);
  289. case XFMEM_CLIPDISABLE:
  290. return std::make_pair(RegName(XFMEM_CLIPDISABLE), fmt::to_string(ClipDisable{.hex = value}));
  291. case XFMEM_VTXSPECS:
  292. return std::make_pair(RegName(XFMEM_VTXSPECS), fmt::to_string(INVTXSPEC{.hex = value}));
  293. case XFMEM_SETNUMCHAN:
  294. return std::make_pair(RegName(XFMEM_SETNUMCHAN),
  295. fmt::format("Number of color channels: {}", value & 3));
  296. break;
  297. case XFMEM_SETCHAN0_AMBCOLOR:
  298. return std::make_pair(RegName(XFMEM_SETCHAN0_AMBCOLOR),
  299. fmt::format("Channel 0 Ambient Color: {:08x}", value));
  300. case XFMEM_SETCHAN1_AMBCOLOR:
  301. return std::make_pair(RegName(XFMEM_SETCHAN1_AMBCOLOR),
  302. fmt::format("Channel 1 Ambient Color: {:08x}", value));
  303. case XFMEM_SETCHAN0_MATCOLOR:
  304. return std::make_pair(RegName(XFMEM_SETCHAN0_MATCOLOR),
  305. fmt::format("Channel 0 Material Color: {:08x}", value));
  306. case XFMEM_SETCHAN1_MATCOLOR:
  307. return std::make_pair(RegName(XFMEM_SETCHAN1_MATCOLOR),
  308. fmt::format("Channel 1 Material Color: {:08x}", value));
  309. case XFMEM_SETCHAN0_COLOR: // Channel Color
  310. return std::make_pair(RegName(XFMEM_SETCHAN0_COLOR),
  311. fmt::format("Channel 0 Color config:\n{}", LitChannel{.hex = value}));
  312. case XFMEM_SETCHAN1_COLOR:
  313. return std::make_pair(RegName(XFMEM_SETCHAN1_COLOR),
  314. fmt::format("Channel 1 Color config:\n{}", LitChannel{.hex = value}));
  315. case XFMEM_SETCHAN0_ALPHA: // Channel Alpha
  316. return std::make_pair(RegName(XFMEM_SETCHAN0_ALPHA),
  317. fmt::format("Channel 0 Alpha config:\n{}", LitChannel{.hex = value}));
  318. case XFMEM_SETCHAN1_ALPHA:
  319. return std::make_pair(RegName(XFMEM_SETCHAN1_ALPHA),
  320. fmt::format("Channel 1 Alpha config:\n{}", LitChannel{.hex = value}));
  321. case XFMEM_DUALTEX:
  322. return std::make_pair(RegName(XFMEM_DUALTEX),
  323. fmt::format("Dual Tex Trans {}", (value & 1) ? "enabled" : "disabled"));
  324. case XFMEM_SETMATRIXINDA:
  325. return std::make_pair(RegName(XFMEM_SETMATRIXINDA),
  326. fmt::format("Matrix index A:\n{}", TMatrixIndexA{.Hex = value}));
  327. case XFMEM_SETMATRIXINDB:
  328. return std::make_pair(RegName(XFMEM_SETMATRIXINDB),
  329. fmt::format("Matrix index B:\n{}", TMatrixIndexB{.Hex = value}));
  330. case XFMEM_SETVIEWPORT:
  331. return std::make_pair(RegName(XFMEM_SETVIEWPORT + 0),
  332. fmt::format("Viewport width: {}", std::bit_cast<float>(value)));
  333. case XFMEM_SETVIEWPORT + 1:
  334. return std::make_pair(RegName(XFMEM_SETVIEWPORT + 1),
  335. fmt::format("Viewport height: {}", std::bit_cast<float>(value)));
  336. case XFMEM_SETVIEWPORT + 2:
  337. return std::make_pair(RegName(XFMEM_SETVIEWPORT + 2),
  338. fmt::format("Viewport z range: {}", std::bit_cast<float>(value)));
  339. case XFMEM_SETVIEWPORT + 3:
  340. return std::make_pair(RegName(XFMEM_SETVIEWPORT + 3),
  341. fmt::format("Viewport x origin: {}", std::bit_cast<float>(value)));
  342. case XFMEM_SETVIEWPORT + 4:
  343. return std::make_pair(RegName(XFMEM_SETVIEWPORT + 4),
  344. fmt::format("Viewport y origin: {}", std::bit_cast<float>(value)));
  345. case XFMEM_SETVIEWPORT + 5:
  346. return std::make_pair(RegName(XFMEM_SETVIEWPORT + 5),
  347. fmt::format("Viewport far z: {}", std::bit_cast<float>(value)));
  348. break;
  349. case XFMEM_SETPROJECTION:
  350. return std::make_pair(RegName(XFMEM_SETPROJECTION + 0),
  351. fmt::format("Projection[0]: {}", std::bit_cast<float>(value)));
  352. case XFMEM_SETPROJECTION + 1:
  353. return std::make_pair(RegName(XFMEM_SETPROJECTION + 1),
  354. fmt::format("Projection[1]: {}", std::bit_cast<float>(value)));
  355. case XFMEM_SETPROJECTION + 2:
  356. return std::make_pair(RegName(XFMEM_SETPROJECTION + 2),
  357. fmt::format("Projection[2]: {}", std::bit_cast<float>(value)));
  358. case XFMEM_SETPROJECTION + 3:
  359. return std::make_pair(RegName(XFMEM_SETPROJECTION + 3),
  360. fmt::format("Projection[3]: {}", std::bit_cast<float>(value)));
  361. case XFMEM_SETPROJECTION + 4:
  362. return std::make_pair(RegName(XFMEM_SETPROJECTION + 4),
  363. fmt::format("Projection[4]: {}", std::bit_cast<float>(value)));
  364. case XFMEM_SETPROJECTION + 5:
  365. return std::make_pair(RegName(XFMEM_SETPROJECTION + 5),
  366. fmt::format("Projection[5]: {}", std::bit_cast<float>(value)));
  367. case XFMEM_SETPROJECTION + 6:
  368. return std::make_pair(RegName(XFMEM_SETPROJECTION + 6),
  369. fmt::to_string(static_cast<ProjectionType>(value)));
  370. case XFMEM_SETNUMTEXGENS:
  371. return std::make_pair(RegName(XFMEM_SETNUMTEXGENS),
  372. fmt::format("Number of tex gens: {}", value & 15));
  373. case XFMEM_SETTEXMTXINFO:
  374. case XFMEM_SETTEXMTXINFO + 1:
  375. case XFMEM_SETTEXMTXINFO + 2:
  376. case XFMEM_SETTEXMTXINFO + 3:
  377. case XFMEM_SETTEXMTXINFO + 4:
  378. case XFMEM_SETTEXMTXINFO + 5:
  379. case XFMEM_SETTEXMTXINFO + 6:
  380. case XFMEM_SETTEXMTXINFO + 7:
  381. return std::make_pair(
  382. fmt::format("XFMEM_SETTEXMTXINFO Matrix {}", address - XFMEM_SETTEXMTXINFO),
  383. fmt::to_string(TexMtxInfo{.hex = value}));
  384. case XFMEM_SETPOSTMTXINFO:
  385. case XFMEM_SETPOSTMTXINFO + 1:
  386. case XFMEM_SETPOSTMTXINFO + 2:
  387. case XFMEM_SETPOSTMTXINFO + 3:
  388. case XFMEM_SETPOSTMTXINFO + 4:
  389. case XFMEM_SETPOSTMTXINFO + 5:
  390. case XFMEM_SETPOSTMTXINFO + 6:
  391. case XFMEM_SETPOSTMTXINFO + 7:
  392. return std::make_pair(
  393. fmt::format("XFMEM_SETPOSTMTXINFO Matrix {}", address - XFMEM_SETPOSTMTXINFO),
  394. fmt::to_string(PostMtxInfo{.hex = value}));
  395. // --------------
  396. // Unknown Regs
  397. // --------------
  398. // Maybe these are for Normals?
  399. case 0x1048: // xfmem.texcoords[0].nrmmtxinfo.hex = data; break; ??
  400. case 0x1049:
  401. case 0x104a:
  402. case 0x104b:
  403. case 0x104c:
  404. case 0x104d:
  405. case 0x104e:
  406. case 0x104f:
  407. return std::make_pair(
  408. fmt::format("Possible Normal Mtx XF reg?: {:x}={:x}", address, value),
  409. "Maybe these are for Normals? xfmem.texcoords[0].nrmmtxinfo.hex = data; break; ??");
  410. break;
  411. case 0x1013:
  412. case 0x1014:
  413. case 0x1015:
  414. case 0x1016:
  415. case 0x1017:
  416. default:
  417. return std::make_pair(fmt::format("Unknown XF Reg: {:x}={:x}", address, value), "");
  418. }
  419. #undef RegName
  420. #undef DescriptionlessReg
  421. }
  422. std::string GetXFMemName(u32 address)
  423. {
  424. if (address >= XFMEM_POSMATRICES && address < XFMEM_POSMATRICES_END)
  425. {
  426. const u32 row = (address - XFMEM_POSMATRICES) / 4;
  427. const u32 col = (address - XFMEM_POSMATRICES) % 4;
  428. return fmt::format("Position matrix row {:2d} col {:2d}", row, col);
  429. }
  430. else if (address >= XFMEM_NORMALMATRICES && address < XFMEM_NORMALMATRICES_END)
  431. {
  432. const u32 row = (address - XFMEM_NORMALMATRICES) / 3;
  433. const u32 col = (address - XFMEM_NORMALMATRICES) % 3;
  434. return fmt::format("Normal matrix row {:2d} col {:2d}", row, col);
  435. }
  436. else if (address >= XFMEM_POSTMATRICES && address < XFMEM_POSTMATRICES_END)
  437. {
  438. const u32 row = (address - XFMEM_POSTMATRICES) / 4;
  439. const u32 col = (address - XFMEM_POSTMATRICES) % 4;
  440. return fmt::format("Post matrix row {:2d} col {:2d}", row, col);
  441. }
  442. else if (address >= XFMEM_LIGHTS && address < XFMEM_LIGHTS_END)
  443. {
  444. const u32 light = (address - XFMEM_LIGHTS) / 16;
  445. const u32 offset = (address - XFMEM_LIGHTS) % 16;
  446. switch (offset)
  447. {
  448. default:
  449. return fmt::format("Light {} unused param {}", light, offset);
  450. case 3:
  451. return fmt::format("Light {} color", light);
  452. case 4:
  453. case 5:
  454. case 6:
  455. return fmt::format("Light {} cosine attenuation {}", light, offset - 4);
  456. case 7:
  457. case 8:
  458. case 9:
  459. return fmt::format("Light {} distance attenuation {}", light, offset - 7);
  460. case 10:
  461. case 11:
  462. case 12:
  463. // Yagcd says light pos or "inf ldir", while dolphin has a union for dpos and sdir with only
  464. // dpos being used nowadays. As far as I can tell only the DX9 engine once at
  465. // Source/Plugins/Plugin_VideoDX9/Src/TransformEngine.cpp used sdir directly...
  466. return fmt::format("Light {0} {1} position or inf ldir {1}", light, "xyz"[offset - 10]);
  467. case 13:
  468. case 14:
  469. case 15:
  470. // Yagcd says light dir or "1/2 angle", dolphin has union for ddir or shalfangle.
  471. // It would make sense if d stood for direction and s for specular, but it's ddir and
  472. // shalfangle that have the comment "specular lights only", both at the same offset,
  473. // while dpos and sdir have none...
  474. return fmt::format("Light {0} {1} direction or half angle {1}", light, "xyz"[offset - 13]);
  475. }
  476. }
  477. else
  478. {
  479. return fmt::format("Unknown memory {:04x}", address);
  480. }
  481. }
  482. std::string GetXFMemDescription(u32 address, u32 value)
  483. {
  484. if ((address >= XFMEM_POSMATRICES && address < XFMEM_POSMATRICES_END) ||
  485. (address >= XFMEM_NORMALMATRICES && address < XFMEM_NORMALMATRICES_END) ||
  486. (address >= XFMEM_POSTMATRICES && address < XFMEM_POSTMATRICES_END))
  487. {
  488. // The matrices all use floats
  489. return fmt::format("{} = {}", GetXFMemName(address), std::bit_cast<float>(value));
  490. }
  491. else if (address >= XFMEM_LIGHTS && address < XFMEM_LIGHTS_END)
  492. {
  493. // Each light is 16 words; for this function we don't care which light it is
  494. const u32 offset = (address - XFMEM_LIGHTS) % 16;
  495. if (offset <= 3)
  496. {
  497. // The unused parameters (0, 1, 2) and the color (3) should be hex-formatted
  498. return fmt::format("{} = {:08x}", GetXFMemName(address), value);
  499. }
  500. else
  501. {
  502. // Everything else is a float
  503. return fmt::format("{} = {}", GetXFMemName(address), std::bit_cast<float>(value));
  504. }
  505. }
  506. else
  507. {
  508. // Unknown address
  509. return fmt::format("{} = {:08x}", GetXFMemName(address), value);
  510. }
  511. }
  512. std::pair<std::string, std::string> GetXFTransferInfo(u16 base_address, u8 transfer_size,
  513. const u8* data)
  514. {
  515. if (base_address > XFMEM_REGISTERS_END)
  516. {
  517. return std::make_pair("Invalid XF Transfer", "Base address past end of address space");
  518. }
  519. else if (transfer_size == 1 && base_address >= XFMEM_REGISTERS_START)
  520. {
  521. // Write directly to a single register
  522. const u32 value = Common::swap32(data);
  523. return GetXFRegInfo(base_address, value);
  524. }
  525. // More complicated cases
  526. fmt::memory_buffer name, desc;
  527. u32 end_address = base_address + transfer_size; // exclusive
  528. // do not allow writes past registers
  529. if (end_address > XFMEM_REGISTERS_END)
  530. {
  531. fmt::format_to(std::back_inserter(name), "Invalid XF Transfer ");
  532. fmt::format_to(std::back_inserter(desc), "Transfer ends past end of address space\n\n");
  533. end_address = XFMEM_REGISTERS_END;
  534. }
  535. // write to XF mem
  536. if (base_address < XFMEM_REGISTERS_START)
  537. {
  538. const u32 xf_mem_base = base_address;
  539. u32 xf_mem_transfer_size = transfer_size;
  540. if (end_address > XFMEM_REGISTERS_START)
  541. {
  542. xf_mem_transfer_size = XFMEM_REGISTERS_START - base_address;
  543. base_address = XFMEM_REGISTERS_START;
  544. }
  545. fmt::format_to(std::back_inserter(name), "Write {} XF mem words at {:04x}",
  546. xf_mem_transfer_size, xf_mem_base);
  547. for (u32 i = 0; i < xf_mem_transfer_size; i++)
  548. {
  549. const auto mem_desc = GetXFMemDescription(xf_mem_base + i, Common::swap32(data));
  550. fmt::format_to(std::back_inserter(desc), "{}{}", i != 0 ? "\n" : "", mem_desc);
  551. data += 4;
  552. }
  553. if (end_address > XFMEM_REGISTERS_START)
  554. fmt::format_to(std::back_inserter(name), "; ");
  555. }
  556. // write to XF regs
  557. if (base_address >= XFMEM_REGISTERS_START)
  558. {
  559. fmt::format_to(std::back_inserter(name), "Write {} XF regs at {:04x}",
  560. end_address - base_address, base_address);
  561. for (u32 address = base_address; address < end_address; address++)
  562. {
  563. const u32 value = Common::swap32(data);
  564. const auto [regname, regdesc] = GetXFRegInfo(address, value);
  565. fmt::format_to(std::back_inserter(desc), "{}\n{}\n", regname, regdesc);
  566. data += 4;
  567. }
  568. }
  569. return std::make_pair(fmt::to_string(name), fmt::to_string(desc));
  570. }
  571. std::pair<std::string, std::string> GetXFIndexedLoadInfo(CPArray array, u32 index, u16 address,
  572. u8 size)
  573. {
  574. const auto desc = fmt::format("Load {} words to XF address {:03x} from CP array {} row {}", size,
  575. address, array, index);
  576. fmt::memory_buffer written;
  577. for (u32 i = 0; i < size; i++)
  578. {
  579. fmt::format_to(std::back_inserter(written), "{}\n", GetXFMemName(address + i));
  580. }
  581. return std::make_pair(desc, fmt::to_string(written));
  582. }