enhanced customizable rendering statistics

metadata
Wenzel Jakob 2013-01-28 16:07:25 -05:00
parent 8d1ef9809c
commit b36db49600
17 changed files with 209 additions and 71 deletions

View File

@ -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<std::string> &results) const;

View File

@ -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();

View File

@ -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

View File

@ -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(); }

View File

@ -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);

View File

@ -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

View File

@ -22,6 +22,7 @@
#include <mitsuba/core/statistics.h>
#include <mitsuba/hw/font.h>
#include <boost/algorithm/string.hpp>
#include <mitsuba/render/scene.h>
#include "banner.h"
MTS_NAMESPACE_BEGIN
@ -235,20 +236,11 @@ public:
std::vector<std::string> keys = props.getPropertyNames();
for (size_t i=0; i<keys.size(); ++i) {
std::string key = boost::to_lower_copy(keys[i]);
key.erase(std::remove_if(key.begin(), key.end(), ::isspace), key.end());
if (boost::starts_with(key, "tag('") && boost::ends_with(key, "')")) {
m_tags[keys[i].substr(5, key.length()-7)] = props.getString(keys[i]);
} else if (boost::starts_with(key, "text(") && boost::ends_with(key, ")")) {
std::vector<std::string> 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> bitmap = m_storage->getBitmap()->convert(
@ -368,23 +360,6 @@ public:
}
}
if (!m_annotations.empty()) {
ref<Font> font = new Font(Font::EBitstreamVeraMono14);
font->convert(bitmap->getPixelFormat(), bitmap->getComponentFormat(), 1.0f);
for (size_t i=0; i<m_annotations.size(); ++i) {
const Point2i &offset = m_annotations[i].offset;
const std::string &text = m_annotations[i].text;
Vector2i size = font->getSize(text);
bitmap->fillRect(offset-Vector2i(4, 4), size + Vector2i(8, 8), Spectrum(0.0f));
font->drawText(bitmap, offset, text);
}
}
for (std::map<std::string, std::string>::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<FileStream> stream = new FileStream(filename, FileStream::ETruncWrite);
/* Attach the custom annotations */
Properties &metadata = bitmap->getMetadata();
std::vector<std::string> keys = m_properties.getPropertyNames();
ref<Font> font;
Float gamma = 1.0f; /// XXX
for (size_t i=0; i<keys.size(); ++i) {
std::string key = keys[i];
key.erase(std::remove_if(key.begin(), key.end(), ::isspace), key.end());
std::string lkey = boost::to_lower_copy(key);
Point2i offset(0, 0);
bool labelAnnotation = false;
if (boost::starts_with(lkey, "metadata['") && boost::ends_with(lkey, "']")) {
key = key.substr(10, key.length()-12);
} else if (boost::starts_with(lkey, "label[") && boost::ends_with(lkey, "]")) {
std::vector<std::string> 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<ImageBlock> m_storage;
std::vector<Annotation> m_annotations;
std::map<std::string, std::string> m_tags;
};
MTS_IMPLEMENT_CLASS_S(HDRFilm, false, Film)

View File

@ -306,7 +306,7 @@ public:
m_destFile = destFile;
}
void develop() {
void develop(const Scene *scene, Float renderTime) {
Log(EDebug, "Developing film ..");
ref<Bitmap> bitmap = m_storage->getBitmap();

View File

@ -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;

View File

@ -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);

View File

@ -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<std::string> keys = metadata.getPropertyNames();
std::vector<std::string> 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<std::string> keys = metadata.getPropertyNames();

View File

@ -156,7 +156,6 @@ template <typename T> struct FormatConverterImpl : public FormatConverter {
precomp[i] = convertScalar<DestFormat>(detail::safe_cast<SourceFormat>(i), sourceGamma, NULL, multiplier, invDestGamma);
}
const DestFormat zero = convertScalar<DestFormat>(0.0f);
const DestFormat one = convertScalar<DestFormat>(1.0f);
Spectrum spec;
@ -193,14 +192,14 @@ template <typename T> struct FormatConverterImpl : public FormatConverter {
case Bitmap::EXYZ:
for (size_t i=0; i<count; ++i) {
DestFormat value = convertScalar<DestFormat>(*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<count; ++i) {
DestFormat value = convertScalar<DestFormat>(*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 <typename T> struct FormatConverterImpl : public FormatConverter {
case Bitmap::EXYZ:
for (size_t i=0; i<count; ++i) {
DestFormat value = convertScalar<DestFormat>(*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 <typename T> struct FormatConverterImpl : public FormatConverter {
case Bitmap::EXYZA:
for (size_t i=0; i<count; ++i) {
DestFormat value = convertScalar<DestFormat>(*source++, sourceGamma, precomp, multiplier, invDestGamma);
*dest++ = zero; *dest++ = value; *dest++ = zero;
*dest++ = 0.950456f*value; *dest++ = value; *dest++ = 1.08875f*value;
*dest++ = convertScalar<DestFormat>(*source++);
}
break;

View File

@ -246,6 +246,14 @@ void Properties::putPropertyNames(std::vector<std::string> &results) const {
results.push_back((*it).first);
}
void Properties::copyAttribute(const Properties &properties,
const std::string &sourceName, const std::string &targetName) {
std::map<std::string, PropertyElement>::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;

View File

@ -28,13 +28,15 @@
#if defined(__OSX__)
#include <sys/sysctl.h>
#elif defined(WIN32)
#include <mach/mach.h>
#elif defined(__WINDOWS__)
#include <direct.h>
#include <psapi.h>
#else
#include <malloc.h>
#endif
#if defined(WIN32)
#if defined(__WINDOWS__)
# include <windows.h>
# include <winsock2.h>
# include <ws2tcpip.h>
@ -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;

View File

@ -54,20 +54,28 @@ void RenderQueue::unregisterListener(RenderListener *listener) {
listener->decRef();
}
Float RenderQueue::getRenderTime(const RenderJob *job) const {
LockGuard lock(m_mutex);
std::map<RenderJob *, JobRecord>::const_iterator it = m_jobs.find(const_cast<RenderJob*>(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<RenderJob *, JobRecord>::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<RenderJob *, JobRecord>::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());

View File

@ -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) {

View File

@ -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<xmlAttributes.getLength(); i++) {
std::string attrValue = transcode(xmlAttributes.getValue(i));
if (attrValue.length() > 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());
}