/* -*-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 using namespace osg; #ifndef GL_TEXTURE_WRAP_R #define GL_TEXTURE_WRAP_R 0x8072 #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 ApplicationUsageProxy Texture_e0(ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_MAX_TEXTURE_SIZE","Set the maximum size of textures."); class TextureObjectManager : public osg::Referenced { public: TextureObjectManager(): _expiryDelay(0.0) { // printf("Constructing TextureObjectManager\n"); } ~TextureObjectManager() { // printf("Destructing TextureObjectManager\n"); } virtual Texture::TextureObject* generateTextureObject(unsigned int contextID,GLenum target); virtual Texture::TextureObject* generateTextureObject(unsigned int contextID, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border); virtual Texture::TextureObject* reuseTextureObject(unsigned int contextID, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border); inline Texture::TextureObject* reuseOrGenerateTextureObject(unsigned int contextID, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border) { Texture::TextureObject* to = reuseTextureObject(contextID,target,numMipmapLevels,internalFormat,width,height,depth,border); if (to) return to; else return generateTextureObject(contextID,target,numMipmapLevels,internalFormat,width,height,depth,border); } void addTextureObjects(Texture::TextureObjectListMap& toblm); void addTextureObjectsFrom(Texture& texture); void flushAllTextureObjects(unsigned int contextID); void flushTextureObjects(unsigned int contextID,double currentTime, double& availableTime); void setExpiryDelay(double expiryDelay) { _expiryDelay = expiryDelay; } double getExpiryDelay() const { return _expiryDelay; } /** How long to keep unused texture objects before deletion. */ double _expiryDelay; Texture::TextureObjectListMap _textureObjectListMap; // mutex to keep access serialized. OpenThreads::Mutex _mutex; }; unsigned int Texture::s_numberTextureReusedLastInLastFrame = 0; unsigned int Texture::s_numberNewTextureInLastFrame = 0; unsigned int Texture::s_numberDeletedTextureInLastFrame = 0; unsigned int s_minimumNumberOfTextureObjectsToRetainInCache = 0; typedef buffered_value< ref_ptr > BufferedExtensions; static BufferedExtensions s_extensions; static ref_ptr s_textureObjectManager = new TextureObjectManager; void Texture::setMinimumNumberOfTextureObjectsToRetainInCache(unsigned int minimum) { s_minimumNumberOfTextureObjectsToRetainInCache = minimum; } unsigned int Texture::getMinimumNumberOfTextureObjectsToRetainInCache() { return s_minimumNumberOfTextureObjectsToRetainInCache; } Texture::TextureObject* TextureObjectManager::generateTextureObject(unsigned int /*contextID*/,GLenum target) { GLuint id; glGenTextures( 1L, &id ); return new Texture::TextureObject(id,target); } static int s_number = 0; Texture::TextureObject* TextureObjectManager::generateTextureObject(unsigned int /*contextID*/, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border) { ++s_number; ++Texture::s_numberNewTextureInLastFrame; // notify(NOTICE)<<"creating new texture object "< lock(_mutex); Texture::TextureObjectList& tol = _textureObjectListMap[contextID]; for(Texture::TextureObjectList::iterator itr = tol.begin(); itr != tol.end(); ++itr) { if ((*itr)->match(target,numMipmapLevels,internalFormat,width,height,depth,border)) { // found usable texture object. Texture::TextureObject* textureObject = (*itr).release(); tol.erase(itr); // notify(NOTICE)<<"reusing texture object "< lock(_mutex); for(unsigned int i=0; i< toblm.size(); ++i) { Texture::TextureObjectList& tol = _textureObjectListMap[i]; tol.insert(tol.end(),toblm[i].begin(),toblm[i].end()); } } void TextureObjectManager::addTextureObjectsFrom(Texture& texture) { OpenThreads::ScopedLock lock(_mutex); texture.takeTextureObjects(_textureObjectListMap); } void TextureObjectManager::flushAllTextureObjects(unsigned int contextID) { OpenThreads::ScopedLock lock(_mutex); Texture::TextureObjectList& tol = _textureObjectListMap[contextID]; // osg::notify(osg::INFO)<<"Flushing texture objects num="<_id<_id)); } tol.clear(); } void TextureObjectManager::flushTextureObjects(unsigned int contextID,double currentTime, double& availableTime) { // if no time available don't try to flush objects. if (availableTime<=0.0) return; unsigned int numObjectsDeleted = 0; unsigned int maxNumObjectsToDelete = 4; const osg::Timer& timer = *osg::Timer::instance(); osg::Timer_t start_tick = timer.tick(); double elapsedTime = 0.0; unsigned int numTexturesDeleted = 0; { OpenThreads::ScopedLock lock(_mutex); Texture::TextureObjectList& tol = _textureObjectListMap[contextID]; // reset the time of any uninitialized objects. Texture::TextureObjectList::iterator itr; for(itr=tol.begin(); itr!=tol.end(); ++itr) { if ((*itr)->_timeStamp==0.0) (*itr)->_timeStamp=currentTime; } double expiryTime = currentTime-_expiryDelay; for(itr=tol.begin(); itr!=tol.end() && elapsedTimes_minimumNumberOfTextureObjectsToRetainInCache && numObjectsDeleted_timeStamp<=expiryTime) { --s_number; ++Texture::s_numberDeletedTextureInLastFrame; glDeleteTextures( 1L, &((*itr)->_id)); itr = tol.erase(itr); ++numTexturesDeleted; ++numObjectsDeleted; } else { ++itr; } elapsedTime = timer.delta_s(start_tick,timer.tick()); } } elapsedTime = timer.delta_s(start_tick,timer.tick()); // if (numTexturesDeleted) notify(osg::NOTICE)<<"Number of Texture's deleted = "<generateTextureObject(contextID,target); else return 0; } Texture::TextureObject* Texture::generateTextureObject(unsigned int contextID, GLenum target, GLint numMipmapLevels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border) { if (getTextureObjectManager()) return getTextureObjectManager()->reuseOrGenerateTextureObject(contextID, target, numMipmapLevels, internalFormat, width, height, depth, border); else return 0; } void Texture::flushAllDeletedTextureObjects(unsigned int contextID) { if (getTextureObjectManager()) getTextureObjectManager()->flushAllTextureObjects(contextID); } void Texture::flushDeletedTextureObjects(unsigned int contextID,double currentTime, double& availbleTime) { if (getTextureObjectManager()) getTextureObjectManager()->flushTextureObjects(contextID, currentTime, availbleTime); } 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(true), _borderColor(0.0, 0.0, 0.0, 0.0), _borderWidth(0), _internalFormatMode(USE_IMAGE_DATA_FORMAT), _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), _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) return 0; } int Texture::compareTextureObjects(const Texture& rhs) const { if (_textureObjectBuffer.size()addTextureObjectsFrom(*this); } void Texture::takeTextureObjects(Texture::TextureObjectListMap& toblm) { for(unsigned int i = 0; i<_textureObjectBuffer.size();++i) { if (_textureObjectBuffer[i].valid()) { //notify(INFO) << "releasing texure "<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; default: break; } } _internalFormat = internalFormat; //osg::notify(osg::NOTICE)<<"Internal 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; } 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); if (extensions->isTextureFilterAnisotropicSupported()) { // 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()) { glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, _borderColor.ptr()); } if (extensions->isShadowSupported() && target == GL_TEXTURE_2D) { 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; bool useHardwareMipMapGeneration = !image.isMipmap() && _useHardwareMipMapGeneration && extensions->isGenerateMipMapSupported(); if( _min_filter == LINEAR || _min_filter == NEAREST || useHardwareMipMapGeneration ) { numMipmapLevels = 1; } else if( image.isMipmap() ) { numMipmapLevels = image.getNumMipmapLevels(); } else { numMipmapLevels = 0; for( ; (width || height) ;++numMipmapLevels) { if (width == 0) width = 1; if (height == 0) height = 1; width >>= 1; height >>= 1; } } } bool Texture::areAllTextureObjectsLoaded() const { for(unsigned int i=0;igetMaxNumberOfGraphicsContexts();++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::notify(osg::NOTICE)<<"glTexImage2D pixelFormat = "<getPixelFormat()<isGenerateMipMapSupported(); // 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::notify(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); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_PRIORITY,0.0f); #ifdef GL_TEXTURE_STORAGE_HINT_APPLE glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_STORAGE_HINT_APPLE , GL_STORAGE_CACHED_APPLE); #endif } unsigned char* data = (unsigned char*)image->data(); bool needImageRescale = inwidth!=image->s() || inheight!=image->t(); if (needImageRescale) { // resize the image to power of two. if (image->isMipmap()) { notify(WARN)<<"Warning:: Mipmapped osg::Image not a power of two, cannot apply to texture."<getPixelFormat(),image->getDataType(),image->getPacking())*inheight; data = new unsigned char [newTotalSize]; if (!data) { notify(WARN)<<"Warning:: Not enough memory to resize image, cannot apply to texture."<getFileName().empty()) notify(NOTICE) << "Scaling image '"<getFileName()<<"' from ("<s()<<","<t()<<") to ("<s()<<","<t()<<") to ("<getPacking()); gluScaleImage(image->getPixelFormat(), image->s(),image->t(),image->getDataType(),image->data(), inwidth,inheight,image->getDataType(),data); } unsigned char* dataMinusOffset = 0; unsigned char* dataPlusOffset = 0; const PixelBufferObject* pbo = image->getPixelBufferObject(); if (pbo && pbo->isPBOSupported(contextID) && !needImageRescale) { pbo->compileBuffer(state); pbo->bindBuffer(contextID); dataMinusOffset = data; dataPlusOffset = reinterpret_cast(pbo->offset()); #ifdef DO_TIMING osg::notify(osg::NOTICE)<<"after PBO "<delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<isMipmap() && _useHardwareMipMapGeneration && generateMipMapSupported; if( _min_filter == LINEAR || _min_filter == NEAREST || useHardwareMipMapGeneration) { bool hardwareMipMapOn = false; if (_min_filter != LINEAR && _min_filter != NEAREST) { if (useHardwareMipMapGeneration) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE); hardwareMipMapOn = true; } if ( !compressed_image) { numMipmapLevels = 1; glTexImage2D( target, 0, _internalFormat, inwidth, inheight, _borderWidth, (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), data -dataMinusOffset+dataPlusOffset); } 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, data-dataMinusOffset+dataPlusOffset); } if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE); } 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(), image->getMipmapData(k)-dataMinusOffset+dataPlusOffset); 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, image->getMipmapData(k)-dataMinusOffset+dataPlusOffset); width >>= 1; height >>= 1; } } } else { if ( !compressed_image) { numMipmapLevels = 0; gluBuild2DMipmaps( target, _internalFormat, inwidth,inheight, (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), data -dataMinusOffset+dataPlusOffset); int width = image->s(); int height = image->t(); for( numMipmapLevels = 0 ; (width || height) ; ++numMipmapLevels) { width >>= 1; height >>= 1; } } else { notify(WARN)<<"Warning:: Compressed image cannot be mip mapped"<unbindBuffer(contextID); } #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::notify(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::notify(osg::NOTICE)<<"glTexSubImage2D pixelFormat = "<getPixelFormat()<isGenerateMipMapSupported(); // select the internalFormat required for the texture. bool compressed_image = isCompressedInternalFormat((GLenum)image->getPixelFormat()); glPixelStorei(GL_UNPACK_ALIGNMENT,image->getPacking()); unsigned char* data = (unsigned char*)image->data(); bool needImageRescale = inwidth!=image->s() || inheight!=image->t(); if (needImageRescale) { // resize the image to power of two. if (image->isMipmap()) { notify(WARN)<<"Warning:: Mipmapped osg::Image not a power of two, cannot apply to texture."<getPixelFormat(),image->getDataType(),image->getPacking())*inheight; data = new unsigned char [newTotalSize]; if (!data) { notify(WARN)<<"Warning:: Not enough memory to resize image, cannot apply to texture."<getFileName().empty()) notify(NOTICE) << "Scaling image '"<getFileName()<<"' from ("<s()<<","<t()<<") to ("<s()<<","<t()<<") to ("<getPacking()); gluScaleImage(image->getPixelFormat(), image->s(),image->t(),image->getDataType(),image->data(), inwidth,inheight,image->getDataType(),data); } bool useHardwareMipMapGeneration = !image->isMipmap() && _useHardwareMipMapGeneration && generateMipMapSupported; unsigned char* dataMinusOffset=0; unsigned char* dataPlusOffset=0; const PixelBufferObject* pbo = image->getPixelBufferObject(); if (pbo && pbo->isPBOSupported(contextID) && !needImageRescale) { pbo->compileBuffer(state); pbo->bindBuffer(contextID); dataMinusOffset = data; dataPlusOffset = reinterpret_cast(pbo->offset()); #ifdef DO_TIMING osg::notify(osg::NOTICE)<<"after PBO "<delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<getPixelFormat(), (GLenum)image->getDataType(), data - dataMinusOffset + dataPlusOffset); } 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, data - dataMinusOffset + dataPlusOffset ); } if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE); } 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(), image->getMipmapData(k) - dataMinusOffset + dataPlusOffset); 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, image->getMipmapData(k) - dataMinusOffset + dataPlusOffset); //state.checkGLErrors("after extensions->glCompressedTexSubImage2D("); width >>= 1; height >>= 1; } } } else { //notify(WARN)<<"Warning:: cannot subload mip mapped texture from non mipmapped image."<unbindBuffer(contextID); } #ifdef DO_TIMING osg::notify(osg::NOTICE)<<"glTexSubImage2D "<delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"< #include void Texture::compileGLObjects(State& state) const { apply(state); } void Texture::resizeGLObjectBuffers(unsigned int maxSize) { _textureObjectBuffer.resize(maxSize); } void Texture::releaseGLObjects(State* state) const { // if (state) osg::notify(osg::NOTICE)<<"Texture::releaseGLObjects contextID="<getContextID()<(this)->dirtyTextureObject(); else { unsigned int contextID = state->getContextID(); if (_textureObjectBuffer[contextID].valid() && getTextureObjectManager()) { OpenThreads::ScopedLock lock(getTextureObjectManager()->_mutex); getTextureObjectManager()->_textureObjectListMap[contextID].push_back(_textureObjectBuffer[contextID]); _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) { setupGLExtensions(contextID); } Texture::Extensions::Extensions(const Extensions& rhs): Referenced() { _isMultiTexturingSupported = rhs._isMultiTexturingSupported; _isTextureFilterAnisotropicSupported = rhs._isTextureFilterAnisotropicSupported; _isTextureCompressionARBSupported = rhs._isTextureCompressionARBSupported; _isTextureCompressionS3TCSupported = rhs._isTextureCompressionS3TCSupported; _isTextureMirroredRepeatSupported = rhs._isTextureMirroredRepeatSupported; _isTextureEdgeClampSupported = rhs._isTextureEdgeClampSupported; _isTextureBorderClampSupported = rhs._isTextureBorderClampSupported; _isGenerateMipMapSupported = rhs._isGenerateMipMapSupported; _maxTextureSize = rhs._maxTextureSize; _glCompressedTexImage2D = rhs._glCompressedTexImage2D; _isShadowSupported = rhs._isShadowSupported; _isShadowAmbientSupported = rhs._isShadowAmbientSupported; _isClientStorageSupported = rhs._isClientStorageSupported; _isNonPowerOfTwoTextureMipMappedSupported = rhs._isNonPowerOfTwoTextureMipMappedSupported; _isNonPowerOfTwoTextureNonMipMappedSupported = rhs._isNonPowerOfTwoTextureNonMipMappedSupported; } void Texture::Extensions::lowestCommonDenominator(const Extensions& rhs) { if (!rhs._isMultiTexturingSupported) _isMultiTexturingSupported = false; if (!rhs._isTextureFilterAnisotropicSupported) _isTextureFilterAnisotropicSupported = false; if (!rhs._isTextureMirroredRepeatSupported) _isTextureMirroredRepeatSupported = false; if (!rhs._isTextureEdgeClampSupported) _isTextureEdgeClampSupported = false; if (!rhs._isTextureBorderClampSupported) _isTextureBorderClampSupported = false; if (!rhs._isTextureCompressionARBSupported) _isTextureCompressionARBSupported = false; if (!rhs._isTextureCompressionS3TCSupported) _isTextureCompressionS3TCSupported = false; if (!rhs._isGenerateMipMapSupported) _isGenerateMipMapSupported = false; if (rhs._maxTextureSize<_maxTextureSize) _maxTextureSize = rhs._maxTextureSize; if (rhs._numTextureUnits<_numTextureUnits) _numTextureUnits = rhs._numTextureUnits; if (!rhs._glCompressedTexImage2D) _glCompressedTexImage2D = 0; if (!rhs._glCompressedTexSubImage2D) _glCompressedTexSubImage2D = 0; if (!rhs._glGetCompressedTexImage) _glGetCompressedTexImage = 0; if (!rhs._isShadowSupported) _isShadowSupported = false; if (!rhs._isShadowAmbientSupported) _isShadowAmbientSupported = false; if (!rhs._isClientStorageSupported) _isClientStorageSupported = false; if (!rhs._isNonPowerOfTwoTextureMipMappedSupported) _isNonPowerOfTwoTextureMipMappedSupported = false; if (!rhs._isNonPowerOfTwoTextureNonMipMappedSupported) _isNonPowerOfTwoTextureNonMipMappedSupported = false; } void Texture::Extensions::setupGLExtensions(unsigned int contextID) { const char* version = (const char*) glGetString( GL_VERSION ); if (!version) { osg::notify(osg::FATAL)<<"Error: In Texture::Extensions::setupGLExtensions(..) OpenGL version test failed, requires valid graphics context."<= 1.3 ) || isGLExtensionSupported(contextID,"GL_ARB_multitexture") || isGLExtensionSupported(contextID,"GL_EXT_multitexture"); _isTextureFilterAnisotropicSupported = isGLExtensionSupported(contextID,"GL_EXT_texture_filter_anisotropic"); _isTextureCompressionARBSupported = ( glVersion >= 1.3 ) || isGLExtensionSupported(contextID,"GL_ARB_texture_compression"); _isTextureCompressionS3TCSupported = isGLExtensionSupported(contextID,"GL_EXT_texture_compression_s3tc"); _isTextureMirroredRepeatSupported = ( glVersion >= 1.4 ) || isGLExtensionSupported(contextID,"GL_IBM_texture_mirrored_repeat") || isGLExtensionSupported(contextID,"GL_ARB_texture_mirrored_repeat"); _isTextureEdgeClampSupported = ( glVersion >= 1.2 ) || isGLExtensionSupported(contextID,"GL_EXT_texture_edge_clamp") || isGLExtensionSupported(contextID,"GL_SGIS_texture_edge_clamp"); _isTextureBorderClampSupported = ( glVersion >= 1.3 ) || isGLExtensionSupported(contextID,"GL_ARB_texture_border_clamp"); _isGenerateMipMapSupported = (strncmp((const char*)glGetString(GL_VERSION),"1.4",3)>=0) || isGLExtensionSupported(contextID,"GL_SGIS_generate_mipmap"); _isShadowSupported = isGLExtensionSupported(contextID,"GL_ARB_shadow"); _isShadowAmbientSupported = isGLExtensionSupported(contextID,"GL_ARB_shadow_ambient"); _isClientStorageSupported = isGLExtensionSupported(contextID,"GL_APPLE_client_storage"); _isNonPowerOfTwoTextureNonMipMappedSupported = ( glVersion >= 2.0 ) || isGLExtensionSupported(contextID,"GL_ARB_texture_non_power_of_two"); _isNonPowerOfTwoTextureMipMappedSupported = _isNonPowerOfTwoTextureNonMipMappedSupported; if (rendererString.find("Radeon")!=std::string::npos || rendererString.find("RADEON")!=std::string::npos) { _isNonPowerOfTwoTextureMipMappedSupported = false; osg::notify(osg::INFO)<<"Disabling _isNonPowerOfTwoTextureMipMappedSupported for ATI hardware."<