Made the lighting engine multithreaded.

develop
Japa 2013-06-26 19:12:14 +05:30 committed by Warmist
parent 354ec37046
commit 5b0cb86278
3 changed files with 167 additions and 45 deletions

@ -22,6 +22,7 @@ IF(UNIX)
SET(PROJECT_LIBS SET(PROJECT_LIBS
# add any extra linux libs here # add any extra linux libs here
lua lua
dfhack-tinythread
${PROJECT_LIBS} ${PROJECT_LIBS}
) )
# windows # windows
@ -29,6 +30,7 @@ ELSE(UNIX)
SET(PROJECT_LIBS SET(PROJECT_LIBS
# add any extra windows libs here # add any extra windows libs here
lua lua
dfhack-tinythread
${PROJECT_LIBS} ${PROJECT_LIBS}
$(NOINHERIT) $(NOINHERIT)
) )

@ -4,6 +4,7 @@
#include <string> #include <string>
#include <math.h> #include <math.h>
#include "tinythread.h"
#include "LuaTools.h" #include "LuaTools.h"
@ -23,13 +24,103 @@
#include "df/plant.h" #include "df/plant.h"
#include "df/plant_raw.h" #include "df/plant_raw.h"
#include <vector>
using df::global::gps; using df::global::gps;
using namespace DFHack; using namespace DFHack;
using df::coord2d; using df::coord2d;
using namespace tthread;
const float RootTwo = 1.4142135623730950488016887242097f; const float RootTwo = 1.4142135623730950488016887242097f;
void lightingEngineViewscreen::lightWorkerThread(void * arg)
{
int thisIndex;
std::vector<lightCell> 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));
}
lightingEngineViewscreen::~lightingEngineViewscreen()
{
indexMutex.lock();
nextIndex = -2;
indexMutex.unlock();
writeMutex.unlock();
for(int i = 0; i < threadList.size(); i++)
{
if(threadList[i])
threadList[i]->join();
}
}
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) lightSource::lightSource(lightCell power,int radius):power(power),flicker(false)
{ {
if(radius >= 0) if(radius >= 0)
@ -89,6 +180,7 @@ lightingEngineViewscreen::lightingEngineViewscreen(renderer_light* target):light
reinit(); reinit();
defaultSettings(); defaultSettings();
loadSettings(); loadSettings();
writeMutex.lock(); //This is needed for later when the threads will all want to write to the buffer.
} }
void lightingEngineViewscreen::reinit() void lightingEngineViewscreen::reinit()
@ -147,11 +239,15 @@ void plotLine(int x0, int y0, int x1, int y1,std::function<bool(int,int,int,int)
if (e2 <= dx) { err += dx; y0 += sy; rdy=sy;} /* e_xy+e_y < 0 */ if (e2 <= dx) { err += dx; y0 += sy; rdy=sy;} /* e_xy+e_y < 0 */
} }
} }
lightCell blend(lightCell a,lightCell b) lightCell blendMax(lightCell a,lightCell b)
{ {
return lightCell(std::max(a.r,b.r),std::max(a.g,b.g),std::max(a.b,b.b)); return lightCell(std::max(a.r,b.r),std::max(a.g,b.g),std::max(a.b,b.b));
} }
bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx,int ty) lightCell blend(lightCell a,lightCell b)
{
return blendMax(a,b);
}
bool lightingEngineViewscreen::lightUpCell(std::vector<lightCell> & target, lightCell& power,int dx,int dy,int tx,int ty)
{ {
if(isInViewport(coord2d(tx,ty),mapPort)) if(isInViewport(coord2d(tx,ty),mapPort))
{ {
@ -182,9 +278,9 @@ bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx
return false; return false;
} }
//float dt=sqrt(dsq); //float dt=sqrt(dsq);
lightCell oldCol=lightMap[tile]; lightCell oldCol=target[tile];
lightCell ncol=blend(power,oldCol); lightCell ncol=blendMax(power,oldCol);
lightMap[tile]=ncol; target[tile]=ncol;
if(wallhack) if(wallhack)
return false; return false;
@ -194,23 +290,22 @@ bool lightingEngineViewscreen::lightUpCell(lightCell& power,int dx,int dy,int tx
else else
return false; return false;
} }
void lightingEngineViewscreen::doRay(lightCell power,int cx,int cy,int tx,int ty) void lightingEngineViewscreen::doRay(std::vector<lightCell> & target, lightCell power,int cx,int cy,int tx,int ty)
{ {
using namespace std::placeholders; using namespace std::placeholders;
lightCell curPower=power; lightCell curPower=power;
plotLine(cx,cy,tx,ty,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(curPower),_1,_2,_3,_4)); plotLine(cx,cy,tx,ty,std::bind(&lightingEngineViewscreen::lightUpCell,this,std::ref(target),std::ref(curPower),_1,_2,_3,_4));
} }
void lightingEngineViewscreen::doFovs()
{
mapPort=getMapViewport();
using namespace std::placeholders;
for(int i=mapPort.first.x;i<mapPort.second.x;i++) void lightingEngineViewscreen::doLight(std::vector<lightCell> & target, int index)
for(int j=mapPort.first.y;j<mapPort.second.y;j++)
{ {
lightSource& csource=lights[getIndex(i,j)]; using namespace std::placeholders;
lightSource& csource=lights[index];
if(csource.radius>0) if(csource.radius>0)
{ {
coord2d coord = getCoords(index);
int i = coord.x;
int j = coord.y;
lightCell power=csource.power; lightCell power=csource.power;
int radius =csource.radius; int radius =csource.radius;
if(csource.flicker) if(csource.flicker)
@ -222,23 +317,30 @@ void lightingEngineViewscreen::doFovs()
int surrounds = 0; int surrounds = 0;
lightCell curPower; lightCell curPower;
lightUpCell(curPower = power, 0, 0,i+0, j+0); lightUpCell(target, curPower = power, 0, 0,i+0, j+0);
{ {
surrounds += lightUpCell(curPower = power, 0, 1,i+0, j+1); surrounds += lightUpCell(target, curPower = power, 0, 1,i+0, j+1);
surrounds += lightUpCell(curPower = power, 1, 1,i+1, j+1); surrounds += lightUpCell(target, curPower = power, 1, 1,i+1, j+1);
surrounds += lightUpCell(curPower = power, 1, 0,i+1, j+0); surrounds += lightUpCell(target, curPower = power, 1, 0,i+1, j+0);
surrounds += lightUpCell(curPower = power, 1,-1,i+1, j-1); surrounds += lightUpCell(target, curPower = power, 1,-1,i+1, j-1);
surrounds += lightUpCell(curPower = power, 0,-1,i+0, j-1); surrounds += lightUpCell(target, curPower = power, 0,-1,i+0, j-1);
surrounds += lightUpCell(curPower = power,-1,-1,i-1, j-1); surrounds += lightUpCell(target, curPower = power,-1,-1,i-1, j-1);
surrounds += lightUpCell(curPower = power,-1, 0,i-1, j+0); surrounds += lightUpCell(target, curPower = power,-1, 0,i-1, j+0);
surrounds += lightUpCell(curPower = power,-1, 1,i-1, j+1); surrounds += lightUpCell(target, curPower = power,-1, 1,i-1, j+1);
} }
if(surrounds) if(surrounds)
{
plotSquare(i,j,radius, plotSquare(i,j,radius,
std::bind(&lightingEngineViewscreen::doRay,this,power,i,j,_1,_2)); std::bind(&lightingEngineViewscreen::doRay,this,std::ref(target),power,i,j,_1,_2));
} }
} }
} }
void lightingEngineViewscreen::doFovs()
{
mapPort=getMapViewport();
doLightThreads();
}
void lightingEngineViewscreen::clear() void lightingEngineViewscreen::clear()
{ {
lightMap.assign(lightMap.size(),lightCell(1,1,1)); lightMap.assign(lightMap.size(),lightCell(1,1,1));

@ -124,7 +124,7 @@ class lightingEngineViewscreen:public lightingEngine
{ {
public: public:
lightingEngineViewscreen(renderer_light* target); lightingEngineViewscreen(renderer_light* target);
~lightingEngineViewscreen();
void reinit(); void reinit();
void calculate(); void calculate();
@ -140,9 +140,10 @@ private:
void doSun(const lightSource& sky,MapExtras::MapCache& map); void doSun(const lightSource& sky,MapExtras::MapCache& map);
void doOcupancyAndLights(); void doOcupancyAndLights();
lightCell propogateSun(MapExtras::Block* b, int x,int y,const lightCell& in,bool lastLevel); lightCell propogateSun(MapExtras::Block* b, int x,int y,const lightCell& in,bool lastLevel);
void doRay(lightCell power,int cx,int cy,int tx,int ty); void doRay(std::vector<lightCell> & target, lightCell power,int cx,int cy,int tx,int ty);
void doFovs(); void doFovs();
bool lightUpCell(lightCell& power,int dx,int dy,int tx,int ty); void doLight(std::vector<lightCell> & target, int index);
bool lightUpCell(std::vector<lightCell> & target, lightCell& power,int dx,int dy,int tx,int ty);
bool addLight(int tileId,const lightSource& light); bool addLight(int tileId,const lightSource& light);
matLightDef* getMaterial(int matType,int matIndex); matLightDef* getMaterial(int matType,int matIndex);
@ -154,10 +155,25 @@ private:
{ {
return x*h+y; return x*h+y;
} }
df::coord2d inline getCoords(int index)
{
return df::coord2d(index/h, index%h);
}
//maps //maps
std::vector<lightCell> lightMap; std::vector<lightCell> lightMap;
std::vector<lightCell> lightMap2;
std::vector<lightCell> ocupancy; std::vector<lightCell> ocupancy;
std::vector<lightSource> lights; std::vector<lightSource> lights;
//Threading stuff
tthread::mutex indexMutex;
tthread::mutex writeMutex;
int nextIndex;
std::vector<tthread::thread *> threadList;
void doLightThreads();
public:
void lightWorkerThread(void * arg);
private:
//settings //settings
///set up sane settings if setting file does not exist. ///set up sane settings if setting file does not exist.
@ -180,4 +196,6 @@ private:
int w,h; int w,h;
DFHack::rect2d mapPort; DFHack::rect2d mapPort;
}; };
lightCell blend(lightCell a,lightCell b);
lightCell blendMax(lightCell a,lightCell b);
#endif #endif