394 lines
11 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010—2017 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #include <lol/engine-internal.h>
  13. #include "lolgl.h"
  14. namespace lol
  15. {
  16. //
  17. // The FramebufferData class
  18. // -------------------------
  19. //
  20. class FramebufferData
  21. {
  22. friend class Framebuffer;
  23. ibox2 m_saved_viewport;
  24. ivec2 m_size;
  25. bool m_bound;
  26. GLuint m_fbo, m_texture, m_depth;
  27. };
  28. //
  29. // The FramebufferFormat struct
  30. // ----------------------
  31. //
  32. uint32_t FramebufferFormat::GetFormat()
  33. {
  34. switch (m_format)
  35. {
  36. #if defined HAVE_GLES_2X
  37. /* FIXME: incomplete */
  38. case RGBA_8:
  39. case RGBA_8_I:
  40. case RGBA_8_UI: return GL_RGBA;
  41. #elif defined __APPLE__ && defined __MACH__
  42. case R_8:
  43. case R_8_I:
  44. case R_8_UI:
  45. case R_8_F:
  46. case R_16:
  47. case R_16_I:
  48. case R_16_UI:
  49. case R_16_F:
  50. case R_32_I:
  51. case R_32:
  52. case R_32_UI:
  53. case R_32_F: return GL_RED;
  54. case RG_8:
  55. case RG_8_I:
  56. case RG_8_UI:
  57. case RG_8_F:
  58. case RG_16:
  59. case RG_16_I:
  60. case RG_16_UI:
  61. case RG_16_F:
  62. case RG_32:
  63. case RG_32_I:
  64. case RG_32_UI:
  65. case RG_32_F: return GL_RG;
  66. case RGB_8:
  67. case RGB_8_I:
  68. case RGB_8_UI:
  69. case RGB_8_F:
  70. case RGB_16:
  71. case RGB_16_I:
  72. case RGB_16_UI:
  73. case RGB_16_F:
  74. case RGB_32:
  75. case RGB_32_I:
  76. case RGB_32_UI:
  77. case RGB_32_F: return (m_invert_rgb)?(GL_BGR):(GL_RGB);
  78. case RGBA_8:
  79. case RGBA_8_I:
  80. case RGBA_8_UI:
  81. case RGBA_8_F:
  82. case RGBA_16:
  83. case RGBA_16_I:
  84. case RGBA_16_UI:
  85. case RGBA_16_F:
  86. case RGBA_32:
  87. case RGBA_32_I:
  88. case RGBA_32_UI:
  89. # if defined GL_BGRA
  90. case RGBA_32_F: return (m_invert_rgb)?(GL_BGRA):(GL_RGBA);
  91. # else
  92. case RGBA_32_F: return GL_RGBA;
  93. # endif
  94. #else
  95. case R_8: return GL_R8;
  96. case R_8_I: return GL_R8I;
  97. case R_8_UI: return GL_R8UI;
  98. case R_16: return GL_R16;
  99. case R_16_I: return GL_R16I;
  100. case R_16_UI: return GL_R16UI;
  101. case R_16_F: return GL_R16F;
  102. case R_32_I: return GL_R32I;
  103. case R_32_UI: return GL_R32UI;
  104. case R_32_F: return GL_R32F;
  105. case RG_8: return GL_RG8;
  106. case RG_8_I: return GL_RG8I;
  107. case RG_8_UI: return GL_RG8UI;
  108. case RG_16: return GL_RG16;
  109. case RG_16_I: return GL_RG16I;
  110. case RG_16_UI: return GL_RG16UI;
  111. case RG_16_F: return GL_RG16F;
  112. case RG_32_I: return GL_RG32I;
  113. case RG_32_UI: return GL_RG32UI;
  114. case RG_32_F: return GL_RG32F;
  115. case RGB_8: return GL_RGB8;
  116. case RGB_8_I: return GL_RGB8I;
  117. case RGB_8_UI: return GL_RGB8UI;
  118. case RGB_16: return GL_RGB16;
  119. case RGB_16_I: return GL_RGB16I;
  120. case RGB_16_UI: return GL_RGB16UI;
  121. case RGB_16_F: return GL_RGB16F;
  122. case RGB_32_I: return GL_RGB32I;
  123. case RGB_32_UI: return GL_RGB32UI;
  124. case RGB_32_F: return GL_RGB32F;
  125. case RGBA_8: return GL_RGBA8;
  126. case RGBA_8_I: return GL_RGBA8I;
  127. case RGBA_8_UI: return GL_RGBA8UI;
  128. case RGBA_16: return GL_RGBA16;
  129. case RGBA_16_I: return GL_RGBA16I;
  130. case RGBA_16_UI: return GL_RGBA16UI;
  131. case RGBA_16_F: return GL_RGBA16F;
  132. case RGBA_32_I: return GL_RGBA32I;
  133. case RGBA_32_UI: return GL_RGBA32UI;
  134. case RGBA_32_F: return GL_RGBA32F;
  135. #endif
  136. default:
  137. ASSERT(false, "unknown framebuffer format %d", m_format);
  138. return 0;
  139. }
  140. }
  141. uint32_t FramebufferFormat::GetFormatOrder()
  142. {
  143. switch (m_format)
  144. {
  145. #if defined HAVE_GLES_2X
  146. /* FIXME: incomplete */
  147. case R_8: case RG_8: case RGB_8: case RGBA_8:
  148. case R_8_I: case RG_8_I: case RGB_8_I: case RGBA_8_I:
  149. return GL_BYTE;
  150. case R_8_UI: case RG_8_UI: case RGB_8_UI: case RGBA_8_UI:
  151. return GL_UNSIGNED_BYTE;
  152. #elif defined __APPLE__ && defined __MACH__
  153. case R_8: case RG_8: case RGB_8: case RGBA_8:
  154. case R_8_I: case RG_8_I: case RGB_8_I: case RGBA_8_I:
  155. return GL_BYTE;
  156. case R_8_UI: case RG_8_UI: case RGB_8_UI: case RGBA_8_UI:
  157. return GL_UNSIGNED_BYTE;
  158. case R_16: case RG_16: case RGB_16: case RGBA_16:
  159. case R_16_I: case RG_16_I: case RGB_16_I: case RGBA_16_I:
  160. return GL_SHORT;
  161. case R_16_UI: case RG_16_UI: case RGB_16_UI: case RGBA_16_UI:
  162. return GL_UNSIGNED_SHORT;
  163. case R_16_F: case RG_16_F: case RGB_16_F: case RGBA_16_F:
  164. ASSERT(false, "unsupported framebuffer format order %d", m_format);
  165. return 0;
  166. case R_32_I: case RG_32_I: case RGB_32_I: case RGBA_32_I:
  167. return GL_INT;
  168. case R_32_UI: case RG_32_UI: case RGB_32_UI: case RGBA_32_UI:
  169. return GL_UNSIGNED_INT;
  170. case R_32_F: case RG_32_F: case RGB_32_F: case RGBA_32_F:
  171. return GL_FLOAT;
  172. #else
  173. case R_8: case R_8_I: case R_8_UI: case R_8_F:
  174. case R_16: case R_16_I: case R_16_UI: case R_16_F:
  175. case R_32: case R_32_I: case R_32_UI: case R_32_F:
  176. return GL_RED;
  177. case RG_8: case RG_8_I: case RG_8_UI: case RG_8_F:
  178. case RG_16: case RG_16_I: case RG_16_UI: case RG_16_F:
  179. case RG_32: case RG_32_I: case RG_32_UI: case RG_32_F:
  180. return GL_RG;
  181. case RGB_8: case RGB_8_I: case RGB_8_UI: case RGB_8_F:
  182. case RGB_16: case RGB_16_I: case RGB_16_UI: case RGB_16_F:
  183. case RGB_32: case RGB_32_I: case RGB_32_UI: case RGB_32_F:
  184. return m_invert_rgb ? GL_BGR : GL_RGB;
  185. case RGBA_8: case RGBA_8_I: case RGBA_8_UI: case RGBA_8_F:
  186. case RGBA_16: case RGBA_16_I: case RGBA_16_UI: case RGBA_16_F:
  187. case RGBA_32: case RGBA_32_I: case RGBA_32_UI: case RGBA_32_F:
  188. # if defined GL_BGRA
  189. return (m_invert_rgb)?(GL_BGRA):(GL_RGBA);
  190. # else
  191. return GL_RGBA;
  192. # endif
  193. #endif
  194. default:
  195. ASSERT(false, "unknown framebuffer format order %d", m_format);
  196. return 0;
  197. }
  198. }
  199. //
  200. // The Framebuffer class
  201. // ----------------------
  202. //
  203. Framebuffer::Framebuffer(ivec2 size, FramebufferFormat fbo_format)
  204. : m_data(new FramebufferData)
  205. {
  206. m_data->m_size = size;
  207. m_data->m_bound = false;
  208. #if GL_VERSION_1_1
  209. GLenum internal_format = fbo_format.GetFormat();
  210. GLenum format = fbo_format.GetFormatOrder();
  211. GLenum depth = GL_DEPTH_COMPONENT;
  212. #elif GL_ES_VERSION_2_0
  213. /* In OpenGL ES, internal format and format must match. */
  214. GLenum internal_format = fbo_format.GetFormat();
  215. GLenum format = fbo_format.GetFormat();
  216. GLenum depth = GL_DEPTH_COMPONENT16; /* for WebGL */
  217. #else
  218. /* In OpenGL ES, internal format and format must match. */
  219. GLenum internal_format = fbo_format.GetFormat();
  220. GLenum format = fbo_format.GetFormat();
  221. #endif
  222. GLenum wrapmode = GL_CLAMP_TO_EDGE;
  223. GLenum filtering = GL_LINEAR;
  224. #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
  225. glGenFramebuffers(1, &m_data->m_fbo);
  226. glBindFramebuffer(GL_FRAMEBUFFER, m_data->m_fbo);
  227. #else
  228. glGenFramebuffersOES(1, &m_data->m_fbo);
  229. glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_data->m_fbo);
  230. #endif
  231. glGenTextures(1, &m_data->m_texture);
  232. glActiveTexture(GL_TEXTURE0);
  233. glBindTexture(GL_TEXTURE_2D, m_data->m_texture);
  234. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapmode);
  235. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapmode);
  236. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
  237. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
  238. glTexImage2D(GL_TEXTURE_2D, 0, internal_format, size.x, size.y, 0,
  239. format, GL_UNSIGNED_BYTE, nullptr);
  240. #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
  241. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  242. GL_TEXTURE_2D, m_data->m_texture, 0);
  243. #else
  244. glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_EXT,
  245. GL_TEXTURE_2D, m_data->m_texture, 0);
  246. #endif
  247. m_data->m_depth = GL_INVALID_ENUM;
  248. #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
  249. if (depth != GL_INVALID_ENUM)
  250. {
  251. /* XXX: might not work on GL ES, see
  252. * http://stackoverflow.com/q/4041682/111461
  253. * See also http://qt-project.org/forums/viewthread/11734 */
  254. glGenRenderbuffers(1, &m_data->m_depth);
  255. glBindRenderbuffer(GL_RENDERBUFFER, m_data->m_depth);
  256. glRenderbufferStorage(GL_RENDERBUFFER, depth, size.x, size.y);
  257. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
  258. GL_RENDERBUFFER, m_data->m_depth);
  259. }
  260. #endif
  261. glBindTexture(GL_TEXTURE_2D, 0);
  262. #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
  263. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  264. ASSERT(status == GL_FRAMEBUFFER_COMPLETE,
  265. "invalid framebuffer status 0x%x", status);
  266. #endif
  267. #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
  268. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  269. #else
  270. glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
  271. #endif
  272. }
  273. Framebuffer::~Framebuffer()
  274. {
  275. #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
  276. glDeleteFramebuffers(1, &m_data->m_fbo);
  277. #else
  278. glDeleteFramebuffersOES(1, &m_data->m_fbo);
  279. #endif
  280. glDeleteTextures(1, &m_data->m_texture);
  281. #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
  282. if (m_data->m_depth != GL_INVALID_ENUM)
  283. glDeleteRenderbuffers(1, &m_data->m_depth);
  284. #endif
  285. delete m_data;
  286. }
  287. TextureUniform Framebuffer::GetTextureUniform() const
  288. {
  289. TextureUniform ret;
  290. ret.m_flags = m_data->m_texture;
  291. return ret;
  292. }
  293. ivec2 Framebuffer::GetSize() const
  294. {
  295. return m_data->m_size;
  296. }
  297. image Framebuffer::GetImage() const
  298. {
  299. image ret(m_data->m_size);
  300. u8vec4 *buffer = ret.lock<PixelFormat::RGBA_8>();
  301. glReadPixels(0, 0, m_data->m_size.x, m_data->m_size.y,
  302. GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  303. ret.unlock(buffer);
  304. return ret;
  305. }
  306. void Framebuffer::Bind()
  307. {
  308. ASSERT(!m_data->m_bound, "trying to bind an already bound framebuffer");
  309. #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
  310. glBindFramebuffer(GL_FRAMEBUFFER, m_data->m_fbo);
  311. #else
  312. glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_data->m_fbo);
  313. #endif
  314. /* FIXME: this should be done in the RenderContext object
  315. * instead, maybe by getting rid of Framebuffer::Bind() and
  316. * creating RenderContext::SetFramebuffer() instead. */
  317. m_data->m_saved_viewport = Renderer::Get()->GetViewport();
  318. Renderer::Get()->SetViewport(ibox2(ivec2::zero, m_data->m_size));
  319. m_data->m_bound = true;
  320. }
  321. void Framebuffer::Unbind()
  322. {
  323. ASSERT(m_data->m_bound, "trying to unbind an unbound framebuffer");
  324. #if GL_VERSION_1_1 || GL_ES_VERSION_2_0
  325. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  326. #else
  327. glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
  328. #endif
  329. Renderer::Get()->SetViewport(m_data->m_saved_viewport);
  330. m_data->m_bound = false;
  331. }
  332. } /* namespace lol */