int mitsuba_app(int argc, char **argv) { int optchar; char *end_ptr = NULL; try { /* Default settings */ int nprocs_avail = getCoreCount(), nprocs = nprocs_avail; int numParallelScenes = 1; std::string nodeName = getHostName(), networkHosts = "", destFile=""; bool quietMode = false, progressBars = true, skipExisting = false; ELogLevel logLevel = EInfo; ref<FileResolver> fileResolver = Thread::getThread()->getFileResolver(); bool treatWarningsAsErrors = false; std::map<std::string, std::string, SimpleStringOrdering> parameters; int blockSize = 32; int flushTimer = -1; if (argc < 2) { help(); return 0; } optind = 1; /* Parse command-line arguments */ while ((optchar = getopt(argc, argv, "a:c:D:s:j:n:o:r:b:p:qhzvtwx")) != -1) { switch (optchar) { case 'a': { std::vector<std::string> paths = tokenize(optarg, ";"); for (int i=(int) paths.size()-1; i>=0; --i) fileResolver->prependPath(paths[i]); } break; case 'c': networkHosts = networkHosts + std::string(";") + std::string(optarg); break; case 'w': treatWarningsAsErrors = true; break; case 'D': { std::vector<std::string> param = tokenize(optarg, "="); if (param.size() != 2) SLog(EError, "Invalid parameter specification \"%s\"", optarg); parameters[param[0]] = param[1]; } break; case 's': { std::ifstream is(optarg); if (is.fail()) SLog(EError, "Could not open host file!"); std::string host; while (is >> host) { if (host.length() < 1 || host.c_str()[0] == '#') continue; networkHosts = networkHosts + std::string(";") + host; } } break; case 'n': nodeName = optarg; break; case 'o': destFile = optarg; break; case 'v': if (logLevel != EDebug) logLevel = EDebug; else logLevel = ETrace; break; case 'x': skipExisting = true; break; case 'p': nprocs = strtol(optarg, &end_ptr, 10); if (*end_ptr != '\0') SLog(EError, "Could not parse the processor count!"); break; case 'j': numParallelScenes = strtol(optarg, &end_ptr, 10); if (*end_ptr != '\0') SLog(EError, "Could not parse the parallel scene count!"); break; case 'r': flushTimer = strtol(optarg, &end_ptr, 10); if (*end_ptr != '\0') SLog(EError, "Could not parse the '-r' parameter argument!"); break; case 'b': blockSize = strtol(optarg, &end_ptr, 10); if (*end_ptr != '\0') SLog(EError, "Could not parse the block size!"); if (blockSize < 2 || blockSize > 128) SLog(EError, "Invalid block size (should be in the range 2-128)"); break; case 'z': progressBars = false; break; case 'q': quietMode = true; break; case 'h': default: help(); return 0; } } ProgressReporter::setEnabled(progressBars); /* Initialize OpenMP */ Thread::initializeOpenMP(nprocs); /* Configure the logging subsystem */ ref<Logger> log = Thread::getThread()->getLogger(); log->setLogLevel(logLevel); log->setErrorLevel(treatWarningsAsErrors ? EWarn : EError); /* Disable the default appenders */ for (size_t i=0; i<log->getAppenderCount(); ++i) { Appender *appender = log->getAppender(i); if (appender->getClass()->derivesFrom(MTS_CLASS(StreamAppender))) log->removeAppender(appender); } log->addAppender(new StreamAppender(formatString("mitsuba.%s.log", nodeName.c_str()))); if (!quietMode) log->addAppender(new StreamAppender(&std::cout)); SLog(EInfo, "Mitsuba version %s, Copyright (c) " MTS_YEAR " Wenzel Jakob", Version(MTS_VERSION).toStringComplete().c_str()); /* Configure the scheduling subsystem */ Scheduler *scheduler = Scheduler::getInstance(); bool useCoreAffinity = nprocs == nprocs_avail; for (int i=0; i<nprocs; ++i) scheduler->registerWorker(new LocalWorker(useCoreAffinity ? i : -1, formatString("wrk%i", i))); std::vector<std::string> hosts = tokenize(networkHosts, ";"); /* Establish network connections to nested servers */ for (size_t i=0; i<hosts.size(); ++i) { const std::string &hostName = hosts[i]; ref<Stream> stream; if (hostName.find("@") == std::string::npos) { int port = MTS_DEFAULT_PORT; std::vector<std::string> tokens = tokenize(hostName, ":"); if (tokens.size() == 0 || tokens.size() > 2) { SLog(EError, "Invalid host specification '%s'!", hostName.c_str()); } else if (tokens.size() == 2) { port = strtol(tokens[1].c_str(), &end_ptr, 10); if (*end_ptr != '\0') SLog(EError, "Invalid host specification '%s'!", hostName.c_str()); } stream = new SocketStream(tokens[0], port); } else { std::string path = "~/mitsuba"; // default path if not specified std::vector<std::string> tokens = tokenize(hostName, "@:"); if (tokens.size() < 2 || tokens.size() > 3) { SLog(EError, "Invalid host specification '%s'!", hostName.c_str()); } else if (tokens.size() == 3) { path = tokens[2]; } std::vector<std::string> cmdLine; cmdLine.push_back(formatString("bash -c 'cd %s; . setpath.sh; mtssrv -ls'", path.c_str())); stream = new SSHStream(tokens[0], tokens[1], cmdLine); } try { scheduler->registerWorker(new RemoteWorker(formatString("net%i", i), stream)); } catch (std::runtime_error &e) { if (hostName.find("@") != std::string::npos) { #if defined(__WINDOWS__) SLog(EWarn, "Please ensure that passwordless authentication " "using plink.exe and pageant.exe is enabled (see the documentation for more information)"); #else SLog(EWarn, "Please ensure that passwordless authentication " "is enabled (e.g. using ssh-agent - see the documentation for more information)"); #endif } throw e; } } scheduler->start(); #if !defined(__WINDOWS__) /* Initialize signal handlers */ struct sigaction sa; sa.sa_handler = signalHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL)) SLog(EError, "Could not install a custom signal handler!"); if (sigaction(SIGFPE, &sa, NULL)) SLog(EError, "Could not install a custom signal handler!"); #endif /* Prepare for parsing scene descriptions */ SAXParser* parser = new SAXParser(); fs::path schemaPath = fileResolver->resolveAbsolute("data/schema/scene.xsd"); /* Check against the 'scene.xsd' XML Schema */ parser->setDoSchema(true); parser->setValidationSchemaFullChecking(true); parser->setValidationScheme(SAXParser::Val_Always); parser->setExternalNoNamespaceSchemaLocation(schemaPath.c_str()); /* Set the handler */ SceneHandler *handler = new SceneHandler(parameters); parser->setDoNamespaces(true); parser->setDocumentHandler(handler); parser->setErrorHandler(handler); renderQueue = new RenderQueue(); ref<FlushThread> flushThread; if (flushTimer > 0) { flushThread = new FlushThread(flushTimer); flushThread->start(); } int jobIdx = 0; for (int i=optind; i<argc; ++i) { fs::path filename = fileResolver->resolve(argv[i]), filePath = fs::absolute(filename).parent_path(), baseName = filename.stem(); ref<FileResolver> frClone = fileResolver->clone(); frClone->prependPath(filePath); Thread::getThread()->setFileResolver(frClone); SLog(EInfo, "Parsing scene description from \"%s\" ..", argv[i]); parser->parse(filename.c_str()); ref<Scene> scene = handler->getScene(); scene->setSourceFile(filename); scene->setDestinationFile(destFile.length() > 0 ? fs::path(destFile) : (filePath / baseName)); scene->setBlockSize(blockSize); if (scene->destinationExists() && skipExisting) continue; ref<RenderJob> thr = new RenderJob(formatString("ren%i", jobIdx++), scene, renderQueue, -1, -1, -1, true, flushTimer > 0); thr->start(); renderQueue->waitLeft(numParallelScenes-1); if (i+1 < argc && numParallelScenes == 1) Statistics::getInstance()->resetAll(); } /* Wait for all render processes to finish */ renderQueue->waitLeft(0); if (flushThread) flushThread->quit(); renderQueue = NULL; delete handler; delete parser; Statistics::getInstance()->printStats(); } catch (const std::exception &e) { std::cerr << "Caught a critical exception: " << e.what() << endl; return -1; } catch (...) { std::cerr << "Caught a critical exception of unknown type!" << endl; return -1; } return 0; }