Canvas.cpp 24 KB

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