/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef GL_TEXTURE_WRAP_R #define GL_TEXTURE_WRAP_R 0x8072 #endif #ifndef GL_TEXTURE_MAX_LEVEL #define GL_TEXTURE_MAX_LEVEL 0x813D #endif #ifndef GL_UNPACK_CLIENT_STORAGE_APPLE #define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 #endif #ifndef GL_APPLE_vertex_array_range #define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D #define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E #define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F #define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 #define GL_STORAGE_CACHED_APPLE 0x85BE #define GL_STORAGE_SHARED_APPLE 0x85BF #endif #ifndef GL_RGB565 #define GL_RGB565 0x8D62 #endif #if 0 #define CHECK_CONSISTENCY checkConsistency(); #else #define CHECK_CONSISTENCY #endif namespace osg { ApplicationUsageProxy Texture_e0(ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_MAX_TEXTURE_SIZE","Set the maximum size of textures."); ApplicationUsageProxy Texture_e1(ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_GL_TEXTURE_STORAGE","ON|OFF or ENABLE|DISABLE, Enables/disables usage of glTexStorage for textures where supported, default is ENABLED."); struct InternalPixelRelations { GLenum sizedInternalFormat; GLint internalFormat; GLenum type; }; InternalPixelRelations sizedInternalFormats[] = { { GL_R8UI , GL_RED_INTEGER_EXT , GL_UNSIGNED_BYTE } , { GL_R8I , GL_RED_INTEGER_EXT , GL_BYTE } , { GL_R16UI , GL_RED_INTEGER_EXT , GL_UNSIGNED_SHORT } , { GL_R16I , GL_RED_INTEGER_EXT , GL_SHORT } , { GL_R32UI , GL_RED_INTEGER_EXT , GL_UNSIGNED_INT } , { GL_R32I , GL_RED_INTEGER_EXT , GL_INT } , { GL_RG8UI , GL_RG_INTEGER , GL_UNSIGNED_BYTE } , { GL_RG8I , GL_RG_INTEGER , GL_BYTE } , { GL_RG16UI , GL_RG_INTEGER , GL_UNSIGNED_SHORT } , { GL_RG16I , GL_RG_INTEGER , GL_SHORT } , { GL_RG32UI , GL_RG_INTEGER , GL_UNSIGNED_INT } , { GL_RG32I , GL_RG_INTEGER , GL_INT } , { GL_RGB8UI_EXT , GL_RGB_INTEGER_EXT , GL_UNSIGNED_BYTE } , { GL_RGB8I_EXT , GL_RGB_INTEGER_EXT , GL_BYTE } , { GL_RGB16UI_EXT , GL_RGB_INTEGER_EXT , GL_UNSIGNED_SHORT } , { GL_RGB16I_EXT , GL_RGB_INTEGER_EXT , GL_SHORT } , { GL_RGB32UI_EXT , GL_RGB_INTEGER_EXT , GL_UNSIGNED_INT } , { GL_RGB32I_EXT , GL_RGB_INTEGER_EXT , GL_INT } , { GL_RGBA8UI_EXT , GL_RGBA_INTEGER_EXT , GL_UNSIGNED_BYTE } , { GL_RGBA8I_EXT , GL_RGBA_INTEGER_EXT , GL_BYTE } // , { GL_RGB10_A2UI_EXT , GL_RGBA_INTEGER_EXT , GL_UNSIGNED_INT_2_10_10_10_REV } , { GL_RGBA16UI_EXT , GL_RGBA_INTEGER_EXT , GL_UNSIGNED_SHORT } , { GL_RGBA16I_EXT , GL_RGBA_INTEGER_EXT , GL_SHORT } , { GL_RGBA32I_EXT , GL_RGBA_INTEGER_EXT , GL_INT } , { GL_RGBA32UI_EXT , GL_RGBA_INTEGER_EXT , GL_UNSIGNED_INT } , { GL_R8_SNORM , GL_RED , GL_BYTE } , { GL_R16_SNORM , GL_RED , GL_SHORT } , { GL_RG8_SNORM , GL_RG , GL_BYTE } , { GL_RG16_SNORM , GL_RG , GL_SHORT } , { GL_RGB8_SNORM , GL_RGB , GL_BYTE } , { GL_RGB16_SNORM , GL_RGB , GL_SHORT } , { GL_RGBA8_SNORM , GL_RGBA , GL_BYTE } , { GL_SRGB8 , GL_RGB , GL_UNSIGNED_BYTE } , { GL_SRGB8_ALPHA8 , GL_RGBA , GL_UNSIGNED_BYTE } , { GL_R8 , GL_RED , GL_UNSIGNED_BYTE } , { GL_R16F , GL_RED , GL_HALF_FLOAT } , { GL_R16F , GL_RED , GL_FLOAT } , { GL_R32F , GL_RED , GL_FLOAT } , { GL_RG8 , GL_RG , GL_UNSIGNED_BYTE } , { GL_RG16F , GL_RG , GL_HALF_FLOAT } , { GL_RG16F , GL_RG , GL_FLOAT } , { GL_RG32F , GL_RG , GL_FLOAT } // , ( GL_RGBA2 , GL_RGB , UNKNOWN ) , { GL_R3_G3_B2 , GL_RGB , GL_UNSIGNED_BYTE_3_3_2 } , { GL_R3_G3_B2 , GL_RGB , GL_UNSIGNED_BYTE_2_3_3_REV } , { GL_RGB4 , GL_RGB , GL_UNSIGNED_SHORT_4_4_4_4 } , { GL_RGB4 , GL_RGB , GL_UNSIGNED_SHORT_4_4_4_4_REV } , { GL_RGB5 , GL_RGB , GL_UNSIGNED_SHORT_5_5_5_1 } , { GL_RGB5 , GL_RGB , GL_UNSIGNED_SHORT_1_5_5_5_REV } , { GL_RGB565 , GL_RGB , GL_UNSIGNED_BYTE } , { GL_RGB565 , GL_RGB , GL_UNSIGNED_SHORT_5_6_5 } , { GL_RGB565 , GL_RGB , GL_UNSIGNED_SHORT_5_6_5_REV } , { GL_RGB8 , GL_RGB , GL_UNSIGNED_BYTE } // , { GL_RGB9_E5 , GL_RGB , GL_UNSIGNED_INT_9_9_9_5, } // , { GL_RGB9_E5 , GL_RGB , GL_UNSIGNED_INT_5_9_9_9_REV, } // , { GL_RGB9_E5 , GL_RGB , GL_HALF_FLOAT } // , { GL_RGB9_E5 , GL_RGB , GL_FLOAT } // , { GL_R11F_G11F_B10F , GL_RGB , GL_UNSIGNED_INT_10F_11F_11F_REV } // , { GL_R11F_G11F_B10F , GL_RGB , GL_HALF_FLOAT } // , { GL_R11F_G11F_B10F , GL_RGB , GL_FLOAT } , { GL_RGB10 , GL_RGB , GL_UNSIGNED_INT_2_10_10_10_REV } , { GL_RGB10 , GL_RGB , GL_UNSIGNED_INT_10_10_10_2 } , { GL_RGB12 , GL_RGB , GL_UNSIGNED_SHORT } , { GL_RGB16F_ARB , GL_RGB , GL_HALF_FLOAT } , { GL_RGB16F_ARB , GL_RGB , GL_FLOAT } , { GL_RGB32F_ARB , GL_RGB , GL_FLOAT } , { GL_RGB5_A1 , GL_RGBA , GL_UNSIGNED_BYTE } , { GL_RGB5_A1 , GL_RGBA , GL_UNSIGNED_SHORT_5_5_5_1 } , { GL_RGB5_A1 , GL_RGBA , GL_UNSIGNED_SHORT_1_5_5_5_REV } , { GL_RGB5_A1 , GL_RGBA , GL_UNSIGNED_INT_10_10_10_2 } , { GL_RGB5_A1 , GL_RGBA , GL_UNSIGNED_INT_2_10_10_10_REV } , { GL_RGBA4 , GL_RGBA , GL_UNSIGNED_BYTE } , { GL_RGBA4 , GL_RGBA , GL_UNSIGNED_SHORT_4_4_4_4 } , { GL_RGBA4 , GL_RGBA , GL_UNSIGNED_SHORT_4_4_4_4_REV } , { GL_RGBA8 , GL_RGBA , GL_UNSIGNED_BYTE } , { GL_RGB10_A2 , GL_RGBA , GL_UNSIGNED_INT_10_10_10_2 } , { GL_RGB10_A2 , GL_RGBA , GL_UNSIGNED_INT_2_10_10_10_REV } , { GL_RGBA12 , GL_RGBA , GL_UNSIGNED_SHORT } // , { GL_RGBA16F , GL_RGBA , GL_HALF_FLOAT } // , { GL_RGBA16F , GL_RGBA , GL_FLOAT } // , { GL_RGBA32F , GL_RGBA , GL_FLOAT } }; InternalPixelRelations sizedDepthAndStencilInternalFormats[] = { { GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT , GL_UNSIGNED_SHORT } , { GL_DEPTH_COMPONENT16 , GL_DEPTH_COMPONENT , GL_UNSIGNED_INT } , { GL_DEPTH_COMPONENT24 , GL_DEPTH_COMPONENT , GL_UNSIGNED_INT } , { GL_DEPTH_COMPONENT32 , GL_DEPTH_COMPONENT , GL_UNSIGNED_INT } , { GL_DEPTH_COMPONENT32F , GL_DEPTH_COMPONENT , GL_FLOAT } // , { GL_DEPTH24_STENCIL8 , GL_DEPTH_STENCIL , GL_UNSIGNED_INT_24_8 } // , { GL_DEPTH32F_STENCIL8 , GL_DEPTH_STENCIL , GL_FLOAT_32_UNSIGNED_INT_24_8_REV } }; InternalPixelRelations compressedInternalFormats[] = { // , { GL_COMPRESSED_RED , GL_RED , GL_COMPRESSED_RED } // , { GL_COMPRESSED_RG , GL_RG , GL_COMPRESSED_RG } { GL_COMPRESSED_RGB , GL_RGB , GL_COMPRESSED_RGB } , { GL_COMPRESSED_RGBA , GL_RGBA , GL_COMPRESSED_RGBA } , { GL_COMPRESSED_SRGB , GL_RGB , GL_COMPRESSED_SRGB } , { GL_COMPRESSED_SRGB_ALPHA , GL_RGBA , GL_COMPRESSED_SRGB_ALPHA } , { GL_COMPRESSED_RED_RGTC1_EXT , GL_RED , GL_COMPRESSED_RED_RGTC1_EXT } , { GL_COMPRESSED_SIGNED_RED_RGTC1_EXT , GL_RED , GL_COMPRESSED_SIGNED_RED_RGTC1_EXT } // , { GL_COMPRESSED_RG_RGTC2 , GL_RG , GL_COMPRESSED_RG_RGTC2 } // , { GL_COMPRESSED_SIGNED_RG_RGTC2 , GL_RG , GL_COMPRESSED_SIGNED_RG_RGTC2 } // , { GL_COMPRESSED_RGBA_BPTC_UNORM , GL_RGBA , GL_COMPRESSED_RGBA_BPTC_UNORM } // , { GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM , GL_RGBA , GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM } // , { GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT , GL_RGB , GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT } // , { GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT , GL_RGB , GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT } , { GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGB , GL_COMPRESSED_RGB_S3TC_DXT1_EXT } , { GL_COMPRESSED_RGBA_S3TC_DXT1_EXT , GL_RGBA , GL_COMPRESSED_RGBA_S3TC_DXT1_EXT } , { GL_COMPRESSED_RGBA_S3TC_DXT3_EXT , GL_RGBA , GL_COMPRESSED_RGBA_S3TC_DXT3_EXT } , { GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , GL_RGBA , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT } // , { GL_COMPRESSED_SRGB_S3TC_DXT1_EXT , GL_RGB , GL_COMPRESSED_SRGB_S3TC_DXT1_EXT } // , { GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT , GL_RGBA , GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT } // , { GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT , GL_RGBA , GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT } // , { GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT , GL_RGBA , GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT } }; bool isSizedInternalFormat(GLint internalFormat) { const size_t formatsCount = sizeof(sizedInternalFormats) / sizeof(sizedInternalFormats[0]); for (size_t i=0; i < formatsCount; ++i) { if((GLenum)internalFormat == sizedInternalFormats[i].sizedInternalFormat) return true; } return false; } GLenum assumeSizedInternalFormat(GLint internalFormat, GLenum type) { const size_t formatsCount = sizeof(sizedInternalFormats) / sizeof(sizedInternalFormats[0]); for (size_t i=0; i < formatsCount; ++i) { if(internalFormat == sizedInternalFormats[i].internalFormat && type == sizedInternalFormats[i].type) return sizedInternalFormats[i].sizedInternalFormat; } return 0; } bool isCompressedInternalFormatSupportedByTexStorrage(GLint internalFormat) { const size_t formatsCount = sizeof(compressedInternalFormats) / sizeof(compressedInternalFormats[0]); for (size_t i=0; i < formatsCount; ++i) { if((GLenum)internalFormat == compressedInternalFormats[i].sizedInternalFormat) return true; } return false; } Texture::TextureObject::~TextureObject() { // OSG_NOTICE<<"Texture::TextureObject::~TextureObject() "<moveToBack(this); } void Texture::TextureObject::setAllocated(GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border) { _allocated=true; if (!match(_profile._target,numMipmapLevels,internalFormat,width,height,depth,border)) { // keep previous size unsigned int previousSize = _profile._size; _profile.set(numMipmapLevels,internalFormat,width,height,depth,border); if (_set) { _set->moveToSet(this, _set->getParent()->getTextureObjectSet(_profile)); // Update texture pool size _set->getParent()->getCurrTexturePoolSize() -= previousSize; _set->getParent()->getCurrTexturePoolSize() += _profile._size; } } } void Texture::TextureProfile::computeSize() { unsigned int numBitsPerTexel = 32; switch(_internalFormat) { case(1): numBitsPerTexel = 8; break; case(GL_ALPHA): numBitsPerTexel = 8; break; case(GL_LUMINANCE): numBitsPerTexel = 8; break; case(GL_INTENSITY): numBitsPerTexel = 8; break; case(GL_LUMINANCE_ALPHA): numBitsPerTexel = 16; break; case(2): numBitsPerTexel = 16; break; case(GL_RGB): numBitsPerTexel = 24; break; case(GL_BGR): numBitsPerTexel = 24; break; case(3): numBitsPerTexel = 24; break; case(GL_RGBA): numBitsPerTexel = 32; break; case(4): numBitsPerTexel = 32; break; case(GL_COMPRESSED_ALPHA_ARB): numBitsPerTexel = 4; break; case(GL_COMPRESSED_INTENSITY_ARB): numBitsPerTexel = 4; break; case(GL_COMPRESSED_LUMINANCE_ALPHA_ARB): numBitsPerTexel = 4; break; case(GL_COMPRESSED_RGB_S3TC_DXT1_EXT): numBitsPerTexel = 4; break; case(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT): numBitsPerTexel = 4; break; case(GL_COMPRESSED_RGB_ARB): numBitsPerTexel = 8; break; case(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT): numBitsPerTexel = 8; break; case(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT): numBitsPerTexel = 8; break; case(GL_COMPRESSED_SIGNED_RED_RGTC1_EXT): numBitsPerTexel = 4; break; case(GL_COMPRESSED_RED_RGTC1_EXT): numBitsPerTexel = 4; break; case(GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT): numBitsPerTexel = 8; break; case(GL_COMPRESSED_RED_GREEN_RGTC2_EXT): numBitsPerTexel = 8; break; case(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG): numBitsPerTexel = 2; break; case(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG): numBitsPerTexel = 2; break; case(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG): numBitsPerTexel = 4; break; case(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG): numBitsPerTexel = 4; break; case(GL_ETC1_RGB8_OES): numBitsPerTexel = 4; break; case(GL_COMPRESSED_RGB8_ETC2): numBitsPerTexel = 4; break; case(GL_COMPRESSED_SRGB8_ETC2): numBitsPerTexel = 4; break; case(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2): numBitsPerTexel = 8; break; case(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2): numBitsPerTexel = 8; break; case(GL_COMPRESSED_RGBA8_ETC2_EAC): numBitsPerTexel = 8; break; case(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC): numBitsPerTexel = 8; break; case(GL_COMPRESSED_R11_EAC): numBitsPerTexel = 4; break; case(GL_COMPRESSED_SIGNED_R11_EAC): numBitsPerTexel = 4; break; case(GL_COMPRESSED_RG11_EAC): numBitsPerTexel = 8; break; case(GL_COMPRESSED_SIGNED_RG11_EAC): numBitsPerTexel = 8; break; } _size = (unsigned int)(ceil(double(_width * _height * _depth * numBitsPerTexel)/8.0)); if (_numMipmapLevels>1) { unsigned int mipmapSize = _size / 4; for(GLint i=0; i<_numMipmapLevels && mipmapSize!=0; ++i) { _size += mipmapSize; mipmapSize /= 4; } } // OSG_NOTICE<<"TO ("<<_width<<", "<<_height<<", "<<_depth<<") size="<<_size<<" numBitsPerTexel="<getContextID()), _profile(profile), _numOfTextureObjects(0), _head(0), _tail(0) { } Texture::TextureObjectSet::~TextureObjectSet() { #if 0 OSG_NOTICE<<"TextureObjectSet::~TextureObjectSet(), _numOfTextureObjects="<<_numOfTextureObjects<_next) { if ((to->_next)->_previous != to) { OSG_NOTICE<<"Texture::TextureObjectSet::checkConsistency() : Error (to->_next)->_previous != to "<_next; } unsigned int totalNumber = numInList + _orphanedTextureObjects.size(); if (totalNumber != _numOfTextureObjects) { OSG_NOTICE<<"Error numInList + _orphanedTextureObjects.size() != _numOfTextureObjects"<get(); _orphanedTextureObjects.push_back(to); remove(to); } // update the TextureObjectManager's running total of active + orphaned TextureObjects _parent->getNumberOrphanedTextureObjects() += numOrphaned; _parent->getNumberActiveTextureObjects() -= numOrphaned; _pendingOrphanedTextureObjects.clear(); CHECK_CONSISTENCY } void Texture::TextureObjectSet::deleteAllTextureObjects() { // OSG_NOTICE<<"Texture::TextureObjectSet::deleteAllTextureObjects()"< lock(_mutex); if (!_pendingOrphanedTextureObjects.empty()) { // OSG_NOTICE<<"Texture::TextureObjectSet::flushDeletedTextureObjects(..) handling orphans"< glto = to; to = to->_next; _orphanedTextureObjects.push_back(glto.get()); remove(glto.get()); ++numOrphaned; ref_ptr original_texture = glto->getTexture(); if (original_texture.valid()) { original_texture->setTextureObject(_contextID,0); } } _parent->getNumberOrphanedTextureObjects() += numOrphaned; _parent->getNumberActiveTextureObjects() -= numOrphaned; // now do the actual delete. flushAllDeletedTextureObjects(); // OSG_NOTICE<<"done GLBufferObjectSet::deleteAllGLBufferObjects()"< glto = to; to = to->_next; ref_ptr original_texture = glto->getTexture(); if (original_texture.valid()) { original_texture->setTextureObject(_contextID,0); } } // the linked list should now be empty _head = 0; _tail = 0; _pendingOrphanedTextureObjects.clear(); _orphanedTextureObjects.clear(); unsigned int numDeleted = _numOfTextureObjects; _numOfTextureObjects = 0; // update the TextureObjectManager's running total of current pool size _parent->getCurrTexturePoolSize() -= numDeleted*_profile._size; _parent->getNumberOrphanedTextureObjects() -= numDeleted; _parent->getNumberDeleted() += numDeleted; } void Texture::TextureObjectSet::flushAllDeletedTextureObjects() { // OSG_NOTICE<<"Texture::TextureObjectSet::flushAllDeletedTextureObjects()"< lock(_mutex); if (!_pendingOrphanedTextureObjects.empty()) { // OSG_NOTICE<<"Texture::TextureObjectSet::flushDeletedTextureObjects(..) handling orphans"<id(); // OSG_NOTICE<<" Deleting textureobject ptr="<get()<<" id="<getCurrTexturePoolSize() -= numDeleted*_profile._size; _parent->getNumberOrphanedTextureObjects() -= numDeleted; _parent->getNumberDeleted() += numDeleted; _orphanedTextureObjects.clear(); } void Texture::TextureObjectSet::discardAllDeletedTextureObjects() { // OSG_NOTICE<<"Texture::TextureObjectSet::discardAllDeletedTextureObjects()"< lock(_mutex); if (!_pendingOrphanedTextureObjects.empty()) { // OSG_NOTICE<<"Texture::TextureObjectSet::flushDeletedTextureObjects(..) handling orphans"<getCurrTexturePoolSize() -= numDiscarded*_profile._size; // update the number of active and orphaned TextureObjects _parent->getNumberOrphanedTextureObjects() -= numDiscarded; _parent->getNumberDeleted() += numDiscarded; // just clear the list as there is nothing else we can do with them when discarding them _orphanedTextureObjects.clear(); } void Texture::TextureObjectSet::flushDeletedTextureObjects(double /*currentTime*/, double& availableTime) { // OSG_NOTICE<<"Texture::TextureObjectSet::flushDeletedTextureObjects(..)"< lock(_mutex); if (!_pendingOrphanedTextureObjects.empty()) { // OSG_NOTICE<<"Texture::TextureObjectSet::flushDeletedTextureObjects(..) handling orphans"<getCurrTexturePoolSize()<=_parent->getMaxTexturePoolSize()) { // OSG_NOTICE<<"Plenty of space in TextureObject pool"<getCurrTexturePoolSize() - _parent->getMaxTexturePoolSize(); unsigned int maxNumObjectsToDelete = _profile._size!=0 ? static_cast(ceil(double(sizeRequired) / double(_profile._size))): _orphanedTextureObjects.size(); OSG_INFO<<"_parent->getCurrTexturePoolSize()="<<_parent->getCurrTexturePoolSize() <<" _parent->getMaxTexturePoolSize()="<< _parent->getMaxTexturePoolSize()<id(); // OSG_NOTICE<<" Deleting textureobject ptr="<get()<<" id="<sizeAvailable) size -= sizeAvailable; else size = 0; flushAllDeletedTextureObjects(); } return size==0; } osg::ref_ptr Texture::TextureObjectSet::takeFromOrphans(Texture* texture) { // take front of orphaned list. ref_ptr to = _orphanedTextureObjects.front(); // remove from orphan list. _orphanedTextureObjects.pop_front(); // assign to new texture to->setTexture(texture); // update the number of active and orphaned TextureObjects _parent->getNumberOrphanedTextureObjects() -= 1; _parent->getNumberActiveTextureObjects() += 1; // place at back of active list addToBack(to.get()); OSG_INFO<<"Reusing orphaned TextureObject, _numOfTextureObjects="<<_numOfTextureObjects< Texture::TextureObjectSet::takeOrGenerate(Texture* texture) { // see if we can recyle TextureObject from the orphan list { OpenThreads::ScopedLock lock(_mutex); if (!_pendingOrphanedTextureObjects.empty()) { handlePendingOrphandedTextureObjects(); return takeFromOrphans(texture); } } if (!_orphanedTextureObjects.empty()) { return takeFromOrphans(texture); } unsigned int minFrameNumber = _parent->getFrameNumber(); // see if we can reuse TextureObject by taking the least recently used active TextureObject if ((_parent->getMaxTexturePoolSize()!=0) && (!_parent->hasSpace(_profile._size)) && (_numOfTextureObjects>1) && (_head != 0) && (_head->_frameLastUsed lock(_mutex); ref_ptr to = _head; ref_ptr original_texture = to->getTexture(); if (original_texture.valid()) { original_texture->setTextureObject(_contextID,0); OSG_INFO<<"TextureObjectSet="<setTexture(texture); return to; } // // no TextureObjects available to recycle so have to create one from scratch // GLuint id; glGenTextures( 1L, &id ); osg::ref_ptr to = new Texture::TextureObject(const_cast(texture),id,_profile); to->_set = this; ++_numOfTextureObjects; // update the current texture pool size _parent->getCurrTexturePoolSize() += _profile._size; _parent->getNumberActiveTextureObjects() += 1; addToBack(to.get()); OSG_INFO<<"Created new " << this << " TextureObject, _numOfTextureObjects "<<_numOfTextureObjects<_previous = "<_previous<_next = "<_next<_frameLastUsed = _parent->getFrameNumber(); // nothing to do if we are already tail if (to==_tail) return; // if no tail exists then assign 'to' as tail and head if (_tail==0) { OSG_NOTICE<<"Error ***************** Should not get here !!!!!!!!!"<_next==0) { OSG_NOTICE<<"Error ***************** Should not get here either !!!!!!!!!"<_previous) { (to->_previous)->_next = to->_next; } else { // 'to' is the head, so moving it to the back will mean we need a new head if (to->_next) { _head = to->_next; } } (to->_next)->_previous = to->_previous; _tail->_next = to; to->_previous = _tail; to->_next = 0; _tail = to; #if 0 OSG_NOTICE<<" m2B after _head = "<<_head<_previous = "<_previous<_next = "<_next<_previous = "<_previous<_next = "<_next<_previous !=0 || to->_next !=0) { moveToBack(to); } else { to->_frameLastUsed = _parent->getFrameNumber(); if (_tail) _tail->_next = to; to->_previous = _tail; if (!_head) _head = to; _tail = to; } #if 0 OSG_NOTICE<<" a2B after _head = "<<_head<_previous = "<_previous<_next = "<_next< lock(_mutex); // disconnect from original texture to->setTexture(0); // add orphan 'to' to the pending list of orphans, these will then be // handled in the handlePendingOrphandedTextureObjects() where the TO's // will be removed from the active list, and then placed in the orhpanTextureObject // list. This double buffered approach to handling orphaned TO's is used // to avoid having to mutex the process of appling active TO's. _pendingOrphanedTextureObjects.push_back(to); #if 0 OSG_NOTICE<<"TextureObjectSet::orphan("<getFrameNumber(); else ++_frameNumber; ++_numFrames; } void Texture::TextureObjectManager::reportStats(std::ostream& out) { double numFrames(_numFrames==0 ? 1.0 : _numFrames); out<<"TextureObjectMananger::reportStats()"<second.get(); numObjectsInLists += os->computeNumTextureObjectsInList(); numActive += os->getNumOfTextureObjects(); numOrphans += os->getNumOrphans(); numPendingOrphans += os->getNumPendingOrphans(); currentSize += os->getProfile()._size * (os->computeNumTextureObjectsInList()+os->getNumOrphans()); out<<" size="<getProfile()._size <<", os->computeNumTextureObjectsInList()"<computeNumTextureObjectsInList() <<", os->getNumOfTextureObjects()"<getNumOfTextureObjects() <<", os->getNumOrphans()"<getNumOrphans() <<", os->getNumPendingOrphans()"<getNumPendingOrphans() <& Texture::getTextureObjectManager(unsigned int contextID) { typedef osg::buffered_object< ref_ptr > TextureObjectManagerBuffer; static TextureObjectManagerBuffer s_TextureObjectManager; if (!s_TextureObjectManager[contextID]) s_TextureObjectManager[contextID] = new Texture::TextureObjectManager(contextID); return s_TextureObjectManager[contextID]; } osg::ref_ptr Texture::generateTextureObject(const Texture* texture, unsigned int contextID, GLenum target) { return getTextureObjectManager(contextID)->generateTextureObject(texture, target); } osg::ref_ptr Texture::generateTextureObject(const Texture* texture, unsigned int contextID, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border) { return getTextureObjectManager(contextID)->generateTextureObject(texture,target,numMipmapLevels,internalFormat,width,height,depth,border); } void Texture::deleteAllTextureObjects(unsigned int contextID) { getTextureObjectManager(contextID)->deleteAllTextureObjects(); } void Texture::discardAllTextureObjects(unsigned int contextID) { getTextureObjectManager(contextID)->discardAllTextureObjects(); } void Texture::flushAllDeletedTextureObjects(unsigned int contextID) { getTextureObjectManager(contextID)->flushAllDeletedTextureObjects(); } void Texture::discardAllDeletedTextureObjects(unsigned int contextID) { getTextureObjectManager(contextID)->discardAllDeletedTextureObjects(); } void Texture::flushDeletedTextureObjects(unsigned int contextID,double currentTime, double& availbleTime) { getTextureObjectManager(contextID)->flushDeletedTextureObjects(currentTime, availbleTime); } void Texture::releaseTextureObject(unsigned int contextID, Texture::TextureObject* to) { getTextureObjectManager(contextID)->releaseTextureObject(to); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Texture class implementation // Texture::Texture(): _wrap_s(CLAMP), _wrap_t(CLAMP), _wrap_r(CLAMP), _min_filter(LINEAR_MIPMAP_LINEAR), // trilinear _mag_filter(LINEAR), _maxAnisotropy(1.0f), _swizzle(GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA), _useHardwareMipMapGeneration(true), _unrefImageDataAfterApply(false), _clientStorageHint(false), _resizeNonPowerOfTwoHint(true), _borderColor(0.0, 0.0, 0.0, 0.0), _borderWidth(0), _internalFormatMode(USE_IMAGE_DATA_FORMAT), _internalFormatType(NORMALIZED), _internalFormat(0), _sourceFormat(0), _sourceType(0), _use_shadow_comparison(false), _shadow_compare_func(LEQUAL), _shadow_texture_mode(LUMINANCE), _shadow_ambient(0) { } Texture::Texture(const Texture& text,const CopyOp& copyop): StateAttribute(text,copyop), _wrap_s(text._wrap_s), _wrap_t(text._wrap_t), _wrap_r(text._wrap_r), _min_filter(text._min_filter), _mag_filter(text._mag_filter), _maxAnisotropy(text._maxAnisotropy), _swizzle(text._swizzle), _useHardwareMipMapGeneration(text._useHardwareMipMapGeneration), _unrefImageDataAfterApply(text._unrefImageDataAfterApply), _clientStorageHint(text._clientStorageHint), _resizeNonPowerOfTwoHint(text._resizeNonPowerOfTwoHint), _borderColor(text._borderColor), _borderWidth(text._borderWidth), _internalFormatMode(text._internalFormatMode), _internalFormatType(text._internalFormatType), _internalFormat(text._internalFormat), _sourceFormat(text._sourceFormat), _sourceType(text._sourceType), _use_shadow_comparison(text._use_shadow_comparison), _shadow_compare_func(text._shadow_compare_func), _shadow_texture_mode(text._shadow_texture_mode), _shadow_ambient(text._shadow_ambient) { } Texture::~Texture() { // delete old texture objects. dirtyTextureObject(); } int Texture::compareTexture(const Texture& rhs) const { COMPARE_StateAttribute_Parameter(_wrap_s) COMPARE_StateAttribute_Parameter(_wrap_t) COMPARE_StateAttribute_Parameter(_wrap_r) COMPARE_StateAttribute_Parameter(_min_filter) COMPARE_StateAttribute_Parameter(_mag_filter) COMPARE_StateAttribute_Parameter(_maxAnisotropy) COMPARE_StateAttribute_Parameter(_swizzle) COMPARE_StateAttribute_Parameter(_useHardwareMipMapGeneration) COMPARE_StateAttribute_Parameter(_internalFormatMode) // only compare _internalFomat is it has alrady been set in both lhs, and rhs if (_internalFormat!=0 && rhs._internalFormat!=0) { COMPARE_StateAttribute_Parameter(_internalFormat) } COMPARE_StateAttribute_Parameter(_sourceFormat) COMPARE_StateAttribute_Parameter(_sourceType) COMPARE_StateAttribute_Parameter(_use_shadow_comparison) COMPARE_StateAttribute_Parameter(_shadow_compare_func) COMPARE_StateAttribute_Parameter(_shadow_texture_mode) COMPARE_StateAttribute_Parameter(_shadow_ambient) COMPARE_StateAttribute_Parameter(_unrefImageDataAfterApply) COMPARE_StateAttribute_Parameter(_clientStorageHint) COMPARE_StateAttribute_Parameter(_resizeNonPowerOfTwoHint) COMPARE_StateAttribute_Parameter(_internalFormatType); return 0; } int Texture::compareTextureObjects(const Texture& rhs) const { if (_textureObjectBuffer.size()isTextureCompressionARBSupported) { switch(image.getPixelFormat()) { case(1): internalFormat = GL_COMPRESSED_ALPHA_ARB; break; case(2): internalFormat = GL_COMPRESSED_LUMINANCE_ALPHA_ARB; break; case(3): internalFormat = GL_COMPRESSED_RGB_ARB; break; case(4): internalFormat = GL_COMPRESSED_RGBA_ARB; break; case(GL_RGB): internalFormat = GL_COMPRESSED_RGB_ARB; break; case(GL_RGBA): internalFormat = GL_COMPRESSED_RGBA_ARB; break; case(GL_ALPHA): internalFormat = GL_COMPRESSED_ALPHA_ARB; break; case(GL_LUMINANCE): internalFormat = GL_COMPRESSED_LUMINANCE_ARB; break; case(GL_LUMINANCE_ALPHA): internalFormat = GL_COMPRESSED_LUMINANCE_ALPHA_ARB; break; case(GL_INTENSITY): internalFormat = GL_COMPRESSED_INTENSITY_ARB; break; } } break; case(USE_S3TC_DXT1_COMPRESSION): if (extensions->isTextureCompressionS3TCSupported) { switch(image.getPixelFormat()) { case(3): internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; case(4): internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; case(GL_RGB): internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; case(GL_RGBA): internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_S3TC_DXT1c_COMPRESSION): if (extensions->isTextureCompressionS3TCSupported) { switch(image.getPixelFormat()) { case(3): case(4): case(GL_RGB): case(GL_RGBA): internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_S3TC_DXT1a_COMPRESSION): if (extensions->isTextureCompressionS3TCSupported) { switch(image.getPixelFormat()) { case(3): case(4): case(GL_RGB): case(GL_RGBA): internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_S3TC_DXT3_COMPRESSION): if (extensions->isTextureCompressionS3TCSupported) { switch(image.getPixelFormat()) { case(3): case(GL_RGB): internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; case(4): case(GL_RGBA): internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_S3TC_DXT5_COMPRESSION): if (extensions->isTextureCompressionS3TCSupported) { switch(image.getPixelFormat()) { case(3): case(GL_RGB): internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; case(4): case(GL_RGBA): internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_PVRTC_2BPP_COMPRESSION): if (extensions->isTextureCompressionPVRTC2BPPSupported) { switch(image.getPixelFormat()) { case(3): case(GL_RGB): internalFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; break; case(4): case(GL_RGBA): internalFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_PVRTC_4BPP_COMPRESSION): if (extensions->isTextureCompressionPVRTC4BPPSupported) { switch(image.getPixelFormat()) { case(3): case(GL_RGB): internalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; case(4): case(GL_RGBA): internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_ETC_COMPRESSION): if (extensions->isTextureCompressionETCSupported) { switch(image.getPixelFormat()) { case(3): case(GL_RGB): internalFormat = GL_ETC1_RGB8_OES; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_ETC2_COMPRESSION): if (extensions->isTextureCompressionETC2Supported) { switch(image.getPixelFormat()) { case(1): case(GL_RED): internalFormat = GL_COMPRESSED_R11_EAC; break; case(2): case(GL_RG): internalFormat = GL_COMPRESSED_RG11_EAC; break; case(3): case(GL_RGB): internalFormat = GL_COMPRESSED_RGB8_ETC2; break; case(4): case(GL_RGBA): internalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_RGTC1_COMPRESSION): if (extensions->isTextureCompressionRGTCSupported) { switch(image.getPixelFormat()) { case(3): case(GL_RGB): internalFormat = GL_COMPRESSED_RED_RGTC1_EXT; break; case(4): case(GL_RGBA): internalFormat = GL_COMPRESSED_RED_RGTC1_EXT; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; case(USE_RGTC2_COMPRESSION): if (extensions->isTextureCompressionRGTCSupported) { switch(image.getPixelFormat()) { case(3): case(GL_RGB): internalFormat = GL_COMPRESSED_RED_GREEN_RGTC2_EXT; break; case(4): case(GL_RGBA): internalFormat = GL_COMPRESSED_RED_GREEN_RGTC2_EXT; break; default: internalFormat = image.getInternalTextureFormat(); break; } } break; default: break; } } #if defined (OSG_GLES1_AVAILABLE) || defined (OSG_GLES2_AVAILABLE) // GLES doesn't cope with internal formats of 1,2,3 and 4 and glTexImage doesn't // handle the _OES pixel formats so map them to the appropriate equivilants. switch(internalFormat) { case(1) : internalFormat = GL_LUMINANCE; break; case(2) : internalFormat = GL_LUMINANCE_ALPHA; break; case(3) : internalFormat = GL_RGB; break; case(4) : internalFormat = GL_RGBA; break; case(GL_RGB8_OES) : internalFormat = GL_RGB; break; case(GL_RGBA8_OES) : internalFormat = GL_RGBA; break; default: break; } #elif defined(OSG_GL3_AVAILABLE) switch(internalFormat) { case(GL_INTENSITY) : internalFormat = GL_RED; break; // should it be swizzled to match RGBA(INTENSITY, INTENSITY, INTENSITY, INTENSITY)? case(GL_LUMINANCE) : internalFormat = GL_RED; break; // should it be swizzled to match RGBA(LUMINANCE, LUMINANCE, LUMINANCE, 1.0)? case(1) : internalFormat = GL_RED; break; // or sould this be GL_ALPHA? case(2) : internalFormat = GL_RG; break; // should we assume GL_LUMINANCE_ALPHA? case(GL_LUMINANCE_ALPHA) : internalFormat = GL_RG; break; // should it be swizlled to match RGAB(LUMUNIANCE, LUMINANCE, LUMINANCE, ALPHA)? case(3) : internalFormat = GL_RGB; break; case(4) : internalFormat = GL_RGBA; break; default: break; } #endif _internalFormat = internalFormat; computeInternalFormatType(); //OSG_NOTICE<<"Internal format="< _sourceFormat = GL_RGBA_INTEGER_EXT) // Should we do this? ( Art, 09. Sept. 2007) // compute internal format type based on the internal format switch(_internalFormat) { case GL_RGBA32UI_EXT: case GL_RGBA16UI_EXT: case GL_RGBA8UI_EXT: case GL_RGB32UI_EXT: case GL_RGB16UI_EXT: case GL_RGB8UI_EXT: case GL_LUMINANCE32UI_EXT: case GL_LUMINANCE16UI_EXT: case GL_LUMINANCE8UI_EXT: case GL_INTENSITY32UI_EXT: case GL_INTENSITY16UI_EXT: case GL_INTENSITY8UI_EXT: case GL_LUMINANCE_ALPHA32UI_EXT: case GL_LUMINANCE_ALPHA16UI_EXT: case GL_LUMINANCE_ALPHA8UI_EXT : _internalFormatType = UNSIGNED_INTEGER; break; case GL_RGBA32I_EXT: case GL_RGBA16I_EXT: case GL_RGBA8I_EXT: case GL_RGB32I_EXT: case GL_RGB16I_EXT: case GL_RGB8I_EXT: case GL_LUMINANCE32I_EXT: case GL_LUMINANCE16I_EXT: case GL_LUMINANCE8I_EXT: case GL_INTENSITY32I_EXT: case GL_INTENSITY16I_EXT: case GL_INTENSITY8I_EXT: case GL_LUMINANCE_ALPHA32I_EXT: case GL_LUMINANCE_ALPHA16I_EXT: case GL_LUMINANCE_ALPHA8I_EXT: _internalFormatType = SIGNED_INTEGER; break; case GL_RGBA32F_ARB: case GL_RGBA16F_ARB: case GL_RGB32F_ARB: case GL_RGB16F_ARB: case GL_LUMINANCE32F_ARB: case GL_LUMINANCE16F_ARB: case GL_INTENSITY32F_ARB: case GL_INTENSITY16F_ARB: case GL_LUMINANCE_ALPHA32F_ARB: case GL_LUMINANCE_ALPHA16F_ARB: _internalFormatType = FLOAT; break; default: _internalFormatType = NORMALIZED; break; }; } bool Texture::isCompressedInternalFormat() const { return isCompressedInternalFormat(getInternalFormat()); } bool Texture::isCompressedInternalFormat(GLint internalFormat) { switch(internalFormat) { case(GL_COMPRESSED_ALPHA_ARB): case(GL_COMPRESSED_INTENSITY_ARB): case(GL_COMPRESSED_LUMINANCE_ALPHA_ARB): case(GL_COMPRESSED_LUMINANCE_ARB): case(GL_COMPRESSED_RGBA_ARB): case(GL_COMPRESSED_RGB_ARB): case(GL_COMPRESSED_RGB_S3TC_DXT1_EXT): case(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT): case(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT): case(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT): case(GL_COMPRESSED_SIGNED_RED_RGTC1_EXT): case(GL_COMPRESSED_RED_RGTC1_EXT): case(GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT): case(GL_COMPRESSED_RED_GREEN_RGTC2_EXT): case(GL_ETC1_RGB8_OES): case(GL_COMPRESSED_RGB8_ETC2): case(GL_COMPRESSED_SRGB8_ETC2): case(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2): case(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2): case(GL_COMPRESSED_RGBA8_ETC2_EAC): case(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC): case(GL_COMPRESSED_R11_EAC): case(GL_COMPRESSED_SIGNED_R11_EAC): case(GL_COMPRESSED_RG11_EAC): case(GL_COMPRESSED_SIGNED_RG11_EAC): case(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG): case(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG): case(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG): case(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG): return true; default: return false; } } void Texture::getCompressedSize(GLenum internalFormat, GLint width, GLint height, GLint depth, GLint& blockSize, GLint& size) { if (internalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT || internalFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) blockSize = 8; else if (internalFormat == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT || internalFormat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) blockSize = 16; else if (internalFormat == GL_ETC1_RGB8_OES) blockSize = 8; else if (internalFormat == GL_COMPRESSED_RGB8_ETC2 || internalFormat == GL_COMPRESSED_SRGB8_ETC2) blockSize = 8; else if (internalFormat == GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 || internalFormat == GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) blockSize = 8; else if (internalFormat == GL_COMPRESSED_RGBA8_ETC2_EAC || internalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) blockSize = 16; else if (internalFormat == GL_COMPRESSED_R11_EAC || internalFormat == GL_COMPRESSED_SIGNED_R11_EAC) blockSize = 8; else if (internalFormat == GL_COMPRESSED_RG11_EAC || internalFormat == GL_COMPRESSED_SIGNED_RG11_EAC) blockSize = 16; else if (internalFormat == GL_COMPRESSED_RED_RGTC1_EXT || internalFormat == GL_COMPRESSED_SIGNED_RED_RGTC1_EXT) blockSize = 8; else if (internalFormat == GL_COMPRESSED_RED_GREEN_RGTC2_EXT || internalFormat == GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT) blockSize = 16; else if (internalFormat == GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG || internalFormat == GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG) { blockSize = 8 * 4; // Pixel by pixel block size for 2bpp GLint widthBlocks = width / 8; GLint heightBlocks = height / 4; GLint bpp = 2; // Clamp to minimum number of blocks if(widthBlocks < 2) widthBlocks = 2; if(heightBlocks < 2) heightBlocks = 2; size = widthBlocks * heightBlocks * ((blockSize * bpp) / 8); return; } else if (internalFormat == GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG || internalFormat == GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG) { blockSize = 4 * 4; // Pixel by pixel block size for 4bpp GLint widthBlocks = width / 4; GLint heightBlocks = height / 4; GLint bpp = 4; // Clamp to minimum number of blocks if(widthBlocks < 2) widthBlocks = 2; if(heightBlocks < 2) heightBlocks = 2; size = widthBlocks * heightBlocks * ((blockSize * bpp) / 8); return; } else { OSG_WARN<<"Texture::getCompressedSize(...) : cannot compute correct size of compressed format ("<(); WrapMode ws = _wrap_s, wt = _wrap_t, wr = _wrap_r; // GL_IBM_texture_mirrored_repeat, fall-back REPEAT if (!extensions->isTextureMirroredRepeatSupported) { if (ws == MIRROR) ws = REPEAT; if (wt == MIRROR) wt = REPEAT; if (wr == MIRROR) wr = REPEAT; } // GL_EXT_texture_edge_clamp, fall-back CLAMP if (!extensions->isTextureEdgeClampSupported) { if (ws == CLAMP_TO_EDGE) ws = CLAMP; if (wt == CLAMP_TO_EDGE) wt = CLAMP; if (wr == CLAMP_TO_EDGE) wr = CLAMP; } if(!extensions->isTextureBorderClampSupported) { if(ws == CLAMP_TO_BORDER) ws = CLAMP; if(wt == CLAMP_TO_BORDER) wt = CLAMP; if(wr == CLAMP_TO_BORDER) wr = CLAMP; } #if defined(OSG_GLES1_AVAILABLE) || defined(OSG_GLES2_AVAILABLE) || defined(OSG_GL3_AVAILABLE) if (ws == CLAMP) ws = CLAMP_TO_EDGE; if (wt == CLAMP) wt = CLAMP_TO_EDGE; if (wr == CLAMP) wr = CLAMP_TO_EDGE; #endif const Image * image = getImage(0); if( image && image->isMipmap() && extensions->isTextureMaxLevelSupported && int( image->getNumMipmapLevels() ) < Image::computeNumberOfMipmapLevels( image->s(), image->t(), image->r() ) ) glTexParameteri( target, GL_TEXTURE_MAX_LEVEL, image->getNumMipmapLevels() - 1 ); glTexParameteri( target, GL_TEXTURE_WRAP_S, ws ); if (target!=GL_TEXTURE_1D) glTexParameteri( target, GL_TEXTURE_WRAP_T, wt ); if (target==GL_TEXTURE_3D) glTexParameteri( target, GL_TEXTURE_WRAP_R, wr ); glTexParameteri( target, GL_TEXTURE_MIN_FILTER, _min_filter); glTexParameteri( target, GL_TEXTURE_MAG_FILTER, _mag_filter); // Art: I think anisotropic filtering is not supported by the integer textures if (extensions->isTextureFilterAnisotropicSupported && _internalFormatType != SIGNED_INTEGER && _internalFormatType != UNSIGNED_INTEGER) { // note, GL_TEXTURE_MAX_ANISOTROPY_EXT will either be defined // by gl.h (or via glext.h) or by include/osg/Texture. glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, _maxAnisotropy); } if (extensions->isTextureSwizzleSupported) { // note, GL_TEXTURE_SWIZZLE_RGBA will either be defined // by gl.h (or via glext.h) or by include/osg/Texture. glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, _swizzle.ptr()); } if (extensions->isTextureBorderClampSupported) { #ifndef GL_TEXTURE_BORDER_COLOR #define GL_TEXTURE_BORDER_COLOR 0x1004 #endif if (_internalFormatType == SIGNED_INTEGER) { GLint color[4] = {(GLint)_borderColor.r(), (GLint)_borderColor.g(), (GLint)_borderColor.b(), (GLint)_borderColor.a()}; extensions->glTexParameterIiv(target, GL_TEXTURE_BORDER_COLOR, color); }else if (_internalFormatType == UNSIGNED_INTEGER) { GLuint color[4] = {(GLuint)_borderColor.r(), (GLuint)_borderColor.g(), (GLuint)_borderColor.b(), (GLuint)_borderColor.a()}; extensions->glTexParameterIuiv(target, GL_TEXTURE_BORDER_COLOR, color); }else{ GLfloat color[4] = {(GLfloat)_borderColor.r(), (GLfloat)_borderColor.g(), (GLfloat)_borderColor.b(), (GLfloat)_borderColor.a()}; glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color); } } // integer textures are not supported by the shadow // GL_TEXTURE_1D_ARRAY_EXT could be included in the check below but its not yet implemented in OSG if (extensions->isShadowSupported && (target == GL_TEXTURE_2D || target == GL_TEXTURE_1D || target == GL_TEXTURE_RECTANGLE || target == GL_TEXTURE_CUBE_MAP || target == GL_TEXTURE_2D_ARRAY_EXT ) && _internalFormatType != SIGNED_INTEGER && _internalFormatType != UNSIGNED_INTEGER) { if (_use_shadow_comparison) { glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC_ARB, _shadow_compare_func); #if defined(OSG_GL1_AVAILABLE) || defined(OSG_GL2_AVAILABLE) glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, _shadow_texture_mode); #endif // if ambient value is 0 - it is default behaviour of GL_ARB_shadow // no need for GL_ARB_shadow_ambient in this case if (extensions->isShadowAmbientSupported && _shadow_ambient > 0) { glTexParameterf(target, TEXTURE_COMPARE_FAIL_VALUE_ARB, _shadow_ambient); } } else { glTexParameteri(target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); } } // Apply image load/store attributes if (extensions->isBindImageTextureSupported() && _imageAttachment.access!=0) { TextureObject* tobj = getTextureObject(contextID); if (tobj) { extensions->glBindImageTexture( _imageAttachment.unit, tobj->id(), _imageAttachment.level, _imageAttachment.layered, _imageAttachment.layer, _imageAttachment.access, _imageAttachment.format!=0 ? _imageAttachment.format : _internalFormat); } } getTextureParameterDirty(state.getContextID()) = false; } void Texture::computeRequiredTextureDimensions(State& state, const osg::Image& image,GLsizei& inwidth, GLsizei& inheight,GLsizei& numMipmapLevels) const { const GLExtensions* extensions = state.get(); int width,height; if( !_resizeNonPowerOfTwoHint && extensions->isNonPowerOfTwoTextureSupported(_min_filter) ) { width = image.s(); height = image.t(); } else { width = Image::computeNearestPowerOfTwo(image.s()-2*_borderWidth)+2*_borderWidth; height = Image::computeNearestPowerOfTwo(image.t()-2*_borderWidth)+2*_borderWidth; } // cap the size to what the graphics hardware can handle. if (width>extensions->maxTextureSize) width = extensions->maxTextureSize; if (height>extensions->maxTextureSize) height = extensions->maxTextureSize; inwidth = width; inheight = height; if( _min_filter == LINEAR || _min_filter == NEAREST) { numMipmapLevels = 1; } else if( image.isMipmap() ) { numMipmapLevels = image.getNumMipmapLevels(); } else { numMipmapLevels = 1; for(int s=1; sisNonPowerOfTwoTextureSupported(_min_filter)="<isNonPowerOfTwoTextureSupported(_min_filter) <getMaxNumberOfGraphicsContexts();++i) { if (_textureObjectBuffer[i]==0) return false; } return true; } void Texture::applyTexImage2D_load(State& state, GLenum target, const Image* image, GLsizei inwidth, GLsizei inheight,GLsizei numMipmapLevels) const { // if we don't have a valid image we can't create a texture! if (!image || !image->data()) return; #ifdef DO_TIMING osg::Timer_t start_tick = osg::Timer::instance()->tick(); OSG_NOTICE<<"glTexImage2D pixelFormat = "<getPixelFormat()<(); // select the internalFormat required for the texture. bool compressed_image = isCompressedInternalFormat((GLenum)image->getPixelFormat()); // If the texture's internal format is a compressed type, then the // user is requesting that the graphics card compress the image if it's // not already compressed. However, if the image is not a multiple of // four in each dimension the subsequent calls to glTexSubImage* will // fail. Revert to uncompressed format in this case. if (isCompressedInternalFormat(_internalFormat) && (((inwidth >> 2) << 2) != inwidth || ((inheight >> 2) << 2) != inheight)) { OSG_NOTICE<<"Received a request to compress an image, but image size is not a multiple of four ("<getPacking()); unsigned int rowLength = image->getRowLength(); bool useClientStorage = extensions->isClientStorageSupported && getClientStorageHint(); if (useClientStorage) { glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE,GL_TRUE); #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE) glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_PRIORITY,0.0f); #endif #ifdef GL_TEXTURE_STORAGE_HINT_APPLE glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_STORAGE_HINT_APPLE , GL_STORAGE_CACHED_APPLE); #endif } unsigned char* dataPtr = (unsigned char*)image->data(); // OSG_NOTICE<<"inwidth="<getFileName()"<getFileName()<s() || inheight!=image->t(); if (needImageRescale) { // resize the image to power of two. if (image->isMipmap()) { OSG_WARN<<"Warning:: Mipmapped osg::Image not a power of two, cannot apply to texture."<getPixelFormat(),image->getDataType(),image->getPacking())*inheight; dataPtr = new unsigned char [newTotalSize]; if (!dataPtr) { OSG_WARN<<"Warning:: Not enough memory to resize image, cannot apply to texture."<getFileName().empty()) { OSG_NOTICE << "Scaling image '"<getFileName()<<"' from ("<s()<<","<t()<<") to ("<s()<<","<t()<<") to ("<getPacking(); psm.pack_row_length = image->getRowLength(); psm.unpack_alignment = image->getPacking(); // rescale the image to the correct size. gluScaleImage(&psm, image->getPixelFormat(), image->s(),image->t(),image->getDataType(),image->data(), inwidth,inheight,image->getDataType(), dataPtr); rowLength = 0; } bool mipmappingRequired = _min_filter != LINEAR && _min_filter != NEAREST; bool useHardwareMipMapGeneration = mipmappingRequired && (!image->isMipmap() && isHardwareMipmapGenerationEnabled(state)); bool useGluBuildMipMaps = mipmappingRequired && (!useHardwareMipMapGeneration && !image->isMipmap()); GLBufferObject* pbo = image->getOrCreateGLBufferObject(state.getContextID()); if (pbo && !needImageRescale && !useGluBuildMipMaps) { state.bindPixelBufferObject(pbo); dataPtr = reinterpret_cast(pbo->getOffset(image->getBufferIndex())); rowLength = 0; #ifdef DO_TIMING OSG_NOTICE<<"after PBO "<delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<getPixelFormat(), (GLenum)image->getDataType(), dataPtr); } else if (extensions->isCompressedTexImage2DSupported()) { numMipmapLevels = 1; GLint blockSize, size; getCompressedSize(_internalFormat, inwidth, inheight, 1, blockSize,size); extensions->glCompressedTexImage2D(target, 0, _internalFormat, inwidth, inheight,0, size, dataPtr); } mipmapAfterTexImage(state, mipmapResult); } else { // we require mip mapping. if(image->isMipmap()) { // image is mip mapped so we take the mip map levels from the image. numMipmapLevels = image->getNumMipmapLevels(); int width = inwidth; int height = inheight; bool useTexStorrage = extensions->isTextureStorageEnabled; GLenum sizedInternalFormat = 0; if(useTexStorrage) { if(extensions->isTexStorage2DSupported() && _borderWidth == 0) { //calculate sized internal format if(!compressed_image) { if(isSizedInternalFormat(_internalFormat)) { sizedInternalFormat = _internalFormat; } else { sizedInternalFormat = assumeSizedInternalFormat((GLenum)image->getInternalTextureFormat(), (GLenum)image->getDataType()); } } else { if(isCompressedInternalFormatSupportedByTexStorrage(_internalFormat)) { sizedInternalFormat = _internalFormat; } } } useTexStorrage &= sizedInternalFormat != 0; // Need to ensure 2x2 and 1x1 mipmaps are not used for // compressed textures stored in texture storage. // Assumes block size is 4 and all mipmap levels present! if ( useTexStorage && compressed_image && numMipmapLevels > 2 ) numMipmapLevels -= 2; } if(useTexStorrage) { //state.checkGLErrors("before extensions->glTexStorage2D("); extensions->glTexStorage2D(target, numMipmapLevels, sizedInternalFormat, width, height); //state.checkGLErrors("after extensions->glTexStorage2D("); if( !compressed_image ) { for( GLsizei k = 0 ; k < numMipmapLevels && (width || height) ;k++) { if (width == 0) width = 1; if (height == 0) height = 1; glTexSubImage2D( target, k, 0, 0, width, height, (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), dataPtr + image->getMipmapOffset(k)); width >>= 1; height >>= 1; } } else if (extensions->isCompressedTexImage2DSupported()) { GLint blockSize,size; for( GLsizei k = 0 ; k < numMipmapLevels && (width || height) ;k++) { if (width == 0) width = 1; if (height == 0) height = 1; getCompressedSize(image->getInternalTextureFormat(), width, height, 1, blockSize,size); //state.checkGLErrors("before extensions->glCompressedTexSubImage2D("); extensions->glCompressedTexSubImage2D(target, k, 0, 0, width, height, (GLenum)image->getPixelFormat(), size, dataPtr + image->getMipmapOffset(k)); //state.checkGLErrors("after extensions->glCompressedTexSubImage2D("); width >>= 1; height >>= 1; } } } else { if( !compressed_image ) { for( GLsizei k = 0 ; k < numMipmapLevels && (width || height) ;k++) { if (width == 0) width = 1; if (height == 0) height = 1; glTexImage2D( target, k, _internalFormat, width, height, _borderWidth, (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), dataPtr + image->getMipmapOffset(k)); width >>= 1; height >>= 1; } } else if (extensions->isCompressedTexImage2DSupported()) { GLint blockSize, size; for( GLsizei k = 0 ; k < numMipmapLevels && (width || height) ;k++) { if (width == 0) width = 1; if (height == 0) height = 1; getCompressedSize(_internalFormat, width, height, 1, blockSize,size); extensions->glCompressedTexImage2D(target, k, _internalFormat, width, height, _borderWidth, size, dataPtr + image->getMipmapOffset(k)); width >>= 1; height >>= 1; } } } } else { if ( !compressed_image) { numMipmapLevels = 0; gluBuild2DMipmaps( target, _internalFormat, inwidth,inheight, (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), dataPtr); int width = image->s(); int height = image->t(); for( numMipmapLevels = 0 ; (width || height) ; ++numMipmapLevels) { width >>= 1; height >>= 1; } } else { OSG_WARN<<"Warning:: Compressed image cannot be mip mapped"<getBufferObject(); if (bo->getCopyDataAndReleaseGLBufferObject()) { pbo->setBufferDataHasBeenRead(image); if (pbo->hasAllBufferDataBeenRead()) { //OSG_NOTICE<<"Release PBO"<releaseGLObjects(&state); } } } #ifdef DO_TIMING static double s_total_time = 0.0; double delta_time = osg::Timer::instance()->delta_m(start_tick,osg::Timer::instance()->tick()); s_total_time += delta_time; OSG_NOTICE<<"glTexImage2D "<data()) return; // image size has changed so we have to re-load the image from scratch. if (image->s()!=inwidth || image->t()!=inheight || image->getInternalTextureFormat()!=inInternalFormat ) { applyTexImage2D_load(state, target, image, inwidth, inheight,numMipmapLevels); return; } // else image size the same as when loaded so we can go ahead and subload // If the texture's internal format is a compressed type, then the // user is requesting that the graphics card compress the image if it's // not already compressed. However, if the image is not a multiple of // four in each dimension the subsequent calls to glTexSubImage* will // fail. Revert to uncompressed format in this case. if (isCompressedInternalFormat(_internalFormat) && (((inwidth >> 2) << 2) != inwidth || ((inheight >> 2) << 2) != inheight)) { applyTexImage2D_load(state, target, image, inwidth, inheight, numMipmapLevels); return; } #ifdef DO_TIMING osg::Timer_t start_tick = osg::Timer::instance()->tick(); OSG_NOTICE<<"glTexSubImage2D pixelFormat = "<getPixelFormat()<(); // select the internalFormat required for the texture. bool compressed_image = isCompressedInternalFormat((GLenum)image->getPixelFormat()); glPixelStorei(GL_UNPACK_ALIGNMENT,image->getPacking()); unsigned int rowLength = image->getRowLength(); unsigned char* dataPtr = (unsigned char*)image->data(); bool needImageRescale = inwidth!=image->s() || inheight!=image->t(); if (needImageRescale) { // resize the image to power of two. if (image->isMipmap()) { OSG_WARN<<"Warning:: Mipmapped osg::Image not a power of two, cannot apply to texture."<getPixelFormat(),image->getDataType(),image->getPacking())*inheight; dataPtr = new unsigned char [newTotalSize]; if (!dataPtr) { OSG_WARN<<"Warning:: Not enough memory to resize image, cannot apply to texture."<getFileName().empty()) { OSG_NOTICE << "Scaling image '"<getFileName()<<"' from ("<s()<<","<t()<<") to ("<s()<<","<t()<<") to ("<getPacking(); psm.unpack_alignment = image->getPacking(); gluScaleImage(&psm, image->getPixelFormat(), image->s(),image->t(),image->getDataType(),image->data(), inwidth,inheight,image->getDataType(), dataPtr); rowLength = 0; } bool mipmappingRequired = _min_filter != LINEAR && _min_filter != NEAREST; bool useHardwareMipMapGeneration = mipmappingRequired && (!image->isMipmap() && isHardwareMipmapGenerationEnabled(state)); bool useGluBuildMipMaps = mipmappingRequired && (!useHardwareMipMapGeneration && !image->isMipmap()); GLBufferObject* pbo = image->getOrCreateGLBufferObject(contextID); if (pbo && !needImageRescale && !useGluBuildMipMaps) { state.bindPixelBufferObject(pbo); dataPtr = reinterpret_cast(pbo->getOffset(image->getBufferIndex())); rowLength = 0; #ifdef DO_TIMING OSG_NOTICE<<"after PBO "<delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<getPixelFormat(), (GLenum)image->getDataType(), dataPtr); } else if (extensions->isCompressedTexImage2DSupported()) { GLint blockSize,size; getCompressedSize(image->getInternalTextureFormat(), inwidth, inheight, 1, blockSize,size); extensions->glCompressedTexSubImage2D(target, 0, 0,0, inwidth, inheight, (GLenum)image->getPixelFormat(), size, dataPtr); } mipmapAfterTexImage(state, mipmapResult); } else { if (image->isMipmap()) { numMipmapLevels = image->getNumMipmapLevels(); int width = inwidth; int height = inheight; if( !compressed_image ) { for( GLsizei k = 0 ; k < numMipmapLevels && (width || height) ;k++) { if (width == 0) width = 1; if (height == 0) height = 1; glTexSubImage2D( target, k, 0, 0, width, height, (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), dataPtr + image->getMipmapOffset(k)); width >>= 1; height >>= 1; } } else if (extensions->isCompressedTexImage2DSupported()) { GLint blockSize,size; for( GLsizei k = 0 ; k < numMipmapLevels && (width || height) ;k++) { if (width == 0) width = 1; if (height == 0) height = 1; getCompressedSize(image->getInternalTextureFormat(), width, height, 1, blockSize,size); //state.checkGLErrors("before extensions->glCompressedTexSubImage2D("); extensions->glCompressedTexSubImage2D(target, k, 0, 0, width, height, (GLenum)image->getPixelFormat(), size, dataPtr + image->getMipmapOffset(k)); //state.checkGLErrors("after extensions->glCompressedTexSubImage2D("); width >>= 1; height >>= 1; } } } else { //OSG_WARN<<"Warning:: cannot subload mip mapped texture from non mipmapped image."<delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<(); if (extensions->isGenerateMipMapSupported) { return true; } // FrameBufferObjects are required for glGenerateMipmap if (extensions->isFrameBufferObjectSupported && extensions->glGenerateMipmap) { return true; } } return false; } Texture::GenerateMipmapMode Texture::mipmapBeforeTexImage(const State& state, bool hardwareMipmapOn) const { if (hardwareMipmapOn) { #if defined( OSG_GLES2_AVAILABLE ) || defined( OSG_GL3_AVAILABLE ) return GENERATE_MIPMAP; #else const GLExtensions* extensions = state.get(); bool useGenerateMipMap = extensions->isFrameBufferObjectSupported && extensions->glGenerateMipmap; if (useGenerateMipMap) { if (extensions->preferGenerateMipmapSGISForPowerOfTwo) { int width = getTextureWidth(); int height = getTextureHeight(); useGenerateMipMap = ((width & (width - 1)) || (height & (height - 1))); } if (useGenerateMipMap) { useGenerateMipMap = (_internalFormatType != SIGNED_INTEGER && _internalFormatType != UNSIGNED_INTEGER); } if (useGenerateMipMap) return GENERATE_MIPMAP; } glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); return GENERATE_MIPMAP_TEX_PARAMETER; #endif } return GENERATE_MIPMAP_NONE; } void Texture::mipmapAfterTexImage(State& state, GenerateMipmapMode beforeResult) const { switch (beforeResult) { case GENERATE_MIPMAP: { unsigned int contextID = state.getContextID(); TextureObject* textureObject = getTextureObject(contextID); if (textureObject) { osg::GLExtensions* ext = state.get(); ext->glGenerateMipmap(textureObject->target()); } break; } case GENERATE_MIPMAP_TEX_PARAMETER: glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); break; case GENERATE_MIPMAP_NONE: break; } } void Texture::generateMipmap(State& state) const { const unsigned int contextID = state.getContextID(); // get the texture object for the current contextID. TextureObject* textureObject = getTextureObject(contextID); // if not initialized before, then do nothing if (textureObject == NULL) return; _texMipmapGenerationDirtyList[contextID] = 0; // if internal format does not provide automatic mipmap generation, then do manual allocation if (_internalFormatType == SIGNED_INTEGER || _internalFormatType == UNSIGNED_INTEGER) { allocateMipmap(state); return; } // get fbo extension which provides us with the glGenerateMipmapEXT function osg::GLExtensions* ext = state.get(); // FrameBufferObjects are required for glGenerateMipmap if (ext->isFrameBufferObjectSupported && ext->glGenerateMipmap) { textureObject->bind(); ext->glGenerateMipmap(textureObject->target()); // inform state that this texture is the current one bound. state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this); // if the function is not supported, then do manual allocation }else { allocateMipmap(state); } } void Texture::compileGLObjects(State& state) const { apply(state); } void Texture::resizeGLObjectBuffers(unsigned int maxSize) { _textureObjectBuffer.resize(maxSize); _texParametersDirtyList.resize(maxSize); _texMipmapGenerationDirtyList.resize(maxSize); } void Texture::releaseGLObjects(State* state) const { // if (state) OSG_NOTICE<<"Texture::releaseGLObjects contextID="<getContextID()<(this)->dirtyTextureObject(); else { unsigned int contextID = state->getContextID(); if (_textureObjectBuffer[contextID].valid()) { Texture::releaseTextureObject(contextID, _textureObjectBuffer[contextID].get()); _textureObjectBuffer[contextID] = 0; } } } }