forsythtriangleorderoptimizer.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. //-----------------------------------------------------------------------------
  2. // This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
  3. // Optimization" algorithm as described here:
  4. // http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
  5. //
  6. // This code was authored and released into the public domain by
  7. // Adrian Stone ([email protected]).
  8. //
  9. // THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  10. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  11. // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
  12. // SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
  13. // LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
  14. // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  15. //-----------------------------------------------------------------------------
  16. #include <assert.h>
  17. #include <math.h>
  18. #include <vector>
  19. #include <limits>
  20. #include <algorithm>
  21. namespace Forsyth
  22. {
  23. typedef unsigned int uint;
  24. typedef unsigned short uint16;
  25. typedef unsigned char byte;
  26. //-----------------------------------------------------------------------------
  27. // OptimizeFaces
  28. //-----------------------------------------------------------------------------
  29. // Parameters:
  30. // indexList
  31. // input index list
  32. // indexCount
  33. // the number of indices in the list
  34. // vertexCount
  35. // the largest index value in indexList
  36. // newIndexList
  37. // a pointer to a preallocated buffer the same size as indexList to
  38. // hold the optimized index list
  39. // lruCacheSize
  40. // the size of the simulated post-transform cache (max:64)
  41. //-----------------------------------------------------------------------------
  42. void OptimizeFaces(const uint16* indexList, uint indexCount, uint vertexCount, uint16* newIndexList, uint16 lruCacheSize);
  43. namespace
  44. {
  45. // code for computing vertex score was taken, as much as possible
  46. // directly from the original publication.
  47. float ComputeVertexCacheScore(int cachePosition, int vertexCacheSize)
  48. {
  49. const float FindVertexScore_CacheDecayPower = 1.5f;
  50. const float FindVertexScore_LastTriScore = 0.75f;
  51. float score = 0.0f;
  52. if ( cachePosition < 0 )
  53. {
  54. // Vertex is not in FIFO cache - no score.
  55. }
  56. else
  57. {
  58. if ( cachePosition < 3 )
  59. {
  60. // This vertex was used in the last triangle,
  61. // so it has a fixed score, whichever of the three
  62. // it's in. Otherwise, you can get very different
  63. // answers depending on whether you add
  64. // the triangle 1,2,3 or 3,1,2 - which is silly.
  65. score = FindVertexScore_LastTriScore;
  66. }
  67. else
  68. {
  69. assert ( cachePosition < vertexCacheSize );
  70. // Points for being high in the cache.
  71. const float scaler = 1.0f / ( vertexCacheSize - 3 );
  72. score = 1.0f - ( cachePosition - 3 ) * scaler;
  73. score = powf ( score, FindVertexScore_CacheDecayPower );
  74. }
  75. }
  76. return score;
  77. }
  78. float ComputeVertexValenceScore(uint numActiveFaces)
  79. {
  80. const float FindVertexScore_ValenceBoostScale = 2.0f;
  81. const float FindVertexScore_ValenceBoostPower = 0.5f;
  82. float score = 0.f;
  83. // Bonus points for having a low number of tris still to
  84. // use the vert, so we get rid of lone verts quickly.
  85. float valenceBoost = powf ( static_cast<float>(numActiveFaces),
  86. -FindVertexScore_ValenceBoostPower );
  87. score += FindVertexScore_ValenceBoostScale * valenceBoost;
  88. return score;
  89. }
  90. const int kMaxVertexCacheSize = 64;
  91. const uint kMaxPrecomputedVertexValenceScores = 64;
  92. float s_vertexCacheScores[kMaxVertexCacheSize+1][kMaxVertexCacheSize];
  93. float s_vertexValenceScores[kMaxPrecomputedVertexValenceScores];
  94. bool ComputeVertexScores()
  95. {
  96. for (int cacheSize=0; cacheSize<=kMaxVertexCacheSize; ++cacheSize)
  97. {
  98. for (int cachePos=0; cachePos<cacheSize; ++cachePos)
  99. {
  100. s_vertexCacheScores[cacheSize][cachePos] = ComputeVertexCacheScore(cachePos, cacheSize);
  101. }
  102. }
  103. for (uint valence=0; valence<kMaxPrecomputedVertexValenceScores; ++valence)
  104. {
  105. s_vertexValenceScores[valence] = ComputeVertexValenceScore(valence);
  106. }
  107. return true;
  108. }
  109. bool s_vertexScoresComputed = ComputeVertexScores();
  110. // inline float FindVertexCacheScore(uint cachePosition, uint maxSizeVertexCache)
  111. // {
  112. // return s_vertexCacheScores[maxSizeVertexCache][cachePosition];
  113. // }
  114. // inline float FindVertexValenceScore(uint numActiveTris)
  115. // {
  116. // return s_vertexValenceScores[numActiveTris];
  117. // }
  118. float FindVertexScore(uint numActiveFaces, uint cachePosition, uint vertexCacheSize)
  119. {
  120. assert(s_vertexScoresComputed);
  121. if ( numActiveFaces == 0 )
  122. {
  123. // No tri needs this vertex!
  124. return -1.0f;
  125. }
  126. float score = 0.f;
  127. if (cachePosition < vertexCacheSize)
  128. {
  129. score += s_vertexCacheScores[vertexCacheSize][cachePosition];
  130. }
  131. if (numActiveFaces < kMaxPrecomputedVertexValenceScores)
  132. {
  133. score += s_vertexValenceScores[numActiveFaces];
  134. }
  135. else
  136. {
  137. score += ComputeVertexValenceScore(numActiveFaces);
  138. }
  139. return score;
  140. }
  141. struct OptimizeVertexData
  142. {
  143. float score;
  144. uint activeFaceListStart;
  145. uint activeFaceListSize;
  146. uint16 cachePos0;
  147. uint16 cachePos1;
  148. OptimizeVertexData() : score(0.f), activeFaceListStart(0), activeFaceListSize(0), cachePos0(0), cachePos1(0) { }
  149. };
  150. }
  151. void OptimizeFaces(const uint16* indexList, uint indexCount, uint vertexCount, uint16* newIndexList, uint16 lruCacheSize)
  152. {
  153. std::vector<OptimizeVertexData> vertexDataList;
  154. vertexDataList.resize(vertexCount);
  155. // compute face count per vertex
  156. for (uint i=0; i<indexCount; ++i)
  157. {
  158. uint16 index = indexList[i];
  159. assert(index < vertexCount);
  160. OptimizeVertexData& vertexData = vertexDataList[index];
  161. vertexData.activeFaceListSize++;
  162. }
  163. std::vector<uint> activeFaceList;
  164. const uint16 kEvictedCacheIndex = std::numeric_limits<uint16>::max();
  165. {
  166. // allocate face list per vertex
  167. uint curActiveFaceListPos = 0;
  168. for (uint i=0; i<vertexCount; ++i)
  169. {
  170. OptimizeVertexData& vertexData = vertexDataList[i];
  171. vertexData.cachePos0 = kEvictedCacheIndex;
  172. vertexData.cachePos1 = kEvictedCacheIndex;
  173. vertexData.activeFaceListStart = curActiveFaceListPos;
  174. curActiveFaceListPos += vertexData.activeFaceListSize;
  175. vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos0, lruCacheSize);
  176. vertexData.activeFaceListSize = 0;
  177. }
  178. activeFaceList.resize(curActiveFaceListPos);
  179. }
  180. // fill out face list per vertex
  181. for (uint i=0; i<indexCount; i+=3)
  182. {
  183. for (uint j=0; j<3; ++j)
  184. {
  185. uint16 index = indexList[i+j];
  186. OptimizeVertexData& vertexData = vertexDataList[index];
  187. activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize] = i;
  188. vertexData.activeFaceListSize++;
  189. }
  190. }
  191. std::vector<byte> processedFaceList;
  192. processedFaceList.resize(indexCount);
  193. uint16 vertexCacheBuffer[(kMaxVertexCacheSize+3)*2];
  194. uint16* cache0 = vertexCacheBuffer;
  195. uint16* cache1 = vertexCacheBuffer+(kMaxVertexCacheSize+3);
  196. uint16 entriesInCache0 = 0;
  197. uint bestFace = 0;
  198. float bestScore = -1.f;
  199. const float maxValenceScore = FindVertexScore(1, kEvictedCacheIndex, lruCacheSize) * 3.f;
  200. for (uint i = 0; i < indexCount; i += 3)
  201. {
  202. if (bestScore < 0.f)
  203. {
  204. // no verts in the cache are used by any unprocessed faces so
  205. // search all unprocessed faces for a new starting point
  206. for (uint j = 0; j < indexCount; j += 3)
  207. {
  208. if (processedFaceList[j] == 0)
  209. {
  210. uint face = j;
  211. float faceScore = 0.f;
  212. for (uint k=0; k<3; ++k)
  213. {
  214. uint16 index = indexList[face+k];
  215. OptimizeVertexData& vertexData = vertexDataList[index];
  216. assert(vertexData.activeFaceListSize > 0);
  217. assert(vertexData.cachePos0 >= lruCacheSize);
  218. faceScore += vertexData.score;
  219. }
  220. if (faceScore > bestScore)
  221. {
  222. bestScore = faceScore;
  223. bestFace = face;
  224. assert(bestScore <= maxValenceScore);
  225. if (bestScore >= maxValenceScore)
  226. {
  227. break;
  228. }
  229. }
  230. }
  231. }
  232. assert(bestScore >= 0.f);
  233. }
  234. processedFaceList[bestFace] = 1;
  235. uint16 entriesInCache1 = 0;
  236. // add bestFace to LRU cache and to newIndexList
  237. for (uint v = 0; v < 3; ++v)
  238. {
  239. uint16 index = indexList[bestFace+v];
  240. newIndexList[i+v] = index;
  241. OptimizeVertexData& vertexData = vertexDataList[index];
  242. if (vertexData.cachePos1 >= entriesInCache1)
  243. {
  244. vertexData.cachePos1 = entriesInCache1;
  245. cache1[entriesInCache1++] = index;
  246. if (vertexData.activeFaceListSize == 1)
  247. {
  248. --vertexData.activeFaceListSize;
  249. continue;
  250. }
  251. }
  252. assert(vertexData.activeFaceListSize > 0);
  253. uint* begin = &activeFaceList[vertexData.activeFaceListStart];
  254. uint* end = &(activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize - 1]) + 1;
  255. uint* it = std::find(begin, end, bestFace);
  256. assert(it != end);
  257. std::swap(*it, *(end-1));
  258. --vertexData.activeFaceListSize;
  259. vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
  260. }
  261. // move the rest of the old verts in the cache down and compute their new scores
  262. for (uint c0 = 0; c0 < entriesInCache0; ++c0)
  263. {
  264. uint16 index = cache0[c0];
  265. OptimizeVertexData& vertexData = vertexDataList[index];
  266. if (vertexData.cachePos1 >= entriesInCache1)
  267. {
  268. vertexData.cachePos1 = entriesInCache1;
  269. cache1[entriesInCache1++] = index;
  270. vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
  271. }
  272. }
  273. // find the best scoring triangle in the current cache (including up to 3 that were just evicted)
  274. bestScore = -1.f;
  275. for (uint c1 = 0; c1 < entriesInCache1; ++c1)
  276. {
  277. uint16 index = cache1[c1];
  278. OptimizeVertexData& vertexData = vertexDataList[index];
  279. vertexData.cachePos0 = vertexData.cachePos1;
  280. vertexData.cachePos1 = kEvictedCacheIndex;
  281. for (uint j=0; j<vertexData.activeFaceListSize; ++j)
  282. {
  283. uint face = activeFaceList[vertexData.activeFaceListStart+j];
  284. float faceScore = 0.f;
  285. for (uint v=0; v<3; v++)
  286. {
  287. uint16 faceIndex = indexList[face+v];
  288. OptimizeVertexData& faceVertexData = vertexDataList[faceIndex];
  289. faceScore += faceVertexData.score;
  290. }
  291. if (faceScore > bestScore)
  292. {
  293. bestScore = faceScore;
  294. bestFace = face;
  295. }
  296. }
  297. }
  298. std::swap(cache0, cache1);
  299. entriesInCache0 = std::min(entriesInCache1, lruCacheSize);
  300. }
  301. }
  302. } // namespace Forsyth