CCGLUtils.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /****************************************************************************
  2. Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
  3. http://www.cocos2d-x.org
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. ****************************************************************************/
  20. #include "CCGLUtils.h"
  21. #include "platform/CCApplication.h"
  22. #include <stdio.h>
  23. #include <cfloat>
  24. #include <cassert>
  25. #include <array>
  26. NS_CC_BEGIN
  27. // todo: use gl to get the supported number
  28. #define MAX_ATTRIBUTE_UNIT 16
  29. #define MAX_TEXTURE_UNIT 32
  30. //IDEA: Consider to use variable to enable/disable cache state since using macro will not be able to close it if there're serious bugs.
  31. //#undef CC_ENABLE_GL_STATE_CACHE
  32. //#define CC_ENABLE_GL_STATE_CACHE 0
  33. namespace
  34. {
  35. GLint __currentVertexBuffer = -1;
  36. GLint __currentIndexBuffer = -1;
  37. GLint __currentVertexArray = -1;
  38. uint32_t __enabledVertexAttribArrayFlag = 0;
  39. VertexAttributePointerInfo __enabledVertexAttribArrayInfo[MAX_ATTRIBUTE_UNIT];
  40. uint8_t __currentActiveTextureUnit = 0;
  41. std::array<BoundTextureInfo, MAX_TEXTURE_UNIT> __boundTextureInfos;
  42. GLint _currentUnpackAlignment = -1;
  43. bool __unpackFlipY = false;
  44. bool __premultiplyAlpha = false;
  45. GLuint __currentOffScreenFbo = 0;
  46. }
  47. //IDEA: need to consider invoking this after restarting game.
  48. void ccInvalidateStateCache()
  49. {
  50. __currentVertexBuffer = -1;
  51. __currentIndexBuffer = -1;
  52. __currentVertexArray = -1;
  53. __enabledVertexAttribArrayFlag = 0;
  54. for (int i = 0; i < MAX_ATTRIBUTE_UNIT; ++i)
  55. __enabledVertexAttribArrayInfo[i] = VertexAttributePointerInfo();
  56. _currentUnpackAlignment = -1;
  57. __unpackFlipY = false;
  58. __premultiplyAlpha = false;
  59. }
  60. /****************************************************************************************
  61. Texture related
  62. ***************************************************************************************/
  63. void ccActiveTexture(GLenum texture)
  64. {
  65. #if CC_ENABLE_GL_STATE_CACHE
  66. auto activeTextureUnit = texture - GL_TEXTURE0;
  67. if(activeTextureUnit < MAX_TEXTURE_UNIT && activeTextureUnit >= 0)
  68. {
  69. __currentActiveTextureUnit = activeTextureUnit;
  70. }
  71. #endif
  72. glActiveTexture(texture);
  73. }
  74. void ccBindTexture(GLenum target, GLuint texture)
  75. {
  76. #if CC_ENABLE_GL_STATE_CACHE
  77. auto& boundTextureInfo = __boundTextureInfos[__currentActiveTextureUnit];
  78. //todo: support cache
  79. if (boundTextureInfo.texture != texture || boundTextureInfo.target != target) {
  80. boundTextureInfo.texture = texture;
  81. boundTextureInfo.target = target;
  82. }
  83. glBindTexture(target, texture);
  84. #else
  85. glBindTexture(target, texture);
  86. #endif
  87. }
  88. BoundTextureInfo* getBoundTextureInfo(uint32_t textureUnit)
  89. {
  90. return &__boundTextureInfos[textureUnit];
  91. }
  92. /****************************************************************************************
  93. FrameBuffer related
  94. ***************************************************************************************/
  95. void ccBindFramebuffer(GLenum target,GLuint buffer)
  96. {
  97. if(Application::getInstance()->isDownsampleEnabled())
  98. {
  99. if(target == GL_FRAMEBUFFER && buffer == Application::getInstance()->getMainFBO())
  100. {
  101. buffer = __currentOffScreenFbo;
  102. }
  103. }
  104. glBindFramebuffer(target , buffer);
  105. }
  106. void ccActiveOffScreenFramebuffer(GLuint offscreenFbo)
  107. {
  108. __currentOffScreenFbo = offscreenFbo;
  109. }
  110. /****************************************************************************************
  111. Buffer related
  112. ***************************************************************************************/
  113. void ccBindBuffer(GLenum target, GLuint buffer)
  114. {
  115. #if CC_ENABLE_GL_STATE_CACHE
  116. if (target == GL_ARRAY_BUFFER)
  117. {
  118. if (__currentVertexBuffer != buffer)
  119. {
  120. __currentVertexBuffer = buffer;
  121. glBindBuffer(target, buffer);
  122. }
  123. }
  124. else if (target == GL_ELEMENT_ARRAY_BUFFER)
  125. {
  126. if (__currentIndexBuffer != buffer)
  127. {
  128. __currentIndexBuffer = buffer;
  129. glBindBuffer(target, buffer);
  130. }
  131. }
  132. else
  133. {
  134. glBindBuffer(target, buffer);
  135. }
  136. #else
  137. // Should cache it, ccVertexAttribPointer depends on it.
  138. __currentVertexBuffer = buffer;
  139. glBindBuffer(target, buffer);
  140. #endif
  141. }
  142. void ccDeleteBuffers(GLsizei n, const GLuint * buffers)
  143. {
  144. for (GLsizei i = 0; i < n; ++i)
  145. {
  146. if (buffers[i] == __currentVertexBuffer)
  147. __currentVertexBuffer = -1;
  148. else if (buffers[i] == __currentIndexBuffer)
  149. __currentIndexBuffer = -1;
  150. }
  151. glDeleteBuffers(n, buffers);
  152. }
  153. GLint ccGetBoundVertexBuffer()
  154. {
  155. return __currentVertexBuffer;
  156. }
  157. GLint ccGetBoundIndexBuffer()
  158. {
  159. return __currentIndexBuffer;
  160. }
  161. void ccBindVertexArray(GLuint VAO)
  162. {
  163. #if CC_ENABLE_GL_STATE_CACHE
  164. if (__currentVertexArray != VAO)
  165. {
  166. __currentVertexArray = VAO;
  167. glBindVertexArray(VAO);
  168. }
  169. #else
  170. __currentVertexArray = VAO;
  171. glBindVertexArray(VAO);
  172. #endif
  173. }
  174. GLint ccGetBoundVertexArray()
  175. {
  176. return __currentVertexArray;
  177. }
  178. /****************************************************************************************
  179. Vertex attribute related
  180. ***************************************************************************************/
  181. void ccEnableVertexAttribArray(GLuint index)
  182. {
  183. assert(index < MAX_ATTRIBUTE_UNIT);
  184. if (index >= MAX_ATTRIBUTE_UNIT)
  185. return;
  186. uint32_t flag = 1 << index;
  187. if (__enabledVertexAttribArrayFlag & flag)
  188. return;
  189. __enabledVertexAttribArrayFlag |= flag;
  190. glEnableVertexAttribArray(index);
  191. }
  192. void ccDisableVertexAttribArray(GLuint index)
  193. {
  194. if (index >= MAX_ATTRIBUTE_UNIT)
  195. return;
  196. uint32_t flag = 1 << index;
  197. if (__enabledVertexAttribArrayFlag & flag)
  198. {
  199. glDisableVertexAttribArray(index);
  200. __enabledVertexAttribArrayFlag &= ~(1 << index);
  201. }
  202. }
  203. void ccVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer)
  204. {
  205. assert(index < MAX_ATTRIBUTE_UNIT);
  206. if (index >= MAX_ATTRIBUTE_UNIT)
  207. return;
  208. __enabledVertexAttribArrayInfo[index] = VertexAttributePointerInfo(__currentVertexBuffer, index, size, type, normalized, stride, pointer);
  209. // IDEA: should check all the values to determine if need to invoke glVertexAttribPointer or not?
  210. // We don't know if it is a good idea to do it because it needs to compare so many parameters.
  211. glVertexAttribPointer(index, size, type, normalized, stride, pointer);
  212. }
  213. const VertexAttributePointerInfo* getVertexAttribPointerInfo(GLuint index)
  214. {
  215. assert(index < MAX_ATTRIBUTE_UNIT);
  216. if (index >= MAX_ATTRIBUTE_UNIT)
  217. return nullptr;
  218. // The index is not enabled, return null.
  219. if (! (__enabledVertexAttribArrayFlag & (1 << index)) )
  220. return nullptr;
  221. return &__enabledVertexAttribArrayInfo[index];
  222. }
  223. /****************************************************************************************
  224. Other functions.
  225. ***************************************************************************************/
  226. void ccViewport(GLint x, GLint y, GLsizei width, GLsizei height)
  227. {
  228. glViewport(x, y, width, height);
  229. }
  230. void ccScissor(GLint x, GLint y, GLsizei width, GLsizei height)
  231. {
  232. glScissor(x, y, width, height);
  233. }
  234. //IDEA:: ONLY SUPPORT RGBA format now.
  235. static void flipPixelsY(GLubyte *pixels, int bytesPerRow, int rows)
  236. {
  237. if( !pixels ) { return; }
  238. GLuint middle = rows/2;
  239. GLuint intsPerRow = bytesPerRow / sizeof(GLuint);
  240. GLuint remainingBytes = bytesPerRow - intsPerRow * sizeof(GLuint);
  241. for( GLuint rowTop = 0, rowBottom = rows-1; rowTop < middle; rowTop++, rowBottom-- ) {
  242. // Swap bytes in packs of sizeof(GLuint) bytes
  243. GLuint *iTop = (GLuint *)(pixels + rowTop * bytesPerRow);
  244. GLuint *iBottom = (GLuint *)(pixels + rowBottom * bytesPerRow);
  245. GLuint itmp;
  246. GLint n = intsPerRow;
  247. do {
  248. itmp = *iTop;
  249. *iTop++ = *iBottom;
  250. *iBottom++ = itmp;
  251. } while(--n > 0);
  252. // Swap the remaining bytes
  253. GLubyte *bTop = (GLubyte *)iTop;
  254. GLubyte *bBottom = (GLubyte *)iBottom;
  255. GLubyte btmp;
  256. switch( remainingBytes ) {
  257. case 3: btmp = *bTop; *bTop++ = *bBottom; *bBottom++ = btmp;
  258. case 2: btmp = *bTop; *bTop++ = *bBottom; *bBottom++ = btmp;
  259. case 1: btmp = *bTop; *bTop = *bBottom; *bBottom = btmp;
  260. }
  261. }
  262. }
  263. static void flipPixelsYByFormat(GLubyte *pixels, GLenum format, uint32_t width, uint32_t height, uint32_t expectedTotalBytes)
  264. {
  265. bool isSupportFlipY = true;
  266. GLsizei bytesPerRow = 0;
  267. switch (format) {
  268. case GL_RGBA:
  269. bytesPerRow = width * 4;
  270. break;
  271. case GL_RGB:
  272. bytesPerRow = width * 3;
  273. break;
  274. case GL_LUMINANCE_ALPHA:
  275. bytesPerRow = width * 2;
  276. break;
  277. case GL_LUMINANCE:
  278. bytesPerRow = width;
  279. break;
  280. default:
  281. isSupportFlipY = false;
  282. break;
  283. }
  284. if (isSupportFlipY)
  285. {
  286. assert(expectedTotalBytes == bytesPerRow * height);
  287. flipPixelsY((GLubyte*)pixels, bytesPerRow, height);
  288. }
  289. else
  290. {
  291. CCLOGERROR("flipPixelsYByFormat: format: 0x%X doesn't support upackFlipY!\n", format);
  292. }
  293. }
  294. // Lookup tables for fast [un]premultiplied alpha color values
  295. // From https://bugzilla.mozilla.org/show_bug.cgi?id=662130
  296. static GLubyte* __premultiplyTable = nullptr;
  297. static const GLubyte* premultiplyTable()
  298. {
  299. if( !__premultiplyTable ) {
  300. __premultiplyTable = (GLubyte*)malloc(256*256);
  301. unsigned char *data = __premultiplyTable;
  302. for( int a = 0; a <= 255; a++ ) {
  303. for( int c = 0; c <= 255; c++ ) {
  304. data[a*256+c] = (a * c + 254) / 255;
  305. }
  306. }
  307. }
  308. return __premultiplyTable;
  309. }
  310. void premultiplyPixels(const GLubyte *inPixels, GLubyte *outPixels, GLenum format, uint32_t width, uint32_t height, uint32_t expectedTotalBytes)
  311. {
  312. const GLubyte *table = premultiplyTable();
  313. int byteLength = 0;
  314. if( format == GL_RGBA )
  315. {
  316. byteLength = width * height * 4;
  317. assert(byteLength == expectedTotalBytes);
  318. for( int i = 0; i < byteLength; i += 4 ) {
  319. unsigned short a = inPixels[i+3] * 256;
  320. outPixels[i+0] = table[ a + inPixels[i+0] ];
  321. outPixels[i+1] = table[ a + inPixels[i+1] ];
  322. outPixels[i+2] = table[ a + inPixels[i+2] ];
  323. outPixels[i+3] = inPixels[i+3];
  324. }
  325. }
  326. else if ( format == GL_LUMINANCE_ALPHA )
  327. {
  328. byteLength = width * height * 2;
  329. assert(byteLength == expectedTotalBytes);
  330. for( int i = 0; i < byteLength; i += 2 ) {
  331. unsigned short a = inPixels[i+1] * 256;
  332. outPixels[i+0] = table[ a + inPixels[i+0] ];
  333. outPixels[i+1] = inPixels[i+1];
  334. }
  335. }
  336. else
  337. {
  338. CCLOGERROR("premultiplyPixels: format: 0x%X doesn't support upackFlipY!\n", format);
  339. }
  340. }
  341. bool ccIsUnpackFlipY()
  342. {
  343. return __unpackFlipY;
  344. }
  345. bool ccIsPremultiplyAlpha()
  346. {
  347. return __premultiplyAlpha;
  348. }
  349. void ccPixelStorei(GLenum pname, GLint param)
  350. {
  351. if (pname == GL_UNPACK_FLIP_Y_WEBGL)
  352. {
  353. __unpackFlipY = param == 0 ? false : true;
  354. return;
  355. }
  356. else if (pname == GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL)
  357. {
  358. __premultiplyAlpha = param == 0 ? false : true;
  359. return;
  360. }
  361. else if (pname == GL_UNPACK_COLORSPACE_CONVERSION_WEBGL)
  362. {
  363. CCLOGERROR("Warning: UNPACK_COLORSPACE_CONVERSION_WEBGL is unsupported\n");
  364. return;
  365. }
  366. else if (pname == GL_UNPACK_ALIGNMENT)
  367. {
  368. #if CC_ENABLE_GL_STATE_CACHE
  369. if (_currentUnpackAlignment != param)
  370. {
  371. glPixelStorei(pname, param);
  372. _currentUnpackAlignment = param;
  373. }
  374. #else
  375. glPixelStorei(pname, param);
  376. #endif
  377. }
  378. else
  379. {
  380. glPixelStorei(pname, param);
  381. }
  382. }
  383. void ccFlipYOrPremultiptyAlphaIfNeeded(GLenum format, GLsizei width, GLsizei height, uint32_t pixelBytes, GLvoid* pixels)
  384. {
  385. if (pixels != nullptr)
  386. {
  387. if (__unpackFlipY)
  388. {
  389. flipPixelsYByFormat((GLubyte*)pixels, format, width, height, pixelBytes);
  390. }
  391. if (__premultiplyAlpha)
  392. {
  393. premultiplyPixels((GLubyte*)pixels, (GLubyte*)pixels, format, width, height, pixelBytes);
  394. }
  395. }
  396. }
  397. GLint ccGetBufferDataSize()
  398. {
  399. GLint result = 0, size = 0;
  400. for( int i = 0; i < MAX_ATTRIBUTE_UNIT; i++ ) {
  401. const VertexAttributePointerInfo *info = getVertexAttribPointerInfo(i);
  402. if (info != nullptr && info->VBO == __currentVertexBuffer) {
  403. switch (info->type)
  404. {
  405. case GL_BYTE:
  406. case GL_UNSIGNED_BYTE:
  407. size = info->size * sizeof(GLbyte);
  408. break;
  409. case GL_SHORT:
  410. case GL_UNSIGNED_SHORT:
  411. size = info->size * sizeof(GLshort);
  412. break;
  413. case GL_FLOAT:
  414. size = info->size * sizeof(GLclampf);
  415. break;
  416. default:
  417. size = 0;
  418. break;
  419. }
  420. result += size;
  421. }
  422. }
  423. return result;
  424. }
  425. NS_CC_END