gfxTextureManager.cpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  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 "platform/platform.h"
  23. #include "gfx/gfxTextureManager.h"
  24. #include "gfx/gfxDevice.h"
  25. #include "gfx/gfxCardProfile.h"
  26. #include "gfx/gfxStringEnumTranslate.h"
  27. #include "gfx/bitmap/ddsUtils.h"
  28. #include "core/strings/stringFunctions.h"
  29. #include "core/util/safeDelete.h"
  30. #include "core/resourceManager.h"
  31. #include "core/volume.h"
  32. #include "core/util/dxt5nmSwizzle.h"
  33. #include "console/consoleTypes.h"
  34. #include "console/engineAPI.h"
  35. //#define DEBUG_SPEW
  36. S32 GFXTextureManager::smTextureReductionLevel = 0;
  37. String GFXTextureManager::smMissingTexturePath("core/art/missingTexture");
  38. String GFXTextureManager::smUnavailableTexturePath("core/art/unavailable");
  39. String GFXTextureManager::smWarningTexturePath("core/art/warnmat");
  40. GFXTextureManager::EventSignal GFXTextureManager::smEventSignal;
  41. static const String sDDSExt( "dds" );
  42. void GFXTextureManager::init()
  43. {
  44. Con::addVariable( "$pref::Video::textureReductionLevel", TypeS32, &smTextureReductionLevel,
  45. "The number of mipmap levels to drop on loaded textures to reduce "
  46. "video memory usage. It will skip any textures that have been defined "
  47. "as not allowing down scaling.\n"
  48. "@ingroup GFX\n" );
  49. Con::addVariable( "$pref::Video::missingTexturePath", TypeRealString, &smMissingTexturePath,
  50. "The file path of the texture to display when the requested texture is missing.\n"
  51. "@ingroup GFX\n" );
  52. Con::addVariable( "$pref::Video::unavailableTexturePath", TypeRealString, &smUnavailableTexturePath,
  53. "@brief The file path of the texture to display when the requested texture is unavailable.\n\n"
  54. "Often this texture is used by GUI controls to indicate that the request image is unavailable.\n"
  55. "@ingroup GFX\n" );
  56. Con::addVariable( "$pref::Video::warningTexturePath", TypeRealString, &smWarningTexturePath,
  57. "The file path of the texture used to warn the developer.\n"
  58. "@ingroup GFX\n" );
  59. }
  60. GFXTextureManager::GFXTextureManager()
  61. {
  62. mListHead = mListTail = NULL;
  63. mTextureManagerState = GFXTextureManager::Living;
  64. // Set up the hash table
  65. mHashCount = 1023;
  66. mHashTable = new GFXTextureObject *[mHashCount];
  67. for(U32 i = 0; i < mHashCount; i++)
  68. mHashTable[i] = NULL;
  69. }
  70. GFXTextureManager::~GFXTextureManager()
  71. {
  72. if( mHashTable )
  73. SAFE_DELETE_ARRAY( mHashTable );
  74. mCubemapTable.clear();
  75. }
  76. U32 GFXTextureManager::getTextureDownscalePower( GFXTextureProfile *profile )
  77. {
  78. if ( !profile || profile->canDownscale() )
  79. return smTextureReductionLevel;
  80. return 0;
  81. }
  82. bool GFXTextureManager::validateTextureQuality( GFXTextureProfile *profile, U32 &width, U32 &height )
  83. {
  84. U32 scaleFactor = getTextureDownscalePower( profile );
  85. if ( scaleFactor == 0 )
  86. return true;
  87. // Otherwise apply the appropriate scale...
  88. width >>= scaleFactor;
  89. height >>= scaleFactor;
  90. return true;
  91. }
  92. void GFXTextureManager::kill()
  93. {
  94. AssertFatal( mTextureManagerState != GFXTextureManager::Dead, "Texture Manager already killed!" );
  95. // Release everything in the cache we can
  96. // so we don't leak any textures.
  97. cleanupCache();
  98. GFXTextureObject *curr = mListHead;
  99. GFXTextureObject *temp;
  100. // Actually delete all the textures we know about.
  101. while( curr != NULL )
  102. {
  103. temp = curr->mNext;
  104. curr->kill();
  105. curr = temp;
  106. }
  107. mCubemapTable.clear();
  108. mTextureManagerState = GFXTextureManager::Dead;
  109. }
  110. void GFXTextureManager::zombify()
  111. {
  112. AssertFatal( mTextureManagerState != GFXTextureManager::Zombie, "Texture Manager already a zombie!" );
  113. // Notify everyone that cares about the zombification!
  114. smEventSignal.trigger( GFXZombify );
  115. // Release unused pool textures.
  116. cleanupPool();
  117. // Release everything in the cache we can.
  118. cleanupCache();
  119. // Free all the device copies of the textures.
  120. GFXTextureObject *temp = mListHead;
  121. while( temp != NULL )
  122. {
  123. freeTexture( temp, true );
  124. temp = temp->mNext;
  125. }
  126. // Finally, note our state.
  127. mTextureManagerState = GFXTextureManager::Zombie;
  128. }
  129. void GFXTextureManager::resurrect()
  130. {
  131. // Reupload all the device copies of the textures.
  132. GFXTextureObject *temp = mListHead;
  133. while( temp != NULL )
  134. {
  135. refreshTexture( temp );
  136. temp = temp->mNext;
  137. }
  138. // Notify callback registries.
  139. smEventSignal.trigger( GFXResurrect );
  140. // Update our state.
  141. mTextureManagerState = GFXTextureManager::Living;
  142. }
  143. void GFXTextureManager::cleanupPool()
  144. {
  145. PROFILE_SCOPE( GFXTextureManager_CleanupPool );
  146. TexturePoolMap::Iterator iter = mTexturePool.begin();
  147. for ( ; iter != mTexturePool.end(); )
  148. {
  149. if ( iter->value->getRefCount() == 1 )
  150. {
  151. // This texture is unreferenced, so take the time
  152. // now to completely remove it from the pool.
  153. TexturePoolMap::Iterator unref = iter;
  154. iter++;
  155. unref->value = NULL;
  156. mTexturePool.erase( unref );
  157. continue;
  158. }
  159. iter++;
  160. }
  161. }
  162. void GFXTextureManager::requestDeleteTexture( GFXTextureObject *texture )
  163. {
  164. // If this is a non-cached texture then just really delete it.
  165. if ( texture->mTextureLookupName.isEmpty() )
  166. {
  167. delete texture;
  168. return;
  169. }
  170. // Set the time and store it.
  171. texture->mDeleteTime = Platform::getTime();
  172. mToDelete.push_back_unique( texture );
  173. }
  174. void GFXTextureManager::cleanupCache( U32 secondsToLive )
  175. {
  176. PROFILE_SCOPE( GFXTextureManager_CleanupCache );
  177. U32 killTime = Platform::getTime() - secondsToLive;
  178. for ( U32 i=0; i < mToDelete.size(); )
  179. {
  180. GFXTextureObject *tex = mToDelete[i];
  181. // If the texture was picked back up by a user
  182. // then just remove it from the list.
  183. if ( tex->getRefCount() != 0 )
  184. {
  185. mToDelete.erase_fast( i );
  186. continue;
  187. }
  188. // If its time has expired delete it for real.
  189. if ( tex->mDeleteTime <= killTime )
  190. {
  191. //Con::errorf( "Killed texture: %s", tex->mTextureLookupName.c_str() );
  192. delete tex;
  193. mToDelete.erase_fast( i );
  194. continue;
  195. }
  196. i++;
  197. }
  198. }
  199. GFXTextureObject *GFXTextureManager::_lookupTexture( const char *hashName, const GFXTextureProfile *profile )
  200. {
  201. GFXTextureObject *ret = hashFind( hashName );
  202. // TODO: Profile checking HERE
  203. return ret;
  204. }
  205. GFXTextureObject *GFXTextureManager::_lookupTexture( const DDSFile *ddsFile, const GFXTextureProfile *profile )
  206. {
  207. if( ddsFile->getTextureCacheString().isNotEmpty() )
  208. {
  209. // Call _lookupTexture()
  210. return _lookupTexture( ddsFile->getTextureCacheString(), profile );
  211. }
  212. return NULL;
  213. }
  214. GFXTextureObject *GFXTextureManager::createTexture( GBitmap *bmp, const String &resourceName, GFXTextureProfile *profile, bool deleteBmp )
  215. {
  216. AssertFatal(bmp, "GFXTextureManager::createTexture() - Got NULL bitmap!");
  217. GFXTextureObject *cacheHit = _lookupTexture( resourceName, profile );
  218. if( cacheHit != NULL)
  219. {
  220. // Con::errorf("Cached texture '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown"));
  221. if (deleteBmp)
  222. delete bmp;
  223. return cacheHit;
  224. }
  225. return _createTexture( bmp, resourceName, profile, deleteBmp, NULL );
  226. }
  227. GFXTextureObject *GFXTextureManager::_createTexture( GBitmap *bmp,
  228. const String &resourceName,
  229. GFXTextureProfile *profile,
  230. bool deleteBmp,
  231. GFXTextureObject *inObj )
  232. {
  233. PROFILE_SCOPE( GFXTextureManager_CreateTexture_Bitmap );
  234. #ifdef DEBUG_SPEW
  235. Platform::outputDebugString( "[GFXTextureManager] _createTexture (GBitmap) '%s'",
  236. resourceName.c_str()
  237. );
  238. #endif
  239. // Massage the bitmap based on any resize rules.
  240. U32 scalePower = getTextureDownscalePower( profile );
  241. GBitmap *realBmp = bmp;
  242. U32 realWidth = bmp->getWidth();
  243. U32 realHeight = bmp->getHeight();
  244. if ( scalePower &&
  245. isPow2(bmp->getWidth()) &&
  246. isPow2(bmp->getHeight()) &&
  247. profile->canDownscale() )
  248. {
  249. // We only work with power of 2 textures for now, so we
  250. // don't have to worry about padding.
  251. // We downscale the bitmap on the CPU... this is the reason
  252. // you should be using DDS which already has good looking mips.
  253. GBitmap *padBmp = bmp;
  254. padBmp->extrudeMipLevels();
  255. scalePower = getMin( scalePower, padBmp->getNumMipLevels() - 1 );
  256. realWidth = getMax( (U32)1, padBmp->getWidth() >> scalePower );
  257. realHeight = getMax( (U32)1, padBmp->getHeight() >> scalePower );
  258. realBmp = new GBitmap( realWidth, realHeight, false, bmp->getFormat() );
  259. // Copy to the new bitmap...
  260. dMemcpy( realBmp->getWritableBits(),
  261. padBmp->getBits(scalePower),
  262. padBmp->getBytesPerPixel() * realWidth * realHeight );
  263. // This line is commented out because createPaddedBitmap is commented out.
  264. // If that line is added back in, this line should be added back in.
  265. // delete padBmp;
  266. }
  267. // Call the internal create... (use the real* variables now, as they
  268. // reflect the reality of the texture we are creating.)
  269. U32 numMips = 0;
  270. GFXFormat realFmt = realBmp->getFormat();
  271. _validateTexParams( realWidth, realHeight, profile, numMips, realFmt );
  272. GFXTextureObject *ret;
  273. if ( inObj )
  274. {
  275. // If the texture has changed in dimensions
  276. // then we need to recreate it.
  277. if ( inObj->getWidth() != realWidth ||
  278. inObj->getHeight() != realHeight ||
  279. inObj->getFormat() != realFmt )
  280. ret = _createTextureObject( realHeight, realWidth, 0, realFmt, profile, numMips, false, 0, inObj );
  281. else
  282. ret = inObj;
  283. }
  284. else
  285. ret = _createTextureObject(realHeight, realWidth, 0, realFmt, profile, numMips );
  286. if(!ret)
  287. {
  288. Con::errorf("GFXTextureManager - failed to create texture (1) for '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown"));
  289. return NULL;
  290. }
  291. // Extrude mip levels
  292. // Don't do this for fonts!
  293. if( ret->mMipLevels > 1 && ( realBmp->getNumMipLevels() == 1 ) && ( realBmp->getFormat() != GFXFormatA8 ) &&
  294. isPow2( realBmp->getHeight() ) && isPow2( realBmp->getWidth() ) && !profile->noMip() )
  295. {
  296. // NOTE: This should really be done by extruding mips INTO a DDS file instead
  297. // of modifying the gbitmap
  298. realBmp->extrudeMipLevels(false);
  299. }
  300. // If _validateTexParams kicked back a different format, than there needs to be
  301. // a conversion
  302. DDSFile *bmpDDS = NULL;
  303. if( realBmp->getFormat() != realFmt )
  304. {
  305. const GFXFormat oldFmt = realBmp->getFormat();
  306. // TODO: Set it up so that ALL format conversions use DDSFile. Rip format
  307. // switching out of GBitmap entirely.
  308. if( !realBmp->setFormat( realFmt ) )
  309. {
  310. // This is not the ideal implementation...
  311. bmpDDS = DDSFile::createDDSFileFromGBitmap( realBmp );
  312. bool convSuccess = false;
  313. if( bmpDDS != NULL )
  314. {
  315. // This shouldn't live here, I don't think
  316. switch( realFmt )
  317. {
  318. case GFXFormatDXT1:
  319. case GFXFormatDXT2:
  320. case GFXFormatDXT3:
  321. case GFXFormatDXT4:
  322. case GFXFormatDXT5:
  323. // If this is a Normal Map profile, than the data needs to be conditioned
  324. // to use the swizzle trick
  325. if( ret->mProfile->getType() == GFXTextureProfile::NormalMap )
  326. {
  327. PROFILE_START(DXT_DXTNMSwizzle);
  328. static DXT5nmSwizzle sDXT5nmSwizzle;
  329. DDSUtil::swizzleDDS( bmpDDS, sDXT5nmSwizzle );
  330. PROFILE_END();
  331. }
  332. convSuccess = DDSUtil::squishDDS( bmpDDS, realFmt );
  333. break;
  334. default:
  335. AssertFatal(false, "Attempting to convert to a non-DXT format");
  336. break;
  337. }
  338. }
  339. if( !convSuccess )
  340. {
  341. Con::errorf( "[GFXTextureManager]: Failed to change source format from %s to %s. Cannot create texture.",
  342. GFXStringTextureFormat[oldFmt], GFXStringTextureFormat[realFmt] );
  343. delete bmpDDS;
  344. return NULL;
  345. }
  346. }
  347. #ifdef TORQUE_DEBUG
  348. else
  349. {
  350. //Con::warnf( "[GFXTextureManager]: Changed bitmap format from %s to %s.",
  351. // GFXStringTextureFormat[oldFmt], GFXStringTextureFormat[realFmt] );
  352. }
  353. #endif
  354. }
  355. // Call the internal load...
  356. if( ( bmpDDS == NULL && !_loadTexture( ret, realBmp ) ) || // If we aren't doing a DDS format change, use bitmap load
  357. ( bmpDDS != NULL && !_loadTexture( ret, bmpDDS ) ) ) // If there is a DDS, than load that instead. A format change took place.
  358. {
  359. Con::errorf("GFXTextureManager - failed to load GBitmap for '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown"));
  360. return NULL;
  361. }
  362. // Do statistics and book-keeping...
  363. // - info for the texture...
  364. ret->mTextureLookupName = resourceName;
  365. ret->mBitmapSize.set(realWidth, realHeight,0);
  366. #ifdef TORQUE_DEBUG
  367. if (resourceName.isNotEmpty())
  368. ret->mDebugDescription = resourceName;
  369. else
  370. ret->mDebugDescription = "Anonymous Texture Object";
  371. #endif
  372. if(profile->doStoreBitmap())
  373. {
  374. // NOTE: may store a downscaled copy!
  375. SAFE_DELETE( ret->mBitmap );
  376. SAFE_DELETE( ret->mDDS );
  377. if( bmpDDS == NULL )
  378. ret->mBitmap = new GBitmap( *realBmp );
  379. else
  380. ret->mDDS = bmpDDS;
  381. }
  382. else
  383. {
  384. // Delete the DDS if we made one
  385. SAFE_DELETE( bmpDDS );
  386. }
  387. if ( !inObj )
  388. _linkTexture( ret );
  389. // - output debug info?
  390. // Save texture for debug purpose
  391. // static int texId = 0;
  392. // char buff[256];
  393. // dSprintf(buff, sizeof(buff), "tex_%d", texId++);
  394. // bmp->writePNGDebug(buff);
  395. // texId++;
  396. // Before we delete the bitmap save our transparency flag
  397. ret->mHasTransparency = realBmp->getHasTransparency();
  398. // Some final cleanup...
  399. if(realBmp != bmp)
  400. SAFE_DELETE(realBmp);
  401. if (deleteBmp)
  402. SAFE_DELETE(bmp);
  403. // Return the new texture!
  404. return ret;
  405. }
  406. GFXTextureObject *GFXTextureManager::createTexture( DDSFile *dds, GFXTextureProfile *profile, bool deleteDDS )
  407. {
  408. AssertFatal(dds, "GFXTextureManager::createTexture() - Got NULL dds!");
  409. // Check the cache first...
  410. GFXTextureObject *cacheHit = _lookupTexture( dds, profile );
  411. if ( cacheHit )
  412. {
  413. // Con::errorf("Cached texture '%s'", (fileName.isNotEmpty() ? fileName.c_str() : "unknown"));
  414. if( deleteDDS )
  415. delete dds;
  416. return cacheHit;
  417. }
  418. return _createTexture( dds, profile, deleteDDS, NULL );
  419. }
  420. GFXTextureObject *GFXTextureManager::_createTexture( DDSFile *dds,
  421. GFXTextureProfile *profile,
  422. bool deleteDDS,
  423. GFXTextureObject *inObj )
  424. {
  425. PROFILE_SCOPE( GFXTextureManager_CreateTexture_DDS );
  426. const char *fileName = dds->getTextureCacheString();
  427. if( !fileName )
  428. fileName = "unknown";
  429. #ifdef DEBUG_SPEW
  430. Platform::outputDebugString( "[GFXTextureManager] _createTexture (DDS) '%s'",
  431. fileName
  432. );
  433. #endif
  434. // Ignore padding from the profile.
  435. U32 numMips = dds->mMipMapCount;
  436. GFXFormat fmt = dds->mFormat;
  437. _validateTexParams( dds->getHeight(), dds->getWidth(), profile, numMips, fmt );
  438. if( fmt != dds->mFormat )
  439. {
  440. Con::errorf( "GFXTextureManager - failed to validate texture parameters for DDS file '%s'", fileName );
  441. return NULL;
  442. }
  443. // Call the internal create... (use the real* variables now, as they
  444. // reflect the reality of the texture we are creating.)
  445. GFXTextureObject *ret;
  446. if ( inObj )
  447. {
  448. // If the texture has changed in dimensions
  449. // then we need to recreate it.
  450. if ( inObj->getWidth() != dds->getWidth() ||
  451. inObj->getHeight() != dds->getHeight() ||
  452. inObj->getFormat() != fmt ||
  453. inObj->getMipLevels() != numMips )
  454. ret = _createTextureObject( dds->getHeight(), dds->getWidth(), 0,
  455. fmt, profile, numMips,
  456. true, 0, inObj );
  457. else
  458. ret = inObj;
  459. }
  460. else
  461. ret = _createTextureObject( dds->getHeight(), dds->getWidth(), 0,
  462. fmt, profile, numMips, true );
  463. if(!ret)
  464. {
  465. Con::errorf("GFXTextureManager - failed to create texture (1) for '%s' DDSFile.", fileName);
  466. return NULL;
  467. }
  468. // Call the internal load...
  469. if(!_loadTexture(ret, dds))
  470. {
  471. Con::errorf("GFXTextureManager - failed to load DDS for '%s'", fileName);
  472. return NULL;
  473. }
  474. // Do statistics and book-keeping...
  475. // - info for the texture...
  476. ret->mTextureLookupName = dds->getTextureCacheString();
  477. ret->mBitmapSize.set( dds->mWidth, dds->mHeight, 0 );
  478. #ifdef TORQUE_DEBUG
  479. ret->mDebugDescription = fileName;
  480. #endif
  481. if(profile->doStoreBitmap())
  482. {
  483. // NOTE: may store a downscaled copy!
  484. SAFE_DELETE( ret->mBitmap );
  485. SAFE_DELETE( ret->mDDS );
  486. ret->mDDS = new DDSFile( *dds );
  487. }
  488. if ( !inObj )
  489. _linkTexture( ret );
  490. // - output debug info?
  491. // Save texture for debug purpose
  492. // static int texId = 0;
  493. // char buff[256];
  494. // dSprintf(buff, sizeof(buff), "tex_%d", texId++);
  495. // bmp->writePNGDebug(buff);
  496. // texId++;
  497. // Save our transparency flag
  498. ret->mHasTransparency = dds->getHasTransparency();
  499. if( deleteDDS )
  500. delete dds;
  501. // Return the new texture!
  502. return ret;
  503. }
  504. GFXTextureObject *GFXTextureManager::createTexture( const Torque::Path &path, GFXTextureProfile *profile )
  505. {
  506. PROFILE_SCOPE( GFXTextureManager_createTexture );
  507. // Resource handles used for loading. Hold on to them
  508. // throughout this function so that change notifications
  509. // don't get added, then removed, and then re-added.
  510. Resource< DDSFile > dds;
  511. Resource< GBitmap > bitmap;
  512. // We need to handle path's that have had "incorrect"
  513. // extensions parsed out of the file name
  514. Torque::Path correctPath = path;
  515. bool textureExt = false;
  516. // Easiest case to handle is when there isn't an extension
  517. if (path.getExtension().isEmpty())
  518. textureExt = true;
  519. // Since "dds" isn't registered with GBitmap currently we
  520. // have to test it separately
  521. if (sDDSExt.equal( path.getExtension(), String::NoCase ) )
  522. textureExt = true;
  523. // Now loop through the rest of the GBitmap extensions
  524. // to see if we have any matches
  525. for ( U32 i = 0; i < GBitmap::sRegistrations.size(); i++ )
  526. {
  527. // If we have gotten a match (either in this loop or before)
  528. // then we can exit
  529. if (textureExt)
  530. break;
  531. const GBitmap::Registration &reg = GBitmap::sRegistrations[i];
  532. const Vector<String> &extensions = reg.extensions;
  533. for ( U32 j = 0; j < extensions.size(); ++j )
  534. {
  535. if ( extensions[j].equal( path.getExtension(), String::NoCase ) )
  536. {
  537. // Found a valid texture extension
  538. textureExt = true;
  539. break;
  540. }
  541. }
  542. }
  543. // If we didn't find a valid texture extension then assume that
  544. // the parsed out "extension" was actually intended to be part of
  545. // the texture name so add it back
  546. if (!textureExt)
  547. {
  548. correctPath.setFileName( Torque::Path::Join( path.getFileName(), '.', path.getExtension() ) );
  549. correctPath.setExtension( String::EmptyString );
  550. }
  551. // Check the cache first...
  552. String pathNoExt = Torque::Path::Join( correctPath.getRoot(), ':', correctPath.getPath() );
  553. pathNoExt = Torque::Path::Join( pathNoExt, '/', correctPath.getFileName() );
  554. GFXTextureObject *retTexObj = _lookupTexture( pathNoExt, profile );
  555. if( retTexObj )
  556. return retTexObj;
  557. const U32 scalePower = getTextureDownscalePower( profile );
  558. // If this is a valid file (has an extension) than load it
  559. Path realPath;
  560. if( Torque::FS::IsFile( correctPath ) )
  561. {
  562. // Check for DDS
  563. if( sDDSExt.equal(correctPath.getExtension(), String::NoCase ) )
  564. {
  565. dds = DDSFile::load( correctPath, scalePower );
  566. if( dds != NULL )
  567. {
  568. realPath = dds.getPath();
  569. retTexObj = createTexture( dds, profile, false );
  570. }
  571. }
  572. else // Let GBitmap take care of it
  573. {
  574. bitmap = GBitmap::load( correctPath );
  575. if( bitmap != NULL )
  576. {
  577. realPath = bitmap.getPath();
  578. retTexObj = createTexture( bitmap, pathNoExt, profile, false );
  579. }
  580. }
  581. }
  582. else
  583. {
  584. // NOTE -- We should probably remove the code from GBitmap that tries different
  585. // extensions for things GBitmap loads, and move it here. I think it should
  586. // be a bit more involved than just a list of extensions. Some kind of
  587. // extension registration thing, maybe.
  588. // Check to see if there is a .DDS file with this name (if no extension is provided)
  589. Torque::Path tryDDSPath = pathNoExt;
  590. if( tryDDSPath.getExtension().isNotEmpty() )
  591. tryDDSPath.setFileName( tryDDSPath.getFullFileName() );
  592. tryDDSPath.setExtension( sDDSExt );
  593. if( Torque::FS::IsFile( tryDDSPath ) )
  594. {
  595. dds = DDSFile::load( tryDDSPath, scalePower );
  596. if( dds != NULL )
  597. {
  598. realPath = dds.getPath();
  599. retTexObj = createTexture( dds, profile, false );
  600. }
  601. }
  602. // Otherwise, retTexObj stays NULL, and fall through to the generic GBitmap
  603. // load.
  604. }
  605. // If we still don't have a texture object yet, feed the correctPath to GBitmap and
  606. // it will try a bunch of extensions
  607. if( retTexObj == NULL )
  608. {
  609. // Find and load the texture.
  610. bitmap = GBitmap::load( correctPath );
  611. if ( bitmap != NULL )
  612. {
  613. realPath = bitmap.getPath();
  614. retTexObj = createTexture( bitmap, pathNoExt, profile, false );
  615. }
  616. }
  617. if ( retTexObj )
  618. {
  619. // Store the path for later use.
  620. retTexObj->mPath = realPath;
  621. // Register the texture file for change notifications.
  622. FS::AddChangeNotification( retTexObj->getPath(), this, &GFXTextureManager::_onFileChanged );
  623. }
  624. // Could put in a final check for 'retTexObj == NULL' here as an error message.
  625. return retTexObj;
  626. }
  627. GFXTextureObject *GFXTextureManager::createTexture( U32 width, U32 height, void *pixels, GFXFormat format, GFXTextureProfile *profile )
  628. {
  629. // For now, stuff everything into a GBitmap and pass it off... This may need to be revisited -- BJG
  630. GBitmap *bmp = new GBitmap(width, height, 0, format);
  631. dMemcpy(bmp->getWritableBits(), pixels, width * height * bmp->getBytesPerPixel());
  632. return createTexture( bmp, String::EmptyString, profile, true );
  633. }
  634. GFXTextureObject *GFXTextureManager::createTexture( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, S32 antialiasLevel )
  635. {
  636. // Deal with sizing issues...
  637. U32 localWidth = width;
  638. U32 localHeight = height;
  639. // TODO: Format check HERE! -patw
  640. validateTextureQuality(profile, localWidth, localHeight);
  641. U32 numMips = numMipLevels;
  642. GFXFormat checkFmt = format;
  643. _validateTexParams( localWidth, localHeight, profile, numMips, checkFmt );
  644. AssertFatal( checkFmt == format, "Anonymous texture didn't get the format it wanted." );
  645. GFXTextureObject *outTex = NULL;
  646. // If this is a pooled profile then look there first.
  647. if ( profile->isPooled() )
  648. {
  649. outTex = _findPooledTexure( localWidth, localHeight, checkFmt,
  650. profile, numMips, antialiasLevel );
  651. // If we got a pooled texture then its
  652. // already setup... just return it.
  653. if ( outTex )
  654. return outTex;
  655. }
  656. // Create the texture if we didn't get one from the pool.
  657. if ( !outTex )
  658. {
  659. outTex = _createTextureObject( localHeight, localWidth, 0, format, profile, numMips, false, antialiasLevel );
  660. // Make sure we add it to the pool.
  661. if ( outTex && profile->isPooled() )
  662. mTexturePool.insertEqual( profile, outTex );
  663. }
  664. if ( !outTex )
  665. {
  666. Con::errorf("GFXTextureManager - failed to create anonymous texture.");
  667. return NULL;
  668. }
  669. // And do book-keeping...
  670. // - texture info
  671. outTex->mBitmapSize.set(localWidth, localHeight, 0);
  672. outTex->mAntialiasLevel = antialiasLevel;
  673. // PWTODO: Need to assign this a lookup name before _linkTexture() is called
  674. // otherwise it won't get a hash insert call
  675. _linkTexture( outTex );
  676. return outTex;
  677. }
  678. GFXTextureObject *GFXTextureManager::createTexture( U32 width,
  679. U32 height,
  680. U32 depth,
  681. void *pixels,
  682. GFXFormat format,
  683. GFXTextureProfile *profile )
  684. {
  685. PROFILE_SCOPE( GFXTextureManager_CreateTexture_3D );
  686. // Create texture...
  687. GFXTextureObject *ret = _createTextureObject( height, width, depth, format, profile, 1 );
  688. if(!ret)
  689. {
  690. Con::errorf("GFXTextureManager - failed to create anonymous texture.");
  691. return NULL;
  692. }
  693. // Call the internal load...
  694. if( !_loadTexture( ret, pixels ) )
  695. {
  696. Con::errorf("GFXTextureManager - failed to load volume texture" );
  697. return NULL;
  698. }
  699. // And do book-keeping...
  700. // - texture info
  701. ret->mBitmapSize.set( width, height, depth );
  702. _linkTexture( ret );
  703. // Return the new texture!
  704. return ret;
  705. }
  706. GFXTextureObject* GFXTextureManager::_findPooledTexure( U32 width,
  707. U32 height,
  708. GFXFormat format,
  709. GFXTextureProfile *profile,
  710. U32 numMipLevels,
  711. S32 antialiasLevel )
  712. {
  713. PROFILE_SCOPE( GFXTextureManager_FindPooledTexure );
  714. GFXTextureObject *outTex;
  715. // First see if we have a free one in the pool.
  716. TexturePoolMap::Iterator iter = mTexturePool.find( profile );
  717. for ( ; iter != mTexturePool.end() && iter->key == profile; iter++ )
  718. {
  719. outTex = iter->value;
  720. // If the reference count is 1 then we're the only
  721. // ones holding on to this texture and we can hand
  722. // it out if the size matches... else its in use.
  723. if ( outTex->getRefCount() != 1 )
  724. continue;
  725. // Check for a match... if so return it. The assignment
  726. // to a GFXTexHandle will take care of incrementing the
  727. // reference count and keeping it from being handed out
  728. // to anyone else.
  729. if ( outTex->getFormat() == format &&
  730. outTex->getWidth() == width &&
  731. outTex->getHeight() == height &&
  732. outTex->getMipLevels() == numMipLevels &&
  733. outTex->mAntialiasLevel == antialiasLevel )
  734. return outTex;
  735. }
  736. return NULL;
  737. }
  738. void GFXTextureManager::hashInsert( GFXTextureObject *object )
  739. {
  740. if ( object->mTextureLookupName.isEmpty() )
  741. return;
  742. U32 key = object->mTextureLookupName.getHashCaseInsensitive() % mHashCount;
  743. object->mHashNext = mHashTable[key];
  744. mHashTable[key] = object;
  745. }
  746. void GFXTextureManager::hashRemove( GFXTextureObject *object )
  747. {
  748. if ( object->mTextureLookupName.isEmpty() )
  749. return;
  750. U32 key = object->mTextureLookupName.getHashCaseInsensitive() % mHashCount;
  751. GFXTextureObject **walk = &mHashTable[key];
  752. while(*walk)
  753. {
  754. if(*walk == object)
  755. {
  756. *walk = object->mHashNext;
  757. break;
  758. }
  759. walk = &((*walk)->mHashNext);
  760. }
  761. }
  762. GFXTextureObject* GFXTextureManager::hashFind( const String &name )
  763. {
  764. if ( name.isEmpty() )
  765. return NULL;
  766. U32 key = name.getHashCaseInsensitive() % mHashCount;
  767. GFXTextureObject *walk = mHashTable[key];
  768. for(; walk; walk = walk->mHashNext)
  769. {
  770. if( walk->mTextureLookupName.equal( name, String::NoCase ) )
  771. break;
  772. }
  773. return walk;
  774. }
  775. void GFXTextureManager::freeTexture(GFXTextureObject *texture, bool zombify)
  776. {
  777. // Ok, let the backend deal with it.
  778. _freeTexture(texture, zombify);
  779. }
  780. void GFXTextureManager::refreshTexture(GFXTextureObject *texture)
  781. {
  782. _refreshTexture(texture);
  783. }
  784. void GFXTextureManager::_linkTexture( GFXTextureObject *obj )
  785. {
  786. // info for the profile
  787. GFXTextureProfile::updateStatsForCreation(obj);
  788. // info for the cache
  789. hashInsert(obj);
  790. // info for the master list
  791. if( mListHead == NULL )
  792. mListHead = obj;
  793. if( mListTail != NULL )
  794. mListTail->mNext = obj;
  795. obj->mPrev = mListTail;
  796. mListTail = obj;
  797. }
  798. void GFXTextureManager::deleteTexture( GFXTextureObject *texture )
  799. {
  800. if ( mTextureManagerState == GFXTextureManager::Dead )
  801. return;
  802. #ifdef DEBUG_SPEW
  803. Platform::outputDebugString( "[GFXTextureManager] deleteTexture '%s'",
  804. texture->mTextureLookupName.c_str()
  805. );
  806. #endif
  807. if( mListHead == texture )
  808. mListHead = texture->mNext;
  809. if( mListTail == texture )
  810. mListTail = texture->mPrev;
  811. hashRemove( texture );
  812. // If we have a path for the texture then
  813. // remove change notifications for it.
  814. Path texPath = texture->getPath();
  815. if ( !texPath.isEmpty() )
  816. FS::RemoveChangeNotification( texPath, this, &GFXTextureManager::_onFileChanged );
  817. GFXTextureProfile::updateStatsForDeletion(texture);
  818. freeTexture( texture );
  819. }
  820. void GFXTextureManager::_validateTexParams( const U32 width, const U32 height,
  821. const GFXTextureProfile *profile,
  822. U32 &inOutNumMips, GFXFormat &inOutFormat )
  823. {
  824. // Validate mipmap parameter. If this profile requests no mips, set mips to 1.
  825. if( profile->noMip() )
  826. {
  827. inOutNumMips = 1;
  828. }
  829. else if( !isPow2( width ) || !isPow2( height ) )
  830. {
  831. // If a texture is not power-of-2 in size for both dimensions, it must
  832. // have only 1 mip level.
  833. inOutNumMips = 1;
  834. }
  835. // Check format, and compatibility with texture profile requirements
  836. bool autoGenSupp = ( inOutNumMips == 0 );
  837. // If the format is non-compressed, and the profile requests a compressed format
  838. // than change the format.
  839. GFXFormat testingFormat = inOutFormat;
  840. if( profile->getCompression() != GFXTextureProfile::None )
  841. {
  842. const int offset = profile->getCompression() - GFXTextureProfile::DXT1;
  843. testingFormat = GFXFormat( GFXFormatDXT1 + offset );
  844. // No auto-gen mips on compressed textures
  845. autoGenSupp = false;
  846. }
  847. // inOutFormat is not modified by this method
  848. bool chekFmt = GFX->getCardProfiler()->checkFormat( testingFormat, profile, autoGenSupp );
  849. if( !chekFmt )
  850. {
  851. // It tested for a compressed format, and didn't like it
  852. if( testingFormat != inOutFormat && profile->getCompression() )
  853. testingFormat = inOutFormat; // Reset to requested format, and try again
  854. // Trying again here, so reset autogen mip
  855. autoGenSupp = ( inOutNumMips == 0 );
  856. // Wow more weak sauce. There should be a better way to do this.
  857. switch( inOutFormat )
  858. {
  859. case GFXFormatR8G8B8:
  860. testingFormat = GFXFormatR8G8B8X8;
  861. chekFmt = GFX->getCardProfiler()->checkFormat( testingFormat, profile, autoGenSupp );
  862. break;
  863. case GFXFormatA8:
  864. testingFormat = GFXFormatR8G8B8A8;
  865. chekFmt = GFX->getCardProfiler()->checkFormat( testingFormat, profile, autoGenSupp );
  866. break;
  867. default:
  868. chekFmt = GFX->getCardProfiler()->checkFormat( testingFormat, profile, autoGenSupp );
  869. break;
  870. }
  871. }
  872. // Write back num mips that need to be generated by GBitmap
  873. if( !chekFmt )
  874. Con::errorf( "Format %s not supported with specified profile.", GFXStringTextureFormat[inOutFormat] );
  875. else
  876. {
  877. inOutFormat = testingFormat;
  878. // If auto gen mipmaps were requested, and they aren't supported for whatever
  879. // reason, than write out the number of mips that need to be generated.
  880. //
  881. // NOTE: Does this belong here?
  882. if( inOutNumMips == 0 && !autoGenSupp )
  883. {
  884. U32 currWidth = width;
  885. U32 currHeight = height;
  886. inOutNumMips = 1;
  887. do
  888. {
  889. currWidth >>= 1;
  890. currHeight >>= 1;
  891. if( currWidth == 0 )
  892. currWidth = 1;
  893. if( currHeight == 0 )
  894. currHeight = 1;
  895. inOutNumMips++;
  896. } while ( currWidth != 1 || currHeight != 1 );
  897. }
  898. }
  899. }
  900. GFXCubemap* GFXTextureManager::createCubemap( const Torque::Path &path )
  901. {
  902. // Very first thing... check the cache.
  903. CubemapTable::Iterator iter = mCubemapTable.find( path.getFullPath() );
  904. if ( iter != mCubemapTable.end() )
  905. return iter->value;
  906. // Not in the cache... we have to load it ourselves.
  907. // First check for a DDS file.
  908. if ( !sDDSExt.equal( path.getExtension(), String::NoCase ) )
  909. {
  910. // At the moment we only support DDS cubemaps.
  911. return NULL;
  912. }
  913. const U32 scalePower = getTextureDownscalePower( NULL );
  914. // Ok... load the DDS file then.
  915. Resource<DDSFile> dds = DDSFile::load( path, scalePower );
  916. if ( !dds || !dds->isCubemap() )
  917. {
  918. // This wasn't a cubemap... give up too.
  919. return NULL;
  920. }
  921. // We loaded the cubemap dds, so now we create the GFXCubemap from it.
  922. GFXCubemap *cubemap = GFX->createCubemap();
  923. cubemap->initStatic( dds );
  924. cubemap->_setPath( path.getFullPath() );
  925. // Store the cubemap into the cache.
  926. mCubemapTable.insertUnique( path.getFullPath(), cubemap );
  927. return cubemap;
  928. }
  929. void GFXTextureManager::releaseCubemap( GFXCubemap *cubemap )
  930. {
  931. if ( mTextureManagerState == GFXTextureManager::Dead )
  932. return;
  933. const String &path = cubemap->getPath();
  934. CubemapTable::Iterator iter = mCubemapTable.find( path );
  935. if ( iter != mCubemapTable.end() && iter->value == cubemap )
  936. mCubemapTable.erase( iter );
  937. // If we have a path for the texture then
  938. // remove change notifications for it.
  939. //Path texPath = texture->getPath();
  940. //if ( !texPath.isEmpty() )
  941. //FS::RemoveChangeNotification( texPath, this, &GFXTextureManager::_onFileChanged );
  942. }
  943. void GFXTextureManager::_onFileChanged( const Torque::Path &path )
  944. {
  945. String pathNoExt = Torque::Path::Join( path.getRoot(), ':', path.getPath() );
  946. pathNoExt = Torque::Path::Join( pathNoExt, '/', path.getFileName() );
  947. // See if we've got it loaded.
  948. GFXTextureObject *obj = hashFind( pathNoExt );
  949. if ( !obj || path != obj->getPath() )
  950. return;
  951. Con::errorf( "[GFXTextureManager::_onFileChanged] : File changed [%s]", path.getFullPath().c_str() );
  952. const U32 scalePower = getTextureDownscalePower( obj->mProfile );
  953. if ( sDDSExt.equal( path.getExtension(), String::NoCase) )
  954. {
  955. Resource<DDSFile> dds = DDSFile::load( path, scalePower );
  956. if ( dds )
  957. _createTexture( dds, obj->mProfile, false, obj );
  958. }
  959. else
  960. {
  961. Resource<GBitmap> bmp = GBitmap::load( path );
  962. if( bmp )
  963. _createTexture( bmp, obj->mTextureLookupName, obj->mProfile, false, obj );
  964. }
  965. }
  966. void GFXTextureManager::reloadTextures()
  967. {
  968. GFXTextureObject *tex = mListHead;
  969. while ( tex != NULL )
  970. {
  971. const Torque::Path path( tex->mPath );
  972. if ( !path.isEmpty() )
  973. {
  974. const U32 scalePower = getTextureDownscalePower( tex->mProfile );
  975. if ( sDDSExt.equal( path.getExtension(), String::NoCase ) )
  976. {
  977. Resource<DDSFile> dds = DDSFile::load( path, scalePower );
  978. if ( dds )
  979. _createTexture( dds, tex->mProfile, false, tex );
  980. }
  981. else
  982. {
  983. Resource<GBitmap> bmp = GBitmap::load( path );
  984. if( bmp )
  985. _createTexture( bmp, tex->mTextureLookupName, tex->mProfile, false, tex );
  986. }
  987. }
  988. tex = tex->mNext;
  989. }
  990. }
  991. DefineEngineFunction( flushTextureCache, void, (),,
  992. "Releases all textures and resurrects the texture manager.\n"
  993. "@ingroup GFX\n" )
  994. {
  995. if ( !GFX || !TEXMGR )
  996. return;
  997. TEXMGR->zombify();
  998. TEXMGR->resurrect();
  999. }
  1000. DefineEngineFunction( cleanupTexturePool, void, (),,
  1001. "Release the unused pooled textures in texture manager freeing up video memory.\n"
  1002. "@ingroup GFX\n" )
  1003. {
  1004. if ( !GFX || !TEXMGR )
  1005. return;
  1006. TEXMGR->cleanupPool();
  1007. }
  1008. DefineEngineFunction( reloadTextures, void, (),,
  1009. "Reload all the textures from disk.\n"
  1010. "@ingroup GFX\n" )
  1011. {
  1012. if ( !GFX || !TEXMGR )
  1013. return;
  1014. TEXMGR->reloadTextures();
  1015. }