/* -*-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 // #define DO_TIMING // #define CHECK_CONSISTENCY namespace osg { ApplicationUsageProxy Texture_e0(ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_MAX_TEXTURE_SIZE","Set the maximum size of textures."); typedef buffered_value< ref_ptr > BufferedExtensions; static BufferedExtensions s_extensions; 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; } _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); #if 0 OSG_NOTICE<<" HPOTO after _head = "<<_head<_previous = "<_previous<_next = "<_next<getNumberOrphanedTextureObjects() += numOrphaned; _parent->getNumberActiveTextureObjects() -= numOrphaned; _pendingOrphanedTextureObjects.clear(); checkConsistency(); } void Texture::TextureObjectSet::deleteAllTextureObjects() { // OSG_NOTICE<<"Texture::TextureObjectSet::deleteAllTextureObjects()"< 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 TextureOjects _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 = static_cast(ceil(double(sizeRequired) / double(_profile._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; } Texture::TextureObject* 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 TextureOjects _parent->getNumberOrphanedTextureObjects() -= 1; _parent->getNumberActiveTextureObjects() += 1; // place at back of active list addToBack(to.get()); OSG_INFO<<"Reusing orphaned TextureObject, _numOfTextureObjects="<<_numOfTextureObjects< lock(_mutex); handlePendingOrphandedTextureObjects(); return takeFromOrphans(texture); } else 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.release(); } // // no TextureObjects available to recycle so have to create one from scratch // GLuint id; glGenTextures( 1L, &id ); TextureObject* 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); 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]; } Texture::TextureObject* Texture::generateTextureObject(const Texture* texture, unsigned int contextID, GLenum target) { return getTextureObjectManager(contextID)->generateTextureObject(texture, target); } Texture::TextureObject* 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), _useHardwareMipMapGeneration(true), _unrefImageDataAfterApply(false), _clientStorageHint(false), _resizeNonPowerOfTwoHint(!OSG_GLES2_FEATURES && !OSG_GL3_FEATURES), _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), _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(_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; } } else internalFormat = image.getInternalTextureFormat(); 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; } } else internalFormat = image.getInternalTextureFormat(); 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; } } else internalFormat = image.getInternalTextureFormat(); 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; } } else internalFormat = image.getInternalTextureFormat(); 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; } } else internalFormat = image.getInternalTextureFormat(); 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; } } else internalFormat = image.getInternalTextureFormat(); break; default: break; } } _internalFormat = internalFormat; // GLES doesn't cope with internal formats of 1,2,3 and 4 so map them to the appropriate equivilants. if (_internalFormat==1) _internalFormat = GL_LUMINANCE; if (_internalFormat==2) _internalFormat = GL_LUMINANCE_ALPHA; if (_internalFormat==3) _internalFormat = GL_RGB; if (_internalFormat==4) _internalFormat = GL_RGBA; 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_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_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 ("<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) 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->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); glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, _shadow_texture_mode); // 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); } } getTextureParameterDirty(state.getContextID()) = false; } void Texture::computeRequiredTextureDimensions(State& state, const osg::Image& image,GLsizei& inwidth, GLsizei& inheight,GLsizei& numMipmapLevels) const { const unsigned int contextID = state.getContextID(); const Extensions* extensions = getExtensions(contextID,true); 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()<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()); 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.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); } 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())); #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; 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"<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()<getPixelFormat()); glPixelStorei(GL_UNPACK_ALIGNMENT,image->getPacking()); 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); } 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())); #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"<isGenerateMipMapSupported()) { return true; } const FBOExtensions* fbo_ext = FBOExtensions::instance(contextID,true); if (fbo_ext->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 int width = getTextureWidth(); int height = getTextureHeight(); //quick bithack to determine whether width or height are non-power-of-two if ((width & (width - 1)) || (height & (height - 1))) { //GL_GENERATE_MIPMAP_SGIS with non-power-of-two textures on NVIDIA hardware //is extremely slow. Use glGenerateMipmapEXT() instead if supported. if (_internalFormatType != SIGNED_INTEGER && _internalFormatType != UNSIGNED_INTEGER) { if (FBOExtensions::instance(state.getContextID(), true)->glGenerateMipmap) { 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::FBOExtensions* fbo_ext = osg::FBOExtensions::instance(contextID, true); fbo_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::FBOExtensions* fbo_ext = osg::FBOExtensions::instance(state.getContextID(), true); // check if the function is supported if (fbo_ext->glGenerateMipmap) { textureObject->bind(); fbo_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; } } } Texture::Extensions* Texture::getExtensions(unsigned int contextID,bool createIfNotInitalized) { if (!s_extensions[contextID] && createIfNotInitalized) s_extensions[contextID] = new Extensions(contextID); return s_extensions[contextID].get(); } void Texture::setExtensions(unsigned int contextID,Extensions* extensions) { s_extensions[contextID] = extensions; } Texture::Extensions::Extensions(unsigned int contextID) { const char* version = (const char*) glGetString( GL_VERSION ); if (!version) { OSG_FATAL<<"Error: In Texture::Extensions::setupGLExtensions(..) OpenGL version test failed, requires valid graphics context."<=2.0) { glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS,&_numTextureUnits); } else { glGetIntegerv(GL_MAX_TEXTURE_UNITS,&_numTextureUnits); } #endif } else { _numTextureUnits = 1; } setGLExtensionFuncPtr(_glCompressedTexImage2D,"glCompressedTexImage2D","glCompressedTexImage2DARB"); setGLExtensionFuncPtr(_glCompressedTexSubImage2D,"glCompressedTexSubImage2D","glCompressedTexSubImage2DARB"); setGLExtensionFuncPtr(_glGetCompressedTexImage,"glGetCompressedTexImage","glGetCompressedTexImageARB");; setGLExtensionFuncPtr(_glTexImage2DMultisample, "glTexImage2DMultisample", "glTexImage2DMultisampleARB"); setGLExtensionFuncPtr(_glTexParameterIiv, "glTexParameterIiv", "glTexParameterIivARB"); setGLExtensionFuncPtr(_glTexParameterIuiv, "glTexParameterIuiv", "glTexParameterIuivARB"); if (_glTexParameterIiv == NULL) setGLExtensionFuncPtr(_glTexParameterIiv, "glTexParameterIivEXT"); if (_glTexParameterIuiv == NULL) setGLExtensionFuncPtr(_glTexParameterIuiv, "glTexParameterIuivEXT"); _isTextureMaxLevelSupported = ( getGLVersionNumber() >= 1.2f ); } }