BsGLPixelBuffer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsGLPixelBuffer.h"
  4. #include "BsGLTexture.h"
  5. #include "BsGLSupport.h"
  6. #include "BsGLPixelFormat.h"
  7. #include "Error/BsException.h"
  8. #include "Utility/BsBitwise.h"
  9. #include "BsGLRenderTexture.h"
  10. #include "Profiling/BsRenderStats.h"
  11. namespace bs { namespace ct
  12. {
  13. GLPixelBuffer::GLPixelBuffer(UINT32 inWidth, UINT32 inHeight, UINT32 inDepth, PixelFormat inFormat, GpuBufferUsage usage)
  14. : mUsage(usage), mIsLocked(false), mWidth(inWidth), mHeight(inHeight), mDepth(inDepth), mFormat(inFormat)
  15. , mBuffer(inWidth, inHeight, inDepth, inFormat)
  16. {
  17. mSizeInBytes = mHeight*mWidth*PixelUtil::getNumElemBytes(mFormat);
  18. mCurrentLockOptions = (GpuLockOptions)0;
  19. }
  20. GLPixelBuffer::~GLPixelBuffer()
  21. {
  22. mBuffer.freeInternalBuffer();
  23. }
  24. void GLPixelBuffer::allocateBuffer()
  25. {
  26. if(mBuffer.getData())
  27. return;
  28. mBuffer.allocateInternalBuffer();
  29. // TODO: use PBO if we're HBU_DYNAMIC
  30. }
  31. void GLPixelBuffer::freeBuffer()
  32. {
  33. if(mUsage & GBU_STATIC)
  34. mBuffer.freeInternalBuffer();
  35. }
  36. void* GLPixelBuffer::lock(UINT32 offset, UINT32 length, GpuLockOptions options)
  37. {
  38. assert(!mIsLocked && "Cannot lock this buffer, it is already locked!");
  39. assert(offset == 0 && length == mSizeInBytes && "Cannot lock memory region, most lock box or entire buffer");
  40. PixelVolume volume(0, 0, 0, mWidth, mHeight, mDepth);
  41. const PixelData& lockedData = lock(volume, options);
  42. return lockedData.getData();
  43. }
  44. const PixelData& GLPixelBuffer::lock(const PixelVolume& lockBox, GpuLockOptions options)
  45. {
  46. allocateBuffer();
  47. if (options != GBL_WRITE_ONLY_DISCARD)
  48. {
  49. // Download the old contents of the texture
  50. download(mBuffer);
  51. }
  52. mCurrentLockOptions = options;
  53. mLockedBox = lockBox;
  54. mCurrentLock = mBuffer.getSubVolume(lockBox);
  55. mIsLocked = true;
  56. return mCurrentLock;
  57. }
  58. void GLPixelBuffer::unlock()
  59. {
  60. assert(mIsLocked && "Cannot unlock this buffer, it is not locked!");
  61. if (mCurrentLockOptions != GBL_READ_ONLY)
  62. {
  63. // From buffer to card, only upload if was locked for writing
  64. upload(mCurrentLock, mLockedBox);
  65. }
  66. freeBuffer();
  67. mIsLocked = false;
  68. }
  69. void GLPixelBuffer::upload(const PixelData& data, const PixelVolume& dest)
  70. {
  71. BS_EXCEPT(RenderingAPIException, "Upload not possible for this pixel buffer type");
  72. }
  73. void GLPixelBuffer::download(const PixelData& data)
  74. {
  75. BS_EXCEPT(RenderingAPIException, "Download not possible for this pixel buffer type");
  76. }
  77. void GLPixelBuffer::blitFromTexture(GLTextureBuffer* src)
  78. {
  79. blitFromTexture(src,
  80. PixelVolume(0, 0, 0, src->getWidth(), src->getHeight(), src->getDepth()),
  81. PixelVolume(0, 0, 0, mWidth, mHeight, mDepth)
  82. );
  83. }
  84. void GLPixelBuffer::blitFromTexture(GLTextureBuffer* src, const PixelVolume& srcBox, const PixelVolume& dstBox)
  85. {
  86. BS_EXCEPT(RenderingAPIException, "BlitFromTexture not possible for this pixel buffer type");
  87. }
  88. void GLPixelBuffer::bindToFramebuffer(GLenum attachment, UINT32 zoffset, bool allLayers)
  89. {
  90. BS_EXCEPT(RenderingAPIException, "Framebuffer bind not possible for this pixel buffer type");
  91. }
  92. GLTextureBuffer::GLTextureBuffer(
  93. GLenum target,
  94. GLuint id,
  95. GLint face,
  96. GLint level,
  97. GpuBufferUsage usage,
  98. PixelFormat format,
  99. UINT32 multisampleCount)
  100. : GLPixelBuffer(0, 0, 0, format, usage), mTarget(target), mFaceTarget(0), mTextureID(id), mFace(face)
  101. , mLevel(level), mMultisampleCount(multisampleCount)
  102. {
  103. GLint value = 0;
  104. glBindTexture(mTarget, mTextureID);
  105. BS_CHECK_GL_ERROR();
  106. // Get face identifier
  107. mFaceTarget = mTarget;
  108. if(mTarget == GL_TEXTURE_CUBE_MAP)
  109. mFaceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + (face % 6);
  110. // Get width
  111. glGetTexLevelParameteriv(mFaceTarget, level, GL_TEXTURE_WIDTH, &value);
  112. BS_CHECK_GL_ERROR();
  113. mWidth = value;
  114. // Get height
  115. if(target == GL_TEXTURE_1D)
  116. value = 1; // Height always 1 for 1D textures
  117. else
  118. {
  119. glGetTexLevelParameteriv(mFaceTarget, level, GL_TEXTURE_HEIGHT, &value);
  120. BS_CHECK_GL_ERROR();
  121. }
  122. mHeight = value;
  123. // Get depth
  124. if(target != GL_TEXTURE_3D)
  125. value = 1; // Depth always 1 for non-3D textures
  126. else
  127. {
  128. glGetTexLevelParameteriv(mFaceTarget, level, GL_TEXTURE_DEPTH, &value);
  129. BS_CHECK_GL_ERROR();
  130. }
  131. mDepth = value;
  132. // Default
  133. mSizeInBytes = PixelUtil::getMemorySize(mWidth, mHeight, mDepth, mFormat);
  134. // Set up pixel box
  135. mBuffer = PixelData(mWidth, mHeight, mDepth, mFormat);
  136. }
  137. GLTextureBuffer::~GLTextureBuffer()
  138. { }
  139. void GLTextureBuffer::upload(const PixelData& data, const PixelVolume& dest)
  140. {
  141. if ((mUsage & TU_RENDERTARGET) != 0)
  142. {
  143. LOGERR("Writing to render texture from CPU not supported.");
  144. return;
  145. }
  146. if ((mUsage & TU_DEPTHSTENCIL) != 0)
  147. {
  148. LOGERR("Writing to depth stencil texture from CPU not supported.");
  149. return;
  150. }
  151. glBindTexture(mTarget, mTextureID);
  152. BS_CHECK_GL_ERROR();
  153. if(PixelUtil::isCompressed(data.getFormat()))
  154. {
  155. if (data.getFormat() != mFormat || !data.isConsecutive())
  156. {
  157. LOGERR("Compressed images must be consecutive, in the source format");
  158. return;
  159. }
  160. GLenum format = GLPixelUtil::getGLInternalFormat(mFormat);
  161. switch(mTarget) {
  162. case GL_TEXTURE_1D:
  163. if (dest.left == 0)
  164. {
  165. glCompressedTexImage1D(GL_TEXTURE_1D, mLevel,
  166. format,
  167. dest.getWidth(),
  168. 0,
  169. data.getConsecutiveSize(),
  170. data.getData());
  171. BS_CHECK_GL_ERROR();
  172. }
  173. else
  174. {
  175. glCompressedTexSubImage1D(GL_TEXTURE_1D, mLevel,
  176. dest.left,
  177. dest.getWidth(),
  178. format, data.getConsecutiveSize(),
  179. data.getData());
  180. BS_CHECK_GL_ERROR();
  181. }
  182. break;
  183. case GL_TEXTURE_2D:
  184. case GL_TEXTURE_CUBE_MAP:
  185. if (dest.left == 0 && dest.top == 0)
  186. {
  187. glCompressedTexImage2D(mFaceTarget, mLevel,
  188. format,
  189. dest.getWidth(),
  190. dest.getHeight(),
  191. 0,
  192. data.getConsecutiveSize(),
  193. data.getData());
  194. BS_CHECK_GL_ERROR();
  195. }
  196. else
  197. {
  198. glCompressedTexSubImage2D(mFaceTarget, mLevel,
  199. dest.left, dest.top,
  200. dest.getWidth(), dest.getHeight(),
  201. format, data.getConsecutiveSize(),
  202. data.getData());
  203. BS_CHECK_GL_ERROR();
  204. }
  205. break;
  206. case GL_TEXTURE_3D:
  207. if (dest.left == 0 && dest.top == 0 && dest.front == 0)
  208. {
  209. glCompressedTexImage3D(GL_TEXTURE_3D, mLevel,
  210. format,
  211. dest.getWidth(),
  212. dest.getHeight(),
  213. dest.getDepth(),
  214. 0,
  215. data.getConsecutiveSize(),
  216. data.getData());
  217. BS_CHECK_GL_ERROR();
  218. }
  219. else
  220. {
  221. glCompressedTexSubImage3D(GL_TEXTURE_3D, mLevel,
  222. dest.left, dest.top, dest.front,
  223. dest.getWidth(), dest.getHeight(), dest.getDepth(),
  224. format, data.getConsecutiveSize(),
  225. data.getData());
  226. BS_CHECK_GL_ERROR();
  227. }
  228. break;
  229. }
  230. }
  231. else
  232. {
  233. if (data.getWidth() != data.getRowPitch())
  234. {
  235. glPixelStorei(GL_UNPACK_ROW_LENGTH, data.getRowPitch());
  236. BS_CHECK_GL_ERROR();
  237. }
  238. if (data.getHeight()*data.getWidth() != data.getSlicePitch())
  239. {
  240. glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, (data.getSlicePitch() / data.getWidth()));
  241. BS_CHECK_GL_ERROR();
  242. }
  243. if (data.getLeft() > 0 || data.getTop() > 0 || data.getFront() > 0)
  244. {
  245. glPixelStorei(
  246. GL_UNPACK_SKIP_PIXELS,
  247. data.getLeft() + data.getRowPitch() * data.getTop() + data.getSlicePitch() * data.getFront());
  248. BS_CHECK_GL_ERROR();
  249. }
  250. if ((data.getWidth()*PixelUtil::getNumElemBytes(data.getFormat())) & 3)
  251. {
  252. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  253. BS_CHECK_GL_ERROR();
  254. }
  255. switch(mTarget) {
  256. case GL_TEXTURE_1D:
  257. glTexSubImage1D(GL_TEXTURE_1D, mLevel,
  258. dest.left,
  259. dest.getWidth(),
  260. GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
  261. data.getData());
  262. BS_CHECK_GL_ERROR();
  263. break;
  264. case GL_TEXTURE_2D:
  265. case GL_TEXTURE_CUBE_MAP:
  266. glTexSubImage2D(mFaceTarget, mLevel,
  267. dest.left, dest.top,
  268. dest.getWidth(), dest.getHeight(),
  269. GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
  270. data.getData());
  271. BS_CHECK_GL_ERROR();
  272. break;
  273. case GL_TEXTURE_2D_ARRAY:
  274. case GL_TEXTURE_3D:
  275. glTexSubImage3D(
  276. mTarget, mLevel,
  277. dest.left, dest.top, dest.front,
  278. dest.getWidth(), dest.getHeight(), dest.getDepth(),
  279. GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
  280. data.getData());
  281. BS_CHECK_GL_ERROR();
  282. break;
  283. }
  284. }
  285. // Restore defaults
  286. glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
  287. BS_CHECK_GL_ERROR();
  288. glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
  289. BS_CHECK_GL_ERROR();
  290. glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
  291. BS_CHECK_GL_ERROR();
  292. glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  293. BS_CHECK_GL_ERROR();
  294. BS_INC_RENDER_STAT_CAT(ResWrite, RenderStatObject_Texture);
  295. }
  296. void GLTextureBuffer::download(const PixelData &data)
  297. {
  298. if (data.getWidth() != getWidth() || data.getHeight() != getHeight() || data.getDepth() != getDepth())
  299. {
  300. LOGERR("Only download of entire buffer is supported by OpenGL.");
  301. return;
  302. }
  303. glBindTexture(mTarget, mTextureID);
  304. BS_CHECK_GL_ERROR();
  305. if(PixelUtil::isCompressed(data.getFormat()))
  306. {
  307. if (data.getFormat() != mFormat || !data.isConsecutive())
  308. {
  309. LOGERR("Compressed images must be consecutive, in the source format");
  310. return;
  311. }
  312. // Data must be consecutive and at beginning of buffer as PixelStorei not allowed
  313. // for compressed formate
  314. glGetCompressedTexImage(mFaceTarget, mLevel, data.getData());
  315. BS_CHECK_GL_ERROR();
  316. }
  317. else
  318. {
  319. if (data.getWidth() != data.getRowPitch())
  320. {
  321. glPixelStorei(GL_PACK_ROW_LENGTH, data.getRowPitch());
  322. BS_CHECK_GL_ERROR();
  323. }
  324. if (data.getHeight()*data.getWidth() != data.getSlicePitch())
  325. {
  326. glPixelStorei(GL_PACK_IMAGE_HEIGHT, (data.getSlicePitch() / data.getWidth()));
  327. BS_CHECK_GL_ERROR();
  328. }
  329. if (data.getLeft() > 0 || data.getTop() > 0 || data.getFront() > 0)
  330. {
  331. glPixelStorei(
  332. GL_PACK_SKIP_PIXELS,
  333. data.getLeft() + data.getRowPitch() * data.getTop() + data.getSlicePitch() * data.getFront());
  334. BS_CHECK_GL_ERROR();
  335. }
  336. if ((data.getWidth()*PixelUtil::getNumElemBytes(data.getFormat())) & 3)
  337. {
  338. glPixelStorei(GL_PACK_ALIGNMENT, 1);
  339. BS_CHECK_GL_ERROR();
  340. }
  341. // We can only get the entire texture
  342. glGetTexImage(mFaceTarget, mLevel, GLPixelUtil::getGLOriginFormat(data.getFormat()),
  343. GLPixelUtil::getGLOriginDataType(data.getFormat()), data.getData());
  344. BS_CHECK_GL_ERROR();
  345. // Restore defaults
  346. glPixelStorei(GL_PACK_ROW_LENGTH, 0);
  347. BS_CHECK_GL_ERROR();
  348. glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
  349. BS_CHECK_GL_ERROR();
  350. glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
  351. BS_CHECK_GL_ERROR();
  352. glPixelStorei(GL_PACK_ALIGNMENT, 4);
  353. BS_CHECK_GL_ERROR();
  354. }
  355. BS_INC_RENDER_STAT_CAT(ResRead, RenderStatObject_Texture);
  356. }
  357. void GLTextureBuffer::bindToFramebuffer(GLenum attachment, UINT32 zoffset, bool allLayers)
  358. {
  359. if(allLayers)
  360. {
  361. switch (mTarget)
  362. {
  363. case GL_TEXTURE_1D:
  364. glFramebufferTexture1D(GL_FRAMEBUFFER, attachment, mFaceTarget, mTextureID, mLevel);
  365. BS_CHECK_GL_ERROR();
  366. break;
  367. case GL_TEXTURE_2D:
  368. glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, mFaceTarget, mTextureID, mLevel);
  369. BS_CHECK_GL_ERROR();
  370. break;
  371. case GL_TEXTURE_2D_MULTISAMPLE:
  372. glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, mFaceTarget, mTextureID, 0);
  373. BS_CHECK_GL_ERROR();
  374. break;
  375. case GL_TEXTURE_CUBE_MAP:
  376. case GL_TEXTURE_3D:
  377. default: // Texture arrays (binding all layers)
  378. glFramebufferTexture(GL_FRAMEBUFFER, attachment, mTextureID, mLevel);
  379. BS_CHECK_GL_ERROR();
  380. break;
  381. }
  382. }
  383. else
  384. {
  385. switch (mTarget)
  386. {
  387. case GL_TEXTURE_3D:
  388. glFramebufferTexture3D(GL_FRAMEBUFFER, attachment, mFaceTarget, mTextureID, mLevel, zoffset);
  389. BS_CHECK_GL_ERROR();
  390. break;
  391. case GL_TEXTURE_CUBE_MAP:
  392. glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, mFaceTarget, mTextureID, mLevel);
  393. BS_CHECK_GL_ERROR();
  394. break;
  395. default: // Texture arrays
  396. glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, mTextureID, mLevel, mFace);
  397. BS_CHECK_GL_ERROR();
  398. break;
  399. }
  400. }
  401. }
  402. void GLTextureBuffer::copyFromFramebuffer(UINT32 zoffset)
  403. {
  404. glBindTexture(mTarget, mTextureID);
  405. BS_CHECK_GL_ERROR();
  406. switch(mTarget)
  407. {
  408. case GL_TEXTURE_1D:
  409. glCopyTexSubImage1D(mFaceTarget, mLevel, 0, 0, 0, mWidth);
  410. BS_CHECK_GL_ERROR();
  411. break;
  412. case GL_TEXTURE_2D:
  413. case GL_TEXTURE_CUBE_MAP:
  414. glCopyTexSubImage2D(mFaceTarget, mLevel, 0, 0, 0, 0, mWidth, mHeight);
  415. BS_CHECK_GL_ERROR();
  416. break;
  417. case GL_TEXTURE_3D:
  418. glCopyTexSubImage3D(mFaceTarget, mLevel, 0, 0, zoffset, 0, 0, mWidth, mHeight);
  419. BS_CHECK_GL_ERROR();
  420. break;
  421. }
  422. }
  423. void GLTextureBuffer::blitFromTexture(GLTextureBuffer* src)
  424. {
  425. GLPixelBuffer::blitFromTexture(src);
  426. }
  427. void GLTextureBuffer::blitFromTexture(GLTextureBuffer* src, const PixelVolume& srcBox, const PixelVolume& dstBox)
  428. {
  429. // If supported, prefer direct image copy. If not supported, or if sample counts don't match, fall back to FB blit
  430. #if BS_OPENGL_4_3 || BS_OPENGLES_3_2
  431. if (src->mMultisampleCount > 1 && mMultisampleCount <= 1) // Resolving MS texture
  432. #endif
  433. {
  434. #if BS_OPENGL_4_3 || BS_OPENGLES_3_2
  435. if (mTarget != GL_TEXTURE_2D || mTarget != GL_TEXTURE_2D_MULTISAMPLE)
  436. BS_EXCEPT(InvalidParametersException, "Non-2D multisampled texture not supported.");
  437. #endif
  438. GLint currentFBO = 0;
  439. glGetIntegerv(GL_FRAMEBUFFER_BINDING, &currentFBO);
  440. BS_CHECK_GL_ERROR();
  441. GLuint readFBO = GLRTTManager::instance().getBlitReadFBO();
  442. GLuint drawFBO = GLRTTManager::instance().getBlitDrawFBO();
  443. // Attach source texture
  444. glBindFramebuffer(GL_FRAMEBUFFER, readFBO);
  445. BS_CHECK_GL_ERROR();
  446. src->bindToFramebuffer(0, 0, true);
  447. // Attach destination texture
  448. glBindFramebuffer(GL_FRAMEBUFFER, drawFBO);
  449. BS_CHECK_GL_ERROR();
  450. bindToFramebuffer(0, 0, true);
  451. // Perform blit
  452. glBindFramebuffer(GL_READ_FRAMEBUFFER, readFBO);
  453. BS_CHECK_GL_ERROR();
  454. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO);
  455. BS_CHECK_GL_ERROR();
  456. glReadBuffer(GL_COLOR_ATTACHMENT0);
  457. BS_CHECK_GL_ERROR();
  458. glDrawBuffer(GL_COLOR_ATTACHMENT0);
  459. BS_CHECK_GL_ERROR();
  460. glBlitFramebuffer(srcBox.left, srcBox.top, srcBox.right, srcBox.bottom,
  461. dstBox.left, dstBox.top, dstBox.right, dstBox.bottom, GL_COLOR_BUFFER_BIT, GL_NEAREST);
  462. BS_CHECK_GL_ERROR();
  463. // Restore the previously bound FBO
  464. glBindFramebuffer(GL_FRAMEBUFFER, currentFBO);
  465. BS_CHECK_GL_ERROR();
  466. }
  467. #if BS_OPENGL_4_3 || BS_OPENGLES_3_2
  468. else // Just plain copy
  469. {
  470. if (mMultisampleCount != src->mMultisampleCount)
  471. BS_EXCEPT(InvalidParametersException, "When copying textures their multisample counts must match.");
  472. if (mTarget == GL_TEXTURE_3D) // 3D textures can't have arrays so their Z coordinate is handled differently
  473. {
  474. glCopyImageSubData(src->mTextureID, src->mTarget, src->mLevel, srcBox.left, srcBox.top, srcBox.front,
  475. mTextureID, mTarget, mLevel, dstBox.left, dstBox.top, dstBox.front, srcBox.getWidth(), srcBox.getHeight(), srcBox.getDepth());
  476. BS_CHECK_GL_ERROR();
  477. }
  478. else
  479. {
  480. glCopyImageSubData(src->mTextureID, src->mTarget, src->mLevel, srcBox.left, srcBox.top, src->mFace,
  481. mTextureID, mTarget, mLevel, dstBox.left, dstBox.top, mFace, srcBox.getWidth(), srcBox.getHeight(), 1);
  482. BS_CHECK_GL_ERROR();
  483. }
  484. }
  485. #endif
  486. }
  487. }}