IndexGenerator.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. // Copyright 2008 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoCommon/IndexGenerator.h"
  4. #include <array>
  5. #include <cstddef>
  6. #include <cstring>
  7. #include "Common/CommonTypes.h"
  8. #include "Common/Logging/Log.h"
  9. #include "VideoCommon/OpcodeDecoding.h"
  10. #include "VideoCommon/VideoConfig.h"
  11. namespace
  12. {
  13. constexpr u16 s_primitive_restart = UINT16_MAX;
  14. template <bool pr>
  15. u16* WriteTriangle(u16* index_ptr, u32 index1, u32 index2, u32 index3)
  16. {
  17. *index_ptr++ = index1;
  18. *index_ptr++ = index2;
  19. *index_ptr++ = index3;
  20. if constexpr (pr)
  21. *index_ptr++ = s_primitive_restart;
  22. return index_ptr;
  23. }
  24. template <bool pr>
  25. u16* AddList(u16* index_ptr, u32 num_verts, u32 index)
  26. {
  27. for (u32 i = 2; i < num_verts; i += 3)
  28. {
  29. index_ptr = WriteTriangle<pr>(index_ptr, index + i - 2, index + i - 1, index + i);
  30. }
  31. return index_ptr;
  32. }
  33. template <bool pr>
  34. u16* AddStrip(u16* index_ptr, u32 num_verts, u32 index)
  35. {
  36. if constexpr (pr)
  37. {
  38. for (u32 i = 0; i < num_verts; ++i)
  39. {
  40. *index_ptr++ = index + i;
  41. }
  42. *index_ptr++ = s_primitive_restart;
  43. }
  44. else
  45. {
  46. bool wind = false;
  47. for (u32 i = 2; i < num_verts; ++i)
  48. {
  49. index_ptr = WriteTriangle<pr>(index_ptr, index + i - 2, index + i - !wind, index + i - wind);
  50. wind ^= true;
  51. }
  52. }
  53. return index_ptr;
  54. }
  55. /**
  56. * FAN simulator:
  57. *
  58. * 2---3
  59. * / \ / \
  60. * 1---0---4
  61. *
  62. * would generate this triangles:
  63. * 012, 023, 034
  64. *
  65. * rotated (for better striping):
  66. * 120, 302, 034
  67. *
  68. * as odd ones have to winded, following strip is fine:
  69. * 12034
  70. *
  71. * so we use 6 indices for 3 triangles
  72. */
  73. template <bool pr>
  74. u16* AddFan(u16* index_ptr, u32 num_verts, u32 index)
  75. {
  76. u32 i = 2;
  77. if constexpr (pr)
  78. {
  79. for (; i + 3 <= num_verts; i += 3)
  80. {
  81. *index_ptr++ = index + i - 1;
  82. *index_ptr++ = index + i + 0;
  83. *index_ptr++ = index;
  84. *index_ptr++ = index + i + 1;
  85. *index_ptr++ = index + i + 2;
  86. *index_ptr++ = s_primitive_restart;
  87. }
  88. for (; i + 2 <= num_verts; i += 2)
  89. {
  90. *index_ptr++ = index + i - 1;
  91. *index_ptr++ = index + i + 0;
  92. *index_ptr++ = index;
  93. *index_ptr++ = index + i + 1;
  94. *index_ptr++ = s_primitive_restart;
  95. }
  96. }
  97. for (; i < num_verts; ++i)
  98. {
  99. index_ptr = WriteTriangle<pr>(index_ptr, index, index + i - 1, index + i);
  100. }
  101. return index_ptr;
  102. }
  103. /*
  104. * QUAD simulator
  105. *
  106. * 0---1 4---5
  107. * |\ | |\ |
  108. * | \ | | \ |
  109. * | \| | \|
  110. * 3---2 7---6
  111. *
  112. * 012,023, 456,467 ...
  113. * or 120,302, 564,746
  114. * or as strip: 1203, 5647
  115. *
  116. * Warning:
  117. * A simple triangle has to be rendered for three vertices.
  118. * ZWW do this for sun rays
  119. */
  120. template <bool pr>
  121. u16* AddQuads(u16* index_ptr, u32 num_verts, u32 index)
  122. {
  123. u32 i = 3;
  124. for (; i < num_verts; i += 4)
  125. {
  126. if constexpr (pr)
  127. {
  128. *index_ptr++ = index + i - 2;
  129. *index_ptr++ = index + i - 1;
  130. *index_ptr++ = index + i - 3;
  131. *index_ptr++ = index + i - 0;
  132. *index_ptr++ = s_primitive_restart;
  133. }
  134. else
  135. {
  136. index_ptr = WriteTriangle<pr>(index_ptr, index + i - 3, index + i - 2, index + i - 1);
  137. index_ptr = WriteTriangle<pr>(index_ptr, index + i - 3, index + i - 1, index + i - 0);
  138. }
  139. }
  140. // three vertices remaining, so render a triangle
  141. if (i == num_verts)
  142. {
  143. index_ptr = WriteTriangle<pr>(index_ptr, index + num_verts - 3, index + num_verts - 2,
  144. index + num_verts - 1);
  145. }
  146. return index_ptr;
  147. }
  148. template <bool pr>
  149. u16* AddQuads_nonstandard(u16* index_ptr, u32 num_verts, u32 index)
  150. {
  151. WARN_LOG_FMT(VIDEO, "Non-standard primitive drawing command GL_DRAW_QUADS_2");
  152. return AddQuads<pr>(index_ptr, num_verts, index);
  153. }
  154. u16* AddLineList(u16* index_ptr, u32 num_verts, u32 index)
  155. {
  156. for (u32 i = 1; i < num_verts; i += 2)
  157. {
  158. *index_ptr++ = index + i - 1;
  159. *index_ptr++ = index + i;
  160. }
  161. return index_ptr;
  162. }
  163. // Shouldn't be used as strips as LineLists are much more common
  164. // so converting them to lists
  165. u16* AddLineStrip(u16* index_ptr, u32 num_verts, u32 index)
  166. {
  167. for (u32 i = 1; i < num_verts; ++i)
  168. {
  169. *index_ptr++ = index + i - 1;
  170. *index_ptr++ = index + i;
  171. }
  172. return index_ptr;
  173. }
  174. template <bool pr, bool linestrip>
  175. u16* AddLines_VSExpand(u16* index_ptr, u32 num_verts, u32 index)
  176. {
  177. // VS Expand uses (index >> 2) as the base vertex
  178. // Bit 0 indicates which side of the line (left/right for a vertical line)
  179. // Bit 1 indicates which point of the line (top/bottom for a vertical line)
  180. // VS Expand assumes the two points will be adjacent vertices
  181. constexpr u32 advance = linestrip ? 1 : 2;
  182. for (u32 i = 1; i < num_verts; i += advance)
  183. {
  184. u32 p0 = (index + i - 1) << 2;
  185. u32 p1 = (index + i - 0) << 2;
  186. if constexpr (pr)
  187. {
  188. *index_ptr++ = p0 + 0;
  189. *index_ptr++ = p0 + 1;
  190. *index_ptr++ = p1 + 2;
  191. *index_ptr++ = p1 + 3;
  192. *index_ptr++ = s_primitive_restart;
  193. }
  194. else
  195. {
  196. *index_ptr++ = p0 + 0;
  197. *index_ptr++ = p0 + 1;
  198. *index_ptr++ = p1 + 2;
  199. *index_ptr++ = p0 + 1;
  200. *index_ptr++ = p1 + 2;
  201. *index_ptr++ = p1 + 3;
  202. }
  203. }
  204. return index_ptr;
  205. }
  206. u16* AddPoints(u16* index_ptr, u32 num_verts, u32 index)
  207. {
  208. for (u32 i = 0; i != num_verts; ++i)
  209. {
  210. *index_ptr++ = index + i;
  211. }
  212. return index_ptr;
  213. }
  214. template <bool pr>
  215. u16* AddPoints_VSExpand(u16* index_ptr, u32 num_verts, u32 index)
  216. {
  217. // VS Expand uses (index >> 2) as the base vertex
  218. // Bottom two bits indicate which of (TL, TR, BL, BR) this is
  219. for (u32 i = 0; i < num_verts; ++i)
  220. {
  221. u32 base = (index + i) << 2;
  222. if constexpr (pr)
  223. {
  224. *index_ptr++ = base + 0;
  225. *index_ptr++ = base + 1;
  226. *index_ptr++ = base + 2;
  227. *index_ptr++ = base + 3;
  228. *index_ptr++ = s_primitive_restart;
  229. }
  230. else
  231. {
  232. *index_ptr++ = base + 0;
  233. *index_ptr++ = base + 1;
  234. *index_ptr++ = base + 2;
  235. *index_ptr++ = base + 1;
  236. *index_ptr++ = base + 2;
  237. *index_ptr++ = base + 3;
  238. }
  239. }
  240. return index_ptr;
  241. }
  242. } // Anonymous namespace
  243. void IndexGenerator::Init()
  244. {
  245. using OpcodeDecoder::Primitive;
  246. if (g_Config.backend_info.bSupportsPrimitiveRestart)
  247. {
  248. m_primitive_table[Primitive::GX_DRAW_QUADS] = AddQuads<true>;
  249. m_primitive_table[Primitive::GX_DRAW_QUADS_2] = AddQuads_nonstandard<true>;
  250. m_primitive_table[Primitive::GX_DRAW_TRIANGLES] = AddList<true>;
  251. m_primitive_table[Primitive::GX_DRAW_TRIANGLE_STRIP] = AddStrip<true>;
  252. m_primitive_table[Primitive::GX_DRAW_TRIANGLE_FAN] = AddFan<true>;
  253. }
  254. else
  255. {
  256. m_primitive_table[Primitive::GX_DRAW_QUADS] = AddQuads<false>;
  257. m_primitive_table[Primitive::GX_DRAW_QUADS_2] = AddQuads_nonstandard<false>;
  258. m_primitive_table[Primitive::GX_DRAW_TRIANGLES] = AddList<false>;
  259. m_primitive_table[Primitive::GX_DRAW_TRIANGLE_STRIP] = AddStrip<false>;
  260. m_primitive_table[Primitive::GX_DRAW_TRIANGLE_FAN] = AddFan<false>;
  261. }
  262. if (g_Config.UseVSForLinePointExpand())
  263. {
  264. if (g_Config.backend_info.bSupportsPrimitiveRestart)
  265. {
  266. m_primitive_table[Primitive::GX_DRAW_LINES] = AddLines_VSExpand<true, false>;
  267. m_primitive_table[Primitive::GX_DRAW_LINE_STRIP] = AddLines_VSExpand<true, true>;
  268. m_primitive_table[Primitive::GX_DRAW_POINTS] = AddPoints_VSExpand<true>;
  269. }
  270. else
  271. {
  272. m_primitive_table[Primitive::GX_DRAW_LINES] = AddLines_VSExpand<false, false>;
  273. m_primitive_table[Primitive::GX_DRAW_LINE_STRIP] = AddLines_VSExpand<false, true>;
  274. m_primitive_table[Primitive::GX_DRAW_POINTS] = AddPoints_VSExpand<false>;
  275. }
  276. }
  277. else
  278. {
  279. m_primitive_table[Primitive::GX_DRAW_LINES] = AddLineList;
  280. m_primitive_table[Primitive::GX_DRAW_LINE_STRIP] = AddLineStrip;
  281. m_primitive_table[Primitive::GX_DRAW_POINTS] = AddPoints;
  282. }
  283. }
  284. void IndexGenerator::Start(u16* index_ptr)
  285. {
  286. m_index_buffer_current = index_ptr;
  287. m_base_index_ptr = index_ptr;
  288. m_base_index = 0;
  289. }
  290. void IndexGenerator::AddIndices(OpcodeDecoder::Primitive primitive, u32 num_vertices)
  291. {
  292. m_index_buffer_current =
  293. m_primitive_table[primitive](m_index_buffer_current, num_vertices, m_base_index);
  294. m_base_index += num_vertices;
  295. }
  296. void IndexGenerator::AddExternalIndices(const u16* indices, u32 num_indices, u32 num_vertices)
  297. {
  298. std::memcpy(m_index_buffer_current, indices, sizeof(u16) * num_indices);
  299. m_index_buffer_current += num_indices;
  300. m_base_index += num_vertices;
  301. }
  302. u32 IndexGenerator::GetRemainingIndices(OpcodeDecoder::Primitive primitive) const
  303. {
  304. u32 max_index = UINT16_MAX;
  305. if (g_Config.UseVSForLinePointExpand() && primitive >= OpcodeDecoder::Primitive::GX_DRAW_LINES)
  306. max_index >>= 2;
  307. // Although we reserve UINT16_MAX for primitive restart, we aren't allowed to use that as an
  308. // actual index. But, the maximum number of vertices a game can send is UINT16_MAX, so up to
  309. // 0xffff indices will be used by the game. These indices would be 0x0000 through 0xfffe,
  310. // and since m_base_index gets incremented for each index used, after that m_base_index
  311. // would be 0xffff and no indices remain. If a game uses 0xfffe vertices, assuming m_base_index
  312. // started at 0 it would end at 0xfffe and one more index could be used. So, we do not need to
  313. // subtract 1 from max_index to correctly reserve one index for primitive restart.
  314. //
  315. // Pocoyo Racing uses a draw command with 0xffff vertices, which previously caused issues; see
  316. // https://bugs.dolphin-emu.org/issues/13136 for details.
  317. if (m_base_index > max_index) [[unlikely]]
  318. {
  319. PanicAlertFmt("GetRemainingIndices would overflow; we've already written too many indices? "
  320. "base index {} > max index {}",
  321. m_base_index, max_index);
  322. return 0;
  323. }
  324. return max_index - m_base_index;
  325. }