OGLTexture2D.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 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 "Graphics.h"
  26. #include "GraphicsImpl.h"
  27. #include "Log.h"
  28. #include "Profiler.h"
  29. #include "Renderer.h"
  30. #include "ResourceCache.h"
  31. #include "Texture2D.h"
  32. #include "DebugNew.h"
  33. OBJECTTYPESTATIC(Texture2D);
  34. Texture2D::Texture2D(Context* context) :
  35. Texture(context),
  36. followWindowSize_(false)
  37. {
  38. target_ = GL_TEXTURE_2D;
  39. }
  40. Texture2D::~Texture2D()
  41. {
  42. Release();
  43. }
  44. void Texture2D::RegisterObject(Context* context)
  45. {
  46. context->RegisterFactory<Texture2D>();
  47. }
  48. bool Texture2D::Load(Deserializer& source)
  49. {
  50. PROFILE(LoadTexture2D);
  51. // In headless mode, do not actually load the texture, just return success
  52. Graphics* graphics = GetSubsystem<Graphics>();
  53. if (!graphics)
  54. return true;
  55. // If over the texture budget, see if materials can be freed to allow textures to be freed
  56. CheckTextureBudget(GetTypeStatic());
  57. SharedPtr<Image> image(new Image(context_));
  58. if (!image->Load(source))
  59. return false;
  60. // Before actually loading the texture, get optional parameters from an XML description file
  61. LoadParameters();
  62. return Load(image);
  63. }
  64. void Texture2D::OnDeviceLost()
  65. {
  66. savedLevels_.Clear();
  67. // Check if save data is supported, in that case save data of each mip level
  68. if (GetDataSize(width_, height_))
  69. {
  70. for (unsigned i = 0; i < levels_; ++i)
  71. {
  72. int levelWidth = GetLevelWidth(i);
  73. int levelHeight = GetLevelHeight(i);
  74. SharedArrayPtr<unsigned char> savedLevel(new unsigned char[GetDataSize(levelWidth, levelHeight)]);
  75. GetData(i, savedLevel.Get());
  76. savedLevels_.Push(savedLevel);
  77. }
  78. }
  79. Release();
  80. }
  81. void Texture2D::OnDeviceReset()
  82. {
  83. if (followWindowSize_)
  84. Create();
  85. else if (!object_)
  86. {
  87. Create();
  88. // Restore texture from save data if it exists
  89. if (savedLevels_.Size())
  90. {
  91. for (unsigned i = 0; i < savedLevels_.Size(); ++i)
  92. {
  93. int levelWidth = GetLevelWidth(i);
  94. int levelHeight = GetLevelHeight(i);
  95. SetData(i, 0, 0, levelWidth, levelHeight, savedLevels_[i].Get());
  96. }
  97. savedLevels_.Clear();
  98. }
  99. }
  100. }
  101. void Texture2D::Release()
  102. {
  103. if (object_)
  104. {
  105. if (!graphics_)
  106. return;
  107. for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
  108. {
  109. if (graphics_->GetTexture(i) == this)
  110. graphics_->SetTexture(i, 0);
  111. }
  112. if (renderSurface_)
  113. renderSurface_->Release();
  114. glDeleteTextures(1, &object_);
  115. object_ = 0;
  116. }
  117. else
  118. {
  119. if (renderSurface_)
  120. renderSurface_->Release();
  121. }
  122. }
  123. bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usage)
  124. {
  125. // Delete the old rendersurface if any
  126. renderSurface_.Reset();
  127. if (usage >= TEXTURE_RENDERTARGET)
  128. {
  129. renderSurface_ = new RenderSurface(this, GL_TEXTURE_2D);
  130. dynamic_ = true;
  131. // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
  132. addressMode_[COORD_U] = ADDRESS_CLAMP;
  133. addressMode_[COORD_V] = ADDRESS_CLAMP;
  134. filterMode_ = FILTER_NEAREST;
  135. requestedLevels_ = 1;
  136. }
  137. else if (usage == TEXTURE_DYNAMIC)
  138. dynamic_ = true;
  139. else
  140. dynamic_ = false;
  141. if (width <= 0 || height <= 0)
  142. followWindowSize_ = true;
  143. else
  144. {
  145. width_ = width;
  146. height_ = height;
  147. followWindowSize_ = false;
  148. }
  149. format_ = format;
  150. return Create();
  151. }
  152. bool Texture2D::SetData(unsigned level, int x, int y, int width, int height, const void* data)
  153. {
  154. if (!object_ || !graphics_)
  155. {
  156. LOGERROR("No texture created, can not set data");
  157. return false;
  158. }
  159. if (!data)
  160. {
  161. LOGERROR("Null source for setting data");
  162. return false;
  163. }
  164. if (level >= levels_)
  165. {
  166. LOGERROR("Illegal mip level for setting data");
  167. return false;
  168. }
  169. bool compressed = format_ == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || format_ == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
  170. format_ == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  171. if (compressed)
  172. {
  173. x &= ~3;
  174. y &= ~3;
  175. }
  176. int levelWidth = GetLevelWidth(level);
  177. int levelHeight = GetLevelHeight(level);
  178. if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || width <= 0 || height <= 0)
  179. {
  180. LOGERROR("Illegal dimensions for setting data");
  181. return false;
  182. }
  183. bool wholeLevel = x == 0 && y == 0 && width == levelWidth && height == levelHeight;
  184. graphics_->SetTextureForUpdate(this);
  185. if (!compressed)
  186. {
  187. if (wholeLevel)
  188. glTexImage2D(target_, level, format_, width, height, 0, GetExternalFormat(format_), GetDataType(format_), data);
  189. else
  190. glTexSubImage2D(target_, level, x, y, width, height, GetExternalFormat(format_), GetDataType(format_), data);
  191. }
  192. else
  193. {
  194. if (wholeLevel)
  195. glCompressedTexImage2D(target_, level, format_, width, height, 0, GetDataSize(width, height), data);
  196. else
  197. glCompressedTexSubImage2D(target_, level, x, y, width, height, format_, GetDataSize(width, height), data);
  198. }
  199. graphics_->SetTexture(0, 0);
  200. return true;
  201. }
  202. bool Texture2D::Load(SharedPtr<Image> image, bool useAlpha)
  203. {
  204. if (!image)
  205. {
  206. LOGERROR("Null image, can not load texture");
  207. return false;
  208. }
  209. unsigned memoryUse = 0;
  210. int quality = QUALITY_HIGH;
  211. Renderer* renderer = GetSubsystem<Renderer>();
  212. if (renderer)
  213. quality = renderer->GetTextureQuality();
  214. if (!image->IsCompressed())
  215. {
  216. unsigned char* levelData = image->GetData();
  217. int levelWidth = image->GetWidth();
  218. int levelHeight = image->GetHeight();
  219. unsigned components = image->GetComponents();
  220. unsigned format = 0;
  221. // Discard unnecessary mip levels
  222. for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
  223. {
  224. image = image->GetNextLevel();
  225. levelData = image->GetData();
  226. levelWidth = image->GetWidth();
  227. levelHeight = image->GetHeight();
  228. }
  229. switch (components)
  230. {
  231. case 1:
  232. format = useAlpha ? Graphics::GetAlphaFormat() : Graphics::GetLuminanceFormat();
  233. break;
  234. case 2:
  235. format = Graphics::GetLuminanceAlphaFormat();
  236. break;
  237. case 3:
  238. format = Graphics::GetRGBFormat();
  239. break;
  240. case 4:
  241. format = Graphics::GetRGBAFormat();
  242. break;
  243. }
  244. SetSize(levelWidth, levelHeight, format);
  245. if (!object_)
  246. return false;
  247. for (unsigned i = 0; i < levels_; ++i)
  248. {
  249. SetData(i, 0, 0, levelWidth, levelHeight, levelData);
  250. memoryUse += levelWidth * levelHeight * components;
  251. if (i < levels_ - 1)
  252. {
  253. image = image->GetNextLevel();
  254. levelData = image->GetData();
  255. levelWidth = image->GetWidth();
  256. levelHeight = image->GetHeight();
  257. }
  258. }
  259. }
  260. else
  261. {
  262. int width = image->GetWidth();
  263. int height = image->GetHeight();
  264. unsigned levels = image->GetNumCompressedLevels();
  265. unsigned format = GetDXTFormat(image->GetCompressedFormat());
  266. unsigned mipsToSkip = mipsToSkip_[quality];
  267. if (mipsToSkip >= levels)
  268. mipsToSkip = levels - 1;
  269. while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4))
  270. --mipsToSkip;
  271. width /= (1 << mipsToSkip);
  272. height /= (1 << mipsToSkip);
  273. SetNumLevels(Max((int)(levels - mipsToSkip), 1));
  274. SetSize(width, height, format);
  275. for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
  276. {
  277. CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
  278. SetData(i, 0, 0, level.width_, level.height_, level.data_);
  279. memoryUse += level.rows_ * level.rowSize_;
  280. }
  281. }
  282. SetMemoryUse(memoryUse);
  283. return true;
  284. }
  285. bool Texture2D::GetData(unsigned level, void* dest) const
  286. {
  287. if (!object_ || !graphics_)
  288. {
  289. LOGERROR("No texture created, can not get data");
  290. return false;
  291. }
  292. if (!dest)
  293. {
  294. LOGERROR("Null destination for getting data");
  295. return false;
  296. }
  297. if (level >= levels_)
  298. {
  299. LOGERROR("Illegal mip level for getting data");
  300. return false;
  301. }
  302. bool compressed = format_ == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || format_ == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
  303. format_ == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  304. graphics_->SetTextureForUpdate(const_cast<Texture2D*>(this));
  305. if (!compressed)
  306. glGetTexImage(target_, level, GetExternalFormat(format_), GetDataType(format_), dest);
  307. else
  308. glGetCompressedTexImage(target_, level, dest);
  309. graphics_->SetTexture(0, 0);
  310. return true;
  311. }
  312. bool Texture2D::Create()
  313. {
  314. Release();
  315. if (!graphics_)
  316. return false;
  317. if (followWindowSize_)
  318. {
  319. width_ = graphics_->GetWidth();
  320. height_ = graphics_->GetHeight();
  321. }
  322. if (!width_ || !height_)
  323. return false;
  324. // If we create a depth stencil texture with packed format, create a renderbuffer instead of an actual texture
  325. // This is because packed depth textures have performance problems with ATI display adapters
  326. if (format_ == Graphics::GetDepthStencilFormat())
  327. {
  328. if (renderSurface_)
  329. {
  330. renderSurface_->CreateRenderBuffer(width_, height_, format_);
  331. return true;
  332. }
  333. else
  334. return false;
  335. }
  336. glGenTextures(1, &object_);
  337. // Ensure that our texture is bound to OpenGL texture unit 0
  338. graphics_->SetTextureForUpdate(this);
  339. // If not compressed, create the initial level 0 texture with null data
  340. unsigned externalFormat = GetExternalFormat(format_);
  341. unsigned dataType = GetDataType(format_);
  342. if (format_ != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && format_ != GL_COMPRESSED_RGBA_S3TC_DXT3_EXT &&
  343. format_ != GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
  344. glTexImage2D(target_, 0, format_, width_, height_, 0, externalFormat, dataType, 0);
  345. // If depth format, get the depth size
  346. if (externalFormat == GL_DEPTH_COMPONENT)
  347. glGetTexLevelParameteriv(target_, 0, GL_TEXTURE_DEPTH_SIZE, &depthBits_);
  348. // Set mipmapping
  349. levels_ = requestedLevels_;
  350. if (!levels_)
  351. {
  352. unsigned maxSize = Max((int)width_, (int)height_);
  353. while (maxSize)
  354. {
  355. maxSize >>= 1;
  356. ++levels_;
  357. }
  358. }
  359. glTexParameteri(target_, GL_TEXTURE_BASE_LEVEL, 0);
  360. glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, levels_ - 1);
  361. // Set initial parameters, then unbind the texture
  362. UpdateParameters();
  363. graphics_->SetTexture(0, 0);
  364. return true;
  365. }