Last active
December 20, 2015 21:09
-
-
Save ofTheo/6195470 to your computer and use it in GitHub Desktop.
threaded texture loading for openFrameworks for desktop ( just cpu ) and iOS ( cpu and gpu ). explanation here: http://forum.openframeworks.cc/index.php/topic,13032.msg56223.html#msg56223
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// threadedTexture.cpp | |
// iPhone_OSX_BuildTheo | |
// | |
// Created by Theodore Watson on 3/18/13. | |
// | |
// | |
#include "threadedLoader.h" | |
threadedLoader::threadedLoader(){ | |
bListenerAdded = false; | |
bSetup = false; | |
} | |
threadedLoader::~threadedLoader(){ | |
clear(); | |
} | |
void threadedLoader::setup(float timePer){ | |
// cout << "threadedLoader :: setup : | " << ofGetElapsedTimef() << endl; | |
if( !bListenerAdded ){ | |
ofAddListener(ofEvents().update, this, &threadedLoader::update); | |
bListenerAdded = true; | |
} | |
timePerImage = timePer; | |
timeLastLoad = 0.0; | |
bSetup = true; | |
} | |
bool threadedLoader::isSetup(){ | |
return bSetup; | |
} | |
void threadedLoader::clear(){ | |
stopThread(); | |
if( bListenerAdded ){ | |
ofRemoveListener(ofEvents().update, this, &threadedLoader::update); | |
bListenerAdded = false; | |
} | |
mutex.lock(); | |
basePtrQ.clear(); | |
mutex.unlock(); | |
mutex2.lock(); | |
basePtrGLQ.clear(); | |
mutex2.unlock(); | |
} | |
void threadedLoader::addToQueue(string path, threadedBaseObject * objPtr){ | |
path = ofToDataPath(path, true); | |
if( bListenerAdded == false && objPtr->needsGLLoad() ){ | |
setup(); | |
} | |
objPtr->setupForThreadedLoad(path); | |
objPtr->setState(threadedBaseObject::STATE_LOADING_DISK); | |
if(isThreadRunning()){ | |
mutex.lock(); | |
basePtrQ.push_back(objPtr); | |
mutex.unlock(); | |
} else { | |
// cout << "threadedLoader :: addToQueue : thread is not running, going to start it now | " << ofGetElapsedTimef() << endl; | |
basePtrQ.push_back(objPtr); | |
startThread(false); | |
} | |
} | |
bool threadedLoader::hasInQueue( threadedBaseObject* objPtr ) { | |
bool bFound = false; | |
mutex.lock(); | |
for(int i = 0; i < basePtrQ.size(); i++ ) { | |
if(basePtrQ[i] == objPtr){ | |
bFound = true; | |
break; | |
} | |
} | |
mutex.unlock(); | |
if( bFound ){ | |
return true; | |
} | |
mutex2.lock(); | |
for(int i = 0; i < basePtrGLQ.size(); i++ ) { | |
if(basePtrGLQ[i] == objPtr){ | |
bFound = true; | |
break; | |
} | |
} | |
mutex2.unlock(); | |
return bFound; | |
} | |
void threadedLoader::removeFromQueue( threadedBaseObject* objPtr ) { | |
mutex.lock(); | |
for(int i = 0; i < basePtrQ.size(); i++ ) { | |
if(basePtrQ[i] == objPtr) { | |
basePtrQ.erase( basePtrQ.begin()+i ); | |
break; | |
} | |
} | |
mutex.unlock(); | |
mutex2.lock(); | |
for(int i = 0; i < basePtrGLQ.size(); i++ ) { | |
if(basePtrGLQ[i] == objPtr){ | |
basePtrGLQ.erase( basePtrGLQ.begin() + i ); | |
break; | |
} | |
} | |
mutex2.unlock(); | |
} | |
void threadedLoader::update(ofEventArgs & args){ | |
if( basePtrGLQ.size() ){ | |
float t = ofGetElapsedTimef(); | |
if( t - timeLastLoad < timePerImage ) return; | |
threadedBaseObject * obj = basePtrGLQ[0]; | |
//cout << " loading in main thread " << obj->getFilePath() << endl; | |
obj->loadGL(); | |
obj->updateLoadTime(); | |
obj->setState(threadedBaseObject::STATE_LOADED); | |
obj->onLoadComplete(); | |
mutex2.lock(); | |
basePtrGLQ.erase(basePtrGLQ.begin(), basePtrGLQ.begin()+1); | |
mutex2.unlock(); | |
timeLastLoad = ofGetElapsedTimef(); | |
}else if( basePtrQ.size() == 0 && isThreadRunning() ){ | |
stopThread(); | |
} | |
} | |
void threadedLoader::threadedFunction(){ | |
while( isThreadRunning() && basePtrQ.size() ){ | |
threadedBaseObject * obj = basePtrQ[0]; | |
if(!obj->load()) { | |
// cout << "could not load it " << endl; | |
obj->loadError(); | |
} else { | |
if( obj->needsGLLoad() ){ | |
// cout << " adding to GL load " << obj->getFilePath() << endl; | |
obj->setState(threadedBaseObject::STATE_LOADING_GL); | |
mutex2.lock(); | |
basePtrGLQ.push_back(obj); | |
mutex2.unlock(); | |
} else { | |
obj->updateLoadTime(); | |
obj->setState(threadedBaseObject::STATE_LOADED); | |
obj->onLoadComplete(); | |
} | |
} | |
mutex.lock(); | |
basePtrQ.erase(basePtrQ.begin(), basePtrQ.begin()+1); | |
mutex.unlock(); | |
ofSleepMillis(10); | |
} | |
stopThread(); | |
} | |
string threadedLoader::toString() { | |
stringstream ss; | |
mutex.lock(); | |
ss << "basePtrQ --------------------- " << endl; | |
for(int i = 0; i < basePtrQ.size(); i++ ) { | |
ss << "[" <<i<<"]: " << basePtrQ[i]->getFilePath() << endl; | |
} | |
mutex.unlock(); | |
mutex2.lock(); | |
ss << "basePtrGLQ --------------------- " << endl; | |
for(int i = 0; i < basePtrGLQ.size(); i++ ) { | |
ss << "["<<i<<"]: " << basePtrGLQ[i]->getFilePath() << endl; | |
} | |
mutex2.unlock(); | |
return ss.str().c_str(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// threadedLoader.h | |
// iPhone_OSX_BuildTheo | |
// | |
// Created by Theodore Watson on 3/20/13. | |
// | |
// | |
#pragma once | |
#include "ofMain.h" | |
class threadedBaseObject{ | |
public: | |
typedef enum{ | |
STATE_EMPTY = 0, | |
STATE_LOADING_DISK, | |
STATE_LOADING_GL, | |
STATE_LOADED, | |
STATE_ERROR | |
}loadedState; | |
//------------------------------------ | |
threadedBaseObject(){ | |
state = STATE_EMPTY; | |
} | |
virtual void setupForThreadedLoad(string path){ | |
filePath = ofToDataPath(path, true); | |
} | |
virtual bool needsGLLoad() = 0; | |
virtual bool load() = 0; | |
//overide this if you have need to load things on the main thread | |
virtual void loadGL(){ | |
} | |
void loadError() { | |
ofLogError( "threadedLoader :: error loading " + getFilePath() ); | |
setState( threadedBaseObject::STATE_ERROR ); | |
} | |
virtual bool isReady(){ | |
return ( state == STATE_LOADED ); | |
} | |
virtual void updateLoadTime(){ | |
timeLoaded = ofGetElapsedTimef(); | |
} | |
virtual void setState(threadedBaseObject::loadedState stateIn){ | |
state = stateIn; | |
} | |
virtual threadedBaseObject::loadedState getState(){ | |
return state; | |
} | |
bool isLoading() { | |
return (state == STATE_LOADING_DISK || state == STATE_LOADING_GL); | |
} | |
virtual void onLoadComplete() { | |
} | |
string getFilePath(){ | |
return filePath; | |
} | |
protected: | |
float timeLoaded; | |
loadedState state; | |
string filePath; | |
}; | |
class threadedLoader : public ofThread{ | |
public: | |
threadedLoader(); | |
~threadedLoader(); | |
bool isSetup(); | |
void setup(float timePerFile = 0.3); | |
void update(ofEventArgs & args); | |
void clear(); | |
void addToQueue(string path, threadedBaseObject * tex); | |
bool hasInQueue( threadedBaseObject * objPtr ); | |
void removeFromQueue( threadedBaseObject* objPtr ); | |
void threadedFunction(); | |
string toString(); | |
protected: | |
vector <threadedBaseObject *> basePtrQ; | |
vector <threadedBaseObject *> basePtrGLQ; | |
ofMutex mutex; | |
ofMutex mutex2; | |
bool bListenerAdded; | |
bool bSetup; | |
float timePerImage; | |
float timeLastLoad; | |
}; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// threadedTexture.h | |
// iPhone_OSX_BuildTheo | |
// | |
// Created by Theodore Watson on 3/18/13. | |
// | |
// | |
#pragma once | |
#include "ofMain.h" | |
#include "threadedLoader.h" | |
class threadedTexture : public ofTexture, public threadedBaseObject{ | |
public: | |
//------------------------------------ | |
threadedTexture(); | |
void clear(); | |
bool load(); | |
void loadGL(); | |
bool needsGLLoad(); | |
void setTex2d( bool aB ); | |
void queueTextureWrap( GLint aWrap ); | |
void setFlipTex( bool aB ); | |
//------------------------------------ | |
void draw(float x, float y); | |
void draw(float x, float y, float z); | |
void draw(float x, float y, float w, float h); | |
void drawScaledWidth(float x, float y, float z, float w); | |
void drawScaledHeight(float x, float y, float z, float h); | |
void drawScaledPercent(float x, float y, float z, float pct); | |
void draw(float x, float y, float z, float w, float h); | |
//------------------------------------ | |
//overide this to make your own placeholder | |
void customDraw(float x, float y, float z, float w, float h); | |
ofPixels * pix; | |
bool bNeedsGLLoad; | |
bool bCustomDraw; | |
bool bTex2d; | |
GLint wrapMode; | |
bool bFlipTex; | |
}; | |
typedef threadedLoader threadedTextureLoader; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// threadedTexture.mm | |
// iPhone_OSX_BuildTheo | |
// | |
// Created by Theodore Watson on 3/18/13. | |
// | |
// | |
#include "threadedTextureLoader.h" | |
static bool bDevice = ( ! ( TARGET_IPHONE_SIMULATOR ) ) && ( TARGET_OS_IPHONE ) ; | |
static bool biOS = TARGET_OS_IPHONE ; | |
#if (TARGET_OS_IPHONE) | |
#import <QuartzCore/QuartzCore.h> | |
#import <OpenGLES/EAGL.h> | |
#include "ofxiPhone.h" | |
#include "ofxiPhoneExtras.h" | |
EAGLContext * contextB = NULL; | |
EAGLContext * contextA = NULL; | |
#endif | |
//------------------------------------ | |
threadedTexture::threadedTexture(){ | |
bCustomDraw = true; | |
pix = NULL; | |
wrapMode = GL_CLAMP_TO_EDGE; | |
setFlipTex( false ); | |
bTex2d = false; | |
bNeedsGLLoad = true; | |
} | |
void threadedTexture::clear(){ | |
if( pix != NULL ){ | |
pix->clear(); | |
delete pix; | |
pix = NULL; | |
} | |
ofTexture::clear(); | |
state = STATE_EMPTY; | |
} | |
bool threadedTexture::load(){ | |
//we need to grab the current context and create a new context. | |
//this just happens once - so don't worry :) | |
#if (TARGET_OS_IPHONE) | |
if(contextB == NULL){ | |
ofxiOSEAGLView * view = ofxiPhoneGetGLView(); | |
//from http://developer.apple.com/library/ios/qa/qa1612/_index.html | |
contextA = view.context; | |
contextB = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:contextA.sharegroup]; | |
if (!contextB || ![EAGLContext setCurrentContext:contextB]) { | |
cout << " error setting context " << endl; | |
}else{ | |
cout << " success context " << endl; | |
} | |
} | |
#endif | |
if( pix == NULL ){ | |
pix = new ofPixels(); | |
} | |
bool bLoadOk = ofLoadImage(*pix, filePath ); | |
//this is where the magic is - right now we are in a thread, but we switch to our second context and do the upload while staying in the thread. | |
#if (TARGET_OS_IPHONE) | |
if( contextB ){ | |
[EAGLContext setCurrentContext:contextB]; | |
loadGL(); | |
glFlush(); | |
// cout << " loading in thread!! " << endl; | |
[EAGLContext setCurrentContext:contextA]; | |
bNeedsGLLoad = false; | |
} | |
#endif | |
return bLoadOk; | |
} | |
void threadedTexture::loadGL(){ | |
if( pix ){ | |
int glType = GL_RGB; | |
if( pix->getImageType() == OF_IMAGE_COLOR_ALPHA ){ | |
glType = GL_RGBA; | |
}else if( pix->getImageType() == OF_IMAGE_GRAYSCALE ){ | |
glType = GL_LUMINANCE; | |
} | |
if(bTex2d) { | |
allocate(pix->getWidth(), pix->getHeight(), glType, false ); | |
loadData( *pix ); | |
setTextureWrap( wrapMode, wrapMode ); | |
} else { | |
allocate(pix->getWidth(), pix->getHeight(), glType ); | |
loadData( *pix ); | |
} | |
if(bFlipTex) { | |
getTextureData().bFlipTexture = true; | |
} | |
pix->clear(); | |
delete pix; | |
pix = NULL; | |
} | |
bCustomDraw = false; | |
} | |
bool threadedTexture::needsGLLoad(){ | |
return bNeedsGLLoad; | |
} | |
void threadedTexture::setTex2d( bool aB ) { | |
bTex2d = aB; | |
} | |
void threadedTexture::queueTextureWrap( GLint aWrap ) { | |
wrapMode = aWrap; | |
} | |
void threadedTexture::setFlipTex( bool aB ) { | |
bFlipTex = aB; | |
} | |
//------------------------------------ | |
void threadedTexture::draw(float x, float y){ | |
if(isReady()) | |
threadedTexture::draw(x,y,0,getWidth(),getHeight()); | |
} | |
//------------------------------------ | |
void threadedTexture::draw(float x, float y, float z){ | |
if(isReady()) | |
threadedTexture::draw(x,y,z,getWidth(),getHeight()); | |
} | |
//------------------------------------ | |
void threadedTexture::draw(float x, float y, float w, float h){ | |
threadedTexture::draw(x,y,0,w,h); | |
} | |
//------------------------------------ | |
void threadedTexture::drawScaledWidth(float x, float y, float z, float w){ | |
if( getWidth() == 0.0 )return; | |
float h = w * ( getHeight() / getWidth() ); | |
draw(x,y,z,w,h); | |
} | |
//------------------------------------ | |
void threadedTexture::drawScaledHeight(float x, float y, float z, float h){ | |
if( getHeight() == 0.0 )return; | |
float w = h * ( getWidth() / getHeight() ); | |
draw(x,y,z,w,h); | |
} | |
//------------------------------------ | |
void threadedTexture::drawScaledPercent(float x, float y, float z, float pct){ | |
draw(x,y,z,getWidth() * pct, getHeight() * pct); | |
} | |
//------------------------------------ | |
void threadedTexture::draw(float x, float y, float z, float w, float h){ | |
if(bCustomDraw){ | |
customDraw(x, y, z, w, h); | |
} | |
if(isReady()){ | |
ofTexture::draw(x,y,z,w,h); | |
} | |
} | |
//------------------------------------ | |
//overide this to make your own placeholder | |
void threadedTexture::customDraw(float x, float y, float z, float w, float h){ | |
if( w == 0.0 ) w = 128; | |
if( h == 0.0 ) h = 128; | |
ofPushStyle(); | |
ofSetColor(0,0,0); | |
ofFill(); | |
ofRect(x, y, z, w, h); | |
ofNoFill(); | |
ofSetColor(255, 255, 255); | |
ofRect(x, y, z, w, h); | |
ofPopStyle(); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
this should be an ADDON !!
super useful 5 stars out 5 would use again.