textureloader.cpp 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595
  1. /*
  2. ** Command & Conquer Renegade(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 "textureloader.h"
  19. #include "mutex.h"
  20. #include "thread.h"
  21. #include "wwdebug.h"
  22. #include "texture.h"
  23. #include "ffactory.h"
  24. #include "wwstring.h"
  25. #include "bufffile.h"
  26. #include "ww3d.h"
  27. #include "texfcach.h"
  28. #include "assetmgr.h"
  29. #include "dx8wrapper.h"
  30. #include "dx8caps.h"
  31. #include "missingtexture.h"
  32. #include "targa.h"
  33. #include <D3dx8tex.h>
  34. #include <cstdio>
  35. #include "wwmemlog.h"
  36. #include "texture.h"
  37. #include "formconv.h"
  38. #include "texturethumbnail.h"
  39. #include "ddsfile.h"
  40. #include "bitmaphandler.h"
  41. bool TextureLoader::TextureLoadSuspended;
  42. #define USE_MANAGED_TEXTURES
  43. ////////////////////////////////////////////////////////////////////////////////
  44. //
  45. // TextureLoadTaskListClass implementation
  46. //
  47. ////////////////////////////////////////////////////////////////////////////////
  48. TextureLoadTaskListClass::TextureLoadTaskListClass(void)
  49. : Root()
  50. {
  51. Root.Next = Root.Prev = &Root;
  52. }
  53. void TextureLoadTaskListClass::Push_Front (TextureLoadTaskClass *task)
  54. {
  55. // task should non-null and not on any list
  56. WWASSERT(task != NULL && task->Next == NULL && task->Prev == NULL);
  57. // update inserted task to point to list
  58. task->Next = Root.Next;
  59. task->Prev = &Root;
  60. task->List = this;
  61. // update list to point to inserted task
  62. Root.Next->Prev = task;
  63. Root.Next = task;
  64. }
  65. void TextureLoadTaskListClass::Push_Back(TextureLoadTaskClass *task)
  66. {
  67. // task should be non-null and not on any list
  68. WWASSERT(task != NULL && task->Next == NULL && task->Prev == NULL);
  69. // update inserted task to point to list
  70. task->Next = &Root;
  71. task->Prev = Root.Prev;
  72. task->List = this;
  73. // update list to point to inserted task
  74. Root.Prev->Next = task;
  75. Root.Prev = task;
  76. }
  77. TextureLoadTaskClass *TextureLoadTaskListClass::Pop_Front(void)
  78. {
  79. // exit early if list is empty
  80. if (Is_Empty()) {
  81. return 0;
  82. }
  83. // otherwise, grab first task and remove it.
  84. TextureLoadTaskClass *task = (TextureLoadTaskClass *)Root.Next;
  85. Remove(task);
  86. return task;
  87. }
  88. TextureLoadTaskClass *TextureLoadTaskListClass::Pop_Back(void)
  89. {
  90. // exit early if list is empty
  91. if (Is_Empty()) {
  92. return 0;
  93. }
  94. // otherwise, grab last task and remove it.
  95. TextureLoadTaskClass *task = (TextureLoadTaskClass *)Root.Prev;
  96. Remove(task);
  97. return task;
  98. }
  99. void TextureLoadTaskListClass::Remove(TextureLoadTaskClass *task)
  100. {
  101. // exit early if task is not on this list.
  102. if (task->List != this) {
  103. return;
  104. }
  105. // update list to skip task
  106. task->Prev->Next = task->Next;
  107. task->Next->Prev = task->Prev;
  108. // update task to no longer point at list
  109. task->Prev = 0;
  110. task->Next = 0;
  111. task->List = 0;
  112. }
  113. ////////////////////////////////////////////////////////////////////////////////
  114. //
  115. // SynchronizedTextureLoadTaskListClass implementation
  116. //
  117. ////////////////////////////////////////////////////////////////////////////////
  118. SynchronizedTextureLoadTaskListClass::SynchronizedTextureLoadTaskListClass(void)
  119. : TextureLoadTaskListClass(),
  120. CriticalSection()
  121. {
  122. }
  123. void SynchronizedTextureLoadTaskListClass::Push_Front(TextureLoadTaskClass *task)
  124. {
  125. FastCriticalSectionClass::LockClass lock(CriticalSection);
  126. TextureLoadTaskListClass::Push_Front(task);
  127. }
  128. void SynchronizedTextureLoadTaskListClass::Push_Back(TextureLoadTaskClass *task)
  129. {
  130. FastCriticalSectionClass::LockClass lock(CriticalSection);
  131. TextureLoadTaskListClass::Push_Back(task);
  132. }
  133. TextureLoadTaskClass *SynchronizedTextureLoadTaskListClass::Pop_Front(void)
  134. {
  135. // this duplicates code inside base class, but saves us an unnecessary lock.
  136. if (Is_Empty()) {
  137. return 0;
  138. }
  139. FastCriticalSectionClass::LockClass lock(CriticalSection);
  140. return TextureLoadTaskListClass::Pop_Front();
  141. }
  142. TextureLoadTaskClass *SynchronizedTextureLoadTaskListClass::Pop_Back(void)
  143. {
  144. // this duplicates code inside base class, but saves us an unnecessary lock.
  145. if (Is_Empty()) {
  146. return 0;
  147. }
  148. FastCriticalSectionClass::LockClass lock(CriticalSection);
  149. return TextureLoadTaskListClass::Pop_Back();
  150. }
  151. void SynchronizedTextureLoadTaskListClass::Remove(TextureLoadTaskClass *task)
  152. {
  153. FastCriticalSectionClass::LockClass lock(CriticalSection);
  154. TextureLoadTaskListClass::Remove(task);
  155. }
  156. // Locks
  157. // To prevent deadlock, threads should acquire locks in the order in which
  158. // they are defined below. No ordering is necessary for the task list locks,
  159. // since one thread can never hold two at once.
  160. static FastCriticalSectionClass _ForegroundCriticalSection;
  161. static FastCriticalSectionClass _BackgroundCriticalSection;
  162. // Lists
  163. static SynchronizedTextureLoadTaskListClass _ForegroundQueue;
  164. static SynchronizedTextureLoadTaskListClass _BackgroundQueue;
  165. static TextureLoadTaskListClass _FreeList;
  166. // The background texture loading thread.
  167. static class LoaderThreadClass : public ThreadClass
  168. {
  169. public:
  170. #ifdef Exception_Handler
  171. LoaderThreadClass(const char *thread_name = "Texture loader thread") : ThreadClass(thread_name, &Exception_Handler) {}
  172. #else
  173. LoaderThreadClass(const char *thread_name = "Texture loader thread") : ThreadClass(thread_name) {}
  174. #endif
  175. void Thread_Function();
  176. } _TextureLoadThread;
  177. // TODO: Legacy - remove this call!
  178. IDirect3DTexture8* Load_Compressed_Texture(
  179. const StringClass& filename,
  180. unsigned reduction_factor,
  181. TextureClass::MipCountType mip_level_count,
  182. WW3DFormat dest_format)
  183. {
  184. // If DDS file isn't available, use TGA file to convert to DDS.
  185. DDSFileClass dds_file(filename,reduction_factor);
  186. if (!dds_file.Is_Available()) return NULL;
  187. if (!dds_file.Load()) return NULL;
  188. unsigned width=dds_file.Get_Width(0);
  189. unsigned height=dds_file.Get_Height(0);
  190. unsigned mips=dds_file.Get_Mip_Level_Count();
  191. // If format isn't defined get the nearest valid texture format to the compressed file format
  192. // Note that the nearest valid format could be anything, even uncompressed.
  193. if (dest_format==WW3D_FORMAT_UNKNOWN) dest_format=Get_Valid_Texture_Format(dds_file.Get_Format(),true);
  194. IDirect3DTexture8* d3d_texture = DX8Wrapper::_Create_DX8_Texture(
  195. width,
  196. height,
  197. dest_format,
  198. (TextureClass::MipCountType)mips);
  199. for (unsigned level=0;level<mips;++level) {
  200. IDirect3DSurface8* d3d_surface=NULL;
  201. WWASSERT(d3d_texture);
  202. DX8_ErrorCode(d3d_texture->GetSurfaceLevel(level/*-reduction_factor*/,&d3d_surface));
  203. dds_file.Copy_Level_To_Surface(level,d3d_surface);
  204. d3d_surface->Release();
  205. }
  206. return d3d_texture;
  207. }
  208. static bool Is_Format_Compressed(WW3DFormat texture_format,bool allow_compression)
  209. {
  210. // Verify that the user isn't requesting compressed texture without hardware support
  211. bool compressed=false;
  212. if (texture_format!=WW3D_FORMAT_UNKNOWN) {
  213. if (!DX8Wrapper::Get_Current_Caps()->Support_DXTC() || !allow_compression) {
  214. WWASSERT(texture_format!=WW3D_FORMAT_DXT1);
  215. WWASSERT(texture_format!=WW3D_FORMAT_DXT2);
  216. WWASSERT(texture_format!=WW3D_FORMAT_DXT3);
  217. WWASSERT(texture_format!=WW3D_FORMAT_DXT4);
  218. WWASSERT(texture_format!=WW3D_FORMAT_DXT5);
  219. }
  220. if (texture_format==WW3D_FORMAT_DXT1 ||
  221. texture_format==WW3D_FORMAT_DXT2 ||
  222. texture_format==WW3D_FORMAT_DXT3 ||
  223. texture_format==WW3D_FORMAT_DXT4 ||
  224. texture_format==WW3D_FORMAT_DXT5) {
  225. compressed=true;
  226. }
  227. }
  228. // If hardware supports DXTC compression, load a compressed texture. Proceed only if the texture format hasn't been
  229. // defined as non-compressed.
  230. compressed|=(
  231. texture_format==WW3D_FORMAT_UNKNOWN &&
  232. DX8Wrapper::Get_Current_Caps()->Support_DXTC() &&
  233. allow_compression);
  234. return compressed;
  235. }
  236. ////////////////////////////////////////////////////////////////////////////////
  237. //
  238. // TextureLoader implementation
  239. //
  240. ////////////////////////////////////////////////////////////////////////////////
  241. void TextureLoader::Init()
  242. {
  243. WWASSERT(!_TextureLoadThread.Is_Running());
  244. ThumbnailManagerClass::Init();
  245. _TextureLoadThread.Execute();
  246. _TextureLoadThread.Set_Priority(-4);
  247. }
  248. void TextureLoader::Deinit()
  249. {
  250. FastCriticalSectionClass::LockClass lock(_BackgroundCriticalSection);
  251. _TextureLoadThread.Stop();
  252. ThumbnailManagerClass::Deinit();
  253. TextureLoadTaskClass::Delete_Free_Pool();
  254. }
  255. bool TextureLoader::Is_DX8_Thread(void)
  256. {
  257. return (ThreadClass::_Get_Current_Thread_ID() == DX8Wrapper::_Get_Main_Thread_ID());
  258. }
  259. // ----------------------------------------------------------------------------
  260. //
  261. // Modify given texture size to nearest valid size on current hardware.
  262. //
  263. // ----------------------------------------------------------------------------
  264. void TextureLoader::Validate_Texture_Size(unsigned& width, unsigned& height)
  265. {
  266. const D3DCAPS8& dx8caps=DX8Wrapper::Get_Current_Caps()->Get_DX8_Caps();
  267. unsigned poweroftwowidth = 1;
  268. while (poweroftwowidth < width) {
  269. poweroftwowidth <<= 1;
  270. }
  271. unsigned poweroftwoheight = 1;
  272. while (poweroftwoheight < height) {
  273. poweroftwoheight <<= 1;
  274. }
  275. if (poweroftwowidth>dx8caps.MaxTextureWidth) {
  276. poweroftwowidth=dx8caps.MaxTextureWidth;
  277. }
  278. if (poweroftwoheight>dx8caps.MaxTextureHeight) {
  279. poweroftwoheight=dx8caps.MaxTextureHeight;
  280. }
  281. if (poweroftwowidth>poweroftwoheight) {
  282. while (poweroftwowidth/poweroftwoheight>8) {
  283. poweroftwoheight*=2;
  284. }
  285. }
  286. else {
  287. while (poweroftwoheight/poweroftwowidth>8) {
  288. poweroftwowidth*=2;
  289. }
  290. }
  291. width=poweroftwowidth;
  292. height=poweroftwoheight;
  293. }
  294. IDirect3DTexture8* TextureLoader::Load_Thumbnail(const StringClass& filename)//,WW3DFormat texture_format)
  295. {
  296. WWASSERT(Is_DX8_Thread());
  297. ThumbnailClass* thumb=NULL;
  298. ThumbnailManagerClass* thumb_man=ThumbnailManagerClass::Peek_List().Head();
  299. while (thumb_man) {
  300. thumb=thumb_man->Peek_Thumbnail_Instance(filename);
  301. if (thumb) break;
  302. thumb_man=thumb_man->Succ();
  303. }
  304. // If no thumb is found return a missing texture
  305. if (!thumb) {
  306. return MissingTexture::_Get_Missing_Texture();
  307. }
  308. WWASSERT(thumb->Get_Format()==WW3D_FORMAT_A4R4G4B4);
  309. unsigned src_pitch=thumb->Get_Width()*2; // Thumbs are always 16 bits
  310. WW3DFormat dest_format;
  311. WW3DFormat texture_format=WW3D_FORMAT_UNKNOWN;
  312. if (texture_format==WW3D_FORMAT_UNKNOWN) {
  313. dest_format=Get_Valid_Texture_Format(WW3D_FORMAT_A4R4G4B4,false); // no compressed formats please
  314. }
  315. else {
  316. dest_format=Get_Valid_Texture_Format(texture_format,false); // no compressed formats please
  317. WWASSERT(dest_format==texture_format);
  318. }
  319. IDirect3DTexture8* sysmem_texture = DX8Wrapper::_Create_DX8_Texture(
  320. thumb->Get_Width(),
  321. thumb->Get_Height(),
  322. dest_format,
  323. TextureClass::MIP_LEVELS_ALL,
  324. #ifdef USE_MANAGED_TEXTURES
  325. D3DPOOL_MANAGED);
  326. #else
  327. D3DPOOL_SYSTEMMEM);
  328. #endif
  329. unsigned level=0;
  330. D3DLOCKED_RECT locked_rects[12];
  331. WWASSERT(sysmem_texture->GetLevelCount()<=12);
  332. // Lock all surfaces
  333. for (level=0;level<sysmem_texture->GetLevelCount();++level) {
  334. DX8_ErrorCode(
  335. sysmem_texture->LockRect(
  336. level,
  337. &locked_rects[level],
  338. NULL,
  339. 0));
  340. }
  341. unsigned char* src_surface=thumb->Peek_Bitmap();
  342. WW3DFormat src_format=thumb->Get_Format();
  343. unsigned width=thumb->Get_Width();
  344. unsigned height=thumb->Get_Height();
  345. for (level=0;level<sysmem_texture->GetLevelCount()-1;++level) {
  346. BitmapHandlerClass::Copy_Image_Generate_Mipmap(
  347. width,
  348. height,
  349. (unsigned char*)locked_rects[level].pBits,
  350. locked_rects[level].Pitch,
  351. dest_format,
  352. src_surface,
  353. src_pitch,
  354. src_format,
  355. (unsigned char*)locked_rects[level+1].pBits, // mipmap
  356. locked_rects[level+1].Pitch);// mipmap
  357. src_format=dest_format;
  358. src_surface=(unsigned char*)locked_rects[level].pBits;
  359. src_pitch=locked_rects[level].Pitch;
  360. width>>=1;
  361. height>>=1;
  362. }
  363. // Unlock all surfaces
  364. for (level=0;level<sysmem_texture->GetLevelCount();++level) {
  365. DX8_ErrorCode(sysmem_texture->UnlockRect(level));
  366. }
  367. #ifdef USE_MANAGED_TEXTURES
  368. return sysmem_texture;
  369. #else
  370. IDirect3DTexture8* d3d_texture = DX8Wrapper::_Create_DX8_Texture(
  371. thumb->Get_Width(),
  372. thumb->Get_Height(),
  373. dest_format,
  374. TextureClass::MIP_LEVELS_ALL,
  375. D3DPOOL_DEFAULT);
  376. DX8CALL(UpdateTexture(sysmem_texture,d3d_texture));
  377. sysmem_texture->Release();
  378. WWDEBUG_SAY(("Created non-managed texture (%s)\n",filename));
  379. return d3d_texture;
  380. #endif
  381. }
  382. // ----------------------------------------------------------------------------
  383. //
  384. // Load image to a surface. The function tries to create texture that matches
  385. // targa format. If suitable format is not available, it selects closest matching
  386. // format and performs color space conversion.
  387. //
  388. // ----------------------------------------------------------------------------
  389. IDirect3DSurface8* TextureLoader::Load_Surface_Immediate(
  390. const StringClass& filename,
  391. WW3DFormat texture_format,
  392. bool allow_compression)
  393. {
  394. WWASSERT(Is_DX8_Thread());
  395. bool compressed=Is_Format_Compressed(texture_format,allow_compression);
  396. if (compressed) {
  397. IDirect3DTexture8* comp_tex=Load_Compressed_Texture(filename,0,TextureClass::MIP_LEVELS_1,WW3D_FORMAT_UNKNOWN);
  398. if (comp_tex) {
  399. IDirect3DSurface8* d3d_surface=NULL;
  400. DX8_ErrorCode(comp_tex->GetSurfaceLevel(0,&d3d_surface));
  401. comp_tex->Release();
  402. return d3d_surface;
  403. }
  404. }
  405. // Make sure the file can be opened. If not, return missing texture.
  406. Targa targa;
  407. if (TARGA_ERROR_HANDLER(targa.Open(filename, TGA_READMODE),filename)) return MissingTexture::_Create_Missing_Surface();
  408. // DX8 uses image upside down compared to TGA
  409. targa.Header.ImageDescriptor ^= TGAIDF_YORIGIN;
  410. WW3DFormat src_format,dest_format;
  411. unsigned src_bpp=0;
  412. Get_WW3D_Format(dest_format,src_format,src_bpp,targa);
  413. if (texture_format!=WW3D_FORMAT_UNKNOWN) {
  414. dest_format=texture_format;
  415. }
  416. // Destination size will be the next power of two square from the larger width and height...
  417. unsigned width, height;
  418. width=targa.Header.Width;
  419. height=targa.Header.Height;
  420. unsigned src_width=targa.Header.Width;
  421. unsigned src_height=targa.Header.Height;
  422. // NOTE: We load the palette but we do not yet support paletted textures!
  423. char palette[256*4];
  424. targa.SetPalette(palette);
  425. if (TARGA_ERROR_HANDLER(targa.Load(filename, TGAF_IMAGE, false),filename)) return MissingTexture::_Create_Missing_Surface();
  426. unsigned char* src_surface=(unsigned char*)targa.GetImage();
  427. // No paletted destination format allowed
  428. unsigned char* converted_surface=NULL;
  429. if (src_format==WW3D_FORMAT_A1R5G5B5 || src_format==WW3D_FORMAT_R5G6B5 || src_format==WW3D_FORMAT_A4R4G4B4 ||
  430. src_format==WW3D_FORMAT_P8 || src_format==WW3D_FORMAT_L8 || src_width!=width || src_height!=height) {
  431. converted_surface=new unsigned char[width*height*4];
  432. dest_format=Get_Valid_Texture_Format(WW3D_FORMAT_A8R8G8B8,false);
  433. BitmapHandlerClass::Copy_Image(
  434. converted_surface,
  435. width,
  436. height,
  437. width*4,
  438. WW3D_FORMAT_A8R8G8B8,//dest_format,
  439. src_surface,
  440. src_width,
  441. src_height,
  442. src_width*src_bpp,
  443. src_format,
  444. (unsigned char*)targa.GetPalette(),
  445. targa.Header.CMapDepth>>3,
  446. false);
  447. src_surface=converted_surface;
  448. src_format=WW3D_FORMAT_A8R8G8B8;//dest_format;
  449. src_width=width;
  450. src_height=height;
  451. src_bpp=Get_Bytes_Per_Pixel(src_format);
  452. }
  453. unsigned src_pitch=src_width*src_bpp;
  454. IDirect3DSurface8* d3d_surface = DX8Wrapper::_Create_DX8_Surface(width,height,dest_format);
  455. WWASSERT(d3d_surface);
  456. D3DLOCKED_RECT locked_rect;
  457. DX8_ErrorCode(
  458. d3d_surface->LockRect(
  459. &locked_rect,
  460. NULL,
  461. 0));
  462. BitmapHandlerClass::Copy_Image(
  463. (unsigned char*)locked_rect.pBits,
  464. width,
  465. height,
  466. locked_rect.Pitch,
  467. dest_format,
  468. src_surface,
  469. src_width,
  470. src_height,
  471. src_pitch,
  472. src_format,
  473. (unsigned char*)targa.GetPalette(),
  474. targa.Header.CMapDepth>>3,
  475. false); // No mipmap
  476. DX8_ErrorCode(d3d_surface->UnlockRect());
  477. if (converted_surface) delete[] converted_surface;
  478. return d3d_surface;
  479. }
  480. void TextureLoader::Request_Thumbnail(TextureClass *tc)
  481. {
  482. // Grab the foreground lock. This prevents the foreground thread
  483. // from retiring any tasks related to this texture. It also
  484. // serializes calls to Request_Thumbnail from multiple threads.
  485. FastCriticalSectionClass::LockClass lock(_ForegroundCriticalSection);
  486. // Has a Direct3D texture already been loaded?
  487. if (tc->Peek_DX8_Texture()) {
  488. return;
  489. }
  490. TextureLoadTaskClass *task = tc->ThumbnailLoadTask;
  491. if (Is_DX8_Thread()) {
  492. // load the thumbnail immediately
  493. TextureLoader::Load_Thumbnail(tc);
  494. // clear any pending thumbnail load
  495. if (task) {
  496. _ForegroundQueue.Remove(task);
  497. task->Destroy();
  498. }
  499. } else {
  500. TextureLoadTaskClass *load_task = tc->TextureLoadTask;
  501. // if texture is not already loading a thumbnail and there is no
  502. // background load near completion. (a background load waiting
  503. // to be applied will be ready at the same time as a queued thumbnail.
  504. // Why do the extra work?)
  505. if (!task && (!load_task || load_task->Get_State() < TextureLoadTaskClass::STATE_LOAD_MIPMAP)) {
  506. // create a thumbnail load task and add to foreground queue.
  507. task = TextureLoadTaskClass::Create(tc, TextureLoadTaskClass::TASK_THUMBNAIL, TextureLoadTaskClass::PRIORITY_LOW);
  508. _ForegroundQueue.Push_Back(task);
  509. }
  510. }
  511. }
  512. void TextureLoader::Request_Background_Loading(TextureClass *tc)
  513. {
  514. // Grab the foreground lock. This prevents the foreground thread
  515. // from retiring any tasks related to this texture. It also
  516. // serializes calls to Request_Background_Loading from other
  517. // threads.
  518. FastCriticalSectionClass::LockClass foreground_lock(_ForegroundCriticalSection);
  519. // Has the texture already been loaded?
  520. if (tc->Is_Initialized()) {
  521. return;
  522. }
  523. TextureLoadTaskClass *task = tc->TextureLoadTask;
  524. // if texture already has a load task, we don't need to create another one.
  525. if (task) {
  526. return;
  527. }
  528. task = TextureLoadTaskClass::Create(tc, TextureLoadTaskClass::TASK_LOAD, TextureLoadTaskClass::PRIORITY_LOW);
  529. if (Is_DX8_Thread()) {
  530. Begin_Load_And_Queue(task);
  531. } else {
  532. _ForegroundQueue.Push_Back(task);
  533. }
  534. }
  535. void TextureLoader::Request_Foreground_Loading(TextureClass *tc)
  536. {
  537. // Grab the foreground lock. This prevents the foreground thread
  538. // from retiring the load tasks for this texture. It also
  539. // serializes calls to Request_Foreground_Loading from other
  540. // threads.
  541. FastCriticalSectionClass::LockClass foreground_lock(_ForegroundCriticalSection);
  542. // Has the texture already been loaded?
  543. if (tc->Is_Initialized()) {
  544. return;
  545. }
  546. TextureLoadTaskClass *task = tc->TextureLoadTask;
  547. TextureLoadTaskClass *task_thumb = tc->ThumbnailLoadTask;
  548. if (Is_DX8_Thread()) {
  549. // since we're in the DX8 thread, we can load the entire
  550. // texture right now.
  551. // if we have a thumbnail task waiting, kill it.
  552. if (task_thumb) {
  553. _ForegroundQueue.Remove(task_thumb);
  554. task_thumb->Destroy();
  555. }
  556. if (task) {
  557. // we need to remove the task from any queue, since we're going
  558. // to finish it up right now.
  559. // halt background thread. After we're holding this lock,
  560. // we know the background thread cannot begin loading
  561. // mipmap levels for this texture.
  562. FastCriticalSectionClass::LockClass background_lock(_BackgroundCriticalSection);
  563. _ForegroundQueue.Remove(task);
  564. _BackgroundQueue.Remove(task);
  565. } else {
  566. // Since the task manages all the state associated with loading
  567. // a texture, we temporarily create one.
  568. task = TextureLoadTaskClass::Create(tc, TextureLoadTaskClass::TASK_LOAD, TextureLoadTaskClass::PRIORITY_HIGH);
  569. }
  570. // finish loading the task and destroy it.
  571. task->Finish_Load();
  572. task->Destroy();
  573. } else {
  574. // we are not in the DX8 thread. We need to add a high-priority loading
  575. // task to the foreground queue.
  576. // Grab the background lock. After we're holding this lock, we
  577. // know the background thread cannot begin loading mipmap levels
  578. // for this texture.
  579. FastCriticalSectionClass::LockClass background_lock(_BackgroundCriticalSection);
  580. // if we have a thumbnail task, we should cancel it. Since we are not
  581. // the foreground thread, we are not allowed to call Destroy(). Instead,
  582. // leave it queued in the completed state so it will be destroyed by Update().
  583. if (task_thumb) {
  584. task_thumb->Set_State(TextureLoadTaskClass::STATE_COMPLETE);
  585. }
  586. if (task) {
  587. // if a load task is waiting on the background queue, we need to
  588. // move it to the foreground queue.
  589. if (task->Get_List() == &_BackgroundQueue) {
  590. // remove task from list
  591. _BackgroundQueue.Remove(task);
  592. // add to foreground queue.
  593. _ForegroundQueue.Push_Back(task);
  594. }
  595. // upgrade the task priority
  596. task->Set_Priority(TextureLoadTaskClass::PRIORITY_HIGH);
  597. } else {
  598. // allocate high priority load task
  599. task = TextureLoadTaskClass::Create(tc, TextureLoadTaskClass::TASK_LOAD, TextureLoadTaskClass::PRIORITY_HIGH);
  600. // add to back of foreground queue.
  601. _ForegroundQueue.Push_Back(task);
  602. }
  603. }
  604. }
  605. void TextureLoader::Flush_Pending_Load_Tasks(void)
  606. {
  607. // This function can only be called from the main thread.
  608. // (Only the main thread can make the DX8 calls necessary
  609. // to complete texture loading. If we wanted to flush
  610. // the pending tasks from another thread, we'd probably
  611. // want to set a bool that is checked by Update().
  612. WWASSERT(Is_DX8_Thread());
  613. for (;;) {
  614. bool done = false;
  615. {
  616. // we have no pending load tasks when both queues are empty
  617. // and the background thread is not processing a texture.
  618. // Grab the background lock. Once we're holding it, we
  619. // know that the background thread is not processing any
  620. // textures.
  621. // NOTE: It's important that we do only hold on to the background
  622. // lock while we check for completion. Otherwise, we will either
  623. // violate the lock order when we call Update() (which grabs
  624. // the foreground lock) or never give the background thread
  625. // a chance to empty its queue.
  626. FastCriticalSectionClass::LockClass background_lock(_BackgroundCriticalSection);
  627. done = _BackgroundQueue.Is_Empty() && _ForegroundQueue.Is_Empty();
  628. }
  629. // exit loop if no entries in list
  630. if (done) {
  631. break;
  632. }
  633. Update();
  634. ThreadClass::Switch_Thread();
  635. }
  636. }
  637. // Nework update macro for texture loader.
  638. #pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
  639. #include <mmsystem.h>
  640. #define UPDATE_NETWORK \
  641. if (network_callback) { \
  642. unsigned long time2 = timeGetTime(); \
  643. if (time2 - time > 20) { \
  644. network_callback(); \
  645. time = time2; \
  646. } \
  647. } \
  648. void TextureLoader::Update(void (*network_callback)(void))
  649. {
  650. WWASSERT_PRINT(Is_DX8_Thread(), "TextureLoader::Update must be called from the main thread!");
  651. if (TextureLoadSuspended) {
  652. return;
  653. }
  654. // grab foreground lock to prevent any other thread from
  655. // modifying texture tasks.
  656. FastCriticalSectionClass::LockClass lock(_ForegroundCriticalSection);
  657. unsigned long time = timeGetTime();
  658. // while we have tasks on the foreground queue
  659. while (TextureLoadTaskClass *task = _ForegroundQueue.Pop_Front()) {
  660. UPDATE_NETWORK;
  661. // dispatch to proper task handler
  662. switch (task->Get_Type()) {
  663. case TextureLoadTaskClass::TASK_THUMBNAIL:
  664. Process_Foreground_Thumbnail(task);
  665. break;
  666. case TextureLoadTaskClass::TASK_LOAD:
  667. Process_Foreground_Load(task);
  668. break;
  669. }
  670. }
  671. TextureClass::Invalidate_Old_Unused_Textures(0);
  672. }
  673. void TextureLoader::Suspend_Texture_Load()
  674. {
  675. WWASSERT_PRINT(Is_DX8_Thread(),"TextureLoader::Suspend_Texture_Load must be called from the main thread!");
  676. TextureLoadSuspended=true;
  677. }
  678. void TextureLoader::Continue_Texture_Load()
  679. {
  680. WWASSERT_PRINT(Is_DX8_Thread(),"TextureLoader::Continue_Texture_Load must be called from the main thread!");
  681. TextureLoadSuspended=false;
  682. }
  683. void TextureLoader::Process_Foreground_Thumbnail(TextureLoadTaskClass *task)
  684. {
  685. switch (task->Get_State()) {
  686. case TextureLoadTaskClass::STATE_NONE:
  687. Load_Thumbnail(task->Peek_Texture());
  688. // NOTE: fall-through is intentional
  689. case TextureLoadTaskClass::STATE_COMPLETE:
  690. task->Destroy();
  691. break;
  692. }
  693. }
  694. void TextureLoader::Process_Foreground_Load(TextureLoadTaskClass *task)
  695. {
  696. // Is high-priority task?
  697. if (task->Get_Priority() == TextureLoadTaskClass::PRIORITY_HIGH) {
  698. task->Finish_Load();
  699. task->Destroy();
  700. return;
  701. }
  702. // otherwise, must be a low-priority task.
  703. switch (task->Get_State()) {
  704. case TextureLoadTaskClass::STATE_NONE:
  705. Begin_Load_And_Queue(task);
  706. break;
  707. case TextureLoadTaskClass::STATE_LOAD_MIPMAP:
  708. task->End_Load();
  709. task->Destroy();
  710. break;
  711. }
  712. }
  713. void TextureLoader::Begin_Load_And_Queue(TextureLoadTaskClass *task)
  714. {
  715. // should only be called from the DX8 thread.
  716. WWASSERT(Is_DX8_Thread());
  717. if (task->Begin_Load()) {
  718. // add to front of background queue. This means the
  719. // background load thread will service tasks in LIFO
  720. // (last in, first out) order.
  721. // NOTE: this was how the old code did it, with a
  722. // comment that mentioned good reasons for doing so,
  723. // without actually listing the reasons. I suspect
  724. // it has something to do with visually important textures,
  725. // like those in the foreground, starting their load last.
  726. _BackgroundQueue.Push_Front(task);
  727. } else {
  728. // unable to load.
  729. task->Apply_Missing_Texture();
  730. task->Destroy();
  731. }
  732. }
  733. void TextureLoader::Load_Thumbnail(TextureClass *tc)
  734. {
  735. // All D3D operations must run from main thread
  736. WWASSERT(Is_DX8_Thread());
  737. // load thumbnail texture
  738. IDirect3DTexture8 *d3d_texture = Load_Thumbnail(tc->Get_Full_Path());
  739. // apply thumbnail to texture
  740. tc->Apply_New_Surface(d3d_texture, false);
  741. // release our reference to thumbnail texture
  742. d3d_texture->Release();
  743. d3d_texture = 0;
  744. }
  745. void LoaderThreadClass::Thread_Function(void)
  746. {
  747. while (running) {
  748. // if there are no tasks on the background queue, no need to grab background lock.
  749. if (!_BackgroundQueue.Is_Empty()) {
  750. // Grab background load so other threads know we could be
  751. // loading a texture.
  752. FastCriticalSectionClass::LockClass lock(_BackgroundCriticalSection);
  753. // try to remove a task from the background queue. This could fail
  754. // if another thread modified the queue between our test above and
  755. // grabbing the lock.
  756. TextureLoadTaskClass* task = _BackgroundQueue.Pop_Front();
  757. if (task) {
  758. // verify task is in proper state for background processing.
  759. WWASSERT(task->Get_Type() == TextureLoadTaskClass::TASK_LOAD);
  760. WWASSERT(task->Get_State() == TextureLoadTaskClass::STATE_LOAD_BEGUN);
  761. // load mip map levels and return to foreground queue for final step.
  762. task->Load();
  763. _ForegroundQueue.Push_Back(task);
  764. }
  765. }
  766. Switch_Thread();
  767. }
  768. }
  769. ////////////////////////////////////////////////////////////////////////////////
  770. //
  771. // TextureLoaderTaskClass implementation
  772. //
  773. ////////////////////////////////////////////////////////////////////////////////
  774. TextureLoadTaskClass::TextureLoadTaskClass()
  775. : Texture (0),
  776. D3DTexture (0),
  777. Format (WW3D_FORMAT_UNKNOWN),
  778. Width (0),
  779. Height (0),
  780. MipLevelCount (0),
  781. Reduction (0),
  782. Type (TASK_NONE),
  783. Priority (PRIORITY_LOW),
  784. State (STATE_NONE)
  785. {
  786. // because texture load tasks are pooled, the constructor and destructor
  787. // don't need to do much. The work of attaching a task to a texture is
  788. // is done by Init() and Deinit().
  789. for (int i = 0; i < TextureClass::MIP_LEVELS_MAX; ++i) {
  790. LockedSurfacePtr[i] = NULL;
  791. LockedSurfacePitch[i] = 0;
  792. }
  793. }
  794. TextureLoadTaskClass::~TextureLoadTaskClass(void)
  795. {
  796. Deinit();
  797. }
  798. TextureLoadTaskClass *TextureLoadTaskClass::Create(TextureClass *tc, TaskType type, PriorityType priority)
  799. {
  800. // recycle or create a new texture load task with the given type
  801. // and priority, then associate the texture with the task.
  802. // pull a load task from front of free list
  803. TextureLoadTaskClass *task = _FreeList.Pop_Front();
  804. // if no tasks on free list, allocate a new task
  805. if (!task) {
  806. task = new TextureLoadTaskClass;
  807. }
  808. task->Init(tc, type, priority);
  809. return task;
  810. }
  811. void TextureLoadTaskClass::Destroy(void)
  812. {
  813. // detach the task from its texture, and return to free pool.
  814. Deinit();
  815. _FreeList.Push_Front(this);
  816. }
  817. void TextureLoadTaskClass::Delete_Free_Pool(void)
  818. {
  819. // free memory for every task in the free pool.
  820. while (TextureLoadTaskClass *task = _FreeList.Pop_Front()) {
  821. delete task;
  822. }
  823. }
  824. void TextureLoadTaskClass::Init(TextureClass* tc, TaskType type, PriorityType priority)
  825. {
  826. WWASSERT(tc);
  827. // NOTE: we must be in the main thread to avoid corrupting the texture's refcount.
  828. WWASSERT(TextureLoader::Is_DX8_Thread());
  829. REF_PTR_SET(Texture, tc);
  830. // Make sure texture has a filename.
  831. WWASSERT(Texture->Get_Full_Path() != "");
  832. Type = type;
  833. Priority = priority;
  834. State = STATE_NONE;
  835. D3DTexture = 0;
  836. Format = Texture->Get_Texture_Format();
  837. Width = 0;
  838. Height = 0;
  839. MipLevelCount = Texture->MipLevelCount;
  840. Reduction = Texture->Get_Reduction();
  841. for (int i = 0; i < TextureClass::MIP_LEVELS_MAX; ++i) {
  842. LockedSurfacePtr[i] = NULL;
  843. LockedSurfacePitch[i] = 0;
  844. }
  845. switch (Type) {
  846. case TASK_THUMBNAIL:
  847. WWASSERT(Texture->ThumbnailLoadTask == NULL);
  848. Texture->ThumbnailLoadTask = this;
  849. break;
  850. case TASK_LOAD:
  851. WWASSERT(Texture->TextureLoadTask == NULL);
  852. Texture->TextureLoadTask = this;
  853. break;
  854. }
  855. }
  856. void TextureLoadTaskClass::Deinit()
  857. {
  858. // task should not be on any list when it is being detached from texture.
  859. WWASSERT(Next == NULL);
  860. WWASSERT(Prev == NULL);
  861. WWASSERT(D3DTexture == NULL);
  862. for (int i = 0; i < TextureClass::MIP_LEVELS_MAX; ++i) {
  863. WWASSERT(LockedSurfacePtr[i] == NULL);
  864. }
  865. if (Texture) {
  866. switch (Type) {
  867. case TASK_THUMBNAIL:
  868. WWASSERT(Texture->ThumbnailLoadTask == this);
  869. Texture->ThumbnailLoadTask = NULL;
  870. break;
  871. case TASK_LOAD:
  872. WWASSERT(Texture->TextureLoadTask == this);
  873. Texture->TextureLoadTask = NULL;
  874. break;
  875. }
  876. // NOTE: we must be in main thread to avoid corrupting Texture's refcount.
  877. WWASSERT(TextureLoader::Is_DX8_Thread());
  878. REF_PTR_RELEASE(Texture);
  879. }
  880. }
  881. bool TextureLoadTaskClass::Begin_Load(void)
  882. {
  883. WWASSERT(TextureLoader::Is_DX8_Thread());
  884. bool loaded = false;
  885. // if allowed, begin a compressed load
  886. if (Texture->Is_Compression_Allowed()) {
  887. loaded = Begin_Compressed_Load();
  888. }
  889. // otherwise, begin an uncompressed load
  890. if (!loaded) {
  891. loaded = Begin_Uncompressed_Load();
  892. }
  893. // if not loaded, abort.
  894. if (!loaded) {
  895. return false;
  896. }
  897. // lock surfaces in preparation for copy
  898. Lock_Surfaces();
  899. State = STATE_LOAD_BEGUN;
  900. return true;
  901. }
  902. // ----------------------------------------------------------------------------
  903. //
  904. // Load mipmap levels to a pre-generated and locked texture object based on
  905. // information in load task object. Try loading from a DDS file first and if
  906. // that fails try a TGA.
  907. //
  908. // ----------------------------------------------------------------------------
  909. bool TextureLoadTaskClass::Load(void)
  910. {
  911. WWMEMLOG(MEM_TEXTURE);
  912. WWASSERT(Peek_D3D_Texture());
  913. bool loaded = false;
  914. // if allowed, try to load compressed mipmaps
  915. if (Texture->Is_Compression_Allowed()) {
  916. loaded = Load_Compressed_Mipmap();
  917. }
  918. // otherwise, load uncompressed mipmaps
  919. if (!loaded) {
  920. loaded = Load_Uncompressed_Mipmap();
  921. }
  922. State = STATE_LOAD_MIPMAP;
  923. return loaded;
  924. }
  925. void TextureLoadTaskClass::End_Load(void)
  926. {
  927. WWASSERT(TextureLoader::Is_DX8_Thread());
  928. Unlock_Surfaces();
  929. Apply(true);
  930. State = STATE_LOAD_COMPLETE;
  931. }
  932. void TextureLoadTaskClass::Finish_Load(void)
  933. {
  934. switch (State) {
  935. // NOTE: fall-through below is intentional.
  936. case STATE_NONE:
  937. if (!Begin_Load()) {
  938. Apply_Missing_Texture();
  939. break;
  940. }
  941. case STATE_LOAD_BEGUN:
  942. Load();
  943. case STATE_LOAD_MIPMAP:
  944. End_Load();
  945. default:
  946. break;
  947. }
  948. }
  949. void TextureLoadTaskClass::Apply_Missing_Texture(void)
  950. {
  951. WWASSERT(TextureLoader::Is_DX8_Thread());
  952. WWASSERT(!D3DTexture);
  953. D3DTexture = MissingTexture::_Get_Missing_Texture();
  954. Apply(true);
  955. }
  956. void TextureLoadTaskClass::Apply(bool initialize)
  957. {
  958. WWASSERT(D3DTexture);
  959. // Verify that none of the mip levels are locked
  960. for (unsigned i=0;i<MipLevelCount;++i) {
  961. WWASSERT(LockedSurfacePtr[i]==NULL);
  962. }
  963. Texture->Apply_New_Surface(D3DTexture, initialize);
  964. D3DTexture->Release();
  965. D3DTexture = NULL;
  966. }
  967. static bool Get_Texture_Information(
  968. const char* filename,
  969. unsigned reduction,
  970. unsigned& w,
  971. unsigned& h,
  972. WW3DFormat& format,
  973. unsigned& mip_count,
  974. bool compressed)
  975. {
  976. ThumbnailClass* thumb=NULL;
  977. ThumbnailManagerClass* thumb_man=ThumbnailManagerClass::Peek_List().Head();
  978. while (thumb_man) {
  979. thumb=thumb_man->Peek_Thumbnail_Instance(filename);
  980. if (thumb) break;
  981. thumb_man=thumb_man->Succ();
  982. }
  983. if (!thumb) {
  984. if (compressed) {
  985. DDSFileClass dds_file(filename, reduction);
  986. if (!dds_file.Is_Available()) return false;
  987. // Destination size will be the next power of two square from the larger width and height...
  988. w = dds_file.Get_Width(0);
  989. h = dds_file.Get_Height(0);
  990. format = dds_file.Get_Format();
  991. mip_count = dds_file.Get_Mip_Level_Count();
  992. return true;
  993. }
  994. Targa targa;
  995. if (TARGA_ERROR_HANDLER(targa.Open(filename, TGA_READMODE), filename)) {
  996. return false;
  997. }
  998. unsigned int bpp;
  999. WW3DFormat dest_format;
  1000. Get_WW3D_Format(dest_format,format,bpp,targa);
  1001. // Destination size will be the next power of two square from the larger width and height...
  1002. w = targa.Header.Width >> reduction;
  1003. h = targa.Header.Height >> reduction;
  1004. mip_count = 0;
  1005. return true;
  1006. }
  1007. if (compressed &&
  1008. thumb->Get_Original_Texture_Format()!=WW3D_FORMAT_DXT1 &&
  1009. thumb->Get_Original_Texture_Format()!=WW3D_FORMAT_DXT2 &&
  1010. thumb->Get_Original_Texture_Format()!=WW3D_FORMAT_DXT3 &&
  1011. thumb->Get_Original_Texture_Format()!=WW3D_FORMAT_DXT4 &&
  1012. thumb->Get_Original_Texture_Format()!=WW3D_FORMAT_DXT5) {
  1013. return false;
  1014. }
  1015. w=thumb->Get_Original_Texture_Width() >> reduction;
  1016. h=thumb->Get_Original_Texture_Height() >> reduction;
  1017. mip_count=thumb->Get_Original_Texture_Mip_Level_Count();
  1018. format=thumb->Get_Original_Texture_Format();
  1019. return true;
  1020. }
  1021. bool TextureLoadTaskClass::Begin_Compressed_Load(void)
  1022. {
  1023. unsigned orig_w,orig_h,orig_mip_count;
  1024. WW3DFormat orig_format;
  1025. if (!Get_Texture_Information(Texture->Get_Full_Path(),Get_Reduction(),orig_w,orig_h,orig_format,orig_mip_count,true)) {
  1026. return false;
  1027. }
  1028. // Destination size will be the next power of two square from the larger width and height...
  1029. unsigned int width = orig_w;
  1030. unsigned int height = orig_h;
  1031. TextureLoader::Validate_Texture_Size(width, height);
  1032. // If the size doesn't match, try and see if texture reduction would help... (mainly for
  1033. // cases where loaded texture is larger than hardware limit)
  1034. if (width != orig_w || height != orig_h) {
  1035. for (unsigned int i = 1; i < orig_mip_count; ++i) {
  1036. unsigned w=orig_w>>i;
  1037. if (w<4) w=4;
  1038. unsigned h=orig_h>>i;
  1039. if (h<4) h=4;
  1040. unsigned tmp_w=w;
  1041. unsigned tmp_h=h;
  1042. TextureLoader::Validate_Texture_Size(w,h);
  1043. if (w == tmp_w && h == tmp_h) {
  1044. Reduction += i;
  1045. width = w;
  1046. height = h;
  1047. break;
  1048. }
  1049. }
  1050. }
  1051. Width = width;
  1052. Height = height;
  1053. Format = Get_Valid_Texture_Format(orig_format, Texture->Is_Compression_Allowed());
  1054. unsigned int mip_level_count = Get_Mip_Level_Count();
  1055. // If texture wants all mip levels, take as many as the file contains (not necessarily all)
  1056. // Otherwise take as many mip levels as the texture wants, not to exceed the count in file...
  1057. if (!mip_level_count) {
  1058. mip_level_count = orig_mip_count;//dds_file.Get_Mip_Level_Count();
  1059. } else if (mip_level_count > orig_mip_count) {//dds_file.Get_Mip_Level_Count()) {
  1060. mip_level_count = orig_mip_count;//dds_file.Get_Mip_Level_Count();
  1061. }
  1062. // Once more, verify that the mip level count is correct (in case it was changed here it might not
  1063. // match the size...well actually it doesn't have to match but it can't be bigger than the size)
  1064. unsigned int max_mip_level_count = 1;
  1065. unsigned int w = 4;
  1066. unsigned int h = 4;
  1067. while (w < Width && h < Height) {
  1068. w += w;
  1069. h += h;
  1070. max_mip_level_count++;
  1071. }
  1072. if (mip_level_count > max_mip_level_count) {
  1073. mip_level_count = max_mip_level_count;
  1074. }
  1075. D3DTexture = DX8Wrapper::_Create_DX8_Texture(
  1076. Width,
  1077. Height,
  1078. Format,
  1079. (TextureClass::MipCountType)mip_level_count,
  1080. #ifdef USE_MANAGED_TEXTURES
  1081. D3DPOOL_MANAGED);
  1082. #else
  1083. D3DPOOL_SYSTEMMEM);
  1084. #endif
  1085. MipLevelCount = mip_level_count;
  1086. return true;
  1087. }
  1088. bool TextureLoadTaskClass::Begin_Uncompressed_Load(void)
  1089. {
  1090. unsigned width,height,orig_mip_count;
  1091. WW3DFormat orig_format;
  1092. if (!Get_Texture_Information(Texture->Get_Full_Path(),Get_Reduction(),width,height,orig_format,orig_mip_count,false)) {
  1093. return false;
  1094. }
  1095. WW3DFormat src_format=orig_format;
  1096. WW3DFormat dest_format=src_format;
  1097. dest_format=Get_Valid_Texture_Format(dest_format,false); // No compressed destination format if reading from targa...
  1098. if ( src_format != WW3D_FORMAT_A8R8G8B8
  1099. && src_format != WW3D_FORMAT_R8G8B8
  1100. && src_format != WW3D_FORMAT_X8R8G8B8) {
  1101. WWDEBUG_SAY(("Invalid TGA format used in %s - only 24 and 32 bit formats should be used!\n", Texture->Get_Full_Path()));
  1102. }
  1103. // Destination size will be the next power of two square from the larger width and height...
  1104. unsigned ow = width;
  1105. unsigned oh = height;
  1106. TextureLoader::Validate_Texture_Size(width, height);
  1107. if (width != ow || height != oh) {
  1108. WWDEBUG_SAY(("Invalid texture size, scaling required. Texture: %s, size: %d x %d -> %d x %d\n", Texture->Get_Full_Path(), ow, oh, width, height));
  1109. }
  1110. Width = width;
  1111. Height = height;
  1112. if (Format == WW3D_FORMAT_UNKNOWN) {
  1113. Format = Get_Valid_Texture_Format(dest_format, false);
  1114. } else {
  1115. Format = Get_Valid_Texture_Format(Format, false);
  1116. }
  1117. D3DTexture = DX8Wrapper::_Create_DX8_Texture(
  1118. Width,
  1119. Height,
  1120. Format,
  1121. Texture->MipLevelCount,
  1122. #ifdef USE_MANAGED_TEXTURES
  1123. D3DPOOL_MANAGED);
  1124. #else
  1125. D3DPOOL_SYSTEMMEM);
  1126. #endif
  1127. return true;
  1128. }
  1129. void TextureLoadTaskClass::Lock_Surfaces(void)
  1130. {
  1131. MipLevelCount = D3DTexture->GetLevelCount();
  1132. for (unsigned int i = 0; i < MipLevelCount; ++i) {
  1133. D3DLOCKED_RECT locked_rect;
  1134. DX8_ErrorCode(
  1135. D3DTexture->LockRect(
  1136. i,
  1137. &locked_rect,
  1138. NULL,
  1139. 0));
  1140. LockedSurfacePtr[i] = (unsigned char *)locked_rect.pBits;
  1141. LockedSurfacePitch[i] = locked_rect.Pitch;
  1142. }
  1143. }
  1144. void TextureLoadTaskClass::Unlock_Surfaces(void)
  1145. {
  1146. for (unsigned int i = 0; i < MipLevelCount; ++i) {
  1147. if (LockedSurfacePtr[i]) {
  1148. WWASSERT(ThreadClass::_Get_Current_Thread_ID() == DX8Wrapper::_Get_Main_Thread_ID());
  1149. DX8_ErrorCode(D3DTexture->UnlockRect(i));
  1150. }
  1151. LockedSurfacePtr[i] = NULL;
  1152. }
  1153. #ifndef USE_MANAGED_TEXTURES
  1154. IDirect3DTexture8* tex = DX8Wrapper::_Create_DX8_Texture(Width, Height, Format, Texture->MipLevelCount,D3DPOOL_DEFAULT);
  1155. DX8CALL(UpdateTexture(D3DTexture,tex));
  1156. D3DTexture->Release();
  1157. D3DTexture=tex;
  1158. WWDEBUG_SAY(("Created non-managed texture (%s)\n",Texture->Get_Full_Path()));
  1159. #endif
  1160. }
  1161. bool TextureLoadTaskClass::Load_Compressed_Mipmap(void)
  1162. {
  1163. DDSFileClass dds_file(Texture->Get_Full_Path(), Get_Reduction());
  1164. // if we can't load from file, indicate rror.
  1165. if (!dds_file.Is_Available() || !dds_file.Load()) {
  1166. return false;
  1167. }
  1168. unsigned int width = Get_Width();
  1169. unsigned int height = Get_Height();
  1170. for (unsigned int level = 0; level < Get_Mip_Level_Count(); ++level) {
  1171. WWASSERT(width && height);
  1172. dds_file.Copy_Level_To_Surface(
  1173. level,
  1174. Get_Format(),
  1175. width,
  1176. height,
  1177. Get_Locked_Surface_Ptr(level),
  1178. Get_Locked_Surface_Pitch(level));
  1179. width >>= 1;
  1180. height >>= 1;
  1181. }
  1182. return true;
  1183. }
  1184. bool TextureLoadTaskClass::Load_Uncompressed_Mipmap(void)
  1185. {
  1186. if (!Get_Mip_Level_Count()) {
  1187. return false;
  1188. }
  1189. Targa targa;
  1190. if (TARGA_ERROR_HANDLER(targa.Open(Texture->Get_Full_Path(), TGA_READMODE), Texture->Get_Full_Path())) {
  1191. return false;
  1192. }
  1193. // DX8 uses image upside down compared to TGA
  1194. targa.Header.ImageDescriptor ^= TGAIDF_YORIGIN;
  1195. WW3DFormat src_format;
  1196. WW3DFormat dest_format;
  1197. unsigned int src_bpp = 0;
  1198. Get_WW3D_Format(dest_format,src_format,src_bpp,targa);
  1199. if (src_format==WW3D_FORMAT_UNKNOWN) return false;
  1200. dest_format = Get_Format(); // Texture can be requested in different format than the most obvious from the TGA
  1201. char palette[256*4];
  1202. targa.SetPalette(palette);
  1203. unsigned int src_width = targa.Header.Width;
  1204. unsigned int src_height = targa.Header.Height;
  1205. unsigned int width = Get_Width();
  1206. unsigned int height = Get_Height();
  1207. // NOTE: We load the palette but we do not yet support paletted textures!
  1208. if (TARGA_ERROR_HANDLER(targa.Load(Texture->Get_Full_Path(), TGAF_IMAGE, false), Texture->Get_Full_Path())) {
  1209. return false;
  1210. }
  1211. unsigned char * src_surface = (unsigned char*)targa.GetImage();
  1212. unsigned char * converted_surface = NULL;
  1213. // No paletted format allowed when generating mipmaps
  1214. if ( src_format == WW3D_FORMAT_A1R5G5B5
  1215. || src_format == WW3D_FORMAT_R5G6B5
  1216. || src_format == WW3D_FORMAT_A4R4G4B4
  1217. || src_format == WW3D_FORMAT_P8
  1218. || src_format == WW3D_FORMAT_L8
  1219. || src_width != width
  1220. || src_height != height) {
  1221. converted_surface = new unsigned char[width*height*4];
  1222. dest_format = Get_Valid_Texture_Format(WW3D_FORMAT_A8R8G8B8, false);
  1223. BitmapHandlerClass::Copy_Image(
  1224. converted_surface,
  1225. width,
  1226. height,
  1227. width*4,
  1228. WW3D_FORMAT_A8R8G8B8, //dest_format,
  1229. src_surface,
  1230. src_width,
  1231. src_height,
  1232. src_width*src_bpp,
  1233. src_format,
  1234. (unsigned char*)targa.GetPalette(),
  1235. targa.Header.CMapDepth>>3,
  1236. false);
  1237. src_surface = converted_surface;
  1238. src_format = WW3D_FORMAT_A8R8G8B8; //dest_format;
  1239. src_width = width;
  1240. src_height = height;
  1241. src_bpp = Get_Bytes_Per_Pixel(src_format);
  1242. }
  1243. unsigned src_pitch = src_width * src_bpp;
  1244. for (unsigned int level = 0; level < Get_Mip_Level_Count(); ++level) {
  1245. WWASSERT(Get_Locked_Surface_Ptr(level));
  1246. BitmapHandlerClass::Copy_Image(
  1247. Get_Locked_Surface_Ptr(level),
  1248. width,
  1249. height,
  1250. Get_Locked_Surface_Pitch(level),
  1251. Get_Format(),
  1252. src_surface,
  1253. src_width,
  1254. src_height,
  1255. src_pitch,
  1256. src_format,
  1257. NULL,
  1258. 0,
  1259. true);
  1260. width >>= 1;
  1261. height >>= 1;
  1262. src_width >>= 1;
  1263. src_height >>= 1;
  1264. if (!width || !height || !src_width || !src_height) {
  1265. break;
  1266. }
  1267. }
  1268. if (converted_surface) {
  1269. delete[] converted_surface;
  1270. }
  1271. return true;
  1272. }
  1273. unsigned char * TextureLoadTaskClass::Get_Locked_Surface_Ptr(unsigned int level)
  1274. {
  1275. WWASSERT(level<MipLevelCount);
  1276. WWASSERT(LockedSurfacePtr[level]);
  1277. return LockedSurfacePtr[level];
  1278. }
  1279. // ----------------------------------------------------------------------------
  1280. //
  1281. // Return locked surface pitch (in bytes) at a specific level. The call will
  1282. // assert if level is greater or equal to the number of mip levels or if the
  1283. // requested level has not been locked.
  1284. //
  1285. // ----------------------------------------------------------------------------
  1286. unsigned int TextureLoadTaskClass::Get_Locked_Surface_Pitch(unsigned int level) const
  1287. {
  1288. WWASSERT(level<MipLevelCount);
  1289. WWASSERT(LockedSurfacePtr[level]);
  1290. return LockedSurfacePitch[level];
  1291. }