Torque3D/Engine/source/gfx/gl/gfxGLTextureTarget.cpp
marauder2k7 3aef90a6bc 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
2025-12-22 10:29:01 +00:00

406 lines
13 KiB
C++

//-----------------------------------------------------------------------------
// 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 "console/console.h"
#include "gfx/gl/gfxGLDevice.h"
#include "gfx/gl/gfxGLTextureTarget.h"
#include "gfx/gl/gfxGLTextureObject.h"
#include "gfx/gl/gfxGLCubemap.h"
#include "gfx/gfxTextureManager.h"
#include "gfx/gl/gfxGLUtils.h"
/// Internal struct used to track texture information for FBO attachments
/// This serves as an abstract base so we can deal with cubemaps and standard
/// 2D/Rect textures through the same interface
class _GFXGLTargetDesc
{
public:
_GFXGLTargetDesc(U32 _mipLevel, U32 _zOffset) :
mipLevel(_mipLevel), zOffset(_zOffset)
{
}
virtual ~_GFXGLTargetDesc() {}
virtual U32 getHandle() = 0;
virtual U32 getWidth() = 0;
virtual U32 getHeight() = 0;
virtual U32 getDepth() = 0;
virtual bool hasMips() = 0;
virtual GLenum getBinding() = 0;
virtual GFXFormat getFormat() = 0;
virtual bool isCompatible(const GFXGLTextureObject* tex) = 0;
U32 getMipLevel() { return mipLevel; }
U32 getZOffset() { return zOffset; }
private:
U32 mipLevel;
U32 zOffset;
};
/// Internal struct used to track 2D/Rect texture information for FBO attachment
class _GFXGLTextureTargetDesc : public _GFXGLTargetDesc
{
public:
_GFXGLTextureTargetDesc(GFXGLTextureObject* tex, U32 _mipLevel, U32 _zOffset, U32 _face = 0, bool isCube = false)
: _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex), mFace(_face), mIsCube(isCube)
{
}
virtual ~_GFXGLTextureTargetDesc() {}
U32 getHandle() override { return mTex->getHandle(); }
U32 getWidth() override { return mTex->getWidth(); }
U32 getHeight() override { return mTex->getHeight(); }
U32 getDepth() override { return mTex->getDepth(); }
U32 getFace() { return mFace; }
bool hasMips() override { return mTex->mMipLevels != 1; }
GLenum getBinding() override { return mIsCube ? GFXGLFaceType[mFace] : mTex->getBinding(); }
GFXFormat getFormat() override { return mTex->getFormat(); }
bool isCompatible(const GFXGLTextureObject* tex) override
{
return mTex->getFormat() == tex->getFormat()
&& mTex->getWidth() == tex->getWidth()
&& mTex->getHeight() == tex->getHeight();
}
GFXGLTextureObject* getTextureObject() const { return mTex; }
private:
StrongRefPtr<GFXGLTextureObject> mTex;
U32 mFace;
bool mIsCube;
};
// Internal implementations
class _GFXGLTextureTargetImpl // TODO OPENGL remove and implement on GFXGLTextureTarget
{
public:
GFXGLTextureTarget* mTarget;
virtual ~_GFXGLTextureTargetImpl() {}
virtual void applyState() = 0;
virtual void makeActive() = 0;
virtual void finish() = 0;
};
// Use FBOs to render to texture. This is the preferred implementation and is almost always used.
class _GFXGLTextureTargetFBOImpl : public _GFXGLTextureTargetImpl
{
public:
GLuint mFramebuffer;
bool mGenMips;
_GFXGLTextureTargetFBOImpl(GFXGLTextureTarget* target);
virtual ~_GFXGLTextureTargetFBOImpl();
void applyState() override;
void makeActive() override;
void finish() override;
};
_GFXGLTextureTargetFBOImpl::_GFXGLTextureTargetFBOImpl(GFXGLTextureTarget* target)
{
mGenMips = target->isGenMipsEnabled();
mTarget = target;
glGenFramebuffers(1, &mFramebuffer);
}
_GFXGLTextureTargetFBOImpl::~_GFXGLTextureTargetFBOImpl()
{
glDeleteFramebuffers(1, &mFramebuffer);
}
void _GFXGLTextureTargetFBOImpl::applyState()
{
// REMINDER: When we implement MRT support, check against GFXGLDevice::getNumRenderTargets()
PRESERVE_FRAMEBUFFER();
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glEnable(GL_FRAMEBUFFER_SRGB);
bool drawbufs[16];
int bufsize = 0;
for (int i = 0; i < 16; i++)
drawbufs[i] = false;
bool hasColor = false;
for (int i = 0; i < GFXGL->getNumRenderTargets(); ++i)
{
_GFXGLTargetDesc* color = mTarget->getTargetDesc(static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0 + i));
if (color)
{
hasColor = true;
const GLenum binding = color->getBinding();
if (binding == GL_TEXTURE_2D || (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding(), color->getHandle(), color->getMipLevel());
else if (binding == GL_TEXTURE_1D)
glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding(), color->getHandle(), color->getMipLevel());
else if (binding == GL_TEXTURE_3D)
glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding(), color->getHandle(), color->getMipLevel(), color->getZOffset());
else
Con::errorf("_GFXGLTextureTargetFBOImpl::applyState - Bad binding");
}
else
{
// Clears the texture (note that the binding is irrelevent)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, 0, 0);
}
}
_GFXGLTargetDesc* depthStecil = mTarget->getTargetDesc(GFXTextureTarget::DepthStencil);
if (depthStecil)
{
// Certain drivers have issues with depth only FBOs. That and the next two asserts assume we have a color target.
AssertFatal(hasColor, "GFXGLTextureTarget::applyState() - Cannot set DepthStencil target without Color0 target!");
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, depthStecil->getBinding(), depthStecil->getHandle(), depthStecil->getMipLevel());
}
else
{
// Clears the texture (note that the binding is irrelevent)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
}
GLenum* buf = new GLenum[bufsize];
int count = 0;
for (int i = 0; i < bufsize; i++)
{
if (drawbufs[i])
{
buf[count] = GL_COLOR_ATTACHMENT0 + i;
count++;
}
}
glDrawBuffers(bufsize, buf);
delete[] buf;
CHECK_FRAMEBUFFER_STATUS();
}
void _GFXGLTextureTargetFBOImpl::makeActive()
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
GFXGL->getOpenglCache()->setCacheBinded(GL_FRAMEBUFFER, mFramebuffer);
int i = 0;
GLenum draws[16];
for (i = 0; i < GFXGL->getNumRenderTargets(); ++i)
{
_GFXGLTargetDesc* color = mTarget->getTargetDesc(static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0 + i));
if (color)
draws[i] = GL_COLOR_ATTACHMENT0 + i;
else
break;
}
glDrawBuffers(i, draws);
}
void _GFXGLTextureTargetFBOImpl::finish()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
GFXGL->getOpenglCache()->setCacheBinded(GL_FRAMEBUFFER, 0);
if (!mGenMips)
return;
for (int i = 0; i < GFXGL->getNumRenderTargets(); ++i)
{
_GFXGLTargetDesc* color = mTarget->getTargetDesc(static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0 + i));
if (!color || !(color->hasMips()))
continue;
// Generate mips if necessary
// Assumes a 2D texture.
GLenum binding = color->getBinding();
binding = (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ? GL_TEXTURE_CUBE_MAP : binding;
PRESERVE_TEXTURE(binding);
glBindTexture(binding, color->getHandle());
glGenerateMipmap(binding);
}
}
// Actual GFXGLTextureTarget interface
GFXGLTextureTarget::GFXGLTextureTarget(bool genMips) : mCopyFboSrc(0), mCopyFboDst(0)
{
mGenMips = genMips;
for (U32 i = 0; i < MaxRenderSlotId; i++)
mTargets[i] = NULL;
GFXTextureManager::addEventDelegate(this, &GFXGLTextureTarget::_onTextureEvent);
_impl = new _GFXGLTextureTargetFBOImpl(this);
glGenFramebuffers(1, &mCopyFboSrc);
glGenFramebuffers(1, &mCopyFboDst);
}
GFXGLTextureTarget::~GFXGLTextureTarget()
{
GFXTextureManager::removeEventDelegate(this, &GFXGLTextureTarget::_onTextureEvent);
glDeleteFramebuffers(1, &mCopyFboSrc);
glDeleteFramebuffers(1, &mCopyFboDst);
}
const Point2I GFXGLTextureTarget::getSize()
{
if (mTargets[Color0].isValid())
return Point2I(mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight());
return Point2I(0, 0);
}
GFXFormat GFXGLTextureTarget::getFormat()
{
if (mTargets[Color0].isValid())
return mTargets[Color0]->getFormat();
return GFXFormatR8G8B8A8;
}
void GFXGLTextureTarget::attachTexture(RenderSlot slot, GFXTextureObject* tex, U32 mipLevel/*=0*/, U32 zOffset /*= 0*/, U32 face /*= 0*/)
{
if (tex == GFXTextureTarget::sDefaultDepthStencil)
tex = GFXGL->getDefaultDepthTex();
// are we readding the same thing, face and all?
_GFXGLTextureTargetDesc* mTex = static_cast<_GFXGLTextureTargetDesc*>(mTargets[slot].ptr());
if ((!tex && !mTex) || (mTex && mTex->getTextureObject() == tex && mTex->getFace() == face))
return;
// Triggers an update when we next render
invalidateState();
// We stash the texture and info into an internal struct.
GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(tex);
if (tex && tex != GFXTextureTarget::sDefaultDepthStencil)
{
mTargets[slot] = new _GFXGLTextureTargetDesc(glTexture, mipLevel, zOffset, face, glTexture->isCubeMap());
}
else
mTargets[slot] = NULL;
}
void GFXGLTextureTarget::attachTexture(RenderSlot slot, GFXCubemap* tex, U32 face, U32 mipLevel/*=0*/)
{
}
void GFXGLTextureTarget::clearAttachments()
{
deactivate();
for (S32 i = 1; i < MaxRenderSlotId; i++)
attachTexture((RenderSlot)i, NULL);
}
void GFXGLTextureTarget::zombify()
{
invalidateState();
// Will be recreated in applyState
_impl = NULL;
}
void GFXGLTextureTarget::resurrect()
{
// Dealt with when the target is next bound
}
void GFXGLTextureTarget::makeActive()
{
_impl->makeActive();
}
void GFXGLTextureTarget::deactivate()
{
_impl->finish();
}
void GFXGLTextureTarget::applyState()
{
if (!isPendingState())
return;
// So we don't do this over and over again
stateApplied();
if (_impl.isNull())
_impl = new _GFXGLTextureTargetFBOImpl(this);
_impl->applyState();
}
_GFXGLTargetDesc* GFXGLTextureTarget::getTargetDesc(RenderSlot slot) const
{
// This can only be called by our implementations, and then will not actually store the pointer so this is (almost) safe
return mTargets[slot].ptr();
}
void GFXGLTextureTarget::_onTextureEvent(GFXTexCallbackCode code)
{
invalidateState();
}
const String GFXGLTextureTarget::describeSelf() const
{
String ret = String::ToString(" Color0 Attachment: %i", mTargets[Color0].isValid() ? mTargets[Color0]->getHandle() : 0);
ret += String::ToString(" Depth Attachment: %i", mTargets[DepthStencil].isValid() ? mTargets[DepthStencil]->getHandle() : 0);
return ret;
}
void GFXGLTextureTarget::resolve()
{
}
void GFXGLTextureTarget::resolveTo(GFXTextureObject* obj)
{
AssertFatal(dynamic_cast<GFXGLTextureObject*>(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject");
GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(obj);
if (GFXGL->mCapabilities.copyImage && mTargets[Color0]->isCompatible(glTexture))
{
GLenum binding = mTargets[Color0]->getBinding();
binding = (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ? GL_TEXTURE_CUBE_MAP : binding;
U32 srcStartDepth = binding == GL_TEXTURE_CUBE_MAP ? mTargets[Color0]->getBinding() - GL_TEXTURE_CUBE_MAP_POSITIVE_X : 0;
glCopyImageSubData(
mTargets[Color0]->getHandle(), binding, 0, 0, 0, srcStartDepth,
glTexture->getHandle(), glTexture->getBinding(), 0, 0, 0, 0,
mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight(), 1);
return;
}
PRESERVE_FRAMEBUFFER();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mCopyFboDst);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, glTexture->getBinding(), glTexture->getHandle(), 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, mCopyFboSrc);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTargets[Color0]->getBinding(), mTargets[Color0]->getHandle(), 0);
glBlitFramebuffer(0, 0, mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight(),
0, 0, glTexture->getWidth(), glTexture->getHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
}