BsGLPixelBuffer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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 "BsException.h"
  8. #include "BsBitwise.h"
  9. #include "BsGLRenderTexture.h"
  10. #include "BsRenderStats.h"
  11. namespace bs
  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), mGLInternalFormat(GL_NONE)
  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. {
  35. mBuffer.freeInternalBuffer();
  36. }
  37. }
  38. void* GLPixelBuffer::lock(UINT32 offset, UINT32 length, GpuLockOptions options)
  39. {
  40. assert(!mIsLocked && "Cannot lock this buffer, it is already locked!");
  41. assert(offset == 0 && length == mSizeInBytes && "Cannot lock memory region, most lock box or entire buffer");
  42. PixelVolume volume(0, 0, 0, mWidth, mHeight, mDepth);
  43. const PixelData& lockedData = lock(volume, options);
  44. return lockedData.getData();
  45. }
  46. const PixelData& GLPixelBuffer::lock(const PixelVolume& lockBox, GpuLockOptions options)
  47. {
  48. allocateBuffer();
  49. if (options != GBL_WRITE_ONLY_DISCARD)
  50. {
  51. // Download the old contents of the texture
  52. download(mBuffer);
  53. }
  54. mCurrentLockOptions = options;
  55. mLockedBox = lockBox;
  56. mCurrentLock = mBuffer.getSubVolume(lockBox);
  57. mIsLocked = true;
  58. return mCurrentLock;
  59. }
  60. void GLPixelBuffer::unlock()
  61. {
  62. assert(mIsLocked && "Cannot unlock this buffer, it is not locked!");
  63. if (mCurrentLockOptions != GBL_READ_ONLY)
  64. {
  65. // From buffer to card, only upload if was locked for writing
  66. upload(mCurrentLock, mLockedBox);
  67. }
  68. freeBuffer();
  69. mIsLocked = false;
  70. }
  71. void GLPixelBuffer::upload(const PixelData& data, const PixelVolume& dest)
  72. {
  73. BS_EXCEPT(RenderingAPIException, "Upload not possible for this pixel buffer type");
  74. }
  75. void GLPixelBuffer::download(const PixelData& data)
  76. {
  77. BS_EXCEPT(RenderingAPIException, "Download not possible for this pixel buffer type");
  78. }
  79. void GLPixelBuffer::blitFromTexture(GLTextureBuffer* src)
  80. {
  81. blitFromTexture(src,
  82. PixelVolume(0, 0, 0, src->getWidth(), src->getHeight(), src->getDepth()),
  83. PixelVolume(0, 0, 0, mWidth, mHeight, mDepth)
  84. );
  85. }
  86. void GLPixelBuffer::blitFromTexture(GLTextureBuffer* src, const PixelVolume& srcBox, const PixelVolume& dstBox)
  87. {
  88. BS_EXCEPT(RenderingAPIException, "BlitFromTexture not possible for this pixel buffer type");
  89. }
  90. void GLPixelBuffer::bindToFramebuffer(GLenum attachment, UINT32 zoffset, bool allLayers)
  91. {
  92. BS_EXCEPT(RenderingAPIException, "Framebuffer bind not possible for this pixel buffer type");
  93. }
  94. GLTextureBuffer::GLTextureBuffer(GLenum target, GLuint id,
  95. GLint face, GLint level, GpuBufferUsage usage,
  96. bool writeGamma, UINT32 multisampleCount):
  97. GLPixelBuffer(0, 0, 0, PF_UNKNOWN, usage),
  98. mTarget(target), mFaceTarget(0), mTextureID(id), mFace(face), mLevel(level), mMultisampleCount(multisampleCount)
  99. {
  100. GLint value = 0;
  101. glBindTexture(mTarget, mTextureID);
  102. // Get face identifier
  103. mFaceTarget = mTarget;
  104. if(mTarget == GL_TEXTURE_CUBE_MAP)
  105. mFaceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + (face % 6);
  106. // Get width
  107. glGetTexLevelParameteriv(mFaceTarget, level, GL_TEXTURE_WIDTH, &value);
  108. mWidth = value;
  109. // Get height
  110. if(target == GL_TEXTURE_1D)
  111. value = 1; // Height always 1 for 1D textures
  112. else
  113. glGetTexLevelParameteriv(mFaceTarget, level, GL_TEXTURE_HEIGHT, &value);
  114. mHeight = value;
  115. // Get depth
  116. if(target != GL_TEXTURE_3D)
  117. value = 1; // Depth always 1 for non-3D textures
  118. else
  119. glGetTexLevelParameteriv(mFaceTarget, level, GL_TEXTURE_DEPTH, &value);
  120. mDepth = value;
  121. // Get format
  122. glGetTexLevelParameteriv(mFaceTarget, level, GL_TEXTURE_INTERNAL_FORMAT, &value);
  123. mGLInternalFormat = value;
  124. mFormat = GLPixelUtil::getClosestEngineFormat(value);
  125. // Default
  126. mSizeInBytes = PixelUtil::getMemorySize(mWidth, mHeight, mDepth, mFormat);
  127. // Set up pixel box
  128. mBuffer = PixelData(mWidth, mHeight, mDepth, mFormat);
  129. }
  130. GLTextureBuffer::~GLTextureBuffer()
  131. { }
  132. void GLTextureBuffer::upload(const PixelData &data, const PixelVolume &dest)
  133. {
  134. if((mUsage & TU_RENDERTARGET) != 0)
  135. BS_EXCEPT(NotImplementedException, "Writing to render texture from CPU not supported.");
  136. if ((mUsage & TU_DEPTHSTENCIL) != 0)
  137. BS_EXCEPT(NotImplementedException, "Writing to depth stencil texture from CPU not supported.");
  138. glBindTexture( mTarget, mTextureID );
  139. if(PixelUtil::isCompressed(data.getFormat()))
  140. {
  141. if(data.getFormat() != mFormat || !data.isConsecutive())
  142. BS_EXCEPT(InvalidParametersException,
  143. "Compressed images must be consecutive, in the source format");
  144. GLenum format = GLPixelUtil::getClosestGLInternalFormat(mFormat);
  145. switch(mTarget) {
  146. case GL_TEXTURE_1D:
  147. if (dest.left == 0)
  148. {
  149. glCompressedTexImage1D(GL_TEXTURE_1D, mLevel,
  150. format,
  151. dest.getWidth(),
  152. 0,
  153. data.getConsecutiveSize(),
  154. data.getData());
  155. }
  156. else
  157. {
  158. glCompressedTexSubImage1D(GL_TEXTURE_1D, mLevel,
  159. dest.left,
  160. dest.getWidth(),
  161. format, data.getConsecutiveSize(),
  162. data.getData());
  163. }
  164. break;
  165. case GL_TEXTURE_2D:
  166. case GL_TEXTURE_CUBE_MAP:
  167. if (dest.left == 0 && dest.top == 0)
  168. {
  169. glCompressedTexImage2D(mFaceTarget, mLevel,
  170. format,
  171. dest.getWidth(),
  172. dest.getHeight(),
  173. 0,
  174. data.getConsecutiveSize(),
  175. data.getData());
  176. }
  177. else
  178. {
  179. glCompressedTexSubImage2D(mFaceTarget, mLevel,
  180. dest.left, dest.top,
  181. dest.getWidth(), dest.getHeight(),
  182. format, data.getConsecutiveSize(),
  183. data.getData());
  184. }
  185. break;
  186. case GL_TEXTURE_3D:
  187. if (dest.left == 0 && dest.top == 0 && dest.front == 0)
  188. {
  189. glCompressedTexImage3D(GL_TEXTURE_3D, mLevel,
  190. format,
  191. dest.getWidth(),
  192. dest.getHeight(),
  193. dest.getDepth(),
  194. 0,
  195. data.getConsecutiveSize(),
  196. data.getData());
  197. }
  198. else
  199. {
  200. glCompressedTexSubImage3D(GL_TEXTURE_3D, mLevel,
  201. dest.left, dest.top, dest.front,
  202. dest.getWidth(), dest.getHeight(), dest.getDepth(),
  203. format, data.getConsecutiveSize(),
  204. data.getData());
  205. }
  206. break;
  207. }
  208. }
  209. else
  210. {
  211. if(data.getWidth() != data.getRowPitch())
  212. glPixelStorei(GL_UNPACK_ROW_LENGTH, data.getRowPitch());
  213. if(data.getHeight()*data.getWidth() != data.getSlicePitch())
  214. glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, (data.getSlicePitch()/data.getWidth()));
  215. if(data.getLeft() > 0 || data.getTop() > 0 || data.getFront() > 0)
  216. glPixelStorei(GL_UNPACK_SKIP_PIXELS, data.getLeft() + data.getRowPitch() * data.getTop() + data.getSlicePitch() * data.getFront());
  217. if((data.getWidth()*PixelUtil::getNumElemBytes(data.getFormat())) & 3)
  218. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  219. switch(mTarget) {
  220. case GL_TEXTURE_1D:
  221. glTexSubImage1D(GL_TEXTURE_1D, mLevel,
  222. dest.left,
  223. dest.getWidth(),
  224. GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
  225. data.getData());
  226. break;
  227. case GL_TEXTURE_2D:
  228. case GL_TEXTURE_CUBE_MAP:
  229. glTexSubImage2D(mFaceTarget, mLevel,
  230. dest.left, dest.top,
  231. dest.getWidth(), dest.getHeight(),
  232. GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
  233. data.getData());
  234. break;
  235. case GL_TEXTURE_3D:
  236. glTexSubImage3D(
  237. GL_TEXTURE_3D, mLevel,
  238. dest.left, dest.top, dest.front,
  239. dest.getWidth(), dest.getHeight(), dest.getDepth(),
  240. GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
  241. data.getData());
  242. break;
  243. }
  244. }
  245. // Restore defaults
  246. glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
  247. glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
  248. glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
  249. glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  250. BS_INC_RENDER_STAT_CAT(ResWrite, RenderStatObject_Texture);
  251. }
  252. void GLTextureBuffer::download(const PixelData &data)
  253. {
  254. if(data.getWidth() != getWidth() || data.getHeight() != getHeight() || data.getDepth() != getDepth())
  255. BS_EXCEPT(InvalidParametersException, "only download of entire buffer is supported by GL");
  256. glBindTexture( mTarget, mTextureID );
  257. if(PixelUtil::isCompressed(data.getFormat()))
  258. {
  259. if(data.getFormat() != mFormat || !data.isConsecutive())
  260. BS_EXCEPT(InvalidParametersException,
  261. "Compressed images must be consecutive, in the source format");
  262. // Data must be consecutive and at beginning of buffer as PixelStorei not allowed
  263. // for compressed formate
  264. glGetCompressedTexImage(mFaceTarget, mLevel, data.getData());
  265. }
  266. else
  267. {
  268. if(data.getWidth() != data.getRowPitch())
  269. glPixelStorei(GL_PACK_ROW_LENGTH, data.getRowPitch());
  270. if(data.getHeight()*data.getWidth() != data.getSlicePitch())
  271. glPixelStorei(GL_PACK_IMAGE_HEIGHT, (data.getSlicePitch()/data.getWidth()));
  272. if(data.getLeft() > 0 || data.getTop() > 0 || data.getFront() > 0)
  273. glPixelStorei(GL_PACK_SKIP_PIXELS, data.getLeft() + data.getRowPitch() * data.getTop() + data.getSlicePitch() * data.getFront());
  274. if((data.getWidth()*PixelUtil::getNumElemBytes(data.getFormat())) & 3)
  275. glPixelStorei(GL_PACK_ALIGNMENT, 1);
  276. // We can only get the entire texture
  277. glGetTexImage(mFaceTarget, mLevel, GLPixelUtil::getGLOriginFormat(data.getFormat()),
  278. GLPixelUtil::getGLOriginDataType(data.getFormat()), data.getData());
  279. // Restore defaults
  280. glPixelStorei(GL_PACK_ROW_LENGTH, 0);
  281. glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
  282. glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
  283. glPixelStorei(GL_PACK_ALIGNMENT, 4);
  284. }
  285. BS_INC_RENDER_STAT_CAT(ResRead, RenderStatObject_Texture);
  286. }
  287. void GLTextureBuffer::bindToFramebuffer(GLenum attachment, UINT32 zoffset, bool allLayers)
  288. {
  289. if(allLayers)
  290. {
  291. switch (mTarget)
  292. {
  293. case GL_TEXTURE_1D:
  294. glFramebufferTexture1D(GL_FRAMEBUFFER, attachment,
  295. mFaceTarget, mTextureID, mLevel);
  296. break;
  297. case GL_TEXTURE_2D:
  298. glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
  299. mFaceTarget, mTextureID, mLevel);
  300. break;
  301. case GL_TEXTURE_2D_MULTISAMPLE:
  302. glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
  303. mFaceTarget, mTextureID, 0);
  304. break;
  305. case GL_TEXTURE_CUBE_MAP:
  306. case GL_TEXTURE_3D:
  307. default: // Texture arrays (binding all layers)
  308. glFramebufferTexture(GL_FRAMEBUFFER, attachment, mTextureID, mLevel);
  309. break;
  310. }
  311. }
  312. else
  313. {
  314. switch (mTarget)
  315. {
  316. case GL_TEXTURE_3D:
  317. glFramebufferTexture3D(GL_FRAMEBUFFER, attachment, mFaceTarget, mTextureID, mLevel, zoffset);
  318. break;
  319. default: // Texture arrays and cube maps
  320. glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, mTextureID, mLevel, mFace);
  321. break;
  322. }
  323. }
  324. }
  325. void GLTextureBuffer::copyFromFramebuffer(UINT32 zoffset)
  326. {
  327. glBindTexture(mTarget, mTextureID);
  328. switch(mTarget)
  329. {
  330. case GL_TEXTURE_1D:
  331. glCopyTexSubImage1D(mFaceTarget, mLevel, 0, 0, 0, mWidth);
  332. break;
  333. case GL_TEXTURE_2D:
  334. case GL_TEXTURE_CUBE_MAP:
  335. glCopyTexSubImage2D(mFaceTarget, mLevel, 0, 0, 0, 0, mWidth, mHeight);
  336. break;
  337. case GL_TEXTURE_3D:
  338. glCopyTexSubImage3D(mFaceTarget, mLevel, 0, 0, zoffset, 0, 0, mWidth, mHeight);
  339. break;
  340. }
  341. }
  342. void GLTextureBuffer::blitFromTexture(GLTextureBuffer* src, const PixelVolume& srcBox, const PixelVolume& dstBox)
  343. {
  344. if (src->mMultisampleCount > 1 && mMultisampleCount <= 1) // Resolving MS texture
  345. {
  346. if (mTarget != GL_TEXTURE_2D || mTarget != GL_TEXTURE_2D_MULTISAMPLE)
  347. BS_EXCEPT(InvalidParametersException, "Non-2D multisampled texture not supported.");
  348. GLint currentFBO = 0;
  349. glGetIntegerv(GL_FRAMEBUFFER_BINDING, &currentFBO);
  350. GLuint readFBO = GLRTTManager::instance().getBlitReadFBO();
  351. GLuint drawFBO = GLRTTManager::instance().getBlitDrawFBO();
  352. // Attach source texture
  353. glBindFramebuffer(GL_FRAMEBUFFER, readFBO);
  354. src->bindToFramebuffer(0, 0, true);
  355. // Attach destination texture
  356. glBindFramebuffer(GL_FRAMEBUFFER, drawFBO);
  357. bindToFramebuffer(0, 0, true);
  358. // Perform blit
  359. glBindFramebuffer(GL_READ_FRAMEBUFFER, readFBO);
  360. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO);
  361. glReadBuffer(GL_COLOR_ATTACHMENT0);
  362. glDrawBuffer(GL_COLOR_ATTACHMENT0);
  363. glBlitFramebuffer(srcBox.left, srcBox.top, srcBox.right, srcBox.bottom,
  364. dstBox.left, dstBox.top, dstBox.right, dstBox.bottom, GL_COLOR_BUFFER_BIT, GL_NEAREST);
  365. // Restore the previously bound FBO
  366. glBindFramebuffer(GL_FRAMEBUFFER, currentFBO);
  367. }
  368. else // Just plain copy
  369. {
  370. if (mMultisampleCount != src->mMultisampleCount)
  371. BS_EXCEPT(InvalidParametersException, "When copying textures their multisample counts must match.");
  372. if (mTarget == GL_TEXTURE_3D) // 3D textures can't have arrays so their Z coordinate is handled differently
  373. {
  374. glCopyImageSubData(src->mTextureID, src->mTarget, src->mLevel, srcBox.left, srcBox.top, srcBox.front,
  375. mTextureID, mTarget, mLevel, dstBox.left, dstBox.top, dstBox.front, srcBox.getWidth(), srcBox.getHeight(), srcBox.getDepth());
  376. }
  377. else
  378. {
  379. glCopyImageSubData(src->mTextureID, src->mTarget, src->mLevel, srcBox.left, srcBox.top, src->mFace,
  380. mTextureID, mTarget, mLevel, dstBox.left, dstBox.top, mFace, srcBox.getWidth(), srcBox.getHeight(), 1);
  381. }
  382. }
  383. }
  384. GLRenderBuffer::GLRenderBuffer(GLenum format, UINT32 width, UINT32 height, GLsizei numSamples):
  385. GLPixelBuffer(width, height, 1, GLPixelUtil::getClosestEngineFormat(format), GBU_DYNAMIC),
  386. mRenderbufferID(0)
  387. {
  388. mGLInternalFormat = format;
  389. glGenRenderbuffers(1, &mRenderbufferID);
  390. glBindRenderbuffer(GL_RENDERBUFFER, mRenderbufferID);
  391. /// Allocate storage for depth buffer
  392. if (numSamples > 0)
  393. {
  394. glRenderbufferStorageMultisample(GL_RENDERBUFFER,
  395. numSamples, format, width, height);
  396. }
  397. else
  398. {
  399. glRenderbufferStorage(GL_RENDERBUFFER, format,
  400. width, height);
  401. }
  402. }
  403. GLRenderBuffer::~GLRenderBuffer()
  404. {
  405. glDeleteRenderbuffers(1, &mRenderbufferID);
  406. }
  407. void GLRenderBuffer::bindToFramebuffer(GLenum attachment, UINT32 zoffset, bool allLayers)
  408. {
  409. assert(zoffset < mDepth);
  410. glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
  411. GL_RENDERBUFFER, mRenderbufferID);
  412. }
  413. };