triListOpt.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "gfx/util/triListOpt.h"
  23. #include "core/frameAllocator.h"
  24. #include "platform/profiler.h"
  25. #include "math/mMathFn.h"
  26. namespace TriListOpt
  27. {
  28. void OptimizeTriangleOrdering(const dsize_t numVerts, const dsize_t numIndices, const U32 *indices, IndexType *outIndices)
  29. {
  30. PROFILE_SCOPE(TriListOpt_OptimizeTriangleOrdering);
  31. if(numVerts == 0 || numIndices == 0)
  32. {
  33. dCopyArray(outIndices, indices, numIndices);
  34. return;
  35. }
  36. const U32 NumPrimitives = numIndices / 3;
  37. AssertFatal(NumPrimitives == U32(mFloor(numIndices / 3.0f)), "Number of indicies not divisible by 3, not a good triangle list.");
  38. //
  39. // Step 1: Run through the data, and initialize
  40. //
  41. FrameTemp<VertData> vertexData(numVerts);
  42. FrameTemp<TriData> triangleData(NumPrimitives);
  43. U32 curIdx = 0;
  44. for(S32 tri = 0; tri < NumPrimitives; tri++)
  45. {
  46. TriData &curTri = triangleData[tri];
  47. for(S32 c = 0; c < 3; c++)
  48. {
  49. const U32 &curVIdx = indices[curIdx];
  50. AssertFatal(curVIdx < numVerts, "Out of range index.");
  51. // Add this vert to the list of verts that define the triangle
  52. curTri.vertIdx[c] = curVIdx;
  53. VertData &curVert = vertexData[curVIdx];
  54. // Increment the number of triangles that reference this vertex
  55. curVert.numUnaddedReferences++;
  56. curIdx++;
  57. }
  58. }
  59. // Allocate per-vertex triangle lists, and calculate the starting score of
  60. // each of the verts
  61. for(S32 v = 0; v < numVerts; v++)
  62. {
  63. VertData &curVert = vertexData[v];
  64. curVert.triIndex = new S32[curVert.numUnaddedReferences];
  65. curVert.score = FindVertexScore::score(curVert);
  66. }
  67. // These variables will be used later, but need to be declared now
  68. S32 nextNextBestTriIdx = -1, nextBestTriIdx = -1;
  69. F32 nextNextBestTriScore = -1.0f, nextBestTriScore = -1.0f;
  70. #define _VALIDATE_TRI_IDX(idx) if(idx > -1) { AssertFatal(idx < NumPrimitives, "Out of range triangle index."); AssertFatal(!triangleData[idx].isInList, "Triangle already in list, bad."); }
  71. #define _CHECK_NEXT_NEXT_BEST(score, idx) { if(score > nextNextBestTriScore) { nextNextBestTriIdx = idx; nextNextBestTriScore = score; } }
  72. #define _CHECK_NEXT_BEST(score, idx) { if(score > nextBestTriScore) { _CHECK_NEXT_NEXT_BEST(nextBestTriScore, nextBestTriIdx); nextBestTriIdx = idx; nextBestTriScore = score; } _VALIDATE_TRI_IDX(nextBestTriIdx); }
  73. // Fill-in per-vertex triangle lists, and sum the scores of each vertex used
  74. // per-triangle, to get the starting triangle score
  75. curIdx = 0;
  76. for(S32 tri = 0; tri < NumPrimitives; tri++)
  77. {
  78. TriData &curTri = triangleData[tri];
  79. for(S32 c = 0; c < 3; c++)
  80. {
  81. const U32 &curVIdx = indices[curIdx];
  82. AssertFatal(curVIdx < numVerts, "Out of range index.");
  83. VertData &curVert = vertexData[curVIdx];
  84. // Add triangle to triangle list
  85. curVert.triIndex[curVert.numReferences++] = tri;
  86. // Add vertex score to triangle score
  87. curTri.score += curVert.score;
  88. curIdx++;
  89. }
  90. // This will pick the first triangle to add to the list in 'Step 2'
  91. _CHECK_NEXT_BEST(curTri.score, tri);
  92. _CHECK_NEXT_NEXT_BEST(curTri.score, tri);
  93. }
  94. //
  95. // Step 2: Start emitting triangles...this is the emit loop
  96. //
  97. LRUCacheModel lruCache;
  98. for(S32 outIdx = 0; outIdx < numIndices; /* this space intentionally left blank */ )
  99. {
  100. // If there is no next best triangle, than search for the next highest
  101. // scored triangle that isn't in the list already
  102. if(nextBestTriIdx < 0)
  103. {
  104. // TODO: Something better than linear performance here...
  105. nextBestTriScore = nextNextBestTriScore = -1.0f;
  106. nextBestTriIdx = nextNextBestTriIdx = -1;
  107. for(S32 tri = 0; tri < NumPrimitives; tri++)
  108. {
  109. TriData &curTri = triangleData[tri];
  110. if(!curTri.isInList)
  111. {
  112. _CHECK_NEXT_BEST(curTri.score, tri);
  113. _CHECK_NEXT_NEXT_BEST(curTri.score, tri);
  114. }
  115. }
  116. }
  117. AssertFatal(nextBestTriIdx > -1, "Ran out of 'nextBestTriangle' before I ran out of indices...not good.");
  118. // Emit the next best triangle
  119. TriData &nextBestTri = triangleData[nextBestTriIdx];
  120. AssertFatal(!nextBestTri.isInList, "Next best triangle already in list, this is no good.");
  121. for(S32 i = 0; i < 3; i++)
  122. {
  123. // Emit index
  124. outIndices[outIdx++] = IndexType(nextBestTri.vertIdx[i]);
  125. // Update the list of triangles on the vert
  126. VertData &curVert = vertexData[nextBestTri.vertIdx[i]];
  127. curVert.numUnaddedReferences--;
  128. for(S32 t = 0; t < curVert.numReferences; t++)
  129. {
  130. if(curVert.triIndex[t] == nextBestTriIdx)
  131. {
  132. curVert.triIndex[t] = -1;
  133. break;
  134. }
  135. }
  136. // Update cache
  137. lruCache.useVertex(nextBestTri.vertIdx[i], &curVert);
  138. }
  139. nextBestTri.isInList = true;
  140. // Enforce cache size, this will update the cache position of all verts
  141. // still in the cache. It will also update the score of the verts in the
  142. // cache, and give back a list of triangle indicies that need updating.
  143. Vector<U32> trisToUpdate;
  144. lruCache.enforceSize(MaxSizeVertexCache, trisToUpdate);
  145. // Now update scores for triangles that need updates, and find the new best
  146. // triangle score/index
  147. nextBestTriIdx = -1;
  148. nextBestTriScore = -1.0f;
  149. for(Vector<U32>::iterator itr = trisToUpdate.begin(); itr != trisToUpdate.end(); itr++)
  150. {
  151. TriData &tri = triangleData[*itr];
  152. // If this triangle isn't already emitted, re-score it
  153. if(!tri.isInList)
  154. {
  155. tri.score = 0.0f;
  156. for(S32 i = 0; i < 3; i++)
  157. tri.score += vertexData[tri.vertIdx[i]].score;
  158. _CHECK_NEXT_BEST(tri.score, *itr);
  159. _CHECK_NEXT_NEXT_BEST(tri.score, *itr);
  160. }
  161. }
  162. // If there was no love finding a good triangle, than see if there is a
  163. // next-next-best triangle, and if there isn't one of those...well than
  164. // I guess we have to find one next time
  165. if(nextBestTriIdx < 0 && nextNextBestTriIdx > -1)
  166. {
  167. if(!triangleData[nextNextBestTriIdx].isInList)
  168. {
  169. nextBestTriIdx = nextNextBestTriIdx;
  170. nextBestTriScore = nextNextBestTriScore;
  171. _VALIDATE_TRI_IDX(nextNextBestTriIdx);
  172. }
  173. // Nuke the next-next best
  174. nextNextBestTriIdx = -1;
  175. nextNextBestTriScore = -1.0f;
  176. }
  177. // Validate triangle we are marking as next-best
  178. _VALIDATE_TRI_IDX(nextBestTriIdx);
  179. }
  180. #undef _CHECK_NEXT_BEST
  181. #undef _CHECK_NEXT_NEXT_BEST
  182. #undef _VALIDATE_TRI_IDX
  183. // FrameTemp will call destructInPlace to clean up vertex lists
  184. }
  185. //------------------------------------------------------------------------------
  186. //------------------------------------------------------------------------------
  187. LRUCacheModel::~LRUCacheModel()
  188. {
  189. for( LRUCacheEntry* entry = mCacheHead; entry != NULL; )
  190. {
  191. LRUCacheEntry* next = entry->next;
  192. delete entry;
  193. entry = next;
  194. }
  195. }
  196. //------------------------------------------------------------------------------
  197. void LRUCacheModel::useVertex(const U32 vIdx, VertData *vData)
  198. {
  199. LRUCacheEntry *search = mCacheHead;
  200. LRUCacheEntry *last = NULL;
  201. while(search != NULL)
  202. {
  203. if(search->vIdx == vIdx)
  204. break;
  205. last = search;
  206. search = search->next;
  207. }
  208. // If this vertex wasn't found in the cache, create a new entry
  209. if(search == NULL)
  210. {
  211. search = new LRUCacheEntry;
  212. search->vIdx = vIdx;
  213. search->vData = vData;
  214. }
  215. if(search != mCacheHead)
  216. {
  217. // Unlink the entry from the linked list
  218. if(last)
  219. last->next = search->next;
  220. // Vertex that got passed in is now at the head of the cache
  221. search->next = mCacheHead;
  222. mCacheHead = search;
  223. }
  224. }
  225. //------------------------------------------------------------------------------
  226. void LRUCacheModel::enforceSize(const dsize_t maxSize, Vector<U32> &outTrisToUpdate)
  227. {
  228. // Clear list of triangles to update scores for
  229. outTrisToUpdate.clear();
  230. dsize_t length = 0;
  231. LRUCacheEntry *next = mCacheHead;
  232. LRUCacheEntry *last = NULL;
  233. // Run through list, up to the max size
  234. while(next != NULL && length < MaxSizeVertexCache)
  235. {
  236. VertData &vData = *next->vData;
  237. // Update cache position on verts still in cache
  238. vData.cachePosition = length++;
  239. for(S32 i = 0; i < vData.numReferences; i++)
  240. {
  241. const S32 &triIdx = vData.triIndex[i];
  242. if(triIdx > -1)
  243. {
  244. S32 j = 0;
  245. for(; j < outTrisToUpdate.size(); j++)
  246. if(outTrisToUpdate[j] == triIdx)
  247. break;
  248. if(j == outTrisToUpdate.size())
  249. outTrisToUpdate.push_back(triIdx);
  250. }
  251. }
  252. // Update score
  253. vData.score = FindVertexScore::score(vData);
  254. last = next;
  255. next = next->next;
  256. }
  257. // NULL out the pointer to the next entry on the last valid entry
  258. last->next = NULL;
  259. // If next != NULL, than we need to prune entries from the tail of the cache
  260. while(next != NULL)
  261. {
  262. // Update cache position on verts which are going to get tossed from cache
  263. next->vData->cachePosition = -1;
  264. LRUCacheEntry *curEntry = next;
  265. next = next->next;
  266. delete curEntry;
  267. }
  268. }
  269. //------------------------------------------------------------------------------
  270. S32 LRUCacheModel::getCachePosition(const U32 vIdx)
  271. {
  272. dsize_t length = 0;
  273. LRUCacheEntry *next = mCacheHead;
  274. while(next != NULL)
  275. {
  276. if(next->vIdx == vIdx)
  277. return length;
  278. length++;
  279. next = next->next;
  280. }
  281. return -1;
  282. }
  283. //------------------------------------------------------------------------------
  284. //------------------------------------------------------------------------------
  285. // http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
  286. namespace FindVertexScore
  287. {
  288. F32 score(const VertData &vertexData)
  289. {
  290. // If nobody needs this vertex, return -1.0
  291. if(vertexData.numUnaddedReferences < 1)
  292. return -1.0f;
  293. F32 Score = 0.0f;
  294. if(vertexData.cachePosition < 0)
  295. {
  296. // Vertex is not in FIFO cache - no score.
  297. }
  298. else
  299. {
  300. if(vertexData.cachePosition < 3)
  301. {
  302. // This vertex was used in the last triangle,
  303. // so it has a fixed score, whichever of the three
  304. // it's in. Otherwise, you can get very different
  305. // answers depending on whether you add
  306. // the triangle 1,2,3 or 3,1,2 - which is silly.
  307. Score = FindVertexScore::LastTriScore;
  308. }
  309. else
  310. {
  311. AssertFatal(vertexData.cachePosition < MaxSizeVertexCache, "Out of range cache position for vertex");
  312. // Points for being high in the cache.
  313. const F32 Scaler = 1.0f / (MaxSizeVertexCache - 3);
  314. Score = 1.0f - (vertexData.cachePosition - 3) * Scaler;
  315. Score = mPow(Score, FindVertexScore::CacheDecayPower);
  316. }
  317. }
  318. // Bonus points for having a low number of tris still to
  319. // use the vert, so we get rid of lone verts quickly.
  320. F32 ValenceBoost = mPow(vertexData.numUnaddedReferences, -FindVertexScore::ValenceBoostPower);
  321. Score += FindVertexScore::ValenceBoostScale * ValenceBoost;
  322. return Score;
  323. }
  324. } // namspace FindVertexScore
  325. } // namespace TriListOpt