From ee1bad84fcc10a5a93d1dc418930dbbbe6b25e03 Mon Sep 17 00:00:00 2001 From: Warmist Date: Sat, 29 Jun 2013 20:55:07 +0300 Subject: [PATCH] Threading rewrite. --- plugins/rendermax/renderer_light.cpp | 424 +++++++++++++++------------ plugins/rendermax/renderer_light.hpp | 66 ++++- 2 files changed, 292 insertions(+), 198 deletions(-) diff --git a/plugins/rendermax/renderer_light.cpp b/plugins/rendermax/renderer_light.cpp index 1ad9ad24f..919e411e1 100644 --- a/plugins/rendermax/renderer_light.cpp +++ b/plugins/rendermax/renderer_light.cpp @@ -35,93 +35,18 @@ using namespace tthread; const float RootTwo = 1.4142135623730950488016887242097f; -void lightingEngineViewscreen::lightWorkerThread(void * arg) +bool isInRect(const coord2d& pos,const rect2d& rect) { - int thisIndex; - std::vector canvas; - while(1) - { - writeMutex.lock(); - writeMutex.unlock(); //Don't start till write access is given. - indexMutex.lock(); - if(nextIndex == -1) //The worker threads should keep going until, and including, index 0. - { - indexMutex.unlock(); - break; - } - else if(nextIndex == -2) - { - indexMutex.unlock(); - return; - } - else - { - thisIndex = nextIndex; - nextIndex--; - indexMutex.unlock(); - if(canvas.size() != lightMap.size()) - { - canvas.resize(lightMap.size(), lightCell(0,0,0)); - } - doLight(canvas, thisIndex); - - } - } - writeMutex.lock(); - for(int i = 0; i < canvas.size(); i++) - { - lightMap[i] = blend(lightMap[i], canvas[i]); - - } - writeMutex.unlock(); - canvas.assign(canvas.size(),lightCell(0,0,0)); + if(pos.x>=rect.first.x && pos.y>=rect.first.y && pos.xjoin(); - } + threading.shutdown(); } -void threadStub(void * arg) -{ - if(arg) - ((lightingEngineViewscreen*)arg)->lightWorkerThread(0); -} - -void lightingEngineViewscreen::doLightThreads() -{ - nextIndex = 0; - int num_threads = thread::hardware_concurrency(); - if(num_threads < 1) num_threads = 1; - if(threadList.empty()) - { - threadList.resize(num_threads, NULL); - } - for(int i = 0; i < num_threads; i++) - { - threadList[i] = new thread(threadStub, this); - } - nextIndex = lightMap.size() - 1; //start at the largest valid index - writeMutex.unlock(); - for(int i = 0; i < num_threads; i++) - { - threadList[i]->join(); - delete threadList[i]; - threadList[i]=0; - } - writeMutex.lock(); -} - - - lightSource::lightSource(lightCell power,int radius):power(power),flicker(false) { if(radius >= 0) @@ -176,12 +101,14 @@ rect2d getMapViewport() } return mkrect_wh(1,1,view_rb,view_height+1); } -lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target),doDebug(false) +lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):lightingEngine(target),doDebug(false),threading(this) { reinit(); defaultSettings(); loadSettings(); - writeMutex.lock(); //This is needed for later when the threads will all want to write to the buffer. + int numTreads=tthread::thread::hardware_concurrency(); + if(numTreads==0)numTreads=1; + threading.start(numTreads); } void lightingEngineViewscreen::reinit() @@ -301,100 +228,6 @@ lightCell blend(lightCell a,lightCell b) { return blendMax(a,b); } -lightCell lightingEngineViewscreen::lightUpCell(std::vector & target,lightCell power,int dx,int dy,int tx,int ty) -{ - if(isInViewport(coord2d(tx,ty),mapPort)) - { - size_t tile=getIndex(tx,ty); - int dsq=dx*dx+dy*dy; - float dt=1; - if(dsq == 1) - dt=1; - else if(dsq == 2) - dt = RootTwo; - else if(dsq == 0) - dt = 0; - else - dt=sqrt((float)dsq); - lightCell& v=ocupancy[tile]; - lightSource& ls=lights[tile]; - bool wallhack=false; - if(v.r+v.g+v.b==0) - wallhack=true; - - if (dsq>0 && !wallhack) - { - power*=v.pow(dt); - } - if(ls.radius>0 && dsq>0) - { - if(power<=ls.power) - return lightCell(); - } - //float dt=sqrt(dsq); - lightCell oldCol=target[tile]; - lightCell ncol=blendMax(power,oldCol); - target[tile]=ncol; - - if(wallhack) - return lightCell(); - - - return power; - } - else - return lightCell(); -} -void lightingEngineViewscreen::doRay(std::vector & target, lightCell power,int cx,int cy,int tx,int ty) -{ - using namespace std::placeholders; - - plotLine(cx,cy,tx,ty,power,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(target),_1,_2,_3,_4,_5)); -} -void lightingEngineViewscreen::doLight(std::vector & target, int index) -{ - using namespace std::placeholders; - lightSource& csource=lights[index]; - if(csource.radius>0) - { - coord2d coord = getCoords(index); - int i = coord.x; - int j = coord.y; - lightCell power=csource.power; - int radius =csource.radius; - if(csource.flicker) - { - float flicker=(rand()/(float)RAND_MAX)/2.0f+0.5f; - radius*=flicker; - power=power*flicker; - } - lightCell surrounds; - - - lightUpCell(target,power, 0, 0,i+0, j+0); - { - surrounds += lightUpCell(target, power, 0, 1,i+0, j+1); - surrounds += lightUpCell(target, power, 1, 1,i+1, j+1); - surrounds += lightUpCell(target, power, 1, 0,i+1, j+0); - surrounds += lightUpCell(target, power, 1,-1,i+1, j-1); - surrounds += lightUpCell(target, power, 0,-1,i+0, j-1); - surrounds += lightUpCell(target, power,-1,-1,i-1, j-1); - surrounds += lightUpCell(target, power,-1, 0,i-1, j+0); - surrounds += lightUpCell(target, power,-1, 1,i-1, j+1); - } - if(surrounds.dot(surrounds)>0.00001f) - { - plotSquare(i,j,radius, - std::bind(&lightingEngineViewscreen::doRay,this,std::ref(target),power,i,j,_1,_2)); - } - } -} - -void lightingEngineViewscreen::doFovs() -{ - mapPort=getMapViewport(); - doLightThreads(); -} void lightingEngineViewscreen::clear() { lightMap.assign(lightMap.size(),lightCell(1,1,1)); @@ -417,7 +250,8 @@ void lightingEngineViewscreen::calculate() lightMap[getIndex(i,j)]=dim; } doOcupancyAndLights(); - doFovs(); + threading.signalDoneOcclusion(); + threading.waitForWrites(); } void lightingEngineViewscreen::updateWindow() { @@ -586,12 +420,7 @@ coord2d lightingEngineViewscreen::worldToViewportCoord(const coord2d& in,const r { return in-window2d+r.first; } -bool lightingEngineViewscreen::isInViewport(const coord2d& in,const rect2d& r) -{ - if(in.x>=r.first.x && in.y>=r.first.y && in.x0.003f) + if(isInRect(pos,vp) && curCell.dot(curCell)>0.003f) { lightSource sun=lightSource(curCell,15); addLight(getIndex(pos.x,pos.y),sun); @@ -719,7 +548,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() pos.y = blockY*16+block_y; df::coord2d gpos=pos; pos=worldToViewportCoord(pos,vp,window2d); - if(!isInViewport(pos,vp)) + if(!isInRect(pos,vp)) continue; int tile=getIndex(pos.x,pos.y); lightCell& curCell=ocupancy[tile]; @@ -790,7 +619,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::coord2d pos=f->pos; pos=worldToViewportCoord(pos,vp,window2d); int tile=getIndex(pos.x,pos.y); - if(isInViewport(pos,vp)) + if(isInRect(pos,vp)) { lightCell fireColor; if(f->density>60) @@ -819,7 +648,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::coord2d pos=cPlant->pos; pos=worldToViewportCoord(pos,vp,window2d); int tile=getIndex(pos.x,pos.y); - if(isInViewport(pos,vp)) + if(isInRect(pos,vp)) { applyMaterial(tile,419,cPlant->material); } @@ -847,7 +676,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() if(amount<=0) continue; pos=worldToViewportCoord(pos,vp,window2d); - if(isInViewport(pos,vp)) + if(isInRect(pos,vp)) { addLight(getIndex(pos.x,pos.y),m->makeSource((float)amount/100)); } @@ -868,7 +697,7 @@ void lightingEngineViewscreen::doOcupancyAndLights() { df::unit *u = df::global::world->units.active[i]; coord2d pos=worldToViewportCoord(coord2d(u->pos.x,u->pos.y),vp,window2d); - if(u->pos.z==window_z && isInViewport(pos,vp)) + if(u->pos.z==window_z && isInRect(pos,vp)) if (DFHack::Units::isCitizen(u) && !u->counters.unconscious) addLight(getIndex(pos.x,pos.y),matCitizen.makeSource()); } @@ -886,11 +715,11 @@ void lightingEngineViewscreen::doOcupancyAndLights() df::coord2d p2(bld->x2,bld->y2); p1=worldToViewportCoord(p1,vp,window2d); p2=worldToViewportCoord(p2,vp,window2d); - if(isInViewport(p1,vp)||isInViewport(p2,vp)) + if(isInRect(p1,vp)||isInRect(p2,vp)) { int tile; - if(isInViewport(p1,vp)) + if(isInRect(p1,vp)) tile=getIndex(p1.x,p1.y); //TODO multitile buildings. How they would work? else tile=getIndex(p2.x,p2.y); @@ -1197,3 +1026,216 @@ void lightingEngineViewscreen::loadSettings() } #undef GETLUAFLAG #undef GETLUANUMBER +/* + * Threading stuff + */ +lightThread::lightThread( lightThreadDispatch& dispatch ):dispatch(dispatch),isDone(false),myThread(0) +{ + +} +lightThread::~lightThread() +{ + if(myThread) + delete myThread; +} + +void lightThread::run() +{ + while(!isDone) + { + { + tthread::lock_guard guard(dispatch.occlusionMutex); + dispatch.occlusionDone.wait(dispatch.occlusionMutex);//wait for work + if(dispatch.unprocessed.size()==0) //spurious wake-up + continue; + if(dispatch.occlusion.size()!=canvas.size()) //oh no somebody resized stuff + canvas.resize(dispatch.occlusion.size()); + } + + + { //get my rectangle (any will do) + tthread::lock_guard guard(dispatch.unprocessedMutex); + myRect=dispatch.unprocessed.top(); + dispatch.unprocessed.pop(); + } + work(); + { + tthread::lock_guard guard(dispatch.writeLock); + combine();//write it back + dispatch.writeCount++; + } + dispatch.writesDone.notify_one();//tell about it to the dispatch. + } +} + +void lightThread::work() +{ + canvas.assign(canvas.size(),lightCell(0,0,0)); + for(int i=myRect.first.x;i0 && !wallhack) + { + power*=v.pow(dt); + } + if(ls.radius>0 && dsq>0) + { + if(power<=ls.power) + return lightCell(); + } + //float dt=sqrt(dsq); + lightCell oldCol=canvas[tile]; + lightCell ncol=blendMax(power,oldCol); + canvas[tile]=ncol; + + if(wallhack) + return lightCell(); + + + return power; + } + else + return lightCell(); +} +void lightThread::doRay(lightCell power,int cx,int cy,int tx,int ty) +{ + using namespace std::placeholders; + + plotLine(cx,cy,tx,ty,power,std::bind(&lightThread::lightUpCell,this,_1,_2,_3,_4,_5)); +} + +void lightThread::doLight( int x,int y ) +{ + using namespace std::placeholders; + lightSource& csource=dispatch.lights[x*dispatch.getH()+y]; + if(csource.radius>0) + { + lightCell power=csource.power; + int radius =csource.radius; + if(csource.flicker) + { + float flicker=(rand()/(float)RAND_MAX)/2.0f+0.5f; + radius*=flicker; + power=power*flicker; + } + lightCell surrounds; + lightUpCell( power, 0, 0,x, y); + for(int i=-1;i<2;i++) + for(int j=-1;j<2;j++) + if(i!=0||j!=0) + surrounds += lightUpCell( power, i, j,x+i, y+j); + if(surrounds.dot(surrounds)>0.00001f) + { + plotSquare(x,y,radius, + std::bind(&lightThread::doRay,this,power,x,y,_1,_2)); + } + } +} +void lightThreadDispatch::signalDoneOcclusion() +{ + { + tthread::lock_guard guardWrite(writeLock); + writeCount=0; + } + tthread::lock_guard guard(occlusionMutex); + + viewPort=getMapViewport(); + int threadCount=threadPool.size(); + int w=viewPort.second.x-viewPort.first.x; + int slicew=w/threadCount; + for(int i=0;ilights),occlusion(parent->ocupancy),lightMap(parent->lightMap),writeCount(0) +{ + +} + +void lightThreadDispatch::shutdown() +{ + for(int i=0;iisDone=true; + threadPool[i]->myThread->join(); + } + threadPool.clear(); +} + +int lightThreadDispatch::getW() +{ + return parent->getW(); +} + +int lightThreadDispatch::getH() +{ + return parent->getH(); +} +void threadStub(void * arg) +{ + if(arg) + ((lightThread*)arg)->run(); +} +void lightThreadDispatch::start(int count) +{ + for(int i=0;i nthread(new lightThread(*this)); + nthread->myThread=new tthread::thread(&threadStub,nthread.get()); + threadPool.push_back(std::move(nthread)); + } +} + +void lightThreadDispatch::waitForWrites() +{ + tthread::lock_guard guard(writeLock); + while(threadPool.size()>writeCount)//missed it somehow already. + { + writesDone.wait(writeLock); //if not, wait a bit + } +} diff --git a/plugins/rendermax/renderer_light.hpp b/plugins/rendermax/renderer_light.hpp index 5466ff01f..d14bbc33f 100644 --- a/plugins/rendermax/renderer_light.hpp +++ b/plugins/rendermax/renderer_light.hpp @@ -4,8 +4,10 @@ #include "Types.h" #include #include +#include +#include #include "modules/MapCache.h" - +bool isInRect(const df::coord2d& pos,const DFHack::rect2d& rect); struct renderer_light : public renderer_wrap { private: void colorizeTile(int x,int y) @@ -130,6 +132,56 @@ struct buildingLightDef float size; buildingLightDef():poweredOnly(false),useMaterial(true),thickness(1.0f),size(1.0f){} }; +class lightThread; +class lightingEngineViewscreen; +class lightThreadDispatch +{ + lightingEngineViewscreen *parent; +public: + DFHack::rect2d viewPort; + + std::vector > threadPool; + std::vector& lights; + + tthread::mutex occlusionMutex; + tthread::condition_variable occlusionDone; //all threads wait for occlusion to finish + tthread::mutex unprocessedMutex; + std::stack unprocessed; //stack of parts of map where lighting is not finished + std::vector& occlusion; + + tthread::mutex writeLock; //mutex for lightMap + std::vector& lightMap; + + tthread::condition_variable writesDone; + int writeCount; + + lightThreadDispatch(lightingEngineViewscreen* p); + void signalDoneOcclusion(); + void shutdown(); + void waitForWrites(); + + int getW(); + int getH(); + void start(int count); +}; +class lightThread +{ + std::vector canvas; + lightThreadDispatch& dispatch; + DFHack::rect2d myRect; + void work(); //main light calculation function + void combine(); //combine existing canvas into global lightmap +public: + tthread::thread *myThread; + bool isDone; //no mutex, because bool is atomic + lightThread(lightThreadDispatch& dispatch); + ~lightThread(); + void run(); +private: + void doLight(int x,int y); + void doRay(lightCell power,int cx,int cy,int tx,int ty); + lightCell lightUpCell(lightCell power,int dx,int dy,int tx,int ty); +}; class lightingEngineViewscreen:public lightingEngine { public: @@ -147,7 +199,7 @@ public: private: df::coord2d worldToViewportCoord(const df::coord2d& in,const DFHack::rect2d& r,const df::coord2d& window2d) ; - bool isInViewport(const df::coord2d& in,const DFHack::rect2d& r); + void doSun(const lightSource& sky,MapExtras::MapCache& map); void doOcupancyAndLights(); @@ -181,13 +233,12 @@ private: std::vector lights; //Threading stuff - tthread::mutex indexMutex; - tthread::mutex writeMutex; - int nextIndex; - std::vector threadList; - void doLightThreads(); + lightThreadDispatch threading; //misc void setHour(float h){dayHour=h;}; + + int getW()const {return w;} + int getH()const {return h;} public: void lightWorkerThread(void * arg); private: @@ -219,6 +270,7 @@ private: std::map,buildingLightDef> buildingDefs; int w,h; DFHack::rect2d mapPort; + friend lightThreadDispatch; }; lightCell blend(lightCell a,lightCell b); lightCell blendMax(lightCell a,lightCell b);