mirror of https://github.com/axmolengine/axmol.git
issue #854: optimize CCTextureCache::addImageAsync()
This commit is contained in:
parent
efa9275d7f
commit
bc60ed7856
|
@ -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*/,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue