Texture.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. /**
  2. * Copyright (c) 2006-2020 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. uint64 SamplerState::toKey() const
  32. {
  33. union { float f; uint32 i; } conv;
  34. conv.f = lodBias;
  35. return (minFilter << 0) | (magFilter << 1) | (mipmapFilter << 2)
  36. | (wrapU << 4) | (wrapV << 7) | (wrapW << 10)
  37. | (maxAnisotropy << 12) | (minLod << 16) | (maxLod << 20)
  38. | (depthSampleMode.hasValue << 24) | (depthSampleMode.value << 25)
  39. | ((uint64)conv.i << 32);
  40. }
  41. SamplerState SamplerState::fromKey(uint64 key)
  42. {
  43. const uint32 BITS_1 = 0x1;
  44. const uint32 BITS_2 = 0x3;
  45. const uint32 BITS_3 = 0x7;
  46. const uint32 BITS_4 = 0xF;
  47. SamplerState s;
  48. s.minFilter = (FilterMode) ((key >> 0) & BITS_1);
  49. s.magFilter = (FilterMode) ((key >> 1) & BITS_1);
  50. s.mipmapFilter = (MipmapFilterMode) ((key >> 2) & BITS_2);
  51. s.wrapU = (WrapMode) ((key >> 4 ) & BITS_3);
  52. s.wrapV = (WrapMode) ((key >> 7 ) & BITS_3);
  53. s.wrapW = (WrapMode) ((key >> 10) & BITS_3);
  54. s.maxAnisotropy = (key >> 12) & BITS_4;
  55. s.minLod = (key >> 16) & BITS_4;
  56. s.maxLod = (key >> 20) & BITS_4;
  57. s.depthSampleMode.hasValue = ((key >> 24) & BITS_1) != 0;
  58. s.depthSampleMode.value = (CompareMode) ((key >> 25) & BITS_4);
  59. union { float f; uint32 i; } conv;
  60. conv.i = (uint32) (key >> 32);
  61. s.lodBias = conv.f;
  62. return s;
  63. }
  64. bool SamplerState::isClampZeroOrOne(WrapMode w)
  65. {
  66. return w == WRAP_CLAMP_ONE || w == WRAP_CLAMP_ZERO;
  67. }
  68. static StringMap<SamplerState::FilterMode, SamplerState::FILTER_MAX_ENUM>::Entry filterModeEntries[] =
  69. {
  70. { "linear", SamplerState::FILTER_LINEAR },
  71. { "nearest", SamplerState::FILTER_NEAREST },
  72. };
  73. static StringMap<SamplerState::FilterMode, SamplerState::FILTER_MAX_ENUM> filterModes(filterModeEntries, sizeof(filterModeEntries));
  74. static StringMap<SamplerState::MipmapFilterMode, SamplerState::MIPMAP_FILTER_MAX_ENUM>::Entry mipmapFilterModeEntries[] =
  75. {
  76. { "none", SamplerState::MIPMAP_FILTER_NONE },
  77. { "linear", SamplerState::MIPMAP_FILTER_LINEAR },
  78. { "nearest", SamplerState::MIPMAP_FILTER_NEAREST },
  79. };
  80. static StringMap<SamplerState::MipmapFilterMode, SamplerState::MIPMAP_FILTER_MAX_ENUM> mipmapFilterModes(mipmapFilterModeEntries, sizeof(mipmapFilterModeEntries));
  81. static StringMap<SamplerState::WrapMode, SamplerState::WRAP_MAX_ENUM>::Entry wrapModeEntries[] =
  82. {
  83. { "clamp", SamplerState::WRAP_CLAMP },
  84. { "clampzero", SamplerState::WRAP_CLAMP_ZERO },
  85. { "clampone", SamplerState::WRAP_CLAMP_ONE },
  86. { "repeat", SamplerState::WRAP_REPEAT },
  87. { "mirroredrepeat", SamplerState::WRAP_MIRRORED_REPEAT },
  88. };
  89. static StringMap<SamplerState::WrapMode, SamplerState::WRAP_MAX_ENUM> wrapModes(wrapModeEntries, sizeof(wrapModeEntries));
  90. bool SamplerState::getConstant(const char *in, FilterMode &out)
  91. {
  92. return filterModes.find(in, out);
  93. }
  94. bool SamplerState::getConstant(FilterMode in, const char *&out)
  95. {
  96. return filterModes.find(in, out);
  97. }
  98. std::vector<std::string> SamplerState::getConstants(FilterMode)
  99. {
  100. return filterModes.getNames();
  101. }
  102. bool SamplerState::getConstant(const char *in, MipmapFilterMode &out)
  103. {
  104. return mipmapFilterModes.find(in, out);
  105. }
  106. bool SamplerState::getConstant(MipmapFilterMode in, const char *&out)
  107. {
  108. return mipmapFilterModes.find(in, out);
  109. }
  110. std::vector<std::string> SamplerState::getConstants(MipmapFilterMode)
  111. {
  112. return mipmapFilterModes.getNames();
  113. }
  114. bool SamplerState::getConstant(const char *in, WrapMode &out)
  115. {
  116. return wrapModes.find(in, out);
  117. }
  118. bool SamplerState::getConstant(WrapMode in, const char *&out)
  119. {
  120. return wrapModes.find(in, out);
  121. }
  122. std::vector<std::string> SamplerState::getConstants(WrapMode)
  123. {
  124. return wrapModes.getNames();
  125. }
  126. love::Type Texture::type("Texture", &Drawable::type);
  127. int Texture::textureCount = 0;
  128. int64 Texture::totalGraphicsMemory = 0;
  129. Texture::Texture(const Settings &settings, const Slices *slices)
  130. : texType(settings.type)
  131. , format(settings.format)
  132. , renderTarget(settings.renderTarget)
  133. , readable(true)
  134. , mipmapsMode(settings.mipmaps)
  135. , sRGB(isGammaCorrect() && !settings.linear)
  136. , width(settings.width)
  137. , height(settings.height)
  138. , depth(settings.type == TEXTURE_VOLUME ? settings.layers : 1)
  139. , layers(settings.type == TEXTURE_2D_ARRAY ? settings.layers : 1)
  140. , mipmapCount(1)
  141. , pixelWidth(0)
  142. , pixelHeight(0)
  143. , requestedMSAA(settings.msaa)
  144. , samplerState()
  145. , graphicsMemorySize(0)
  146. , usingDefaultTexture(false)
  147. {
  148. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  149. if (slices != nullptr && slices->getMipmapCount() > 0 && slices->getSliceCount() > 0)
  150. {
  151. texType = slices->getTextureType();
  152. if (requestedMSAA > 1)
  153. throw love::Exception("MSAA textures cannot be created from image data.");
  154. int dataMipmaps = 1;
  155. if (slices->validate() && slices->getMipmapCount() > 1)
  156. dataMipmaps = slices->getMipmapCount();
  157. love::image::ImageDataBase *slice = slices->get(0, 0);
  158. format = slice->getFormat();
  159. pixelWidth = slice->getWidth();
  160. pixelHeight = slice->getHeight();
  161. if (texType == TEXTURE_2D_ARRAY)
  162. layers = slices->getSliceCount();
  163. else if (texType == TEXTURE_VOLUME)
  164. depth = slices->getSliceCount();
  165. width = (int) (pixelWidth / settings.dpiScale + 0.5);
  166. height = (int) (pixelHeight / settings.dpiScale + 0.5);
  167. if (isCompressed() && dataMipmaps <= 1)
  168. mipmapsMode = MIPMAPS_NONE;
  169. }
  170. else
  171. {
  172. if (isCompressed())
  173. throw love::Exception("Compressed textures must be created with initial data.");
  174. pixelWidth = (int) ((width * settings.dpiScale) + 0.5);
  175. pixelHeight = (int) ((height * settings.dpiScale) + 0.5);
  176. }
  177. if (settings.readable.hasValue)
  178. readable = settings.readable.value;
  179. else
  180. readable = !renderTarget || !isPixelFormatDepthStencil(format);
  181. format = gfx->getSizedFormat(format, renderTarget, readable, sRGB);
  182. if (mipmapsMode == MIPMAPS_AUTO && isCompressed())
  183. mipmapsMode = MIPMAPS_MANUAL;
  184. if (mipmapsMode != MIPMAPS_NONE)
  185. mipmapCount = getTotalMipmapCount(pixelWidth, pixelHeight, depth);
  186. if (pixelWidth <= 0 || pixelHeight <= 0 || layers <= 0 || depth <= 0)
  187. throw love::Exception("Texture dimensions must be greater than 0.");
  188. if (texType != TEXTURE_2D && requestedMSAA > 1)
  189. throw love::Exception("MSAA is only supported for textures with the 2D texture type.");
  190. if (!renderTarget && requestedMSAA > 1)
  191. throw love::Exception("MSAA is only supported with render target textures.");
  192. if (readable && isPixelFormatDepthStencil(format) && settings.msaa > 1)
  193. throw love::Exception("Readable depth/stencil textures with MSAA are not currently supported.");
  194. if ((!readable || settings.msaa > 1) && mipmapsMode != MIPMAPS_NONE)
  195. throw love::Exception("Non-readable and MSAA textures cannot have mipmaps.");
  196. if (!readable && texType != TEXTURE_2D)
  197. throw love::Exception("Non-readable pixel formats are only supported for 2D texture types.");
  198. if (isCompressed() && renderTarget)
  199. throw love::Exception("Compressed textures cannot be render targets.");
  200. if (!gfx->isPixelFormatSupported(format, renderTarget, readable, sRGB))
  201. {
  202. const char *fstr = "unknown";
  203. love::getConstant(format, fstr);
  204. const char *readablestr = "";
  205. if (readable != !isPixelFormatDepthStencil(format))
  206. readablestr = readable ? " readable" : " non-readable";
  207. const char *rtstr = "";
  208. if (renderTarget)
  209. rtstr = " as a render target";
  210. throw love::Exception("The %s%s pixel format is not supported%s on this system.", fstr, readablestr, rtstr);
  211. }
  212. if (!gfx->getCapabilities().textureTypes[texType])
  213. {
  214. const char *textypestr = "unknown";
  215. Texture::getConstant(texType, textypestr);
  216. throw love::Exception("%s textures are not supported on this system.", textypestr);
  217. }
  218. validateDimensions(renderTarget || !readable);
  219. samplerState = gfx->getDefaultSamplerState();
  220. Quad::Viewport v = {0, 0, (double) width, (double) height};
  221. quad.set(new Quad(v, width, height), Acquire::NORETAIN);
  222. ++textureCount;
  223. }
  224. Texture::~Texture()
  225. {
  226. --textureCount;
  227. setGraphicsMemorySize(0);
  228. }
  229. void Texture::setGraphicsMemorySize(int64 bytes)
  230. {
  231. totalGraphicsMemory = std::max(totalGraphicsMemory - graphicsMemorySize, (int64) 0);
  232. bytes = std::max(bytes, (int64) 0);
  233. graphicsMemorySize = bytes;
  234. totalGraphicsMemory += bytes;
  235. }
  236. void Texture::draw(Graphics *gfx, const Matrix4 &m)
  237. {
  238. draw(gfx, quad, m);
  239. }
  240. void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
  241. {
  242. if (!readable)
  243. throw love::Exception("Textures with non-readable formats cannot be drawn.");
  244. if (renderTarget && gfx->isRenderTargetActive(this))
  245. throw love::Exception("Cannot render a Texture to itself.");
  246. if (texType == TEXTURE_2D_ARRAY)
  247. {
  248. drawLayer(gfx, q->getLayer(), q, localTransform);
  249. return;
  250. }
  251. const Matrix4 &tm = gfx->getTransform();
  252. bool is2D = tm.isAffine2DTransform();
  253. Graphics::StreamDrawCommand cmd;
  254. cmd.formats[0] = getSinglePositionFormat(is2D);
  255. cmd.formats[1] = CommonFormat::STf_RGBAub;
  256. cmd.indexMode = TRIANGLEINDEX_QUADS;
  257. cmd.vertexCount = 4;
  258. cmd.texture = this;
  259. Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
  260. Matrix4 t(tm, localTransform);
  261. if (is2D)
  262. t.transformXY((Vector2 *) data.stream[0], q->getVertexPositions(), 4);
  263. else
  264. t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
  265. const Vector2 *texcoords = q->getVertexTexCoords();
  266. STf_RGBAub *vertexdata = (STf_RGBAub *) data.stream[1];
  267. Color32 c = toColor32(gfx->getColor());
  268. for (int i = 0; i < 4; i++)
  269. {
  270. vertexdata[i].s = texcoords[i].x;
  271. vertexdata[i].t = texcoords[i].y;
  272. vertexdata[i].color = c;
  273. }
  274. }
  275. void Texture::drawLayer(Graphics *gfx, int layer, const Matrix4 &m)
  276. {
  277. drawLayer(gfx, layer, quad, m);
  278. }
  279. void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
  280. {
  281. if (!readable)
  282. throw love::Exception("Textures with non-readable formats cannot be drawn.");
  283. if (renderTarget && gfx->isRenderTargetActive(this, layer))
  284. throw love::Exception("Cannot render a Texture to itself.");
  285. if (texType != TEXTURE_2D_ARRAY)
  286. throw love::Exception("drawLayer can only be used with Array Textures.");
  287. if (layer < 0 || layer >= layers)
  288. throw love::Exception("Invalid layer: %d (Texture has %d layers)", layer + 1, layers);
  289. Color32 c = toColor32(gfx->getColor());
  290. const Matrix4 &tm = gfx->getTransform();
  291. bool is2D = tm.isAffine2DTransform();
  292. Matrix4 t(tm, m);
  293. Graphics::StreamDrawCommand cmd;
  294. cmd.formats[0] = getSinglePositionFormat(is2D);
  295. cmd.formats[1] = CommonFormat::STPf_RGBAub;
  296. cmd.indexMode = TRIANGLEINDEX_QUADS;
  297. cmd.vertexCount = 4;
  298. cmd.texture = this;
  299. cmd.standardShaderType = Shader::STANDARD_ARRAY;
  300. Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
  301. if (is2D)
  302. t.transformXY((Vector2 *) data.stream[0], q->getVertexPositions(), 4);
  303. else
  304. t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
  305. const Vector2 *texcoords = q->getVertexTexCoords();
  306. STPf_RGBAub *vertexdata = (STPf_RGBAub *) data.stream[1];
  307. for (int i = 0; i < 4; i++)
  308. {
  309. vertexdata[i].s = texcoords[i].x;
  310. vertexdata[i].t = texcoords[i].y;
  311. vertexdata[i].p = (float) layer;
  312. vertexdata[i].color = c;
  313. }
  314. }
  315. void Texture::uploadImageData(love::image::ImageDataBase *d, int level, int slice, int x, int y)
  316. {
  317. love::image::ImageData *id = dynamic_cast<love::image::ImageData *>(d);
  318. love::thread::EmptyLock lock;
  319. if (id != nullptr)
  320. lock.setLock(id->getMutex());
  321. Rect rect = {x, y, d->getWidth(), d->getHeight()};
  322. uploadByteData(d->getFormat(), d->getData(), d->getSize(), level, slice, rect, d);
  323. }
  324. void Texture::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, int x, int y, bool reloadmipmaps)
  325. {
  326. if (!isReadable())
  327. throw love::Exception("replacePixels can only be called on readable Textures.");
  328. if (getMSAA() > 1)
  329. throw love::Exception("replacePixels cannot be called on a MSAA Texture.");
  330. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  331. if (gfx != nullptr && gfx->isRenderTargetActive(this))
  332. throw love::Exception("replacePixels cannot be called on this Texture while it's an active render target.");
  333. // No effect if the texture hasn't been created yet.
  334. if (getHandle() == 0 || usingDefaultTexture)
  335. return;
  336. if (d->getFormat() != getPixelFormat())
  337. throw love::Exception("Pixel formats must match.");
  338. if (mipmap < 0 || mipmap >= getMipmapCount())
  339. throw love::Exception("Invalid texture mipmap index %d.", mipmap + 1);
  340. if (slice < 0 || (texType == TEXTURE_CUBE && slice >= 6)
  341. || (texType == TEXTURE_VOLUME && slice >= getDepth(mipmap))
  342. || (texType == TEXTURE_2D_ARRAY && slice >= getLayerCount()))
  343. {
  344. throw love::Exception("Invalid texture slice index %d.", slice + 1);
  345. }
  346. Rect rect = {x, y, d->getWidth(), d->getHeight()};
  347. int mipw = getPixelWidth(mipmap);
  348. int miph = getPixelHeight(mipmap);
  349. if (rect.x < 0 || rect.y < 0 || rect.w <= 0 || rect.h <= 0
  350. || (rect.x + rect.w) > mipw || (rect.y + rect.h) > miph)
  351. {
  352. throw love::Exception("Invalid rectangle dimensions (x=%d, y=%d, w=%d, h=%d) for %dx%d Texture.", rect.x, rect.y, rect.w, rect.h, mipw, miph);
  353. }
  354. // We don't currently support partial updates of compressed textures.
  355. if (isPixelFormatCompressed(d->getFormat()) && (rect.x != 0 || rect.y != 0 || rect.w != mipw || rect.h != miph))
  356. throw love::Exception("Compressed textures only support replacing the entire Texture.");
  357. Graphics::flushStreamDrawsGlobal();
  358. uploadImageData(d, mipmap, slice, x, y);
  359. if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1)
  360. generateMipmaps();
  361. }
  362. void Texture::replacePixels(const void *data, size_t size, int slice, int mipmap, const Rect &rect, bool reloadmipmaps)
  363. {
  364. if (!isReadable() || getMSAA() > 1)
  365. return;
  366. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  367. if (gfx != nullptr && gfx->isRenderTargetActive(this))
  368. return;
  369. Graphics::flushStreamDrawsGlobal();
  370. uploadByteData(format, data, size, mipmap, slice, rect, nullptr);
  371. if (reloadmipmaps && mipmap == 0 && getMipmapCount() > 1)
  372. generateMipmaps();
  373. }
  374. love::image::ImageData *Texture::newImageData(love::image::Image *module, int slice, int mipmap, const Rect &r)
  375. {
  376. if (!isReadable())
  377. throw love::Exception("Texture:newImageData cannot be called on non-readable Textures.");
  378. if (!isRenderTarget())
  379. throw love::Exception("Texture:newImageData can only be called on render target Textures.");
  380. if (isPixelFormatDepthStencil(getPixelFormat()))
  381. throw love::Exception("Texture:newImageData cannot be called on Textures with depth/stencil pixel formats.");
  382. if (r.x < 0 || r.y < 0 || r.w <= 0 || r.h <= 0 || (r.x + r.w) > getPixelWidth(mipmap) || (r.y + r.h) > getPixelHeight(mipmap))
  383. throw love::Exception("Invalid rectangle dimensions.");
  384. if (slice < 0 || (texType == TEXTURE_VOLUME && slice >= getDepth(mipmap))
  385. || (texType == TEXTURE_2D_ARRAY && slice >= layers)
  386. || (texType == TEXTURE_CUBE && slice >= 6))
  387. {
  388. throw love::Exception("Invalid slice index.");
  389. }
  390. Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  391. if (gfx != nullptr && gfx->isRenderTargetActive(this))
  392. throw love::Exception("Texture:newImageData cannot be called while that Texture is an active render target.");
  393. PixelFormat dataformat = getLinearPixelFormat(getPixelFormat());
  394. if (!image::ImageData::validPixelFormat(dataformat))
  395. {
  396. const char *formatname = "unknown";
  397. love::getConstant(dataformat, formatname);
  398. throw love::Exception("ImageData with the '%s' pixel format is not supported.", formatname);
  399. }
  400. return module->newImageData(r.w, r.h, dataformat);
  401. }
  402. TextureType Texture::getTextureType() const
  403. {
  404. return texType;
  405. }
  406. PixelFormat Texture::getPixelFormat() const
  407. {
  408. return format;
  409. }
  410. Texture::MipmapsMode Texture::getMipmapsMode() const
  411. {
  412. return mipmapsMode;
  413. }
  414. bool Texture::isRenderTarget() const
  415. {
  416. return renderTarget;
  417. }
  418. bool Texture::isReadable() const
  419. {
  420. return readable;
  421. }
  422. bool Texture::isCompressed() const
  423. {
  424. return isPixelFormatCompressed(format);
  425. }
  426. bool Texture::isFormatLinear() const
  427. {
  428. return isGammaCorrect() && !sRGB && format != PIXELFORMAT_sRGBA8_UNORM;
  429. }
  430. bool Texture::isValidSlice(int slice) const
  431. {
  432. if (slice < 0)
  433. return false;
  434. if (texType == TEXTURE_CUBE)
  435. return slice < 6;
  436. else if (texType == TEXTURE_VOLUME)
  437. return slice < depth;
  438. else if (texType == TEXTURE_2D_ARRAY)
  439. return slice < layers;
  440. else if (slice > 0)
  441. return false;
  442. return true;
  443. }
  444. int Texture::getWidth(int mip) const
  445. {
  446. return std::max(width >> mip, 1);
  447. }
  448. int Texture::getHeight(int mip) const
  449. {
  450. return std::max(height >> mip, 1);
  451. }
  452. int Texture::getDepth(int mip) const
  453. {
  454. return std::max(depth >> mip, 1);
  455. }
  456. int Texture::getLayerCount() const
  457. {
  458. return layers;
  459. }
  460. int Texture::getMipmapCount() const
  461. {
  462. return mipmapCount;
  463. }
  464. int Texture::getPixelWidth(int mip) const
  465. {
  466. return std::max(pixelWidth >> mip, 1);
  467. }
  468. int Texture::getPixelHeight(int mip) const
  469. {
  470. return std::max(pixelHeight >> mip, 1);
  471. }
  472. float Texture::getDPIScale() const
  473. {
  474. return (float) pixelHeight / (float) height;
  475. }
  476. int Texture::getRequestedMSAA() const
  477. {
  478. return requestedMSAA;
  479. }
  480. void Texture::setSamplerState(const SamplerState &s)
  481. {
  482. if (!readable)
  483. return;
  484. if (s.depthSampleMode.hasValue && !isPixelFormatDepth(format))
  485. throw love::Exception("Only depth textures can have a depth sample compare mode.");
  486. Graphics::flushStreamDrawsGlobal();
  487. samplerState = s;
  488. if (samplerState.mipmapFilter != SamplerState::MIPMAP_FILTER_NONE && getMipmapCount() == 1)
  489. samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
  490. if (texType == TEXTURE_CUBE)
  491. samplerState.wrapU = samplerState.wrapV = samplerState.wrapW = SamplerState::WRAP_CLAMP;
  492. }
  493. const SamplerState &Texture::getSamplerState() const
  494. {
  495. return samplerState;
  496. }
  497. Quad *Texture::getQuad() const
  498. {
  499. return quad;
  500. }
  501. int Texture::getTotalMipmapCount(int w, int h)
  502. {
  503. return (int) log2(std::max(w, h)) + 1;
  504. }
  505. int Texture::getTotalMipmapCount(int w, int h, int d)
  506. {
  507. return (int) log2(std::max(std::max(w, h), d)) + 1;
  508. }
  509. bool Texture::validateDimensions(bool throwException) const
  510. {
  511. bool success = true;
  512. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  513. if (gfx == nullptr)
  514. return false;
  515. const Graphics::Capabilities &caps = gfx->getCapabilities();
  516. int max2Dsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE];
  517. int max3Dsize = (int) caps.limits[Graphics::LIMIT_VOLUME_TEXTURE_SIZE];
  518. int maxcubesize = (int) caps.limits[Graphics::LIMIT_CUBE_TEXTURE_SIZE];
  519. int maxlayers = (int) caps.limits[Graphics::LIMIT_TEXTURE_LAYERS];
  520. int largestdim = 0;
  521. const char *largestname = nullptr;
  522. if ((texType == TEXTURE_2D || texType == TEXTURE_2D_ARRAY) && (pixelWidth > max2Dsize || pixelHeight > max2Dsize))
  523. {
  524. success = false;
  525. largestdim = std::max(pixelWidth, pixelHeight);
  526. largestname = pixelWidth > pixelHeight ? "pixel width" : "pixel height";
  527. }
  528. else if (texType == TEXTURE_2D_ARRAY && layers > maxlayers)
  529. {
  530. success = false;
  531. largestdim = layers;
  532. largestname = "array layer count";
  533. }
  534. else if (texType == TEXTURE_CUBE && (pixelWidth > maxcubesize || pixelWidth != pixelHeight))
  535. {
  536. success = false;
  537. largestdim = std::max(pixelWidth, pixelHeight);
  538. largestname = pixelWidth > pixelHeight ? "pixel width" : "pixel height";
  539. if (throwException && pixelWidth != pixelHeight)
  540. throw love::Exception("Cubemap textures must have equal width and height.");
  541. }
  542. else if (texType == TEXTURE_VOLUME && (pixelWidth > max3Dsize || pixelHeight > max3Dsize || depth > max3Dsize))
  543. {
  544. success = false;
  545. largestdim = std::max(std::max(pixelWidth, pixelHeight), depth);
  546. if (largestdim == pixelWidth)
  547. largestname = "pixel width";
  548. else if (largestdim == pixelHeight)
  549. largestname = "pixel height";
  550. else
  551. largestname = "pixel depth";
  552. }
  553. if (throwException && largestname != nullptr)
  554. throw love::Exception("Cannot create texture: %s of %d is too large for this system.", largestname, largestdim);
  555. return success;
  556. }
  557. Texture::Slices::Slices(TextureType textype)
  558. : textureType(textype)
  559. {
  560. }
  561. void Texture::Slices::clear()
  562. {
  563. data.clear();
  564. }
  565. void Texture::Slices::set(int slice, int mipmap, love::image::ImageDataBase *d)
  566. {
  567. if (textureType == TEXTURE_VOLUME)
  568. {
  569. if (mipmap >= (int) data.size())
  570. data.resize(mipmap + 1);
  571. if (slice >= (int) data[mipmap].size())
  572. data[mipmap].resize(slice + 1);
  573. data[mipmap][slice].set(d);
  574. }
  575. else
  576. {
  577. if (slice >= (int) data.size())
  578. data.resize(slice + 1);
  579. if (mipmap >= (int) data[slice].size())
  580. data[slice].resize(mipmap + 1);
  581. data[slice][mipmap].set(d);
  582. }
  583. }
  584. love::image::ImageDataBase *Texture::Slices::get(int slice, int mipmap) const
  585. {
  586. if (slice < 0 || slice >= getSliceCount(mipmap))
  587. return nullptr;
  588. if (mipmap < 0 || mipmap >= getMipmapCount(slice))
  589. return nullptr;
  590. if (textureType == TEXTURE_VOLUME)
  591. return data[mipmap][slice].get();
  592. else
  593. return data[slice][mipmap].get();
  594. }
  595. void Texture::Slices::add(love::image::CompressedImageData *cdata, int startslice, int startmip, bool addallslices, bool addallmips)
  596. {
  597. int slicecount = addallslices ? cdata->getSliceCount() : 1;
  598. int mipcount = addallmips ? cdata->getMipmapCount() : 1;
  599. for (int mip = 0; mip < mipcount; mip++)
  600. {
  601. for (int slice = 0; slice < slicecount; slice++)
  602. set(startslice + slice, startmip + mip, cdata->getSlice(slice, mip));
  603. }
  604. }
  605. int Texture::Slices::getSliceCount(int mip) const
  606. {
  607. if (textureType == TEXTURE_VOLUME)
  608. {
  609. if (mip < 0 || mip >= (int) data.size())
  610. return 0;
  611. return (int) data[mip].size();
  612. }
  613. else
  614. return (int) data.size();
  615. }
  616. int Texture::Slices::getMipmapCount(int slice) const
  617. {
  618. if (textureType == TEXTURE_VOLUME)
  619. return (int) data.size();
  620. else
  621. {
  622. if (slice < 0 || slice >= (int) data.size())
  623. return 0;
  624. return data[slice].size();
  625. }
  626. }
  627. bool Texture::Slices::validate() const
  628. {
  629. int slicecount = getSliceCount();
  630. int mipcount = getMipmapCount(0);
  631. if (slicecount == 0 || mipcount == 0)
  632. throw love::Exception("At least one ImageData or CompressedImageData is required.");
  633. if (textureType == TEXTURE_CUBE && slicecount != 6)
  634. throw love::Exception("Cube textures must have exactly 6 sides.");
  635. image::ImageDataBase *firstdata = get(0, 0);
  636. int w = firstdata->getWidth();
  637. int h = firstdata->getHeight();
  638. int depth = textureType == TEXTURE_VOLUME ? slicecount : 1;
  639. PixelFormat format = firstdata->getFormat();
  640. int expectedmips = Texture::getTotalMipmapCount(w, h, depth);
  641. if (mipcount != expectedmips && mipcount != 1)
  642. throw love::Exception("Texture does not have all required mipmap levels (expected %d, got %d)", expectedmips, mipcount);
  643. if (textureType == TEXTURE_CUBE && w != h)
  644. throw love::Exception("Cube textures must have equal widths and heights for each cube face.");
  645. int mipw = w;
  646. int miph = h;
  647. int mipslices = slicecount;
  648. for (int mip = 0; mip < mipcount; mip++)
  649. {
  650. if (textureType == TEXTURE_VOLUME)
  651. {
  652. slicecount = getSliceCount(mip);
  653. if (slicecount != mipslices)
  654. throw love::Exception("Invalid number of image data layers in mipmap level %d (expected %d, got %d)", mip+1, mipslices, slicecount);
  655. }
  656. for (int slice = 0; slice < slicecount; slice++)
  657. {
  658. auto slicedata = get(slice, mip);
  659. if (slicedata == nullptr)
  660. throw love::Exception("Missing image data (slice %d, mipmap level %d)", slice+1, mip+1);
  661. int realw = slicedata->getWidth();
  662. int realh = slicedata->getHeight();
  663. if (getMipmapCount(slice) != mipcount)
  664. throw love::Exception("All texture layers must have the same mipmap count.");
  665. if (mipw != realw)
  666. throw love::Exception("Width of image data (slice %d, mipmap level %d) is incorrect (expected %d, got %d)", slice+1, mip+1, mipw, realw);
  667. if (miph != realh)
  668. throw love::Exception("Height of image data (slice %d, mipmap level %d) is incorrect (expected %d, got %d)", slice+1, mip+1, miph, realh);
  669. if (format != slicedata->getFormat())
  670. throw love::Exception("All texture slices and mipmaps must have the same pixel format.");
  671. }
  672. mipw = std::max(mipw / 2, 1);
  673. miph = std::max(miph / 2, 1);
  674. if (textureType == TEXTURE_VOLUME)
  675. mipslices = std::max(mipslices / 2, 1);
  676. }
  677. return true;
  678. }
  679. static StringMap<TextureType, TEXTURE_MAX_ENUM>::Entry texTypeEntries[] =
  680. {
  681. { "2d", TEXTURE_2D },
  682. { "volume", TEXTURE_VOLUME },
  683. { "array", TEXTURE_2D_ARRAY },
  684. { "cube", TEXTURE_CUBE },
  685. };
  686. static StringMap<TextureType, TEXTURE_MAX_ENUM> texTypes(texTypeEntries, sizeof(texTypeEntries));
  687. static StringMap<Texture::MipmapsMode, Texture::MIPMAPS_MAX_ENUM>::Entry mipmapEntries[] =
  688. {
  689. { "none", Texture::MIPMAPS_NONE },
  690. { "manual", Texture::MIPMAPS_MANUAL },
  691. { "auto", Texture::MIPMAPS_AUTO },
  692. };
  693. static StringMap<Texture::MipmapsMode, Texture::MIPMAPS_MAX_ENUM> mipmapModes(mipmapEntries, sizeof(mipmapEntries));
  694. static StringMap<Texture::SettingType, Texture::SETTING_MAX_ENUM>::Entry settingTypeEntries[] =
  695. {
  696. { "width", Texture::SETTING_WIDTH },
  697. { "height", Texture::SETTING_HEIGHT },
  698. { "layers", Texture::SETTING_LAYERS },
  699. { "mipmaps", Texture::SETTING_MIPMAPS },
  700. { "format", Texture::SETTING_FORMAT },
  701. { "linear", Texture::SETTING_LINEAR },
  702. { "type", Texture::SETTING_TYPE },
  703. { "dpiscale", Texture::SETTING_DPI_SCALE },
  704. { "msaa", Texture::SETTING_MSAA },
  705. { "canvas", Texture::SETTING_RENDER_TARGET },
  706. { "readable", Texture::SETTING_READABLE },
  707. };
  708. static StringMap<Texture::SettingType, Texture::SETTING_MAX_ENUM> settingTypes(settingTypeEntries, sizeof(settingTypeEntries));
  709. bool Texture::getConstant(const char *in, TextureType &out)
  710. {
  711. return texTypes.find(in, out);
  712. }
  713. bool Texture::getConstant(TextureType in, const char *&out)
  714. {
  715. return texTypes.find(in, out);
  716. }
  717. std::vector<std::string> Texture::getConstants(TextureType)
  718. {
  719. return texTypes.getNames();
  720. }
  721. bool Texture::getConstant(const char *in, MipmapsMode &out)
  722. {
  723. return mipmapModes.find(in, out);
  724. }
  725. bool Texture::getConstant(MipmapsMode in, const char *&out)
  726. {
  727. return mipmapModes.find(in, out);
  728. }
  729. std::vector<std::string> Texture::getConstants(MipmapsMode)
  730. {
  731. return mipmapModes.getNames();
  732. }
  733. bool Texture::getConstant(const char *in, SettingType &out)
  734. {
  735. return settingTypes.find(in, out);
  736. }
  737. bool Texture::getConstant(SettingType in, const char *&out)
  738. {
  739. return settingTypes.find(in, out);
  740. }
  741. const char *Texture::getConstant(SettingType in)
  742. {
  743. const char *name = nullptr;
  744. getConstant(in, name);
  745. return name;
  746. }
  747. std::vector<std::string> Texture::getConstants(SettingType)
  748. {
  749. return settingTypes.getNames();
  750. }
  751. } // graphics
  752. } // love