2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "platform/platform.h"
# include "gfx/gl/gfxGLTextureManager.h"
# include "gfx/gl/gfxGLEnumTranslate.h"
# include "gfx/gfxCardProfile.h"
# include "core/util/safeDelete.h"
# include "gfx/gl/gfxGLUtils.h"
# include <squish.h>
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
GFXGLTextureManager : : GFXGLTextureManager ( )
{
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
GFXGLTextureManager : : ~ GFXGLTextureManager ( )
{
SAFE_DELETE_ARRAY ( mHashTable ) ;
}
//-----------------------------------------------------------------------------
// createTexture
//-----------------------------------------------------------------------------
GFXTextureObject * GFXGLTextureManager : : _createTextureObject ( U32 height ,
U32 width ,
U32 depth ,
GFXFormat format ,
GFXTextureProfile * profile ,
U32 numMipLevels ,
bool forceMips ,
S32 antialiasLevel ,
GFXTextureObject * inTex )
{
AssertFatal ( format > = 0 & & format < GFXFormat_COUNT , " GFXGLTextureManager::_createTexture - invalid format! " ) ;
GFXGLTextureObject * retTex ;
if ( inTex )
{
AssertFatal ( dynamic_cast < GFXGLTextureObject * > ( inTex ) , " GFXGLTextureManager::_createTexture() - Bad inTex type! " ) ;
retTex = static_cast < GFXGLTextureObject * > ( inTex ) ;
retTex - > release ( ) ;
2014-11-08 16:41:17 +00:00
retTex - > reInit ( ) ;
2012-09-19 15:15:01 +00:00
}
else
{
retTex = new GFXGLTextureObject ( GFX , profile ) ;
retTex - > registerResourceWithDevice ( GFX ) ;
}
innerCreateTexture ( retTex , height , width , depth , format , profile , numMipLevels , forceMips ) ;
return retTex ;
}
//-----------------------------------------------------------------------------
// innerCreateTexture
//-----------------------------------------------------------------------------
// This just creates the texture, no info is actually loaded to it. We do that later.
void GFXGLTextureManager : : innerCreateTexture ( GFXGLTextureObject * retTex ,
U32 height ,
U32 width ,
U32 depth ,
GFXFormat format ,
GFXTextureProfile * profile ,
U32 numMipLevels ,
bool forceMips )
{
// No 24 bit formats. They trigger various oddities because hardware (and Apple's drivers apparently...) don't natively support them.
if ( format = = GFXFormatR8G8B8 )
format = GFXFormatR8G8B8A8 ;
retTex - > mFormat = format ;
retTex - > mIsZombie = false ;
retTex - > mIsNPoT2 = false ;
2014-11-08 16:41:17 +00:00
GLenum binding = ( ( height = = 1 | | width = = 1 ) & & ( height ! = width ) ) ? GL_TEXTURE_1D : ( ( depth = = 0 ) ? GL_TEXTURE_2D : GL_TEXTURE_3D ) ;
2012-09-19 15:15:01 +00:00
if ( ( profile - > testFlag ( GFXTextureProfile : : RenderTarget ) | | profile - > testFlag ( GFXTextureProfile : : ZTarget ) ) & & ( ! isPow2 ( width ) | | ! isPow2 ( height ) ) & & ! depth )
retTex - > mIsNPoT2 = true ;
retTex - > mBinding = binding ;
// Bind it
2014-11-08 16:41:17 +00:00
PRESERVE_TEXTURE ( binding ) ;
glBindTexture ( retTex - > getBinding ( ) , retTex - > getHandle ( ) ) ;
2012-09-19 15:15:01 +00:00
// Create it
2014-11-08 16:41:17 +00:00
// @todo OPENGL - Creating mipmaps for compressed formats. Not supported on OpenGL ES and bugged on AMD. We use mipmaps present on file.
if ( forceMips & & ! retTex - > mIsNPoT2 & & ! isCompressedFormat ( format ) )
2012-09-19 15:15:01 +00:00
{
2014-11-08 16:41:17 +00:00
retTex - > mMipLevels = numMipLevels > 1 ? numMipLevels : 0 ;
2012-09-19 15:15:01 +00:00
}
else if ( profile - > testFlag ( GFXTextureProfile : : NoMipmap ) | | profile - > testFlag ( GFXTextureProfile : : RenderTarget ) | | numMipLevels = = 1 | | retTex - > mIsNPoT2 )
{
retTex - > mMipLevels = 1 ;
}
else
{
2014-11-08 16:41:17 +00:00
retTex - > mMipLevels = numMipLevels ;
2012-09-19 15:15:01 +00:00
}
2014-11-08 16:41:17 +00:00
// @todo OPENGL - OpenGL ES2 not support mipmaps on NPOT textures
#if 0
2012-09-19 15:15:01 +00:00
if ( ! retTex - > mIsNPoT2 )
{
if ( ! isPow2 ( width ) )
width = getNextPow2 ( width ) ;
if ( ! isPow2 ( height ) )
height = getNextPow2 ( height ) ;
if ( depth & & ! isPow2 ( depth ) )
depth = getNextPow2 ( depth ) ;
}
2014-11-08 16:41:17 +00:00
# endif
2012-09-19 15:15:01 +00:00
AssertFatal ( GFXGLTextureInternalFormat [ format ] ! = GL_ZERO , " GFXGLTextureManager::innerCreateTexture - invalid internal format " ) ;
AssertFatal ( GFXGLTextureFormat [ format ] ! = GL_ZERO , " GFXGLTextureManager::innerCreateTexture - invalid format " ) ;
AssertFatal ( GFXGLTextureType [ format ] ! = GL_ZERO , " GFXGLTextureManager::innerCreateTexture - invalid type " ) ;
2014-11-08 16:41:17 +00:00
//calculate num mipmaps
if ( retTex - > mMipLevels = = 0 )
retTex - > mMipLevels = getMaxMipmaps ( width , height , 1 ) ;
glTexParameteri ( binding , GL_TEXTURE_MAX_LEVEL , retTex - > mMipLevels - 1 ) ;
if ( gglHasExtension ( ARB_texture_storage ) )
{
if ( binding = = GL_TEXTURE_2D )
glTexStorage2D ( retTex - > getBinding ( ) , retTex - > mMipLevels , GFXGLTextureInternalFormat [ format ] , width , height ) ;
else if ( binding = = GL_TEXTURE_1D )
glTexStorage1D ( retTex - > getBinding ( ) , retTex - > mMipLevels , GFXGLTextureInternalFormat [ format ] , getMax ( width , height ) ) ;
else
glTexStorage3D ( retTex - > getBinding ( ) , retTex - > mMipLevels , GFXGLTextureInternalFormat [ format ] , width , height , depth ) ;
}
else
{
//If it wasn't for problems on amd drivers this next part could be really simplified and we wouldn't need to go through manually creating our
//mipmap pyramid and instead just use glGenerateMipmap
if ( isCompressedFormat ( format ) )
{
AssertFatal ( binding = = GL_TEXTURE_2D ,
" GFXGLTextureManager::innerCreateTexture - Only compressed 2D textures are supported " ) ;
U32 tempWidth = width ;
U32 tempHeight = height ;
U32 size = getCompressedSurfaceSize ( format , height , width ) ;
//Fill compressed images with 0's
U8 * pTemp = ( U8 * ) dMalloc ( sizeof ( U8 ) * size ) ;
dMemset ( pTemp , 0 , size ) ;
for ( U32 i = 0 ; i < retTex - > mMipLevels ; i + + )
{
tempWidth = getMax ( U32 ( 1 ) , width > > i ) ;
tempHeight = getMax ( U32 ( 1 ) , height > > i ) ;
size = getCompressedSurfaceSize ( format , width , height , i ) ;
glCompressedTexImage2D ( binding , i , GFXGLTextureInternalFormat [ format ] , tempWidth , tempHeight , 0 , size , pTemp ) ;
}
dFree ( pTemp ) ;
}
else
{
if ( binding = = GL_TEXTURE_2D )
glTexImage2D ( binding , 0 , GFXGLTextureInternalFormat [ format ] , width , height , 0 , GFXGLTextureFormat [ format ] , GFXGLTextureType [ format ] , NULL ) ;
else if ( binding = = GL_TEXTURE_1D )
glTexImage1D ( binding , 0 , GFXGLTextureInternalFormat [ format ] , ( width > 1 ? width : height ) , 0 , GFXGLTextureFormat [ format ] , GFXGLTextureType [ format ] , NULL ) ;
else
glTexImage3D ( GL_TEXTURE_3D , 0 , GFXGLTextureInternalFormat [ format ] , width , height , depth , 0 , GFXGLTextureFormat [ format ] , GFXGLTextureType [ format ] , NULL ) ;
if ( retTex - > mMipLevels > 1 )
glGenerateMipmap ( binding ) ;
}
}
2012-09-19 15:15:01 +00:00
// Complete the texture
2014-11-08 16:41:17 +00:00
// Complete the texture - this does get changed later but we need to complete the texture anyway
if ( retTex - > mMipLevels = = 1 )
glTexParameteri ( binding , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
else
glTexParameteri ( binding , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_LINEAR ) ;
2012-09-19 15:15:01 +00:00
glTexParameteri ( binding , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
glTexParameteri ( binding , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE ) ;
glTexParameteri ( binding , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE ) ;
if ( binding = = GL_TEXTURE_3D )
glTexParameteri ( binding , GL_TEXTURE_WRAP_R , GL_CLAMP_TO_EDGE ) ;
2014-11-08 16:41:17 +00:00
if ( GFXGLTextureSwizzle [ format ] )
glTexParameteriv ( binding , GL_TEXTURE_SWIZZLE_RGBA , GFXGLTextureSwizzle [ format ] ) ;
2012-09-19 15:15:01 +00:00
// Get the size from GL (you never know...)
GLint texHeight , texWidth , texDepth = 0 ;
glGetTexLevelParameteriv ( binding , 0 , GL_TEXTURE_WIDTH , & texWidth ) ;
glGetTexLevelParameteriv ( binding , 0 , GL_TEXTURE_HEIGHT , & texHeight ) ;
if ( binding = = GL_TEXTURE_3D )
glGetTexLevelParameteriv ( binding , 0 , GL_TEXTURE_DEPTH , & texDepth ) ;
retTex - > mTextureSize . set ( texWidth , texHeight , texDepth ) ;
}
//-----------------------------------------------------------------------------
// loadTexture - GBitmap
//-----------------------------------------------------------------------------
static void _fastTextureLoad ( GFXGLTextureObject * texture , GBitmap * pDL )
{
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER_ARB , texture - > getBuffer ( ) ) ;
U32 bufSize = pDL - > getWidth ( 0 ) * pDL - > getHeight ( 0 ) * pDL - > getBytesPerPixel ( ) ;
glBufferData ( GL_PIXEL_UNPACK_BUFFER_ARB , bufSize , NULL , GL_STREAM_DRAW ) ;
if ( pDL - > getFormat ( ) = = GFXFormatR8G8B8A8 | | pDL - > getFormat ( ) = = GFXFormatR8G8B8X8 )
2014-11-08 16:41:17 +00:00
{
FrameAllocatorMarker mem ;
U8 * pboMemory = ( U8 * ) mem . alloc ( bufSize ) ;
2012-09-19 15:15:01 +00:00
GFX - > getDeviceSwizzle32 ( ) - > ToBuffer ( pboMemory , pDL - > getBits ( 0 ) , bufSize ) ;
2014-11-08 16:41:17 +00:00
glBufferSubData ( GL_PIXEL_UNPACK_BUFFER_ARB , 0 , bufSize , pboMemory ) ;
}
2012-09-19 15:15:01 +00:00
else
2014-11-08 16:41:17 +00:00
{
glBufferSubData ( GL_PIXEL_UNPACK_BUFFER_ARB , 0 , bufSize , pDL - > getBits ( 0 ) ) ;
}
2012-09-19 15:15:01 +00:00
2014-11-08 16:41:17 +00:00
if ( texture - > getBinding ( ) = = GL_TEXTURE_2D )
glTexSubImage2D ( texture - > getBinding ( ) , 0 , 0 , 0 , pDL - > getWidth ( 0 ) , pDL - > getHeight ( 0 ) , GFXGLTextureFormat [ pDL - > getFormat ( ) ] , GFXGLTextureType [ pDL - > getFormat ( ) ] , NULL ) ;
else
glTexSubImage1D ( texture - > getBinding ( ) , 0 , 0 , ( pDL - > getWidth ( 0 ) > 1 ? pDL - > getWidth ( 0 ) : pDL - > getHeight ( 0 ) ) , GFXGLTextureFormat [ pDL - > getFormat ( ) ] , GFXGLTextureType [ pDL - > getFormat ( ) ] , NULL ) ;
2012-09-19 15:15:01 +00:00
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER_ARB , 0 ) ;
}
static void _slowTextureLoad ( GFXGLTextureObject * texture , GBitmap * pDL )
{
2014-11-08 16:41:17 +00:00
if ( texture - > getBinding ( ) = = GL_TEXTURE_2D )
glTexSubImage2D ( texture - > getBinding ( ) , 0 , 0 , 0 , pDL - > getWidth ( 0 ) , pDL - > getHeight ( 0 ) , GFXGLTextureFormat [ pDL - > getFormat ( ) ] , GFXGLTextureType [ pDL - > getFormat ( ) ] , pDL - > getBits ( 0 ) ) ;
else
glTexSubImage1D ( texture - > getBinding ( ) , 0 , 0 , ( pDL - > getWidth ( 0 ) > 1 ? pDL - > getWidth ( 0 ) : pDL - > getHeight ( 0 ) ) , GFXGLTextureFormat [ pDL - > getFormat ( ) ] , GFXGLTextureType [ pDL - > getFormat ( ) ] , pDL - > getBits ( 0 ) ) ;
2012-09-19 15:15:01 +00:00
}
bool GFXGLTextureManager : : _loadTexture ( GFXTextureObject * aTexture , GBitmap * pDL )
{
GFXGLTextureObject * texture = static_cast < GFXGLTextureObject * > ( aTexture ) ;
2014-11-08 16:41:17 +00:00
AssertFatal ( texture - > getBinding ( ) = = GL_TEXTURE_1D | | texture - > getBinding ( ) = = GL_TEXTURE_2D ,
" GFXGLTextureManager::_loadTexture(GBitmap) - This method can only be used with 1D/2D textures " ) ;
2012-09-19 15:15:01 +00:00
2014-11-08 16:41:17 +00:00
if ( texture - > getBinding ( ) = = GL_TEXTURE_3D )
2012-09-19 15:15:01 +00:00
return false ;
// No 24bit formats.
if ( pDL - > getFormat ( ) = = GFXFormatR8G8B8 )
pDL - > setFormat ( GFXFormatR8G8B8A8 ) ;
// Bind to edit
2014-11-08 16:41:17 +00:00
PRESERVE_TEXTURE ( texture - > getBinding ( ) ) ;
2012-09-19 15:15:01 +00:00
glBindTexture ( texture - > getBinding ( ) , texture - > getHandle ( ) ) ;
2014-11-08 16:41:17 +00:00
texture - > mFormat = pDL - > getFormat ( ) ;
2012-09-19 15:15:01 +00:00
if ( pDL - > getFormat ( ) = = GFXFormatR8G8B8A8 | | pDL - > getFormat ( ) = = GFXFormatR8G8B8X8 )
_fastTextureLoad ( texture , pDL ) ;
else
_slowTextureLoad ( texture , pDL ) ;
2014-11-08 16:41:17 +00:00
if ( texture - > getMipLevels ( ) ! = 1 )
glGenerateMipmap ( texture - > getBinding ( ) ) ;
2012-09-19 15:15:01 +00:00
return true ;
}
bool GFXGLTextureManager : : _loadTexture ( GFXTextureObject * aTexture , DDSFile * dds )
{
AssertFatal ( ! ( dds - > mFormat = = GFXFormatDXT2 | | dds - > mFormat = = GFXFormatDXT4 ) , " GFXGLTextureManager::_loadTexture - OpenGL does not support DXT2 or DXT4 compressed textures " ) ;
GFXGLTextureObject * texture = static_cast < GFXGLTextureObject * > ( aTexture ) ;
AssertFatal ( texture - > getBinding ( ) = = GL_TEXTURE_2D ,
" GFXGLTextureManager::_loadTexture(DDSFile) - This method can only be used with 2D textures " ) ;
if ( texture - > getBinding ( ) ! = GL_TEXTURE_2D )
return false ;
2014-11-08 16:41:17 +00:00
PRESERVE_TEXTURE ( texture - > getBinding ( ) ) ;
2012-09-19 15:15:01 +00:00
glBindTexture ( texture - > getBinding ( ) , texture - > getHandle ( ) ) ;
2014-11-08 16:41:17 +00:00
texture - > mFormat = dds - > mFormat ;
2012-09-19 15:15:01 +00:00
U32 numMips = dds - > mSurfaces [ 0 ] - > mMips . size ( ) ;
if ( GFX - > getCardProfiler ( ) - > queryProfile ( " GL::Workaround::noManualMips " ) )
numMips = 1 ;
for ( U32 i = 0 ; i < numMips ; i + + )
{
2014-11-08 16:41:17 +00:00
if ( isCompressedFormat ( dds - > mFormat ) )
2012-09-19 15:15:01 +00:00
{
if ( ( ! isPow2 ( dds - > getWidth ( ) ) | | ! isPow2 ( dds - > getHeight ( ) ) ) & & GFX - > getCardProfiler ( ) - > queryProfile ( " GL::Workaround::noCompressedNPoTTextures " ) )
{
U32 squishFlag = squish : : kDxt1 ;
switch ( dds - > mFormat )
{
case GFXFormatDXT3 :
squishFlag = squish : : kDxt3 ;
break ;
case GFXFormatDXT5 :
squishFlag = squish : : kDxt5 ;
break ;
default :
break ;
}
U8 * uncompressedTex = new U8 [ dds - > getWidth ( i ) * dds - > getHeight ( i ) * 4 ] ;
squish : : DecompressImage ( uncompressedTex , dds - > getWidth ( i ) , dds - > getHeight ( i ) , dds - > mSurfaces [ 0 ] - > mMips [ i ] , squishFlag ) ;
glTexSubImage2D ( texture - > getBinding ( ) , i , 0 , 0 , dds - > getWidth ( i ) , dds - > getHeight ( i ) , GL_RGBA , GL_UNSIGNED_BYTE , uncompressedTex ) ;
delete [ ] uncompressedTex ;
}
else
glCompressedTexSubImage2D ( texture - > getBinding ( ) , i , 0 , 0 , dds - > getWidth ( i ) , dds - > getHeight ( i ) , GFXGLTextureInternalFormat [ dds - > mFormat ] , dds - > getSurfaceSize ( dds - > getHeight ( ) , dds - > getWidth ( ) , i ) , dds - > mSurfaces [ 0 ] - > mMips [ i ] ) ;
}
else
glTexSubImage2D ( texture - > getBinding ( ) , i , 0 , 0 , dds - > getWidth ( i ) , dds - > getHeight ( i ) , GFXGLTextureFormat [ dds - > mFormat ] , GFXGLTextureType [ dds - > mFormat ] , dds - > mSurfaces [ 0 ] - > mMips [ i ] ) ;
}
2014-11-08 16:41:17 +00:00
if ( numMips ! = 1 & & ! isCompressedFormat ( dds - > mFormat ) )
glGenerateMipmap ( texture - > getBinding ( ) ) ;
2012-09-19 15:15:01 +00:00
return true ;
}
bool GFXGLTextureManager : : _loadTexture ( GFXTextureObject * aTexture , void * raw )
{
if ( aTexture - > getDepth ( ) < 1 )
return false ;
GFXGLTextureObject * texture = static_cast < GFXGLTextureObject * > ( aTexture ) ;
PRESERVE_3D_TEXTURE ( ) ;
2014-11-08 16:41:17 +00:00
glBindTexture ( texture - > getBinding ( ) , texture - > getHandle ( ) ) ;
2012-09-19 15:15:01 +00:00
glTexSubImage3D ( GL_TEXTURE_3D , 0 , 0 , 0 , 0 , texture - > getWidth ( ) , texture - > getHeight ( ) , texture - > getDepth ( ) , GFXGLTextureFormat [ texture - > mFormat ] , GFXGLTextureType [ texture - > mFormat ] , raw ) ;
return true ;
}
bool GFXGLTextureManager : : _freeTexture ( GFXTextureObject * texture , bool zombify /*= false*/ )
{
if ( zombify )
static_cast < GFXGLTextureObject * > ( texture ) - > zombify ( ) ;
else
static_cast < GFXGLTextureObject * > ( texture ) - > release ( ) ;
return true ;
}
bool GFXGLTextureManager : : _refreshTexture ( GFXTextureObject * texture )
{
U32 usedStrategies = 0 ;
GFXGLTextureObject * realTex = static_cast < GFXGLTextureObject * > ( texture ) ;
if ( texture - > mProfile - > doStoreBitmap ( ) )
{
if ( realTex - > isZombie ( ) )
{
realTex - > resurrect ( ) ;
innerCreateTexture ( realTex , texture - > getHeight ( ) , texture - > getWidth ( ) , texture - > getDepth ( ) , texture - > mFormat , texture - > mProfile , texture - > mMipLevels ) ;
}
if ( texture - > mBitmap )
_loadTexture ( texture , texture - > mBitmap ) ;
if ( texture - > mDDS )
return false ;
usedStrategies + + ;
}
if ( texture - > mProfile - > isRenderTarget ( ) | | texture - > mProfile - > isDynamic ( ) | | texture - > mProfile - > isZTarget ( ) | | ! usedStrategies )
{
realTex - > release ( ) ;
realTex - > resurrect ( ) ;
innerCreateTexture ( realTex , texture - > getHeight ( ) , texture - > getWidth ( ) , texture - > getDepth ( ) , texture - > mFormat , texture - > mProfile , texture - > mMipLevels ) ;
realTex - > reloadFromCache ( ) ;
usedStrategies + + ;
}
AssertFatal ( usedStrategies < 2 , " GFXGLTextureManager::_refreshTexture - Inconsistent profile flags (store bitmap and dynamic/target " ) ;
return true ;
}