2
0

TileAllocator.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Renderer/TileAllocator.h>
  6. namespace anki {
  7. class TileAllocator::Tile
  8. {
  9. public:
  10. Timestamp m_lightTimestamp = 0; ///< The last timestamp the light got updated
  11. Timestamp m_lastUsedTimestamp = 0; ///< The last timestamp this tile was used
  12. U64 m_lightUuid = 0;
  13. U32 m_lightDrawcallCount = 0;
  14. Array<U32, 4> m_viewport = {};
  15. Array<U32, 4> m_subTiles = {MAX_U32, MAX_U32, MAX_U32, MAX_U32};
  16. U32 m_superTile = MAX_U32;
  17. U8 m_lightLod = 0;
  18. U8 m_lightFace = 0;
  19. };
  20. class TileAllocator::HashMapKey
  21. {
  22. public:
  23. U64 m_lightUuid;
  24. U64 m_face;
  25. U64 computeHash() const
  26. {
  27. return anki::computeHash(this, sizeof(*this), 693);
  28. }
  29. };
  30. TileAllocator::~TileAllocator()
  31. {
  32. m_lightInfoToTileIdx.destroy(m_alloc);
  33. m_allTiles.destroy(m_alloc);
  34. m_lodFirstTileIndex.destroy(m_alloc);
  35. }
  36. void TileAllocator::init(HeapAllocator<U8> alloc, U32 tileCountX, U32 tileCountY, U32 lodCount, Bool enableCaching)
  37. {
  38. // Preconditions
  39. ANKI_ASSERT(tileCountX > 0);
  40. ANKI_ASSERT(tileCountY > 0);
  41. ANKI_ASSERT(lodCount > 0);
  42. // Store some stuff
  43. m_tileCountX = U16(tileCountX);
  44. m_tileCountY = U16(tileCountY);
  45. m_lodCount = U8(lodCount);
  46. m_alloc = alloc;
  47. m_cachingEnabled = enableCaching;
  48. m_lodFirstTileIndex.create(m_alloc, lodCount + 1);
  49. // Create the tile array & index ranges
  50. U32 tileCount = 0;
  51. for(U32 lod = 0; lod < lodCount; ++lod)
  52. {
  53. const U32 lodTileCountX = tileCountX >> lod;
  54. const U32 lodTileCountY = tileCountY >> lod;
  55. ANKI_ASSERT((lodTileCountX << lod) == tileCountX && "Every LOD should be power of 2 of its parent LOD");
  56. ANKI_ASSERT((lodTileCountY << lod) == tileCountY && "Every LOD should be power of 2 of its parent LOD");
  57. m_lodFirstTileIndex[lod] = tileCount;
  58. tileCount += lodTileCountX * lodTileCountY;
  59. }
  60. ANKI_ASSERT(tileCount >= tileCountX * tileCountY);
  61. m_allTiles.create(m_alloc, tileCount);
  62. m_lodFirstTileIndex[lodCount] = tileCount - 1;
  63. // Init the tiles
  64. U32 tileIdx = 0;
  65. for(U32 lod = 0; lod < lodCount; ++lod)
  66. {
  67. const U32 lodTileCountX = tileCountX >> lod;
  68. const U32 lodTileCountY = tileCountY >> lod;
  69. for(U32 y = 0; y < lodTileCountY; ++y)
  70. {
  71. for(U32 x = 0; x < lodTileCountX; ++x)
  72. {
  73. ANKI_ASSERT(tileIdx >= m_lodFirstTileIndex[lod] && tileIdx <= m_lodFirstTileIndex[lod + 1]);
  74. Tile& tile = m_allTiles[tileIdx];
  75. tile.m_viewport[0] = x << lod;
  76. tile.m_viewport[1] = y << lod;
  77. tile.m_viewport[2] = 1 << lod;
  78. tile.m_viewport[3] = 1 << lod;
  79. if(lod > 0)
  80. {
  81. // Has sub tiles
  82. for(U32 j = 0; j < 2; ++j)
  83. {
  84. for(U32 i = 0; i < 2; ++i)
  85. {
  86. const U32 subTileIdx = translateTileIdx((x << 1) + i, (y << 1) + j, lod - 1);
  87. m_allTiles[subTileIdx].m_superTile = tileIdx;
  88. tile.m_subTiles[j * 2 + i] = subTileIdx;
  89. }
  90. }
  91. }
  92. else
  93. {
  94. // No sub-tiles
  95. }
  96. ++tileIdx;
  97. }
  98. }
  99. }
  100. }
  101. void TileAllocator::updateSubTiles(const Tile& updateFrom)
  102. {
  103. if(updateFrom.m_subTiles[0] == MAX_U32)
  104. {
  105. return;
  106. }
  107. for(U32 idx : updateFrom.m_subTiles)
  108. {
  109. m_allTiles[idx].m_lightTimestamp = updateFrom.m_lightTimestamp;
  110. m_allTiles[idx].m_lastUsedTimestamp = updateFrom.m_lastUsedTimestamp;
  111. m_allTiles[idx].m_lightUuid = updateFrom.m_lightUuid;
  112. m_allTiles[idx].m_lightDrawcallCount = updateFrom.m_lightDrawcallCount;
  113. m_allTiles[idx].m_lightLod = updateFrom.m_lightLod;
  114. m_allTiles[idx].m_lightFace = updateFrom.m_lightFace;
  115. updateSubTiles(m_allTiles[idx]);
  116. }
  117. }
  118. void TileAllocator::updateSuperTiles(const Tile& updateFrom)
  119. {
  120. if(updateFrom.m_superTile != MAX_U32)
  121. {
  122. m_allTiles[updateFrom.m_superTile].m_lightUuid = 0;
  123. m_allTiles[updateFrom.m_superTile].m_lastUsedTimestamp = updateFrom.m_lastUsedTimestamp;
  124. updateSuperTiles(m_allTiles[updateFrom.m_superTile]);
  125. }
  126. }
  127. Bool TileAllocator::searchTileRecursively(U32 crntTileIdx, U32 crntTileLod, U32 allocationLod, Timestamp crntTimestamp,
  128. U32& emptyTileIdx, U32& toKickTileIdx,
  129. Timestamp& tileToKickMinTimestamp) const
  130. {
  131. const Tile& tile = m_allTiles[crntTileIdx];
  132. if(crntTileLod == allocationLod)
  133. {
  134. // We may have a candidate
  135. const Bool done =
  136. evaluateCandidate(crntTileIdx, crntTimestamp, emptyTileIdx, toKickTileIdx, tileToKickMinTimestamp);
  137. if(done)
  138. {
  139. return true;
  140. }
  141. }
  142. else if(tile.m_subTiles[0] != MAX_U32)
  143. {
  144. // Move down the hierarchy
  145. ANKI_ASSERT(allocationLod < crntTileLod);
  146. for(const U32 idx : tile.m_subTiles)
  147. {
  148. const Bool done = searchTileRecursively(idx, crntTileLod >> 1, allocationLod, crntTimestamp, emptyTileIdx,
  149. toKickTileIdx, tileToKickMinTimestamp);
  150. if(done)
  151. {
  152. return true;
  153. }
  154. }
  155. }
  156. return false;
  157. }
  158. Bool TileAllocator::evaluateCandidate(U32 tileIdx, Timestamp crntTimestamp, U32& emptyTileIdx, U32& toKickTileIdx,
  159. Timestamp& tileToKickMinTimestamp) const
  160. {
  161. const Tile& tile = m_allTiles[tileIdx];
  162. if(m_cachingEnabled)
  163. {
  164. if(tile.m_lastUsedTimestamp == 0)
  165. {
  166. // Found empty
  167. emptyTileIdx = tileIdx;
  168. return true;
  169. }
  170. else if(tile.m_lastUsedTimestamp != crntTimestamp && tile.m_lastUsedTimestamp < tileToKickMinTimestamp)
  171. {
  172. // Found one with low timestamp
  173. toKickTileIdx = tileIdx;
  174. tileToKickMinTimestamp = tile.m_lightTimestamp;
  175. }
  176. }
  177. else
  178. {
  179. if(tile.m_lastUsedTimestamp != crntTimestamp)
  180. {
  181. emptyTileIdx = tileIdx;
  182. return true;
  183. }
  184. }
  185. return false;
  186. }
  187. TileAllocatorResult TileAllocator::allocate(Timestamp crntTimestamp, Timestamp lightTimestamp, U64 lightUuid,
  188. U32 lightFace, U32 drawcallCount, U32 lod, Array<U32, 4>& tileViewport)
  189. {
  190. // Preconditions
  191. ANKI_ASSERT(crntTimestamp > 0);
  192. ANKI_ASSERT(lightTimestamp > 0);
  193. ANKI_ASSERT(lightTimestamp <= crntTimestamp);
  194. ANKI_ASSERT(lightUuid != 0);
  195. ANKI_ASSERT(lightFace < 6);
  196. ANKI_ASSERT(lod < m_lodCount);
  197. // 1) Search if it's already cached
  198. HashMapKey key;
  199. if(m_cachingEnabled)
  200. {
  201. key.m_lightUuid = lightUuid;
  202. key.m_face = lightFace;
  203. auto it = m_lightInfoToTileIdx.find(key);
  204. if(it != m_lightInfoToTileIdx.getEnd())
  205. {
  206. Tile& tile = m_allTiles[*it];
  207. if(tile.m_lightUuid != lightUuid || tile.m_lightLod != lod || tile.m_lightFace != lightFace)
  208. {
  209. // Cache entry is wrong, remove it
  210. m_lightInfoToTileIdx.erase(m_alloc, it);
  211. }
  212. else
  213. {
  214. // Same light & lod & face, found the cache entry.
  215. ANKI_ASSERT(tile.m_lastUsedTimestamp != crntTimestamp
  216. && "Trying to allocate the same thing twice in this timestamp?");
  217. ANKI_ASSERT(tile.m_lightUuid == lightUuid && tile.m_lightLod == lod && tile.m_lightFace == lightFace);
  218. tileViewport = {tile.m_viewport[0], tile.m_viewport[1], tile.m_viewport[2], tile.m_viewport[3]};
  219. const Bool needsReRendering =
  220. tile.m_lightDrawcallCount != drawcallCount || tile.m_lightTimestamp != lightTimestamp;
  221. tile.m_lightTimestamp = lightTimestamp;
  222. tile.m_lastUsedTimestamp = crntTimestamp;
  223. tile.m_lightDrawcallCount = drawcallCount;
  224. updateTileHierarchy(tile);
  225. return (needsReRendering) ? TileAllocatorResult::ALLOCATION_SUCCEEDED : TileAllocatorResult::CACHED;
  226. }
  227. }
  228. }
  229. // Start searching for a suitable tile. Do a hieratchical search to end up with better locality and not better
  230. // utilization of the atlas' space
  231. U32 emptyTileIdx = MAX_U32;
  232. U32 toKickTileIdx = MAX_U32;
  233. Timestamp tileToKickMinTimestamp = MAX_TIMESTAMP;
  234. const U32 maxLod = m_lodCount - 1;
  235. if(lod == maxLod)
  236. {
  237. // This search is simple, iterate the tiles of the max LOD
  238. for(U32 tileIdx = m_lodFirstTileIndex[maxLod]; tileIdx <= m_lodFirstTileIndex[maxLod + 1]; ++tileIdx)
  239. {
  240. const Bool done =
  241. evaluateCandidate(tileIdx, crntTimestamp, emptyTileIdx, toKickTileIdx, tileToKickMinTimestamp);
  242. if(done)
  243. {
  244. break;
  245. }
  246. }
  247. }
  248. else
  249. {
  250. // Need to do a recursive search
  251. for(U32 tileIdx = m_lodFirstTileIndex[maxLod]; tileIdx <= m_lodFirstTileIndex[maxLod + 1]; ++tileIdx)
  252. {
  253. const Bool done = searchTileRecursively(tileIdx, maxLod, lod, crntTimestamp, emptyTileIdx, toKickTileIdx,
  254. tileToKickMinTimestamp);
  255. if(done)
  256. {
  257. break;
  258. }
  259. }
  260. }
  261. U32 allocatedTileIdx;
  262. if(emptyTileIdx != MAX_U32)
  263. {
  264. allocatedTileIdx = emptyTileIdx;
  265. }
  266. else if(toKickTileIdx != MAX_U32)
  267. {
  268. allocatedTileIdx = toKickTileIdx;
  269. }
  270. else
  271. {
  272. // Out of tiles
  273. return TileAllocatorResult::ALLOCATION_FAILED;
  274. }
  275. // Allocation succedded, need to do some bookkeeping
  276. // Mark the allocated tile
  277. Tile& allocatedTile = m_allTiles[allocatedTileIdx];
  278. allocatedTile.m_lightTimestamp = lightTimestamp;
  279. allocatedTile.m_lastUsedTimestamp = crntTimestamp;
  280. allocatedTile.m_lightUuid = lightUuid;
  281. allocatedTile.m_lightDrawcallCount = drawcallCount;
  282. allocatedTile.m_lightLod = U8(lod);
  283. allocatedTile.m_lightFace = U8(lightFace);
  284. updateTileHierarchy(allocatedTile);
  285. // Update the cache
  286. if(m_cachingEnabled)
  287. {
  288. m_lightInfoToTileIdx.emplace(m_alloc, key, allocatedTileIdx);
  289. }
  290. // Return
  291. tileViewport = {allocatedTile.m_viewport[0], allocatedTile.m_viewport[1], allocatedTile.m_viewport[2],
  292. allocatedTile.m_viewport[3]};
  293. return TileAllocatorResult::ALLOCATION_SUCCEEDED;
  294. }
  295. void TileAllocator::invalidateCache(U64 lightUuid, U32 lightFace)
  296. {
  297. ANKI_ASSERT(m_cachingEnabled);
  298. ANKI_ASSERT(lightUuid > 0);
  299. HashMapKey key;
  300. key.m_lightUuid = lightUuid;
  301. key.m_face = lightFace;
  302. auto it = m_lightInfoToTileIdx.find(key);
  303. if(it != m_lightInfoToTileIdx.getEnd())
  304. {
  305. m_lightInfoToTileIdx.erase(m_alloc, it);
  306. }
  307. }
  308. } // end namespace anki