1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6
7 Copyright (c) 2000-2013 Torus Knot Software Ltd
8
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28
29 #include "OgreGLES2HardwareBufferManager.h"
30 #include "OgreGLES2HardwarePixelBuffer.h"
31 #include "OgreGLES2PixelFormat.h"
32 #include "OgreGLES2FBORenderTexture.h"
33 #include "OgreGLES2GpuProgram.h"
34 #include "OgreGLES2Util.h"
35 #include "OgreGLES2RenderSystem.h"
36 #include "OgreGLES2StateCacheManager.h"
37 #include "OgreRoot.h"
38 #include "OgreGLSLESLinkProgramManager.h"
39 #include "OgreGLSLESLinkProgram.h"
40 #include "OgreGLSLESProgramPipelineManager.h"
41 #include "OgreGLSLESProgramPipeline.h"
42
computeLog(GLuint value)43 static int computeLog(GLuint value)
44 {
45 int i;
46
47 i = 0;
48
49 /* Error! */
50 if (value == 0) return -1;
51
52 for (;;)
53 {
54 if (value & 1)
55 {
56 /* Error! */
57 if (value != 1) return -1;
58 return i;
59 }
60 value = value >> 1;
61 i++;
62 }
63 }
64
65 namespace Ogre {
GLES2HardwarePixelBuffer(uint32 width,uint32 height,uint32 depth,PixelFormat format,HardwareBuffer::Usage usage)66 GLES2HardwarePixelBuffer::GLES2HardwarePixelBuffer(uint32 width, uint32 height,
67 uint32 depth, PixelFormat format,
68 HardwareBuffer::Usage usage)
69 : HardwarePixelBuffer(width, height, depth, format, usage, false, false),
70 mBuffer(width, height, depth, format),
71 mGLInternalFormat(GL_NONE)
72 {
73 }
74
~GLES2HardwarePixelBuffer()75 GLES2HardwarePixelBuffer::~GLES2HardwarePixelBuffer()
76 {
77 // Force free buffer
78 delete [] (uint8*)mBuffer.data;
79 }
80
allocateBuffer()81 void GLES2HardwarePixelBuffer::allocateBuffer()
82 {
83 if (mBuffer.data)
84 // Already allocated
85 return;
86
87 mBuffer.data = new uint8[mSizeInBytes];
88 // TODO use PBO if we're HBU_DYNAMIC
89 }
90
freeBuffer()91 void GLES2HardwarePixelBuffer::freeBuffer()
92 {
93 // Free buffer if we're STATIC to save memory
94 if (mUsage & HBU_STATIC)
95 {
96 delete [] (uint8*)mBuffer.data;
97 mBuffer.data = 0;
98 }
99 }
100
lockImpl(const Image::Box lockBox,LockOptions options)101 PixelBox GLES2HardwarePixelBuffer::lockImpl(const Image::Box lockBox, LockOptions options)
102 {
103 allocateBuffer();
104 if (options != HardwareBuffer::HBL_DISCARD)
105 {
106 // Download the old contents of the texture
107 download(mBuffer);
108 }
109 mCurrentLockOptions = options;
110 mLockedBox = lockBox;
111 return mBuffer.getSubVolume(lockBox);
112 }
113
unlockImpl(void)114 void GLES2HardwarePixelBuffer::unlockImpl(void)
115 {
116 if (mCurrentLockOptions != HardwareBuffer::HBL_READ_ONLY)
117 {
118 // From buffer to card, only upload if was locked for writing
119 upload(mCurrentLock, mLockedBox);
120 }
121 freeBuffer();
122 }
123
blitFromMemory(const PixelBox & src,const Image::Box & dstBox)124 void GLES2HardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox)
125 {
126 if (!mBuffer.contains(dstBox))
127 {
128 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
129 "Destination box out of range",
130 "GLES2HardwarePixelBuffer::blitFromMemory");
131 }
132
133 PixelBox scaled;
134
135 if (src.getWidth() != dstBox.getWidth() ||
136 src.getHeight() != dstBox.getHeight() ||
137 src.getDepth() != dstBox.getDepth())
138 {
139 // Scale to destination size.
140 // This also does pixel format conversion if needed
141 allocateBuffer();
142 scaled = mBuffer.getSubVolume(dstBox);
143 Image::scale(src, scaled, Image::FILTER_BILINEAR);
144 }
145 #if OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS
146 else if ((src.format != mFormat) ||
147 ((GLES2PixelUtil::getGLOriginFormat(src.format) == 0) && (src.format != PF_R8G8B8)))
148 #else
149 else if (GLES2PixelUtil::getGLOriginFormat(src.format) == 0)
150 #endif
151 {
152 // Extents match, but format is not accepted as valid source format for GL
153 // do conversion in temporary buffer
154 allocateBuffer();
155 scaled = mBuffer.getSubVolume(dstBox);
156 PixelUtil::bulkPixelConversion(src, scaled);
157 }
158 else
159 {
160 allocateBuffer();
161
162 // No scaling or conversion needed
163 scaled = PixelBox(src.getWidth(), src.getHeight(), src.getDepth(), src.format, src.data);
164
165 if (src.format == PF_R8G8B8)
166 {
167 size_t srcSize = PixelUtil::getMemorySize(src.getWidth(), src.getHeight(), src.getDepth(), src.format);
168 scaled.format = PF_B8G8R8;
169 scaled.data = new uint8[srcSize];
170 memcpy(scaled.data, src.data, srcSize);
171 PixelUtil::bulkPixelConversion(src, scaled);
172 }
173 #if OGRE_PLATFORM == OGRE_PLATFORM_NACL
174 if (src.format == PF_A8R8G8B8)
175 {
176 scaled.format = PF_A8B8G8R8;
177 PixelUtil::bulkPixelConversion(src, scaled);
178 }
179 #endif
180 }
181
182 upload(scaled, dstBox);
183 freeBuffer();
184 }
185
blitToMemory(const Image::Box & srcBox,const PixelBox & dst)186 void GLES2HardwarePixelBuffer::blitToMemory(const Image::Box &srcBox, const PixelBox &dst)
187 {
188 if (!mBuffer.contains(srcBox))
189 {
190 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
191 "source box out of range",
192 "GLES2HardwarePixelBuffer::blitToMemory");
193 }
194
195 if (srcBox.left == 0 && srcBox.right == getWidth() &&
196 srcBox.top == 0 && srcBox.bottom == getHeight() &&
197 srcBox.front == 0 && srcBox.back == getDepth() &&
198 dst.getWidth() == getWidth() &&
199 dst.getHeight() == getHeight() &&
200 dst.getDepth() == getDepth() &&
201 GLES2PixelUtil::getGLOriginFormat(dst.format) != 0)
202 {
203 // The direct case: the user wants the entire texture in a format supported by GL
204 // so we don't need an intermediate buffer
205 download(dst);
206 }
207 else
208 {
209 // Use buffer for intermediate copy
210 allocateBuffer();
211 // Download entire buffer
212 download(mBuffer);
213 if(srcBox.getWidth() != dst.getWidth() ||
214 srcBox.getHeight() != dst.getHeight() ||
215 srcBox.getDepth() != dst.getDepth())
216 {
217 // We need scaling
218 Image::scale(mBuffer.getSubVolume(srcBox), dst, Image::FILTER_BILINEAR);
219 }
220 else
221 {
222 // Just copy the bit that we need
223 PixelUtil::bulkPixelConversion(mBuffer.getSubVolume(srcBox), dst);
224 }
225 freeBuffer();
226 }
227 }
228
upload(const PixelBox & data,const Image::Box & dest)229 void GLES2HardwarePixelBuffer::upload(const PixelBox &data, const Image::Box &dest)
230 {
231 OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
232 "Upload not possible for this pixelbuffer type",
233 "GLES2HardwarePixelBuffer::upload");
234 }
235
download(const PixelBox & data)236 void GLES2HardwarePixelBuffer::download(const PixelBox &data)
237 {
238 OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
239 "Download not possible for this pixelbuffer type",
240 "GLES2HardwarePixelBuffer::download");
241 }
242
bindToFramebuffer(GLenum attachment,size_t zoffset)243 void GLES2HardwarePixelBuffer::bindToFramebuffer(GLenum attachment, size_t zoffset)
244 {
245 OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
246 "Framebuffer bind not possible for this pixelbuffer type",
247 "GLES2HardwarePixelBuffer::bindToFramebuffer");
248 }
249
250 // TextureBuffer
GLES2TextureBuffer(const String & baseName,GLenum target,GLuint id,GLint width,GLint height,GLint depth,GLint internalFormat,GLint format,GLint face,GLint level,Usage usage,bool crappyCard,bool writeGamma,uint fsaa)251 GLES2TextureBuffer::GLES2TextureBuffer(const String &baseName, GLenum target, GLuint id,
252 GLint width, GLint height, GLint depth, GLint internalFormat, GLint format,
253 GLint face, GLint level, Usage usage, bool crappyCard,
254 bool writeGamma, uint fsaa)
255 : GLES2HardwarePixelBuffer(0, 0, 0, PF_UNKNOWN, usage),
256 mTarget(target), mTextureID(id), mBufferId(0), mFace(face), mLevel(level), mSoftwareMipmap(crappyCard)
257 {
258 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(mTarget, mTextureID);
259
260 // Get face identifier
261 mFaceTarget = mTarget;
262 if(mTarget == GL_TEXTURE_CUBE_MAP)
263 mFaceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
264
265 // Calculate the width and height of the texture at this mip level
266 mWidth = mLevel == 0 ? width : width / static_cast<size_t>(pow(2.0f, level));
267 mHeight = mLevel == 0 ? height : height / static_cast<size_t>(pow(2.0f, level));
268 if(mWidth < 1)
269 mWidth = 1;
270 if(mHeight < 1)
271 mHeight = 1;
272
273 #if OGRE_NO_GLES3_SUPPORT == 0
274 if(target != GL_TEXTURE_3D && target != GL_TEXTURE_2D_ARRAY)
275 mDepth = 1; // Depth always 1 for non-3D textures
276 else
277 mDepth = depth;
278 #else
279 // Only 2D is supported so depth is always 1
280 mDepth = 1;
281 #endif
282
283 mGLInternalFormat = internalFormat;
284 mFormat = GLES2PixelUtil::getClosestOGREFormat(internalFormat, format);
285
286 mRowPitch = mWidth;
287 mSlicePitch = mHeight*mWidth;
288 mSizeInBytes = PixelUtil::getMemorySize(mWidth, mHeight, mDepth, mFormat);
289
290 #if OGRE_DEBUG_MODE
291 // Log a message
292 std::stringstream str;
293 str << "GLES2HardwarePixelBuffer constructed for texture " << baseName
294 << " id " << mTextureID << " face " << mFace << " level " << mLevel << ":"
295 << " width=" << mWidth << " height="<< mHeight << " depth=" << mDepth
296 << " format=" << PixelUtil::getFormatName(mFormat);
297 LogManager::getSingleton().logMessage(LML_NORMAL, str.str());
298 #endif
299
300 // Set up a pixel box
301 mBuffer = PixelBox(mWidth, mHeight, mDepth, mFormat);
302
303 if (mWidth==0 || mHeight==0 || mDepth==0)
304 // We are invalid, do not allocate a buffer
305 return;
306
307 // Is this a render target?
308 if (mUsage & TU_RENDERTARGET)
309 {
310 // Create render target for each slice
311 mSliceTRT.reserve(mDepth);
312 for(size_t zoffset=0; zoffset<mDepth; ++zoffset)
313 {
314 String name;
315 name = "rtt/" + StringConverter::toString((size_t)this) + "/" + baseName;
316 GLES2SurfaceDesc surface;
317 surface.buffer = this;
318 surface.zoffset = zoffset;
319 RenderTexture *trt = GLES2RTTManager::getSingleton().createRenderTexture(name, surface, writeGamma, fsaa);
320 mSliceTRT.push_back(trt);
321 Root::getSingleton().getRenderSystem()->attachRenderTarget(*mSliceTRT[zoffset]);
322 }
323 }
324 }
325
~GLES2TextureBuffer()326 GLES2TextureBuffer::~GLES2TextureBuffer()
327 {
328 if (mUsage & TU_RENDERTARGET)
329 {
330 // Delete all render targets that are not yet deleted via _clearSliceRTT because the rendertarget
331 // was deleted by the user.
332 for (SliceTRT::const_iterator it = mSliceTRT.begin(); it != mSliceTRT.end(); ++it)
333 {
334 Root::getSingleton().getRenderSystem()->destroyRenderTarget((*it)->getName());
335 }
336 }
337 }
338
339 #if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID
updateTextureId(GLuint textureID)340 void GLES2TextureBuffer::updateTextureId(GLuint textureID)
341 {
342 mTextureID = textureID;
343 }
344 #endif
345
346 #if OGRE_NO_GLES3_SUPPORT == 0
upload(const PixelBox & data,const Image::Box & dest)347 void GLES2TextureBuffer::upload(const PixelBox &data, const Image::Box &dest)
348 {
349 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(mTarget, mTextureID);
350
351 OGRE_CHECK_GL_ERROR(glGenBuffers(1, &mBufferId));
352
353 // Upload data to PBO
354 OGRE_CHECK_GL_ERROR(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBufferId));
355
356 // Calculate size for all mip levels of the texture
357 size_t dataSize = 0;
358 if(mTarget == GL_TEXTURE_2D_ARRAY)
359 {
360 dataSize = PixelUtil::getMemorySize(dest.getWidth(), dest.getHeight(), dest.getDepth(), data.format);
361 }
362 else
363 {
364 dataSize = PixelUtil::getMemorySize(data.getWidth(), data.getHeight(), mDepth, data.format);
365 }
366 OGRE_CHECK_GL_ERROR(glBufferData(GL_PIXEL_UNPACK_BUFFER, dataSize, NULL,
367 GLES2HardwareBufferManager::getGLUsage(mUsage)));
368
369 #if OGRE_DEBUG_MODE
370 std::stringstream str;
371 str << "GLES2TextureBuffer::upload: " << mTextureID
372 << " pixel buffer: " << mBufferId
373 << " bytes: " << mSizeInBytes
374 << " dest depth: " << dest.getDepth()
375 << " dest front: " << dest.front
376 << " datasize: " << dataSize
377 << " face: " << mFace << " level: " << mLevel
378 << " width: " << mWidth << " height: "<< mHeight << " depth: " << mDepth
379 << " format: " << PixelUtil::getFormatName(mFormat)
380 << " data format: " << PixelUtil::getFormatName(data.format);
381 LogManager::getSingleton().logMessage(LML_NORMAL, str.str());
382 #endif
383 void* pBuffer = 0;
384 OGRE_CHECK_GL_ERROR(pBuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, dataSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_RANGE_BIT));
385
386 if(pBuffer == 0)
387 {
388 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
389 "Texture Buffer: Out of memory",
390 "GLES2TextureBuffer::upload");
391 }
392
393 // Copy to destination buffer
394 memcpy(pBuffer, data.data, dataSize);
395 GLboolean mapped = false;
396 OGRE_CHECK_GL_ERROR(mapped = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
397 if(!mapped)
398 {
399 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
400 "Buffer data corrupted, please reload",
401 "GLES2TextureBuffer::upload");
402 }
403
404 if (PixelUtil::isCompressed(data.format))
405 {
406 if(data.format != mFormat || !data.isConsecutive())
407 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
408 "Compressed images must be consecutive, in the source format",
409 "GLES2TextureBuffer::upload");
410
411 GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(mFormat);
412 // Data must be consecutive and at beginning of buffer as PixelStorei not allowed
413 // for compressed formats
414 switch(mTarget) {
415 case GL_TEXTURE_2D:
416 case GL_TEXTURE_CUBE_MAP:
417 OGRE_CHECK_GL_ERROR(glCompressedTexSubImage2D(mFaceTarget, mLevel,
418 dest.left, dest.top,
419 dest.getWidth(), dest.getHeight(),
420 format, data.getConsecutiveSize(),
421 NULL));
422 break;
423 case GL_TEXTURE_3D:
424 case GL_TEXTURE_2D_ARRAY:
425 OGRE_CHECK_GL_ERROR(glCompressedTexSubImage3D(mTarget, mLevel,
426 dest.left, dest.top, dest.front,
427 dest.getWidth(), dest.getHeight(), dest.getDepth(),
428 format, data.getConsecutiveSize(),
429 NULL));
430 break;
431 }
432
433 }
434 else
435 {
436 if(data.getWidth() != data.rowPitch)
437 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, data.rowPitch));
438 if(data.getHeight()*data.getWidth() != data.slicePitch)
439 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, (data.slicePitch/data.getWidth())));
440 if(data.left > 0 || data.top > 0 || data.front > 0)
441 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_SKIP_PIXELS, data.left + data.rowPitch * data.top + data.slicePitch * data.front));
442 if((data.getWidth()*PixelUtil::getNumElemBytes(data.format)) & 3) {
443 // Standard alignment of 4 is not right
444 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
445 }
446 #if OGRE_DEBUG_MODE
447 LogManager::getSingleton().logMessage("GLES2TextureBuffer::upload - ID: " + StringConverter::toString(mTextureID) +
448 " Target: " + StringConverter::toString(mTarget) +
449 " Format: " + PixelUtil::getFormatName(data.format) +
450 " Origin format: " + StringConverter::toString(GLES2PixelUtil::getGLOriginFormat(data.format), 0, std::ios::hex) +
451 " Data type: " + StringConverter::toString(GLES2PixelUtil::getGLOriginDataType(data.format), 0, ' ', std::ios::hex));
452 #endif
453 switch(mTarget) {
454 case GL_TEXTURE_2D:
455 case GL_TEXTURE_CUBE_MAP:
456 OGRE_CHECK_GL_ERROR(glTexSubImage2D(mFaceTarget, mLevel,
457 dest.left, dest.top,
458 dest.getWidth(), dest.getHeight(),
459 GLES2PixelUtil::getGLOriginFormat(data.format), GLES2PixelUtil::getGLOriginDataType(data.format),
460 NULL));
461 break;
462 case GL_TEXTURE_3D:
463 case GL_TEXTURE_2D_ARRAY:
464 OGRE_CHECK_GL_ERROR(glTexSubImage3D(
465 mTarget, mLevel,
466 dest.left, dest.top, dest.front,
467 dest.getWidth(), dest.getHeight(), dest.getDepth(),
468 GLES2PixelUtil::getGLOriginFormat(data.format), GLES2PixelUtil::getGLOriginDataType(data.format),
469 NULL));
470 break;
471 }
472 if (mUsage & TU_AUTOMIPMAP && (mTarget == GL_TEXTURE_2D_ARRAY || mTarget == GL_TEXTURE_3D))
473 {
474 OGRE_CHECK_GL_ERROR(glGenerateMipmap(mTarget));
475 }
476 }
477
478 // Delete PBO
479 OGRE_CHECK_GL_ERROR(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
480 OGRE_CHECK_GL_ERROR(glDeleteBuffers(1, &mBufferId));
481 mBufferId = 0;
482
483 // Restore defaults
484 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
485 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0));
486 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0));
487 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 4));
488 }
489
490 //-----------------------------------------------------------------------------
download(const PixelBox & data)491 void GLES2TextureBuffer::download(const PixelBox &data)
492 {
493 if(data.getWidth() != getWidth() ||
494 data.getHeight() != getHeight() ||
495 data.getDepth() != getDepth())
496 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "only download of entire buffer is supported by GL",
497 "GLES2TextureBuffer::download");
498
499 // Upload data to PBO
500 OGRE_CHECK_GL_ERROR(glGenBuffers(1, &mBufferId));
501 OGRE_CHECK_GL_ERROR(glBindBuffer(GL_PIXEL_PACK_BUFFER, mBufferId));
502
503 OGRE_CHECK_GL_ERROR(glBufferData(GL_PIXEL_PACK_BUFFER, mSizeInBytes, NULL,
504 GLES2HardwareBufferManager::getGLUsage(mUsage)));
505
506 #if OGRE_DEBUG_MODE
507 std::stringstream str;
508 str << "GLES2TextureBuffer::download: " << mTextureID
509 << " pixel buffer: " << mBufferId
510 << " bytes: " << mSizeInBytes
511 << " face: " << mFace << " level: " << mLevel
512 << " width: " << mWidth << " height: "<< mHeight << " depth: " << mDepth
513 << " format: " << PixelUtil::getFormatName(mFormat);
514 LogManager::getSingleton().logMessage(LML_NORMAL, str.str());
515 #endif
516 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(mTarget, mTextureID);
517 if(PixelUtil::isCompressed(data.format))
518 {
519 if(data.format != mFormat || !data.isConsecutive())
520 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
521 "Compressed images must be consecutive, in the source format",
522 "GLES2TextureBuffer::download");
523 }
524 else
525 {
526 if(data.getWidth() != data.rowPitch)
527 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)data.rowPitch));
528 if(data.left > 0 || data.top > 0 || data.front > 0)
529 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_PACK_SKIP_PIXELS, (GLint)(data.left + data.rowPitch * data.top + data.slicePitch * data.front)));
530 if((data.getWidth()*PixelUtil::getNumElemBytes(data.format)) & 3) {
531 // Standard alignment of 4 is not right
532 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, 1));
533 }
534
535 // Restore defaults
536 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_PACK_ROW_LENGTH, 0));
537 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_PACK_SKIP_PIXELS, 0));
538 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, 4));
539 }
540
541 GLint offsetInBytes = 0;
542 size_t width = mWidth;
543 size_t height = mHeight;
544 size_t depth = mDepth;
545 for(GLint i = 0; i < mLevel; i++)
546 {
547 offsetInBytes += PixelUtil::getMemorySize(width, height, depth, data.format);
548
549 if(width > 1)
550 width = width / 2;
551 if(height > 1)
552 height = height / 2;
553 if(depth > 1)
554 depth = depth / 2;
555 }
556
557 void* pBuffer;
558 OGRE_CHECK_GL_ERROR(pBuffer = glMapBufferRange(GL_PIXEL_PACK_BUFFER, offsetInBytes, mSizeInBytes, GL_MAP_READ_BIT));
559
560 if(pBuffer == 0)
561 {
562 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
563 "Texture Buffer: Out of memory",
564 "GLES2TextureBuffer::download");
565 }
566
567 // Copy to destination buffer
568 memcpy(data.data, pBuffer, mSizeInBytes);
569
570 GLboolean mapped;
571 OGRE_CHECK_GL_ERROR(mapped = glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
572 if(!mapped)
573 {
574 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
575 "Buffer data corrupted, please reload",
576 "GLES2TextureBuffer::download");
577 }
578
579 // Delete PBO
580 OGRE_CHECK_GL_ERROR(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
581 OGRE_CHECK_GL_ERROR(glDeleteBuffers(1, &mBufferId));
582 mBufferId = 0;
583 }
584 #else
upload(const PixelBox & data,const Image::Box & dest)585 void GLES2TextureBuffer::upload(const PixelBox &data, const Image::Box &dest)
586 {
587 #if OGRE_DEBUG_MODE
588 LogManager::getSingleton().logMessage("GLES2TextureBuffer::upload - ID: " + StringConverter::toString(mTextureID) +
589 " Target: " + StringConverter::toString(mTarget) +
590 " Format: " + PixelUtil::getFormatName(data.format) +
591 " Origin format: " + StringConverter::toString(GLES2PixelUtil::getGLOriginFormat(data.format), 0, std::ios::hex) +
592 " Data type: " + StringConverter::toString(GLES2PixelUtil::getGLOriginDataType(data.format), 0, ' ', std::ios::hex));
593 #endif
594 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(mTarget, mTextureID);
595
596 if (PixelUtil::isCompressed(data.format))
597 {
598 if(data.format != mFormat || !data.isConsecutive())
599 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
600 "Compressed images must be consecutive, in the source format",
601 "GLES2TextureBuffer::upload");
602
603 GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(mFormat);
604 // Data must be consecutive and at beginning of buffer as PixelStorei not allowed
605 // for compressed formats
606 if (dest.left == 0 && dest.top == 0)
607 {
608 OGRE_CHECK_GL_ERROR(glCompressedTexImage2D(mFaceTarget, mLevel,
609 format,
610 dest.getWidth(),
611 dest.getHeight(),
612 0,
613 data.getConsecutiveSize(),
614 data.data));
615 }
616 else
617 {
618 OGRE_CHECK_GL_ERROR(glCompressedTexSubImage2D(mFaceTarget, mLevel,
619 dest.left, dest.top,
620 dest.getWidth(), dest.getHeight(),
621 format, data.getConsecutiveSize(),
622 data.data));
623 }
624 }
625 else if (mSoftwareMipmap)
626 {
627 if (data.getWidth() != data.rowPitch)
628 {
629 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
630 "Unsupported texture format",
631 "GLES2TextureBuffer::upload");
632 }
633
634 if (data.getHeight() * data.getWidth() != data.slicePitch)
635 {
636 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
637 "Unsupported texture format",
638 "GLES2TextureBuffer::upload");
639 }
640
641 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
642 buildMipmaps(data);
643 }
644 else
645 {
646 if (data.getWidth() != data.rowPitch)
647 {
648 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
649 "Unsupported texture format",
650 "GLES2TextureBuffer::upload");
651 }
652
653 if (data.getHeight() * data.getWidth() != data.slicePitch)
654 {
655 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
656 "Unsupported texture format",
657 "GLES2TextureBuffer::upload");
658 }
659
660 if ((data.getWidth() * PixelUtil::getNumElemBytes(data.format)) & 3) {
661 // Standard alignment of 4 is not right
662 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
663 }
664
665 switch(mTarget)
666 {
667 case GL_TEXTURE_2D:
668 case GL_TEXTURE_CUBE_MAP:
669 OGRE_CHECK_GL_ERROR(glTexSubImage2D(mFaceTarget,
670 mLevel,
671 dest.left, dest.top,
672 dest.getWidth(), dest.getHeight(),
673 GLES2PixelUtil::getGLOriginFormat(data.format),
674 GLES2PixelUtil::getGLOriginDataType(data.format),
675 data.data));
676 break;
677 }
678 }
679
680 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 4));
681 }
682
683 //-----------------------------------------------------------------------------
download(const PixelBox & data)684 void GLES2TextureBuffer::download(const PixelBox &data)
685 {
686 if(data.getWidth() != getWidth() ||
687 data.getHeight() != getHeight() ||
688 data.getDepth() != getDepth())
689 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "only download of entire buffer is supported by GL ES",
690 "GLES2TextureBuffer::download");
691
692 if(PixelUtil::isCompressed(data.format))
693 {
694 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
695 "Compressed images cannot be downloaded by GL ES",
696 "GLES2TextureBuffer::download");
697 }
698
699 if((data.getWidth()*PixelUtil::getNumElemBytes(data.format)) & 3) {
700 // Standard alignment of 4 is not right
701 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, 1));
702 }
703
704 GLint currentFBO = 0;
705 GLuint tempFBO = 0;
706 OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFBO));
707 OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &tempFBO));
708 OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, tempFBO));
709
710 // Construct a temp PixelBox that is RGBA because GL_RGBA/GL_UNSIGNED_BYTE is the only combination that is
711 // guaranteed to work on all platforms.
712 int sizeInBytes = PixelUtil::getMemorySize(data.getWidth(), data.getHeight(), data.getDepth(), PF_A8B8G8R8);
713 PixelBox tempBox = PixelBox(data.getWidth(), data.getHeight(), data.getDepth(), PF_A8B8G8R8);
714 tempBox.data = new uint8[sizeInBytes];
715
716 switch (mTarget)
717 {
718 case GL_TEXTURE_2D:
719 case GL_TEXTURE_CUBE_MAP:
720 OGRE_CHECK_GL_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureID, 0));
721 OGRE_CHECK_GL_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER));
722 OGRE_CHECK_GL_ERROR(glReadPixels(0, 0, data.getWidth(), data.getHeight(),
723 GL_RGBA,
724 GL_UNSIGNED_BYTE,
725 tempBox.data));
726 break;
727 }
728
729 PixelUtil::bulkPixelConversion(tempBox, data);
730
731 delete[] (uint8*) tempBox.data;
732 tempBox.data = 0;
733
734 // Restore defaults
735 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, 4));
736 OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, currentFBO));
737 OGRE_CHECK_GL_ERROR(glDeleteFramebuffers(1, &tempFBO));
738 }
739 #endif
740 //-----------------------------------------------------------------------------
bindToFramebuffer(GLenum attachment,size_t zoffset)741 void GLES2TextureBuffer::bindToFramebuffer(GLenum attachment, size_t zoffset)
742 {
743 assert(zoffset < mDepth);
744 OGRE_CHECK_GL_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
745 mFaceTarget, mTextureID, mLevel));
746 }
747
copyFromFramebuffer(size_t zoffset)748 void GLES2TextureBuffer::copyFromFramebuffer(size_t zoffset)
749 {
750 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(mTarget, mTextureID);
751 OGRE_CHECK_GL_ERROR(glCopyTexSubImage2D(mFaceTarget, mLevel, 0, 0, 0, 0, mWidth, mHeight));
752 }
753
754 //-----------------------------------------------------------------------------
blit(const HardwarePixelBufferSharedPtr & src,const Image::Box & srcBox,const Image::Box & dstBox)755 void GLES2TextureBuffer::blit(const HardwarePixelBufferSharedPtr &src, const Image::Box &srcBox, const Image::Box &dstBox)
756 {
757 GLES2TextureBuffer *srct = static_cast<GLES2TextureBuffer *>(src.getPointer());
758 // TODO: Check for FBO support first
759 // Destination texture must be 2D or Cube
760 // Source texture must be 2D
761 if(((src->getUsage() & TU_RENDERTARGET) == 0 && (srct->mTarget == GL_TEXTURE_2D))
762 #if OGRE_NO_GLES3_SUPPORT == 0
763 || ((srct->mTarget == GL_TEXTURE_3D) && (mTarget != GL_TEXTURE_2D_ARRAY))
764 #endif
765 )
766 {
767 blitFromTexture(srct, srcBox, dstBox);
768 }
769 else
770 {
771 GLES2HardwarePixelBuffer::blit(src, srcBox, dstBox);
772 }
773 }
774
775 //-----------------------------------------------------------------------------
776 // Very fast texture-to-texture blitter and hardware bi/trilinear scaling implementation using FBO
777 // Destination texture must be 1D, 2D, 3D, or Cube
778 // Source texture must be 1D, 2D or 3D
779 // Supports compressed formats as both source and destination format, it will use the hardware DXT compressor
780 // if available.
781 // @author W.J. van der Laan
blitFromTexture(GLES2TextureBuffer * src,const Image::Box & srcBox,const Image::Box & dstBox)782 void GLES2TextureBuffer::blitFromTexture(GLES2TextureBuffer *src, const Image::Box &srcBox, const Image::Box &dstBox)
783 {
784 return; // todo - add a shader attach...
785 // std::cerr << "GLES2TextureBuffer::blitFromTexture " <<
786 // src->mTextureID << ":" << srcBox.left << "," << srcBox.top << "," << srcBox.right << "," << srcBox.bottom << " " <<
787 // mTextureID << ":" << dstBox.left << "," << dstBox.top << "," << dstBox.right << "," << dstBox.bottom << std::endl;
788
789 // Store reference to FBO manager
790 GLES2FBOManager *fboMan = static_cast<GLES2FBOManager *>(GLES2RTTManager::getSingletonPtr());
791
792 RenderSystem* rsys = Root::getSingleton().getRenderSystem();
793 rsys->_disableTextureUnitsFrom(0);
794 glActiveTexture(GL_TEXTURE0);
795
796 // Disable alpha, depth and scissor testing, disable blending,
797 // and disable culling
798 glDisable(GL_DEPTH_TEST);
799 glDisable(GL_SCISSOR_TEST);
800 glDisable(GL_BLEND);
801 glDisable(GL_CULL_FACE);
802
803 // Set up source texture
804 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(src->mTarget, src->mTextureID);
805
806 // Set filtering modes depending on the dimensions and source
807 if(srcBox.getWidth() == dstBox.getWidth() &&
808 srcBox.getHeight() == dstBox.getHeight() &&
809 srcBox.getDepth() == dstBox.getDepth())
810 {
811 // Dimensions match -- use nearest filtering (fastest and pixel correct)
812 if(src->mUsage & TU_AUTOMIPMAP)
813 {
814 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST));
815 }
816 else
817 {
818 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
819 }
820 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
821 }
822 else
823 {
824 // Dimensions don't match -- use bi or trilinear filtering depending on the
825 // source texture.
826 if(src->mUsage & TU_AUTOMIPMAP)
827 {
828 // Automatic mipmaps, we can safely use trilinear filter which
829 // brings greatly improved quality for minimisation.
830 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
831 }
832 else
833 {
834 // Manual mipmaps, stay safe with bilinear filtering so that no
835 // intermipmap leakage occurs.
836 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
837 }
838 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
839 }
840 // Clamp to edge (fastest)
841 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
842 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
843 #if OGRE_NO_GLES3_SUPPORT == 0
844 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE));
845
846 // Set origin base level mipmap to make sure we source from the right mip
847 // level.
848 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_BASE_LEVEL, src->mLevel));
849 #endif
850 // Store old binding so it can be restored later
851 GLint oldfb;
852 OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb));
853
854 // Set up temporary FBO
855 OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fboMan->getTemporaryFBO()));
856
857 GLuint tempTex = 0;
858 if(!fboMan->checkFormat(mFormat))
859 {
860 // If target format not directly supported, create intermediate texture
861 GLenum tempFormat = GLES2PixelUtil::getClosestGLInternalFormat(fboMan->getSupportedAlternative(mFormat));
862 OGRE_CHECK_GL_ERROR(glGenTextures(1, &tempTex));
863 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(GL_TEXTURE_2D, tempTex);
864
865 if(getGLES2SupportRef()->checkExtension("GL_APPLE_texture_max_level") || gleswIsSupported(3, 0))
866 getGLES2SupportRef()->getStateCacheManager()->setTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL_APPLE, 0);
867
868 // Allocate temporary texture of the size of the destination area
869 OGRE_CHECK_GL_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, tempFormat,
870 GLES2PixelUtil::optionalPO2(dstBox.getWidth()), GLES2PixelUtil::optionalPO2(dstBox.getHeight()),
871 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
872 OGRE_CHECK_GL_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
873 GL_TEXTURE_2D, tempTex, 0));
874 // Set viewport to size of destination slice
875 OGRE_CHECK_GL_ERROR(glViewport(0, 0, dstBox.getWidth(), dstBox.getHeight()));
876 }
877 else
878 {
879 // We are going to bind directly, so set viewport to size and position of destination slice
880 OGRE_CHECK_GL_ERROR(glViewport(dstBox.left, dstBox.top, dstBox.getWidth(), dstBox.getHeight()));
881 }
882
883 // Process each destination slice
884 for(size_t slice = dstBox.front; slice < dstBox.back; ++slice)
885 {
886 if(!tempTex)
887 {
888 // Bind directly
889 bindToFramebuffer(GL_COLOR_ATTACHMENT0, slice);
890 }
891
892 // Calculate source texture coordinates
893 float u1 = (float)srcBox.left / (float)src->mWidth;
894 float v1 = (float)srcBox.top / (float)src->mHeight;
895 float u2 = (float)srcBox.right / (float)src->mWidth;
896 float v2 = (float)srcBox.bottom / (float)src->mHeight;
897 // Calculate source slice for this destination slice
898 float w = (float)(slice - dstBox.front) / (float)dstBox.getDepth();
899 // Get slice # in source
900 w = w * (float)srcBox.getDepth() + srcBox.front;
901 // Normalise to texture coordinate in 0.0 .. 1.0
902 w = (w+0.5f) / (float)src->mDepth;
903
904 // Finally we're ready to rumble
905 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(src->mTarget, src->mTextureID);
906 OGRE_CHECK_GL_ERROR(glEnable(src->mTarget));
907
908 GLfloat squareVertices[] = {
909 -1.0f, -1.0f,
910 1.0f, -1.0f,
911 -1.0f, 1.0f,
912 1.0f, 1.0f,
913 };
914 GLfloat texCoords[] = {
915 u1, v1, w,
916 u2, v1, w,
917 u2, v2, w,
918 u1, v2, w
919 };
920
921 GLuint posAttrIndex = 0;
922 GLuint texAttrIndex = 0;
923 if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS))
924 {
925 GLSLESProgramPipeline* programPipeline = GLSLESProgramPipelineManager::getSingleton().getActiveProgramPipeline();
926 posAttrIndex = (GLuint)programPipeline->getAttributeIndex(VES_POSITION, 0);
927 texAttrIndex = (GLuint)programPipeline->getAttributeIndex(VES_TEXTURE_COORDINATES, 0);
928 }
929 else
930 {
931 GLSLESLinkProgram* linkProgram = GLSLESLinkProgramManager::getSingleton().getActiveLinkProgram();
932 posAttrIndex = (GLuint)linkProgram->getAttributeIndex(VES_POSITION, 0);
933 texAttrIndex = (GLuint)linkProgram->getAttributeIndex(VES_TEXTURE_COORDINATES, 0);
934 }
935
936 // Draw the textured quad
937 OGRE_CHECK_GL_ERROR(glVertexAttribPointer(posAttrIndex,
938 2,
939 GL_FLOAT,
940 0,
941 0,
942 squareVertices));
943 OGRE_CHECK_GL_ERROR(glEnableVertexAttribArray(posAttrIndex));
944 OGRE_CHECK_GL_ERROR(glVertexAttribPointer(texAttrIndex,
945 3,
946 GL_FLOAT,
947 0,
948 0,
949 texCoords));
950 OGRE_CHECK_GL_ERROR(glEnableVertexAttribArray(texAttrIndex));
951
952 OGRE_CHECK_GL_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
953
954 OGRE_CHECK_GL_ERROR(glDisable(src->mTarget));
955
956 if(tempTex)
957 {
958 // Copy temporary texture
959 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(mTarget, mTextureID);
960 switch(mTarget)
961 {
962 case GL_TEXTURE_2D:
963 case GL_TEXTURE_CUBE_MAP:
964 OGRE_CHECK_GL_ERROR(glCopyTexSubImage2D(mFaceTarget, mLevel,
965 dstBox.left, dstBox.top,
966 0, 0, dstBox.getWidth(), dstBox.getHeight()));
967 break;
968 }
969 }
970 }
971 // Finish up
972 if(!tempTex)
973 {
974 // Generate mipmaps
975 if(mUsage & TU_AUTOMIPMAP)
976 {
977 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(mTarget, mTextureID);
978 OGRE_CHECK_GL_ERROR(glGenerateMipmap(mTarget));
979 }
980 }
981
982 // Reset source texture to sane state
983 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(src->mTarget, src->mTextureID);
984
985 #if OGRE_NO_GLES3_SUPPORT == 0
986 OGRE_CHECK_GL_ERROR(glTexParameteri(src->mTarget, GL_TEXTURE_BASE_LEVEL, 0));
987
988 // Detach texture from temporary framebuffer
989 if(mFormat == PF_DEPTH)
990 {
991 OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
992 GL_RENDERBUFFER, 0));
993 }
994 else
995 #endif
996 {
997 OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
998 GL_RENDERBUFFER, 0));
999 }
1000 // Restore old framebuffer
1001 OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, oldfb));
1002 OGRE_CHECK_GL_ERROR(glDeleteTextures(1, &tempTex));
1003 }
1004 //-----------------------------------------------------------------------------
1005 // blitFromMemory doing hardware trilinear scaling
blitFromMemory(const PixelBox & src_orig,const Image::Box & dstBox)1006 void GLES2TextureBuffer::blitFromMemory(const PixelBox &src_orig, const Image::Box &dstBox)
1007 {
1008 // Fall back to normal GLHardwarePixelBuffer::blitFromMemory in case
1009 // - FBO is not supported
1010 // - Either source or target is luminance due doesn't looks like supported by hardware
1011 // - the source dimensions match the destination ones, in which case no scaling is needed
1012 // TODO: Check that extension is NOT available
1013 if(PixelUtil::isLuminance(src_orig.format) ||
1014 PixelUtil::isLuminance(mFormat) ||
1015 (src_orig.getWidth() == dstBox.getWidth() &&
1016 src_orig.getHeight() == dstBox.getHeight() &&
1017 src_orig.getDepth() == dstBox.getDepth()))
1018 {
1019 GLES2HardwarePixelBuffer::blitFromMemory(src_orig, dstBox);
1020 return;
1021 }
1022 if(!mBuffer.contains(dstBox))
1023 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Destination box out of range",
1024 "GLES2TextureBuffer::blitFromMemory");
1025 // For scoped deletion of conversion buffer
1026 MemoryDataStreamPtr buf;
1027 PixelBox src;
1028
1029 // First, convert the srcbox to a OpenGL compatible pixel format
1030 if(GLES2PixelUtil::getGLOriginFormat(src_orig.format) == 0)
1031 {
1032 // Convert to buffer internal format
1033 buf.bind(OGRE_NEW MemoryDataStream(PixelUtil::getMemorySize(src_orig.getWidth(), src_orig.getHeight(),
1034 src_orig.getDepth(), mFormat)));
1035 src = PixelBox(src_orig.getWidth(), src_orig.getHeight(), src_orig.getDepth(), mFormat, buf->getPtr());
1036 PixelUtil::bulkPixelConversion(src_orig, src);
1037 }
1038 else
1039 {
1040 // No conversion needed
1041 src = src_orig;
1042 }
1043
1044 // Create temporary texture to store source data
1045 GLuint id = 0;
1046 GLenum target =
1047 #if OGRE_NO_GLES3_SUPPORT == 0
1048 (src.getDepth() != 1) ? GL_TEXTURE_3D :
1049 #endif
1050 GL_TEXTURE_2D;
1051
1052 GLsizei width = (GLsizei)GLES2PixelUtil::optionalPO2(src.getWidth());
1053 GLsizei height = (GLsizei)GLES2PixelUtil::optionalPO2(src.getHeight());
1054 GLsizei depth = (GLsizei)GLES2PixelUtil::optionalPO2(src.getDepth());
1055 GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(src.format);
1056
1057 // Generate texture name
1058 OGRE_CHECK_GL_ERROR(glGenTextures(1, &id));
1059
1060 // Set texture type
1061 getGLES2SupportRef()->getStateCacheManager()->bindGLTexture(target, id);
1062
1063 if(getGLES2SupportRef()->checkExtension("GL_APPLE_texture_max_level") || gleswIsSupported(3, 0))
1064 getGLES2SupportRef()->getStateCacheManager()->setTexParameteri(target, GL_TEXTURE_MAX_LEVEL_APPLE, 1000);
1065
1066 // Allocate texture memory
1067 #if OGRE_NO_GLES3_SUPPORT == 0
1068 if(src.getDepth() != 1)
1069 {
1070 OGRE_CHECK_GL_ERROR(glTexStorage3D(GL_TEXTURE_3D, 1, format, GLsizei(width), GLsizei(height), GLsizei(depth)));
1071 }
1072 else
1073 {
1074 OGRE_CHECK_GL_ERROR(glTexStorage2D(GL_TEXTURE_2D, 1, format, GLsizei(width), GLsizei(height)));
1075 }
1076 #else
1077 GLenum datatype = (GLsizei)GLES2PixelUtil::getGLOriginDataType(src.format);
1078 OGRE_CHECK_GL_ERROR(glTexImage2D(target, 0, format, width, height, 0, format, datatype, 0));
1079 #endif
1080
1081 // GL texture buffer
1082 GLES2TextureBuffer tex(StringUtil::BLANK, target, id, width, height, depth, format, src.format,
1083 0, 0, (Usage)(TU_AUTOMIPMAP|HBU_STATIC_WRITE_ONLY), false, false, 0);
1084
1085 // Upload data to 0,0,0 in temporary texture
1086 Image::Box tempTarget(0, 0, 0, src.getWidth(), src.getHeight(), src.getDepth());
1087 tex.upload(src, tempTarget);
1088
1089 // Blit
1090 blitFromTexture(&tex, tempTarget, dstBox);
1091
1092 // Delete temp texture
1093 OGRE_CHECK_GL_ERROR(glDeleteTextures(1, &id));
1094 }
1095
getRenderTarget(size_t zoffset)1096 RenderTexture *GLES2TextureBuffer::getRenderTarget(size_t zoffset)
1097 {
1098 assert(mUsage & TU_RENDERTARGET);
1099 assert(zoffset < mDepth);
1100 return mSliceTRT[zoffset];
1101 }
1102
buildMipmaps(const PixelBox & data)1103 void GLES2TextureBuffer::buildMipmaps(const PixelBox &data)
1104 {
1105 GLsizei width;
1106 GLsizei height;
1107 GLsizei depth;
1108 int logW;
1109 int logH;
1110 int level;
1111 PixelBox scaled = data;
1112 scaled.data = data.data;
1113 scaled.left = data.left;
1114 scaled.right = data.right;
1115 scaled.top = data.top;
1116 scaled.bottom = data.bottom;
1117 scaled.front = data.front;
1118 scaled.back = data.back;
1119
1120 width = (GLsizei)data.getWidth();
1121 height = (GLsizei)data.getHeight();
1122 depth = (GLsizei)data.getDepth();
1123
1124 logW = computeLog(width);
1125 logH = computeLog(height);
1126 level = (logW > logH ? logW : logH);
1127
1128 for (int mip = 0; mip <= level; mip++)
1129 {
1130 GLenum glFormat = GLES2PixelUtil::getGLOriginFormat(scaled.format);
1131 GLenum dataType = GLES2PixelUtil::getGLOriginDataType(scaled.format);
1132 GLenum internalFormat = glFormat;
1133 #if OGRE_NO_GLES3_SUPPORT == 0
1134 // In GL ES 3, the internalformat and format parameters do not need to be identical
1135 internalFormat = GLES2PixelUtil::getClosestGLInternalFormat(scaled.format);
1136 #endif
1137 switch(mTarget)
1138 {
1139 case GL_TEXTURE_2D:
1140 case GL_TEXTURE_CUBE_MAP:
1141 OGRE_CHECK_GL_ERROR(glTexImage2D(mFaceTarget,
1142 mip,
1143 internalFormat,
1144 width, height,
1145 0,
1146 glFormat,
1147 dataType,
1148 scaled.data));
1149 break;
1150 #if OGRE_NO_GLES3_SUPPORT == 0
1151 case GL_TEXTURE_3D:
1152 case GL_TEXTURE_2D_ARRAY:
1153 OGRE_CHECK_GL_ERROR(glTexImage3D(mFaceTarget,
1154 mip,
1155 internalFormat,
1156 width, height, depth,
1157 0,
1158 glFormat,
1159 dataType,
1160 scaled.data));
1161 break;
1162 #endif
1163 }
1164
1165 if (mip != 0)
1166 {
1167 OGRE_DELETE[] (uint8*) scaled.data;
1168 scaled.data = 0;
1169 }
1170
1171 if (width > 1)
1172 {
1173 width = width / 2;
1174 }
1175
1176 if (height > 1)
1177 {
1178 height = height / 2;
1179 }
1180
1181 size_t sizeInBytes = PixelUtil::getMemorySize(width, height, 1,
1182 data.format);
1183 scaled = PixelBox(width, height, 1, data.format);
1184 scaled.data = new uint8[sizeInBytes];
1185 Image::scale(data, scaled, Image::FILTER_LINEAR);
1186 }
1187
1188 // Delete the scaled data for the last level
1189 if (level > 0)
1190 {
1191 delete[] (uint8*) scaled.data;
1192 scaled.data = 0;
1193 }
1194 }
1195
1196 //********* GLES2RenderBuffer
1197 //-----------------------------------------------------------------------------
GLES2RenderBuffer(GLenum format,size_t width,size_t height,GLsizei numSamples)1198 GLES2RenderBuffer::GLES2RenderBuffer(GLenum format, size_t width, size_t height, GLsizei numSamples):
1199 GLES2HardwarePixelBuffer(width, height, 1, GLES2PixelUtil::getClosestOGREFormat(format, GL_RGBA), HBU_WRITE_ONLY)
1200 {
1201 mGLInternalFormat = format;
1202 mNumSamples = numSamples;
1203
1204 // Generate renderbuffer
1205 OGRE_CHECK_GL_ERROR(glGenRenderbuffers(1, &mRenderbufferID));
1206
1207 // Bind it to FBO
1208 OGRE_CHECK_GL_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, mRenderbufferID));
1209
1210 // Allocate storage for depth buffer
1211 if (mNumSamples > 0)
1212 {
1213 if(getGLES2SupportRef()->checkExtension("GL_APPLE_framebuffer_multisample") || gleswIsSupported(3, 0))
1214 {
1215 OGRE_CHECK_GL_ERROR(glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER,
1216 mNumSamples, mGLInternalFormat, mWidth, mHeight));
1217 }
1218 }
1219 else
1220 {
1221 OGRE_CHECK_GL_ERROR(glRenderbufferStorage(GL_RENDERBUFFER, mGLInternalFormat,
1222 mWidth, mHeight));
1223 }
1224 }
1225 //-----------------------------------------------------------------------------
~GLES2RenderBuffer()1226 GLES2RenderBuffer::~GLES2RenderBuffer()
1227 {
1228 OGRE_CHECK_GL_ERROR(glDeleteRenderbuffers(1, &mRenderbufferID));
1229 }
1230 //-----------------------------------------------------------------------------
bindToFramebuffer(GLenum attachment,size_t zoffset)1231 void GLES2RenderBuffer::bindToFramebuffer(GLenum attachment, size_t zoffset)
1232 {
1233 assert(zoffset < mDepth);
1234 OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
1235 GL_RENDERBUFFER, mRenderbufferID));
1236 }
1237 }
1238