Update GFXTextureManager and GBitmap

GBitmap Changes:
Added all other formats to gbitmap that we support
gbitmap now supports cubemaps
added converters for all these other formats
added stb_image_resize for extrudemips so we can extrude mipmaps for all other formats

GFXTextureManager
Can now directly make cubemaps and texture arrays based on the GFXTextureProfile
API implementations for all functions that cubemaps and arrays needed
This commit is contained in:
marauder2k7 2025-12-22 10:29:01 +00:00
parent 975fc924cc
commit 3aef90a6bc
66 changed files with 4235 additions and 2590 deletions

View file

@ -64,7 +64,7 @@ GFXGLTextureObject::~GFXGLTextureObject()
kill();
}
GFXLockedRect* GFXGLTextureObject::lock(U32 mipLevel, RectI *inRect)
GFXLockedRect* GFXGLTextureObject::lock(U32 mipLevel /*= 0*/, RectI* inRect /*= NULL*/, U32 faceIndex /*= 0*/)
{
//AssertFatal(mBinding != GL_TEXTURE_3D, "GFXGLTextureObject::lock - We don't support locking 3D textures yet");
U32 width = mTextureSize.x >> mipLevel;
@ -100,7 +100,7 @@ GFXLockedRect* GFXGLTextureObject::lock(U32 mipLevel, RectI *inRect)
return &mLockedRect;
}
void GFXGLTextureObject::unlock(U32 mipLevel)
void GFXGLTextureObject::unlock(U32 mipLevel /*= 0*/, U32 faceIndex /*= 0*/)
{
if(!mLockedRect.bits)
return;
@ -175,38 +175,231 @@ bool GFXGLTextureObject::copyToBmp(GBitmap * bmp)
FrameAllocatorMarker mem;
const bool isCubemap = (mBinding == GL_TEXTURE_CUBE_MAP);
const U32 numFaces = isCubemap ? 6 : 1;
U32 mipLevels = getMipLevels();
for (U32 mip = 0; mip < mipLevels; mip++)
for (U32 mip = 0; mip < getMipLevels(); mip++)
{
U32 srcPixelCount = bmp->getSurfaceSize(mip)/ srcBytesPerPixel;
U32 width = getWidth() >> mip;
U32 height = getHeight() >> mip;
if (width == 0) width = 1;
if (height == 0) height = 1;
U8* dest = bmp->getWritableBits(mip);
U8* orig = (U8*)mem.alloc(srcPixelCount * srcBytesPerPixel);
// Check if multisampled
GLint samples = 0;
GLenum target = mBinding;
if (mBinding == GL_TEXTURE_CUBE_MAP)
target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
glGetTexImage(mBinding, mip, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], orig);
if (mFormat == GFXFormatR16G16B16A16F)
glGetTexLevelParameteriv(target, mip, GL_TEXTURE_SAMPLES, &samples);
if (samples > 0)
{
dMemcpy(dest, orig, srcPixelCount * srcBytesPerPixel);
Con::warnf("GFXGLTextureObject::copyToBmp - Texture is multisampled (%d samples) at mip %d; resolve first.", samples, mip);
return false;
}
else
{
for (int i = 0; i < srcPixelCount; ++i)
{
dest[0] = orig[0];
dest[1] = orig[1];
dest[2] = orig[2];
if (dstBytesPerPixel == 4)
dest[3] = orig[3];
orig += srcBytesPerPixel;
dest += dstBytesPerPixel;
for (U32 face = 0; face < numFaces; face++)
{
GLenum faceTarget = isCubemap ? GFXGLFaceType[face] : mBinding;
U32 pixelCount = width * height;
U8* srcPixels = (U8*)mem.alloc(pixelCount * srcBytesPerPixel);
U8* dest = bmp->getWritableBits(mip, face);
if (!dest)
{
Con::errorf("GFXGLTextureObject::copyToBmp - No destination bits for mip=%u face=%u", mip, face);
continue;
}
// Read texture data
glGetTexImage(faceTarget, mip, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], srcPixels);
if (mFormat == GFXFormatR16G16B16A16F)
{
dMemcpy(dest, srcPixels, pixelCount * srcBytesPerPixel);
}
else
{
// Simple 8-bit per channel copy (RGBA)
U8* src = srcPixels;
for (U32 i = 0; i < pixelCount; ++i)
{
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
if (dstBytesPerPixel == 4)
dest[3] = src[3];
src += srcBytesPerPixel;
dest += dstBytesPerPixel;
}
}
} // face
} // mip
glBindTexture(mBinding, 0);
return true;
}
void GFXGLTextureObject::updateTextureSlot(const GFXTexHandle& texHandle, const U32 slot, const S32 face)
{
if (!texHandle.isValid())
return;
GFXGLTextureObject* srcTex = static_cast<GFXGLTextureObject*>(texHandle.getPointer());
if (!srcTex || srcTex->getHandle() == 0)
return;
const GLenum dstTarget = mBinding; // destination binding (this)
const GLenum srcTarget = srcTex->getBinding(); // source binding
const bool srcIsCube = (srcTarget == GL_TEXTURE_CUBE_MAP || srcTarget == GL_TEXTURE_CUBE_MAP_ARRAY);
// Determine list of faces to copy from source
U32 firstFace = 0;
U32 faceCount = 1;
if (face >= 0)
{
firstFace = (U32)face;
faceCount = 1;
}
else if (srcIsCube)
{
firstFace = 0;
faceCount = 6;
}
else
{
firstFace = 0;
faceCount = 1;
}
// Ensure textures are valid
if (!glIsTexture(mHandle) || !glIsTexture(srcTex->getHandle()))
{
Con::errorf("updateTextureSlot: invalid GL texture handle src=%u dst=%u", srcTex->getHandle(), mHandle);
return;
}
// If copyImage supported, prefer that. We'll copy face-by-face (one-layer depth = 1)
if (GFXGL->mCapabilities.copyImage)
{
for (U32 mip = 0; mip < getMipLevels(); ++mip)
{
const GLsizei mipW = getMax(1u, srcTex->getWidth() >> mip);
const GLsizei mipH = getMax(1u, srcTex->getHeight() >> mip);
for (U32 f = firstFace; f < firstFace + faceCount; ++f)
{
// Compute source z offset (for cube arrays it's layer index; for cubemap it's face index)
GLint srcZ = 0;
if (srcTarget == GL_TEXTURE_CUBE_MAP_ARRAY)
{
srcZ = f;
}
else if (srcTarget == GL_TEXTURE_CUBE_MAP)
{
srcZ = f;
}
else
{
srcZ = 0; // 2D source
}
// Compute destination layer (z offset) depending on destination type
GLint dstZ = 0;
if (dstTarget == GL_TEXTURE_CUBE_MAP_ARRAY)
{
// each slot is a whole cubemap => slot * 6 + faceIndex
dstZ = (GLint)(slot * 6 + f);
}
else if (dstTarget == GL_TEXTURE_2D_ARRAY)
{
dstZ = (GLint)slot; // each slot is a single layer
}
else if (dstTarget == GL_TEXTURE_CUBE_MAP)
{
dstZ = (GLint)f;
}
else
{
dstZ = 0; // 2D texture target
}
// Copy single layer/face at this mip
glCopyImageSubData(
srcTex->getHandle(), srcTarget, mip, 0, 0, srcZ,
mHandle, dstTarget, mip, 0, 0, dstZ,
mipW, mipH, 1
);
GLenum err = glGetError();
if (err != GL_NO_ERROR)
Con::errorf("glCopyImageSubData failed with 0x%X (mip=%u face=%u)", err, mip, f);
}
}
}
glBindTexture(mBinding, 0);
return true;
return;
}
// Fallback: CPU-side copy using glGetTexImage + glTexSubImage
for (U32 mip = 0; mip < getMipLevels() && mip < srcTex->getMipLevels(); ++mip)
{
const GLsizei mipW = getMax(1u, srcTex->getWidth() >> mip);
const GLsizei mipH = getMax(1u, srcTex->getHeight() >> mip);
const U32 pixelSize = GFXFormat_getByteSize(mFormat); // assuming same fmt for src/dst
const U32 dataSize = mipW * mipH * pixelSize;
FrameAllocatorMarker mem;
U8* buffer = (U8*)mem.alloc(dataSize);
glBindTexture(srcTarget, srcTex->getHandle());
glBindTexture(dstTarget, mHandle);
for (U32 f = firstFace; f < firstFace + faceCount; ++f)
{
GLenum srcFaceTarget = srcTarget;
if (srcTarget == GL_TEXTURE_CUBE_MAP)
srcFaceTarget = GFXGLFaceType[f];
// read pixels from source
glGetTexImage(srcFaceTarget, mip, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
GLint dstLayer = 0;
if (dstTarget == GL_TEXTURE_CUBE_MAP_ARRAY)
dstLayer = (GLint)(slot * 6 + f);
else if (dstTarget == GL_TEXTURE_2D_ARRAY)
dstLayer = (GLint)slot;
else if (dstTarget == GL_TEXTURE_CUBE_MAP)
dstLayer = (GLint)f;
else
dstLayer = 0;
if (dstTarget == GL_TEXTURE_CUBE_MAP)
{
GLenum dstFaceTarget = GFXGLFaceType[f];
glTexSubImage2D(dstFaceTarget, mip, 0, 0, mipW, mipH,
GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
}
else if (dstTarget == GL_TEXTURE_2D)
{
glTexSubImage2D(GL_TEXTURE_2D, mip, 0, 0, mipW, mipH,
GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
}
else if (dstTarget == GL_TEXTURE_2D_ARRAY || dstTarget == GL_TEXTURE_CUBE_MAP_ARRAY)
{
glTexSubImage3D(dstTarget, mip, 0, 0, dstLayer, mipW, mipH, 1,
GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
}
}
glBindTexture(dstTarget, 0);
glBindTexture(srcTarget, 0);
}
}
void GFXGLTextureObject::copyTo(GFXTextureObject* dstTex)
{
}
void GFXGLTextureObject::initSamplerState(const GFXSamplerStateDesc &ssd)