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.
This commit is contained in:
William Chen 2012-11-29 18:04:07 -05:00
parent c53cfe88a7
commit 132a76d801
2 changed files with 70 additions and 20 deletions

View File

@ -59,12 +59,19 @@ CCRenderTexture::CCRenderTexture()
, m_nClearStencil(0) , m_nClearStencil(0)
, m_bAutoDraw(false) , m_bAutoDraw(false)
{ {
#if CC_ENABLE_CACHE_TEXTURE_DATA
// Listen this event to save render texture before come to background. // Listen this event to save render texture before come to background.
// Then it can be restored after coming to foreground on Android. // Then it can be restored after coming to foreground on Android.
CCNotificationCenter::sharedNotificationCenter()->addObserver(this, CCNotificationCenter::sharedNotificationCenter()->addObserver(this,
callfuncO_selector(CCRenderTexture::listenToBackground), callfuncO_selector(CCRenderTexture::listenToBackground),
EVENT_COME_TO_BACKGROUND, EVENT_COME_TO_BACKGROUND,
NULL); NULL);
CCNotificationCenter::sharedNotificationCenter()->addObserver(this,
callfuncO_selector(CCRenderTexture::listenToForeground),
EVNET_COME_TO_FOREGROUND, // this is misspelt
NULL);
#endif
} }
CCRenderTexture::~CCRenderTexture() CCRenderTexture::~CCRenderTexture()
@ -78,29 +85,59 @@ CCRenderTexture::~CCRenderTexture()
glDeleteRenderbuffers(1, &m_uDepthRenderBufffer); glDeleteRenderbuffers(1, &m_uDepthRenderBufffer);
} }
CC_SAFE_DELETE(m_pUITextureImage); CC_SAFE_DELETE(m_pUITextureImage);
#if CC_ENABLE_CACHE_TEXTURE_DATA
CCNotificationCenter::sharedNotificationCenter()->removeObserver(this, EVENT_COME_TO_BACKGROUND); CCNotificationCenter::sharedNotificationCenter()->removeObserver(this, EVENT_COME_TO_BACKGROUND);
CCNotificationCenter::sharedNotificationCenter()->removeObserver(this, EVNET_COME_TO_FOREGROUND);
#endif
} }
void CCRenderTexture::listenToBackground(cocos2d::CCObject *obj) void CCRenderTexture::listenToBackground(cocos2d::CCObject *obj)
{ {
#if CC_ENABLE_CACHE_TEXTURE_DATA #if CC_ENABLE_CACHE_TEXTURE_DATA
CC_SAFE_DELETE(m_pUITextureImage); CC_SAFE_DELETE(m_pUITextureImage);
// to get the rendered texture data // to get the rendered texture data
m_pUITextureImage = newCCImage(); m_pUITextureImage = newCCImage(false);
if (m_pUITextureImage) if (m_pUITextureImage)
{ {
const CCSize& s = m_pTexture->getContentSizeInPixels(); const CCSize& s = m_pTexture->getContentSizeInPixels();
VolatileTexture::addDataTexture(m_pTexture, m_pUITextureImage->getData(), kTexture2DPixelFormat_RGBA8888, s); VolatileTexture::addDataTexture(m_pTexture, m_pUITextureImage->getData(), kTexture2DPixelFormat_RGBA8888, s);
}
if ( m_pTextureCopy )
{
VolatileTexture::addDataTexture(m_pTextureCopy, m_pUITextureImage->getData(), kTexture2DPixelFormat_RGBA8888, s);
}
}
else else
{ {
CCLOG("Cache rendertexture failed!"); 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 #endif
} }
@ -590,7 +627,7 @@ bool CCRenderTexture::saveToFile(const char *szFilePath)
{ {
bool bRet = false; bool bRet = false;
CCImage *pImage = newCCImage(); CCImage *pImage = newCCImage(true);
if (pImage) if (pImage)
{ {
bRet = pImage->saveToFile(szFilePath, kCCImageFormatJPEG); bRet = pImage->saveToFile(szFilePath, kCCImageFormatJPEG);
@ -605,7 +642,7 @@ bool CCRenderTexture::saveToFile(const char *fileName, tCCImageFormat format)
CCAssert(format == kCCImageFormatJPEG || format == kCCImageFormatPNG, CCAssert(format == kCCImageFormatJPEG || format == kCCImageFormatPNG,
"the image can only be saved as JPG or PNG format"); "the image can only be saved as JPG or PNG format");
CCImage *pImage = newCCImage(); CCImage *pImage = newCCImage(true);
if (pImage) if (pImage)
{ {
std::string fullpath = CCFileUtils::sharedFileUtils()->getWriteablePath() + fileName; std::string fullpath = CCFileUtils::sharedFileUtils()->getWriteablePath() + fileName;
@ -619,7 +656,7 @@ bool CCRenderTexture::saveToFile(const char *fileName, tCCImageFormat format)
} }
/* get buffer as CCImage */ /* get buffer as CCImage */
CCImage* CCRenderTexture::newCCImage() CCImage* CCRenderTexture::newCCImage(bool flipImage)
{ {
CCAssert(m_ePixelFormat == kCCTexture2DPixelFormat_RGBA8888, "only RGBA8888 can be saved as image"); 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); glReadPixels(0,0,nSavedBufferWidth, nSavedBufferHeight,GL_RGBA,GL_UNSIGNED_BYTE, pTempData);
this->end(); this->end();
// to get the actual texture data if ( flipImage ) // -- flip is only required when saving image to file
// #640 the image read from rendertexture is dirty
for (int i = 0; i < nSavedBufferHeight; ++i)
{ {
memcpy(&pBuffer[i * nSavedBufferWidth * 4], // to get the actual texture data
&pTempData[(nSavedBufferHeight - i - 1) * nSavedBufferWidth * 4], // #640 the image read from rendertexture is dirty
nSavedBufferWidth * 4); 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); } while (0);
CC_SAFE_DELETE_ARRAY(pBuffer); CC_SAFE_DELETE_ARRAY(pBuffer);

View File

@ -128,7 +128,7 @@ public:
/* creates a new CCImage from with the texture's data. /* creates a new CCImage from with the texture's data.
Caller is responsible for releasing it by calling delete. 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. /** 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. Returns YES if the operation is successful.
@ -145,6 +145,11 @@ public:
*/ */
void listenToBackground(CCObject *obj); 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. */ /** 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; unsigned int getClearFlags() const;
void setClearFlags(unsigned int uClearFlags); void setClearFlags(unsigned int uClearFlags);