metadata
Wenzel Jakob 2013-01-27 21:11:08 -05:00
commit b3bde09b8a
25 changed files with 352 additions and 87 deletions

View File

@ -42,7 +42,6 @@
#include <mitsuba/core/constants.h> #include <mitsuba/core/constants.h>
#include <mitsuba/core/fwd.h> #include <mitsuba/core/fwd.h>
#include <mitsuba/render/fwd.h> #include <mitsuba/render/fwd.h>
#include <mitsuba/core/math.h>
#include <mitsuba/core/object.h> #include <mitsuba/core/object.h>
#include <mitsuba/core/ref.h> #include <mitsuba/core/ref.h>
#include <mitsuba/core/tls.h> #include <mitsuba/core/tls.h>

View File

@ -33,7 +33,10 @@ MTS_NAMESPACE_BEGIN
/// Write a Log message to the console (to be used within subclasses of <tt>Object</tt>) /// Write a Log message to the console (to be used within subclasses of <tt>Object</tt>)
#define Log(level, fmt, ...) do { \ #define Log(level, fmt, ...) do { \
mitsuba::Logger *logger = mitsuba::Thread::getThread()->getLogger(); \ mitsuba::Thread *thread = mitsuba::Thread::getThread(); \
if (EXPECT_NOT_TAKEN(thread == NULL)) \
throw std::runtime_error("Null thread pointer"); \
mitsuba::Logger *logger = thread->getLogger(); \
if (level >= logger->getLogLevel()) \ if (level >= logger->getLogLevel()) \
logger->log(level, m_theClass, \ logger->log(level, m_theClass, \
__FILE__, __LINE__, fmt, ## __VA_ARGS__); \ __FILE__, __LINE__, fmt, ## __VA_ARGS__); \
@ -44,7 +47,10 @@ MTS_NAMESPACE_BEGIN
* outside of classes that derive from Object) * outside of classes that derive from Object)
*/ */
#define SLog(level, fmt, ...) do { \ #define SLog(level, fmt, ...) do { \
mitsuba::Logger *logger = mitsuba::Thread::getThread()->getLogger(); \ mitsuba::Thread *thread = mitsuba::Thread::getThread(); \
if (EXPECT_NOT_TAKEN(thread == NULL)) \
throw std::runtime_error("Null thread pointer"); \
mitsuba::Logger *logger = thread->getLogger(); \
if (level >= logger->getLogLevel()) \ if (level >= logger->getLogLevel()) \
logger->log(level, NULL, \ logger->log(level, NULL, \
__FILE__, __LINE__, fmt, ## __VA_ARGS__); \ __FILE__, __LINE__, fmt, ## __VA_ARGS__); \

View File

@ -31,7 +31,7 @@ MTS_NAMESPACE_BEGIN
* \brief Generic LRU cache implementation * \brief Generic LRU cache implementation
* *
* Based on the bimap implementation by Tim Day * Based on the bimap implementation by Tim Day
* (http://www.bottlenose.demon.co.uk/article/lru.pdf). * (http://timday.bitbucket.org/lru.html).
* *
* This cache does not support multithreading out of the box -- it * This cache does not support multithreading out of the box -- it
* will need to be protected using some form of locking mechanism. * will need to be protected using some form of locking mechanism.

View File

@ -140,7 +140,7 @@ public:
perm[i] = i; perm[i] = i;
/* Build the kd-tree and compute a suitable permutation of the elements */ /* Build the kd-tree and compute a suitable permutation of the elements */
m_root = build(m_aabb, 0, &perm[0], &temp[0], &perm[0], &perm[m_items.size()]); m_root = build(m_aabb, 0, &perm[0], &temp[0], &perm[0], &perm[0] + m_items.size());
/* Apply the permutation */ /* Apply the permutation */
permute_inplace(&m_items[0], perm); permute_inplace(&m_items[0], perm);

View File

@ -472,7 +472,7 @@ public:
template <typename T> void readArray(T *array, size_t count); template <typename T> void readArray(T *array, size_t count);
/** /**
* \brief Read a known-size array from the stream (uses partial template * \brief Read a known-size array from the stream (uses partial template
* specialization to select a method appropriate to the data type) * specialization to select a method appropriate to the data type)
*/ */
template <typename T, size_t N> inline void readArray(T (&arr)[N]) { template <typename T, size_t N> inline void readArray(T (&arr)[N]) {
@ -484,9 +484,9 @@ public:
* specialization to select a method appropriate to the data type) * specialization to select a method appropriate to the data type)
*/ */
template <typename T> void writeArray(const T *array, size_t count); template <typename T> void writeArray(const T *array, size_t count);
/** /**
* \brief Write a known-size array to the stream (uses partial template * \brief Write a known-size array to the stream (uses partial template
* specialization to select a method appropriate to the data type) * specialization to select a method appropriate to the data type)
*/ */
template <typename T, size_t N> inline void writeArray(const T (&arr)[N]) { template <typename T, size_t N> inline void writeArray(const T (&arr)[N]) {

View File

@ -141,7 +141,8 @@ protected:
} }
inline static void destruct(void *data) { inline static void destruct(void *data) {
delete static_cast<ValueType *>(data); if (data)
delete static_cast<ValueType *>(data);
} }
protected: protected:
detail::ThreadLocalBase m_base; detail::ThreadLocalBase m_base;

View File

@ -189,6 +189,13 @@ template <typename T> inline TVector1<T> normalize(const TVector1<T> &v) {
return v / v.length(); return v / v.length();
} }
template <typename T> inline TVector1<T> normalizeStrict(const TVector1<T> &v, const char *errMsg) {
Float length = v.length();
if (length == 0)
SLog(EError, "normalizeStrict(): %s", errMsg);
return v / length;
}
template <> inline TVector1<int> TVector1<int>::operator/(int s) const { template <> inline TVector1<int> TVector1<int>::operator/(int s) const {
#ifdef MTS_DEBUG #ifdef MTS_DEBUG
if (s == 0) if (s == 0)
@ -380,6 +387,13 @@ template <typename T> inline TVector2<T> normalize(const TVector2<T> &v) {
return v / v.length(); return v / v.length();
} }
template <typename T> inline TVector2<T> normalizeStrict(const TVector2<T> &v, const char *errMsg) {
Float length = v.length();
if (length == 0)
SLog(EError, "normalizeStrict(): %s", errMsg);
return v / length;
}
template <> inline TVector2<int> TVector2<int>::operator/(int s) const { template <> inline TVector2<int> TVector2<int>::operator/(int s) const {
#ifdef MTS_DEBUG #ifdef MTS_DEBUG
if (s == 0) if (s == 0)
@ -582,6 +596,13 @@ template <typename T> inline TVector3<T> normalize(const TVector3<T> &v) {
return v / v.length(); return v / v.length();
} }
template <typename T> inline TVector3<T> normalizeStrict(const TVector3<T> &v, const char *errMsg) {
Float length = v.length();
if (length == 0)
SLog(EError, "normalizeStrict(): %s", errMsg);
return v / length;
}
template <> inline TVector3<int> TVector3<int>::operator/(int s) const { template <> inline TVector3<int> TVector3<int>::operator/(int s) const {
#ifdef MTS_DEBUG #ifdef MTS_DEBUG
if (s == 0) if (s == 0)
@ -781,6 +802,13 @@ template <typename T> inline TVector4<T> normalize(const TVector4<T> &v) {
return v / v.length(); return v / v.length();
} }
template <typename T> inline TVector4<T> normalizeStrict(const TVector4<T> &v, const char *errMsg) {
Float length = v.length();
if (length == 0)
SLog(EError, "normalizeStrict(): %s", errMsg);
return v / length;
}
template <> inline TVector4<int> TVector4<int>::operator/(int s) const { template <> inline TVector4<int> TVector4<int>::operator/(int s) const {
#ifdef MTS_DEBUG #ifdef MTS_DEBUG
if (s == 0) if (s == 0)

View File

@ -101,6 +101,9 @@ public:
/// Free the memory taken up by staticInitialization() /// Free the memory taken up by staticInitialization()
static void staticShutdown(); static void staticShutdown();
/// Push a cleanup handler to be executed after loading the scene is done
static void pushCleanupHandler(void (*cleanup)());
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Implementation of the SAX DocumentHandler interface // Implementation of the SAX DocumentHandler interface
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------

View File

@ -310,6 +310,32 @@ protected:
/// Load a Mitsuba compressed triangle mesh substream /// Load a Mitsuba compressed triangle mesh substream
void loadCompressed(Stream *stream, int idx = 0); void loadCompressed(Stream *stream, int idx = 0);
/**
* \brief Reads the header information of a compressed file, returning
* the version ID.
*
* This function assumes the stream is at the beginning of the compressed
* file and leaves the stream located right after the header.
*/
static short readHeader(Stream *stream);
/**
* \brief Read the idx-th entry from the offset diccionary at the end of
* the stream, which has to be open already, given the file version tag.
* This function modifies the position of the stream.
*/
static size_t readOffset(Stream *stream, short version, int idx);
/**
* \brief Read the entirety of the end-of-file offset dictionary from the
* already open stream, replacing the contents of the input vector.
* If the file is not large enough the function returns -1
* and does not modify the vector.
* This function modifies the position of the stream.
*/
static int readOffsetDictionary(Stream *stream, short version,
std::vector<size_t>& outOffsets);
/// Prepare internal tables for sampling uniformly wrt. area /// Prepare internal tables for sampling uniformly wrt. area
void prepareSamplingTable(); void prepareSamplingTable();
protected: protected:

View File

@ -8,8 +8,8 @@ macro(add_film)
endmacro() endmacro()
add_film(mfilm mfilm.cpp) add_film(mfilm mfilm.cpp)
add_film(ldrfilm ldrfilm.cpp banner.h) add_film(ldrfilm ldrfilm.cpp banner.h MTS_HW)
add_film(hdrfilm hdrfilm.cpp banner.h) add_film(hdrfilm hdrfilm.cpp banner.h MTS_HW)
if (OPENEXR_FOUND) if (OPENEXR_FOUND)
include_directories(${ILMBASE_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS}) include_directories(${ILMBASE_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS})

View File

@ -50,7 +50,7 @@ void PSSMLTSampler::serialize(Stream *stream, InstanceManager *manager) const {
} }
void PSSMLTSampler::configure() { void PSSMLTSampler::configure() {
m_logRatio = -std::log(m_s2/m_s1); m_logRatio = -math::fastlog(m_s2/m_s1);
m_time = 0; m_time = 0;
m_largeStepTime = 0; m_largeStepTime = 0;
m_largeStep = false; m_largeStep = false;

View File

@ -238,7 +238,7 @@ extern "C" {
p->mgr.free_in_buffer = 0; p->mgr.free_in_buffer = 0;
} }
METHODDEF(void) jpeg_error_exit (j_common_ptr cinfo) { METHODDEF(void) jpeg_error_exit (j_common_ptr cinfo) throw(std::runtime_error) {
char msg[JMSG_LENGTH_MAX]; char msg[JMSG_LENGTH_MAX];
(*cinfo->err->format_message) (cinfo, msg); (*cinfo->err->format_message) (cinfo, msg);
SLog(EError, "Critcal libjpeg error: %s", msg); SLog(EError, "Critcal libjpeg error: %s", msg);
@ -1370,7 +1370,7 @@ void Bitmap::writePNG(Stream *stream, int compression) const {
text[i].compression = PNG_TEXT_COMPRESSION_NONE; text[i].compression = PNG_TEXT_COMPRESSION_NONE;
} }
png_set_text(png_ptr, info_ptr, text, keys.size()); png_set_text(png_ptr, info_ptr, text, (int) keys.size());
if (m_gamma == -1) if (m_gamma == -1)
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);

View File

@ -373,9 +373,9 @@ void Thread::yield() {
void Thread::exit() { void Thread::exit() {
Log(EDebug, "Thread \"%s\" has finished", d->name.c_str()); Log(EDebug, "Thread \"%s\" has finished", d->name.c_str());
d->running = false; d->running = false;
decRef(); Assert(ThreadPrivate::self->get() == this);
ThreadPrivate::self->set(NULL);
detail::destroyLocalTLS(); detail::destroyLocalTLS();
decRef();
} }
std::string Thread::toString() const { std::string Thread::toString() const {

View File

@ -17,16 +17,26 @@
*/ */
#include <mitsuba/core/tls.h> #include <mitsuba/core/tls.h>
#include <boost/thread/mutex.hpp> #include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/locks.hpp> #include <boost/thread/locks.hpp>
#include <boost/unordered_map.hpp> #include <boost/unordered_set.hpp>
#include <set>
#include <boost/scoped_ptr.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#if defined(__OSX__) #if defined(__OSX__)
# include <pthread.h> # include <pthread.h>
#endif #endif
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
namespace mi = boost::multi_index;
/* The native TLS classes on Linux/MacOS/Windows only support a limited number /* The native TLS classes on Linux/MacOS/Windows only support a limited number
of dynamically allocated entries (usually 1024 or 1088). Furthermore, they of dynamically allocated entries (usually 1024 or 1088). Furthermore, they
do not provide appropriate cleanup semantics when the TLS object or one of do not provide appropriate cleanup semantics when the TLS object or one of
@ -45,16 +55,37 @@ struct TLSEntry {
inline TLSEntry() : data(NULL), destructFunctor(NULL) { } inline TLSEntry() : data(NULL), destructFunctor(NULL) { }
}; };
/// Per-thread TLS entry map /// boost multi-index element to act as replacement of map<Key,T>
struct PerThreadData { template<typename T1, typename T2>
typedef boost::unordered_map<void *, TLSEntry> Map; struct mutable_pair {
mutable_pair(const T1 &f, const T2 &s) : first(f), second(s) { }
Map map; T1 first;
boost::mutex mutex; mutable T2 second;
}; };
/// List of all PerThreadData data structures (one for each thred) /// Per-thread TLS entry map
std::set<PerThreadData *> ptdGlobal; struct PerThreadData {
typedef mutable_pair<void *, TLSEntry> MapData;
typedef mi::member<MapData, void *, &MapData::first> key_member;
struct seq_tag {};
struct key_tag {};
typedef mi::multi_index_container<MapData,
mi::indexed_by<
mi::hashed_unique<mi::tag<key_tag>, key_member>,
mi::sequenced<mi::tag<seq_tag> >
>
> Map;
typedef mi::index<Map, key_tag>::type::iterator key_iterator;
typedef mi::index<Map, seq_tag>::type::reverse_iterator reverse_iterator;
Map map;
boost::recursive_mutex mutex;
};
/// List of all PerThreadData data structures (one for each thread)
boost::unordered_set<PerThreadData *> ptdGlobal;
/// Lock to protect ptdGlobal /// Lock to protect ptdGlobal
boost::mutex ptdGlobalLock; boost::mutex ptdGlobalLock;
@ -79,10 +110,10 @@ struct ThreadLocalBase::ThreadLocalPrivate {
and clean up where necessary */ and clean up where necessary */
boost::lock_guard<boost::mutex> guard(ptdGlobalLock); boost::lock_guard<boost::mutex> guard(ptdGlobalLock);
for (std::set<PerThreadData *>::iterator it = ptdGlobal.begin(); for (boost::unordered_set<PerThreadData *>::iterator it = ptdGlobal.begin();
it != ptdGlobal.end(); ++it) { it != ptdGlobal.end(); ++it) {
PerThreadData *ptd = *it; PerThreadData *ptd = *it;
boost::unique_lock<boost::mutex> lock(ptd->mutex); boost::unique_lock<boost::recursive_mutex> lock(ptd->mutex);
PerThreadData::Map::iterator it2 = ptd->map.find(this); PerThreadData::Map::iterator it2 = ptd->map.find(this);
TLSEntry entry; TLSEntry entry;
@ -102,25 +133,32 @@ struct ThreadLocalBase::ThreadLocalPrivate {
/// Look up a TLS entry. The goal is to make this operation very fast! /// Look up a TLS entry. The goal is to make this operation very fast!
std::pair<void *, bool> get() { std::pair<void *, bool> get() {
bool existed = true; bool existed = true;
void *data;
#if defined(__OSX__) #if defined(__OSX__)
PerThreadData *ptd = (PerThreadData *) pthread_getspecific(ptdLocal); PerThreadData *ptd = (PerThreadData *) pthread_getspecific(ptdLocal);
#else #else
PerThreadData *ptd = ptdLocal; PerThreadData *ptd = ptdLocal;
#endif #endif
if (EXPECT_NOT_TAKEN(!ptd))
throw std::runtime_error("Internal error: call to ThreadLocalPrivate::get() "
" precedes the construction of thread-specific data structures!");
/* This is an uncontended thread-local lock (i.e. not to worry) */ /* This is an uncontended thread-local lock (i.e. not to worry) */
boost::lock_guard<boost::mutex> guard(ptd->mutex); boost::lock_guard<boost::recursive_mutex> guard(ptd->mutex);
TLSEntry &entry = ptd->map[this]; PerThreadData::key_iterator it = ptd->map.find(this);
if (EXPECT_TAKEN(it != ptd->map.end())) {
if (EXPECT_NOT_TAKEN(!entry.data)) { data = it->second.data;
} else {
/* This is the first access from this thread */ /* This is the first access from this thread */
entry.data = constructFunctor(); TLSEntry entry;
entry.data = data = constructFunctor();
entry.destructFunctor = destructFunctor; entry.destructFunctor = destructFunctor;
ptd->map.insert(PerThreadData::MapData(this, entry));
existed = false; existed = false;
} }
return std::make_pair(entry.data, existed); return std::make_pair(data, existed);
} }
}; };
@ -191,10 +229,11 @@ void destroyLocalTLS() {
PerThreadData *ptd = ptdLocal; PerThreadData *ptd = ptdLocal;
#endif #endif
boost::unique_lock<boost::mutex> lock(ptd->mutex); boost::unique_lock<boost::recursive_mutex> lock(ptd->mutex);
for (PerThreadData::Map::iterator it = ptd->map.begin(); // Destroy the data in reverse order of creation
it != ptd->map.end(); ++it) { for (PerThreadData::reverse_iterator it = mi::get<PerThreadData::seq_tag>(ptd->map).rbegin();
it != mi::get<PerThreadData::seq_tag>(ptd->map).rend(); ++it) {
TLSEntry &entry = it->second; TLSEntry &entry = it->second;
entry.destructFunctor(entry.data); entry.destructFunctor(entry.data);
} }

View File

@ -189,8 +189,8 @@ Transform Transform::glOrthographic(Float clipLeft, Float clipRight,
} }
Transform Transform::lookAt(const Point &p, const Point &t, const Vector &up) { Transform Transform::lookAt(const Point &p, const Point &t, const Vector &up) {
Vector dir = normalize(t-p); Vector dir = normalizeStrict(t-p, "lookAt(): 'origin' and 'target' coincide!");
Vector left = normalize(cross(up, dir)); Vector left = normalizeStrict(cross(up, dir), "lookAt(): the forward and upward direction must be linearly independent!");
Vector newUp = cross(dir, left); Vector newUp = cross(dir, left);
Matrix4x4 result, inverse; Matrix4x4 result, inverse;

View File

@ -137,7 +137,7 @@ Point2 Warp::squareToStdNormal(const Point2 &sample) {
} }
Float Warp::squareToStdNormalPdf(const Point2 &pos) { Float Warp::squareToStdNormalPdf(const Point2 &pos) {
return INV_TWOPI * std::exp(-(pos.x*pos.x + pos.y*pos.y)/2.0f); return INV_TWOPI * math::fastexp(-(pos.x*pos.x + pos.y*pos.y)/2.0f);
} }
static Float intervalToTent(Float sample) { static Float intervalToTent(Float sample) {

View File

@ -99,8 +99,8 @@ void Font::drawText(Bitmap *dest, Point2i pos, const std::string &text) const {
); );
Point2i sourceOffset( Point2i sourceOffset(
glyph.tx.x * m_bitmap->getWidth(), (int) (glyph.tx.x * m_bitmap->getWidth()),
glyph.tx.y * m_bitmap->getHeight()); (int) (glyph.tx.y * m_bitmap->getHeight()));
dest->accumulate(m_bitmap.get(), sourceOffset, targetOffset, glyph.size); dest->accumulate(m_bitmap.get(), sourceOffset, targetOffset, glyph.size);
@ -120,7 +120,7 @@ Vector2i Font::getSize(const std::string &text) const {
if (character == '\r') if (character == '\r')
continue; continue;
if (character == '\n') { if (character == '\n') {
size.y += getMaxVerticalBearing()*(4.0 / 3.0); size.y += (int) (getMaxVerticalBearing()*(4.0 / 3.0));
size.x = std::max(size.x, pos); size.x = std::max(size.x, pos);
pos = 0; pos = 0;
continue; continue;

View File

@ -206,7 +206,7 @@ struct path_to_python_str {
struct TSpectrum_to_Spectrum { struct TSpectrum_to_Spectrum {
static PyObject* convert(const TSpectrum<Float, 3> &spectrum) { static PyObject* convert(const TSpectrum<Float, SPECTRUM_SAMPLES> &spectrum) {
return bp::incref(bp::object(Spectrum(spectrum)).ptr()); return bp::incref(bp::object(Spectrum(spectrum)).ptr());
} }
}; };
@ -532,7 +532,7 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromXYZ_overloads, fromXYZ, 3, 4)
void export_core() { void export_core() {
bp::to_python_converter<fs::path, path_to_python_str>(); bp::to_python_converter<fs::path, path_to_python_str>();
bp::to_python_converter<TSpectrum<Float, 3>, TSpectrum_to_Spectrum>(); bp::to_python_converter<TSpectrum<Float, SPECTRUM_SAMPLES>, TSpectrum_to_Spectrum>();
bp::implicitly_convertible<std::string, fs::path>(); bp::implicitly_convertible<std::string, fs::path>();
bp::object coreModule( bp::object coreModule(

View File

@ -31,6 +31,7 @@
#include <mitsuba/core/fresolver.h> #include <mitsuba/core/fresolver.h>
#include <mitsuba/render/scene.h> #include <mitsuba/render/scene.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/unordered_set.hpp>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
XERCES_CPP_NAMESPACE_USE XERCES_CPP_NAMESPACE_USE
@ -46,6 +47,10 @@ XERCES_CPP_NAMESPACE_USE
level, NULL, __FILE__, __LINE__, fmt, ## __VA_ARGS__) level, NULL, __FILE__, __LINE__, fmt, ## __VA_ARGS__)
#endif #endif
typedef void (*CleanupFun) ();
typedef boost::unordered_set<CleanupFun> CleanupSet;
static PrimitiveThreadLocal<CleanupSet> __cleanup_tls;
SceneHandler::SceneHandler(const SAXParser *parser, SceneHandler::SceneHandler(const SAXParser *parser,
const ParameterMap &params, NamedObjectMap *namedObjects, const ParameterMap &params, NamedObjectMap *namedObjects,
bool isIncludedFile) : m_parser(parser), m_params(params), bool isIncludedFile) : m_parser(parser), m_params(params),
@ -155,6 +160,13 @@ void SceneHandler::startDocument() {
void SceneHandler::endDocument() { void SceneHandler::endDocument() {
SAssert(m_scene != NULL); SAssert(m_scene != NULL);
/* Call cleanup handlers */
CleanupSet &cleanup = __cleanup_tls.get();
for (CleanupSet::iterator it = cleanup.begin();
it != cleanup.end(); ++it)
(*it)();
cleanup.clear();
} }
void SceneHandler::characters(const XMLCh* const name, void SceneHandler::characters(const XMLCh* const name,
@ -247,6 +259,10 @@ void SceneHandler::startElement(const XMLCh* const xmlName,
m_context.push(context); m_context.push(context);
} }
void SceneHandler::pushCleanupHandler(void (*cleanup)()) {
__cleanup_tls.get().insert(cleanup);
}
void SceneHandler::endElement(const XMLCh* const xmlName) { void SceneHandler::endElement(const XMLCh* const xmlName) {
std::string name = transcode(xmlName); std::string name = transcode(xmlName);
ParseContext &context = m_context.top(); ParseContext &context = m_context.top();

View File

@ -171,51 +171,19 @@ static void readHelper(Stream *stream, bool fileDoublePrecision,
} }
} }
void TriMesh::loadCompressed(Stream *_stream, int index) { void TriMesh::loadCompressed(Stream *_stream, int index) {
ref<Stream> stream = _stream; ref<Stream> stream = _stream;
if (stream->getByteOrder() != Stream::ELittleEndian) if (stream->getByteOrder() != Stream::ELittleEndian)
Log(EError, "Tried to unserialize a shape from a stream, " Log(EError, "Tried to unserialize a shape from a stream, "
"which was not previously set to little endian byte order!"); "which was not previously set to little endian byte order!");
short format = stream->readShort(); const short version = readHeader(stream);
if (format == 0x1C04)
Log(EError, "Encountered a geometry file generated by an old "
"version of Mitsuba. Please re-import the scene to update this file "
"to the current format.");
if (format != MTS_FILEFORMAT_HEADER)
Log(EError, "Encountered an invalid file format!");
short version = stream->readShort();
if (version != MTS_FILEFORMAT_VERSION_V3 &&
version != MTS_FILEFORMAT_VERSION_V4)
Log(EError, "Encountered an incompatible file version!");
if (index != 0) { if (index != 0) {
size_t streamSize = stream->getSize(); const size_t offset = readOffset(stream, version, index);
stream->seek(offset);
/* Determine the position of the requested substream. This stream->skip(sizeof(short) * 2); // Skip the header
is stored at the end of the file */
stream->seek(streamSize - sizeof(uint32_t));
uint32_t count = stream->readUInt();
if (index < 0 || index > (int) count) {
Log(EError, "Unable to unserialize mesh, "
"shape index is out of range! (requested %i out of 0..%i)",
index, count-1);
}
// Seek to the correct position
if (version == MTS_FILEFORMAT_VERSION_V4) {
stream->seek(stream->getSize() - sizeof(uint64_t) * (count-index) - sizeof(uint32_t));
stream->seek(stream->readSize());
} else {
stream->seek(stream->getSize() - sizeof(uint32_t) * (count-index + 1));
stream->seek(stream->readUInt());
}
stream->skip(sizeof(short) * 2);
} }
stream = new ZStream(stream); stream = new ZStream(stream);
@ -282,6 +250,91 @@ void TriMesh::loadCompressed(Stream *_stream, int index) {
m_flipNormals = false; m_flipNormals = false;
} }
short TriMesh::readHeader(Stream *stream) {
short format = stream->readShort();
if (format == 0x1C04) {
Log(EError, "Encountered a geometry file generated by an old "
"version of Mitsuba. Please re-import the scene to update this file "
"to the current format.");
}
if (format != MTS_FILEFORMAT_HEADER) {
Log(EError, "Encountered an invalid file format!");
}
short version = stream->readShort();
if (version != MTS_FILEFORMAT_VERSION_V3 &&
version != MTS_FILEFORMAT_VERSION_V4) {
Log(EError, "Encountered an incompatible file version!");
}
return version;
}
size_t TriMesh::readOffset(Stream *stream, short version, int idx) {
const size_t streamSize = stream->getSize();
/* Determine the position of the requested substream. This is stored
at the end of the file */
stream->seek(streamSize - sizeof(uint32_t));
uint32_t count = stream->readUInt();
if (idx < 0 || idx > (int) count) {
Log(EError, "Unable to unserialize mesh, "
"shape index is out of range! (requested %i out of 0..%i)",
idx, count-1);
}
// Seek to the correct position
if (version == MTS_FILEFORMAT_VERSION_V4) {
stream->seek(stream->getSize() - sizeof(uint64_t) * (count-idx) - sizeof(uint32_t));
return stream->readSize();
} else {
Assert(version == MTS_FILEFORMAT_VERSION_V3);
stream->seek(stream->getSize() - sizeof(uint32_t) * (count-idx + 1));
return stream->readUInt();
}
}
int TriMesh::readOffsetDictionary(Stream *stream, short version,
std::vector<size_t>& outOffsets) {
const size_t streamSize = stream->getSize();
stream->seek(streamSize - sizeof(uint32_t));
const uint32_t count = stream->readUInt();
// Check if the stream is large enough to contain that number of meshes
const size_t minSize = sizeof(uint32_t) + count *
( 2*sizeof(uint16_t) // Header
+ sizeof(uint32_t) // Flags
+ sizeof(char) // Name
+ 2*sizeof(uint64_t) // Number of vertices and triangles
+ 3*sizeof(float) // One vertex
+ 3*sizeof(uint32_t)); // One triangle
if (streamSize >= minSize) {
outOffsets.resize(count);
if (version == MTS_FILEFORMAT_VERSION_V4) {
stream->seek(stream->getSize() - sizeof(uint64_t) * count - sizeof(uint32_t));
if (typeid(size_t) == typeid(uint64_t)) {
stream->readArray(&outOffsets[0], count);
} else {
for (size_t i = 0; i < count; ++i)
outOffsets[i] = stream->readSize();
}
} else {
stream->seek(stream->getSize() - sizeof(uint32_t) * (count + 1));
Assert(version == MTS_FILEFORMAT_VERSION_V3);
if (typeid(size_t) == typeid(uint32_t)) {
stream->readArray(&outOffsets[0], count);
} else {
for (size_t i = 0; i < count; ++i) {
outOffsets[i] = (size_t) stream->readUInt();
}
}
}
return count;
} else {
Log(EDebug, "The serialized mesh does not contain a valid dictionary");
return -1;
}
}
TriMesh::~TriMesh() { TriMesh::~TriMesh() {
if (m_positions) if (m_positions)
delete[] m_positions; delete[] m_positions;

View File

@ -19,6 +19,10 @@
#if !defined(__MAXEXP_H) #if !defined(__MAXEXP_H)
#define __MAXEXP_H #define __MAXEXP_H
#if defined(_MSC_VER)
# include <functional>
#endif
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
class MaxExpDist { class MaxExpDist {

View File

@ -1486,7 +1486,7 @@ void MainWindow::onExportDialogClose(int reason) {
QFileDialog *dialog = static_cast<QFileDialog *>(sender()); QFileDialog *dialog = static_cast<QFileDialog *>(sender());
m_currentChild = NULL; m_currentChild = NULL;
if (reason == QDialog::Accepted) { if (reason == QDialog::Accepted) {
QString fileName = dialog->selectedFiles().value(0); QString fileName = dialog->selectedFiles().value(0);
settings.setValue("fileDialogState", dialog->saveState()); settings.setValue("fileDialogState", dialog->saveState());
exportImage(fileName); exportImage(fileName);

View File

@ -52,8 +52,8 @@ public:
Float eval(Float x) const { Float eval(Float x) const {
Float alpha = -1.0f / (2.0f * m_stddev*m_stddev); Float alpha = -1.0f / (2.0f * m_stddev*m_stddev);
return std::max((Float) 0.0f, return std::max((Float) 0.0f,
std::exp(alpha * x * x) - math::fastexp(alpha * x * x) -
std::exp(alpha * m_radius * m_radius)); math::fastexp(alpha * m_radius * m_radius));
} }
std::string toString() const { std::string toString() const {

View File

@ -17,10 +17,17 @@
*/ */
#include <mitsuba/render/trimesh.h> #include <mitsuba/render/trimesh.h>
#include <mitsuba/render/scenehandler.h>
#include <mitsuba/core/properties.h> #include <mitsuba/core/properties.h>
#include <mitsuba/core/fstream.h> #include <mitsuba/core/fstream.h>
#include <mitsuba/core/fresolver.h> #include <mitsuba/core/fresolver.h>
#include <mitsuba/core/timer.h> #include <mitsuba/core/timer.h>
#include <mitsuba/core/lrucache.h>
#include <boost/make_shared.hpp>
/// How many files to keep open in the cache, per thread
#define MTS_SERIALIZED_CACHE_SIZE 4
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -152,9 +159,8 @@ public:
/* Load the geometry */ /* Load the geometry */
Log(EInfo, "Loading shape %i from \"%s\" ..", shapeIndex, filePath.filename().string().c_str()); Log(EInfo, "Loading shape %i from \"%s\" ..", shapeIndex, filePath.filename().string().c_str());
ref<FileStream> stream = new FileStream(filePath, FileStream::EReadOnly);
ref<Timer> timer = new Timer(); ref<Timer> timer = new Timer();
loadCompressed(stream, shapeIndex); loadCompressed(filePath, shapeIndex);
Log(EDebug, "Done (" SIZE_T_FMT " triangles, " SIZE_T_FMT " vertices, %i ms)", Log(EDebug, "Done (" SIZE_T_FMT " triangles, " SIZE_T_FMT " vertices, %i ms)",
m_triangleCount, m_vertexCount, timer->getMilliseconds()); m_triangleCount, m_vertexCount, timer->getMilliseconds());
@ -202,8 +208,92 @@ public:
: TriMesh(stream, manager) { } : TriMesh(stream, manager) { }
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private:
/**
* Helper class for loading serialized meshes from the same file
* repeatedly: it is common for scene to load multiple meshes from the same
* file, most times even in ascending order. This class loads the mesh
* offsets dictionary only once and keeps the stream open.
*
* Instances of this class are not thread safe.
*/
class MeshLoader {
public:
MeshLoader(const fs::path& filePath) {
m_fstream = new FileStream(filePath, FileStream::EReadOnly);
m_fstream->setByteOrder(Stream::ELittleEndian);
const short version = SerializedMesh::readHeader(m_fstream);
if (SerializedMesh::readOffsetDictionary(m_fstream,
version, m_offsets) < 0) {
// Assume there is a single mesh in the file at offset 0
m_offsets.resize(1, 0);
}
}
/**
* Positions the stream at the location for the given shape index.
* Returns the modified stream.
*/
inline FileStream* seekStream(size_t shapeIndex) {
if (shapeIndex > m_offsets.size()) {
SLog(EError, "Unable to unserialize mesh, "
"shape index is out of range! (requested %i out of 0..%i)",
shapeIndex, (int) (m_offsets.size()-1));
}
const size_t pos = m_offsets[shapeIndex];
m_fstream->seek(pos);
return m_fstream;
}
private:
std::vector<size_t> m_offsets;
ref<FileStream> m_fstream;
};
struct FileStreamCache : LRUCache<fs::path, std::less<fs::path>,
boost::shared_ptr<MeshLoader> > {
inline boost::shared_ptr<MeshLoader> get(const fs::path& path) {
bool dummy;
return LRUCache::get(path, dummy);
}
FileStreamCache() : LRUCache(MTS_SERIALIZED_CACHE_SIZE,
&boost::make_shared<MeshLoader, const fs::path&>) {}
};
/// Release all currently held offset caches / file streams
static void flushCache() {
m_cache.set(NULL);
}
/// Loads the mesh from the thread-local file stream cache
void loadCompressed(const fs::path& filePath, const int idx) {
if (EXPECT_NOT_TAKEN(idx < 0)) {
Log(EError, "Unable to unserialize mesh, "
"shape index is negative! (requested %i out of 0..%i)", idx);
}
// Get the thread local cache; create it if this is the first time
FileStreamCache* cache = m_cache.get();
if (EXPECT_NOT_TAKEN(cache == NULL)) {
cache = new FileStreamCache();
m_cache.set(cache);
SceneHandler::pushCleanupHandler(&SerializedMesh::flushCache);
}
boost::shared_ptr<MeshLoader> meshLoader = cache->get(filePath);
Assert(meshLoader != NULL);
TriMesh::loadCompressed(meshLoader->seekStream((size_t) idx));
}
static ThreadLocal<FileStreamCache> m_cache;
}; };
ThreadLocal<SerializedMesh::FileStreamCache> SerializedMesh::m_cache;
MTS_IMPLEMENT_CLASS_S(SerializedMesh, false, TriMesh) MTS_IMPLEMENT_CLASS_S(SerializedMesh, false, TriMesh)
MTS_EXPORT_PLUGIN(SerializedMesh, "Serialized mesh loader"); MTS_EXPORT_PLUGIN(SerializedMesh, "Serialized mesh loader");
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -218,7 +218,7 @@ void blueNoisePointSet(const Scene *scene, const std::vector<Shape *> &shapes,
Cell &cell = cells[cellID]; Cell &cell = cells[cellID];
int arrayIndex = cell.firstIndex + trial; int arrayIndex = cell.firstIndex + trial;
if (arrayIndex > (int) samples.size() || if (arrayIndex >= (int) samples.size() ||
samples[arrayIndex].cellID != cellID || samples[arrayIndex].cellID != cellID ||
cell.sample != -1) cell.sample != -1)
continue; continue;