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,
|
2025-12-22 10:29:01 +00:00
|
|
|
U32 arraySize,
|
2012-09-19 15:15:01 +00:00
|
|
|
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 );
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
innerCreateTexture(retTex, height, width, depth, format, profile, numMipLevels, forceMips, arraySize);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
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,
|
2025-12-22 10:29:01 +00:00
|
|
|
bool forceMips,
|
|
|
|
|
U32 arraySize)
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
|
|
|
|
// No 24 bit formats. They trigger various oddities because hardware (and Apple's drivers apparently...) don't natively support them.
|
2017-06-23 16:36:20 +00:00
|
|
|
if (format == GFXFormatR8G8B8)
|
2012-09-19 15:15:01 +00:00
|
|
|
format = GFXFormatR8G8B8A8;
|
2017-06-23 16:36:20 +00:00
|
|
|
else if (format == GFXFormatR8G8B8_SRGB)
|
|
|
|
|
format = GFXFormatR8G8B8A8_SRGB;
|
2025-12-22 10:29:01 +00:00
|
|
|
|
|
|
|
|
retTex->mProfile = profile;
|
2012-09-19 15:15:01 +00:00
|
|
|
retTex->mFormat = format;
|
|
|
|
|
retTex->mIsZombie = false;
|
|
|
|
|
retTex->mIsNPoT2 = false;
|
|
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
const bool isCube = profile->isCubeMap();
|
|
|
|
|
GLenum binding;
|
|
|
|
|
|
|
|
|
|
if (isCube)
|
|
|
|
|
{
|
|
|
|
|
binding = (arraySize > 1) ? GL_TEXTURE_CUBE_MAP_ARRAY : GL_TEXTURE_CUBE_MAP;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const bool is3D = (depth > 1);
|
|
|
|
|
const bool is1D = (height == 1 && width > 1);
|
|
|
|
|
|
|
|
|
|
if (is3D)
|
|
|
|
|
binding = GL_TEXTURE_3D;
|
|
|
|
|
else if (is1D)
|
|
|
|
|
binding = (arraySize > 1) ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D;
|
|
|
|
|
else
|
|
|
|
|
binding = (arraySize > 1) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
|
|
|
|
|
}
|
|
|
|
|
|
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.
|
2017-06-23 16:36:20 +00:00
|
|
|
if( forceMips && !retTex->mIsNPoT2 && !ImageUtil::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
|
|
|
}
|
2025-08-06 17:57:59 +00:00
|
|
|
else if(profile->testFlag(GFXTextureProfile::NoMipmap) || numMipLevels == 1)
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
|
|
|
|
retTex->mMipLevels = 1;
|
|
|
|
|
}
|
2025-08-06 17:57:59 +00:00
|
|
|
else if (profile->testFlag(GFXTextureProfile::RenderTarget))
|
|
|
|
|
{
|
|
|
|
|
if (numMipLevels == 0) //auto
|
|
|
|
|
numMipLevels = mFloor(mLog2(mMax(width, height))) + 1;
|
|
|
|
|
else if (numMipLevels > 1) //capped
|
|
|
|
|
numMipLevels = mMin(numMipLevels, mFloor(mLog2(mMax(width, height))) + 1);
|
|
|
|
|
retTex->mMipLevels = mClampF(numMipLevels, 1, 13);
|
|
|
|
|
}
|
2012-09-19 15:15:01 +00:00
|
|
|
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 );
|
2025-12-22 10:29:01 +00:00
|
|
|
|
|
|
|
|
bool hasTexStorage = false;
|
|
|
|
|
// not supported when creating these.
|
|
|
|
|
if (arraySize > 1 || isCube || profile->isDynamic())
|
|
|
|
|
hasTexStorage = false;
|
|
|
|
|
|
|
|
|
|
const bool isCompressed = ImageUtil::isCompressedFormat(format);
|
|
|
|
|
|
|
|
|
|
// --- Allocation by binding ---
|
|
|
|
|
if (binding == GL_TEXTURE_CUBE_MAP)
|
2014-11-08 16:41:17 +00:00
|
|
|
{
|
2025-12-22 10:29:01 +00:00
|
|
|
// Single cubemap: prefer glTexStorage2D if available, else per-face texImage2D
|
|
|
|
|
if (hasTexStorage)
|
|
|
|
|
{
|
|
|
|
|
// Some drivers accept texStorage2D with GL_TEXTURE_CUBE_MAP
|
|
|
|
|
glTexStorage2D(GL_TEXTURE_CUBE_MAP, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Explicitly allocate each face/level
|
|
|
|
|
for (U32 face = 0; face < 6; ++face)
|
|
|
|
|
{
|
|
|
|
|
for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
|
|
|
|
|
{
|
|
|
|
|
U32 mipW = getMax(1u, width >> mip);
|
|
|
|
|
U32 mipH = getMax(1u, height >> mip);
|
|
|
|
|
|
|
|
|
|
if (isCompressed)
|
|
|
|
|
{
|
|
|
|
|
U32 size = getCompressedSurfaceSize(format, width, height, mip);
|
|
|
|
|
glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mip, GFXGLTextureInternalFormat[format], mipW, mipH, 0, size, nullptr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mip, GFXGLTextureInternalFormat[format], mipW, mipH, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-11-08 16:41:17 +00:00
|
|
|
}
|
2025-12-22 10:29:01 +00:00
|
|
|
else if (binding == GL_TEXTURE_CUBE_MAP_ARRAY)
|
2014-11-08 16:41:17 +00:00
|
|
|
{
|
2025-12-22 10:29:01 +00:00
|
|
|
// cube-map array: layers = arraySize * 6
|
|
|
|
|
U32 layers = getMax(1u, arraySize) * 6u;
|
|
|
|
|
if (hasTexStorage)
|
|
|
|
|
{
|
|
|
|
|
glTexStorage3D(GL_TEXTURE_CUBE_MAP_ARRAY, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height, layers);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// fallback to glTexImage3D with NULL data
|
|
|
|
|
for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
|
|
|
|
|
{
|
|
|
|
|
U32 mipW = getMax(1u, width >> mip);
|
|
|
|
|
U32 mipH = getMax(1u, height >> mip);
|
|
|
|
|
glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, GFXGLTextureInternalFormat[format], mipW, mipH, layers, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (binding == GL_TEXTURE_2D_ARRAY)
|
|
|
|
|
{
|
|
|
|
|
// 2D texture array: depth = arraySize (layers)
|
|
|
|
|
U32 layers = getMax(1u, arraySize);
|
|
|
|
|
if (hasTexStorage)
|
|
|
|
|
{
|
|
|
|
|
glTexStorage3D(GL_TEXTURE_2D_ARRAY, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height, layers);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
|
|
|
|
|
{
|
|
|
|
|
U32 mipW = getMax(1u, width >> mip);
|
|
|
|
|
U32 mipH = getMax(1u, height >> mip);
|
|
|
|
|
glTexImage3D(GL_TEXTURE_2D_ARRAY, mip, GFXGLTextureInternalFormat[format], mipW, mipH, layers, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (binding == GL_TEXTURE_1D_ARRAY)
|
|
|
|
|
{
|
|
|
|
|
// 1D array stored as GL_TEXTURE_1D_ARRAY. glTexStorage2D can be used for 1D arrays with height=layers on many drivers.
|
|
|
|
|
U32 layers = getMax(1u, arraySize);
|
|
|
|
|
if (hasTexStorage)
|
|
|
|
|
{
|
|
|
|
|
// glTexStorage2D works for GL_TEXTURE_1D_ARRAY (width, layers)
|
|
|
|
|
glTexStorage2D(GL_TEXTURE_1D_ARRAY, retTex->mMipLevels, GFXGLTextureInternalFormat[format], getMax(width, height), layers);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// fallback: allocate as 2D where the "height" dimension is layers via glTexImage2D? Not ideal.
|
|
|
|
|
// Safer: use glTexImage2D with target GL_TEXTURE_1D_ARRAY is invalid; instead use glTexImage3D with depth=layers
|
|
|
|
|
for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
|
|
|
|
|
{
|
|
|
|
|
U32 mipW = getMax(1u, getMax(width, height) >> mip);
|
|
|
|
|
glTexImage3D(GL_TEXTURE_1D_ARRAY, mip, GFXGLTextureInternalFormat[format], mipW, layers, 1, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (binding == GL_TEXTURE_1D)
|
|
|
|
|
{
|
|
|
|
|
if (hasTexStorage)
|
|
|
|
|
glTexStorage1D(GL_TEXTURE_1D, retTex->mMipLevels, GFXGLTextureInternalFormat[format], getMax(width, height));
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
|
|
|
|
|
{
|
|
|
|
|
U32 mipW = getMax(1u, getMax(width, height) >> mip);
|
|
|
|
|
glTexImage1D(GL_TEXTURE_1D, mip, GFXGLTextureInternalFormat[format], mipW, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (binding == GL_TEXTURE_3D)
|
|
|
|
|
{
|
|
|
|
|
if (hasTexStorage)
|
|
|
|
|
glTexStorage3D(GL_TEXTURE_3D, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height, depth);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
|
|
|
|
|
{
|
|
|
|
|
U32 mipW = getMax(1u, width >> mip);
|
|
|
|
|
U32 mipH = getMax(1u, height >> mip);
|
|
|
|
|
U32 mipD = getMax(1u, depth >> mip);
|
|
|
|
|
glTexImage3D(GL_TEXTURE_3D, mip, GFXGLTextureInternalFormat[format], mipW, mipH, mipD, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // GL_TEXTURE_2D (default)
|
|
|
|
|
{
|
|
|
|
|
if (hasTexStorage)
|
|
|
|
|
glTexStorage2D(GL_TEXTURE_2D, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
|
|
|
|
|
{
|
|
|
|
|
U32 mipW = getMax(1u, width >> mip);
|
|
|
|
|
U32 mipH = getMax(1u, height >> mip);
|
2014-11-08 16:41:17 +00:00
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
if (isCompressed)
|
|
|
|
|
{
|
|
|
|
|
U32 size = getCompressedSurfaceSize(format, width, height, mip);
|
|
|
|
|
glCompressedTexImage2D(GL_TEXTURE_2D, mip, GFXGLTextureInternalFormat[format], mipW, mipH, 0, size, nullptr);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, mip, GFXGLTextureInternalFormat[format], mipW, mipH, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-11-08 16:41:17 +00:00
|
|
|
}
|
2025-12-22 10:29:01 +00:00
|
|
|
|
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
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
GLint texHeight = 0, texWidth = 0, texDepth = 0;
|
|
|
|
|
|
|
|
|
|
GLenum queryTarget = binding;
|
|
|
|
|
if (binding == GL_TEXTURE_CUBE_MAP)
|
|
|
|
|
{
|
|
|
|
|
// Query a specific face, e.g. +X
|
|
|
|
|
queryTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glGetTexLevelParameteriv(queryTarget, 0, GL_TEXTURE_WIDTH, &texWidth);
|
|
|
|
|
glGetTexLevelParameteriv(queryTarget, 0, GL_TEXTURE_HEIGHT, &texHeight);
|
|
|
|
|
if (binding == GL_TEXTURE_3D)
|
|
|
|
|
glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_DEPTH, &texDepth);
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
retTex->mTextureSize.set(texWidth, texHeight, texDepth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// loadTexture - GBitmap
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
static void _textureUpload(const S32 width, const S32 height,const S32 bytesPerPixel,const GFXGLTextureObject* texture, const GFXFormat fmt, const U8* data,const S32 mip=0, const U32 face = 0, Swizzle<U8, 4> *pSwizzle = NULL)
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
2016-05-07 02:05:32 +00:00
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->getBuffer());
|
2017-06-23 16:36:20 +00:00
|
|
|
U32 bufSize = width * height * bytesPerPixel;
|
2016-05-07 02:05:32 +00:00
|
|
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, bufSize, NULL, GL_STREAM_DRAW);
|
2017-06-23 16:36:20 +00:00
|
|
|
|
|
|
|
|
if(pSwizzle)
|
2014-11-08 16:41:17 +00:00
|
|
|
{
|
2016-05-07 03:44:41 +00:00
|
|
|
PROFILE_SCOPE(Swizzle32_Upload);
|
2016-05-07 00:35:40 +00:00
|
|
|
U8* pboMemory = (U8*)dMalloc(bufSize);
|
2017-06-23 16:36:20 +00:00
|
|
|
pSwizzle->ToBuffer(pboMemory, data, bufSize);
|
|
|
|
|
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, bufSize, pboMemory);
|
2016-05-07 00:35:40 +00:00
|
|
|
dFree(pboMemory);
|
2014-11-08 16:41:17 +00:00
|
|
|
}
|
2012-09-19 15:15:01 +00:00
|
|
|
else
|
2014-11-08 16:41:17 +00:00
|
|
|
{
|
2016-05-07 03:44:41 +00:00
|
|
|
PROFILE_SCOPE(SwizzleNull_Upload);
|
2017-06-23 16:36:20 +00:00
|
|
|
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, bufSize, data);
|
2014-11-08 16:41:17 +00:00
|
|
|
}
|
2017-06-23 16:36:20 +00:00
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
if(texture->getBinding() == GL_TEXTURE_CUBE_MAP)
|
|
|
|
|
glTexSubImage2D(GFXGLFaceType[face], mip, 0, 0, width, height, GFXGLTextureFormat[fmt], GFXGLTextureType[fmt], NULL);
|
|
|
|
|
else if (texture->getBinding() == GL_TEXTURE_2D)
|
2017-06-23 16:36:20 +00:00
|
|
|
glTexSubImage2D(texture->getBinding(), mip, 0, 0, width, height, GFXGLTextureFormat[fmt], GFXGLTextureType[fmt], NULL);
|
2014-11-08 16:41:17 +00:00
|
|
|
else
|
2017-06-23 16:36:20 +00:00
|
|
|
glTexSubImage1D(texture->getBinding(), mip, 0, (width > 1 ? width : height), GFXGLTextureFormat[fmt], GFXGLTextureType[fmt], NULL);
|
2012-09-19 15:15:01 +00:00
|
|
|
|
2017-06-23 16:36:20 +00:00
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL)
|
|
|
|
|
{
|
2025-12-22 10:29:01 +00:00
|
|
|
PROFILE_SCOPE(GFXGLTextureManager_loadTextureGBitmap);
|
2012-09-19 15:15:01 +00:00
|
|
|
GFXGLTextureObject *texture = static_cast<GFXGLTextureObject*>(aTexture);
|
2025-12-22 10:29:01 +00:00
|
|
|
|
|
|
|
|
const GLenum target = texture->getBinding();
|
|
|
|
|
|
|
|
|
|
AssertFatal(target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP,
|
|
|
|
|
"GFXGLTextureManager::_loadTexture(GBitmap) - This method can only be used with 1D/2D and CubeMap 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;
|
2025-12-22 10:29:01 +00:00
|
|
|
//
|
|
|
|
|
//// No 24bit formats.
|
|
|
|
|
//if(pDL->getFormat() == GFXFormatR8G8B8)
|
|
|
|
|
// pDL->setFormat(GFXFormatR8G8B8A8);
|
|
|
|
|
//else if (pDL->getFormat() == GFXFormatR8G8B8_SRGB)
|
|
|
|
|
// pDL->setFormat(GFXFormatR8G8B8A8_SRGB);
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
// 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
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
const U32 mipLevels = texture->getMipLevels();
|
|
|
|
|
const bool isCubemap = (target == GL_TEXTURE_CUBE_MAP) && pDL->getNumFaces() > 1;
|
|
|
|
|
U32 faceCount = isCubemap ? 6 : 1;
|
2014-11-08 16:41:17 +00:00
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
for (U32 mip = 0; mip < mipLevels; mip++)
|
|
|
|
|
{
|
|
|
|
|
const GLsizei width = getMax(1u, pDL->getWidth(mip));
|
|
|
|
|
const GLsizei height = getMax(1u, pDL->getHeight(mip));
|
|
|
|
|
for (U32 face = 0; face < faceCount; ++face)
|
|
|
|
|
{
|
|
|
|
|
_textureUpload(width, height, pDL->getBytesPerPixel(), texture, pDL->getFormat(), pDL->getBits(mip,face), mip, face);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!ImageUtil::isCompressedFormat(pDL->getFormat()))
|
|
|
|
|
glGenerateMipmap(texture->getBinding());
|
|
|
|
|
|
|
|
|
|
glBindTexture(target, 0);
|
2012-09-19 15:15:01 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
|
|
|
|
|
{
|
2025-12-22 10:29:01 +00:00
|
|
|
PROFILE_SCOPE(GFXGLTextureManager_loadTextureDDS);
|
2012-09-19 15:15:01 +00:00
|
|
|
GFXGLTextureObject* texture = static_cast<GFXGLTextureObject*>(aTexture);
|
2025-12-22 10:29:01 +00:00
|
|
|
|
|
|
|
|
const GLenum target = texture->getBinding();
|
|
|
|
|
|
|
|
|
|
const bool isCube = texture->getBinding() == GL_TEXTURE_CUBE_MAP && dds->isCubemap();
|
|
|
|
|
const bool isCompressed = ImageUtil::isCompressedFormat(texture->mFormat);
|
|
|
|
|
|
|
|
|
|
AssertFatal(target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP,
|
|
|
|
|
"GFXGLTextureManager::_loadTexture(DDS) - This method can only be used with 1D/2D and CubeMap textures");
|
|
|
|
|
|
|
|
|
|
if (texture->getBinding() == GL_TEXTURE_3D)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
PRESERVE_TEXTURE(target);
|
|
|
|
|
glBindTexture(target, texture->getHandle());
|
|
|
|
|
|
|
|
|
|
const U32 numFaces = isCube ? 6 : 1;
|
|
|
|
|
const U32 numMips = dds->mSurfaces[0]->mMips.size();
|
2017-06-23 16:36:20 +00:00
|
|
|
const GFXFormat fmt = texture->mFormat;
|
2016-05-07 01:24:52 +00:00
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
for (U32 face = 0; face < numFaces; ++face)
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
2025-12-22 10:29:01 +00:00
|
|
|
// Skip empty surfaces
|
|
|
|
|
if (!dds->mSurfaces[face])
|
|
|
|
|
continue;
|
2016-05-07 03:44:41 +00:00
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
for (U32 mip = 0; mip < numMips; ++mip)
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
2025-12-22 10:29:01 +00:00
|
|
|
const U32 mipWidth = getMax(1u, dds->getWidth(mip));
|
|
|
|
|
const U32 mipHeight = getMax(1u, dds->getHeight(mip));
|
|
|
|
|
|
|
|
|
|
GLenum uploadTarget = target;
|
|
|
|
|
if (isCube)
|
|
|
|
|
uploadTarget = GFXGLFaceType[face];
|
|
|
|
|
|
|
|
|
|
if (isCompressed)
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
2025-12-22 10:29:01 +00:00
|
|
|
// Handle NPOT workaround
|
|
|
|
|
if ((!isPow2(mipWidth) || !isPow2(mipHeight)) && GFX->getCardProfiler()->queryProfile("GL::Workaround::noCompressedNPoTTextures"))
|
|
|
|
|
{
|
|
|
|
|
U8* uncompressedTex = new U8[mipWidth * mipHeight * 4];
|
|
|
|
|
ImageUtil::decompress(dds->mSurfaces[face]->mMips[mip], uncompressedTex, mipWidth, mipHeight, fmt);
|
|
|
|
|
glTexSubImage2D(uploadTarget,
|
|
|
|
|
mip, 0, 0, mipWidth, mipHeight, GL_RGBA, GL_UNSIGNED_BYTE, uncompressedTex
|
|
|
|
|
);
|
|
|
|
|
delete[] uncompressedTex;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
glCompressedTexImage2D(uploadTarget,
|
|
|
|
|
mip, GFXGLTextureInternalFormat[fmt], mipWidth, mipHeight, 0,
|
|
|
|
|
dds->getSurfaceSize(mip), dds->mSurfaces[face]->mMips[mip]
|
|
|
|
|
);
|
|
|
|
|
}
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
|
|
|
|
else
|
2025-12-22 10:29:01 +00:00
|
|
|
{
|
|
|
|
|
Swizzle<U8, 4>* pSwizzle = nullptr;
|
|
|
|
|
if (fmt == GFXFormatR8G8B8A8 || fmt == GFXFormatR8G8B8X8 || fmt == GFXFormatR8G8B8A8_SRGB ||
|
|
|
|
|
fmt == GFXFormatR8G8B8A8_LINEAR_FORCE || fmt == GFXFormatB8G8R8A8)
|
|
|
|
|
pSwizzle = &Swizzles::bgra;
|
|
|
|
|
|
|
|
|
|
_textureUpload(
|
|
|
|
|
mipWidth, mipHeight, dds->mBytesPerPixel, texture, fmt,
|
|
|
|
|
dds->mSurfaces[face]->mMips[mip], mip, face, pSwizzle);
|
|
|
|
|
}
|
2017-06-23 16:36:20 +00:00
|
|
|
|
|
|
|
|
}
|
2012-09-19 15:15:01 +00:00
|
|
|
}
|
2014-11-08 16:41:17 +00:00
|
|
|
|
2025-12-22 10:29:01 +00:00
|
|
|
if (numMips != 1 && !isCompressed)
|
2014-11-08 16:41:17 +00:00
|
|
|
glGenerateMipmap(texture->getBinding());
|
2025-12-22 10:29:01 +00:00
|
|
|
|
|
|
|
|
glBindTexture(target, 0);
|
2012-09-19 15:15:01 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, void *raw)
|
|
|
|
|
{
|
2016-05-07 03:44:41 +00:00
|
|
|
PROFILE_SCOPE(GFXGLTextureManager_loadTextureRaw);
|
2012-09-19 15:15:01 +00:00
|
|
|
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;
|
|
|
|
|
}
|