D3D9Texture2D.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. //
  2. // Copyright (c) 2008-2016 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../../Precompiled.h"
  23. #include "../../Core/Context.h"
  24. #include "../../Core/Profiler.h"
  25. #include "../../Graphics/Graphics.h"
  26. #include "../../Graphics/GraphicsEvents.h"
  27. #include "../../Graphics/GraphicsImpl.h"
  28. #include "../../Graphics/Renderer.h"
  29. #include "../../Graphics/Texture2D.h"
  30. #include "../../IO/Log.h"
  31. #include "../../IO/FileSystem.h"
  32. #include "../../Resource/ResourceCache.h"
  33. #include "../../Resource/XMLFile.h"
  34. #include "../../DebugNew.h"
  35. namespace Atomic
  36. {
  37. void Texture2D::OnDeviceLost()
  38. {
  39. if (usage_ > TEXTURE_STATIC)
  40. Release();
  41. }
  42. void Texture2D::OnDeviceReset()
  43. {
  44. if (usage_ > TEXTURE_STATIC || !object_.ptr_ || dataPending_)
  45. {
  46. // If has a resource file, reload through the resource cache. Otherwise just recreate.
  47. ResourceCache* cache = GetSubsystem<ResourceCache>();
  48. if (cache->Exists(GetName()))
  49. dataLost_ = !cache->ReloadResource(this);
  50. if (!object_.ptr_)
  51. {
  52. Create();
  53. dataLost_ = true;
  54. }
  55. }
  56. dataPending_ = false;
  57. }
  58. void Texture2D::Release()
  59. {
  60. if (graphics_)
  61. {
  62. for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
  63. {
  64. if (graphics_->GetTexture(i) == this)
  65. graphics_->SetTexture(i, 0);
  66. }
  67. }
  68. if (renderSurface_)
  69. renderSurface_->Release();
  70. ATOMIC_SAFE_RELEASE(object_.ptr_);
  71. }
  72. bool Texture2D::SetData(unsigned level, int x, int y, int width, int height, const void* data)
  73. {
  74. ATOMIC_PROFILE(SetTextureData);
  75. if (!object_.ptr_)
  76. {
  77. ATOMIC_LOGERROR("No texture created, can not set data");
  78. return false;
  79. }
  80. if (!data)
  81. {
  82. ATOMIC_LOGERROR("Null source for setting data");
  83. return false;
  84. }
  85. if (level >= levels_)
  86. {
  87. ATOMIC_LOGERROR("Illegal mip level for setting data");
  88. return false;
  89. }
  90. if (graphics_->IsDeviceLost())
  91. {
  92. ATOMIC_LOGWARNING("Texture data assignment while device is lost");
  93. dataPending_ = true;
  94. return true;
  95. }
  96. if (IsCompressed())
  97. {
  98. x &= ~3;
  99. y &= ~3;
  100. }
  101. int levelWidth = GetLevelWidth(level);
  102. int levelHeight = GetLevelHeight(level);
  103. if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || width <= 0 || height <= 0)
  104. {
  105. ATOMIC_LOGERROR("Illegal dimensions for setting data");
  106. return false;
  107. }
  108. D3DLOCKED_RECT d3dLockedRect;
  109. RECT d3dRect;
  110. d3dRect.left = x;
  111. d3dRect.top = y;
  112. d3dRect.right = x + width;
  113. d3dRect.bottom = y + height;
  114. DWORD flags = 0;
  115. if (level == 0 && x == 0 && y == 0 && width == levelWidth && height == levelHeight && usage_ > TEXTURE_STATIC)
  116. flags |= D3DLOCK_DISCARD;
  117. HRESULT hr = ((IDirect3DTexture9*)object_.ptr_)->LockRect(level, &d3dLockedRect, (flags & D3DLOCK_DISCARD) ? 0 : &d3dRect, flags);
  118. if (FAILED(hr))
  119. {
  120. ATOMIC_LOGD3DERROR("Could not lock texture", hr);
  121. return false;
  122. }
  123. if (IsCompressed())
  124. {
  125. height = (height + 3) >> 2;
  126. y >>= 2;
  127. }
  128. unsigned char* src = (unsigned char*)data;
  129. unsigned rowSize = GetRowDataSize(width);
  130. // GetRowDataSize() returns CPU-side (source) data size, so need to convert for X8R8G8B8
  131. if (format_ == D3DFMT_X8R8G8B8)
  132. rowSize = rowSize / 3 * 4;
  133. // Perform conversion from RGB / RGBA as necessary
  134. switch (format_)
  135. {
  136. default:
  137. for (int i = 0; i < height; ++i)
  138. {
  139. unsigned char* dest = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch;
  140. memcpy(dest, src, rowSize);
  141. src += rowSize;
  142. }
  143. break;
  144. case D3DFMT_X8R8G8B8:
  145. for (int i = 0; i < height; ++i)
  146. {
  147. unsigned char* dest = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch;
  148. for (int j = 0; j < width; ++j)
  149. {
  150. *dest++ = src[2];
  151. *dest++ = src[1];
  152. *dest++ = src[0];
  153. *dest++ = 255;
  154. src += 3;
  155. }
  156. }
  157. break;
  158. case D3DFMT_A8R8G8B8:
  159. for (int i = 0; i < height; ++i)
  160. {
  161. unsigned char* dest = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch;
  162. for (int j = 0; j < width; ++j)
  163. {
  164. *dest++ = src[2];
  165. *dest++ = src[1];
  166. *dest++ = src[0];
  167. *dest++ = src[3];
  168. src += 4;
  169. }
  170. }
  171. break;
  172. }
  173. ((IDirect3DTexture9*)object_.ptr_)->UnlockRect(level);
  174. return true;
  175. }
  176. bool Texture2D::SetData(Image* image, bool useAlpha)
  177. {
  178. if (!image)
  179. {
  180. ATOMIC_LOGERROR("Null image, can not load texture");
  181. return false;
  182. }
  183. // Use a shared ptr for managing the temporary mip images created during this function
  184. SharedPtr<Image> mipImage;
  185. unsigned memoryUse = sizeof(Texture2D);
  186. int quality = QUALITY_HIGH;
  187. Renderer* renderer = GetSubsystem<Renderer>();
  188. if (renderer)
  189. quality = renderer->GetTextureQuality();
  190. if (!image->IsCompressed())
  191. {
  192. unsigned char* levelData = image->GetData();
  193. int levelWidth = image->GetWidth();
  194. int levelHeight = image->GetHeight();
  195. unsigned components = image->GetComponents();
  196. unsigned format = 0;
  197. // Discard unnecessary mip levels
  198. for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
  199. {
  200. mipImage = image->GetNextLevel(); image = mipImage;
  201. levelData = image->GetData();
  202. levelWidth = image->GetWidth();
  203. levelHeight = image->GetHeight();
  204. }
  205. switch (components)
  206. {
  207. case 1:
  208. format = useAlpha ? Graphics::GetAlphaFormat() : Graphics::GetLuminanceFormat();
  209. break;
  210. case 2:
  211. format = Graphics::GetLuminanceAlphaFormat();
  212. break;
  213. case 3:
  214. format = Graphics::GetRGBFormat();
  215. break;
  216. case 4:
  217. format = Graphics::GetRGBAFormat();
  218. break;
  219. default:
  220. assert(false); // Should never reach here
  221. break;
  222. }
  223. // If image was previously compressed, reset number of requested levels to avoid error if level count is too high for new size
  224. if (IsCompressed() && requestedLevels_ > 1)
  225. requestedLevels_ = 0;
  226. SetSize(levelWidth, levelHeight, format);
  227. for (unsigned i = 0; i < levels_; ++i)
  228. {
  229. SetData(i, 0, 0, levelWidth, levelHeight, levelData);
  230. memoryUse += levelWidth * levelHeight * components;
  231. if (i < levels_ - 1)
  232. {
  233. mipImage = image->GetNextLevel(); image = mipImage;
  234. levelData = image->GetData();
  235. levelWidth = image->GetWidth();
  236. levelHeight = image->GetHeight();
  237. }
  238. }
  239. }
  240. else
  241. {
  242. int width = image->GetWidth();
  243. int height = image->GetHeight();
  244. unsigned levels = image->GetNumCompressedLevels();
  245. unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
  246. bool needDecompress = false;
  247. if (!format)
  248. {
  249. format = Graphics::GetRGBAFormat();
  250. needDecompress = true;
  251. }
  252. unsigned mipsToSkip = mipsToSkip_[quality];
  253. if (mipsToSkip >= levels)
  254. mipsToSkip = levels - 1;
  255. while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4))
  256. --mipsToSkip;
  257. width /= (1 << mipsToSkip);
  258. height /= (1 << mipsToSkip);
  259. SetNumLevels(Max((levels - mipsToSkip), 1U));
  260. SetSize(width, height, format);
  261. for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
  262. {
  263. CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
  264. if (!needDecompress)
  265. {
  266. SetData(i, 0, 0, level.width_, level.height_, level.data_);
  267. memoryUse += level.rows_ * level.rowSize_;
  268. }
  269. else
  270. {
  271. unsigned char* rgbaData = new unsigned char[level.width_ * level.height_ * 4];
  272. level.Decompress(rgbaData);
  273. SetData(i, 0, 0, level.width_, level.height_, rgbaData);
  274. memoryUse += level.width_ * level.height_ * 4;
  275. delete[] rgbaData;
  276. }
  277. }
  278. }
  279. SetMemoryUse(memoryUse);
  280. return true;
  281. }
  282. bool Texture2D::GetData(unsigned level, void* dest) const
  283. {
  284. if (!object_.ptr_)
  285. {
  286. ATOMIC_LOGERROR("No texture created, can not get data");
  287. return false;
  288. }
  289. if (!dest)
  290. {
  291. ATOMIC_LOGERROR("Null destination for getting data");
  292. return false;
  293. }
  294. if (level >= levels_)
  295. {
  296. ATOMIC_LOGERROR("Illegal mip level for getting data");
  297. return false;
  298. }
  299. if (graphics_->IsDeviceLost())
  300. {
  301. ATOMIC_LOGWARNING("Getting texture data while device is lost");
  302. return false;
  303. }
  304. int levelWidth = GetLevelWidth(level);
  305. int levelHeight = GetLevelHeight(level);
  306. D3DLOCKED_RECT d3dLockedRect;
  307. RECT d3dRect;
  308. d3dRect.left = 0;
  309. d3dRect.top = 0;
  310. d3dRect.right = levelWidth;
  311. d3dRect.bottom = levelHeight;
  312. IDirect3DSurface9* offscreenSurface = 0;
  313. // Need to use a offscreen surface & GetRenderTargetData() for rendertargets
  314. if (renderSurface_)
  315. {
  316. if (level != 0)
  317. {
  318. ATOMIC_LOGERROR("Can only get mip level 0 data from a rendertarget");
  319. return false;
  320. }
  321. IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
  322. HRESULT hr = device->CreateOffscreenPlainSurface((UINT)width_, (UINT)height_, (D3DFORMAT)format_,
  323. D3DPOOL_SYSTEMMEM, &offscreenSurface, 0);
  324. if (FAILED(hr))
  325. {
  326. ATOMIC_SAFE_RELEASE(offscreenSurface);
  327. ATOMIC_LOGD3DERROR("Could not create surface for getting rendertarget data", hr);
  328. return false;
  329. }
  330. hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurface_->GetSurface(), offscreenSurface);
  331. if (FAILED(hr))
  332. {
  333. ATOMIC_LOGD3DERROR("Could not get rendertarget data", hr);
  334. offscreenSurface->Release();
  335. return false;
  336. }
  337. hr = offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY);
  338. if (FAILED(hr))
  339. {
  340. ATOMIC_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr);
  341. offscreenSurface->Release();
  342. return false;
  343. }
  344. }
  345. else
  346. {
  347. HRESULT hr = ((IDirect3DTexture9*)object_.ptr_)->LockRect(level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY);
  348. if (FAILED(hr))
  349. {
  350. ATOMIC_LOGD3DERROR("Could not lock texture", hr);
  351. return false;
  352. }
  353. }
  354. int height = levelHeight;
  355. if (IsCompressed())
  356. height = (height + 3) >> 2;
  357. unsigned char* destPtr = (unsigned char*)dest;
  358. unsigned rowSize = GetRowDataSize(levelWidth);
  359. // GetRowDataSize() returns CPU-side (destination) data size, so need to convert for X8R8G8B8
  360. if (format_ == D3DFMT_X8R8G8B8)
  361. rowSize = rowSize / 3 * 4;
  362. // Perform conversion to RGB / RGBA as necessary
  363. switch (format_)
  364. {
  365. default:
  366. for (int i = 0; i < height; ++i)
  367. {
  368. unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch;
  369. memcpy(destPtr, src, rowSize);
  370. destPtr += rowSize;
  371. }
  372. break;
  373. case D3DFMT_X8R8G8B8:
  374. for (int i = 0; i < height; ++i)
  375. {
  376. unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch;
  377. for (int j = 0; j < levelWidth; ++j)
  378. {
  379. destPtr[2] = *src++;
  380. destPtr[1] = *src++;
  381. destPtr[0] = *src++;
  382. ++src;
  383. destPtr += 3;
  384. }
  385. }
  386. break;
  387. case D3DFMT_A8R8G8B8:
  388. for (int i = 0; i < height; ++i)
  389. {
  390. unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch;
  391. for (int j = 0; j < levelWidth; ++j)
  392. {
  393. destPtr[2] = *src++;
  394. destPtr[1] = *src++;
  395. destPtr[0] = *src++;
  396. destPtr[3] = *src++;
  397. destPtr += 4;
  398. }
  399. }
  400. break;
  401. }
  402. if (offscreenSurface)
  403. {
  404. offscreenSurface->UnlockRect();
  405. offscreenSurface->Release();
  406. }
  407. else
  408. ((IDirect3DTexture9*)object_.ptr_)->UnlockRect(level);
  409. return true;
  410. }
  411. bool Texture2D::Create()
  412. {
  413. Release();
  414. if (!graphics_ || !width_ || !height_)
  415. return false;
  416. if (graphics_->IsDeviceLost())
  417. {
  418. ATOMIC_LOGWARNING("Texture creation while device is lost");
  419. return true;
  420. }
  421. unsigned pool = usage_ > TEXTURE_STATIC ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
  422. unsigned d3dUsage = 0;
  423. switch (usage_)
  424. {
  425. case TEXTURE_DYNAMIC:
  426. d3dUsage |= D3DUSAGE_DYNAMIC;
  427. break;
  428. case TEXTURE_RENDERTARGET:
  429. d3dUsage |= D3DUSAGE_RENDERTARGET;
  430. break;
  431. case TEXTURE_DEPTHSTENCIL:
  432. d3dUsage |= D3DUSAGE_DEPTHSTENCIL;
  433. break;
  434. default:
  435. break;
  436. }
  437. IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
  438. // If creating a depth-stencil texture, and it is not supported, create a depth-stencil surface instead
  439. if (usage_ == TEXTURE_DEPTHSTENCIL && !graphics_->GetImpl()->CheckFormatSupport((D3DFORMAT)format_, d3dUsage, D3DRTYPE_TEXTURE))
  440. {
  441. HRESULT hr = device->CreateDepthStencilSurface(
  442. (UINT)width_,
  443. (UINT)height_,
  444. (D3DFORMAT)format_,
  445. D3DMULTISAMPLE_NONE,
  446. 0,
  447. FALSE,
  448. (IDirect3DSurface9**)&renderSurface_->surface_,
  449. 0);
  450. if (FAILED(hr))
  451. {
  452. ATOMIC_SAFE_RELEASE(renderSurface_->surface_);
  453. ATOMIC_LOGD3DERROR("Could not create depth-stencil surface", hr);
  454. return false;
  455. }
  456. levels_ = 1;
  457. }
  458. else
  459. {
  460. HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture(
  461. (UINT)width_,
  462. (UINT)height_,
  463. requestedLevels_,
  464. d3dUsage,
  465. (D3DFORMAT)format_,
  466. (D3DPOOL)pool,
  467. (IDirect3DTexture9**)&object_,
  468. 0);
  469. if (FAILED(hr))
  470. {
  471. ATOMIC_SAFE_RELEASE(object_.ptr_);
  472. ATOMIC_LOGD3DERROR("Could not create texture", hr);
  473. return false;
  474. }
  475. levels_ = ((IDirect3DTexture9*)object_.ptr_)->GetLevelCount();
  476. if (usage_ >= TEXTURE_RENDERTARGET)
  477. {
  478. hr = ((IDirect3DTexture9*)object_.ptr_)->GetSurfaceLevel(0, (IDirect3DSurface9**)&renderSurface_->surface_);
  479. if (FAILED(hr))
  480. ATOMIC_LOGD3DERROR("Could not get rendertarget surface", hr);
  481. }
  482. }
  483. return true;
  484. }
  485. }