Canvas.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. /**
  2. * Copyright (c) 2006-2015 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 "Image.h"
  22. #include "Graphics.h"
  23. #include "common/Matrix.h"
  24. #include <cstring> // For memcpy
  25. #include <algorithm> // For min/max
  26. namespace love
  27. {
  28. namespace graphics
  29. {
  30. namespace opengl
  31. {
  32. static GLenum createFBO(GLuint &framebuffer, GLuint texture)
  33. {
  34. // get currently bound fbo to reset to it later
  35. GLint current_fbo;
  36. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
  37. glGenFramebuffers(1, &framebuffer);
  38. gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  39. if (texture != 0)
  40. {
  41. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
  42. // Initialize the texture to transparent black.
  43. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  44. glClear(GL_COLOR_BUFFER_BIT);
  45. }
  46. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  47. // unbind framebuffer
  48. gl.bindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
  49. return status;
  50. }
  51. static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer)
  52. {
  53. glGenRenderbuffers(1, &buffer);
  54. glBindRenderbuffer(GL_RENDERBUFFER, buffer);
  55. glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, iformat, width, height);
  56. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, buffer);
  57. glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
  58. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  59. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  60. if (status == GL_FRAMEBUFFER_COMPLETE)
  61. {
  62. // Initialize the buffer to transparent black.
  63. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  64. glClear(GL_COLOR_BUFFER_BIT);
  65. }
  66. else
  67. {
  68. glDeleteRenderbuffers(1, &buffer);
  69. buffer = 0;
  70. }
  71. return status;
  72. }
  73. Canvas *Canvas::current = nullptr;
  74. OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
  75. bool Canvas::screenHasSRGB = false;
  76. int Canvas::canvasCount = 0;
  77. Canvas::Canvas(int width, int height, Format format, int msaa)
  78. : fbo(0)
  79. , resolve_fbo(0)
  80. , texture(0)
  81. , msaa_buffer(0)
  82. , depth_stencil(0)
  83. , format(format)
  84. , requested_samples(msaa)
  85. , actual_samples(0)
  86. , texture_memory(0)
  87. {
  88. this->width = width;
  89. this->height = height;
  90. float w = static_cast<float>(width);
  91. float h = static_cast<float>(height);
  92. // Vertices are ordered for use with triangle strips:
  93. // 0----2
  94. // | / |
  95. // | / |
  96. // 1----3
  97. // world coordinates
  98. vertices[0].x = 0;
  99. vertices[0].y = 0;
  100. vertices[1].x = 0;
  101. vertices[1].y = h;
  102. vertices[2].x = w;
  103. vertices[2].y = 0;
  104. vertices[3].x = w;
  105. vertices[3].y = h;
  106. // texture coordinates
  107. vertices[0].s = 0;
  108. vertices[0].t = 0;
  109. vertices[1].s = 0;
  110. vertices[1].t = 1;
  111. vertices[2].s = 1;
  112. vertices[2].t = 0;
  113. vertices[3].s = 1;
  114. vertices[3].t = 1;
  115. loadVolatile();
  116. ++canvasCount;
  117. }
  118. Canvas::~Canvas()
  119. {
  120. --canvasCount;
  121. // reset framebuffer if still using this one
  122. if (current == this)
  123. stopGrab();
  124. unloadVolatile();
  125. }
  126. bool Canvas::createMSAAFBO(GLenum internalformat)
  127. {
  128. actual_samples = requested_samples;
  129. if (actual_samples <= 1)
  130. {
  131. actual_samples = 0;
  132. return false;
  133. }
  134. // Create our FBO without a texture.
  135. status = createFBO(fbo, 0);
  136. GLuint previous = gl.getDefaultFBO();
  137. if (current != this)
  138. {
  139. if (current != nullptr)
  140. previous = current->fbo;
  141. gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
  142. }
  143. // Create and attach the MSAA buffer for our FBO.
  144. status = createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer);
  145. // Create the FBO used for the MSAA resolve, and attach the texture.
  146. if (status == GL_FRAMEBUFFER_COMPLETE)
  147. status = createFBO(resolve_fbo, texture);
  148. if (status != GL_FRAMEBUFFER_COMPLETE)
  149. {
  150. // Clean up.
  151. glDeleteFramebuffers(1, &fbo);
  152. glDeleteFramebuffers(1, &resolve_fbo);
  153. glDeleteRenderbuffers(1, &msaa_buffer);
  154. fbo = msaa_buffer = resolve_fbo = 0;
  155. actual_samples = 0;
  156. }
  157. if (current != this)
  158. gl.bindFramebuffer(GL_FRAMEBUFFER, previous);
  159. return status == GL_FRAMEBUFFER_COMPLETE;
  160. }
  161. bool Canvas::loadVolatile()
  162. {
  163. OpenGL::TempDebugGroup debuggroup("Canvas load");
  164. fbo = depth_stencil = texture = 0;
  165. resolve_fbo = msaa_buffer = 0;
  166. status = GL_FRAMEBUFFER_COMPLETE;
  167. // glTexImage2D is guaranteed to error in this case.
  168. if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
  169. {
  170. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  171. return false;
  172. }
  173. // getMaxRenderbufferSamples will be 0 on systems that don't support
  174. // multisampled renderbuffers / don't export FBO multisample extensions.
  175. requested_samples = std::min(requested_samples, gl.getMaxRenderbufferSamples());
  176. requested_samples = std::max(requested_samples, 0);
  177. glGenTextures(1, &texture);
  178. gl.bindTexture(texture);
  179. if (GLAD_ANGLE_texture_usage)
  180. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
  181. setFilter(filter);
  182. setWrap(wrap);
  183. GLenum internalformat = GL_RGBA;
  184. GLenum externalformat = GL_RGBA;
  185. GLenum textype = GL_UNSIGNED_BYTE;
  186. convertFormat(format, internalformat, externalformat, textype);
  187. // in GLES2, the internalformat and format params of TexImage have to match.
  188. GLint iformat = (GLint) internalformat;
  189. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
  190. iformat = (GLint) externalformat;
  191. while (glGetError() != GL_NO_ERROR)
  192. /* Clear the error buffer. */;
  193. glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0,
  194. externalformat, textype, nullptr);
  195. if (glGetError() != GL_NO_ERROR)
  196. {
  197. gl.deleteTexture(texture);
  198. texture = 0;
  199. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  200. return false;
  201. }
  202. // Try to create a MSAA FBO if requested. On failure (or no requested MSAA),
  203. // fall back to a regular FBO.
  204. if (!createMSAAFBO(internalformat))
  205. status = createFBO(fbo, texture);
  206. if (status != GL_FRAMEBUFFER_COMPLETE)
  207. {
  208. if (fbo != 0)
  209. {
  210. glDeleteFramebuffers(1, &fbo);
  211. fbo = 0;
  212. }
  213. return false;
  214. }
  215. size_t prevmemsize = texture_memory;
  216. texture_memory = (getFormatBitsPerPixel(format) * width * height) / 8;
  217. if (msaa_buffer != 0)
  218. texture_memory += (texture_memory * actual_samples);
  219. gl.updateTextureMemorySize(prevmemsize, texture_memory);
  220. return true;
  221. }
  222. void Canvas::unloadVolatile()
  223. {
  224. glDeleteFramebuffers(1, &fbo);
  225. glDeleteFramebuffers(1, &resolve_fbo);
  226. glDeleteRenderbuffers(1, &depth_stencil);
  227. glDeleteRenderbuffers(1, &msaa_buffer);
  228. gl.deleteTexture(texture);
  229. fbo = 0;
  230. resolve_fbo = 0;
  231. depth_stencil = 0;
  232. msaa_buffer = 0;
  233. texture = 0;
  234. attachedCanvases.clear();
  235. gl.updateTextureMemorySize(texture_memory, 0);
  236. texture_memory = 0;
  237. }
  238. void Canvas::drawv(const Matrix4 &t, const Vertex *v)
  239. {
  240. OpenGL::TempDebugGroup debuggroup("Canvas draw");
  241. OpenGL::TempTransform transform(gl);
  242. transform.get() *= t;
  243. gl.bindTexture(texture);
  244. gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
  245. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x);
  246. glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s);
  247. gl.prepareDraw();
  248. gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
  249. }
  250. void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  251. {
  252. Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky);
  253. drawv(t, vertices);
  254. }
  255. void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  256. {
  257. Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky);
  258. const Vertex *v = quad->getVertices();
  259. drawv(t, v);
  260. }
  261. void Canvas::setFilter(const Texture::Filter &f)
  262. {
  263. if (!validateFilter(f, false))
  264. throw love::Exception("Invalid texture filter.");
  265. filter = f;
  266. gl.bindTexture(texture);
  267. gl.setTextureFilter(filter);
  268. }
  269. bool Canvas::setWrap(const Texture::Wrap &w)
  270. {
  271. bool success = true;
  272. wrap = w;
  273. if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
  274. && (width != next_p2(width) || height != next_p2(height)))
  275. {
  276. if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP)
  277. success = false;
  278. // If we only have limited NPOT support then the wrap mode must be CLAMP.
  279. wrap.s = wrap.t = WRAP_CLAMP;
  280. }
  281. gl.bindTexture(texture);
  282. gl.setTextureWrap(wrap);
  283. return success;
  284. }
  285. const void *Canvas::getHandle() const
  286. {
  287. return &texture;
  288. }
  289. void Canvas::setupGrab()
  290. {
  291. // already grabbing
  292. if (current == this)
  293. return;
  294. // cleanup after previous Canvas
  295. if (current != nullptr)
  296. {
  297. systemViewport = current->systemViewport;
  298. current->stopGrab(true);
  299. }
  300. else
  301. systemViewport = gl.getViewport();
  302. // indicate we are using this Canvas.
  303. current = this;
  304. // bind the framebuffer object.
  305. gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
  306. gl.setViewport({0, 0, width, height});
  307. // Set up the projection matrix
  308. gl.matrices.projection.push_back(Matrix4::ortho(0.0, (float) width, 0.0, (float) height));
  309. }
  310. void Canvas::startGrab(const std::vector<Canvas *> &canvases)
  311. {
  312. // Whether the new canvas list is different from the old one.
  313. // A more thorough check is done below.
  314. bool canvaseschanged = canvases.size() != attachedCanvases.size();
  315. bool hasSRGBcanvas = (format == FORMAT_SRGB);
  316. if (canvases.size() > 0)
  317. {
  318. if (!isMultiCanvasSupported())
  319. throw love::Exception("Multi-canvas rendering is not supported on this system.");
  320. if ((int) canvases.size() + 1 > gl.getMaxRenderTargets())
  321. throw love::Exception("This system can't simultaneously render to %d canvases.", canvases.size()+1);
  322. if (actual_samples != 0)
  323. throw love::Exception("Multi-canvas rendering is not supported with MSAA.");
  324. }
  325. bool multiformatsupported = isMultiFormatMultiCanvasSupported();
  326. for (size_t i = 0; i < canvases.size(); i++)
  327. {
  328. if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
  329. throw love::Exception("All canvases must have the same dimensions.");
  330. Format otherformat = canvases[i]->getTextureFormat();
  331. if (otherformat != format && !multiformatsupported)
  332. throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats.");
  333. if (canvases[i]->getMSAA() != 0)
  334. throw love::Exception("Multi-canvas rendering is not supported with MSAA.");
  335. if (!canvaseschanged && canvases[i] != attachedCanvases[i])
  336. canvaseschanged = true;
  337. if (otherformat == FORMAT_SRGB)
  338. hasSRGBcanvas = true;
  339. }
  340. OpenGL::TempDebugGroup debuggroup("Canvas set");
  341. setupGrab();
  342. // Make sure the correct sRGB setting is used when drawing to the canvases.
  343. if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
  344. {
  345. if (hasSRGBcanvas && !gl.hasFramebufferSRGB())
  346. gl.setFramebufferSRGB(true);
  347. else if (!hasSRGBcanvas && gl.hasFramebufferSRGB())
  348. gl.setFramebufferSRGB(false);
  349. }
  350. // Don't attach anything if there's nothing to change.
  351. if (!canvaseschanged)
  352. return;
  353. // Attach the canvas textures to the active FBO and set up MRTs.
  354. std::vector<GLenum> drawbuffers;
  355. drawbuffers.reserve(canvases.size() + 1);
  356. drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
  357. // Attach the canvas textures to the currently bound framebuffer.
  358. for (int i = 0; i < (int) canvases.size(); i++)
  359. {
  360. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
  361. GL_TEXTURE_2D, *(GLuint *) canvases[i]->getHandle(), 0);
  362. drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
  363. }
  364. // set up multiple render targets
  365. glDrawBuffers((int) drawbuffers.size(), &drawbuffers[0]);
  366. // We want to avoid reference cycles, so we don't retain the attached
  367. // Canvases here. The code in Graphics::setCanvas retains them.
  368. attachedCanvases = canvases;
  369. }
  370. void Canvas::startGrab()
  371. {
  372. OpenGL::TempDebugGroup debuggroup("Canvas set");
  373. setupGrab();
  374. // Make sure the correct sRGB setting is used when drawing to the canvas.
  375. if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
  376. {
  377. if (format == FORMAT_SRGB && !gl.hasFramebufferSRGB())
  378. gl.setFramebufferSRGB(true);
  379. else if (format != FORMAT_SRGB && gl.hasFramebufferSRGB())
  380. gl.setFramebufferSRGB(false);
  381. }
  382. if (attachedCanvases.size() == 0)
  383. return;
  384. // Make sure the FBO is only using a single draw buffer.
  385. glDrawBuffer(GL_COLOR_ATTACHMENT0);
  386. attachedCanvases.clear();
  387. }
  388. void Canvas::stopGrab(bool switchingToOtherCanvas)
  389. {
  390. // i am not grabbing. leave me alone
  391. if (current != this)
  392. return;
  393. OpenGL::TempDebugGroup debuggroup("Canvas un-set");
  394. // Make sure the canvas texture is up to date if we're using MSAA.
  395. resolveMSAA(false);
  396. gl.matrices.projection.pop_back();
  397. if (!switchingToOtherCanvas)
  398. {
  399. // bind system framebuffer.
  400. gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO());
  401. current = nullptr;
  402. gl.setViewport(systemViewport);
  403. if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
  404. {
  405. if (screenHasSRGB && !gl.hasFramebufferSRGB())
  406. gl.setFramebufferSRGB(true);
  407. else if (!screenHasSRGB && gl.hasFramebufferSRGB())
  408. gl.setFramebufferSRGB(false);
  409. }
  410. }
  411. }
  412. bool Canvas::checkCreateStencil()
  413. {
  414. // Do nothing if we've already created the stencil buffer.
  415. if (depth_stencil != 0)
  416. return true;
  417. OpenGL::TempDebugGroup debuggroup("Canvas create stencil");
  418. if (current != this)
  419. gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
  420. GLenum format = GL_STENCIL_INDEX8;
  421. GLenum attachment = GL_STENCIL_ATTACHMENT;
  422. // Prefer a combined depth/stencil buffer.
  423. if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
  424. || GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
  425. {
  426. format = GL_DEPTH_STENCIL;
  427. attachment = GL_DEPTH_STENCIL_ATTACHMENT;
  428. }
  429. glGenRenderbuffers(1, &depth_stencil);
  430. glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil);
  431. if (requested_samples > 1)
  432. glRenderbufferStorageMultisample(GL_RENDERBUFFER, requested_samples, format, width, height);
  433. else
  434. glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
  435. // Attach the stencil buffer to the framebuffer object.
  436. glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, depth_stencil);
  437. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  438. bool success = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
  439. // We don't want the stencil buffer filled with garbage.
  440. if (success)
  441. glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  442. else
  443. {
  444. glDeleteRenderbuffers(1, &depth_stencil);
  445. depth_stencil = 0;
  446. }
  447. if (current && current != this)
  448. gl.bindFramebuffer(GL_FRAMEBUFFER, current->fbo);
  449. else if (!current)
  450. gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO());
  451. return success;
  452. }
  453. love::image::ImageData *Canvas::newImageData(love::image::Image *image, int x, int y, int w, int h)
  454. {
  455. if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > width || (y + h) > height)
  456. throw love::Exception("Invalid ImageData rectangle dimensions.");
  457. int row = 4 * w;
  458. int size = row * h;
  459. GLubyte *pixels = nullptr;
  460. try
  461. {
  462. pixels = new GLubyte[size];
  463. }
  464. catch (std::bad_alloc &)
  465. {
  466. throw love::Exception("Out of memory.");
  467. }
  468. // Make sure the canvas texture is up to date if we're using MSAA.
  469. if (current == this)
  470. resolveMSAA(false);
  471. // Our texture is attached to 'resolve_fbo' when we use MSAA.
  472. if (resolve_fbo != 0)
  473. gl.bindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
  474. else
  475. gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
  476. glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  477. GLuint prevfbo = current ? current->fbo : gl.getDefaultFBO();
  478. gl.bindFramebuffer(GL_FRAMEBUFFER, prevfbo);
  479. // The new ImageData now owns the pixel data, so we don't delete it here.
  480. return image->newImageData(w, h, pixels, true);
  481. }
  482. bool Canvas::resolveMSAA(bool restoreprev)
  483. {
  484. if (resolve_fbo == 0 || msaa_buffer == 0)
  485. return false;
  486. OpenGL::TempDebugGroup debuggroup("Canvas MSAA resolve");
  487. GLint w = width;
  488. GLint h = height;
  489. // Do the MSAA resolve by blitting the MSAA renderbuffer to the texture.
  490. // For many of the MSAA extensions that add suffixes to the functions, we
  491. // assign function pointers in OpenGL.cpp so we can call the core functions.
  492. gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
  493. gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
  494. if (GLAD_APPLE_framebuffer_multisample)
  495. glResolveMultisampleFramebufferAPPLE();
  496. else
  497. glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
  498. if (restoreprev)
  499. {
  500. GLuint fbo = current ? current->fbo : gl.getDefaultFBO();
  501. gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
  502. }
  503. return true;
  504. }
  505. Canvas::Format Canvas::getSizedFormat(Canvas::Format format)
  506. {
  507. switch (format)
  508. {
  509. case FORMAT_NORMAL:
  510. // 32-bit render targets don't have guaranteed support on OpenGL ES 2.
  511. if (GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8))
  512. return FORMAT_RGBA4;
  513. else
  514. return FORMAT_RGBA8;
  515. case FORMAT_HDR:
  516. return FORMAT_RGBA16F;
  517. default:
  518. return format;
  519. }
  520. }
  521. void Canvas::convertFormat(Canvas::Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type)
  522. {
  523. format = getSizedFormat(format);
  524. externalformat = GL_RGBA;
  525. switch (format)
  526. {
  527. case FORMAT_RGBA4:
  528. internalformat = GL_RGBA4;
  529. type = GL_UNSIGNED_SHORT_4_4_4_4;
  530. break;
  531. case FORMAT_RGB5A1:
  532. internalformat = GL_RGB5_A1;
  533. type = GL_UNSIGNED_SHORT_5_5_5_1;
  534. break;
  535. case FORMAT_RGB565:
  536. internalformat = GL_RGB565;
  537. externalformat = GL_RGB;
  538. type = GL_UNSIGNED_SHORT_5_6_5;
  539. break;
  540. case FORMAT_R8:
  541. internalformat = GL_R8;
  542. externalformat = GL_RED;
  543. type = GL_UNSIGNED_BYTE;
  544. break;
  545. case FORMAT_RG8:
  546. internalformat = GL_RG8;
  547. externalformat = GL_RG;
  548. type = GL_UNSIGNED_BYTE;
  549. break;
  550. case FORMAT_RGBA8:
  551. default:
  552. internalformat = GL_RGBA8;
  553. type = GL_UNSIGNED_BYTE;
  554. break;
  555. case FORMAT_RGB10A2:
  556. internalformat = GL_RGB10_A2;
  557. type = GL_UNSIGNED_INT_2_10_10_10_REV;
  558. break;
  559. case FORMAT_RG11B10F:
  560. internalformat = GL_R11F_G11F_B10F;
  561. externalformat = GL_RGB;
  562. type = GL_UNSIGNED_INT_10F_11F_11F_REV;
  563. break;
  564. case FORMAT_R16F:
  565. internalformat = GL_R16F;
  566. externalformat = GL_RED;
  567. if (GLAD_OES_texture_half_float)
  568. type = GL_HALF_FLOAT_OES;
  569. else if (GLAD_VERSION_1_0)
  570. type = GL_FLOAT;
  571. else
  572. type = GL_HALF_FLOAT;
  573. break;
  574. case FORMAT_RG16F:
  575. internalformat = GL_RG16F;
  576. externalformat = GL_RG;
  577. if (GLAD_OES_texture_half_float)
  578. type = GL_HALF_FLOAT_OES;
  579. else if (GLAD_VERSION_1_0)
  580. type = GL_FLOAT;
  581. else
  582. type = GL_HALF_FLOAT;
  583. break;
  584. case FORMAT_RGBA16F:
  585. internalformat = GL_RGBA16F;
  586. if (GLAD_OES_texture_half_float)
  587. type = GL_HALF_FLOAT_OES;
  588. else if (GLAD_VERSION_1_0)
  589. type = GL_FLOAT;
  590. else
  591. type = GL_HALF_FLOAT;
  592. break;
  593. case FORMAT_R32F:
  594. internalformat = GL_R32F;
  595. externalformat = GL_RED;
  596. type = GL_FLOAT;
  597. break;
  598. case FORMAT_RG32F:
  599. internalformat = GL_RG32F;
  600. externalformat = GL_RG;
  601. type = GL_FLOAT;
  602. break;
  603. case FORMAT_RGBA32F:
  604. internalformat = GL_RGBA32F;
  605. type = GL_FLOAT;
  606. break;
  607. case FORMAT_SRGB:
  608. internalformat = GL_SRGB8_ALPHA8;
  609. type = GL_UNSIGNED_BYTE;
  610. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
  611. externalformat = GL_SRGB_ALPHA;
  612. break;
  613. }
  614. }
  615. size_t Canvas::getFormatBitsPerPixel(Format format)
  616. {
  617. switch (getSizedFormat(format))
  618. {
  619. case FORMAT_R8:
  620. return 8;
  621. case FORMAT_RGBA4:
  622. case FORMAT_RGB5A1:
  623. case FORMAT_RGB565:
  624. case FORMAT_RG8:
  625. case FORMAT_R16F:
  626. return 16;
  627. case FORMAT_RGBA8:
  628. case FORMAT_RGB10A2:
  629. case FORMAT_RG11B10F:
  630. case FORMAT_RG16F:
  631. case FORMAT_R32F:
  632. case FORMAT_SRGB:
  633. default:
  634. return 32;
  635. case FORMAT_RGBA16F:
  636. case FORMAT_RG32F:
  637. return 64;
  638. case FORMAT_RGBA32F:
  639. return 128;
  640. }
  641. }
  642. bool Canvas::isSupported()
  643. {
  644. return GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object;
  645. }
  646. bool Canvas::isMultiCanvasSupported()
  647. {
  648. // system must support at least 4 simultaneous active canvases.
  649. return gl.getMaxRenderTargets() >= 4;
  650. }
  651. bool Canvas::isMultiFormatMultiCanvasSupported()
  652. {
  653. return isMultiCanvasSupported() && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
  654. }
  655. bool Canvas::supportedFormats[] = {false};
  656. bool Canvas::checkedFormats[] = {false};
  657. bool Canvas::isFormatSupported(Canvas::Format format)
  658. {
  659. if (!isSupported())
  660. return false;
  661. bool supported = true;
  662. format = getSizedFormat(format);
  663. switch (format)
  664. {
  665. case FORMAT_RGBA4:
  666. case FORMAT_RGB5A1:
  667. supported = true;
  668. break;
  669. case FORMAT_RGB565:
  670. supported = GLAD_ES_VERSION_2_0 || GLAD_VERSION_4_2 || GLAD_ARB_ES2_compatibility;
  671. break;
  672. case FORMAT_R8:
  673. case FORMAT_RG8:
  674. if (GLAD_VERSION_1_0)
  675. supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_rg;
  676. else if (GLAD_ES_VERSION_2_0)
  677. supported = GLAD_ES_VERSION_3_0 || GLAD_EXT_texture_rg;
  678. break;
  679. case FORMAT_RGBA8:
  680. supported = GLAD_VERSION_1_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8;
  681. break;
  682. case FORMAT_RGB10A2:
  683. supported = GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0;
  684. break;
  685. case FORMAT_RG11B10F:
  686. supported = GLAD_VERSION_3_0 || GLAD_EXT_packed_float || GLAD_APPLE_color_buffer_packed_float;
  687. break;
  688. case FORMAT_R16F:
  689. case FORMAT_RG16F:
  690. if (GLAD_VERSION_1_0)
  691. supported = GLAD_VERSION_3_0 || (GLAD_ARB_texture_float && GLAD_ARB_texture_rg);
  692. else
  693. supported = GLAD_EXT_color_buffer_half_float && (GLAD_ES_VERSION_3_0 || (GLAD_OES_texture_half_float && GLAD_EXT_texture_rg));
  694. break;
  695. case FORMAT_RGBA16F:
  696. if (GLAD_VERSION_1_0)
  697. supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_float;
  698. else if (GLAD_ES_VERSION_2_0)
  699. supported = GLAD_EXT_color_buffer_half_float && (GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float);
  700. break;
  701. case FORMAT_R32F:
  702. case FORMAT_RG32F:
  703. supported = GLAD_VERSION_3_0 || (GLAD_ARB_texture_float && GLAD_ARB_texture_rg);
  704. break;
  705. case FORMAT_RGBA32F:
  706. supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_float;
  707. break;
  708. case FORMAT_SRGB:
  709. if (GLAD_VERSION_1_0)
  710. {
  711. supported = GLAD_VERSION_3_0 || ((GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB)
  712. && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB));
  713. }
  714. else
  715. supported = GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB;
  716. break;
  717. default:
  718. supported = false;
  719. break;
  720. }
  721. if (!supported)
  722. return false;
  723. if (checkedFormats[format])
  724. return supportedFormats[format];
  725. // Even though we might have the necessary OpenGL version or extension,
  726. // drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching
  727. // a texture to a FBO whose format the driver doesn't like. So we should
  728. // test with an actual FBO.
  729. GLenum internalformat = GL_RGBA;
  730. GLenum externalformat = GL_RGBA;
  731. GLenum textype = GL_UNSIGNED_BYTE;
  732. convertFormat(format, internalformat, externalformat, textype);
  733. // in GLES2, the internalformat and format params of TexImage have to match.
  734. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
  735. internalformat = externalformat;
  736. GLuint texture = 0;
  737. glGenTextures(1, &texture);
  738. gl.bindTexture(texture);
  739. Texture::Filter f;
  740. f.min = f.mag = Texture::FILTER_NEAREST;
  741. gl.setTextureFilter(f);
  742. Texture::Wrap w;
  743. gl.setTextureWrap(w);
  744. glTexImage2D(GL_TEXTURE_2D, 0, internalformat, 2, 2, 0, externalformat, textype, nullptr);
  745. GLuint fbo = 0;
  746. supported = (createFBO(fbo, texture) == GL_FRAMEBUFFER_COMPLETE);
  747. glDeleteFramebuffers(1, &fbo);
  748. gl.deleteTexture(texture);
  749. // Cache the result so we don't do this for every isFormatSupported call.
  750. checkedFormats[format] = true;
  751. supportedFormats[format] = supported;
  752. return supported;
  753. }
  754. bool Canvas::getConstant(const char *in, Format &out)
  755. {
  756. return formats.find(in, out);
  757. }
  758. bool Canvas::getConstant(Format in, const char *&out)
  759. {
  760. return formats.find(in, out);
  761. }
  762. StringMap<Canvas::Format, Canvas::FORMAT_MAX_ENUM>::Entry Canvas::formatEntries[] =
  763. {
  764. {"normal", FORMAT_NORMAL},
  765. {"hdr", FORMAT_HDR},
  766. {"rgba4", FORMAT_RGBA4},
  767. {"rgb5a1", FORMAT_RGB5A1},
  768. {"rgb565", FORMAT_RGB565},
  769. {"r8", FORMAT_R8},
  770. {"rg8", FORMAT_RG8},
  771. {"rgba8", FORMAT_RGBA8},
  772. {"rgb10a2", FORMAT_RGB10A2},
  773. {"rg11b10f", FORMAT_RG11B10F},
  774. {"r16f", FORMAT_R16F},
  775. {"rg16f", FORMAT_RG16F},
  776. {"rgba16f", FORMAT_RGBA16F},
  777. {"r32f", FORMAT_R32F},
  778. {"rg32f", FORMAT_RG32F},
  779. {"rgba32f", FORMAT_RGBA32F},
  780. {"srgb", FORMAT_SRGB},
  781. };
  782. StringMap<Canvas::Format, Canvas::FORMAT_MAX_ENUM> Canvas::formats(Canvas::formatEntries, sizeof(Canvas::formatEntries));
  783. } // opengl
  784. } // graphics
  785. } // love