gfxGLTextureTarget.cpp
Engine/source/gfx/gl/gfxGLTextureTarget.cpp
Classes:
class
Internal struct used to track Cubemap texture information for FBO attachment.
class
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
Internal struct used to track 2D/Rect texture information for FBO attachment.
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "console/console.h" 25#include "gfx/gl/gfxGLDevice.h" 26#include "gfx/gl/gfxGLTextureTarget.h" 27#include "gfx/gl/gfxGLTextureObject.h" 28#include "gfx/gl/gfxGLCubemap.h" 29#include "gfx/gfxTextureManager.h" 30#include "gfx/gl/gfxGLUtils.h" 31 32/// Internal struct used to track texture information for FBO attachments 33/// This serves as an abstract base so we can deal with cubemaps and standard 34/// 2D/Rect textures through the same interface 35class _GFXGLTargetDesc 36{ 37public: 38 _GFXGLTargetDesc(U32 _mipLevel, U32 _zOffset) : 39 mipLevel(_mipLevel), zOffset(_zOffset) 40 { 41 } 42 43 virtual ~_GFXGLTargetDesc() {} 44 45 virtual U32 getHandle() = 0; 46 virtual U32 getWidth() = 0; 47 virtual U32 getHeight() = 0; 48 virtual U32 getDepth() = 0; 49 virtual bool hasMips() = 0; 50 virtual GLenum getBinding() = 0; 51 virtual GFXFormat getFormat() = 0; 52 virtual bool isCompatible(const GFXGLTextureObject* tex) = 0; 53 54 U32 getMipLevel() { return mipLevel; } 55 U32 getZOffset() { return zOffset; } 56 57private: 58 U32 mipLevel; 59 U32 zOffset; 60}; 61 62/// Internal struct used to track 2D/Rect texture information for FBO attachment 63class _GFXGLTextureTargetDesc : public _GFXGLTargetDesc 64{ 65public: 66 _GFXGLTextureTargetDesc(GFXGLTextureObject* tex, U32 _mipLevel, U32 _zOffset) 67 : _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex) 68 { 69 } 70 71 virtual ~_GFXGLTextureTargetDesc() {} 72 73 virtual U32 getHandle() { return mTex->getHandle(); } 74 virtual U32 getWidth() { return mTex->getWidth(); } 75 virtual U32 getHeight() { return mTex->getHeight(); } 76 virtual U32 getDepth() { return mTex->getDepth(); } 77 virtual bool hasMips() { return mTex->mMipLevels != 1; } 78 virtual GLenum getBinding() { return mTex->getBinding(); } 79 virtual GFXFormat getFormat() { return mTex->getFormat(); } 80 virtual bool isCompatible(const GFXGLTextureObject* tex) 81 { 82 return mTex->getFormat() == tex->getFormat() 83 && mTex->getWidth() == tex->getWidth() 84 && mTex->getHeight() == tex->getHeight(); 85 } 86 GFXGLTextureObject* getTextureObject() const {return mTex; } 87 88private: 89 StrongRefPtr<GFXGLTextureObject> mTex; 90}; 91 92/// Internal struct used to track Cubemap texture information for FBO attachment 93class _GFXGLCubemapTargetDesc : public _GFXGLTargetDesc 94{ 95public: 96 _GFXGLCubemapTargetDesc(GFXGLCubemap* tex, U32 _face, U32 _mipLevel, U32 _zOffset) 97 : _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex), mFace(_face) 98 { 99 } 100 101 virtual ~_GFXGLCubemapTargetDesc() {} 102 103 virtual U32 getHandle() { return mTex->getHandle(); } 104 virtual U32 getWidth() { return mTex->getWidth(); } 105 virtual U32 getHeight() { return mTex->getHeight(); } 106 virtual U32 getDepth() { return 0; } 107 virtual bool hasMips() { return mTex->getMipMapLevels() != 1; } 108 virtual GLenum getBinding() { return GFXGLCubemap::getEnumForFaceNumber(mFace); } 109 virtual GFXFormat getFormat() { return mTex->getFormat(); } 110 virtual bool isCompatible(const GFXGLTextureObject* tex) 111 { 112 return mTex->getFormat() == tex->getFormat() 113 && mTex->getWidth() == tex->getWidth() 114 && mTex->getHeight() == tex->getHeight(); 115 } 116 117private: 118 StrongRefPtr<GFXGLCubemap> mTex; 119 U32 mFace; 120}; 121 122// Internal implementations 123class _GFXGLTextureTargetImpl // TODO OPENGL remove and implement on GFXGLTextureTarget 124{ 125public: 126 GFXGLTextureTarget* mTarget; 127 128 virtual ~_GFXGLTextureTargetImpl() {} 129 130 virtual void applyState() = 0; 131 virtual void makeActive() = 0; 132 virtual void finish() = 0; 133}; 134 135// Use FBOs to render to texture. This is the preferred implementation and is almost always used. 136class _GFXGLTextureTargetFBOImpl : public _GFXGLTextureTargetImpl 137{ 138public: 139 GLuint mFramebuffer; 140 bool mGenMips; 141 142 _GFXGLTextureTargetFBOImpl(GFXGLTextureTarget* target); 143 virtual ~_GFXGLTextureTargetFBOImpl(); 144 145 virtual void applyState(); 146 virtual void makeActive(); 147 virtual void finish(); 148}; 149 150_GFXGLTextureTargetFBOImpl::_GFXGLTextureTargetFBOImpl(GFXGLTextureTarget* target) 151{ 152 mGenMips = target->isGenMipsEnabled(); 153 mTarget = target; 154 glGenFramebuffers(1, &mFramebuffer); 155} 156 157_GFXGLTextureTargetFBOImpl::~_GFXGLTextureTargetFBOImpl() 158{ 159 glDeleteFramebuffers(1, &mFramebuffer); 160} 161 162void _GFXGLTextureTargetFBOImpl::applyState() 163{ 164 // REMINDER: When we implement MRT support, check against GFXGLDevice::getNumRenderTargets() 165 166 PRESERVE_FRAMEBUFFER(); 167 glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); 168 glEnable(GL_FRAMEBUFFER_SRGB); 169 bool drawbufs[16]; 170 int bufsize = 0; 171 for (int i = 0; i < 16; i++) 172 drawbufs[i] = false; 173 bool hasColor = false; 174 for(int i = 0; i < GFXGL->getNumRenderTargets(); ++i) 175 { 176 _GFXGLTargetDesc* color = mTarget->getTargetDesc( static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0+i )); 177 if(color) 178 { 179 hasColor = true; 180 const GLenum binding = color->getBinding(); 181 if( binding == GL_TEXTURE_2D || (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ) 182 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding( ), color->getHandle( ), color->getMipLevel( ) ); 183 else if( binding == GL_TEXTURE_1D ) 184 glFramebufferTexture1D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding( ), color->getHandle( ), color->getMipLevel( ) ); 185 else if( binding == GL_TEXTURE_3D ) 186 glFramebufferTexture3D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding( ), color->getHandle( ), color->getMipLevel( ), color->getZOffset( ) ); 187 else 188 Con::errorf("_GFXGLTextureTargetFBOImpl::applyState - Bad binding"); 189 } 190 else 191 { 192 // Clears the texture (note that the binding is irrelevent) 193 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_TEXTURE_2D, 0, 0); 194 } 195 } 196 197 _GFXGLTargetDesc* depthStecil = mTarget->getTargetDesc(GFXTextureTarget::DepthStencil); 198 if(depthStecil) 199 { 200 // Certain drivers have issues with depth only FBOs. That and the next two asserts assume we have a color target. 201 AssertFatal(hasColor, "GFXGLTextureTarget::applyState() - Cannot set DepthStencil target without Color0 target!"); 202 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, depthStecil->getBinding(), depthStecil->getHandle(), depthStecil->getMipLevel()); 203 } 204 else 205 { 206 // Clears the texture (note that the binding is irrelevent) 207 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); 208 } 209 210 GLenum *buf = new GLenum[bufsize]; 211 int count = 0; 212 for (int i = 0; i < bufsize; i++) 213 { 214 if (drawbufs[i]) 215 { 216 buf[count] = GL_COLOR_ATTACHMENT0 + i; 217 count++; 218 } 219 } 220 221 glDrawBuffers(bufsize, buf); 222 223 delete[] buf; 224 CHECK_FRAMEBUFFER_STATUS(); 225} 226 227void _GFXGLTextureTargetFBOImpl::makeActive() 228{ 229 glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); 230 GFXGL->getOpenglCache()->setCacheBinded(GL_FRAMEBUFFER, mFramebuffer); 231 232 int i = 0; 233 GLenum draws[16]; 234 for( i = 0; i < GFXGL->getNumRenderTargets(); ++i) 235 { 236 _GFXGLTargetDesc* color = mTarget->getTargetDesc( static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0+i )); 237 if(color) 238 draws[i] = GL_COLOR_ATTACHMENT0 + i; 239 else 240 break; 241 } 242 243 glDrawBuffers( i, draws ); 244} 245 246void _GFXGLTextureTargetFBOImpl::finish() 247{ 248 glBindFramebuffer(GL_FRAMEBUFFER, 0); 249 GFXGL->getOpenglCache()->setCacheBinded(GL_FRAMEBUFFER, 0); 250 251 if (!mGenMips) 252 return; 253 254 for(int i = 0; i < GFXGL->getNumRenderTargets(); ++i) 255 { 256 _GFXGLTargetDesc* color = mTarget->getTargetDesc( static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0+i ) ); 257 if(!color || !(color->hasMips())) 258 continue; 259 260 // Generate mips if necessary 261 // Assumes a 2D texture. 262 GLenum binding = color->getBinding(); 263 binding = (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ? GL_TEXTURE_CUBE_MAP : binding; 264 265 PRESERVE_TEXTURE( binding ); 266 glBindTexture( binding, color->getHandle() ); 267 glGenerateMipmap( binding ); 268 } 269} 270 271// Actual GFXGLTextureTarget interface 272GFXGLTextureTarget::GFXGLTextureTarget(bool genMips) : mCopyFboSrc(0), mCopyFboDst(0) 273{ 274 mGenMips = genMips; 275 for(U32 i=0; i<MaxRenderSlotId; i++) 276 mTargets[i] = NULL; 277 278 GFXTextureManager::addEventDelegate( this, &GFXGLTextureTarget::_onTextureEvent ); 279 280 _impl = new _GFXGLTextureTargetFBOImpl(this); 281 282 glGenFramebuffers(1, &mCopyFboSrc); 283 glGenFramebuffers(1, &mCopyFboDst); 284} 285 286GFXGLTextureTarget::~GFXGLTextureTarget() 287{ 288 GFXTextureManager::removeEventDelegate(this, &GFXGLTextureTarget::_onTextureEvent); 289 290 glDeleteFramebuffers(1, &mCopyFboSrc); 291 glDeleteFramebuffers(1, &mCopyFboDst); 292} 293 294const Point2I GFXGLTextureTarget::getSize() 295{ 296 if(mTargets[Color0].isValid()) 297 return Point2I(mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight()); 298 299 return Point2I(0, 0); 300} 301 302GFXFormat GFXGLTextureTarget::getFormat() 303{ 304 if(mTargets[Color0].isValid()) 305 return mTargets[Color0]->getFormat(); 306 307 return GFXFormatR8G8B8A8; 308} 309 310void GFXGLTextureTarget::attachTexture( RenderSlot slot, GFXTextureObject *tex, U32 mipLevel/*=0*/, U32 zOffset /*= 0*/ ) 311{ 312 if( tex == GFXTextureTarget::sDefaultDepthStencil ) 313 tex = GFXGL->getDefaultDepthTex(); 314 315 _GFXGLTextureTargetDesc* mTex = static_cast<_GFXGLTextureTargetDesc*>(mTargets[slot].ptr()); 316 if( (!tex && !mTex) || (mTex && mTex->getTextureObject() == tex) ) 317 return; 318 319 // Triggers an update when we next render 320 invalidateState(); 321 322 // We stash the texture and info into an internal struct. 323 GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(tex); 324 if(tex && tex != GFXTextureTarget::sDefaultDepthStencil) 325 mTargets[slot] = new _GFXGLTextureTargetDesc(glTexture, mipLevel, zOffset); 326 else 327 mTargets[slot] = NULL; 328} 329 330void GFXGLTextureTarget::attachTexture( RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel/*=0*/ ) 331{ 332 // No depth cubemaps, sorry 333 AssertFatal(slot != DepthStencil, "GFXGLTextureTarget::attachTexture (cube) - Cube depth textures not supported!"); 334 if(slot == DepthStencil) 335 return; 336 337 // Triggers an update when we next render 338 invalidateState(); 339 340 // We stash the texture and info into an internal struct. 341 GFXGLCubemap* glTexture = static_cast<GFXGLCubemap*>(tex); 342 if(tex) 343 mTargets[slot] = new _GFXGLCubemapTargetDesc(glTexture, face, mipLevel, 0); 344 else 345 mTargets[slot] = NULL; 346} 347 348void GFXGLTextureTarget::clearAttachments() 349{ 350 deactivate(); 351 for(S32 i=1; i<MaxRenderSlotId; i++) 352 attachTexture((RenderSlot)i, NULL); 353} 354 355void GFXGLTextureTarget::zombify() 356{ 357 invalidateState(); 358 359 // Will be recreated in applyState 360 _impl = NULL; 361} 362 363void GFXGLTextureTarget::resurrect() 364{ 365 // Dealt with when the target is next bound 366} 367 368void GFXGLTextureTarget::makeActive() 369{ 370 _impl->makeActive(); 371} 372 373void GFXGLTextureTarget::deactivate() 374{ 375 _impl->finish(); 376} 377 378void GFXGLTextureTarget::applyState() 379{ 380 if(!isPendingState()) 381 return; 382 383 // So we don't do this over and over again 384 stateApplied(); 385 386 if(_impl.isNull()) 387 _impl = new _GFXGLTextureTargetFBOImpl(this); 388 389 _impl->applyState(); 390} 391 392_GFXGLTargetDesc* GFXGLTextureTarget::getTargetDesc(RenderSlot slot) const 393{ 394 // This can only be called by our implementations, and then will not actually store the pointer so this is (almost) safe 395 return mTargets[slot].ptr(); 396} 397 398void GFXGLTextureTarget::_onTextureEvent( GFXTexCallbackCode code ) 399{ 400 invalidateState(); 401} 402 403const String GFXGLTextureTarget::describeSelf() const 404{ 405 String ret = String::ToString(" Color0 Attachment: %i", mTargets[Color0].isValid() ? mTargets[Color0]->getHandle() : 0); 406 ret += String::ToString(" Depth Attachment: %i", mTargets[DepthStencil].isValid() ? mTargets[DepthStencil]->getHandle() : 0); 407 408 return ret; 409} 410 411void GFXGLTextureTarget::resolve() 412{ 413} 414 415void GFXGLTextureTarget::resolveTo(GFXTextureObject* obj) 416{ 417 AssertFatal(dynamic_cast<GFXGLTextureObject*>(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject"); 418 GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(obj); 419 420 if( GFXGL->mCapabilities.copyImage && mTargets[Color0]->isCompatible(glTexture) ) 421 { 422 GLenum binding = mTargets[Color0]->getBinding(); 423 binding = (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ? GL_TEXTURE_CUBE_MAP : binding; 424 U32 srcStartDepth = binding == GL_TEXTURE_CUBE_MAP ? mTargets[Color0]->getBinding() - GL_TEXTURE_CUBE_MAP_POSITIVE_X : 0; 425 glCopyImageSubData( 426 mTargets[Color0]->getHandle(), binding, 0, 0, 0, srcStartDepth, 427 glTexture->getHandle(), glTexture->getBinding(), 0, 0, 0, 0, 428 mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight(), 1); 429 430 return; 431 } 432 433 PRESERVE_FRAMEBUFFER(); 434 435 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mCopyFboDst); 436 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, glTexture->getBinding(), glTexture->getHandle(), 0); 437 438 glBindFramebuffer(GL_READ_FRAMEBUFFER, mCopyFboSrc); 439 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTargets[Color0]->getBinding(), mTargets[Color0]->getHandle(), 0); 440 441 glBlitFramebuffer(0, 0, mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight(), 442 0, 0, glTexture->getWidth(), glTexture->getHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST); 443} 444