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);
|
||||
}
|
||||
|
||||
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*/,
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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,40 +74,86 @@ 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.
|
||||
* The implemntation of pthread_cond_wait() of win32 has a bug, it can not
|
||||
* wait the condition at first time.
|
||||
*/
|
||||
/* 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.
|
||||
*/
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
static bool firstRun = true;
|
||||
if (firstRun)
|
||||
{
|
||||
pthread_cond_wait(&s_condition, &s_conditionMutex);
|
||||
firstRun = false;
|
||||
}
|
||||
static bool firstRun = true;
|
||||
if (firstRun)
|
||||
{
|
||||
pthread_cond_wait(&s_condition, &s_conditionMutex);
|
||||
firstRun = false;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue