Canvas.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. /**
  2. * Copyright (c) 2006-2017 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. #include "Canvas.h"
  21. #include "graphics/Graphics.h"
  22. #include <algorithm> // For min/max
  23. namespace love
  24. {
  25. namespace graphics
  26. {
  27. namespace opengl
  28. {
  29. static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat format, GLuint texture, int layers)
  30. {
  31. // get currently bound fbo to reset to it later
  32. GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
  33. glGenFramebuffers(1, &framebuffer);
  34. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer);
  35. if (texture != 0)
  36. {
  37. bool unusedSRGB = false;
  38. OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, false, unusedSRGB);
  39. int faces = texType == TEXTURE_CUBE ? 6 : 1;
  40. // Make sure all faces and layers of the texture are initialized to
  41. // transparent black. This is unfortunately probably pretty slow for
  42. // 2D-array and 3D textures with a lot of layers...
  43. for (int layer = layers - 1; layer >= 0; layer--)
  44. {
  45. for (int face = faces - 1; face >= 0; face--)
  46. {
  47. for (GLenum attachment : fmt.framebufferAttachments)
  48. {
  49. if (attachment == GL_NONE)
  50. continue;
  51. gl.framebufferTexture(attachment, texType, texture, 0, layer, face);
  52. if (isPixelFormatDepthStencil(format))
  53. glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  54. else
  55. {
  56. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  57. glClear(GL_COLOR_BUFFER_BIT);
  58. }
  59. }
  60. }
  61. }
  62. }
  63. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  64. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
  65. return status;
  66. }
  67. static bool createRenderbuffer(int width, int height, int &samples, PixelFormat pixelformat, GLuint &buffer)
  68. {
  69. int reqsamples = samples;
  70. bool unusedSRGB = false;
  71. OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, true, unusedSRGB);
  72. GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
  73. // Temporary FBO used to clear the renderbuffer.
  74. GLuint fbo = 0;
  75. glGenFramebuffers(1, &fbo);
  76. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
  77. glGenRenderbuffers(1, &buffer);
  78. glBindRenderbuffer(GL_RENDERBUFFER, buffer);
  79. if (samples > 1)
  80. glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, fmt.internalformat, width, height);
  81. else
  82. glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, width, height);
  83. for (GLenum attachment : fmt.framebufferAttachments)
  84. {
  85. if (attachment != GL_NONE)
  86. glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, buffer);
  87. }
  88. if (samples > 1)
  89. glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
  90. else
  91. samples = 0;
  92. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  93. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  94. if (status == GL_FRAMEBUFFER_COMPLETE && (reqsamples <= 1 || samples > 1))
  95. {
  96. if (isPixelFormatDepthStencil(pixelformat))
  97. glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  98. else
  99. {
  100. // Initialize the buffer to transparent black.
  101. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  102. glClear(GL_COLOR_BUFFER_BIT);
  103. }
  104. }
  105. else
  106. {
  107. glDeleteRenderbuffers(1, &buffer);
  108. buffer = 0;
  109. samples = 0;
  110. }
  111. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
  112. gl.deleteFramebuffer(fbo);
  113. return status == GL_FRAMEBUFFER_COMPLETE;
  114. }
  115. Canvas::Canvas(const Settings &settings)
  116. : love::graphics::Canvas(settings.type)
  117. , fbo(0)
  118. , texture(0)
  119. , renderbuffer(0)
  120. , requestedSamples(settings.msaa)
  121. , actualSamples(0)
  122. , textureMemory(0)
  123. {
  124. width = settings.width;
  125. height = settings.height;
  126. pixelWidth = (int) ((width * settings.pixeldensity) + 0.5);
  127. pixelHeight = (int) ((height * settings.pixeldensity) + 0.5);
  128. if (texType == TEXTURE_VOLUME)
  129. depth = settings.layers;
  130. else if (texType == TEXTURE_2D_ARRAY)
  131. layers = settings.layers;
  132. else
  133. layers = 1;
  134. if (width <= 0 || height <= 0 || layers <= 0)
  135. throw love::Exception("Canvas dimensions must be greater than 0.");
  136. if (texType != TEXTURE_2D && settings.msaa > 1)
  137. throw love::Exception("MSAA is only supported for Canvases with the 2D texture type.");
  138. format = getSizedFormat(settings.format);
  139. if (settings.readable.set)
  140. readable = settings.readable.value;
  141. else
  142. readable = !isPixelFormatDepthStencil(format);
  143. initQuad();
  144. loadVolatile();
  145. if (status != GL_FRAMEBUFFER_COMPLETE)
  146. throw love::Exception("Cannot create Canvas: %s", OpenGL::framebufferStatusString(status));
  147. }
  148. Canvas::~Canvas()
  149. {
  150. unloadVolatile();
  151. }
  152. bool Canvas::loadVolatile()
  153. {
  154. if (texture != 0)
  155. return true;
  156. if (!Canvas::isSupported())
  157. throw love::Exception("Canvases are not supported by your OpenGL drivers!");
  158. if (!Canvas::isFormatSupported(format, readable))
  159. {
  160. const char *fstr = "rgba8";
  161. love::getConstant(Canvas::getSizedFormat(format), fstr);
  162. throw love::Exception("The %s canvas format is not supported by your OpenGL drivers.", fstr);
  163. }
  164. if (requestedSamples > 1 && texType != TEXTURE_2D)
  165. throw love::Exception("MSAA is only supported for 2D texture types.");
  166. if (!readable && texType != TEXTURE_2D)
  167. throw love::Exception("Non-readable pixel formats are only supported for 2D texture types.");
  168. if (!gl.isTextureTypeSupported(texType))
  169. {
  170. const char *textypestr = "unknown";
  171. getConstant(texType, textypestr);
  172. throw love::Exception("%s textures are not supported on this system!", textypestr);
  173. }
  174. switch (texType)
  175. {
  176. case TEXTURE_2D:
  177. if (pixelWidth > gl.getMax2DTextureSize())
  178. throw TextureTooLargeException("width", pixelWidth);
  179. else if (pixelHeight > gl.getMax2DTextureSize())
  180. throw TextureTooLargeException("height", pixelHeight);
  181. break;
  182. case TEXTURE_VOLUME:
  183. if (pixelWidth > gl.getMax3DTextureSize())
  184. throw TextureTooLargeException("width", pixelWidth);
  185. else if (pixelHeight > gl.getMax3DTextureSize())
  186. throw TextureTooLargeException("height", pixelHeight);
  187. else if (depth > gl.getMax3DTextureSize())
  188. throw TextureTooLargeException("depth", depth);
  189. break;
  190. case TEXTURE_2D_ARRAY:
  191. if (pixelWidth > gl.getMax2DTextureSize())
  192. throw TextureTooLargeException("width", pixelWidth);
  193. else if (pixelHeight > gl.getMax2DTextureSize())
  194. throw TextureTooLargeException("height", pixelHeight);
  195. else if (layers > gl.getMaxTextureLayers())
  196. throw TextureTooLargeException("array layer count", layers);
  197. break;
  198. case TEXTURE_CUBE:
  199. if (pixelWidth != pixelHeight)
  200. throw love::Exception("Cubemap textures must have equal width and height.");
  201. else if (pixelWidth > gl.getMaxCubeTextureSize())
  202. throw TextureTooLargeException("width", pixelWidth);
  203. break;
  204. default:
  205. break;
  206. }
  207. OpenGL::TempDebugGroup debuggroup("Canvas load");
  208. fbo = texture = 0;
  209. renderbuffer = 0;
  210. status = GL_FRAMEBUFFER_COMPLETE;
  211. if (isReadable())
  212. {
  213. glGenTextures(1, &texture);
  214. gl.bindTextureToUnit(this, 0, false);
  215. GLenum gltype = OpenGL::getGLTextureType(texType);
  216. if (GLAD_ANGLE_texture_usage)
  217. glTexParameteri(gltype, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
  218. setFilter(filter);
  219. setWrap(wrap);
  220. while (glGetError() != GL_NO_ERROR)
  221. /* Clear the error buffer. */;
  222. bool isSRGB = format == PIXELFORMAT_sRGBA8;
  223. if (!gl.rawTexStorage(texType, 1, format, isSRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers))
  224. {
  225. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  226. return false;
  227. }
  228. if (glGetError() != GL_NO_ERROR)
  229. {
  230. gl.deleteTexture(texture);
  231. texture = 0;
  232. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  233. return false;
  234. }
  235. // Create a canvas-local FBO used for glReadPixels as well as MSAA blitting.
  236. status = createFBO(fbo, texType, format, texture, texType == TEXTURE_VOLUME ? depth : layers);
  237. if (status != GL_FRAMEBUFFER_COMPLETE)
  238. {
  239. if (fbo != 0)
  240. {
  241. gl.deleteFramebuffer(fbo);
  242. fbo = 0;
  243. }
  244. return false;
  245. }
  246. }
  247. // getMaxRenderbufferSamples will be 0 on systems that don't support
  248. // multisampled renderbuffers / don't export FBO multisample extensions.
  249. actualSamples = requestedSamples;
  250. actualSamples = std::min(actualSamples, gl.getMaxRenderbufferSamples());
  251. actualSamples = std::max(actualSamples, 0);
  252. actualSamples = actualSamples == 1 ? 0 : actualSamples;
  253. if (!isReadable() || actualSamples > 0)
  254. createRenderbuffer(pixelWidth, pixelHeight, actualSamples, format, renderbuffer);
  255. size_t prevmemsize = textureMemory;
  256. textureMemory = getPixelFormatSize(format) * pixelWidth * pixelHeight;
  257. if (actualSamples > 0 && isReadable())
  258. textureMemory += (textureMemory * actualSamples);
  259. else if (actualSamples > 0)
  260. textureMemory *= actualSamples;
  261. gl.updateTextureMemorySize(prevmemsize, textureMemory);
  262. return true;
  263. }
  264. void Canvas::unloadVolatile()
  265. {
  266. if (fbo != 0)
  267. gl.deleteFramebuffer(fbo);
  268. if (renderbuffer != 0)
  269. glDeleteRenderbuffers(1, &renderbuffer);
  270. if (texture != 0)
  271. gl.deleteTexture(texture);
  272. fbo = 0;
  273. renderbuffer = 0;
  274. texture = 0;
  275. gl.updateTextureMemorySize(textureMemory, 0);
  276. textureMemory = 0;
  277. }
  278. void Canvas::setFilter(const Texture::Filter &f)
  279. {
  280. if (!validateFilter(f, false))
  281. throw love::Exception("Invalid texture filter.");
  282. filter = f;
  283. gl.bindTextureToUnit(this, 0, false);
  284. gl.setTextureFilter(texType, filter);
  285. }
  286. bool Canvas::setWrap(const Texture::Wrap &w)
  287. {
  288. bool success = true;
  289. bool forceclamp = texType == TEXTURE_CUBE;
  290. wrap = w;
  291. // If we only have limited NPOT support then the wrap mode must be CLAMP.
  292. if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
  293. && (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight) || depth != nextP2(depth)))
  294. {
  295. forceclamp = true;
  296. }
  297. if (forceclamp)
  298. {
  299. if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP || wrap.r != WRAP_CLAMP)
  300. success = false;
  301. wrap.s = wrap.t = wrap.r = WRAP_CLAMP;
  302. }
  303. if (!gl.isClampZeroTextureWrapSupported())
  304. {
  305. if (wrap.s == WRAP_CLAMP_ZERO) wrap.s = WRAP_CLAMP;
  306. if (wrap.t == WRAP_CLAMP_ZERO) wrap.t = WRAP_CLAMP;
  307. if (wrap.r == WRAP_CLAMP_ZERO) wrap.r = WRAP_CLAMP;
  308. }
  309. gl.bindTextureToUnit(this, 0, false);
  310. gl.setTextureWrap(texType, wrap);
  311. return success;
  312. }
  313. bool Canvas::setMipmapSharpness(float /*sharpness*/)
  314. {
  315. return false;
  316. }
  317. ptrdiff_t Canvas::getHandle() const
  318. {
  319. return texture;
  320. }
  321. love::image::ImageData *Canvas::newImageData(love::image::Image *module, int slice, int x, int y, int w, int h)
  322. {
  323. if (!isReadable())
  324. throw love::Exception("Canvas:newImageData cannot be called on non-readable Canvases.");
  325. if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > getPixelWidth() || (y + h) > getPixelHeight())
  326. throw love::Exception("Invalid rectangle dimensions.");
  327. if (slice < 0 || (texType == TEXTURE_VOLUME && slice >= depth)
  328. || (texType == TEXTURE_2D_ARRAY && slice >= layers)
  329. || (texType == TEXTURE_CUBE && slice >= 6))
  330. {
  331. throw love::Exception("Invalid slice index.");
  332. }
  333. Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  334. if (gfx != nullptr && gfx->isCanvasActive(this))
  335. throw love::Exception("Canvas:newImageData cannot be called while that Canvas is currently active.");
  336. PixelFormat dataformat;
  337. switch (getPixelFormat())
  338. {
  339. case PIXELFORMAT_RGB10A2: // FIXME: Conversions aren't supported in GLES
  340. dataformat = PIXELFORMAT_RGBA16;
  341. break;
  342. case PIXELFORMAT_R16F:
  343. case PIXELFORMAT_RG16F:
  344. case PIXELFORMAT_RGBA16F:
  345. case PIXELFORMAT_RG11B10F: // FIXME: Conversions aren't supported in GLES
  346. dataformat = PIXELFORMAT_RGBA16F;
  347. break;
  348. case PIXELFORMAT_R32F:
  349. case PIXELFORMAT_RG32F:
  350. case PIXELFORMAT_RGBA32F:
  351. dataformat = PIXELFORMAT_RGBA32F;
  352. break;
  353. default:
  354. dataformat = PIXELFORMAT_RGBA8;
  355. break;
  356. }
  357. love::image::ImageData *imagedata = module->newImageData(w, h, dataformat);
  358. bool isSRGB = false;
  359. OpenGL::TextureFormat fmt = gl.convertPixelFormat(dataformat, false, isSRGB);
  360. GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
  361. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getFBO());
  362. if (slice > 0)
  363. {
  364. int layer = texType == TEXTURE_CUBE ? 0 : slice;
  365. int face = texType == TEXTURE_CUBE ? slice : 0;
  366. gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, layer, face);
  367. }
  368. glReadPixels(x, y, w, h, fmt.externalformat, fmt.type, imagedata->getData());
  369. if (slice > 0)
  370. gl.framebufferTexture(GL_COLOR_ATTACHMENT0, texType, texture, 0, 0, 0);
  371. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
  372. return imagedata;
  373. }
  374. PixelFormat Canvas::getSizedFormat(PixelFormat format)
  375. {
  376. switch (format)
  377. {
  378. case PIXELFORMAT_NORMAL:
  379. if (isGammaCorrect())
  380. return PIXELFORMAT_sRGBA8;
  381. else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8, true, true, false))
  382. // 32-bit render targets don't have guaranteed support on GLES2.
  383. return PIXELFORMAT_RGBA4;
  384. else
  385. return PIXELFORMAT_RGBA8;
  386. case PIXELFORMAT_HDR:
  387. return PIXELFORMAT_RGBA16F;
  388. default:
  389. return format;
  390. }
  391. }
  392. bool Canvas::isSupported()
  393. {
  394. return GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object;
  395. }
  396. bool Canvas::isMultiFormatMultiCanvasSupported()
  397. {
  398. return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
  399. }
  400. Canvas::SupportedFormat Canvas::supportedFormats[] = {};
  401. Canvas::SupportedFormat Canvas::checkedFormats[] = {};
  402. bool Canvas::isFormatSupported(PixelFormat format)
  403. {
  404. return isFormatSupported(format, !isPixelFormatDepthStencil(format));
  405. }
  406. bool Canvas::isFormatSupported(PixelFormat format, bool readable)
  407. {
  408. if (!isSupported())
  409. return false;
  410. const char *fstr = "?";
  411. love::getConstant(format, fstr);
  412. bool supported = true;
  413. format = getSizedFormat(format);
  414. if (!OpenGL::isPixelFormatSupported(format, true, readable, false))
  415. return false;
  416. if (checkedFormats[format].get(readable))
  417. return supportedFormats[format].get(readable);
  418. // Even though we might have the necessary OpenGL version or extension,
  419. // drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching
  420. // a texture to a FBO whose format the driver doesn't like. So we should
  421. // test with an actual FBO.
  422. GLuint texture = 0;
  423. GLuint renderbuffer = 0;
  424. bool unusedSRGB = false;
  425. OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, readable, unusedSRGB);
  426. GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
  427. GLuint fbo = 0;
  428. glGenFramebuffers(1, &fbo);
  429. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
  430. // Make sure at least something is bound to a color attachment. I believe
  431. // this is required on ES2 but I'm not positive.
  432. if (isPixelFormatDepthStencil(format))
  433. gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D), 0, 0, 0);
  434. if (readable)
  435. {
  436. glGenTextures(1, &texture);
  437. gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
  438. Texture::Filter f;
  439. f.min = f.mag = Texture::FILTER_NEAREST;
  440. gl.setTextureFilter(TEXTURE_2D, f);
  441. Texture::Wrap w;
  442. gl.setTextureWrap(TEXTURE_2D, w);
  443. unusedSRGB = false;
  444. gl.rawTexStorage(TEXTURE_2D, 1, format, unusedSRGB, 1, 1);
  445. }
  446. else
  447. {
  448. glGenRenderbuffers(1, &renderbuffer);
  449. glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
  450. glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1);
  451. }
  452. for (GLenum attachment : fmt.framebufferAttachments)
  453. {
  454. if (attachment == GL_NONE)
  455. continue;
  456. if (readable)
  457. gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0);
  458. else
  459. glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer);
  460. }
  461. supported = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
  462. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
  463. gl.deleteFramebuffer(fbo);
  464. if (texture != 0)
  465. gl.deleteTexture(texture);
  466. if (renderbuffer != 0)
  467. glDeleteRenderbuffers(1, &renderbuffer);
  468. // Cache the result so we don't do this for every isFormatSupported call.
  469. checkedFormats[format].set(readable, true);
  470. supportedFormats[format].set(readable, supported);
  471. return supported;
  472. }
  473. } // opengl
  474. } // graphics
  475. } // love