00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "renderserver.h"
00024 #include "api.h"
00025 #include "context.h"
00026 #include "paramset.h"
00027 #include "error.h"
00028
00029 #include <boost/version.hpp>
00030 #if (BOOST_VERSION < 103401)
00031 #include <boost/filesystem/operations.hpp>
00032 #else
00033 #include <boost/filesystem.hpp>
00034 #endif
00035 #include <boost/asio.hpp>
00036
00037 using namespace lux;
00038 using namespace boost::iostreams;
00039 using namespace boost::filesystem;
00040 using namespace std;
00041 using boost::asio::ip::tcp;
00042
00043
00044
00045
00046
00047 RenderServer::RenderServer(int tCount, int port) : threadCount(tCount),
00048 tcpPort(port), state(UNSTARTED), serverThread(NULL)
00049 {
00050 }
00051
00052 RenderServer::~RenderServer()
00053 {
00054 if ((state == READY) && (state == BUSY))
00055 stop();
00056 }
00057
00058 void RenderServer::start() {
00059 if (state != UNSTARTED) {
00060 stringstream ss;
00061 ss << "Can not start a rendering server in state: " << state;
00062 luxError(LUX_SYSTEM, LUX_ERROR, ss.str().c_str());
00063
00064 return;
00065 }
00066
00067
00068 serverThread = new NetworkRenderServerThread(this);
00069 serverThread->serverThread = new boost::thread(boost::bind(
00070 NetworkRenderServerThread::run, serverThread));
00071
00072 state = READY;
00073 }
00074
00075 void RenderServer::join()
00076 {
00077 if ((state != READY) && (state != BUSY)) {
00078 stringstream ss;
00079 ss << "Can not join a rendering server in state: " << state;
00080 luxError(LUX_SYSTEM, LUX_ERROR, ss.str().c_str());
00081
00082 return;
00083 }
00084
00085 serverThread->join();
00086 }
00087
00088 void RenderServer::stop()
00089 {
00090 if ((state != READY) && (state != BUSY)) {
00091 stringstream ss;
00092 ss << "Can not stop a rendering server in state: " << state;
00093 luxError(LUX_SYSTEM, LUX_ERROR, ss.str().c_str());
00094
00095 return;
00096 }
00097
00098 serverThread->interrupt();
00099 serverThread->join();
00100
00101 state = STOPPED;
00102 }
00103
00104
00105
00106
00107
00108 static void printInfoThread()
00109 {
00110 while (true) {
00111 boost::xtime xt;
00112 boost::xtime_get(&xt, boost::TIME_UTC);
00113 xt.sec += 5;
00114 boost::thread::sleep(xt);
00115
00116 boost::posix_time::time_duration td(0, 0,
00117 (int) luxStatistics("secElapsed"), 0);
00118
00119 int sampleSec = (int)luxStatistics("samplesSec");
00120
00121 if (sampleSec > 0) {
00122 stringstream ss;
00123 ss << td << " " << sampleSec << " samples/sec " << " "
00124 << (float) luxStatistics("samplesPx") << " samples/pix";
00125 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00126 }
00127 }
00128 }
00129
00130 static void processCommandFilm(void (&f)(const string &, const ParamSet &), basic_istream<char> &stream)
00131 {
00132 string type;
00133 getline(stream, type);
00134
00135 if((type != "fleximage") && (type != "multiimage")) {
00136 stringstream ss;
00137 ss << "Unsupported film type for server rendering: " << type;
00138 luxError(LUX_SYSTEM, LUX_ERROR, ss.str().c_str());
00139
00140 return;
00141 }
00142
00143 boost::archive::text_iarchive ia(stream);
00144 ParamSet params;
00145 ia >> params;
00146
00147
00148
00149 params.EraseBool("write_exr");
00150 params.EraseBool("write_exr_ZBuf");
00151 params.EraseBool("write_png");
00152 params.EraseBool("write_png_ZBuf");
00153 params.EraseBool("write_tga");
00154 params.EraseBool("write_tga_ZBuf");
00155 params.EraseBool("write_resume_flm");
00156
00157 bool no = false;
00158 params.AddBool("write_exr", &no);
00159 params.AddBool("write_exr_ZBuf", &no);
00160 params.AddBool("write_png", &no);
00161 params.AddBool("write_png_ZBuf", &no);
00162 params.AddBool("write_tga", &no);
00163 params.AddBool("write_tga_ZBuf", &no);
00164 params.AddBool("write_resume_flm", &no);
00165
00166 f(type.c_str(), params);
00167 }
00168
00169 static void processFile(const string &fileParam, ParamSet ¶ms, vector<string> &tmpFileList, basic_istream<char> &stream)
00170 {
00171 string originalFile = params.FindOneString(fileParam, "");
00172 if (originalFile.size()) {
00173
00174 string fileExt = "";
00175 size_t idx = originalFile.find_last_of('.');
00176 if (idx != string::npos)
00177 fileExt = originalFile.substr(idx);
00178
00179
00180 char buf[64];
00181 if (tmpFileList.size())
00182 snprintf(buf, 64, "%5s_%08u%s", tmpFileList[0].c_str(),
00183 (u_int)tmpFileList.size(), fileExt.c_str());
00184 else
00185 snprintf(buf, 64, "00000_%08u%s",
00186 (u_int)tmpFileList.size(), fileExt.c_str());
00187 string file = string(buf);
00188
00189
00190 params.AddString(fileParam, &file);
00191
00192
00193 string slen;
00194 getline(stream, slen);
00195 getline(stream, slen);
00196
00197 int len = atoi(slen.c_str());
00198
00199 stringstream ss("");
00200 ss << "Receiving file: '" << originalFile << "' (in '" <<
00201 file << "' size: " << (len / 1024) << " Kbytes)";
00202 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00203
00204
00205 if (len > 0) {
00206
00207 char *buf = new char[len];
00208 stream.read(buf, len);
00209
00210 ofstream out(file.c_str(), ios::out | ios::binary);
00211 out.write(buf, len);
00212 out.flush();
00213 tmpFileList.push_back(file);
00214
00215 if (out.fail()) {
00216 std::stringstream ss;
00217 ss << "There was an error while writing file '" << file << "'";
00218 luxError(LUX_SYSTEM, LUX_ERROR, ss.str().c_str());
00219 }
00220
00221 delete buf;
00222 }
00223 }
00224 }
00225
00226 static void processCommand(void (&f)(const string &, const ParamSet &), vector<string> &tmpFileList, basic_istream<char> &stream)
00227 {
00228 string type;
00229 getline(stream, type);
00230
00231 ParamSet params;
00232 boost::archive::text_iarchive ia(stream);
00233 ia >> params;
00234
00235 processFile("mapname", params, tmpFileList, stream);
00236 processFile("iesname", params, tmpFileList, stream);
00237
00238 f(type.c_str(), params);
00239 }
00240
00241 static void processCommand(void (&f)(const string &), basic_istream<char> &stream)
00242 {
00243 string type;
00244 getline(stream, type);
00245
00246 f(type.c_str());
00247 }
00248
00249 static void processCommand(void (&f)(float, float), basic_istream<char> &stream)
00250 {
00251 float ax, ay;
00252 stream >> ax;
00253 stream >> ay;
00254 f(ax, ay);
00255 }
00256
00257 static void processCommand(void (&f)(float, float, float), basic_istream<char> &stream)
00258 {
00259 float ax, ay, az;
00260 stream >> ax;
00261 stream >> ay;
00262 stream >> az;
00263 f(ax, ay, az);
00264 }
00265
00266 static void processCommand(void (&f)(float[16]), basic_istream<char> &stream)
00267 {
00268 float t[16];
00269 for (int i = 0; i < 16; ++i)
00270 stream >> t[i];
00271 f(t);
00272 }
00273
00274 static void processCommand(void (&f)(const string &, float, float, const string &), basic_istream<char> &stream)
00275 {
00276 string name, transform;
00277 float a, b;
00278
00279 stream >> name;
00280 stream >> a;
00281 stream >> b;
00282 stream >> transform;
00283
00284 f(name, a, b, transform);
00285 }
00286
00287
00288 string RenderServer::createNewSessionID() {
00289 char buf[4 * 4 + 4];
00290 sprintf(buf, "%04d_%04d_%04d_%04d", rand() % 9999, rand() % 9999,
00291 rand() % 9999, rand() % 9999);
00292
00293 return string(buf);
00294 }
00295
00296 bool RenderServer::validateAccess(basic_istream<char> &stream) const {
00297 if (serverThread->renderServer->state != RenderServer::BUSY)
00298 return false;
00299
00300 string sid;
00301 if (!getline(stream, sid))
00302 return false;
00303
00304 stringstream ss;
00305 ss << "Validating SID: " << sid << " = " << currentSID;
00306 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00307
00308 return (sid == currentSID);
00309 }
00310
00311
00312 void NetworkRenderServerThread::run(NetworkRenderServerThread *serverThread)
00313 {
00314
00315
00316 static const unsigned int CMD_LUXINIT = 2531407474U,
00317 CMD_LUXTRANSLATE = 2039302476U,
00318 CMD_LUXROTATE = 3982485453U,
00319 CMD_LUXSCALE = 1943523046U,
00320 CMD_LUXLOOKAT = 3747502632U,
00321 CMD_LUXCONCATTRANSFORM = 449663410U,
00322 CMD_LUXTRANSFORM = 2039102042U,
00323 CMD_LUXIDENTITY = 97907816U,
00324 CMD_LUXCOORDINATESYSTEM = 1707244427U,
00325 CMD_LUXCOORDSYSTRANSFORM = 2024449520U,
00326 CMD_LUXPIXELFILTER = 2384561510U,
00327 CMD_LUXFILM = 2531294310U,
00328 CMD_LUXSAMPLER = 3308802546U,
00329 CMD_LUXACCELERATOR = 1613429731U,
00330 CMD_LUXSURFACEINTEGRATOR = 4011931910U,
00331 CMD_LUXVOLUMEINTEGRATOR = 2954204437U,
00332 CMD_LUXCAMERA = 3378604391U,
00333 CMD_LUXWORLDBEGIN = 1247285547U,
00334 CMD_LUXATTRIBUTEBEGIN = 684297207U,
00335 CMD_LUXATTRIBUTEEND = 3427929065U,
00336 CMD_LUXTRANSFORMBEGIN = 567425599U,
00337 CMD_LUXTRANSFORMEND = 2773125169U,
00338 CMD_LUXTEXTURE = 475043887U,
00339 CMD_LUXMATERIAL = 4064803661U,
00340 CMD_LUXLIGHTGROUP = 770727715U,
00341 CMD_LUXMAKENAMEDMATERIAL = 2355625968U,
00342 CMD_LUXNAMEDMATERIAL = 922480690U,
00343 CMD_LUXLIGHTSOURCE = 130489799U,
00344 CMD_LUXAREALIGHTSOURCE = 515057184U,
00345 CMD_LUXPORTALSHAPE = 3416559329U,
00346 CMD_LUXSHAPE = 1943702863U,
00347 CMD_LUXREVERSEORIENTATION = 2027239206U,
00348 CMD_LUXVOLUME = 4138761078U,
00349 CMD_LUXOBJECTBEGIN = 1097337658U,
00350 CMD_LUXOBJECTEND = 229760620U,
00351 CMD_LUXOBJECTINSTANCE = 4125664042U,
00352 CMD_LUXWORLDEND = 1582674973U,
00353 CMD_LUXGETFILM = 859419430U,
00354 CMD_SERVER_DISCONNECT = 2500584742U,
00355 CMD_SERVER_CONNECT = 332355398U,
00356 CMD_VOID = 5381U,
00357 CMD_SPACE = 177605U,
00358 CMD_MOTIONINSTANCE = 4223946185U,
00359 CMD_LUXSETEPSILON = 3945573060U;
00360
00361 int listenPort = serverThread->renderServer->tcpPort;
00362 stringstream ss;
00363 ss << "Launching server [" << serverThread->renderServer->threadCount <<
00364 " threads] mode on port '" << listenPort << "'.";
00365 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00366
00367 vector<string> tmpFileList;
00368 try {
00369 boost::asio::io_service io_service;
00370 tcp::endpoint endpoint(tcp::v4(), listenPort);
00371 tcp::acceptor acceptor(io_service, endpoint);
00372
00373 while (true) {
00374 tcp::iostream stream;
00375 acceptor.accept(*stream.rdbuf());
00376 stream.setf(ios::scientific, ios::floatfield);
00377 stream.precision(16);
00378
00379
00380 string command;
00381 while (getline(stream, command)) {
00382 unsigned int hash = DJBHash(command);
00383
00384 if ((command != "") && (command != " ")) {
00385 ss.str("");
00386 ss << "Server processing command: '" << command <<
00387 "' (hash: " << hash << ")";
00388 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00389 }
00390
00391
00392 switch (hash) {
00393 case CMD_VOID:
00394 case CMD_SPACE:
00395 break;
00396 case CMD_SERVER_DISCONNECT:
00397 if (!serverThread->renderServer->validateAccess(stream))
00398 break;
00399
00400
00401 luxExit();
00402 luxWait();
00403 luxCleanup();
00404
00405
00406 for (size_t i = 1; i < tmpFileList.size(); i++)
00407 remove(tmpFileList[i]);
00408
00409 serverThread->renderServer->state = RenderServer::READY;
00410 break;
00411 case CMD_SERVER_CONNECT:
00412 if (serverThread->renderServer->state == RenderServer::READY) {
00413 serverThread->renderServer->state = RenderServer::BUSY;
00414 stream << "OK" << endl;
00415
00416
00417 serverThread->renderServer->currentSID =
00418 RenderServer::createNewSessionID();
00419 ss.str("");
00420 ss << "New session ID: " << serverThread->renderServer->currentSID;
00421 luxError(LUX_NOERROR, LUX_INFO, ss.str().c_str());
00422 stream << serverThread->renderServer->currentSID << endl;
00423
00424 tmpFileList.clear();
00425 char buf[6];
00426 snprintf(buf, 6, "%05d", listenPort);
00427 tmpFileList.push_back(string(buf));
00428 } else
00429 stream << "BUSY" << endl;
00430 break;
00431 case CMD_LUXINIT:
00432 luxError(LUX_BUG, LUX_SEVERE, "Server already initialized");
00433 break;
00434 case CMD_LUXTRANSLATE:
00435 processCommand(Context::luxTranslate, stream);
00436 break;
00437 case CMD_LUXROTATE: {
00438 float angle, ax, ay, az;
00439 stream >> angle;
00440 stream >> ax;
00441 stream >> ay;
00442 stream >> az;
00443 luxRotate(angle, ax, ay, az);
00444 break;
00445 }
00446 case CMD_LUXSCALE:
00447 processCommand(Context::luxScale, stream);
00448 break;
00449 case CMD_LUXLOOKAT: {
00450 float ex, ey, ez, lx, ly, lz, ux, uy, uz;
00451 stream >> ex;
00452 stream >> ey;
00453 stream >> ez;
00454 stream >> lx;
00455 stream >> ly;
00456 stream >> lz;
00457 stream >> ux;
00458 stream >> uy;
00459 stream >> uz;
00460 luxLookAt(ex, ey, ez, lx, ly, lz, ux, uy, uz);
00461 break;
00462 }
00463 case CMD_LUXCONCATTRANSFORM:
00464 processCommand(Context::luxConcatTransform, stream);
00465 break;
00466 case CMD_LUXTRANSFORM:
00467 processCommand(Context::luxTransform, stream);
00468 break;
00469 case CMD_LUXIDENTITY:
00470 Context::luxIdentity();
00471 break;
00472 case CMD_LUXCOORDINATESYSTEM:
00473 processCommand(Context::luxCoordinateSystem, stream);
00474 break;
00475 case CMD_LUXCOORDSYSTRANSFORM:
00476 processCommand(Context::luxCoordSysTransform, stream);
00477 break;
00478 case CMD_LUXPIXELFILTER:
00479 processCommand(Context::luxPixelFilter, tmpFileList, stream);
00480 break;
00481 case CMD_LUXFILM: {
00482
00483
00484
00485 processCommandFilm(Context::luxFilm, stream);
00486 break;
00487 }
00488 case CMD_LUXSAMPLER:
00489 processCommand(Context::luxSampler, tmpFileList, stream);
00490 break;
00491 case CMD_LUXACCELERATOR:
00492 processCommand(Context::luxAccelerator, tmpFileList, stream);
00493 break;
00494 case CMD_LUXSURFACEINTEGRATOR:
00495 processCommand(Context::luxSurfaceIntegrator, tmpFileList, stream);
00496 break;
00497 case CMD_LUXVOLUMEINTEGRATOR:
00498 processCommand(Context::luxVolumeIntegrator, tmpFileList, stream);
00499 break;
00500 case CMD_LUXCAMERA:
00501 processCommand(Context::luxCamera, tmpFileList, stream);
00502 break;
00503 case CMD_LUXWORLDBEGIN:
00504 Context::luxWorldBegin();
00505 break;
00506 case CMD_LUXATTRIBUTEBEGIN:
00507 Context::luxAttributeBegin();
00508 break;
00509 case CMD_LUXATTRIBUTEEND:
00510 Context::luxAttributeEnd();
00511 break;
00512 case CMD_LUXTRANSFORMBEGIN:
00513 Context::luxTransformBegin();
00514 break;
00515 case CMD_LUXTRANSFORMEND:
00516 Context::luxTransformEnd();
00517 break;
00518 case CMD_LUXTEXTURE: {
00519 string name, type, texname;
00520 ParamSet params;
00521
00522 getline(stream, name);
00523 getline(stream, type);
00524 getline(stream, texname);
00525
00526 boost::archive::text_iarchive ia(stream);
00527 ia >> params;
00528
00529 processFile("filename", params, tmpFileList, stream);
00530
00531 Context::luxTexture(name.c_str(), type.c_str(), texname.c_str(), params);
00532 break;
00533 }
00534 case CMD_LUXMATERIAL:
00535 processCommand(Context::luxMaterial, tmpFileList, stream);
00536 break;
00537 case CMD_LUXMAKENAMEDMATERIAL:
00538 processCommand(Context::luxMakeNamedMaterial, tmpFileList, stream);
00539 break;
00540 case CMD_LUXNAMEDMATERIAL:
00541 processCommand(Context::luxNamedMaterial, tmpFileList, stream);
00542 break;
00543 case CMD_LUXLIGHTGROUP:
00544 processCommand(Context::luxLightGroup, tmpFileList, stream);
00545 break;
00546 case CMD_LUXLIGHTSOURCE:
00547 processCommand(Context::luxLightSource, tmpFileList, stream);
00548 break;
00549 case CMD_LUXAREALIGHTSOURCE:
00550 processCommand(Context::luxAreaLightSource, tmpFileList, stream);
00551 break;
00552 case CMD_LUXPORTALSHAPE:
00553 processCommand(Context::luxPortalShape, tmpFileList, stream);
00554 break;
00555 case CMD_LUXSHAPE:
00556 processCommand(Context::luxShape, tmpFileList, stream);
00557 break;
00558 case CMD_LUXREVERSEORIENTATION:
00559 Context::luxReverseOrientation();
00560 break;
00561 case CMD_LUXVOLUME:
00562 processCommand(Context::luxVolume, tmpFileList, stream);
00563 break;
00564 case CMD_LUXOBJECTBEGIN:
00565 processCommand(Context::luxObjectBegin, stream);
00566 break;
00567 case CMD_LUXOBJECTEND:
00568 Context::luxObjectEnd();
00569 break;
00570 case CMD_LUXOBJECTINSTANCE:
00571 processCommand(Context::luxObjectInstance, stream);
00572 break;
00573 case CMD_MOTIONINSTANCE:
00574 processCommand(Context::luxMotionInstance, stream);
00575 break;
00576 case CMD_LUXWORLDEND: {
00577 serverThread->engineThread = new boost::thread(&luxWorldEnd);
00578
00579
00580 while (!luxStatistics("sceneIsReady")) {
00581 boost::xtime xt;
00582 boost::xtime_get(&xt, boost::TIME_UTC);
00583 xt.sec += 1;
00584 boost::thread::sleep(xt);
00585 }
00586
00587
00588 if(!serverThread->infoThread)
00589 serverThread->infoThread = new boost::thread(&printInfoThread);
00590
00591
00592 int threadsToAdd = serverThread->renderServer->threadCount;
00593 while (--threadsToAdd)
00594 Context::luxAddThread();
00595 break;
00596 }
00597 case CMD_LUXGETFILM: {
00598 if (!serverThread->renderServer->validateAccess(stream))
00599 break;
00600
00601
00602 if (serverThread->renderServer->state == RenderServer::BUSY) {
00603 luxError(LUX_NOERROR, LUX_INFO, "Transmitting film samples");
00604
00605 Context::luxTransmitFilm(stream);
00606 stream.close();
00607
00608 luxError(LUX_NOERROR, LUX_INFO, "Finished film samples transmission");
00609 } else {
00610 luxError(LUX_SYSTEM, LUX_ERROR, "Received a GetFilm command after a ServerDisconnect");
00611 stream.close();
00612 }
00613 break;
00614 }
00615 case CMD_LUXSETEPSILON:
00616 processCommand(Context::luxSetEpsilon, stream);
00617 break;
00618 default:
00619 ss.str("");
00620 ss << "Unknown command '" << command << "'. Ignoring";
00621 luxError(LUX_BUG, LUX_SEVERE, ss.str().c_str());
00622 break;
00623 }
00624
00625
00626 }
00627 }
00628 } catch (exception& e) {
00629 ss.str("");
00630 ss << "Internal error: " << e.what();
00631 luxError(LUX_BUG, LUX_ERROR, ss.str().c_str());
00632 }
00633 }