issue #854: optimize CCTextureCache::addImageAsync()

This commit is contained in:
minggo 2011-11-22 16:47:24 +08:00
parent efa9275d7f
commit bc60ed7856
5 changed files with 142 additions and 73 deletions

View File

@ -95,6 +95,13 @@ bool CCImage::initWithImageFile(const char * strPath, EImageFormat eImgFmt/* = e
return initWithImageData(data.getBuffer(), data.getSize(), eImgFmt); return initWithImageData(data.getBuffer(), data.getSize(), eImgFmt);
} }
bool CCImage::initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType)
{
CC_UNUSED_PARAM(imageType);
CCFileData data(fullpath, "rb");
return initWithImageData(data.getBuffer(), data.getSize(), imageType);
}
bool CCImage::initWithImageData(void * pData, bool CCImage::initWithImageData(void * pData,
int nDataLen, int nDataLen,
EImageFormat eFmt/* = eSrcFmtPng*/, EImageFormat eFmt/* = eSrcFmtPng*/,

View File

@ -63,6 +63,15 @@ public:
*/ */
bool initWithImageFile(const char * strPath, EImageFormat imageType = kFmtPng); bool initWithImageFile(const char * strPath, EImageFormat imageType = kFmtPng);
/*
@brief The same meaning as initWithImageFile, but it is thread safe. It is casued by
loadImage() in CCTextureCache.cpp.
@param fullpath full path of the file
@param imageType the type of image, now only support tow types.
@return true if load correctly
*/
bool initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType = kFmtPng);
/** /**
@brief Load image from stream buffer. @brief Load image from stream buffer.

View File

@ -23,11 +23,11 @@ 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
****************************************************************************/ ****************************************************************************/
#define COCOS2D_DEBUG 1
#include <stack> #include <stack>
#include <string> #include <string>
#include <cctype> #include <cctype>
#include <queue>
#include "CCTextureCache.h" #include "CCTextureCache.h"
#include "CCTexture2D.h" #include "CCTexture2D.h"
#include "ccMacros.h" #include "ccMacros.h"
@ -50,13 +50,23 @@ typedef struct _AsyncStruct
SEL_CallFuncO selector; SEL_CallFuncO selector;
} AsyncStruct; } AsyncStruct;
static cocos2d::CCImage* s_pImageAsync; typedef struct _ImageInfo
// only allow one loading thread at a time {
static pthread_mutex_t s_loadingThreadMutex; AsyncStruct *asyncStruct;
CCImage *image;
} ImageInfo;
static pthread_t s_loadingThread;
static pthread_mutex_t s_asyncStructQueueMutex;
static pthread_mutex_t s_ImageInfoMutex;
// condition // condition
static pthread_cond_t s_condition; static pthread_cond_t s_condition;
static pthread_mutex_t s_conditionMutex; static pthread_mutex_t s_conditionMutex;
static AsyncStruct *s_pAsyncObject;
static std::queue<AsyncStruct*> *s_pAsyncStructQueue;
static std::queue<ImageInfo*> *s_pImageQueue;
static void* loadImage(void* data) static void* loadImage(void* data)
{ {
@ -64,24 +74,19 @@ static void* loadImage(void* data)
CCThread thread; CCThread thread;
thread.createAutoreleasePool(); thread.createAutoreleasePool();
if (! ((AsyncStruct*)data)->filename.c_str()) AsyncStruct *pAsyncStruct = NULL;
CCImage *pImage = NULL;
while (true)
{ {
return 0; std::queue<AsyncStruct*> *pQueue = s_pAsyncStructQueue;
}
// one loading thread at a time pthread_mutex_lock(&s_asyncStructQueueMutex);
pthread_mutex_lock(&s_loadingThreadMutex); if (pQueue->empty())
{
pthread_mutex_unlock(&s_asyncStructQueueMutex);
s_pAsyncObject = (AsyncStruct*)data; /* Wait for rendering thread to add loading image info.
const char *filename = s_pAsyncObject->filename.c_str();
CCLOG("thread 0x%x is loading image %s", pthread_self(), filename);
CCImage *tmpImage = new CCImage();
tmpImage->initWithImageFile(filename);
s_pImageAsync = tmpImage;
/* Wait for rendering thread to comsume the image.
* The implemntation of pthread_cond_wait() of win32 has a bug, it can not * The implemntation of pthread_cond_wait() of win32 has a bug, it can not
* wait the condition at first time. * wait the condition at first time.
*/ */
@ -95,9 +100,60 @@ static void* loadImage(void* data)
#endif #endif
pthread_cond_wait(&s_condition, &s_conditionMutex); pthread_cond_wait(&s_condition, &s_conditionMutex);
CCLOG("thread 0x%x has pass the condition, new loading thread is avalable", pthread_self()); continue;
}
else
{
// get async struct from queue
pAsyncStruct = pQueue->front();
pQueue->pop();
pthread_mutex_unlock(&s_asyncStructQueueMutex);
}
pthread_mutex_unlock(&s_loadingThreadMutex); const char *filename = pAsyncStruct->filename.c_str();
CCLOG("thread 0x%x is loading image %s", pthread_self(), filename);
// generate image
CCImage *pImage = NULL;
if (std::string::npos != pAsyncStruct->filename.find(".jpg") || std::string::npos != pAsyncStruct->filename.find(".jpeg"))
{
pImage = new CCImage();
if (!pImage->initWithImageFileThreadSafe(filename, cocos2d::CCImage::kFmtJpg))
{
delete pImage;
CCLOG("can not load %s", filename);
continue;
}
}
else if (std::string::npos != pAsyncStruct->filename.find(".png"))
{
pImage = new CCImage();
if (! pImage->initWithImageFileThreadSafe(filename, cocos2d::CCImage::kFmtPng))
{
delete pImage;
CCLOG("can not load %s", filename);
continue;
}
}
else
{
CCLog("unsupportted format %s",filename);
delete pAsyncStruct;
continue;
}
// generate image info
ImageInfo *pImageInfo = new ImageInfo();
pImageInfo->asyncStruct = pAsyncStruct;
pImageInfo->image = pImage;
// put the image info into the queue
pthread_mutex_lock(&s_ImageInfoMutex);
s_pImageQueue->push(pImageInfo);
pthread_mutex_unlock(&s_ImageInfoMutex);
}
return 0; return 0;
} }
@ -167,39 +223,59 @@ void CCTextureCache::addImageAsync(const char *path, SelectorProtocol *target, S
static bool firstRun = true; static bool firstRun = true;
if (firstRun) if (firstRun)
{ {
pthread_mutex_init(&s_loadingThreadMutex, NULL); s_pAsyncStructQueue = new queue<AsyncStruct*>();
s_pImageQueue = new queue<ImageInfo*>();
pthread_mutex_init(&s_asyncStructQueueMutex, NULL);
pthread_mutex_init(&s_conditionMutex, NULL); pthread_mutex_init(&s_conditionMutex, NULL);
pthread_cond_init(&s_condition, NULL); pthread_cond_init(&s_condition, NULL);
s_pImageAsync = NULL; pthread_mutex_init(&s_ImageInfoMutex, NULL);
pthread_create(&s_loadingThread, NULL, loadImage, NULL);
CCScheduler::sharedScheduler()->scheduleSelector(schedule_selector(CCTextureCache::addImageAsyncCallBack), this, 0, false); CCScheduler::sharedScheduler()->scheduleSelector(schedule_selector(CCTextureCache::addImageAsyncCallBack), this, 0, false);
firstRun = false; firstRun = false;
} }
// generate async struct
AsyncStruct *data = new AsyncStruct(); AsyncStruct *data = new AsyncStruct();
data->filename = fullpath.c_str(); data->filename = fullpath.c_str();
data->target = target; data->target = target;
data->selector = selector; data->selector = selector;
// load image in a new thread // add async struct into queue
pthread_t p; pthread_mutex_lock(&s_asyncStructQueueMutex);
pthread_create(&p, NULL, loadImage, (void*)data); s_pAsyncStructQueue->push(data);
pthread_cond_signal(&s_condition);
pthread_mutex_unlock(&s_asyncStructQueueMutex);
} }
void CCTextureCache::addImageAsyncCallBack(ccTime dt) void CCTextureCache::addImageAsyncCallBack(ccTime dt)
{ {
// the image is generated in loading thread // the image is generated in loading thread
if (s_pImageAsync != NULL) std::queue<ImageInfo*> *imagesQueue = s_pImageQueue;
{
SelectorProtocol *target = s_pAsyncObject->target; pthread_mutex_lock(&s_ImageInfoMutex);
SEL_CallFuncO selector = s_pAsyncObject->selector; if (imagesQueue->empty())
const char* filename = s_pAsyncObject->filename.c_str(); {
pthread_mutex_unlock(&s_ImageInfoMutex);
}
else
{
ImageInfo *pImageInfo = imagesQueue->front();
imagesQueue->pop();
pthread_mutex_unlock(&s_ImageInfoMutex);
AsyncStruct *pAsyncStruct = pImageInfo->asyncStruct;
CCImage *pImage = pImageInfo->image;
SelectorProtocol *target = pAsyncStruct->target;
SEL_CallFuncO selector = pAsyncStruct->selector;
const char* filename = pAsyncStruct->filename.c_str();
// generate texture in render thread // generate texture in render thread
CCTexture2D *texture = new CCTexture2D(); CCTexture2D *texture = new CCTexture2D();
texture->initWithImage(s_pImageAsync); texture->initWithImage(pImage);
// cache the texture // cache the texture
m_pTextures->setObject(texture, filename); m_pTextures->setObject(texture, filename);
@ -207,13 +283,9 @@ void CCTextureCache::addImageAsyncCallBack(ccTime dt)
(target->*selector)(texture); (target->*selector)(texture);
// the object is newed in addImageAsync() and will be assigned in the loading thread delete pImage;
delete s_pAsyncObject; delete pAsyncStruct;
delete pImageInfo;
delete s_pImageAsync;
s_pImageAsync = NULL;
pthread_cond_signal(&s_condition);
} }
} }

View File

@ -6,7 +6,7 @@
using namespace cocos2d; using namespace cocos2d;
TextureCacheTest::TextureCacheTest() TextureCacheTest::TextureCacheTest()
: m_nNumberOfSprites(24) : m_nNumberOfSprites(20)
, m_nNumberOfLoadedSprites(0) , m_nNumberOfLoadedSprites(0)
{ {
CCSize size = CCDirector::sharedDirector()->getWinSize(); CCSize size = CCDirector::sharedDirector()->getWinSize();
@ -20,8 +20,6 @@ TextureCacheTest::TextureCacheTest()
this->addChild(m_pLabelLoading); this->addChild(m_pLabelLoading);
this->addChild(m_pLabelPercent); this->addChild(m_pLabelPercent);
CCTime::gettimeofdayCocos2d(&m_time, NULL);
// load textrues // load textrues
CCTextureCache::sharedTextureCache()->addImageAsync("Images/HelloWorld.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack)); CCTextureCache::sharedTextureCache()->addImageAsync("Images/HelloWorld.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
CCTextureCache::sharedTextureCache()->addImageAsync("Images/grossini.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack)); CCTextureCache::sharedTextureCache()->addImageAsync("Images/grossini.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
@ -42,11 +40,7 @@ TextureCacheTest::TextureCacheTest()
CCTextureCache::sharedTextureCache()->addImageAsync("Images/background1.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack)); CCTextureCache::sharedTextureCache()->addImageAsync("Images/background1.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
CCTextureCache::sharedTextureCache()->addImageAsync("Images/background2.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack)); CCTextureCache::sharedTextureCache()->addImageAsync("Images/background2.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
CCTextureCache::sharedTextureCache()->addImageAsync("Images/background3.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack)); CCTextureCache::sharedTextureCache()->addImageAsync("Images/background3.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
CCTextureCache::sharedTextureCache()->addImageAsync("Images/background1-hd.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
CCTextureCache::sharedTextureCache()->addImageAsync("Images/background2-hd.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
CCTextureCache::sharedTextureCache()->addImageAsync("Images/background3-hd.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
CCTextureCache::sharedTextureCache()->addImageAsync("Images/blocks.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack)); CCTextureCache::sharedTextureCache()->addImageAsync("Images/blocks.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
CCTextureCache::sharedTextureCache()->addImageAsync("Images/blocks-hd.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
} }
void TextureCacheTest::loadingCallBack(CCObject *obj) void TextureCacheTest::loadingCallBack(CCObject *obj)
@ -66,12 +60,6 @@ void TextureCacheTest::loadingCallBack(CCObject *obj)
void TextureCacheTest::addSprite() void TextureCacheTest::addSprite()
{ {
struct cc_timeval endTime;
CCTime::gettimeofdayCocos2d(&endTime, NULL);
CCLog("time is %d secodes %d ns", endTime.tv_sec - m_time.tv_sec, endTime.tv_usec - m_time.tv_usec);
CCSize size = CCDirector::sharedDirector()->getWinSize(); CCSize size = CCDirector::sharedDirector()->getWinSize();
// create sprites // create sprites
@ -99,11 +87,7 @@ void TextureCacheTest::addSprite()
CCSprite *s16 = CCSprite::spriteWithFile("Images/background1.png"); CCSprite *s16 = CCSprite::spriteWithFile("Images/background1.png");
CCSprite *s17 = CCSprite::spriteWithFile("Images/background2.png"); CCSprite *s17 = CCSprite::spriteWithFile("Images/background2.png");
CCSprite *s18 = CCSprite::spriteWithFile("Images/background3.png"); CCSprite *s18 = CCSprite::spriteWithFile("Images/background3.png");
CCSprite *s19 = CCSprite::spriteWithFile("Images/background1-hd.png"); CCSprite *s19 = CCSprite::spriteWithFile("Images/blocks.png");
CCSprite *s20 = CCSprite::spriteWithFile("Images/background2-hd.png");
CCSprite *s21 = CCSprite::spriteWithFile("Images/background3-hd.png");
CCSprite *s22 = CCSprite::spriteWithFile("Images/blocks.png");
CCSprite *s23 = CCSprite::spriteWithFile("Images/blocks-hd.png");
s1->setPosition(CCPointMake(50, 50)); s1->setPosition(CCPointMake(50, 50));
s2->setPosition(CCPointMake(60, 50)); s2->setPosition(CCPointMake(60, 50));

View File

@ -18,9 +18,6 @@ private:
cocos2d::CCLabelTTF *m_pLabelPercent; cocos2d::CCLabelTTF *m_pLabelPercent;
int m_nNumberOfSprites; int m_nNumberOfSprites;
int m_nNumberOfLoadedSprites; int m_nNumberOfLoadedSprites;
struct cocos2d::cc_timeval m_time;
}; };
class TextureCacheTestScene : public TestScene class TextureCacheTestScene : public TestScene