OGLTexture2D.cpp 13 KB

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