texturefile.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "texturefile.h"
  19. #include "textureloader.h"
  20. #include "ww3d.h"
  21. #include "wwstring.h"
  22. #include "rawfile.h"
  23. #include "ffactory.h"
  24. #include "nstrdup.h"
  25. #include "texfcach.h"
  26. #include "assetmgr.h"
  27. #ifdef WW3D_DX8
  28. #include <srCore.hpp>
  29. #include <srSurfaceIOManager.hpp>
  30. #include "texture.h"
  31. static TextureFileClass* head;
  32. static bool mipmaps=true;
  33. static int texture_count;
  34. static int locked_surface_count;
  35. static int texture_id;
  36. /*
  37. ** Definitions of static members:
  38. */
  39. unsigned int TextureFileClass::_CurrentTimeStamp = 0U;
  40. float TextureFileClass::_SwitchThreshold = 0.75f; // Default value
  41. // ----------------------------------------------------------------------------
  42. static int Calculate_Size(srColorSurfaceIFace* surface,int red_factor=0)
  43. {
  44. // Set performance statistics
  45. int p=surface->getPitch();
  46. int h=surface->getHeight();
  47. int size=0;
  48. p<<=red_factor;
  49. h<<=red_factor;
  50. while (h) {
  51. size+=p*h;
  52. p>>=1;
  53. h>>=1;
  54. }
  55. return size;
  56. }
  57. // ----------------------------------------------------------------------------
  58. void TextureFileClass::Switch_Mipmaping_Debug()
  59. {
  60. mipmaps=!mipmaps;
  61. TextureFileClass* n=head;
  62. while (n) {
  63. if (mipmaps) n->params.setMipmap(MIPMAP_DEFAULT);
  64. else n->params.setMipmap(MIPMAP_NONE);
  65. n=n->Succ;
  66. }
  67. }
  68. // ----------------------------------------------------------------------------
  69. int TextureFileClass::Get_Total_Locked_Surface_Size()
  70. {
  71. int total_locked_surface_size=0;
  72. TextureFileClass* n=head;
  73. while (n) {
  74. total_locked_surface_size+=n->Get_Locked_Surface_Size();
  75. n=n->Succ;
  76. }
  77. return total_locked_surface_size;
  78. }
  79. // ----------------------------------------------------------------------------
  80. int TextureFileClass::Get_Total_Texture_Size()
  81. {
  82. int total_texture_size=0;
  83. TextureFileClass* n=head;
  84. while (n) {
  85. total_texture_size+=n->Get_Texture_Size();
  86. n=n->Succ;
  87. }
  88. return total_texture_size;
  89. }
  90. // ----------------------------------------------------------------------------
  91. int TextureFileClass::Get_Total_Non_Reduced_Texture_Size()
  92. {
  93. int total_texture_size=0;
  94. TextureFileClass* n=head;
  95. while (n) {
  96. total_texture_size+=n->Get_Non_Reduced_Texture_Size();
  97. n=n->Succ;
  98. }
  99. return total_texture_size;
  100. }
  101. // ----------------------------------------------------------------------------
  102. int TextureFileClass::Get_Total_Texture_Count()
  103. {
  104. return texture_count;
  105. }
  106. // ----------------------------------------------------------------------------
  107. int TextureFileClass::Get_Total_Locked_Surface_Count()
  108. {
  109. return locked_surface_count;
  110. }
  111. StringClass TextureFileClass::List_Missing_Files() // Print out a list of missing files
  112. {
  113. StringClass s(0,true);
  114. TextureFileClass* n=head;
  115. s=
  116. "Missing textures\n\n"
  117. "id name\n"
  118. "------------------------\n";
  119. int missing_count=0;
  120. while (n) {
  121. if (n->Get_File_Error()) {
  122. StringClass tmp;
  123. tmp.Format("%4.4d %s\n",n->ID(),n->Get_File_Name());
  124. s+=tmp;
  125. }
  126. n=n->Succ;
  127. }
  128. if (!missing_count) s="No missing textures\n\n";
  129. else {
  130. StringClass tmp;
  131. tmp.Format("\nTotal missing textures: %d",missing_count);
  132. s+=tmp;
  133. }
  134. return s;
  135. }
  136. // ----------------------------------------------------------------------------
  137. TextureFileClass::TextureFileClass(const char * filename) :
  138. LockedSurface(0),
  139. LockedSurfaceReductionFactor(0),
  140. LockedSurfaceSize(0),
  141. TextureSize(0),
  142. NonReducedTextureSize(0),
  143. CurrentReductionFactor(0),
  144. DesiredReductionFactor(0.0f),
  145. TimeStampOfLastRequestReductionCall(_CurrentTimeStamp - 1),
  146. TimeStampOfLastProcessReductionCall(_CurrentTimeStamp - 1),
  147. FileName(0),
  148. TextureFrameHandle(getNewFrameHandle()),
  149. TempSurfacePtr(0),
  150. id(texture_id++),
  151. flash(false),
  152. flash_store_surface(NULL),
  153. file_error(false)
  154. {
  155. if(filename && *filename) {
  156. FileName = nstrdup(filename);
  157. } else {
  158. FileName = 0;
  159. }
  160. if(FileName) {
  161. setName(FileName);
  162. if (!TextureLoader::Texture_File_Exists(FileName)) {
  163. file_error=true;
  164. }
  165. }
  166. // Load locked surface (or not) according to current locked surface reduction factor set in
  167. // texture loader
  168. Load_Locked_Surface();
  169. texture_count++;
  170. Succ=head;
  171. head=this;
  172. flags.clear(GENERATESURFACE_FAILURE);
  173. flags.set(DIRTY_DEFAULTS);
  174. if (WW3D::Get_Texture_Thumbnail_Mode()!=WW3D::TEXTURE_THUMBNAIL_MODE_RESIZING) {
  175. ReductionEnabled=false;
  176. }
  177. }
  178. TextureFileClass::~TextureFileClass()
  179. {
  180. texture_count--;
  181. if (flash_store_surface) flash_store_surface->release();
  182. if (LockedSurface) locked_surface_count--;
  183. TextureFileClass* n=head;
  184. if (n==this) {
  185. head=Succ;
  186. }
  187. else {
  188. while (n) {
  189. if (n->Succ==this) {
  190. n->Succ=Succ;
  191. break;
  192. }
  193. n=n->Succ;
  194. }
  195. }
  196. invalidate();
  197. if (FileName) delete [] FileName;
  198. if (LockedSurface) LockedSurface->release();
  199. }
  200. // Copy CTor and assignment operator assert for now - if anyone hits the
  201. // assert we might need to actually implement them 8^).
  202. TextureFileClass::TextureFileClass(const TextureFileClass & src)
  203. {
  204. WWASSERT(0);
  205. }
  206. TextureFileClass & TextureFileClass::operator = (const TextureFileClass &that)
  207. {
  208. if (this != &that) {
  209. srTexture::operator = (that);
  210. WWASSERT(0);
  211. }
  212. return *this;
  213. }
  214. srTextureIFace::FrameHandle TextureFileClass::getTextureFrameHandle(void)
  215. {
  216. if (flags.test(GENERATESURFACE_FAILURE))
  217. return 0;
  218. return TextureFrameHandle;
  219. }
  220. void TextureFileClass::getMipmapData(MultiRequest& m)
  221. {
  222. // First some assertions...
  223. WWASSERT(m.largeLOD <= m.smallLOD && m.smallLOD < MAX_LOD);
  224. WWASSERT_PRINT(m.levels[m.largeLOD],"TextureFileClass::getMipmapData() -- NULL surface passed in!\n");
  225. if (!m.levels[m.largeLOD]) return;
  226. /*
  227. ** If the temporary surface pointer points to a surface (this will be a new surface which just
  228. ** got loaded in this frame) use it and release it - done.
  229. ** Otherwise, if we have a locked surface, then normally the current reduction factor should
  230. ** be equal or greater to the locked surface reduction factor so we can use the locked surface
  231. ** (possibly scaled down). If the current reduction factor is less than that of the locked
  232. ** surface, this means that in the past we have loaded up a surface larger than the locked
  233. ** surface, and it has just been thrown out of the API/GERD/HW texture cache. In this case we
  234. ** have no choice but to scale the locked surface up to fill the surface handed to us, and we
  235. ** will request a background load task for the real surface.
  236. ** If there is no locked surface, this means that the texture is never reduced or loaded in
  237. ** the background - we perform a normal blocking load of the surface, use it, and release it
  238. ** (if you want behavior similar to "cached mode" in srTextureFile you should set the locked
  239. ** surface reduction factor to 0, instead of having no locked surface).
  240. */
  241. if (TempSurfacePtr) {
  242. Fill_Multi_Request_From_Surface(m, TempSurfacePtr);
  243. Release_Temp_Surface();
  244. } else {
  245. TextureSize=0;
  246. NonReducedTextureSize=0;
  247. if (LockedSurface) {
  248. Fill_Multi_Request_From_Surface(m, LockedSurface);
  249. if (CurrentReductionFactor < LockedSurfaceReductionFactor) {
  250. WWDEBUG_SAY(("Scaling locked surface up!\n"));
  251. CurrentReductionFactor = LockedSurfaceReductionFactor;
  252. float des_red_factor = floor(DesiredReductionFactor + 0.5f);
  253. WWASSERT(des_red_factor >= 0.0f);
  254. unsigned int int_desired_reduction_factor = (int)des_red_factor;
  255. // If int_desired_reduction_factor is smaller than the locked surface reduction
  256. // factor (meaning we want a larger surface), request to load it.
  257. if (int_desired_reduction_factor < LockedSurfaceReductionFactor) {
  258. texture_loader_info.reduction_factor = int_desired_reduction_factor;
  259. TextureLoader::Add_Load_Task(this);
  260. }
  261. }
  262. else {
  263. // This else clause should only be entered in cases where Process_Reduction() is not
  264. // called every frame on the texture for some reason. Such textures will not resize,
  265. // but this at least ensures that they will get loaded.
  266. Process_Reduction();
  267. }
  268. } else {
  269. Load_Temp_Surface();
  270. if (TempSurfacePtr) {
  271. TextureSize=Calculate_Size(TempSurfacePtr);
  272. NonReducedTextureSize=TextureSize;
  273. Fill_Multi_Request_From_Surface(m, TempSurfacePtr);
  274. Release_Temp_Surface();
  275. }
  276. }
  277. }
  278. }
  279. void TextureFileClass::invalidate(void)
  280. {
  281. Release_Temp_Surface();
  282. invalidateFrameHandle(TextureFrameHandle);
  283. flags.clear(GENERATESURFACE_FAILURE);
  284. }
  285. void TextureFileClass::setupDefaultValues(void)
  286. {
  287. if (flags.test(DIRTY_DEFAULTS))
  288. {
  289. flags.clear (DIRTY_DEFAULTS);
  290. /*
  291. ** If the temporary surface pointer points to a surface (this will be a new surface which
  292. ** just got loaded in this frame by Apply_New_Surface) get defaults from it - done.
  293. ** Otherwise, if we have a locked surface, then normally the current reduction factor should
  294. ** be equal or greater to the locked surface reduction factor so we can get defaults from
  295. ** the locked surface (possibly scaled down). If the current reduction factor is less than
  296. ** that of the locked surface, this means that in the past we have loaded up a surface
  297. ** larger than the locked surface, and it has just been thrown out of the API/GERD/HW
  298. ** texture cache. In this case we set the current reduction factor to be equal to that of
  299. ** the locked surface, and get defaults from the locked surface.
  300. ** If there is no locked surface, this means that the texture is never reduced or loaded in
  301. ** the background - we perform a normal blocking load of the surface, store it in the temp
  302. ** surface pointer (so getMipmapData can also use it), and get defaults from it.
  303. */
  304. if (TempSurfacePtr) {
  305. setupDefaultValuesFromSurface(TempSurfacePtr);
  306. TextureSize=Calculate_Size(TempSurfacePtr);
  307. NonReducedTextureSize=Calculate_Size(TempSurfacePtr,CurrentReductionFactor);
  308. } else {
  309. if (LockedSurface) {
  310. setupDefaultValuesFromSurface(LockedSurface);
  311. if (CurrentReductionFactor >= LockedSurfaceReductionFactor) {
  312. // Scale size down by difference between current and locked reduction factors
  313. unsigned int diff = CurrentReductionFactor - LockedSurfaceReductionFactor;
  314. defaultDimensions.width = defaultDimensions.width >> diff;
  315. if (defaultDimensions.width == 0) defaultDimensions.width = 1;
  316. defaultDimensions.height = defaultDimensions.height >> diff;
  317. if (defaultDimensions.height == 0) defaultDimensions.height = 1;
  318. } else {
  319. CurrentReductionFactor = LockedSurfaceReductionFactor;
  320. }
  321. TextureSize=0;
  322. NonReducedTextureSize=0;
  323. } else {
  324. Load_Temp_Surface();
  325. if (TempSurfacePtr) {
  326. setupDefaultValuesFromSurface(TempSurfacePtr);
  327. TextureSize=Calculate_Size(TempSurfacePtr);
  328. NonReducedTextureSize=TextureSize;
  329. }
  330. }
  331. }
  332. }
  333. }
  334. void TextureFileClass::Apply_New_Surface()
  335. {
  336. invalidate();
  337. TempSurfacePtr = texture_loader_info.new_surface;
  338. texture_loader_info.new_surface = 0;
  339. flags.set(DIRTY_DEFAULTS);
  340. CurrentReductionFactor = texture_loader_info.reduction_factor;
  341. }
  342. // This is used by an object to request a reduction factor on all its textures. Only the smallest
  343. // reduction factor requested in a given frame is preserved.
  344. void TextureFileClass::Request_Reduction(float reduction_factor)
  345. {
  346. if (!ReductionEnabled) return;
  347. if (reduction_factor < 0.0f) reduction_factor = 0.0f;
  348. // If this is the first request in this time-stamp, overwrite desired reduction factor -
  349. // otherwise current factor is the lesser of current and requested.
  350. if (TimeStampOfLastRequestReductionCall != _CurrentTimeStamp) {
  351. DesiredReductionFactor = reduction_factor;
  352. // Update time stamp
  353. TimeStampOfLastRequestReductionCall = _CurrentTimeStamp;
  354. } else {
  355. DesiredReductionFactor = MIN(reduction_factor, DesiredReductionFactor);
  356. }
  357. }
  358. // This is used during rendering - if a textures desired reduction level is "different enough"
  359. // from its current one, we fix it: either by updating from the locked surface (if the reduction
  360. // factor is equal or greater to that of the locked surface) or by asking the texture loader to
  361. // load a new reduction level of the texture in the background.
  362. // NOTE - if there is no locked surface, we do nothing (since textures without locked surfaces do
  363. // not perform reduction).
  364. void TextureFileClass::Process_Reduction(void)
  365. {
  366. // Check if this is the first Process_Reduction() call this frame - otherwise do nothing
  367. if (TimeStampOfLastProcessReductionCall != _CurrentTimeStamp) {
  368. // If we are doing reductions and the difference between the current and desired reduction
  369. // factors is above the threshold, fix it.
  370. if (LockedSurface && fabs(float(CurrentReductionFactor) - DesiredReductionFactor) > _SwitchThreshold) {
  371. // Find the integer desired reduction factor
  372. float des_red_factor = floor(DesiredReductionFactor + 0.5f);
  373. WWASSERT(des_red_factor >= 0.0f);
  374. unsigned int int_desired_reduction_factor = (int)des_red_factor;
  375. if (int_desired_reduction_factor >= LockedSurfaceReductionFactor) {
  376. // Can update from the locked surface:
  377. CurrentReductionFactor = int_desired_reduction_factor;
  378. invalidate();
  379. } else {
  380. // Need to load bigger surface
  381. texture_loader_info.reduction_factor = int_desired_reduction_factor;
  382. TextureLoader::Add_Load_Task(this);
  383. }
  384. }
  385. // Update time stamp
  386. TimeStampOfLastProcessReductionCall = _CurrentTimeStamp;
  387. }
  388. }
  389. void TextureFileClass::_Set_Switch_Threshold(float switch_threshold)
  390. {
  391. _SwitchThreshold = MIN(switch_threshold, 0.999);
  392. _SwitchThreshold = MAX(_SwitchThreshold, 0.5);
  393. }
  394. float TextureFileClass::_Get_Switch_Threshold(void)
  395. {
  396. return _SwitchThreshold;
  397. }
  398. void TextureFileClass::Load_Temp_Surface(void)
  399. {
  400. if (TempSurfacePtr) invalidate();
  401. if (!FileName) return;
  402. TempSurfacePtr = 0;
  403. //
  404. // Use the texture file caching mechanism to load the surface. If no cache
  405. // is initialized, then simply load the surface from the file.
  406. //
  407. TextureFileCache* cache=WW3DAssetManager::Get_Instance()->Texture_File_Cache();
  408. if (cache != NULL) {
  409. cache->Validate_Texture(FileName);
  410. TempSurfacePtr = cache->Get_Surface(
  411. Get_File_Name(),
  412. 0); // No reduction!
  413. } else {
  414. TempSurfacePtr = ::Load_Surface(FileName);
  415. }
  416. }
  417. void TextureFileClass::Release_Temp_Surface(void)
  418. {
  419. if (TempSurfacePtr)
  420. {
  421. TempSurfacePtr->release();
  422. TempSurfacePtr = 0;
  423. }
  424. }
  425. void TextureFileClass::Fill_Multi_Request_From_Surface(MultiRequest& m, srColorSurfaceIFace* surface)
  426. {
  427. m.levels[m.largeLOD]->copy (*surface);
  428. for (LOD i = m.largeLOD+1; i <= m.smallLOD; i++)
  429. {
  430. WWASSERT(m.levels[i] && m.levels[i-1]);
  431. if (m.levels[i] && m.levels[i-1]) // just debug check
  432. m.levels[i]->copy (*(m.levels[i-1]));
  433. }
  434. }
  435. // Debug features
  436. // Make texture flash (Warning! Slow, for debug only!!!)
  437. void TextureFileClass::Set_Texture_Flash(bool b)
  438. {
  439. flash=b;
  440. if (flash) {
  441. Load_Temp_Surface();
  442. flash_store_surface=TempSurfacePtr;
  443. flash_store_surface->addReference();
  444. }
  445. else {
  446. // Restore the original surface... the surface will be released after applying to use
  447. invalidateFrameHandle(TextureFrameHandle);
  448. Release_Temp_Surface();
  449. TempSurfacePtr=flash_store_surface;
  450. flash_store_surface=NULL;
  451. }
  452. }
  453. // Return texture flash state
  454. bool TextureFileClass::Get_Texture_Flash() const
  455. {
  456. return flash;
  457. }
  458. TextureFileClass* TextureFileClass::Get_Texture(int id)
  459. {
  460. TextureFileClass* n=head;
  461. while (n) {
  462. if (n->ID()==id) return n;
  463. n=n->Succ;
  464. }
  465. return NULL;
  466. }
  467. static unsigned latest_time;
  468. static bool flash_state;
  469. static unsigned flash_counter;
  470. const int flash_time=500;
  471. void TextureFileClass::Update_Texture_Flash()
  472. {
  473. int cur_time=WW3D::Get_Sync_Time();
  474. int delta=cur_time-latest_time;
  475. latest_time=cur_time;
  476. flash_counter+=delta;
  477. if (flash_counter>flash_time) {
  478. flash_counter-=flash_time;
  479. if (flash_counter>flash_time) flash_counter=0;
  480. flash_state=!flash_state;
  481. }
  482. else return;
  483. srColorSurface* s=NULL;
  484. if (!flash_state) {
  485. int w=srCore.getSurface()->getWidth();
  486. srColorSurfaceIFace::PixelFormat pf;
  487. srCore.getSurface()->getPixelFormat(pf);
  488. s=W3DNEW srColorSurface(pf,w,w);
  489. s->copy(*srCore.getSurface());
  490. }
  491. TextureFileClass* n=head;
  492. while (n) {
  493. if (n->flash) {
  494. n->invalidateFrameHandle(n->TextureFrameHandle);
  495. n->Release_Temp_Surface();
  496. if (s) {
  497. n->TempSurfacePtr=s;
  498. s->addReference(); // Surface will be release after the use
  499. }
  500. else {
  501. n->TempSurfacePtr=n->flash_store_surface;
  502. n->TempSurfacePtr->addReference(); // Surface will be release after the use
  503. }
  504. }
  505. n=n->Succ;
  506. }
  507. s->release();
  508. }
  509. // If false is passed, the texture will load the largest LOD size and stick to that
  510. void TextureFileClass::Enable_Reduction(bool b)
  511. {
  512. if (ReductionEnabled==b) return;
  513. ReductionEnabled=b;
  514. if (b) {
  515. Load_Locked_Surface();
  516. }
  517. else {
  518. if (LockedSurface) LockedSurface->release();
  519. LockedSurface=NULL;
  520. LockedSurfaceReductionFactor=0;
  521. CurrentReductionFactor=0;
  522. DesiredReductionFactor=0;
  523. Load_Temp_Surface();
  524. if (TempSurfacePtr) {
  525. setupDefaultValuesFromSurface(TempSurfacePtr);
  526. TextureSize=Calculate_Size(TempSurfacePtr);
  527. NonReducedTextureSize=TextureSize;
  528. }
  529. }
  530. }
  531. void TextureFileClass::Load_Locked_Surface()
  532. {
  533. if (LockedSurface) LockedSurface->release();
  534. LockedSurface=NULL;
  535. LockedSurfaceReductionFactor=0;
  536. if (FileName) {
  537. TextureLoader::Load_Locked_Surface_Immediate(FileName, LockedSurface, LockedSurfaceReductionFactor);
  538. if (LockedSurface) {
  539. // If we have a locked surface, set the current reduction factor to be equal to the
  540. // locked surface reduction factor (this is the highest resolution that we can render
  541. // with at this time)
  542. CurrentReductionFactor = LockedSurfaceReductionFactor;
  543. LockedSurfaceSize=Calculate_Size(LockedSurface);
  544. locked_surface_count++;
  545. }
  546. }
  547. }
  548. #endif //WW3D_DX8