Texture.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. /**
  2. * Copyright (c) 2006-2018 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. // LOVE
  21. #include "common/config.h"
  22. #include "Texture.h"
  23. #include "Graphics.h"
  24. // C
  25. #include <cmath>
  26. #include <algorithm>
  27. namespace love
  28. {
  29. namespace graphics
  30. {
  31. love::Type Texture::type("Texture", &Drawable::type);
  32. Texture::Filter Texture::defaultFilter;
  33. Texture::FilterMode Texture::defaultMipmapFilter = Texture::FILTER_LINEAR;
  34. float Texture::defaultMipmapSharpness = 0.0f;
  35. int64 Texture::totalGraphicsMemory = 0;
  36. Texture::Texture(TextureType texType)
  37. : texType(texType)
  38. , format(PIXELFORMAT_UNKNOWN)
  39. , readable(true)
  40. , width(0)
  41. , height(0)
  42. , depth(1)
  43. , layers(1)
  44. , mipmapCount(1)
  45. , pixelWidth(0)
  46. , pixelHeight(0)
  47. , filter(defaultFilter)
  48. , wrap()
  49. , mipmapSharpness(defaultMipmapSharpness)
  50. , graphicsMemorySize(0)
  51. {
  52. }
  53. Texture::~Texture()
  54. {
  55. setGraphicsMemorySize(0);
  56. }
  57. void Texture::initQuad()
  58. {
  59. Quad::Viewport v = {0, 0, (double) width, (double) height};
  60. quad.set(new Quad(v, width, height), Acquire::NORETAIN);
  61. }
  62. void Texture::setGraphicsMemorySize(int64 bytes)
  63. {
  64. totalGraphicsMemory = std::max(totalGraphicsMemory - graphicsMemorySize, (int64) 0);
  65. bytes = std::max(bytes, (int64) 0);
  66. graphicsMemorySize = bytes;
  67. totalGraphicsMemory += bytes;
  68. }
  69. TextureType Texture::getTextureType() const
  70. {
  71. return texType;
  72. }
  73. PixelFormat Texture::getPixelFormat() const
  74. {
  75. return format;
  76. }
  77. bool Texture::isReadable() const
  78. {
  79. return readable;
  80. }
  81. bool Texture::isValidSlice(int slice) const
  82. {
  83. if (slice < 0)
  84. return false;
  85. if (texType == TEXTURE_CUBE)
  86. return slice < 6;
  87. else if (texType == TEXTURE_VOLUME)
  88. return slice < depth;
  89. else if (texType == TEXTURE_2D_ARRAY)
  90. return slice < layers;
  91. else if (slice > 0)
  92. return false;
  93. return true;
  94. }
  95. void Texture::draw(Graphics *gfx, const Matrix4 &m)
  96. {
  97. draw(gfx, quad, m);
  98. }
  99. void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
  100. {
  101. using namespace vertex;
  102. if (!readable)
  103. throw love::Exception("Textures with non-readable formats cannot be drawn.");
  104. if (texType == TEXTURE_2D_ARRAY)
  105. {
  106. drawLayer(gfx, q->getLayer(), q, localTransform);
  107. return;
  108. }
  109. const Matrix4 &tm = gfx->getTransform();
  110. bool is2D = tm.isAffine2DTransform();
  111. Graphics::StreamDrawCommand cmd;
  112. cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
  113. cmd.formats[1] = CommonFormat::STf_RGBAub;
  114. cmd.indexMode = TriangleIndexMode::QUADS;
  115. cmd.vertexCount = 4;
  116. cmd.texture = this;
  117. Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
  118. Matrix4 t(tm, localTransform);
  119. if (is2D)
  120. t.transformXY((Vector2 *) data.stream[0], q->getVertexPositions(), 4);
  121. else
  122. t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
  123. const Vector2 *texcoords = q->getVertexTexCoords();
  124. vertex::STf_RGBAub *vertexdata = (vertex::STf_RGBAub *) data.stream[1];
  125. Color c = toColor(gfx->getColor());
  126. for (int i = 0; i < 4; i++)
  127. {
  128. vertexdata[i].s = texcoords[i].x;
  129. vertexdata[i].t = texcoords[i].y;
  130. vertexdata[i].color = c;
  131. }
  132. }
  133. void Texture::drawLayer(Graphics *gfx, int layer, const Matrix4 &m)
  134. {
  135. drawLayer(gfx, layer, quad, m);
  136. }
  137. void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
  138. {
  139. using namespace vertex;
  140. if (!readable)
  141. throw love::Exception("Textures with non-readable formats cannot be drawn.");
  142. if (texType != TEXTURE_2D_ARRAY)
  143. throw love::Exception("drawLayer can only be used with Array Textures!");
  144. if (layer < 0 || layer >= layers)
  145. throw love::Exception("Invalid layer: %d (Texture has %d layers)", layer + 1, layers);
  146. Color c = toColor(gfx->getColor());
  147. const Matrix4 &tm = gfx->getTransform();
  148. bool is2D = tm.isAffine2DTransform();
  149. Matrix4 t(tm, m);
  150. Graphics::StreamDrawCommand cmd;
  151. cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
  152. cmd.formats[1] = CommonFormat::STPf_RGBAub;
  153. cmd.indexMode = TriangleIndexMode::QUADS;
  154. cmd.vertexCount = 4;
  155. cmd.texture = this;
  156. cmd.standardShaderType = Shader::STANDARD_ARRAY;
  157. Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
  158. if (is2D)
  159. t.transformXY((Vector2 *) data.stream[0], q->getVertexPositions(), 4);
  160. else
  161. t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
  162. const Vector2 *texcoords = q->getVertexTexCoords();
  163. vertex::STPf_RGBAub *vertexdata = (vertex::STPf_RGBAub *) data.stream[1];
  164. for (int i = 0; i < 4; i++)
  165. {
  166. vertexdata[i].s = texcoords[i].x;
  167. vertexdata[i].t = texcoords[i].y;
  168. vertexdata[i].p = (float) layer;
  169. vertexdata[i].color = c;
  170. }
  171. }
  172. int Texture::getWidth(int mip) const
  173. {
  174. return std::max(width >> mip, 1);
  175. }
  176. int Texture::getHeight(int mip) const
  177. {
  178. return std::max(height >> mip, 1);
  179. }
  180. int Texture::getDepth(int mip) const
  181. {
  182. return std::max(depth >> mip, 1);
  183. }
  184. int Texture::getLayerCount() const
  185. {
  186. return layers;
  187. }
  188. int Texture::getMipmapCount() const
  189. {
  190. return mipmapCount;
  191. }
  192. int Texture::getPixelWidth(int mip) const
  193. {
  194. return std::max(pixelWidth >> mip, 1);
  195. }
  196. int Texture::getPixelHeight(int mip) const
  197. {
  198. return std::max(pixelHeight >> mip, 1);
  199. }
  200. float Texture::getDPIScale() const
  201. {
  202. return (float) pixelHeight / (float) height;
  203. }
  204. void Texture::setFilter(const Filter &f)
  205. {
  206. if (!validateFilter(f, getMipmapCount() > 1))
  207. {
  208. if (f.mipmap != FILTER_NONE && getMipmapCount() == 1)
  209. throw love::Exception("Non-mipmapped texture cannot have mipmap filtering.");
  210. else
  211. throw love::Exception("Invalid texture filter.");
  212. }
  213. Graphics::flushStreamDrawsGlobal();
  214. filter = f;
  215. }
  216. const Texture::Filter &Texture::getFilter() const
  217. {
  218. return filter;
  219. }
  220. const Texture::Wrap &Texture::getWrap() const
  221. {
  222. return wrap;
  223. }
  224. float Texture::getMipmapSharpness() const
  225. {
  226. return mipmapSharpness;
  227. }
  228. void Texture::setDepthSampleMode(Optional<CompareMode> mode)
  229. {
  230. if (mode.hasValue && (!readable || !isPixelFormatDepthStencil(format)))
  231. throw love::Exception("Only readable depth textures can have a depth sample compare mode.");
  232. }
  233. Optional<CompareMode> Texture::getDepthSampleMode() const
  234. {
  235. return depthCompareMode;
  236. }
  237. Quad *Texture::getQuad() const
  238. {
  239. return quad;
  240. }
  241. bool Texture::validateFilter(const Filter &f, bool mipmapsAllowed)
  242. {
  243. if (!mipmapsAllowed && f.mipmap != FILTER_NONE)
  244. return false;
  245. if (f.mag != FILTER_LINEAR && f.mag != FILTER_NEAREST)
  246. return false;
  247. if (f.min != FILTER_LINEAR && f.min != FILTER_NEAREST)
  248. return false;
  249. if (f.mipmap != FILTER_LINEAR && f.mipmap != FILTER_NEAREST && f.mipmap != FILTER_NONE)
  250. return false;
  251. return true;
  252. }
  253. int Texture::getTotalMipmapCount(int w, int h)
  254. {
  255. return (int) log2(std::max(w, h)) + 1;
  256. }
  257. int Texture::getTotalMipmapCount(int w, int h, int d)
  258. {
  259. return (int) log2(std::max(std::max(w, h), d)) + 1;
  260. }
  261. bool Texture::validateDimensions(bool throwException) const
  262. {
  263. bool success = true;
  264. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  265. if (gfx == nullptr)
  266. return false;
  267. const Graphics::Capabilities &caps = gfx->getCapabilities();
  268. int max2Dsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE];
  269. int max3Dsize = (int) caps.limits[Graphics::LIMIT_VOLUME_TEXTURE_SIZE];
  270. int maxcubesize = (int) caps.limits[Graphics::LIMIT_CUBE_TEXTURE_SIZE];
  271. int maxlayers = (int) caps.limits[Graphics::LIMIT_TEXTURE_LAYERS];
  272. int largestdim = 0;
  273. const char *largestname = nullptr;
  274. if ((texType == TEXTURE_2D || texType == TEXTURE_2D_ARRAY) && (pixelWidth > max2Dsize || pixelHeight > max2Dsize))
  275. {
  276. success = false;
  277. largestdim = std::max(pixelWidth, pixelHeight);
  278. largestname = pixelWidth > pixelHeight ? "pixel width" : "pixel height";
  279. }
  280. else if (texType == TEXTURE_2D_ARRAY && layers > maxlayers)
  281. {
  282. success = false;
  283. largestdim = layers;
  284. largestname = "array layer count";
  285. }
  286. else if (texType == TEXTURE_CUBE && (pixelWidth > maxcubesize || pixelWidth != pixelHeight))
  287. {
  288. success = false;
  289. largestdim = std::max(pixelWidth, pixelHeight);
  290. largestname = pixelWidth > pixelHeight ? "pixel width" : "pixel height";
  291. if (throwException && pixelWidth != pixelHeight)
  292. throw love::Exception("Cubemap textures must have equal width and height.");
  293. }
  294. else if (texType == TEXTURE_VOLUME && (pixelWidth > max3Dsize || pixelHeight > max3Dsize || depth > max3Dsize))
  295. {
  296. success = false;
  297. largestdim = std::max(std::max(pixelWidth, pixelHeight), depth);
  298. if (largestdim == pixelWidth)
  299. largestname = "pixel width";
  300. else if (largestdim == pixelHeight)
  301. largestname = "pixel height";
  302. else
  303. largestname = "pixel depth";
  304. }
  305. if (throwException && largestname != nullptr)
  306. throw love::Exception("Cannot create texture: %s of %d is too large for this system.", largestname, largestdim);
  307. return success;
  308. }
  309. bool Texture::getConstant(const char *in, TextureType &out)
  310. {
  311. return texTypes.find(in, out);
  312. }
  313. bool Texture::getConstant(TextureType in, const char *&out)
  314. {
  315. return texTypes.find(in, out);
  316. }
  317. std::vector<std::string> Texture::getConstants(TextureType)
  318. {
  319. return texTypes.getNames();
  320. }
  321. bool Texture::getConstant(const char *in, FilterMode &out)
  322. {
  323. return filterModes.find(in, out);
  324. }
  325. bool Texture::getConstant(FilterMode in, const char *&out)
  326. {
  327. return filterModes.find(in, out);
  328. }
  329. std::vector<std::string> Texture::getConstants(FilterMode)
  330. {
  331. return filterModes.getNames();
  332. }
  333. bool Texture::getConstant(const char *in, WrapMode &out)
  334. {
  335. return wrapModes.find(in, out);
  336. }
  337. bool Texture::getConstant(WrapMode in, const char *&out)
  338. {
  339. return wrapModes.find(in, out);
  340. }
  341. std::vector<std::string> Texture::getConstants(WrapMode)
  342. {
  343. return wrapModes.getNames();
  344. }
  345. StringMap<TextureType, TEXTURE_MAX_ENUM>::Entry Texture::texTypeEntries[] =
  346. {
  347. { "2d", TEXTURE_2D },
  348. { "volume", TEXTURE_VOLUME },
  349. { "array", TEXTURE_2D_ARRAY },
  350. { "cube", TEXTURE_CUBE },
  351. };
  352. StringMap<TextureType, TEXTURE_MAX_ENUM> Texture::texTypes(Texture::texTypeEntries, sizeof(Texture::texTypeEntries));
  353. StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
  354. {
  355. { "linear", FILTER_LINEAR },
  356. { "nearest", FILTER_NEAREST },
  357. { "none", FILTER_NONE },
  358. };
  359. StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM> Texture::filterModes(Texture::filterModeEntries, sizeof(Texture::filterModeEntries));
  360. StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEntries[] =
  361. {
  362. { "clamp", WRAP_CLAMP },
  363. { "clampzero", WRAP_CLAMP_ZERO },
  364. { "repeat", WRAP_REPEAT },
  365. { "mirroredrepeat", WRAP_MIRRORED_REPEAT },
  366. };
  367. StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
  368. } // graphics
  369. } // love