vt.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333
  1. /*
  2. * Copyright 2018 Ales Mlakar. All rights reserved.
  3. * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
  4. */
  5. /*
  6. * Reference(s):
  7. * - Sparse Virtual Textures by Sean Barrett
  8. * http://web.archive.org/web/20190103162611/http://silverspaceship.com/src/svt/
  9. * - Based on Virtual Texture Demo by Brad Blanchard
  10. * http://web.archive.org/web/20190103162638/http://linedef.com/virtual-texture-demo.html
  11. * - Mars texture
  12. * http://web.archive.org/web/20190103162730/http://www.celestiamotherlode.net/catalog/mars.php
  13. */
  14. #include <bx/file.h>
  15. #include <bx/sort.h>
  16. #include "vt.h"
  17. namespace vt
  18. {
  19. // Constants
  20. static const int s_channelCount = 4;
  21. static const int s_tileFileDataOffset = sizeof(VirtualTextureInfo);
  22. // Page
  23. Page::operator size_t() const
  24. {
  25. return size_t((uint32_t(m_mip) << 16) | uint32_t((uint16_t(m_x) << 8) | uint16_t(m_y)));
  26. }
  27. // PageCount
  28. PageCount::PageCount(Page _page, int _count)
  29. : m_page(_page)
  30. , m_count(_count)
  31. {
  32. }
  33. int PageCount::compareTo(const PageCount& other) const
  34. {
  35. if (other.m_page.m_mip != m_page.m_mip)
  36. {
  37. return bx::clamp<int>(other.m_page.m_mip - m_page.m_mip, -1, 1);
  38. }
  39. return bx::clamp<int>(other.m_count - m_count, -1, 1);
  40. }
  41. // VirtualTextureInfo
  42. VirtualTextureInfo::VirtualTextureInfo()
  43. : m_virtualTextureSize(0)
  44. , m_tileSize(0)
  45. , m_borderSize(0)
  46. {
  47. }
  48. int VirtualTextureInfo::GetPageSize() const
  49. {
  50. return m_tileSize + 2 * m_borderSize;
  51. }
  52. int VirtualTextureInfo::GetPageTableSize() const
  53. {
  54. return m_virtualTextureSize / m_tileSize;
  55. }
  56. StagingPool::StagingPool(int _width, int _height, int _count, bool _readBack)
  57. : m_stagingTextureIndex(0)
  58. , m_width(_width)
  59. , m_height(_height)
  60. , m_flags(0)
  61. {
  62. m_flags = BGFX_TEXTURE_BLIT_DST | BGFX_SAMPLER_UVW_CLAMP;
  63. if (_readBack)
  64. {
  65. m_flags |= BGFX_TEXTURE_READ_BACK;
  66. }
  67. grow(_count);
  68. }
  69. StagingPool::~StagingPool()
  70. {
  71. for (int i = 0; i < (int)m_stagingTextures.size(); ++i)
  72. {
  73. bgfx::destroy(m_stagingTextures[i]);
  74. }
  75. }
  76. void StagingPool::grow(int count)
  77. {
  78. while ((int)m_stagingTextures.size() < count)
  79. {
  80. auto stagingTexture = bgfx::createTexture2D((uint16_t)m_width, (uint16_t)m_height, false, 1, bgfx::TextureFormat::BGRA8, m_flags);
  81. m_stagingTextures.push_back(stagingTexture);
  82. }
  83. }
  84. bgfx::TextureHandle StagingPool::getTexture()
  85. {
  86. return m_stagingTextures[m_stagingTextureIndex];
  87. }
  88. void StagingPool::next()
  89. {
  90. m_stagingTextureIndex = (m_stagingTextureIndex + 1) % (int)m_stagingTextures.size();
  91. }
  92. PageIndexer::PageIndexer(VirtualTextureInfo* _info)
  93. : m_info(_info)
  94. {
  95. m_mipcount = int(bx::log2((float)m_info->GetPageTableSize()) + 1);
  96. m_sizes.resize(m_mipcount);
  97. for (int i = 0; i < m_mipcount; ++i)
  98. {
  99. m_sizes[i] = (m_info->m_virtualTextureSize / m_info->m_tileSize) >> i;
  100. }
  101. m_offsets.resize(m_mipcount);
  102. m_count = 0;
  103. for (int i = 0; i < m_mipcount; ++i)
  104. {
  105. m_offsets[i] = m_count;
  106. m_count += m_sizes[i] * m_sizes[i];
  107. }
  108. // Calculate reverse mapping
  109. m_reverse.resize(m_count);
  110. for (int i = 0; i < m_mipcount; ++i)
  111. {
  112. int size = m_sizes[i];
  113. for (int y = 0; y < size; ++y)
  114. {
  115. for (int x = 0; x < size; ++x)
  116. {
  117. Page page = { x, y, i };
  118. m_reverse[getIndexFromPage(page)] = page;
  119. }
  120. }
  121. }
  122. }
  123. int PageIndexer::getIndexFromPage(Page page)
  124. {
  125. int offset = m_offsets[page.m_mip];
  126. int stride = m_sizes[page.m_mip];
  127. return offset + page.m_y * stride + page.m_x;
  128. }
  129. Page PageIndexer::getPageFromIndex(int index)
  130. {
  131. return m_reverse[index];
  132. }
  133. bool PageIndexer::isValid(Page page)
  134. {
  135. if (page.m_mip < 0)
  136. {
  137. return false;
  138. }
  139. if (page.m_mip >= m_mipcount)
  140. {
  141. return false;
  142. }
  143. if (page.m_x < 0)
  144. {
  145. return false;
  146. }
  147. if (page.m_x >= m_sizes[page.m_mip])
  148. {
  149. return false;
  150. }
  151. if (page.m_y < 0)
  152. {
  153. return false;
  154. }
  155. if (page.m_y >= m_sizes[page.m_mip])
  156. {
  157. return false;
  158. }
  159. return true;
  160. }
  161. int PageIndexer::getCount() const
  162. {
  163. return m_count;
  164. }
  165. int PageIndexer::getMipCount() const
  166. {
  167. return m_mipcount;
  168. }
  169. SimpleImage::SimpleImage(int _width, int _height, int _channelCount, uint8_t _clearValue)
  170. : m_width(_width)
  171. , m_height(_height)
  172. , m_channelCount(_channelCount)
  173. {
  174. m_data.resize(m_width * m_height * m_channelCount);
  175. clear(_clearValue);
  176. }
  177. SimpleImage::SimpleImage(int _width, int _height, int _channelCount, tinystl::vector<uint8_t>& _data)
  178. : m_width(_width)
  179. , m_height(_height)
  180. , m_channelCount(_channelCount)
  181. {
  182. m_data = _data;
  183. }
  184. void SimpleImage::copy(Point dest_offset, SimpleImage& src, Rect src_rect)
  185. {
  186. int width = bx::min(m_width - dest_offset.m_x, src_rect.m_width);
  187. int height = bx::min(m_height - dest_offset.m_y, src_rect.m_height);
  188. int channels = bx::min(m_channelCount, src.m_channelCount);
  189. for (int j = 0; j < height; ++j)
  190. {
  191. for (int i = 0; i < width; ++i)
  192. {
  193. int i1 = ((j + dest_offset.m_y) * m_width + (i + dest_offset.m_x)) * m_channelCount;
  194. int i2 = ((j + src_rect.m_y) * src.m_width + (i + src_rect.m_x)) * src.m_channelCount;
  195. for (int c = 0; c < channels; ++c)
  196. {
  197. m_data[i1 + c] = src.m_data[i2 + c];
  198. }
  199. }
  200. }
  201. }
  202. void SimpleImage::clear(uint8_t clearValue)
  203. {
  204. bx::memSet(&m_data[0], clearValue, m_width * m_height * m_channelCount);
  205. }
  206. void SimpleImage::fill(Rect rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
  207. {
  208. for (int y = rect.minY(); y < rect.maxY(); ++y)
  209. {
  210. for (int x = rect.minX(); x < rect.maxX(); ++x)
  211. {
  212. m_data[m_channelCount * (y * m_width + x) + 0] = b;
  213. m_data[m_channelCount * (y * m_width + x) + 1] = g;
  214. m_data[m_channelCount * (y * m_width + x) + 2] = r;
  215. m_data[m_channelCount * (y * m_width + x) + 3] = a;
  216. }
  217. }
  218. }
  219. void SimpleImage::mipmap(uint8_t* source, int size, int channels, uint8_t* dest)
  220. {
  221. int mipsize = size / 2;
  222. for (int y = 0; y < mipsize; ++y)
  223. {
  224. for (int x = 0; x < mipsize; ++x)
  225. {
  226. for (int c = 0; c < channels; ++c)
  227. {
  228. int index = channels * ((y * 2) * size + (x * 2)) + c;
  229. int sum_value = 4 >> 1;
  230. sum_value += source[index + channels * (0 * size + 0)];
  231. sum_value += source[index + channels * (0 * size + 1)];
  232. sum_value += source[index + channels * (1 * size + 0)];
  233. sum_value += source[index + channels * (1 * size + 1)];
  234. dest[channels * (y * mipsize + x) + c] = (uint8_t)(sum_value / 4);
  235. }
  236. }
  237. }
  238. }
  239. Quadtree::Quadtree(Rect _rect, int _level)
  240. : m_rectangle(_rect)
  241. , m_level(_level)
  242. {
  243. for (int i = 0; i < 4; ++i)
  244. {
  245. m_children[i] = nullptr;
  246. }
  247. }
  248. Quadtree::~Quadtree()
  249. {
  250. for (int i = 0; i < 4; ++i)
  251. {
  252. if (m_children[i] != nullptr)
  253. {
  254. BX_DELETE(VirtualTexture::getAllocator(), m_children[i]);
  255. }
  256. }
  257. }
  258. void Quadtree::add(Page request, Point mapping)
  259. {
  260. int scale = 1 << request.m_mip; // Same as pow( 2, mip )
  261. int x = request.m_x * scale;
  262. int y = request.m_y * scale;
  263. Quadtree* node = this;
  264. while (request.m_mip < node->m_level)
  265. {
  266. for (int i = 0; i < 4; ++i)
  267. {
  268. auto rect = node->getRectangle(i);
  269. if (rect.contains({ x, y }))
  270. {
  271. // Create a new one if needed
  272. if (node->m_children[i] == nullptr)
  273. {
  274. node->m_children[i] = BX_NEW(VirtualTexture::getAllocator(), Quadtree)(rect, node->m_level - 1);
  275. node = node->m_children[i];
  276. break;
  277. }
  278. // Otherwise traverse the tree
  279. else
  280. {
  281. node = node->m_children[i];
  282. break;
  283. }
  284. }
  285. }
  286. }
  287. // We have created the correct node, now set the mapping
  288. node->m_mapping = mapping;
  289. }
  290. void Quadtree::remove(Page request)
  291. {
  292. int index;
  293. auto node = findPage(this, request, &index);
  294. if (node != nullptr)
  295. {
  296. BX_DELETE(VirtualTexture::getAllocator(), node->m_children[index]);
  297. node->m_children[index] = nullptr;
  298. }
  299. }
  300. void Quadtree::write(SimpleImage& image, int miplevel)
  301. {
  302. write(this, image, miplevel);
  303. }
  304. Rect Quadtree::getRectangle(int index)
  305. {
  306. int x = m_rectangle.m_x;
  307. int y = m_rectangle.m_y;
  308. int w = m_rectangle.m_width / 2;
  309. int h = m_rectangle.m_width / 2;
  310. switch (index)
  311. {
  312. case 0: return { x , y , w, h };
  313. case 1: return { x + w, y , w, h };
  314. case 2: return { x + w, y + h, w, h };
  315. case 3: return { x , y + h, w, h };
  316. default: break;
  317. }
  318. return { 0, 0, 0, 0 };
  319. }
  320. void Quadtree::write(Quadtree* node, SimpleImage& image, int miplevel)
  321. {
  322. if (node->m_level >= miplevel)
  323. {
  324. int rx = node->m_rectangle.m_x >> miplevel;
  325. int ry = node->m_rectangle.m_y >> miplevel;
  326. int rw = node->m_rectangle.m_width >> miplevel;
  327. int rh = node->m_rectangle.m_width >> miplevel;
  328. image.fill({ rx, ry, rw, rh }, (uint8_t)node->m_mapping.m_x, (uint8_t)node->m_mapping.m_y, (uint8_t)node->m_level, 255);
  329. for (int i = 0; i < 4; ++i)
  330. {
  331. auto child = node->m_children[i];
  332. if (child != nullptr)
  333. {
  334. Quadtree::write(child, image, miplevel);
  335. }
  336. }
  337. }
  338. }
  339. Quadtree* Quadtree::findPage(Quadtree* node, Page request, int* index)
  340. {
  341. int scale = 1 << request.m_mip; // Same as pow( 2, mip )
  342. int x = request.m_x * scale;
  343. int y = request.m_y * scale;
  344. // Find the parent of the child we want to remove
  345. bool exitloop = false;
  346. while (!exitloop)
  347. {
  348. exitloop = true;
  349. for (int i = 0; i < 4; ++i)
  350. {
  351. if (node->m_children[i] != nullptr && node->m_children[i]->m_rectangle.contains({ x, y }))
  352. {
  353. // We found it
  354. if (request.m_mip == node->m_level - 1)
  355. {
  356. *index = i;
  357. return node;
  358. }
  359. // Check the children
  360. else
  361. {
  362. node = node->m_children[i];
  363. exitloop = false;
  364. }
  365. }
  366. }
  367. }
  368. // We couldn't find it so it must not exist anymore
  369. *index = -1;
  370. return nullptr;
  371. }
  372. // PageTable
  373. PageTable::PageTable(PageCache* _cache, VirtualTextureInfo* _info, PageIndexer* _indexer)
  374. : m_info(_info)
  375. , m_indexer(_indexer)
  376. , m_quadtree(nullptr)
  377. , m_quadtreeDirty(true) // Force quadtree dirty on startup
  378. {
  379. auto size = m_info->GetPageTableSize();
  380. m_quadtree = BX_NEW(VirtualTexture::getAllocator(), Quadtree)({ 0, 0, size, size }, (int)bx::log2((float)size));
  381. m_texture = bgfx::createTexture2D((uint16_t)size, (uint16_t)size, true, 1, bgfx::TextureFormat::BGRA8, BGFX_SAMPLER_UVW_CLAMP | BGFX_SAMPLER_POINT);
  382. _cache->added = [=](Page page, Point pt) { m_quadtreeDirty = true; m_quadtree->add(page, pt); };
  383. _cache->removed = [=](Page page, Point pt) { m_quadtreeDirty = true; m_quadtree->remove(page); BX_UNUSED(pt); };
  384. auto PageTableSizeLog2 = m_indexer->getMipCount();
  385. for (int i = 0; i < PageTableSizeLog2; ++i)
  386. {
  387. int mipSize = m_info->GetPageTableSize() >> i;
  388. auto simpleImage = BX_NEW(VirtualTexture::getAllocator(), SimpleImage)(mipSize, mipSize, s_channelCount);
  389. auto stagingTexture = bgfx::createTexture2D((uint16_t)mipSize, (uint16_t)mipSize, false, 1, bgfx::TextureFormat::BGRA8, BGFX_SAMPLER_UVW_CLAMP | BGFX_SAMPLER_POINT);
  390. m_images.push_back(simpleImage);
  391. m_stagingTextures.push_back(stagingTexture);
  392. }
  393. }
  394. PageTable::~PageTable()
  395. {
  396. BX_DELETE(VirtualTexture::getAllocator(), m_quadtree);
  397. bgfx::destroy(m_texture);
  398. for (int i = 0; i < (int)m_images.size(); ++i)
  399. {
  400. BX_DELETE(VirtualTexture::getAllocator(), m_images[i]);
  401. }
  402. for (int i = 0; i < (int)m_stagingTextures.size(); ++i)
  403. {
  404. bgfx::destroy(m_stagingTextures[i]);
  405. }
  406. }
  407. void PageTable::update(bgfx::ViewId blitViewId)
  408. {
  409. if (!m_quadtreeDirty)
  410. {
  411. return;
  412. }
  413. m_quadtreeDirty = false;
  414. auto PageTableSizeLog2 = m_indexer->getMipCount();
  415. for (int i = 0; i < PageTableSizeLog2; ++i)
  416. {
  417. m_quadtree->write(*m_images[i], i);
  418. auto stagingTexture = m_stagingTextures[i];
  419. auto size = uint16_t(m_info->GetPageTableSize() >> i);
  420. bgfx::updateTexture2D(stagingTexture, 0, 0, 0, 0, size, size, bgfx::copy(&m_images[i]->m_data[0], size * size * s_channelCount));
  421. bgfx::blit(blitViewId, m_texture, uint8_t(i), 0, 0, 0, stagingTexture, 0, 0, 0, 0, size, size);
  422. }
  423. }
  424. bgfx::TextureHandle PageTable::getTexture()
  425. {
  426. return m_texture;
  427. }
  428. // PageLoader
  429. PageLoader::PageLoader(TileDataFile* _tileDataFile, PageIndexer* _indexer, VirtualTextureInfo* _info)
  430. : m_colorMipLevels(false)
  431. , m_showBorders(false)
  432. , m_tileDataFile(_tileDataFile)
  433. , m_indexer(_indexer)
  434. , m_info(_info)
  435. {
  436. }
  437. void PageLoader::submit(Page request)
  438. {
  439. ReadState state;
  440. state.m_page = request;
  441. loadPage(state);
  442. onPageLoadComplete(state);
  443. }
  444. void PageLoader::loadPage(ReadState& state)
  445. {
  446. int size = m_info->GetPageSize() * m_info->GetPageSize() * s_channelCount;
  447. state.m_data.resize(size);
  448. if (m_colorMipLevels)
  449. {
  450. copyColor(&state.m_data[0], state.m_page);
  451. }
  452. else if (m_tileDataFile != nullptr)
  453. {
  454. m_tileDataFile->readPage(m_indexer->getIndexFromPage(state.m_page), &state.m_data[0]);
  455. }
  456. if (m_showBorders)
  457. {
  458. copyBorder(&state.m_data[0]);
  459. }
  460. }
  461. void PageLoader::onPageLoadComplete(ReadState& state)
  462. {
  463. loadComplete(state.m_page, &state.m_data[0]);
  464. }
  465. void PageLoader::copyBorder(uint8_t* image)
  466. {
  467. int pagesize = m_info->GetPageSize();
  468. int bordersize = m_info->m_borderSize;
  469. for (int i = 0; i < pagesize; ++i)
  470. {
  471. int xindex = bordersize * pagesize + i;
  472. image[xindex * s_channelCount + 0] = 0;
  473. image[xindex * s_channelCount + 1] = 255;
  474. image[xindex * s_channelCount + 2] = 0;
  475. image[xindex * s_channelCount + 3] = 255;
  476. int yindex = i * pagesize + bordersize;
  477. image[yindex * s_channelCount + 0] = 0;
  478. image[yindex * s_channelCount + 1] = 255;
  479. image[yindex * s_channelCount + 2] = 0;
  480. image[yindex * s_channelCount + 3] = 255;
  481. }
  482. }
  483. void PageLoader::copyColor(uint8_t* image, Page request)
  484. {
  485. static const Color colors[] =
  486. {
  487. { 0, 0, 255, 255 },
  488. { 0, 255, 255, 255 },
  489. { 255, 0, 0, 255 },
  490. { 255, 0, 255, 255 },
  491. { 255, 255, 0, 255 },
  492. { 64, 64, 192, 255 },
  493. { 64, 192, 64, 255 },
  494. { 64, 192, 192, 255 },
  495. { 192, 64, 64, 255 },
  496. { 192, 64, 192, 255 },
  497. { 192, 192, 64, 255 },
  498. { 0, 255, 0, 255 },
  499. };
  500. int pagesize = m_info->GetPageSize();
  501. for (int y = 0; y < pagesize; ++y)
  502. {
  503. for (int x = 0; x < pagesize; ++x)
  504. {
  505. image[(y * pagesize + x) * s_channelCount + 0] = colors[request.m_mip].m_b;
  506. image[(y * pagesize + x) * s_channelCount + 1] = colors[request.m_mip].m_g;
  507. image[(y * pagesize + x) * s_channelCount + 2] = colors[request.m_mip].m_r;
  508. image[(y * pagesize + x) * s_channelCount + 3] = colors[request.m_mip].m_a;
  509. }
  510. }
  511. }
  512. PageCache::PageCache(TextureAtlas* _atlas, PageLoader* _loader, int _count)
  513. : m_atlas(_atlas)
  514. , m_loader(_loader)
  515. , m_count(_count)
  516. {
  517. clear();
  518. m_loader->loadComplete = [&](Page page, uint8_t* data) { loadComplete(page, data); };
  519. }
  520. // Update the pages's position in the lru
  521. bool PageCache::touch(Page page)
  522. {
  523. if (m_loading.find(page) == m_loading.end())
  524. {
  525. if (m_lru_used.find(page) != m_lru_used.end())
  526. {
  527. // Find the page (slow!!) and add it to the back of the list
  528. for (auto it = m_lru.begin(); it != m_lru.end(); ++it)
  529. {
  530. if (it->m_page == page)
  531. {
  532. auto lruPage = *it;
  533. m_lru.erase(it);
  534. m_lru.push_back(lruPage);
  535. return true;
  536. }
  537. }
  538. return false;
  539. }
  540. }
  541. return false;
  542. }
  543. // Schedule a load if not already loaded or loading
  544. bool PageCache::request(Page request, bgfx::ViewId blitViewId)
  545. {
  546. m_blitViewId = blitViewId;
  547. if (m_loading.find(request) == m_loading.end())
  548. {
  549. if (m_lru_used.find(request) == m_lru_used.end())
  550. {
  551. m_loading.insert(request);
  552. m_loader->submit(request);
  553. return true;
  554. }
  555. }
  556. return false;
  557. }
  558. void PageCache::clear()
  559. {
  560. for (auto& lru_page : m_lru)
  561. {
  562. if (m_lru_used.find(lru_page.m_page) != m_lru_used.end())
  563. {
  564. removed(lru_page.m_page, lru_page.m_point);
  565. }
  566. }
  567. m_lru_used.clear();
  568. m_lru.clear();
  569. m_lru.reserve(m_count * m_count);
  570. m_current = 0;
  571. }
  572. void PageCache::loadComplete(Page page, uint8_t* data)
  573. {
  574. m_loading.erase(page);
  575. // Find a place in the atlas for the data
  576. Point pt;
  577. if (m_current == m_count * m_count)
  578. {
  579. // Remove the oldest lru page and remember it's location so we can use it
  580. auto lru_page = m_lru[0];
  581. m_lru.erase(m_lru.begin());
  582. m_lru_used.erase(lru_page.m_page);
  583. pt = lru_page.m_point;
  584. // Notify that we removed a page
  585. removed(lru_page.m_page, lru_page.m_point);
  586. }
  587. else
  588. {
  589. pt = { m_current % m_count, m_current / m_count };
  590. ++m_current;
  591. if (m_current == m_count * m_count)
  592. {
  593. bx::debugPrintf("Atlas is full!");
  594. }
  595. }
  596. // Notify atlas that he can upload the page and add the page to lru
  597. m_atlas->uploadPage(pt, data, m_blitViewId);
  598. m_lru.push_back({ page, pt });
  599. m_lru_used.insert(page);
  600. // Signal that we added a page
  601. added(page, pt);
  602. }
  603. // TextureAtlas
  604. TextureAtlas::TextureAtlas(VirtualTextureInfo* _info, int _count, int _uploadsperframe)
  605. : m_info(_info)
  606. , m_stagingPool(_info->GetPageSize(), _info->GetPageSize(), _uploadsperframe, false)
  607. {
  608. // Create atlas texture
  609. int pagesize = m_info->GetPageSize();
  610. int size = _count * pagesize;
  611. m_texture = bgfx::createTexture2D(
  612. (uint16_t)size
  613. , (uint16_t)size
  614. , false
  615. , 1
  616. , bgfx::TextureFormat::BGRA8
  617. , BGFX_SAMPLER_UVW_CLAMP
  618. );
  619. }
  620. TextureAtlas::~TextureAtlas()
  621. {
  622. bgfx::destroy(m_texture);
  623. }
  624. void TextureAtlas::setUploadsPerFrame(int count)
  625. {
  626. m_stagingPool.grow(count);
  627. }
  628. void TextureAtlas::uploadPage(Point pt, uint8_t* data, bgfx::ViewId blitViewId)
  629. {
  630. // Get next staging texture to write to
  631. auto writer = m_stagingPool.getTexture();
  632. m_stagingPool.next();
  633. // Update texture with new atlas data
  634. auto pagesize = uint16_t(m_info->GetPageSize());
  635. bgfx::updateTexture2D(
  636. writer
  637. , 0
  638. , 0
  639. , 0
  640. , 0
  641. , pagesize
  642. , pagesize
  643. , bgfx::copy(data, pagesize * pagesize * s_channelCount)
  644. );
  645. // Copy the texture part to the actual atlas texture
  646. auto xpos = uint16_t(pt.m_x * pagesize);
  647. auto ypos = uint16_t(pt.m_y * pagesize);
  648. bgfx::blit(blitViewId, m_texture, 0, xpos, ypos, 0, writer, 0, 0, 0, 0, pagesize, pagesize);
  649. }
  650. bgfx::TextureHandle TextureAtlas::getTexture()
  651. {
  652. return m_texture;
  653. }
  654. // FeedbackBuffer
  655. FeedbackBuffer::FeedbackBuffer(VirtualTextureInfo* _info, int _width, int _height)
  656. : m_info(_info)
  657. , m_width(_width)
  658. , m_height(_height)
  659. , m_stagingPool(_width, _height, 1, true)
  660. {
  661. // Setup classes
  662. m_indexer = BX_NEW(VirtualTexture::getAllocator(), PageIndexer)(m_info);
  663. m_requests.resize(m_indexer->getCount());
  664. // Initialize and clear buffers
  665. m_downloadBuffer.resize(m_width * m_height * s_channelCount);
  666. bx::memSet(&m_downloadBuffer[0], 0, m_width * m_height * s_channelCount);
  667. clear();
  668. // Initialize feedback frame buffer
  669. bgfx::TextureHandle feedbackFrameBufferTextures[] =
  670. {
  671. bgfx::createTexture2D(uint16_t(m_width), uint16_t(m_height), false, 1, bgfx::TextureFormat::BGRA8, BGFX_TEXTURE_RT),
  672. bgfx::createTexture2D(uint16_t(m_width), uint16_t(m_height), false, 1, bgfx::TextureFormat::D32F, BGFX_TEXTURE_RT),
  673. };
  674. m_feedbackFrameBuffer = bgfx::createFrameBuffer(BX_COUNTOF(feedbackFrameBufferTextures), feedbackFrameBufferTextures, true);
  675. m_lastStagingTexture = { bgfx::kInvalidHandle };
  676. }
  677. FeedbackBuffer::~FeedbackBuffer()
  678. {
  679. BX_DELETE(VirtualTexture::getAllocator(), m_indexer);
  680. bgfx::destroy(m_feedbackFrameBuffer);
  681. }
  682. void FeedbackBuffer::clear()
  683. {
  684. // Clear Table
  685. bx::memSet(&m_requests[0], 0, sizeof(int) * m_indexer->getCount());
  686. }
  687. void FeedbackBuffer::copy(bgfx::ViewId viewId)
  688. {
  689. m_lastStagingTexture = m_stagingPool.getTexture();
  690. // Copy feedback buffer render target to staging texture
  691. bgfx::blit(viewId, m_lastStagingTexture, 0, 0, bgfx::getTexture(m_feedbackFrameBuffer));
  692. m_stagingPool.next();
  693. }
  694. void FeedbackBuffer::download()
  695. {
  696. // Check if there's an already rendered feedback buffer available
  697. if (m_lastStagingTexture.idx == bgfx::kInvalidHandle)
  698. {
  699. return;
  700. }
  701. // Read the texture
  702. bgfx::readTexture(m_lastStagingTexture, &m_downloadBuffer[0]);
  703. // Loop through pixels and check if anything was written
  704. auto data = &m_downloadBuffer[0];
  705. auto colors = (Color*)data;
  706. auto dataSize = m_width * m_height;
  707. for (int i = 0; i < dataSize; ++i)
  708. {
  709. auto& color = colors[i];
  710. if (color.m_a >= 0xff)
  711. {
  712. // Page found! Add it to the request queue
  713. Page request = { color.m_b, color.m_g, color.m_r };
  714. addRequestAndParents(request);
  715. // Clear the pixel, so that we don't have to do it in another pass
  716. color = { 0,0,0,0 };
  717. }
  718. }
  719. }
  720. // This function validates the pages and adds the page's parents
  721. // We do this so that we can fall back to them if we run out of memory
  722. void FeedbackBuffer::addRequestAndParents(Page request)
  723. {
  724. auto PageTableSizeLog2 = m_indexer->getMipCount();
  725. auto count = PageTableSizeLog2 - request.m_mip;
  726. for (int i = 0; i < count; ++i)
  727. {
  728. int xpos = request.m_x >> i;
  729. int ypos = request.m_y >> i;
  730. Page page = { xpos, ypos, request.m_mip + i };
  731. // If it's not a valid page (position or mip out of range) just skip it
  732. if (!m_indexer->isValid(page))
  733. {
  734. return;
  735. }
  736. ++m_requests[m_indexer->getIndexFromPage(page)];
  737. }
  738. }
  739. const tinystl::vector<int>& FeedbackBuffer::getRequests() const
  740. {
  741. return m_requests;
  742. }
  743. bgfx::FrameBufferHandle FeedbackBuffer::getFrameBuffer()
  744. {
  745. return m_feedbackFrameBuffer;
  746. }
  747. int FeedbackBuffer::getWidth() const
  748. {
  749. return m_width;
  750. }
  751. int FeedbackBuffer::getHeight() const
  752. {
  753. return m_height;
  754. }
  755. // VirtualTexture
  756. VirtualTexture::VirtualTexture(TileDataFile* _tileDataFile, VirtualTextureInfo* _info, int _atlassize, int _uploadsperframe, int _mipBias)
  757. : m_tileDataFile(_tileDataFile)
  758. , m_info(_info)
  759. , m_uploadsPerFrame(_uploadsperframe)
  760. , m_mipBias(_mipBias)
  761. {
  762. m_atlasCount = _atlassize / m_info->GetPageSize();
  763. // Setup indexer
  764. m_indexer = BX_NEW(VirtualTexture::getAllocator(), PageIndexer)(m_info);
  765. m_pagesToLoad.reserve(m_indexer->getCount());
  766. // Setup classes
  767. m_atlas = BX_NEW(VirtualTexture::getAllocator(), TextureAtlas)(m_info, m_atlasCount, m_uploadsPerFrame);
  768. m_loader = BX_NEW(VirtualTexture::getAllocator(), PageLoader)(m_tileDataFile, m_indexer, m_info);
  769. m_cache = BX_NEW(VirtualTexture::getAllocator(), PageCache)(m_atlas, m_loader, m_atlasCount);
  770. m_pageTable = BX_NEW(VirtualTexture::getAllocator(), PageTable)(m_cache, m_info, m_indexer);
  771. // Create uniforms
  772. u_vt_settings_1 = bgfx::createUniform("u_vt_settings_1", bgfx::UniformType::Vec4);
  773. u_vt_settings_2 = bgfx::createUniform("u_vt_settings_2", bgfx::UniformType::Vec4);
  774. s_vt_page_table = bgfx::createUniform("s_vt_page_table", bgfx::UniformType::Sampler);
  775. s_vt_texture_atlas = bgfx::createUniform("s_vt_texture_atlas", bgfx::UniformType::Sampler);
  776. }
  777. VirtualTexture::~VirtualTexture()
  778. {
  779. // Destroy
  780. BX_DELETE(VirtualTexture::getAllocator(), m_indexer);
  781. BX_DELETE(VirtualTexture::getAllocator(), m_atlas);
  782. BX_DELETE(VirtualTexture::getAllocator(), m_loader);
  783. BX_DELETE(VirtualTexture::getAllocator(), m_cache);
  784. BX_DELETE(VirtualTexture::getAllocator(), m_pageTable);
  785. // Destroy all uniforms and textures
  786. bgfx::destroy(u_vt_settings_1);
  787. bgfx::destroy(u_vt_settings_2);
  788. bgfx::destroy(s_vt_page_table);
  789. bgfx::destroy(s_vt_texture_atlas);
  790. }
  791. int VirtualTexture::getMipBias() const
  792. {
  793. return m_mipBias;
  794. }
  795. void VirtualTexture::setMipBias(int value)
  796. {
  797. m_mipBias = bx::max(0, value);
  798. }
  799. void VirtualTexture::setUniforms()
  800. {
  801. struct
  802. {
  803. struct
  804. {
  805. float VirtualTextureSize;
  806. float ooAtlasScale;
  807. float BorderScale;
  808. float BorderOffset;
  809. } m_settings_1;
  810. struct
  811. {
  812. float MipBias;
  813. float PageTableSize;
  814. float unused1;
  815. float unused2;
  816. } m_settings_2;
  817. } uniforms;
  818. int pagesize = m_info->GetPageSize();
  819. uniforms.m_settings_1.VirtualTextureSize = (float)m_info->m_virtualTextureSize;
  820. uniforms.m_settings_1.ooAtlasScale = 1.0f / (float)m_atlasCount;
  821. uniforms.m_settings_1.BorderScale = (float)((pagesize - 2.0f * m_info->m_borderSize) / pagesize);
  822. uniforms.m_settings_1.BorderOffset = (float)m_info->m_borderSize / (float)pagesize;
  823. uniforms.m_settings_2.MipBias = (float)m_mipBias;
  824. uniforms.m_settings_2.PageTableSize = (float)m_info->GetPageTableSize();
  825. uniforms.m_settings_2.unused1 = uniforms.m_settings_2.unused2 = 0.0f;
  826. bgfx::setUniform(u_vt_settings_1, &uniforms.m_settings_1);
  827. bgfx::setUniform(u_vt_settings_2, &uniforms.m_settings_2);
  828. bgfx::setTexture(0, s_vt_page_table, m_pageTable->getTexture());
  829. bgfx::setTexture(1, s_vt_texture_atlas, m_atlas->getTexture());
  830. }
  831. void VirtualTexture::setUploadsPerFrame(int count)
  832. {
  833. m_uploadsPerFrame = count;
  834. m_atlas->setUploadsPerFrame(count);
  835. }
  836. int VirtualTexture::getUploadsPerFrame() const
  837. {
  838. return m_uploadsPerFrame;
  839. }
  840. void VirtualTexture::enableShowBoarders(bool enable)
  841. {
  842. if (m_loader->m_showBorders == enable)
  843. {
  844. return;
  845. }
  846. m_loader->m_showBorders = enable;
  847. clear();
  848. }
  849. bool VirtualTexture::isShowBoardersEnabled() const
  850. {
  851. return m_loader->m_showBorders;
  852. }
  853. void VirtualTexture::enableColorMipLevels(bool enable)
  854. {
  855. if (m_loader->m_colorMipLevels == enable)
  856. {
  857. return;
  858. }
  859. m_loader->m_colorMipLevels = enable;
  860. clear();
  861. }
  862. bool VirtualTexture::isColorMipLevelsEnabled() const
  863. {
  864. return m_loader->m_colorMipLevels;
  865. }
  866. bgfx::TextureHandle VirtualTexture::getAtlastTexture()
  867. {
  868. return m_atlas->getTexture();
  869. }
  870. bgfx::TextureHandle VirtualTexture::getPageTableTexture()
  871. {
  872. return m_pageTable->getTexture();
  873. }
  874. void VirtualTexture::clear()
  875. {
  876. m_cache->clear();
  877. }
  878. void VirtualTexture::update(const tinystl::vector<int>& requests, bgfx::ViewId blitViewId)
  879. {
  880. m_pagesToLoad.clear();
  881. // Find out what is already in memory
  882. // If it is, update it's position in the LRU collection
  883. // Otherwise add it to the list of pages to load
  884. int touched = 0;
  885. for (int i = 0; i < (int)requests.size(); ++i)
  886. {
  887. if (requests[i] > 0)
  888. {
  889. PageCount pc(m_indexer->getPageFromIndex(i), requests[i]);
  890. if (!m_cache->touch(pc.m_page))
  891. {
  892. m_pagesToLoad.push_back(pc);
  893. }
  894. else
  895. {
  896. ++touched;
  897. }
  898. }
  899. }
  900. // Check to make sure we don't thrash
  901. if (touched < m_atlasCount * m_atlasCount)
  902. {
  903. // sort by low res to high res and number of requests
  904. bx::quickSort(
  905. m_pagesToLoad.begin()
  906. , uint32_t(m_pagesToLoad.size())
  907. , sizeof(vt::PageCount)
  908. , [](const void* _a, const void* _b) -> int32_t {
  909. const vt::PageCount& lhs = *(const vt::PageCount*)(_a);
  910. const vt::PageCount& rhs = *(const vt::PageCount*)(_b);
  911. return lhs.compareTo(rhs);
  912. });
  913. // if more pages than will fit in memory or more than update per frame drop high res pages with lowest use count
  914. int loadcount = bx::min(bx::min((int)m_pagesToLoad.size(), m_uploadsPerFrame), m_atlasCount * m_atlasCount);
  915. for (int i = 0; i < loadcount; ++i)
  916. m_cache->request(m_pagesToLoad[i].m_page, blitViewId);
  917. }
  918. else
  919. {
  920. // The problem here is that all pages in cache are requested and the new or high res ones don't get uploaded
  921. // We can adjust the mip bias to make it all fit. This solves the problem of page cache thrashing
  922. --m_mipBias;
  923. }
  924. // Update the page table
  925. m_pageTable->update(blitViewId);
  926. }
  927. bx::AllocatorI* VirtualTexture::s_allocator = nullptr;
  928. void VirtualTexture::setAllocator(bx::AllocatorI* allocator)
  929. {
  930. s_allocator = allocator;
  931. }
  932. bx::AllocatorI* VirtualTexture::getAllocator()
  933. {
  934. return s_allocator;
  935. }
  936. TileDataFile::TileDataFile(const bx::FilePath& filename, VirtualTextureInfo* _info, bool _readWrite) : m_info(_info)
  937. {
  938. const char* access = _readWrite ? "w+b" : "rb";
  939. m_file = fopen(filename.getCPtr(), access);
  940. m_size = m_info->GetPageSize() * m_info->GetPageSize() * s_channelCount;
  941. }
  942. TileDataFile::~TileDataFile()
  943. {
  944. fclose(m_file);
  945. }
  946. void TileDataFile::readInfo()
  947. {
  948. fseek(m_file, 0, SEEK_SET);
  949. auto ret = fread(m_info, sizeof(*m_info), 1, m_file);
  950. BX_UNUSED(ret);
  951. m_size = m_info->GetPageSize() * m_info->GetPageSize() * s_channelCount;
  952. }
  953. void TileDataFile::writeInfo()
  954. {
  955. fseek(m_file, 0, SEEK_SET);
  956. auto ret = fwrite(m_info, sizeof(*m_info), 1, m_file);
  957. BX_UNUSED(ret);
  958. }
  959. void TileDataFile::readPage(int index, uint8_t* data)
  960. {
  961. fseek(m_file, m_size * index + s_tileFileDataOffset, SEEK_SET);
  962. auto ret = fread(data, m_size, 1, m_file);
  963. BX_UNUSED(ret);
  964. }
  965. void TileDataFile::writePage(int index, uint8_t* data)
  966. {
  967. fseek(m_file, m_size * index + s_tileFileDataOffset, SEEK_SET);
  968. auto ret = fwrite(data, m_size, 1, m_file);
  969. BX_UNUSED(ret);
  970. }
  971. // TileGenerator
  972. TileGenerator::TileGenerator(VirtualTextureInfo* _info)
  973. : m_info(_info)
  974. , m_indexer(nullptr)
  975. , m_tileDataFile(nullptr)
  976. , m_sourceImage(nullptr)
  977. , m_page1Image(nullptr)
  978. , m_page2Image(nullptr)
  979. , m_2xtileImage(nullptr)
  980. , m_4xtileImage(nullptr)
  981. , m_tileImage(nullptr)
  982. {
  983. m_tilesize = m_info->m_tileSize;
  984. m_pagesize = m_info->GetPageSize();
  985. }
  986. TileGenerator::~TileGenerator()
  987. {
  988. if (m_sourceImage != nullptr)
  989. {
  990. bimg::imageFree(m_sourceImage);
  991. }
  992. BX_DELETE(VirtualTexture::getAllocator(), m_indexer);
  993. BX_DELETE(VirtualTexture::getAllocator(), m_page1Image);
  994. BX_DELETE(VirtualTexture::getAllocator(), m_page2Image);
  995. BX_DELETE(VirtualTexture::getAllocator(), m_2xtileImage);
  996. BX_DELETE(VirtualTexture::getAllocator(), m_4xtileImage);
  997. BX_DELETE(VirtualTexture::getAllocator(), m_tileImage);
  998. }
  999. bool TileGenerator::generate(const bx::FilePath& _filePath)
  1000. {
  1001. const bx::StringView baseName = _filePath.getBaseName();
  1002. // Generate cache filename
  1003. char tmp[256];
  1004. bx::snprintf(tmp, sizeof(tmp), "%.*s.vt", baseName.getLength(), baseName.getPtr() );
  1005. bx::FilePath cacheFilePath("temp");
  1006. cacheFilePath.join(tmp);
  1007. // Check if tile file already exist
  1008. {
  1009. bx::Error err;
  1010. bx::FileReader fileReader;
  1011. if (bx::open(&fileReader, cacheFilePath, &err) )
  1012. {
  1013. bx::close(&fileReader);
  1014. bx::debugPrintf("Tile data file '%s' already exists. Skipping generation.\n", cacheFilePath.getCPtr() );
  1015. return true;
  1016. }
  1017. }
  1018. // Read image
  1019. {
  1020. bx::debugPrintf("Reading image '%s'.\n", _filePath.getCPtr() );
  1021. bx::Error err;
  1022. bx::FileReader fileReader;
  1023. if (!bx::open(&fileReader, _filePath, &err) )
  1024. {
  1025. bx::debugPrintf("Image open failed'%s'.\n", _filePath.getCPtr() );
  1026. return false;
  1027. }
  1028. int64_t size = bx::getSize(&fileReader);
  1029. if (0 == size)
  1030. {
  1031. bx::debugPrintf("Image '%s' size is 0.\n", _filePath.getCPtr() );
  1032. return false;
  1033. }
  1034. uint8_t* rawImage = (uint8_t*)BX_ALLOC(VirtualTexture::getAllocator(), size_t(size) );
  1035. bx::read(&fileReader, rawImage, int32_t(size), &err);
  1036. bx::close(&fileReader);
  1037. if (!err.isOk() )
  1038. {
  1039. bx::debugPrintf("Image read failed'%s'.\n", _filePath.getCPtr() );
  1040. BX_FREE(VirtualTexture::getAllocator(), rawImage);
  1041. return false;
  1042. }
  1043. m_sourceImage = bimg::imageParse(VirtualTexture::getAllocator(), rawImage, uint32_t(size), bimg::TextureFormat::BGRA8, &err);
  1044. BX_FREE(VirtualTexture::getAllocator(), rawImage);
  1045. if (!err.isOk() )
  1046. {
  1047. bx::debugPrintf("Image parse failed'%s'.\n", _filePath.getCPtr() );
  1048. return false;
  1049. }
  1050. }
  1051. // Setup
  1052. m_info->m_virtualTextureSize = int(m_sourceImage->m_width);
  1053. m_indexer = BX_NEW(VirtualTexture::getAllocator(), PageIndexer)(m_info);
  1054. // Open tile data file
  1055. m_tileDataFile = BX_NEW(VirtualTexture::getAllocator(), TileDataFile)(cacheFilePath, m_info, true);
  1056. m_page1Image = BX_NEW(VirtualTexture::getAllocator(), SimpleImage)(m_pagesize, m_pagesize, s_channelCount, 0xff);
  1057. m_page2Image = BX_NEW(VirtualTexture::getAllocator(), SimpleImage)(m_pagesize, m_pagesize, s_channelCount, 0xff);
  1058. m_tileImage = BX_NEW(VirtualTexture::getAllocator(), SimpleImage)(m_tilesize, m_tilesize, s_channelCount, 0xff);
  1059. m_2xtileImage = BX_NEW(VirtualTexture::getAllocator(), SimpleImage)(m_tilesize * 2, m_tilesize * 2, s_channelCount, 0xff);
  1060. m_4xtileImage = BX_NEW(VirtualTexture::getAllocator(), SimpleImage)(m_tilesize * 4, m_tilesize * 4, s_channelCount, 0xff);
  1061. // Generate tiles
  1062. bx::debugPrintf("Generating tiles\n");
  1063. auto mipcount = m_indexer->getMipCount();
  1064. for (int i = 0; i < mipcount; ++i)
  1065. {
  1066. int count = (m_info->m_virtualTextureSize / m_tilesize) >> i;
  1067. bx::debugPrintf("Generating Mip:%d Count:%dx%d\n", i, count, count);
  1068. for (int y = 0; y < count; ++y)
  1069. {
  1070. for (int x = 0; x < count; ++x)
  1071. {
  1072. Page page = { x, y, i };
  1073. int index = m_indexer->getIndexFromPage(page);
  1074. CopyTile(*m_page1Image, page);
  1075. m_tileDataFile->writePage(index, &m_page1Image->m_data[0]);
  1076. }
  1077. }
  1078. }
  1079. bx::debugPrintf("Finising\n");
  1080. // Write header
  1081. m_tileDataFile->writeInfo();
  1082. // Close tile file
  1083. BX_DELETE(VirtualTexture::getAllocator(), m_tileDataFile);
  1084. m_tileDataFile = nullptr;
  1085. bx::debugPrintf("Done!\n");
  1086. return true;
  1087. }
  1088. void TileGenerator::CopyTile(SimpleImage& image, Page request)
  1089. {
  1090. if (request.m_mip == 0)
  1091. {
  1092. int x = request.m_x * m_tilesize - m_info->m_borderSize;
  1093. int y = request.m_y * m_tilesize - m_info->m_borderSize;
  1094. // Copy sub-image with border
  1095. auto srcPitch = m_sourceImage->m_width * s_channelCount;
  1096. auto src = (uint8_t*)m_sourceImage->m_data;
  1097. auto dstPitch = image.m_width * image.m_channelCount;
  1098. auto dst = &image.m_data[0];
  1099. for (int iy = 0; iy < m_pagesize; ++iy)
  1100. {
  1101. int ry = bx::clamp(y + iy, 0, (int)m_sourceImage->m_height - 1);
  1102. for (int ix = 0; ix < m_pagesize; ++ix)
  1103. {
  1104. int rx = bx::clamp(x + ix, 0, (int)m_sourceImage->m_width - 1);
  1105. bx::memCopy(&dst[iy * dstPitch + ix * image.m_channelCount], &src[ry * srcPitch + rx * s_channelCount], image.m_channelCount);
  1106. }
  1107. }
  1108. }
  1109. else
  1110. {
  1111. int xpos = request.m_x << 1;
  1112. int ypos = request.m_y << 1;
  1113. int mip = request.m_mip - 1;
  1114. int size = m_info->GetPageTableSize() >> mip;
  1115. m_4xtileImage->clear((uint8_t)request.m_mip);
  1116. for (int y = 0; y < 4; ++y)
  1117. {
  1118. for (int x = 0; x < 4; ++x)
  1119. {
  1120. Page page = { xpos + x - 1, ypos + y - 1, mip };
  1121. // Wrap so we get the border sections of other pages
  1122. page.m_x = (int)bx::mod((float)page.m_x, (float)size);
  1123. page.m_y = (int)bx::mod((float)page.m_y, (float)size);
  1124. m_tileDataFile->readPage(m_indexer->getIndexFromPage(page), &m_page2Image->m_data[0]);
  1125. Rect src_rect = { m_info->m_borderSize, m_info->m_borderSize, m_tilesize, m_tilesize };
  1126. Point dst_offset = { x * m_tilesize, y * m_tilesize };
  1127. m_4xtileImage->copy(dst_offset, *m_page2Image, src_rect);
  1128. }
  1129. }
  1130. SimpleImage::mipmap(&m_4xtileImage->m_data[0], m_4xtileImage->m_width, s_channelCount, &m_2xtileImage->m_data[0]);
  1131. Rect srect = { m_tilesize / 2 - m_info->m_borderSize, m_tilesize / 2 - m_info->m_borderSize, m_pagesize, m_pagesize };
  1132. image.copy({ 0,0 }, *m_2xtileImage, srect);
  1133. }
  1134. }
  1135. } // namespace vt