Canvas.cpp 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. /**
  2. * Copyright (c) 2006-2014 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 "common/config.h"
  25. #include <cstring> // For memcpy
  26. #include <limits>
  27. namespace love
  28. {
  29. namespace graphics
  30. {
  31. namespace opengl
  32. {
  33. // strategy for fbo creation, interchangable at runtime:
  34. // none, opengl >= 3.0, extensions
  35. struct FramebufferStrategy
  36. {
  37. virtual ~FramebufferStrategy() {}
  38. /// create a new framebuffer and texture
  39. /**
  40. * @param[out] framebuffer Framebuffer name
  41. * @param[in] texture Texture name
  42. * @return Creation status
  43. */
  44. virtual GLenum createFBO(GLuint &, GLuint)
  45. {
  46. return GL_FRAMEBUFFER_UNSUPPORTED;
  47. }
  48. /// Create a stencil buffer and attach it to the active framebuffer object
  49. /**
  50. * @param[in] width Width of the stencil buffer
  51. * @param[in] height Height of the stencil buffer
  52. * @param[in] samples Number of samples to use
  53. * @param[out] stencil Name for stencil buffer
  54. * @return Whether the stencil buffer was successfully created
  55. **/
  56. virtual bool createStencil(int, int, int, GLuint &)
  57. {
  58. return false;
  59. }
  60. /// Create a MSAA renderbuffer and attach it to the active FBO.
  61. /**
  62. * @param[in] width Width of the MSAA buffer
  63. * @param[in] height Height of the MSAA buffer
  64. * @param[inout] samples Number of samples to use
  65. * @param[in] internalformat The internal format to use for the buffer
  66. * @param[out] buffer Name for the MSAA buffer
  67. * @return Whether the MSAA buffer was successfully created and attached
  68. **/
  69. virtual bool createMSAABuffer(int, int, int &, GLenum, GLuint &)
  70. {
  71. return false;
  72. }
  73. /// remove objects
  74. /**
  75. * @param[in] framebuffer Framebuffer name
  76. * @param[in] depth_stencil Name for packed depth and stencil buffer
  77. */
  78. virtual void deleteFBO(GLuint, GLuint, GLuint) {}
  79. virtual void bindFBO(GLuint) {}
  80. /// attach additional canvases to the active framebuffer for rendering
  81. /**
  82. * @param[in] canvases List of canvases to attach
  83. **/
  84. virtual void setAttachments(const std::vector<Canvas *> &) {}
  85. /// stop using all additional attached canvases
  86. virtual void setAttachments() {}
  87. };
  88. struct FramebufferStrategyGL3 : public FramebufferStrategy
  89. {
  90. virtual GLenum createFBO(GLuint &framebuffer, GLuint texture)
  91. {
  92. // get currently bound fbo to reset to it later
  93. GLint current_fbo;
  94. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
  95. // create framebuffer
  96. glGenFramebuffers(1, &framebuffer);
  97. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  98. if (texture != 0)
  99. {
  100. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  101. GL_TEXTURE_2D, texture, 0);
  102. }
  103. // check status
  104. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  105. // unbind framebuffer
  106. glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
  107. return status;
  108. }
  109. virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
  110. {
  111. // create combined depth/stencil buffer
  112. glDeleteRenderbuffers(1, &stencil);
  113. glGenRenderbuffers(1, &stencil);
  114. glBindRenderbuffer(GL_RENDERBUFFER, stencil);
  115. if (samples > 1)
  116. glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH_STENCIL, width, height);
  117. else
  118. glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
  119. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
  120. GL_RENDERBUFFER, stencil);
  121. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  122. // check status
  123. if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
  124. {
  125. glDeleteRenderbuffers(1, &stencil);
  126. stencil = 0;
  127. return false;
  128. }
  129. return true;
  130. }
  131. virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer)
  132. {
  133. glGenRenderbuffers(1, &buffer);
  134. glBindRenderbuffer(GL_RENDERBUFFER, buffer);
  135. glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat,
  136. width, height);
  137. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  138. GL_RENDERBUFFER, buffer);
  139. glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
  140. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  141. if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
  142. {
  143. glDeleteRenderbuffers(1, &buffer);
  144. buffer = 0;
  145. return false;
  146. }
  147. return true;
  148. }
  149. virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer)
  150. {
  151. if (depth_stencil != 0)
  152. glDeleteRenderbuffers(1, &depth_stencil);
  153. if (msaa_buffer != 0)
  154. glDeleteRenderbuffers(1, &msaa_buffer);
  155. if (framebuffer != 0)
  156. glDeleteFramebuffers(1, &framebuffer);
  157. }
  158. virtual void bindFBO(GLuint framebuffer)
  159. {
  160. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  161. }
  162. virtual void setAttachments()
  163. {
  164. // set a single render target
  165. glDrawBuffer(GL_COLOR_ATTACHMENT0);
  166. }
  167. virtual void setAttachments(const std::vector<Canvas *> &canvases)
  168. {
  169. if (canvases.size() == 0)
  170. {
  171. setAttachments();
  172. return;
  173. }
  174. std::vector<GLenum> drawbuffers;
  175. drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
  176. // Attach the canvas textures to the currently bound framebuffer.
  177. for (size_t i = 0; i < canvases.size(); i++)
  178. {
  179. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
  180. GL_TEXTURE_2D, canvases[i]->getGLTexture(), 0);
  181. drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
  182. }
  183. // set up multiple render targets
  184. if (GLEE_VERSION_2_0)
  185. glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
  186. else if (GLEE_ARB_draw_buffers)
  187. glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
  188. }
  189. };
  190. struct FramebufferStrategyPackedEXT : public FramebufferStrategy
  191. {
  192. virtual GLenum createFBO(GLuint &framebuffer, GLuint texture)
  193. {
  194. GLint current_fbo;
  195. glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &current_fbo);
  196. // create framebuffer
  197. glGenFramebuffersEXT(1, &framebuffer);
  198. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
  199. glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
  200. GL_TEXTURE_2D, texture, 0);
  201. // check status
  202. GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
  203. // unbind framebuffer
  204. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint) current_fbo);
  205. return status;
  206. }
  207. virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
  208. {
  209. // create combined depth/stencil buffer
  210. glDeleteRenderbuffersEXT(1, &stencil);
  211. glGenRenderbuffersEXT(1, &stencil);
  212. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
  213. if (samples > 1)
  214. {
  215. glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
  216. GL_DEPTH_STENCIL, width, height);
  217. }
  218. else
  219. {
  220. glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
  221. width, height);
  222. }
  223. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
  224. GL_RENDERBUFFER_EXT, stencil);
  225. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
  226. // check status
  227. if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
  228. {
  229. glDeleteRenderbuffersEXT(1, &stencil);
  230. stencil = 0;
  231. return false;
  232. }
  233. return true;
  234. }
  235. virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer)
  236. {
  237. if (!GLEE_EXT_framebuffer_multisample)
  238. return false;
  239. glGenRenderbuffersEXT(1, &buffer);
  240. glBindRenderbufferEXT(GL_RENDERBUFFER, buffer);
  241. glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
  242. internalformat, width, height);
  243. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  244. GL_RENDERBUFFER, buffer);
  245. glGetRenderbufferParameterivEXT(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
  246. glBindRenderbufferEXT(GL_RENDERBUFFER, 0);
  247. if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
  248. {
  249. glDeleteRenderbuffersEXT(1, &buffer);
  250. buffer = 0;
  251. return false;
  252. }
  253. return true;
  254. }
  255. virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer)
  256. {
  257. if (depth_stencil != 0)
  258. glDeleteRenderbuffersEXT(1, &depth_stencil);
  259. if (msaa_buffer != 0)
  260. glDeleteRenderbuffersEXT(1, &msaa_buffer);
  261. if (framebuffer != 0)
  262. glDeleteFramebuffersEXT(1, &framebuffer);
  263. }
  264. virtual void bindFBO(GLuint framebuffer)
  265. {
  266. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
  267. }
  268. virtual void setAttachments()
  269. {
  270. // set a single render target
  271. glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
  272. }
  273. virtual void setAttachments(const std::vector<Canvas *> &canvases)
  274. {
  275. if (canvases.size() == 0)
  276. {
  277. setAttachments();
  278. return;
  279. }
  280. std::vector<GLenum> drawbuffers;
  281. drawbuffers.push_back(GL_COLOR_ATTACHMENT0_EXT);
  282. // Attach the canvas textures to the currently bound framebuffer.
  283. for (size_t i = 0; i < canvases.size(); i++)
  284. {
  285. glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + i,
  286. GL_TEXTURE_2D, canvases[i]->getGLTexture(), 0);
  287. drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
  288. }
  289. // set up multiple render targets
  290. if (GLEE_VERSION_2_0)
  291. glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
  292. else if (GLEE_ARB_draw_buffers)
  293. glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
  294. }
  295. };
  296. struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
  297. {
  298. virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
  299. {
  300. // create stencil buffer
  301. glDeleteRenderbuffersEXT(1, &stencil);
  302. glGenRenderbuffersEXT(1, &stencil);
  303. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
  304. if (samples > 1)
  305. {
  306. glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
  307. GL_STENCIL_INDEX, width, height);
  308. }
  309. else
  310. {
  311. glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
  312. width, height);
  313. }
  314. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
  315. GL_RENDERBUFFER_EXT, stencil);
  316. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
  317. // check status
  318. if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
  319. {
  320. glDeleteRenderbuffersEXT(1, &stencil);
  321. stencil = 0;
  322. return false;
  323. }
  324. return true;
  325. }
  326. bool isSupported()
  327. {
  328. GLuint fb = 0, stencil = 0;
  329. GLenum status = createFBO(fb, 0);
  330. deleteFBO(fb, stencil, 0);
  331. return status == GL_FRAMEBUFFER_COMPLETE;
  332. }
  333. };
  334. FramebufferStrategy *strategy = nullptr;
  335. FramebufferStrategy strategyNone;
  336. FramebufferStrategyGL3 strategyGL3;
  337. FramebufferStrategyPackedEXT strategyPackedEXT;
  338. FramebufferStrategyEXT strategyEXT;
  339. Canvas *Canvas::current = nullptr;
  340. OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
  341. bool Canvas::screenHasSRGB = false;
  342. static void getStrategy()
  343. {
  344. if (!strategy)
  345. {
  346. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
  347. strategy = &strategyGL3;
  348. else if (GLEE_EXT_framebuffer_object && GLEE_EXT_packed_depth_stencil)
  349. strategy = &strategyPackedEXT;
  350. else if (GLEE_EXT_framebuffer_object && strategyEXT.isSupported())
  351. strategy = &strategyEXT;
  352. else
  353. strategy = &strategyNone;
  354. }
  355. }
  356. Canvas::Canvas(int width, int height, Texture::Format format, int fsaa)
  357. : fbo(0)
  358. , resolve_fbo(0)
  359. , texture(0)
  360. , fsaa_buffer(0)
  361. , depth_stencil(0)
  362. , format(format)
  363. , fsaa_samples(fsaa)
  364. , fsaa_dirty(false)
  365. {
  366. this->width = width;
  367. this->height = height;
  368. float w = static_cast<float>(width);
  369. float h = static_cast<float>(height);
  370. // world coordinates
  371. vertices[0].x = 0;
  372. vertices[0].y = 0;
  373. vertices[1].x = 0;
  374. vertices[1].y = h;
  375. vertices[2].x = w;
  376. vertices[2].y = h;
  377. vertices[3].x = w;
  378. vertices[3].y = 0;
  379. // texture coordinates
  380. vertices[0].s = 0;
  381. vertices[0].t = 0;
  382. vertices[1].s = 0;
  383. vertices[1].t = 1;
  384. vertices[2].s = 1;
  385. vertices[2].t = 1;
  386. vertices[3].s = 1;
  387. vertices[3].t = 0;
  388. getStrategy();
  389. loadVolatile();
  390. }
  391. Canvas::~Canvas()
  392. {
  393. // reset framebuffer if still using this one
  394. if (current == this)
  395. stopGrab();
  396. unloadVolatile();
  397. }
  398. bool Canvas::createFSAAFBO(GLenum internalformat)
  399. {
  400. // Create our FBO without a texture.
  401. status = strategy->createFBO(fbo, 0);
  402. GLuint previous = 0;
  403. if (current != this)
  404. {
  405. if (current != nullptr)
  406. previous = current->fbo;
  407. strategy->bindFBO(fbo);
  408. }
  409. // Create and attach the MSAA buffer for our FBO.
  410. if (strategy->createMSAABuffer(width, height, fsaa_samples, internalformat, fsaa_buffer))
  411. status = GL_FRAMEBUFFER_COMPLETE;
  412. else
  413. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  414. // Create the FBO used for the MSAA resolve, and attach the texture.
  415. if (status == GL_FRAMEBUFFER_COMPLETE)
  416. status = strategy->createFBO(resolve_fbo, texture);
  417. if (status != GL_FRAMEBUFFER_COMPLETE)
  418. {
  419. // Clean up.
  420. strategy->deleteFBO(fbo, 0, fsaa_buffer);
  421. strategy->deleteFBO(resolve_fbo, 0, 0);
  422. fbo = fsaa_buffer = resolve_fbo = 0;
  423. fsaa_samples = 0;
  424. }
  425. if (current != this)
  426. strategy->bindFBO(previous);
  427. return status == GL_FRAMEBUFFER_COMPLETE;
  428. }
  429. bool Canvas::loadVolatile()
  430. {
  431. fbo = depth_stencil = texture = 0;
  432. resolve_fbo = fsaa_buffer = 0;
  433. status = GL_FRAMEBUFFER_COMPLETE;
  434. // glTexImage2D is guaranteed to error in this case.
  435. if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
  436. {
  437. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  438. return false;
  439. }
  440. glGenTextures(1, &texture);
  441. gl.bindTexture(texture);
  442. setFilter(filter);
  443. setWrap(wrap);
  444. GLint internalformat;
  445. GLenum textype;
  446. switch (format)
  447. {
  448. case Texture::FORMAT_HDR:
  449. internalformat = GL_RGBA16F;
  450. textype = GL_FLOAT;
  451. break;
  452. case Texture::FORMAT_SRGB:
  453. internalformat = GL_SRGB8_ALPHA8;
  454. textype = GL_UNSIGNED_BYTE;
  455. break;
  456. case Texture::FORMAT_NORMAL:
  457. default:
  458. internalformat = GL_RGBA8;
  459. textype = GL_UNSIGNED_BYTE;
  460. }
  461. while (glGetError() != GL_NO_ERROR)
  462. /* Clear the error buffer. */;
  463. glTexImage2D(GL_TEXTURE_2D,
  464. 0,
  465. internalformat,
  466. width, height,
  467. 0,
  468. GL_RGBA,
  469. textype,
  470. nullptr);
  471. if (glGetError() != GL_NO_ERROR)
  472. {
  473. status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
  474. return false;
  475. }
  476. int max_samples = 0;
  477. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object
  478. || GLEE_EXT_framebuffer_multisample)
  479. {
  480. glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
  481. }
  482. if (fsaa_samples > max_samples)
  483. fsaa_samples = max_samples;
  484. // Try to create a FSAA FBO if requested.
  485. bool fsaasuccess = false;
  486. if (fsaa_samples > 1)
  487. fsaasuccess = createFSAAFBO(internalformat);
  488. // On failure (or no requested FSAA), fall back to a regular FBO.
  489. if (!fsaasuccess)
  490. status = strategy->createFBO(fbo, texture);
  491. if (status != GL_FRAMEBUFFER_COMPLETE)
  492. return false;
  493. clear(Color(0, 0, 0, 0));
  494. fsaa_dirty = (fsaa_buffer != 0);
  495. return true;
  496. }
  497. void Canvas::unloadVolatile()
  498. {
  499. strategy->deleteFBO(fbo, depth_stencil, fsaa_buffer);
  500. strategy->deleteFBO(resolve_fbo, 0, 0);
  501. gl.deleteTexture(texture);
  502. fbo = depth_stencil = texture = 0;
  503. resolve_fbo = fsaa_buffer = 0;
  504. for (size_t i = 0; i < attachedCanvases.size(); i++)
  505. attachedCanvases[i]->release();
  506. attachedCanvases.clear();
  507. }
  508. void Canvas::drawv(const Matrix &t, const Vertex *v)
  509. {
  510. glPushMatrix();
  511. glMultMatrixf((const GLfloat *)t.getElements());
  512. predraw();
  513. glEnableClientState(GL_VERTEX_ARRAY);
  514. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  515. glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].x);
  516. glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
  517. gl.prepareDraw();
  518. glDrawArrays(GL_QUADS, 0, 4);
  519. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  520. glDisableClientState(GL_VERTEX_ARRAY);
  521. postdraw();
  522. glPopMatrix();
  523. }
  524. void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  525. {
  526. static Matrix t;
  527. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  528. drawv(t, vertices);
  529. }
  530. void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  531. {
  532. static Matrix t;
  533. t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
  534. const Vertex *v = quad->getVertices();
  535. drawv(t, v);
  536. }
  537. void Canvas::setFilter(const Texture::Filter &f)
  538. {
  539. filter = f;
  540. gl.bindTexture(texture);
  541. gl.setTextureFilter(filter);
  542. }
  543. void Canvas::setWrap(const Texture::Wrap &w)
  544. {
  545. wrap = w;
  546. gl.bindTexture(texture);
  547. gl.setTextureWrap(wrap);
  548. }
  549. GLuint Canvas::getGLTexture() const
  550. {
  551. return texture;
  552. }
  553. void Canvas::predraw()
  554. {
  555. // We need to make sure the texture is up-to-date by resolving the MSAA
  556. // buffer (which we render to when the canvas is active) to it.
  557. resolveMSAA();
  558. gl.bindTexture(texture);
  559. }
  560. void Canvas::setupGrab()
  561. {
  562. // already grabbing
  563. if (current == this)
  564. return;
  565. // cleanup after previous Canvas
  566. if (current != nullptr)
  567. {
  568. systemViewport = current->systemViewport;
  569. current->stopGrab(true);
  570. }
  571. else
  572. systemViewport = gl.getViewport();
  573. // indicate we are using this Canvas.
  574. current = this;
  575. // bind the framebuffer object.
  576. strategy->bindFBO(fbo);
  577. gl.setViewport(OpenGL::Viewport(0, 0, width, height));
  578. // Reset the projection matrix
  579. glMatrixMode(GL_PROJECTION);
  580. glPushMatrix();
  581. glLoadIdentity();
  582. // Set up orthographic view (no depth)
  583. glOrtho(0.0, width, 0.0, height, -1.0, 1.0);
  584. // Switch back to modelview matrix
  585. glMatrixMode(GL_MODELVIEW);
  586. // Make sure the correct sRGB setting is used when drawing to the canvas.
  587. if (format == FORMAT_SRGB)
  588. glEnable(GL_FRAMEBUFFER_SRGB);
  589. else if (screenHasSRGB)
  590. glDisable(GL_FRAMEBUFFER_SRGB);
  591. if (fsaa_buffer != 0)
  592. fsaa_dirty = true;
  593. }
  594. void Canvas::startGrab(const std::vector<Canvas *> &canvases)
  595. {
  596. // Whether the new canvas list is different from the old one.
  597. // A more thorough check is done below.
  598. bool canvaseschanged = canvases.size() != attachedCanvases.size();
  599. if (canvases.size() > 0)
  600. {
  601. if (!isMultiCanvasSupported())
  602. throw love::Exception("Multi-canvas rendering is not supported on this system.");
  603. if ((int) canvases.size() + 1 > gl.getMaxRenderTargets())
  604. throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1);
  605. if (fsaa_samples != 0)
  606. throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
  607. }
  608. for (size_t i = 0; i < canvases.size(); i++)
  609. {
  610. if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
  611. throw love::Exception("All canvas arguments must have the same dimensions.");
  612. if (canvases[i]->getTextureFormat() != format)
  613. throw love::Exception("All canvas arguments must have the same texture format.");
  614. if (canvases[i]->getFSAA() != 0)
  615. throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
  616. if (!canvaseschanged && canvases[i] != attachedCanvases[i])
  617. canvaseschanged = true;
  618. }
  619. setupGrab();
  620. // Don't attach anything if there's nothing to change.
  621. if (!canvaseschanged)
  622. return;
  623. // Attach the canvas textures to the active FBO and set up MRTs.
  624. strategy->setAttachments(canvases);
  625. for (size_t i = 0; i < canvases.size(); i++)
  626. canvases[i]->retain();
  627. for (size_t i = 0; i < attachedCanvases.size(); i++)
  628. attachedCanvases[i]->release();
  629. attachedCanvases = canvases;
  630. }
  631. void Canvas::startGrab()
  632. {
  633. setupGrab();
  634. if (attachedCanvases.size() == 0)
  635. return;
  636. // make sure the FBO is only using a single canvas
  637. strategy->setAttachments();
  638. // release any previously attached canvases
  639. for (size_t i = 0; i < attachedCanvases.size(); i++)
  640. attachedCanvases[i]->release();
  641. attachedCanvases.clear();
  642. }
  643. void Canvas::stopGrab(bool switchingToOtherCanvas)
  644. {
  645. // i am not grabbing. leave me alone
  646. if (current != this)
  647. return;
  648. glMatrixMode(GL_PROJECTION);
  649. glPopMatrix();
  650. glMatrixMode(GL_MODELVIEW);
  651. if (switchingToOtherCanvas)
  652. {
  653. if (format == FORMAT_SRGB)
  654. glDisable(GL_FRAMEBUFFER_SRGB);
  655. }
  656. else
  657. {
  658. // bind system framebuffer.
  659. strategy->bindFBO(0);
  660. current = nullptr;
  661. gl.setViewport(systemViewport);
  662. if (format == FORMAT_SRGB && !screenHasSRGB)
  663. glDisable(GL_FRAMEBUFFER_SRGB);
  664. else if (format != FORMAT_SRGB && screenHasSRGB)
  665. glEnable(GL_FRAMEBUFFER_SRGB);
  666. }
  667. }
  668. void Canvas::clear(Color c)
  669. {
  670. if (strategy == &strategyNone)
  671. return;
  672. GLuint previous = 0;
  673. if (current != this)
  674. {
  675. if (current != nullptr)
  676. previous = current->fbo;
  677. strategy->bindFBO(fbo);
  678. }
  679. GLfloat glcolor[] = {c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f};
  680. // We don't need to worry about multiple FBO attachments or global clear
  681. // color state when OpenGL 3.0+ is supported.
  682. if (GLEE_VERSION_3_0)
  683. {
  684. glClearBufferfv(GL_COLOR, 0, glcolor);
  685. if (depth_stencil != 0)
  686. {
  687. GLint stencilvalue = 0;
  688. glClearBufferiv(GL_STENCIL, 0, &stencilvalue);
  689. }
  690. }
  691. else
  692. {
  693. // glClear will clear all active draw buffers, so we need to temporarily
  694. // detach any other canvases (when MRT is being used.)
  695. if (attachedCanvases.size() > 0)
  696. strategy->setAttachments();
  697. // Don't use the state-shadowed gl.setClearColor because we want to save
  698. // the previous clear color.
  699. glClearColor(glcolor[0], glcolor[1], glcolor[2], glcolor[3]);
  700. glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  701. if (attachedCanvases.size() > 0)
  702. strategy->setAttachments(attachedCanvases);
  703. // Restore the global clear color.
  704. gl.setClearColor(gl.getClearColor());
  705. }
  706. if (current != this)
  707. strategy->bindFBO(previous);
  708. if (fsaa_buffer != 0)
  709. fsaa_dirty = true;
  710. }
  711. bool Canvas::checkCreateStencil()
  712. {
  713. // Do nothing if we've already created the stencil buffer.
  714. if (depth_stencil != 0)
  715. return true;
  716. if (current != this)
  717. strategy->bindFBO(fbo);
  718. bool success = strategy->createStencil(width, height, fsaa_samples, depth_stencil);
  719. if (current && current != this)
  720. strategy->bindFBO(current->fbo);
  721. else if (!current)
  722. strategy->bindFBO(0);
  723. return success;
  724. }
  725. love::image::ImageData *Canvas::getImageData(love::image::Image *image)
  726. {
  727. resolveMSAA();
  728. int row = 4 * width;
  729. int size = row * height;
  730. GLubyte *pixels = new GLubyte[size];
  731. // Our texture is attached to 'resolve_fbo' when we use MSAA.
  732. if (fsaa_samples > 1 && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
  733. glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
  734. else if (fsaa_samples > 1 && GLEE_EXT_framebuffer_multisample)
  735. glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo);
  736. else
  737. strategy->bindFBO(fbo);
  738. glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  739. if (current)
  740. strategy->bindFBO(current->fbo);
  741. else
  742. strategy->bindFBO(0);
  743. // The new ImageData now owns the pixel data, so we don't delete it here.
  744. love::image::ImageData *img = image->newImageData(width, height, (void *)pixels, true);
  745. return img;
  746. }
  747. void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
  748. {
  749. resolveMSAA();
  750. // Our texture is attached to 'resolve_fbo' when we use MSAA.
  751. if (fsaa_samples > 1 && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
  752. glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
  753. else if (fsaa_samples > 1 && GLEE_EXT_framebuffer_multisample)
  754. glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo);
  755. else if (current != this)
  756. strategy->bindFBO(fbo);
  757. glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba);
  758. if (current && current != this)
  759. strategy->bindFBO(current->fbo);
  760. else if (!current)
  761. strategy->bindFBO(0);
  762. }
  763. bool Canvas::resolveMSAA()
  764. {
  765. if (resolve_fbo == 0 || fsaa_buffer == 0)
  766. return false;
  767. if (!fsaa_dirty)
  768. return true;
  769. GLuint previous = 0;
  770. if (current != nullptr)
  771. previous = current->fbo;
  772. // Do the MSAA resolve by blitting the MSAA renderbuffer to the texture.
  773. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
  774. {
  775. glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
  776. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
  777. glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
  778. GL_COLOR_BUFFER_BIT, GL_NEAREST);
  779. }
  780. else if (GLEE_EXT_framebuffer_multisample && GLEE_EXT_framebuffer_blit)
  781. {
  782. glBindFramebufferEXT(GL_READ_FRAMEBUFFER, fbo);
  783. glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, resolve_fbo);
  784. glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height,
  785. GL_COLOR_BUFFER_BIT, GL_NEAREST);
  786. }
  787. else
  788. return false;
  789. strategy->bindFBO(previous);
  790. if (current != this)
  791. fsaa_dirty = false;
  792. return true;
  793. }
  794. bool Canvas::isSupported()
  795. {
  796. getStrategy();
  797. return (strategy != &strategyNone);
  798. }
  799. bool Canvas::isHDRSupported()
  800. {
  801. return GLEE_VERSION_3_0 || (isSupported() && GLEE_ARB_texture_float);
  802. }
  803. bool Canvas::isSRGBSupported()
  804. {
  805. if (GLEE_VERSION_3_0)
  806. return true;
  807. if (!isSupported())
  808. return false;
  809. return (GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB)
  810. && GLEE_EXT_texture_sRGB;
  811. }
  812. bool Canvas::isMultiCanvasSupported()
  813. {
  814. // system must support at least 4 simultanious active canvases.
  815. return gl.getMaxRenderTargets() >= 4;
  816. }
  817. void Canvas::bindDefaultCanvas()
  818. {
  819. if (current != nullptr)
  820. current->stopGrab();
  821. }
  822. } // opengl
  823. } // graphics
  824. } // love