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);
}
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,
int nDataLen,
EImageFormat eFmt/* = eSrcFmtPng*/,

View File

@ -63,6 +63,15 @@ public:
*/
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.

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
THE SOFTWARE.
****************************************************************************/
#define COCOS2D_DEBUG 1
#include <stack>
#include <string>
#include <cctype>
#include <queue>
#include "CCTextureCache.h"
#include "CCTexture2D.h"
#include "ccMacros.h"
@ -50,13 +50,23 @@ typedef struct _AsyncStruct
SEL_CallFuncO selector;
} AsyncStruct;
static cocos2d::CCImage* s_pImageAsync;
// only allow one loading thread at a time
static pthread_mutex_t s_loadingThreadMutex;
typedef struct _ImageInfo
{
AsyncStruct *asyncStruct;
CCImage *image;
} ImageInfo;
static pthread_t s_loadingThread;
static pthread_mutex_t s_asyncStructQueueMutex;
static pthread_mutex_t s_ImageInfoMutex;
// condition
static pthread_cond_t s_condition;
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)
{
@ -64,24 +74,19 @@ static void* loadImage(void* data)
CCThread thread;
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_loadingThreadMutex);
pthread_mutex_lock(&s_asyncStructQueueMutex);
if (pQueue->empty())
{
pthread_mutex_unlock(&s_asyncStructQueueMutex);
s_pAsyncObject = (AsyncStruct*)data;
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.
/* Wait for rendering thread to add loading image info.
* The implemntation of pthread_cond_wait() of win32 has a bug, it can not
* wait the condition at first time.
*/
@ -95,9 +100,60 @@ static void* loadImage(void* data)
#endif
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;
}
@ -167,39 +223,59 @@ void CCTextureCache::addImageAsync(const char *path, SelectorProtocol *target, S
static bool firstRun = true;
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_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);
firstRun = false;
}
// generate async struct
AsyncStruct *data = new AsyncStruct();
data->filename = fullpath.c_str();
data->target = target;
data->selector = selector;
// load image in a new thread
pthread_t p;
pthread_create(&p, NULL, loadImage, (void*)data);
// add async struct into queue
pthread_mutex_lock(&s_asyncStructQueueMutex);
s_pAsyncStructQueue->push(data);
pthread_cond_signal(&s_condition);
pthread_mutex_unlock(&s_asyncStructQueueMutex);
}
void CCTextureCache::addImageAsyncCallBack(ccTime dt)
{
// the image is generated in loading thread
if (s_pImageAsync != NULL)
{
std::queue<ImageInfo*> *imagesQueue = s_pImageQueue;
SelectorProtocol *target = s_pAsyncObject->target;
SEL_CallFuncO selector = s_pAsyncObject->selector;
const char* filename = s_pAsyncObject->filename.c_str();
pthread_mutex_lock(&s_ImageInfoMutex);
if (imagesQueue->empty())
{
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
CCTexture2D *texture = new CCTexture2D();
texture->initWithImage(s_pImageAsync);
texture->initWithImage(pImage);
// cache the texture
m_pTextures->setObject(texture, filename);
@ -207,13 +283,9 @@ void CCTextureCache::addImageAsyncCallBack(ccTime dt)
(target->*selector)(texture);
// the object is newed in addImageAsync() and will be assigned in the loading thread
delete s_pAsyncObject;
delete s_pImageAsync;
s_pImageAsync = NULL;
pthread_cond_signal(&s_condition);
delete pImage;
delete pAsyncStruct;
delete pImageInfo;
}
}

View File

@ -6,7 +6,7 @@
using namespace cocos2d;
TextureCacheTest::TextureCacheTest()
: m_nNumberOfSprites(24)
: m_nNumberOfSprites(20)
, m_nNumberOfLoadedSprites(0)
{
CCSize size = CCDirector::sharedDirector()->getWinSize();
@ -20,8 +20,6 @@ TextureCacheTest::TextureCacheTest()
this->addChild(m_pLabelLoading);
this->addChild(m_pLabelPercent);
CCTime::gettimeofdayCocos2d(&m_time, NULL);
// load textrues
CCTextureCache::sharedTextureCache()->addImageAsync("Images/HelloWorld.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/background2.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-hd.png", this, callfuncO_selector(TextureCacheTest::loadingCallBack));
}
void TextureCacheTest::loadingCallBack(CCObject *obj)
@ -66,12 +60,6 @@ void TextureCacheTest::loadingCallBack(CCObject *obj)
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();
// create sprites
@ -99,11 +87,7 @@ void TextureCacheTest::addSprite()
CCSprite *s16 = CCSprite::spriteWithFile("Images/background1.png");
CCSprite *s17 = CCSprite::spriteWithFile("Images/background2.png");
CCSprite *s18 = CCSprite::spriteWithFile("Images/background3.png");
CCSprite *s19 = CCSprite::spriteWithFile("Images/background1-hd.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");
CCSprite *s19 = CCSprite::spriteWithFile("Images/blocks.png");
s1->setPosition(CCPointMake(50, 50));
s2->setPosition(CCPointMake(60, 50));

View File

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