From 132a76d8010261c85f9fe19a3201d19db040109a Mon Sep 17 00:00:00 2001 From: William Chen Date: Thu, 29 Nov 2012 18:04:07 -0500 Subject: [PATCH] Fix for CCRenderTexture on Android sleep/resume When an android app goes into the background, all gl context is lost, including any frame buffer objects that are required for rendering to a texture. When the android app returns to focus, a new FBO is generated for each render texture allowing rendering to the texture to continue. --- cocos2dx/misc_nodes/CCRenderTexture.cpp | 83 +++++++++++++++++++------ cocos2dx/misc_nodes/CCRenderTexture.h | 7 ++- 2 files changed, 70 insertions(+), 20 deletions(-) diff --git a/cocos2dx/misc_nodes/CCRenderTexture.cpp b/cocos2dx/misc_nodes/CCRenderTexture.cpp index 51044cc47c..87831a422f 100644 --- a/cocos2dx/misc_nodes/CCRenderTexture.cpp +++ b/cocos2dx/misc_nodes/CCRenderTexture.cpp @@ -59,12 +59,19 @@ CCRenderTexture::CCRenderTexture() , m_nClearStencil(0) , m_bAutoDraw(false) { +#if CC_ENABLE_CACHE_TEXTURE_DATA // Listen this event to save render texture before come to background. // Then it can be restored after coming to foreground on Android. CCNotificationCenter::sharedNotificationCenter()->addObserver(this, - callfuncO_selector(CCRenderTexture::listenToBackground), - EVENT_COME_TO_BACKGROUND, - NULL); + callfuncO_selector(CCRenderTexture::listenToBackground), + EVENT_COME_TO_BACKGROUND, + NULL); + + CCNotificationCenter::sharedNotificationCenter()->addObserver(this, + callfuncO_selector(CCRenderTexture::listenToForeground), + EVNET_COME_TO_FOREGROUND, // this is misspelt + NULL); +#endif } CCRenderTexture::~CCRenderTexture() @@ -78,29 +85,59 @@ CCRenderTexture::~CCRenderTexture() glDeleteRenderbuffers(1, &m_uDepthRenderBufffer); } CC_SAFE_DELETE(m_pUITextureImage); - + +#if CC_ENABLE_CACHE_TEXTURE_DATA CCNotificationCenter::sharedNotificationCenter()->removeObserver(this, EVENT_COME_TO_BACKGROUND); + CCNotificationCenter::sharedNotificationCenter()->removeObserver(this, EVNET_COME_TO_FOREGROUND); +#endif } void CCRenderTexture::listenToBackground(cocos2d::CCObject *obj) { #if CC_ENABLE_CACHE_TEXTURE_DATA - CC_SAFE_DELETE(m_pUITextureImage); // to get the rendered texture data - m_pUITextureImage = newCCImage(); - + m_pUITextureImage = newCCImage(false); if (m_pUITextureImage) { const CCSize& s = m_pTexture->getContentSizeInPixels(); VolatileTexture::addDataTexture(m_pTexture, m_pUITextureImage->getData(), kTexture2DPixelFormat_RGBA8888, s); - } + + if ( m_pTextureCopy ) + { + VolatileTexture::addDataTexture(m_pTextureCopy, m_pUITextureImage->getData(), kTexture2DPixelFormat_RGBA8888, s); + } + } else { CCLOG("Cache rendertexture failed!"); } + + glDeleteFramebuffers(1, &m_uFBO); + m_uFBO = 0; +#endif +} + +void CCRenderTexture::listenToForeground(cocos2d::CCObject *obj) +{ +#if CC_ENABLE_CACHE_TEXTURE_DATA + // -- regenerate frame buffer object and attach the texture + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO); + + glGenFramebuffers(1, &m_uFBO); + glBindFramebuffer(GL_FRAMEBUFFER, m_uFBO); + + m_pTexture->setAliasTexParameters(); + + if ( m_pTextureCopy ) + { + m_pTextureCopy->setAliasTexParameters(); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTexture->getName(), 0); + glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO); #endif } @@ -590,7 +627,7 @@ bool CCRenderTexture::saveToFile(const char *szFilePath) { bool bRet = false; - CCImage *pImage = newCCImage(); + CCImage *pImage = newCCImage(true); if (pImage) { bRet = pImage->saveToFile(szFilePath, kCCImageFormatJPEG); @@ -605,7 +642,7 @@ bool CCRenderTexture::saveToFile(const char *fileName, tCCImageFormat format) CCAssert(format == kCCImageFormatJPEG || format == kCCImageFormatPNG, "the image can only be saved as JPG or PNG format"); - CCImage *pImage = newCCImage(); + CCImage *pImage = newCCImage(true); if (pImage) { std::string fullpath = CCFileUtils::sharedFileUtils()->getWriteablePath() + fileName; @@ -619,7 +656,7 @@ bool CCRenderTexture::saveToFile(const char *fileName, tCCImageFormat format) } /* get buffer as CCImage */ -CCImage* CCRenderTexture::newCCImage() +CCImage* CCRenderTexture::newCCImage(bool flipImage) { CCAssert(m_ePixelFormat == kCCTexture2DPixelFormat_RGBA8888, "only RGBA8888 can be saved as image"); @@ -656,16 +693,24 @@ CCImage* CCRenderTexture::newCCImage() glReadPixels(0,0,nSavedBufferWidth, nSavedBufferHeight,GL_RGBA,GL_UNSIGNED_BYTE, pTempData); this->end(); - // to get the actual texture data - // #640 the image read from rendertexture is dirty - for (int i = 0; i < nSavedBufferHeight; ++i) + if ( flipImage ) // -- flip is only required when saving image to file { - memcpy(&pBuffer[i * nSavedBufferWidth * 4], - &pTempData[(nSavedBufferHeight - i - 1) * nSavedBufferWidth * 4], - nSavedBufferWidth * 4); - } + // to get the actual texture data + // #640 the image read from rendertexture is dirty + for (int i = 0; i < nSavedBufferHeight; ++i) + { + memcpy(&pBuffer[i * nSavedBufferWidth * 4], + &pTempData[(nSavedBufferHeight - i - 1) * nSavedBufferWidth * 4], + nSavedBufferWidth * 4); + } - pImage->initWithImageData(pBuffer, nSavedBufferWidth * nSavedBufferHeight * 4, CCImage::kFmtRawData, nSavedBufferWidth, nSavedBufferHeight, 8); + pImage->initWithImageData(pBuffer, nSavedBufferWidth * nSavedBufferHeight * 4, CCImage::kFmtRawData, nSavedBufferWidth, nSavedBufferHeight, 8); + } + else + { + pImage->initWithImageData(pTempData, nSavedBufferWidth * nSavedBufferHeight * 4, CCImage::kFmtRawData, nSavedBufferWidth, nSavedBufferHeight, 8); + } + } while (0); CC_SAFE_DELETE_ARRAY(pBuffer); diff --git a/cocos2dx/misc_nodes/CCRenderTexture.h b/cocos2dx/misc_nodes/CCRenderTexture.h index 26aff3e68d..313cd67d19 100644 --- a/cocos2dx/misc_nodes/CCRenderTexture.h +++ b/cocos2dx/misc_nodes/CCRenderTexture.h @@ -128,7 +128,7 @@ public: /* creates a new CCImage from with the texture's data. Caller is responsible for releasing it by calling delete. */ - CCImage* newCCImage(); + CCImage* newCCImage(bool flipImage = true); /** saves the texture into a file using JPEG format. The file will be saved in the Documents folder. Returns YES if the operation is successful. @@ -145,6 +145,11 @@ public: */ void listenToBackground(CCObject *obj); + /** Listen "come to foreground" message and restore the frame buffer object + It only has effect on Android. + */ + void listenToForeground(CCObject *obj); + /** Valid flags: GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT. They can be OR'ed. Valid when "autoDraw is YES. */ unsigned int getClearFlags() const; void setClearFlags(unsigned int uClearFlags);