Cross-platform renderer on iOS: control the main loop from C++ class -
i’m porting opengl-based renderer (written in c++) ios , ran following problem:
on windows renderer-class has _renderproject (e.g. game i’m making) , _view object again has glfw window. loop in renderer follows:
void renderer::runrenderer() { while (_running && _view->isrunning()) { draw((_view->gettime() - _initialtime)); } }
the draw function looks this:
void renderer::draw(double currenttime) { [...] // call loop function of project if (_renderproject) _renderproject->loopfunction(currenttime - _elapsedtime, currenttime); [...] }
now want same thing ios don’t know how loop in renderer class. use cadisplaylink don't know how call c++ memeber function there.
update
following code written @kazuki sakamoto changed code this:
/* main loop using cadisplaylink */ typedef void (^block_t)(double); @interface renderercaller : nsobject { cadisplaylink *_displaylink; } @property (nonatomic, copy) block_t updateblock; @end @implementation renderercaller - (void)start:(block_t)block { self.updateblock = block; // check whether loop running if(_displaylink == nil) { // specify update method _displaylink = [cadisplaylink displaylinkwithtarget:self selector:@selector(update:)]; // add display link run loop (will called 60 times per second) [_displaylink addtorunloop:[nsrunloop currentrunloop] formode:nsdefaultrunloopmode]; } } - (void)stop { // check whether loop stopped if (_displaylink != nil) { // if display link present, gets invalidated (loop stops) [_displaylink invalidate]; _displaylink = nil; } } - (void)update:(cadisplaylink *)sender { double time = [sender timestamp]; updateblock(time); /// error, doesn't know "updateblock" } @end /* renderer */ void renderer::runrenderer() { if (_renderercaller == nil) _renderercaller = [[renderercaller alloc] init]; [_renderercaller start:^(double time) { this->draw(time); }]; } void renderer::stoprenderer() { [_renderercaller stop]; } void renderer::terminaterenderer() { stoprenderer(); _renderercaller = nil; [...] } void renderer::draw(double time) { [...] if (_renderproject) _renderproject->loopfunction(time - _elapsedtime, time); [...] }
i error in following line: updateblock(time);
update 2 had change _updateblock(time);
reason works! thank much! made day today :-)
you're there. create objective-c class call c++ method.
for example, cocos2d-x created ccdirectorcaller
objective-c class in objective-c++ source code (.mm file).
https://github.com/cocos2d/cocos2d-x/blob/v3/cocos/platform/ios/ccdirectorcaller-ios.mm
added
you want use cadisplaylink
opengl es render loop. , want use c++ main loop. right?
cadisplaylink
needs nsrunloop
. needs objective-c object, target
, selector
. thus, must create @ least 1 objective-c class cadisplaylink
.
see friday q&a 2010-01-01: nsrunloop internals.
so, there main nsrunloop
cadisplaylink
in main thread. infinite loop in main nsrunloop
not totally idea because nsrunloop
can't loop due infinite loop. obvious. thus, if want use c++ main loop, should create thread.
main thread other thread +-----------+ +-------------+ | | | | v | v | main nsrunloop | c++ main loop | | | | | | cadisplaylink | | | call target's method | | | | | | | | | | +-----------+ +-------------|
and if want use cadisplaylink
schedule rendering, need implement rendezvous thread synchronization pattern between main nsrunloop
, c++ main loop.
it's complicated enough. that's why recommend create objective-c class call c++ method, not c++ main loop.
i had make draw function of renderer public , renderer class singleton
not really.
// **pseudo code** // in objective-c++ source code typedef void (^block_t)(double); @interface renderercaller : nsobject @property (nonatomic, copy) block_t updateblock; @end @implementation renderercaller - (void)start:(block_t)block { self.updateblock = block; cadisplaylink *link = [cadisplaylink displaylinkwithtarget:self selector:@selector(update:)]; ... } - (void)update:(cadisplaylink *)sender { updateblock(elapsed time using sender); } @end void renderer::startmainloop() { renderercaller *renderercaller = [[renderercaller alloc] init]; [renderercaller start:^(double elapsed){ this->draw(elapsed); }]; }
Comments
Post a Comment