diff --git a/include/mitsuba/core/properties.h b/include/mitsuba/core/properties.h index 2b898836..bc505075 100644 --- a/include/mitsuba/core/properties.h +++ b/include/mitsuba/core/properties.h @@ -185,6 +185,10 @@ public: /// Return one of the parameters (converting it to a string if necessary, with default value) std::string getAsString(const std::string &name, const std::string &defVal) const; + /// Copy an attribute from another Properties object and potentially rename it + void copyAttribute(const Properties &properties, + const std::string &sourceName, const std::string &targetName); + /// Store an array containing the names of all stored properties void putPropertyNames(std::vector &results) const; diff --git a/include/mitsuba/core/util.h b/include/mitsuba/core/util.h index 985afeef..7bd67cb6 100644 --- a/include/mitsuba/core/util.h +++ b/include/mitsuba/core/util.h @@ -110,6 +110,9 @@ extern MTS_EXPORT_CORE int getCoreCount(); /// Return the host name of this machine extern MTS_EXPORT_CORE std::string getHostName(); +/// Return the process private memory usage in bytes +extern MTS_EXPORT_CORE size_t getPrivateMemoryUsage(); + /// Return the fully qualified domain name of this machine extern MTS_EXPORT_CORE std::string getFQDN(); diff --git a/include/mitsuba/render/film.h b/include/mitsuba/render/film.h index fb3361e2..72674c4e 100644 --- a/include/mitsuba/render/film.h +++ b/include/mitsuba/render/film.h @@ -61,7 +61,7 @@ public: virtual void setDestinationFile(const fs::path &filename, uint32_t blockSize) = 0; /// Develop the film and write the result to the previously specified filename - virtual void develop() = 0; + virtual void develop(const Scene *scene, Float renderTime) = 0; /** * \brief Develop the contents of a subregion of the film and store diff --git a/include/mitsuba/render/renderjob.h b/include/mitsuba/render/renderjob.h index ba870552..edc4b7f0 100644 --- a/include/mitsuba/render/renderjob.h +++ b/include/mitsuba/render/renderjob.h @@ -75,7 +75,7 @@ public: bool interactive = false); /// Write out the current (partially rendered) image - inline void flush() { m_scene->flush(); } + inline void flush() { m_scene->flush(m_queue, this); } /// Cancel a running render job inline void cancel() { m_scene->cancel(); } diff --git a/include/mitsuba/render/renderqueue.h b/include/mitsuba/render/renderqueue.h index 3a9944a5..fd3ca3e4 100644 --- a/include/mitsuba/render/renderqueue.h +++ b/include/mitsuba/render/renderqueue.h @@ -76,6 +76,9 @@ public: /// Remove a (finished) render job from the queue void removeJob(RenderJob *thr, bool wasCancelled); + /// Return the amount of time spent rendering the given job (in seconds) + Float getRenderTime(const RenderJob *job) const; + /// Register a render listener void registerListener(RenderListener *listener); diff --git a/include/mitsuba/render/scene.h b/include/mitsuba/render/scene.h index 7302e5f6..fbc68ac0 100644 --- a/include/mitsuba/render/scene.h +++ b/include/mitsuba/render/scene.h @@ -133,7 +133,7 @@ public: int sceneResID, int sensorResID, int samplerResID); /// Write out the current (partially rendered) image - void flush(); + void flush(RenderQueue *queue, const RenderJob *job); /** * \brief Cancel a running rendering job diff --git a/src/films/hdrfilm.cpp b/src/films/hdrfilm.cpp index 57034d88..d71508c6 100644 --- a/src/films/hdrfilm.cpp +++ b/src/films/hdrfilm.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "banner.h" MTS_NAMESPACE_BEGIN @@ -235,20 +236,11 @@ public: std::vector keys = props.getPropertyNames(); for (size_t i=0; i args = tokenize(key.substr(5, key.length()-6), " ,"); - - if (args.size() != 2) - Log(EError, "Text command '%s' has an invalid number of arguments!", key.c_str()); - - Annotation annotation; - annotation.offset = Point2i(atoi(args[0].c_str()), atoi(args[1].c_str())); - annotation.text = props.getString(keys[i]); - m_annotations.push_back(annotation); - } + if ((boost::starts_with(key, "tag('") && boost::ends_with(key, "')")) || + (boost::starts_with(key, "text(") && boost::ends_with(key, ")"))) + props.markQueried(keys[i]); } m_storage = new ImageBlock(Bitmap::ESpectrumAlphaWeight, m_cropSize); @@ -350,7 +342,7 @@ public: m_destFile = destFile; } - void develop() { + void develop(const Scene *scene, Float renderTime) { Log(EDebug, "Developing film .."); ref bitmap = m_storage->getBitmap()->convert( @@ -368,23 +360,6 @@ public: } } - if (!m_annotations.empty()) { - ref font = new Font(Font::EBitstreamVeraMono14); - font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), 1.0f); - - for (size_t i=0; igetSize(text); - bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f)); - font->drawText(bitmap, offset, text); - } - } - - for (std::map::const_iterator it = m_tags.begin(); - it != m_tags.end(); ++it) - bitmap->setMetadataString(it->first, it->second); - fs::path filename = m_destFile; std::string extension = boost::to_lower_copy(filename.extension().string()); std::string properExtension = (m_fileFormat == Bitmap::EOpenEXR) ? ".exr" : ".rgbe"; @@ -394,13 +369,115 @@ public: Log(EInfo, "Writing image to \"%s\" ..", filename.string().c_str()); ref stream = new FileStream(filename, FileStream::ETruncWrite); + /* Attach the custom annotations */ + Properties &metadata = bitmap->getMetadata(); + std::vector keys = m_properties.getPropertyNames(); + ref font; + Float gamma = 1.0f; /// XXX + + for (size_t i=0; i args = tokenize(key.substr(6, key.length()-7), " ,"); + if (args.size() != 2) + Log(EError, "Label command '%s' has an invalid number of arguments!", key.c_str()); + char *end_ptr = NULL; + offset.x = strtol(args[0].c_str(), &end_ptr, 10); + if (*end_ptr != '\0') + Log(EError, "Label command '%s' has an invalid position argument!", key.c_str()); + offset.y = strtol(args[1].c_str(), &end_ptr, 10); + if (*end_ptr != '\0') + Log(EError, "Label command '%s' has an invalid position argument!", key.c_str()); + labelAnnotation = true; + + if (font == NULL) { + font = new Font(Font::EBitstreamVeraMono14); + font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), gamma); + } + } else { + continue; + } + + Properties::EPropertyType type = m_properties.getType(keys[i]); + if (type == Properties::EString) { + std::string value = m_properties.getString(keys[i]); + + while (true) { + char *strt; + if (!(strt = strchr((char *) value.c_str(), '$'))) + break; + + if (strncasecmp(strt, "$rendertime", 11) == 0) { + value.replace(strt-value.c_str(), 11, timeString(renderTime)); + } else if (strncasecmp(strt, "$memusage", 11) == 0) { + value.replace(strt-value.c_str(), 11, memString(getPrivateMemoryUsage())); + } else { + char *par1, *par2; + if (!(par1 = strchr(strt, '['))) + break; + if (!(par2 = strchr(par1, ']'))) + break; + + std::string propSource = value.substr(strt-value.c_str()+1, par1-strt-1); + std::string propKey = value.substr(par1-value.c_str()+1, par2-par1-1); + propSource.erase(std::remove_if(propSource.begin(), propSource.end(), ::isspace), propSource.end()); + propKey.erase(std::remove_if(propKey.begin(), propKey.end(), ::isspace), propKey.end()); + + if (!boost::starts_with(propKey, "'") || !boost::ends_with(propKey, "'")) + Log(EError, "Encountered invalid key '%s'", propKey.c_str()); + + propKey = propKey.substr(1, propKey.length()-2); + + const ConfigurableObject *source = NULL; + if (propSource == "film") + source = scene->getFilm(); + else if (propSource == "sampler") + source = scene->getSampler(); + else if (propSource == "sensor") + source = scene->getSensor(); + else if (propSource == "integrator") + source = scene->getIntegrator(); + else + Log(EError, "Unknown data source '%s' (must be film/sampler/sensor/integrator)", propSource.c_str()); + + std::string replacement; + if (propKey == "type") + replacement = source->getProperties().getPluginName(); + else + replacement = source->getProperties().getAsString(propKey); + + value.replace(strt-value.c_str(), par2-strt+1, replacement); + } + cout << value << endl; + } + if (labelAnnotation) { + Vector2i size = font->getSize(value); + bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f)); + font->drawText(bitmap, offset, value); + } else { + metadata.setString(key, value); + } + } else { + if (labelAnnotation) + Log(EError, "Only string-valued label annotations are supported!"); + metadata.copyAttribute(m_properties, keys[i], key); + } + } + /* Attach the log file to the image if this is requested */ Logger *logger = Thread::getThread()->getLogger(); std::string log; if (m_attachLog && logger->readLog(log)) { log += "\n\n"; log += Statistics::getInstance()->getStats(); - bitmap->setMetadataString("log", log); + metadata.setString("log", log); } bitmap->write(m_fileFormat, stream); @@ -451,9 +528,6 @@ protected: bool m_attachLog; fs::path m_destFile; ref m_storage; - - std::vector m_annotations; - std::map m_tags; }; MTS_IMPLEMENT_CLASS_S(HDRFilm, false, Film) diff --git a/src/films/ldrfilm.cpp b/src/films/ldrfilm.cpp index ea187fba..a2c3a62e 100644 --- a/src/films/ldrfilm.cpp +++ b/src/films/ldrfilm.cpp @@ -306,7 +306,7 @@ public: m_destFile = destFile; } - void develop() { + void develop(const Scene *scene, Float renderTime) { Log(EDebug, "Developing film .."); ref bitmap = m_storage->getBitmap(); diff --git a/src/films/mfilm.cpp b/src/films/mfilm.cpp index 7b0792e6..3f786e69 100644 --- a/src/films/mfilm.cpp +++ b/src/films/mfilm.cpp @@ -238,7 +238,7 @@ public: m_destFile = destFile; } - void develop() { + void develop(const Scene *scene, Float renderTime) { Log(EDebug, "Developing film .."); fs::path filename = m_destFile; diff --git a/src/films/tiledhdrfilm.cpp b/src/films/tiledhdrfilm.cpp index 6666a7ee..5476168c 100644 --- a/src/films/tiledhdrfilm.cpp +++ b/src/films/tiledhdrfilm.cpp @@ -156,7 +156,7 @@ public: } virtual ~TiledHDRFilm() { - develop(); + develop(NULL, 0); } void serialize(Stream *stream, InstanceManager *manager) const { @@ -167,7 +167,7 @@ public: void setDestinationFile(const fs::path &destFile, uint32_t blockSize) { if (m_output) - develop(); + develop(NULL, 0); Bitmap::EPixelFormat pixelFormat = m_pixelFormat; #if SPECTRUM_SAMPLES == 3 @@ -436,7 +436,7 @@ public: return false; /* Not supported by the tiled EXR film! */ } - void develop() { + void develop(const Scene *scene, Float renderTime) { if (m_output) { Log(EInfo, "Closing EXR file (%u tiles in total, peak memory usage: %u tiles)..", m_blocksH * m_blocksV, m_peakUsage); diff --git a/src/libcore/bitmap.cpp b/src/libcore/bitmap.cpp index 4c450d4d..770c054d 100644 --- a/src/libcore/bitmap.cpp +++ b/src/libcore/bitmap.cpp @@ -1355,7 +1355,7 @@ void Bitmap::writePNG(Stream *stream, int compression) const { png_text *text = NULL; Properties metadata(m_metadata); - metadata.setString("generated-by", "Mitsuba version " MTS_VERSION); + metadata.setString("generatedBy", "Mitsuba version " MTS_VERSION); std::vector keys = metadata.getPropertyNames(); std::vector values(keys.size()); @@ -1923,7 +1923,7 @@ void Bitmap::writeOpenEXR(Stream *stream, #endif Properties metadata(m_metadata); - metadata.setString("generated-by", "Mitsuba version " MTS_VERSION); + metadata.setString("generatedBy", "Mitsuba version " MTS_VERSION); std::vector keys = metadata.getPropertyNames(); diff --git a/src/libcore/fmtconv.cpp b/src/libcore/fmtconv.cpp index ae831528..7f8d3b73 100644 --- a/src/libcore/fmtconv.cpp +++ b/src/libcore/fmtconv.cpp @@ -156,7 +156,6 @@ template struct FormatConverterImpl : public FormatConverter { precomp[i] = convertScalar(detail::safe_cast(i), sourceGamma, NULL, multiplier, invDestGamma); } - const DestFormat zero = convertScalar(0.0f); const DestFormat one = convertScalar(1.0f); Spectrum spec; @@ -193,14 +192,14 @@ template struct FormatConverterImpl : public FormatConverter { case Bitmap::EXYZ: for (size_t i=0; i(*source++, sourceGamma, precomp, multiplier, invDestGamma); - *dest++ = zero; *dest++ = value; *dest++ = zero; + *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value; } break; case Bitmap::EXYZA: for (size_t i=0; i(*source++, sourceGamma, precomp, multiplier, invDestGamma); - *dest++ = zero; *dest++ = value; *dest++ = zero; *dest++ = one; + *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value; *dest++ = one; } break; @@ -271,7 +270,7 @@ template struct FormatConverterImpl : public FormatConverter { case Bitmap::EXYZ: for (size_t i=0; i(*source++, sourceGamma, precomp, multiplier, invDestGamma); - *dest++ = zero; *dest++ = value; *dest++ = zero; + *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value; source++; } break; @@ -279,7 +278,7 @@ template struct FormatConverterImpl : public FormatConverter { case Bitmap::EXYZA: for (size_t i=0; i(*source++, sourceGamma, precomp, multiplier, invDestGamma); - *dest++ = zero; *dest++ = value; *dest++ = zero; + *dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value; *dest++ = convertScalar(*source++); } break; diff --git a/src/libcore/properties.cpp b/src/libcore/properties.cpp index b5922a88..ba4db6ec 100644 --- a/src/libcore/properties.cpp +++ b/src/libcore/properties.cpp @@ -246,6 +246,14 @@ void Properties::putPropertyNames(std::vector &results) const { results.push_back((*it).first); } +void Properties::copyAttribute(const Properties &properties, + const std::string &sourceName, const std::string &targetName) { + std::map::const_iterator it = properties.m_elements->find(sourceName); + if (it == properties.m_elements->end()) + SLog(EError, "copyAttribute(): Could not find parameter \"%s\"!", sourceName.c_str()); + m_elements->operator[](targetName) = it->second; +} + bool Properties::operator==(const Properties &p) const { if (m_pluginName != p.m_pluginName || m_id != p.m_id || m_elements->size() != p.m_elements->size()) return false; diff --git a/src/libcore/util.cpp b/src/libcore/util.cpp index 6ecbff78..0dffeccc 100644 --- a/src/libcore/util.cpp +++ b/src/libcore/util.cpp @@ -28,13 +28,15 @@ #if defined(__OSX__) #include -#elif defined(WIN32) +#include +#elif defined(__WINDOWS__) #include +#include #else #include #endif -#if defined(WIN32) +#if defined(__WINDOWS__) # include # include # include @@ -134,7 +136,7 @@ std::string memString(size_t size) { } void * __restrict allocAligned(size_t size) { -#if defined(WIN32) +#if defined(__WINDOWS__) return _aligned_malloc(size, L1_CACHE_LINE_SIZE); #elif defined(__OSX__) /* OSX malloc already returns 16-byte aligned data suitable @@ -146,7 +148,7 @@ void * __restrict allocAligned(size_t size) { } void freeAligned(void *ptr) { -#if defined(WIN32) +#if defined(__WINDOWS__) _aligned_free(ptr); #else free(ptr); @@ -154,7 +156,7 @@ void freeAligned(void *ptr) { } int getCoreCount() { -#if defined(WIN32) +#if defined(__WINDOWS__) SYSTEM_INFO sys_info; GetSystemInfo(&sys_info); return sys_info.dwNumberOfProcessors; @@ -169,7 +171,45 @@ int getCoreCount() { #endif } -#if defined(WIN32) +size_t getPrivateMemoryUsage() { +#if defined(__WINDOWS__) + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); + return (size_t) pmc.PrivateUsage; +#elif defined(__OSX__) + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + if (task_info(mach_task_self(), TASK_BASIC_INFO, + (task_info_t)&t_info, &t_info_count) != KERN_SUCCESS) + return 0; + + /// XXX todo +#else + FILE* file = fopen("/proc/self/status", "r"); + if (!file) + return 0; + + char buffer[128]; + size_t result = 0; + while (fgets(buffer, sizeof(buffer), file) != NULL) { + if (strncmp(buffer, "VmRSS:", 6) != 0 && + strncmp(buffer, "VmSwap:", 7) != 0) + continue; + + char *line = buffer; + while (*line < '0' || *line > '9') + ++line; + line[strlen(line)-3] = '\0'; + result += (size_t) atoi(line) * 1024; + } + + fclose(file); + return result; +#endif +} + +#if defined(__WINDOWS__) std::string lastErrorText() { DWORD errCode = GetLastError(); char *errorText = NULL; @@ -192,7 +232,7 @@ std::string lastErrorText() { bool enableFPExceptions() { bool exceptionsWereEnabled = false; -#if defined(WIN32) +#if defined(__WINDOWS__) _clearfp(); uint32_t cw = _controlfp(0, 0); exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); @@ -211,7 +251,7 @@ bool enableFPExceptions() { bool disableFPExceptions() { bool exceptionsWereEnabled = false; -#if defined(WIN32) +#if defined(__WINDOWS__) _clearfp(); uint32_t cw = _controlfp(0, 0); exceptionsWereEnabled = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); @@ -230,7 +270,7 @@ bool disableFPExceptions() { void restoreFPExceptions(bool oldState) { bool currentState; -#if defined(WIN32) +#if defined(__WINDOWS__) uint32_t cw = _controlfp(0, 0); currentState = ~cw & (_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW); #elif defined(__OSX__) @@ -249,7 +289,7 @@ void restoreFPExceptions(bool oldState) { std::string getHostName() { char hostName[128]; if (gethostname(hostName, sizeof(hostName)) != 0) -#if defined(WIN32) +#if defined(__WINDOWS__) SLog(EError, "Could not retrieve the computer's host name: %s!", lastErrorText().c_str()); #else @@ -280,7 +320,7 @@ std::string getFQDN() { fqdn, NI_MAXHOST, NULL, 0, 0); if (retVal != 0) { freeaddrinfo(addrInfo); -#if defined(WIN32) +#if defined(__WINDOWS__) SLog(EWarn, "Could not retrieve the computer's fully " "qualified domain name: error %i!", WSAGetLastError()); #else @@ -304,7 +344,7 @@ std::string formatString(const char *fmt, ...) { char tmp[512]; va_list iterator; -#if defined(WIN32) +#if defined(__WINDOWS__) va_start(iterator, fmt); size_t size = _vscprintf(fmt, iterator) + 1; diff --git a/src/librender/renderqueue.cpp b/src/librender/renderqueue.cpp index 31eb2263..23c84a7a 100644 --- a/src/librender/renderqueue.cpp +++ b/src/librender/renderqueue.cpp @@ -54,20 +54,28 @@ void RenderQueue::unregisterListener(RenderListener *listener) { listener->decRef(); } +Float RenderQueue::getRenderTime(const RenderJob *job) const { + LockGuard lock(m_mutex); + std::map::const_iterator it = m_jobs.find(const_cast(job)); + if (it == m_jobs.end()) + Log(EError, "RenderQueue::getRenderJob() - job not found!"); + + unsigned int ms = m_timer->getMilliseconds() - it->second.startTime; + return ms / 1000.0f; +} + void RenderQueue::flush() { LockGuard lock(m_mutex); std::map::iterator it = m_jobs.begin(); - for (; it != m_jobs.end(); ++it) { + for (; it != m_jobs.end(); ++it) (*it).first->flush(); - } } void RenderQueue::removeJob(RenderJob *job, bool cancelled) { LockGuard lock(m_mutex); std::map::iterator it = m_jobs.find(job); - if (it == m_jobs.end()) { + if (it == m_jobs.end()) Log(EError, "RenderQueue::removeRenderJob() - job not found!"); - } JobRecord &rec = (*it).second; unsigned int ms = m_timer->getMilliseconds() - rec.startTime; Log(EInfo, "Render time: %s", timeString(ms/1000.0f, true).c_str()); diff --git a/src/librender/scene.cpp b/src/librender/scene.cpp index 21513cdf..0ebc510f 100644 --- a/src/librender/scene.cpp +++ b/src/librender/scene.cpp @@ -451,8 +451,8 @@ void Scene::cancel() { m_integrator->cancel(); } -void Scene::flush() { - m_sensor->getFilm()->develop(); +void Scene::flush(RenderQueue *queue, const RenderJob *job) { + m_sensor->getFilm()->develop(this, queue->getRenderTime(job)); } void Scene::setDestinationFile(const fs::path &name) { @@ -467,7 +467,7 @@ void Scene::postprocess(RenderQueue *queue, const RenderJob *job, int sceneResID, int sensorResID, int samplerResID) { m_integrator->postprocess(this, queue, job, sceneResID, sensorResID, samplerResID); - m_sensor->getFilm()->develop(); + m_sensor->getFilm()->develop(this, queue->getRenderTime(job)); } void Scene::addChild(const std::string &name, ConfigurableObject *child) { diff --git a/src/librender/scenehandler.cpp b/src/librender/scenehandler.cpp index 299ea7c2..777eae9d 100644 --- a/src/librender/scenehandler.cpp +++ b/src/librender/scenehandler.cpp @@ -196,7 +196,6 @@ void SceneHandler::startElement(const XMLCh* const xmlName, ParseContext context((name == "scene") ? NULL : &m_context.top()); - /* Convert attributes to ISO-8859-1 */ for (size_t i=0; i 0 && attrValue.find('$') != attrValue.npos) { @@ -208,7 +207,7 @@ void SceneHandler::startElement(const XMLCh* const xmlName, ++pos; } } - if (attrValue.find('$') != attrValue.npos) + if (attrValue.find('$') != attrValue.npos && attrValue.find('[') == attrValue.npos) XMLLog(EError, "The scene referenced an undefined parameter: \"%s\"", attrValue.c_str()); }