OGLTexture2D.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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. if (!graphics_->GetRenderTargetSupport())
  130. return false;
  131. renderSurface_ = new RenderSurface(this, GL_TEXTURE_2D);
  132. dynamic_ = true;
  133. // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
  134. addressMode_[COORD_U] = ADDRESS_CLAMP;
  135. addressMode_[COORD_V] = ADDRESS_CLAMP;
  136. filterMode_ = FILTER_NEAREST;
  137. requestedLevels_ = 1;
  138. }
  139. else if (usage == TEXTURE_DYNAMIC)
  140. dynamic_ = true;
  141. else
  142. dynamic_ = false;
  143. if (width <= 0 || height <= 0)
  144. followWindowSize_ = true;
  145. else
  146. {
  147. width_ = width;
  148. height_ = height;
  149. followWindowSize_ = false;
  150. }
  151. format_ = format;
  152. return Create();
  153. }
  154. bool Texture2D::SetData(unsigned level, int x, int y, int width, int height, const void* data)
  155. {
  156. if (!object_ || !graphics_)
  157. {
  158. LOGERROR("No texture created, can not set data");
  159. return false;
  160. }
  161. if (!data)
  162. {
  163. LOGERROR("Null source for setting data");
  164. return false;
  165. }
  166. if (level >= levels_)
  167. {
  168. LOGERROR("Illegal mip level for setting data");
  169. return false;
  170. }
  171. bool compressed = format_ == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || format_ == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
  172. format_ == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  173. if (compressed)
  174. {
  175. x &= ~3;
  176. y &= ~3;
  177. }
  178. int levelWidth = GetLevelWidth(level);
  179. int levelHeight = GetLevelHeight(level);
  180. if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || width <= 0 || height <= 0)
  181. {
  182. LOGERROR("Illegal dimensions for setting data");
  183. return false;
  184. }
  185. bool wholeLevel = x == 0 && y == 0 && width == levelWidth && height == levelHeight;
  186. graphics_->SetTextureForUpdate(this);
  187. if (!compressed)
  188. {
  189. if (wholeLevel)
  190. glTexImage2D(target_, level, format_, width, height, 0, GetExternalFormat(format_), GetDataType(format_), data);
  191. else
  192. glTexSubImage2D(target_, level, x, y, width, height, GetExternalFormat(format_), GetDataType(format_), data);
  193. }
  194. else
  195. {
  196. if (wholeLevel)
  197. glCompressedTexImage2D(target_, level, format_, width, height, 0, GetDataSize(width, height), data);
  198. else
  199. glCompressedTexSubImage2D(target_, level, x, y, width, height, format_, GetDataSize(width, height), data);
  200. }
  201. graphics_->SetTexture(0, 0);
  202. return true;
  203. }
  204. bool Texture2D::Load(SharedPtr<Image> image, bool useAlpha)
  205. {
  206. if (!image)
  207. {
  208. LOGERROR("Null image, can not load texture");
  209. return false;
  210. }
  211. unsigned memoryUse = 0;
  212. int quality = QUALITY_HIGH;
  213. Renderer* renderer = GetSubsystem<Renderer>();
  214. if (renderer)
  215. quality = renderer->GetTextureQuality();
  216. if (!image->IsCompressed())
  217. {
  218. unsigned char* levelData = image->GetData();
  219. int levelWidth = image->GetWidth();
  220. int levelHeight = image->GetHeight();
  221. unsigned components = image->GetComponents();
  222. unsigned format = 0;
  223. // Discard unnecessary mip levels
  224. for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
  225. {
  226. image = image->GetNextLevel();
  227. levelData = image->GetData();
  228. levelWidth = image->GetWidth();
  229. levelHeight = image->GetHeight();
  230. }
  231. switch (components)
  232. {
  233. case 1:
  234. format = useAlpha ? Graphics::GetAlphaFormat() : Graphics::GetLuminanceFormat();
  235. break;
  236. case 2:
  237. format = Graphics::GetLuminanceAlphaFormat();
  238. break;
  239. case 3:
  240. format = Graphics::GetRGBFormat();
  241. break;
  242. case 4:
  243. format = Graphics::GetRGBAFormat();
  244. break;
  245. }
  246. SetSize(levelWidth, levelHeight, format);
  247. if (!object_)
  248. return false;
  249. for (unsigned i = 0; i < levels_; ++i)
  250. {
  251. SetData(i, 0, 0, levelWidth, levelHeight, levelData);
  252. memoryUse += levelWidth * levelHeight * components;
  253. if (i < levels_ - 1)
  254. {
  255. image = image->GetNextLevel();
  256. levelData = image->GetData();
  257. levelWidth = image->GetWidth();
  258. levelHeight = image->GetHeight();
  259. }
  260. }
  261. }
  262. else
  263. {
  264. int width = image->GetWidth();
  265. int height = image->GetHeight();
  266. unsigned levels = image->GetNumCompressedLevels();
  267. unsigned format = GetDXTFormat(image->GetCompressedFormat());
  268. unsigned mipsToSkip = mipsToSkip_[quality];
  269. if (mipsToSkip >= levels)
  270. mipsToSkip = levels - 1;
  271. while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4))
  272. --mipsToSkip;
  273. width /= (1 << mipsToSkip);
  274. height /= (1 << mipsToSkip);
  275. SetNumLevels(Max((int)(levels - mipsToSkip), 1));
  276. SetSize(width, height, format);
  277. for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
  278. {
  279. CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
  280. SetData(i, 0, 0, level.width_, level.height_, level.data_);
  281. memoryUse += level.rows_ * level.rowSize_;
  282. }
  283. }
  284. SetMemoryUse(memoryUse);
  285. return true;
  286. }
  287. bool Texture2D::GetData(unsigned level, void* dest) const
  288. {
  289. if (!object_ || !graphics_)
  290. {
  291. LOGERROR("No texture created, can not get data");
  292. return false;
  293. }
  294. if (!dest)
  295. {
  296. LOGERROR("Null destination for getting data");
  297. return false;
  298. }
  299. if (level >= levels_)
  300. {
  301. LOGERROR("Illegal mip level for getting data");
  302. return false;
  303. }
  304. bool compressed = format_ == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || format_ == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
  305. format_ == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  306. graphics_->SetTextureForUpdate(const_cast<Texture2D*>(this));
  307. if (!compressed)
  308. glGetTexImage(target_, level, GetExternalFormat(format_), GetDataType(format_), dest);
  309. else
  310. glGetCompressedTexImage(target_, level, dest);
  311. graphics_->SetTexture(0, 0);
  312. return true;
  313. }
  314. bool Texture2D::Create()
  315. {
  316. Release();
  317. if (!graphics_)
  318. return false;
  319. if (followWindowSize_)
  320. {
  321. width_ = graphics_->GetWidth();
  322. height_ = graphics_->GetHeight();
  323. }
  324. if (!width_ || !height_)
  325. return false;
  326. // If we create a depth stencil texture with packed format, create a renderbuffer instead of an actual texture
  327. // This is because packed depth textures have performance problems with ATI display adapters
  328. if (format_ == Graphics::GetDepthStencilFormat())
  329. {
  330. if (renderSurface_)
  331. {
  332. renderSurface_->CreateRenderBuffer(width_, height_, format_);
  333. return true;
  334. }
  335. else
  336. return false;
  337. }
  338. glGenTextures(1, &object_);
  339. // Ensure that our texture is bound to OpenGL texture unit 0
  340. graphics_->SetTextureForUpdate(this);
  341. // If not compressed, create the initial level 0 texture with null data
  342. unsigned externalFormat = GetExternalFormat(format_);
  343. unsigned dataType = GetDataType(format_);
  344. if (format_ != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && format_ != GL_COMPRESSED_RGBA_S3TC_DXT3_EXT &&
  345. format_ != GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
  346. glTexImage2D(target_, 0, format_, width_, height_, 0, externalFormat, dataType, 0);
  347. // If depth format, get the depth size
  348. if (externalFormat == GL_DEPTH_COMPONENT)
  349. glGetTexLevelParameteriv(target_, 0, GL_TEXTURE_DEPTH_SIZE, &depthBits_);
  350. // Set mipmapping
  351. levels_ = requestedLevels_;
  352. if (!levels_)
  353. {
  354. unsigned maxSize = Max((int)width_, (int)height_);
  355. while (maxSize)
  356. {
  357. maxSize >>= 1;
  358. ++levels_;
  359. }
  360. }
  361. glTexParameteri(target_, GL_TEXTURE_BASE_LEVEL, 0);
  362. glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, levels_ - 1);
  363. // Set initial parameters, then unbind the texture
  364. UpdateParameters();
  365. graphics_->SetTexture(0, 0);
  366. return true;
  367. }