D3D9TextureCube.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2012 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "Context.h"
  25. #include "FileSystem.h"
  26. #include "Graphics.h"
  27. #include "GraphicsImpl.h"
  28. #include "Log.h"
  29. #include "Profiler.h"
  30. #include "Renderer.h"
  31. #include "ResourceCache.h"
  32. #include "TextureCube.h"
  33. #include "XMLFile.h"
  34. #include "DebugNew.h"
  35. #ifdef _MSC_VER
  36. #pragma warning(disable:4355)
  37. #endif
  38. OBJECTTYPESTATIC(TextureCube);
  39. TextureCube::TextureCube(Context* context) :
  40. Texture(context),
  41. lockedLevel_(-1)
  42. {
  43. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  44. faceMemoryUse_[i] = 0;
  45. }
  46. TextureCube::~TextureCube()
  47. {
  48. Release();
  49. }
  50. void TextureCube::RegisterObject(Context* context)
  51. {
  52. context->RegisterFactory<TextureCube>();
  53. }
  54. void TextureCube::OnDeviceLost()
  55. {
  56. if (pool_ == D3DPOOL_DEFAULT)
  57. {
  58. Release();
  59. dataLost_ = true;
  60. }
  61. }
  62. void TextureCube::OnDeviceReset()
  63. {
  64. if (pool_ == D3DPOOL_DEFAULT)
  65. {
  66. // If has a file name, reload through the resource cache. Otherwise just recreate.
  67. if (!GetName().Trimmed().Empty())
  68. {
  69. if (GetSubsystem<ResourceCache>()->ReloadResource(this))
  70. dataLost_ = false;
  71. }
  72. else
  73. Create();
  74. }
  75. }
  76. void TextureCube::Release()
  77. {
  78. if (object_)
  79. {
  80. if (!graphics_)
  81. return;
  82. for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
  83. {
  84. if (graphics_->GetTexture(i) == this)
  85. graphics_->SetTexture(i, 0);
  86. }
  87. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  88. {
  89. if (renderSurfaces_[i])
  90. renderSurfaces_[i]->Release();
  91. }
  92. ((IDirect3DCubeTexture9*)object_)->Release();
  93. object_ = 0;
  94. }
  95. }
  96. bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
  97. {
  98. if (size <= 0)
  99. {
  100. LOGERROR("Zero or negative cube texture size");
  101. return false;
  102. }
  103. if (usage == TEXTURE_DEPTHSTENCIL)
  104. {
  105. LOGERROR("Depth-stencil usage not supported for cube maps");
  106. return false;
  107. }
  108. // Delete the old rendersurfaces if any
  109. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  110. {
  111. renderSurfaces_[i].Reset();
  112. faceMemoryUse_[i] = 0;
  113. }
  114. pool_ = D3DPOOL_MANAGED;
  115. usage_ = 0;
  116. if (usage == TEXTURE_RENDERTARGET)
  117. {
  118. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  119. renderSurfaces_[i] = new RenderSurface(this);
  120. usage_ |= D3DUSAGE_RENDERTARGET;
  121. pool_ = D3DPOOL_DEFAULT;
  122. // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
  123. addressMode_[COORD_U] = ADDRESS_CLAMP;
  124. addressMode_[COORD_V] = ADDRESS_CLAMP;
  125. addressMode_[COORD_W] = ADDRESS_CLAMP;
  126. filterMode_ = FILTER_NEAREST;
  127. requestedLevels_ = 1;
  128. }
  129. else if (usage == TEXTURE_DYNAMIC)
  130. {
  131. usage_ |= D3DUSAGE_DYNAMIC;
  132. pool_ = D3DPOOL_DEFAULT;
  133. }
  134. width_ = size;
  135. height_ = size;
  136. format_ = format;
  137. return Create();
  138. }
  139. bool TextureCube::SetData(CubeMapFace face, unsigned level, int x, int y, int width, int height, const void* data)
  140. {
  141. if (!object_)
  142. {
  143. LOGERROR("No texture created, can not set data");
  144. return false;
  145. }
  146. if (!data)
  147. {
  148. LOGERROR("Null source for setting data");
  149. return false;
  150. }
  151. if (level >= levels_)
  152. {
  153. LOGERROR("Illegal mip level for setting data");
  154. return false;
  155. }
  156. if (IsCompressed())
  157. {
  158. x &= ~3;
  159. y &= ~3;
  160. }
  161. int levelWidth = GetLevelWidth(level);
  162. int levelHeight = GetLevelHeight(level);
  163. if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || width <= 0 || height <= 0)
  164. {
  165. LOGERROR("Illegal dimensions for setting data");
  166. return false;
  167. }
  168. D3DLOCKED_RECT d3dLockedRect;
  169. RECT d3dRect;
  170. d3dRect.left = x;
  171. d3dRect.top = y;
  172. d3dRect.right = x + width;
  173. d3dRect.bottom = y + height;
  174. DWORD flags = 0;
  175. if (level == 0 && x == 0 && y == 0 && width == levelWidth && height == levelHeight && pool_ == D3DPOOL_DEFAULT)
  176. flags |= D3DLOCK_DISCARD;
  177. if (FAILED(((IDirect3DCubeTexture9*)object_)->LockRect((D3DCUBEMAP_FACES)face, level, &d3dLockedRect, (flags &
  178. D3DLOCK_DISCARD) ? 0 : &d3dRect, flags)))
  179. {
  180. LOGERROR("Could not lock texture");
  181. return false;
  182. }
  183. if (IsCompressed())
  184. {
  185. height = (height + 3) >> 2;
  186. y >>= 2;
  187. }
  188. unsigned char* src = (unsigned char*)data;
  189. unsigned rowSize = GetRowDataSize(width);
  190. unsigned rowOffset = GetRowDataSize(x);
  191. // GetRowDataSize() returns CPU-side (source) data size, so need to convert for X8R8G8B8
  192. if (format_ == D3DFMT_X8R8G8B8)
  193. {
  194. rowSize = rowSize / 3 * 4;
  195. rowOffset = rowOffset / 3 * 4;
  196. }
  197. // Perform conversion from RGB / RGBA as necessary
  198. switch (format_)
  199. {
  200. default:
  201. for (int i = 0; i < height; ++i)
  202. {
  203. unsigned char* dest = (unsigned char*)d3dLockedRect.pBits + (y + i) * d3dLockedRect.Pitch + rowOffset;
  204. memcpy(dest, src, rowSize);
  205. src += rowSize;
  206. }
  207. break;
  208. case D3DFMT_X8R8G8B8:
  209. for (int i = 0; i < height; ++i)
  210. {
  211. unsigned char* dest = (unsigned char*)d3dLockedRect.pBits + (y + i) * d3dLockedRect.Pitch + rowOffset;
  212. for (int j = 0; j < width; ++j)
  213. {
  214. *dest++ = src[2]; *dest++ = src[1]; *dest++ = src[0]; *dest++ = 255;
  215. src += 3;
  216. }
  217. }
  218. break;
  219. case D3DFMT_A8R8G8B8:
  220. for (int i = 0; i < height; ++i)
  221. {
  222. unsigned char* dest = (unsigned char*)d3dLockedRect.pBits + (y + i) * d3dLockedRect.Pitch + rowOffset;
  223. for (int j = 0; j < width; ++j)
  224. {
  225. *dest++ = src[2]; *dest++ = src[1]; *dest++ = src[0]; *dest++ = src[3];
  226. src += 4;
  227. }
  228. }
  229. break;
  230. }
  231. ((IDirect3DCubeTexture9*)object_)->UnlockRect((D3DCUBEMAP_FACES)face, level);
  232. return true;
  233. }
  234. bool TextureCube::Load(Deserializer& source)
  235. {
  236. PROFILE(LoadTextureCube);
  237. ResourceCache* cache = GetSubsystem<ResourceCache>();
  238. // In headless mode, do not actually load the texture, just return success
  239. Graphics* graphics = GetSubsystem<Graphics>();
  240. if (!graphics)
  241. return true;
  242. // If over the texture budget, see if materials can be freed to allow textures to be freed
  243. CheckTextureBudget(GetTypeStatic());
  244. String texPath, texName, texExt;
  245. SplitPath(GetName(), texPath, texName, texExt);
  246. SharedPtr<XMLFile> xml(new XMLFile(context_));
  247. if (!xml->Load(source))
  248. return false;
  249. LoadParameters(xml);
  250. XMLElement textureElem = xml->GetRoot();
  251. XMLElement faceElem = textureElem.GetChild("face");
  252. unsigned faces = 0;
  253. while (faceElem && faces < MAX_CUBEMAP_FACES)
  254. {
  255. String name = faceElem.GetAttribute("name");
  256. String faceTexPath, faceTexName, faceTexExt;
  257. SplitPath(name, faceTexPath, faceTexName, faceTexExt);
  258. // If path is empty, add the XML file path
  259. if (faceTexPath.Empty())
  260. name = texPath + name;
  261. SharedPtr<Image> image(cache->GetResource<Image>(name));
  262. Load((CubeMapFace)faces, image);
  263. faces++;
  264. faceElem = faceElem.GetNext("face");
  265. }
  266. return true;
  267. }
  268. bool TextureCube::Load(CubeMapFace face, Deserializer& source)
  269. {
  270. PROFILE(LoadTextureCube);
  271. SharedPtr<Image> image(new Image(context_));
  272. if (!image->Load(source))
  273. return false;
  274. return Load(face, image);
  275. }
  276. bool TextureCube::Load(CubeMapFace face, SharedPtr<Image> image, bool useAlpha)
  277. {
  278. if (!image)
  279. {
  280. LOGERROR("Null image, can not load texture");
  281. return false;
  282. }
  283. unsigned memoryUse = 0;
  284. int quality = QUALITY_HIGH;
  285. Renderer* renderer = GetSubsystem<Renderer>();
  286. if (renderer)
  287. quality = renderer->GetTextureQuality();
  288. if (!image->IsCompressed())
  289. {
  290. unsigned char* levelData = image->GetData();
  291. int levelWidth = image->GetWidth();
  292. int levelHeight = image->GetHeight();
  293. unsigned components = image->GetComponents();
  294. unsigned format = 0;
  295. if (levelWidth != levelHeight)
  296. {
  297. LOGERROR("Cube texture width not equal to height");
  298. return false;
  299. }
  300. // Discard unnecessary mip levels
  301. for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
  302. {
  303. image = image->GetNextLevel();
  304. levelWidth = image->GetWidth();
  305. levelHeight = image->GetHeight();
  306. }
  307. switch (components)
  308. {
  309. case 1:
  310. format = useAlpha ? Graphics::GetAlphaFormat() : Graphics::GetLuminanceFormat();
  311. break;
  312. case 2:
  313. format = Graphics::GetLuminanceAlphaFormat();
  314. break;
  315. case 3:
  316. format = Graphics::GetRGBFormat();
  317. break;
  318. case 4:
  319. format = Graphics::GetRGBAFormat();
  320. break;
  321. }
  322. // Create the texture when face 0 is being loaded, check that rest of the faces are same size & format
  323. if (!face)
  324. SetSize(levelWidth, format);
  325. else
  326. {
  327. if (!object_)
  328. {
  329. LOGERROR("Cube texture face 0 must be loaded first");
  330. return false;
  331. }
  332. if (levelWidth != width_ || format != format_)
  333. {
  334. LOGERROR("Cube texture face does not match size or format of face 0");
  335. return false;
  336. }
  337. }
  338. for (unsigned i = 0; i < levels_; ++i)
  339. {
  340. SetData(face, i, 0, 0, levelWidth, levelHeight, levelData);
  341. memoryUse += levelWidth * levelHeight * components;
  342. if (i < levels_ - 1)
  343. {
  344. image = image->GetNextLevel();
  345. levelData = image->GetData();
  346. levelWidth = image->GetWidth();
  347. levelHeight = image->GetHeight();
  348. }
  349. }
  350. }
  351. else
  352. {
  353. int width = image->GetWidth();
  354. int height = image->GetHeight();
  355. unsigned levels = image->GetNumCompressedLevels();
  356. unsigned format = GetDXTFormat(image->GetCompressedFormat());
  357. if (width != height)
  358. {
  359. LOGERROR("Cube texture width not equal to height");
  360. return false;
  361. }
  362. unsigned mipsToSkip = mipsToSkip_[quality];
  363. if (mipsToSkip >= levels)
  364. mipsToSkip = levels - 1;
  365. while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4))
  366. --mipsToSkip;
  367. width /= (1 << mipsToSkip);
  368. height /= (1 << mipsToSkip);
  369. // Create the texture when face 0 is being loaded, assume rest of the faces are same size & format
  370. if (!face)
  371. {
  372. SetNumLevels(Max((int)(levels - mipsToSkip), 1));
  373. SetSize(width, format);
  374. }
  375. else
  376. {
  377. if (!object_)
  378. {
  379. LOGERROR("Cube texture face 0 must be loaded first");
  380. return false;
  381. }
  382. if (width != width_ || format != format_)
  383. {
  384. LOGERROR("Cube texture face does not match size or format of face 0");
  385. return false;
  386. }
  387. }
  388. for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
  389. {
  390. CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
  391. SetData(face, i, 0, 0, level.width_, level.height_, level.data_);
  392. memoryUse += level.rows_ * level.rowSize_;
  393. }
  394. }
  395. faceMemoryUse_[face] = memoryUse;
  396. unsigned totalMemoryUse = sizeof(TextureCube);
  397. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  398. totalMemoryUse += faceMemoryUse_[i];
  399. SetMemoryUse(totalMemoryUse);
  400. return true;
  401. }
  402. bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
  403. {
  404. if (!object_)
  405. {
  406. LOGERROR("No texture created, can not get data");
  407. return false;
  408. }
  409. if (!dest)
  410. {
  411. LOGERROR("Null destination for getting data");
  412. return false;
  413. }
  414. if (level >= levels_)
  415. {
  416. LOGERROR("Illegal mip level for getting data");
  417. return false;
  418. }
  419. int levelWidth = GetLevelWidth(level);
  420. int levelHeight = GetLevelHeight(level);
  421. D3DLOCKED_RECT d3dLockedRect;
  422. RECT d3dRect;
  423. d3dRect.left = 0;
  424. d3dRect.top = 0;
  425. d3dRect.right = levelWidth;
  426. d3dRect.bottom = levelHeight;
  427. if (FAILED(((IDirect3DCubeTexture9*)object_)->LockRect((D3DCUBEMAP_FACES)face, level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY)))
  428. {
  429. LOGERROR("Could not lock texture");
  430. return false;
  431. }
  432. int height = levelHeight;
  433. if (IsCompressed())
  434. height = (height + 3) >> 2;
  435. unsigned char* destPtr = (unsigned char*)dest;
  436. unsigned rowSize = GetRowDataSize(levelWidth);
  437. // GetRowDataSize() returns CPU-side (destination) data size, so need to convert for X8R8G8B8
  438. if (format_ == D3DFMT_X8R8G8B8)
  439. rowSize = rowSize / 3 * 4;
  440. // Perform conversion to RGB / RGBA as necessary
  441. switch (format_)
  442. {
  443. default:
  444. for (int i = 0; i < height; ++i)
  445. {
  446. unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch;
  447. memcpy(destPtr, src, rowSize);
  448. destPtr += rowSize;
  449. }
  450. break;
  451. case D3DFMT_X8R8G8B8:
  452. for (int i = 0; i < height; ++i)
  453. {
  454. unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch;
  455. for (int j = 0; j < levelWidth; ++j)
  456. {
  457. destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; ++src;
  458. destPtr += 3;
  459. }
  460. }
  461. break;
  462. case D3DFMT_A8R8G8B8:
  463. for (int i = 0; i < height; ++i)
  464. {
  465. unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch;
  466. for (int j = 0; j < levelWidth; ++j)
  467. {
  468. destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; destPtr[3] = *src++;
  469. destPtr += 4;
  470. }
  471. }
  472. break;
  473. }
  474. ((IDirect3DCubeTexture9*)object_)->UnlockRect((D3DCUBEMAP_FACES)face, level);
  475. return true;
  476. }
  477. bool TextureCube::Create()
  478. {
  479. Release();
  480. if (!graphics_)
  481. return false;
  482. if (!width_ || !height_)
  483. return false;
  484. IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
  485. if (!device || FAILED(device->CreateCubeTexture(
  486. width_,
  487. requestedLevels_,
  488. usage_,
  489. (D3DFORMAT)format_,
  490. (D3DPOOL)pool_,
  491. (IDirect3DCubeTexture9**)&object_,
  492. 0)))
  493. {
  494. LOGERROR("Could not create cube texture");
  495. return false;
  496. }
  497. levels_ = ((IDirect3DCubeTexture9*)object_)->GetLevelCount();
  498. if (usage_ & D3DUSAGE_RENDERTARGET)
  499. {
  500. for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
  501. {
  502. ((IDirect3DCubeTexture9*)object_)->GetCubeMapSurface((D3DCUBEMAP_FACES)i, 0,
  503. (IDirect3DSurface9**)&renderSurfaces_[i]->surface_);
  504. }
  505. }
  506. return true;
  507. }