123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- //-----------------------------------------------------------------------------
- // 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)
- : _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex)
- {
- }
-
- virtual ~_GFXGLTextureTargetDesc() {}
-
- virtual U32 getHandle() { return mTex->getHandle(); }
- virtual U32 getWidth() { return mTex->getWidth(); }
- virtual U32 getHeight() { return mTex->getHeight(); }
- virtual U32 getDepth() { return mTex->getDepth(); }
- virtual bool hasMips() { return mTex->mMipLevels != 1; }
- virtual GLenum getBinding() { return mTex->getBinding(); }
- virtual GFXFormat getFormat() { return mTex->getFormat(); }
- virtual bool isCompatible(const GFXGLTextureObject* tex)
- {
- return mTex->getFormat() == tex->getFormat()
- && mTex->getWidth() == tex->getWidth()
- && mTex->getHeight() == tex->getHeight();
- }
- GFXGLTextureObject* getTextureObject() const {return mTex; }
-
- private:
- StrongRefPtr<GFXGLTextureObject> mTex;
- };
- /// Internal struct used to track Cubemap texture information for FBO attachment
- class _GFXGLCubemapTargetDesc : public _GFXGLTargetDesc
- {
- public:
- _GFXGLCubemapTargetDesc(GFXGLCubemap* tex, U32 _face, U32 _mipLevel, U32 _zOffset)
- : _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex), mFace(_face)
- {
- }
-
- virtual ~_GFXGLCubemapTargetDesc() {}
-
- virtual U32 getHandle() { return mTex->getHandle(); }
- virtual U32 getWidth() { return mTex->getWidth(); }
- virtual U32 getHeight() { return mTex->getHeight(); }
- virtual U32 getDepth() { return 0; }
- virtual bool hasMips() { return mTex->getMipMapLevels() != 1; }
- virtual GLenum getBinding() { return GFXGLCubemap::getEnumForFaceNumber(mFace); }
- virtual GFXFormat getFormat() { return mTex->getFormat(); }
- virtual bool isCompatible(const GFXGLTextureObject* tex)
- {
- return mTex->getFormat() == tex->getFormat()
- && mTex->getWidth() == tex->getWidth()
- && mTex->getHeight() == tex->getHeight();
- }
-
- private:
- StrongRefPtr<GFXGLCubemap> mTex;
- U32 mFace;
- };
- // 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();
-
- virtual void applyState();
- virtual void makeActive();
- virtual void finish();
- };
- _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*/ )
- {
- if( tex == GFXTextureTarget::sDefaultDepthStencil )
- tex = GFXGL->getDefaultDepthTex();
- _GFXGLTextureTargetDesc* mTex = static_cast<_GFXGLTextureTargetDesc*>(mTargets[slot].ptr());
- if( (!tex && !mTex) || (mTex && mTex->getTextureObject() == tex) )
- 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);
- else
- mTargets[slot] = NULL;
- }
- void GFXGLTextureTarget::attachTexture( RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel/*=0*/ )
- {
- // No depth cubemaps, sorry
- AssertFatal(slot != DepthStencil, "GFXGLTextureTarget::attachTexture (cube) - Cube depth textures not supported!");
- if(slot == DepthStencil)
- return;
-
- // Triggers an update when we next render
- invalidateState();
-
- // We stash the texture and info into an internal struct.
- GFXGLCubemap* glTexture = static_cast<GFXGLCubemap*>(tex);
- if(tex)
- mTargets[slot] = new _GFXGLCubemapTargetDesc(glTexture, face, mipLevel, 0);
- else
- mTargets[slot] = NULL;
- }
- 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);
- }
|