diff --git a/data/pch/mitsuba_precompiled.hpp b/data/pch/mitsuba_precompiled.hpp index 9d31a26d..97b8f846 100644 --- a/data/pch/mitsuba_precompiled.hpp +++ b/data/pch/mitsuba_precompiled.hpp @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include diff --git a/include/mitsuba/core/logger.h b/include/mitsuba/core/logger.h index 0a2ad6b1..8e7c1f15 100644 --- a/include/mitsuba/core/logger.h +++ b/include/mitsuba/core/logger.h @@ -33,7 +33,10 @@ MTS_NAMESPACE_BEGIN /// Write a Log message to the console (to be used within subclasses of Object) #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()) \ logger->log(level, m_theClass, \ __FILE__, __LINE__, fmt, ## __VA_ARGS__); \ @@ -44,7 +47,10 @@ MTS_NAMESPACE_BEGIN * outside of classes that derive from Object) */ #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()) \ logger->log(level, NULL, \ __FILE__, __LINE__, fmt, ## __VA_ARGS__); \ diff --git a/include/mitsuba/core/lrucache.h b/include/mitsuba/core/lrucache.h index 18e7bb74..466c9ee5 100644 --- a/include/mitsuba/core/lrucache.h +++ b/include/mitsuba/core/lrucache.h @@ -31,7 +31,7 @@ MTS_NAMESPACE_BEGIN * \brief Generic LRU cache implementation * * 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 * will need to be protected using some form of locking mechanism. diff --git a/include/mitsuba/core/octree.h b/include/mitsuba/core/octree.h index 04b9d36e..fa9f6b85 100644 --- a/include/mitsuba/core/octree.h +++ b/include/mitsuba/core/octree.h @@ -140,7 +140,7 @@ public: perm[i] = i; /* 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 */ permute_inplace(&m_items[0], perm); diff --git a/include/mitsuba/core/stream.h b/include/mitsuba/core/stream.h index 112efc3b..a1c6826b 100644 --- a/include/mitsuba/core/stream.h +++ b/include/mitsuba/core/stream.h @@ -472,7 +472,7 @@ public: template 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) */ template inline void readArray(T (&arr)[N]) { @@ -484,9 +484,9 @@ public: * specialization to select a method appropriate to the data type) */ template 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) */ template inline void writeArray(const T (&arr)[N]) { diff --git a/include/mitsuba/core/tls.h b/include/mitsuba/core/tls.h index 00c81a1b..a5d2b8b6 100644 --- a/include/mitsuba/core/tls.h +++ b/include/mitsuba/core/tls.h @@ -141,7 +141,8 @@ protected: } inline static void destruct(void *data) { - delete static_cast(data); + if (data) + delete static_cast(data); } protected: detail::ThreadLocalBase m_base; diff --git a/include/mitsuba/core/vector.h b/include/mitsuba/core/vector.h index a7933056..df8a10e3 100644 --- a/include/mitsuba/core/vector.h +++ b/include/mitsuba/core/vector.h @@ -189,6 +189,13 @@ template inline TVector1 normalize(const TVector1 &v) { return v / v.length(); } +template inline TVector1 normalizeStrict(const TVector1 &v, const char *errMsg) { + Float length = v.length(); + if (length == 0) + SLog(EError, "normalizeStrict(): %s", errMsg); + return v / length; +} + template <> inline TVector1 TVector1::operator/(int s) const { #ifdef MTS_DEBUG if (s == 0) @@ -380,6 +387,13 @@ template inline TVector2 normalize(const TVector2 &v) { return v / v.length(); } +template inline TVector2 normalizeStrict(const TVector2 &v, const char *errMsg) { + Float length = v.length(); + if (length == 0) + SLog(EError, "normalizeStrict(): %s", errMsg); + return v / length; +} + template <> inline TVector2 TVector2::operator/(int s) const { #ifdef MTS_DEBUG if (s == 0) @@ -582,6 +596,13 @@ template inline TVector3 normalize(const TVector3 &v) { return v / v.length(); } +template inline TVector3 normalizeStrict(const TVector3 &v, const char *errMsg) { + Float length = v.length(); + if (length == 0) + SLog(EError, "normalizeStrict(): %s", errMsg); + return v / length; +} + template <> inline TVector3 TVector3::operator/(int s) const { #ifdef MTS_DEBUG if (s == 0) @@ -781,6 +802,13 @@ template inline TVector4 normalize(const TVector4 &v) { return v / v.length(); } +template inline TVector4 normalizeStrict(const TVector4 &v, const char *errMsg) { + Float length = v.length(); + if (length == 0) + SLog(EError, "normalizeStrict(): %s", errMsg); + return v / length; +} + template <> inline TVector4 TVector4::operator/(int s) const { #ifdef MTS_DEBUG if (s == 0) diff --git a/include/mitsuba/render/scenehandler.h b/include/mitsuba/render/scenehandler.h index c8a4a149..38ebae2e 100644 --- a/include/mitsuba/render/scenehandler.h +++ b/include/mitsuba/render/scenehandler.h @@ -101,6 +101,9 @@ public: /// Free the memory taken up by staticInitialization() 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 // ----------------------------------------------------------------------- diff --git a/include/mitsuba/render/trimesh.h b/include/mitsuba/render/trimesh.h index a68535cd..4f6a8606 100644 --- a/include/mitsuba/render/trimesh.h +++ b/include/mitsuba/render/trimesh.h @@ -310,6 +310,32 @@ protected: /// Load a Mitsuba compressed triangle mesh substream 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& outOffsets); + /// Prepare internal tables for sampling uniformly wrt. area void prepareSamplingTable(); protected: diff --git a/src/films/CMakeLists.txt b/src/films/CMakeLists.txt index 20f268e5..c9633ce8 100644 --- a/src/films/CMakeLists.txt +++ b/src/films/CMakeLists.txt @@ -8,8 +8,8 @@ macro(add_film) endmacro() add_film(mfilm mfilm.cpp) -add_film(ldrfilm ldrfilm.cpp banner.h) -add_film(hdrfilm hdrfilm.cpp banner.h) +add_film(ldrfilm ldrfilm.cpp banner.h MTS_HW) +add_film(hdrfilm hdrfilm.cpp banner.h MTS_HW) if (OPENEXR_FOUND) include_directories(${ILMBASE_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS}) diff --git a/src/integrators/pssmlt/pssmlt_sampler.cpp b/src/integrators/pssmlt/pssmlt_sampler.cpp index 2b4e8aad..98288e03 100644 --- a/src/integrators/pssmlt/pssmlt_sampler.cpp +++ b/src/integrators/pssmlt/pssmlt_sampler.cpp @@ -50,7 +50,7 @@ void PSSMLTSampler::serialize(Stream *stream, InstanceManager *manager) const { } void PSSMLTSampler::configure() { - m_logRatio = -std::log(m_s2/m_s1); + m_logRatio = -math::fastlog(m_s2/m_s1); m_time = 0; m_largeStepTime = 0; m_largeStep = false; diff --git a/src/libcore/bitmap.cpp b/src/libcore/bitmap.cpp index 7c233339..4c450d4d 100644 --- a/src/libcore/bitmap.cpp +++ b/src/libcore/bitmap.cpp @@ -238,7 +238,7 @@ extern "C" { 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]; (*cinfo->err->format_message) (cinfo, 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; } - 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) png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); diff --git a/src/libcore/thread.cpp b/src/libcore/thread.cpp index 2811463b..efd38dd1 100644 --- a/src/libcore/thread.cpp +++ b/src/libcore/thread.cpp @@ -373,9 +373,9 @@ void Thread::yield() { void Thread::exit() { Log(EDebug, "Thread \"%s\" has finished", d->name.c_str()); d->running = false; - decRef(); - ThreadPrivate::self->set(NULL); + Assert(ThreadPrivate::self->get() == this); detail::destroyLocalTLS(); + decRef(); } std::string Thread::toString() const { diff --git a/src/libcore/tls.cpp b/src/libcore/tls.cpp index 7237ec54..75f6912b 100644 --- a/src/libcore/tls.cpp +++ b/src/libcore/tls.cpp @@ -17,16 +17,26 @@ */ #include + #include +#include #include -#include -#include +#include + +#include +#include +#include +#include +#include + #if defined(__OSX__) # include #endif MTS_NAMESPACE_BEGIN +namespace mi = boost::multi_index; + /* The native TLS classes on Linux/MacOS/Windows only support a limited number of dynamically allocated entries (usually 1024 or 1088). Furthermore, they 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) { } }; -/// Per-thread TLS entry map -struct PerThreadData { - typedef boost::unordered_map Map; +/// boost multi-index element to act as replacement of map +template +struct mutable_pair { + mutable_pair(const T1 &f, const T2 &s) : first(f), second(s) { } - Map map; - boost::mutex mutex; + T1 first; + mutable T2 second; }; -/// List of all PerThreadData data structures (one for each thred) -std::set ptdGlobal; +/// Per-thread TLS entry map +struct PerThreadData { + typedef mutable_pair MapData; + typedef mi::member key_member; + struct seq_tag {}; + struct key_tag {}; + + typedef mi::multi_index_container, key_member>, + mi::sequenced > + > + > Map; + typedef mi::index::type::iterator key_iterator; + typedef mi::index::type::reverse_iterator reverse_iterator; + + Map map; + boost::recursive_mutex mutex; +}; + +/// List of all PerThreadData data structures (one for each thread) +boost::unordered_set ptdGlobal; /// Lock to protect ptdGlobal boost::mutex ptdGlobalLock; @@ -79,10 +110,10 @@ struct ThreadLocalBase::ThreadLocalPrivate { and clean up where necessary */ boost::lock_guard guard(ptdGlobalLock); - for (std::set::iterator it = ptdGlobal.begin(); + for (boost::unordered_set::iterator it = ptdGlobal.begin(); it != ptdGlobal.end(); ++it) { PerThreadData *ptd = *it; - boost::unique_lock lock(ptd->mutex); + boost::unique_lock lock(ptd->mutex); PerThreadData::Map::iterator it2 = ptd->map.find(this); TLSEntry entry; @@ -102,25 +133,32 @@ struct ThreadLocalBase::ThreadLocalPrivate { /// Look up a TLS entry. The goal is to make this operation very fast! std::pair get() { bool existed = true; + void *data; #if defined(__OSX__) PerThreadData *ptd = (PerThreadData *) pthread_getspecific(ptdLocal); #else PerThreadData *ptd = ptdLocal; #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) */ - boost::lock_guard guard(ptd->mutex); - TLSEntry &entry = ptd->map[this]; - - if (EXPECT_NOT_TAKEN(!entry.data)) { + boost::lock_guard guard(ptd->mutex); + PerThreadData::key_iterator it = ptd->map.find(this); + if (EXPECT_TAKEN(it != ptd->map.end())) { + data = it->second.data; + } else { /* This is the first access from this thread */ - entry.data = constructFunctor(); + TLSEntry entry; + entry.data = data = constructFunctor(); entry.destructFunctor = destructFunctor; + ptd->map.insert(PerThreadData::MapData(this, entry)); existed = false; } - return std::make_pair(entry.data, existed); + return std::make_pair(data, existed); } }; @@ -191,10 +229,11 @@ void destroyLocalTLS() { PerThreadData *ptd = ptdLocal; #endif - boost::unique_lock lock(ptd->mutex); + boost::unique_lock lock(ptd->mutex); - for (PerThreadData::Map::iterator it = ptd->map.begin(); - it != ptd->map.end(); ++it) { + // Destroy the data in reverse order of creation + for (PerThreadData::reverse_iterator it = mi::get(ptd->map).rbegin(); + it != mi::get(ptd->map).rend(); ++it) { TLSEntry &entry = it->second; entry.destructFunctor(entry.data); } diff --git a/src/libcore/transform.cpp b/src/libcore/transform.cpp index 71004bec..5f056310 100644 --- a/src/libcore/transform.cpp +++ b/src/libcore/transform.cpp @@ -189,8 +189,8 @@ Transform Transform::glOrthographic(Float clipLeft, Float clipRight, } Transform Transform::lookAt(const Point &p, const Point &t, const Vector &up) { - Vector dir = normalize(t-p); - Vector left = normalize(cross(up, dir)); + Vector dir = normalizeStrict(t-p, "lookAt(): 'origin' and 'target' coincide!"); + Vector left = normalizeStrict(cross(up, dir), "lookAt(): the forward and upward direction must be linearly independent!"); Vector newUp = cross(dir, left); Matrix4x4 result, inverse; diff --git a/src/libcore/warp.cpp b/src/libcore/warp.cpp index e10f766a..65872c3d 100644 --- a/src/libcore/warp.cpp +++ b/src/libcore/warp.cpp @@ -137,7 +137,7 @@ Point2 Warp::squareToStdNormal(const Point2 &sample) { } 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) { diff --git a/src/libhw/font.cpp b/src/libhw/font.cpp index 8f288c40..5349fd72 100644 --- a/src/libhw/font.cpp +++ b/src/libhw/font.cpp @@ -99,8 +99,8 @@ void Font::drawText(Bitmap *dest, Point2i pos, const std::string &text) const { ); Point2i sourceOffset( - glyph.tx.x * m_bitmap->getWidth(), - glyph.tx.y * m_bitmap->getHeight()); + (int) (glyph.tx.x * m_bitmap->getWidth()), + (int) (glyph.tx.y * m_bitmap->getHeight())); dest->accumulate(m_bitmap.get(), sourceOffset, targetOffset, glyph.size); @@ -120,7 +120,7 @@ Vector2i Font::getSize(const std::string &text) const { if (character == '\r') continue; 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); pos = 0; continue; diff --git a/src/libpython/core.cpp b/src/libpython/core.cpp index f6c301c8..f65c7164 100644 --- a/src/libpython/core.cpp +++ b/src/libpython/core.cpp @@ -206,7 +206,7 @@ struct path_to_python_str { struct TSpectrum_to_Spectrum { - static PyObject* convert(const TSpectrum &spectrum) { + static PyObject* convert(const TSpectrum &spectrum) { 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() { bp::to_python_converter(); - bp::to_python_converter, TSpectrum_to_Spectrum>(); + bp::to_python_converter, TSpectrum_to_Spectrum>(); bp::implicitly_convertible(); bp::object coreModule( diff --git a/src/librender/scenehandler.cpp b/src/librender/scenehandler.cpp index 30017839..299ea7c2 100644 --- a/src/librender/scenehandler.cpp +++ b/src/librender/scenehandler.cpp @@ -31,6 +31,7 @@ #include #include #include +#include MTS_NAMESPACE_BEGIN XERCES_CPP_NAMESPACE_USE @@ -46,6 +47,10 @@ XERCES_CPP_NAMESPACE_USE level, NULL, __FILE__, __LINE__, fmt, ## __VA_ARGS__) #endif +typedef void (*CleanupFun) (); +typedef boost::unordered_set CleanupSet; +static PrimitiveThreadLocal __cleanup_tls; + SceneHandler::SceneHandler(const SAXParser *parser, const ParameterMap ¶ms, NamedObjectMap *namedObjects, bool isIncludedFile) : m_parser(parser), m_params(params), @@ -155,6 +160,13 @@ void SceneHandler::startDocument() { void SceneHandler::endDocument() { 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, @@ -247,6 +259,10 @@ void SceneHandler::startElement(const XMLCh* const xmlName, m_context.push(context); } +void SceneHandler::pushCleanupHandler(void (*cleanup)()) { + __cleanup_tls.get().insert(cleanup); +} + void SceneHandler::endElement(const XMLCh* const xmlName) { std::string name = transcode(xmlName); ParseContext &context = m_context.top(); diff --git a/src/librender/trimesh.cpp b/src/librender/trimesh.cpp index 5329dbaa..79d660d4 100644 --- a/src/librender/trimesh.cpp +++ b/src/librender/trimesh.cpp @@ -171,51 +171,19 @@ static void readHelper(Stream *stream, bool fileDoublePrecision, } } - void TriMesh::loadCompressed(Stream *_stream, int index) { ref stream = _stream; if (stream->getByteOrder() != Stream::ELittleEndian) 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(); - 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!"); + const short version = readHeader(stream); if (index != 0) { - 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 (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); + const size_t offset = readOffset(stream, version, index); + stream->seek(offset); + stream->skip(sizeof(short) * 2); // Skip the header } stream = new ZStream(stream); @@ -282,6 +250,91 @@ void TriMesh::loadCompressed(Stream *_stream, int index) { 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& 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() { if (m_positions) delete[] m_positions; diff --git a/src/medium/maxexp.h b/src/medium/maxexp.h index 010dbfab..2d38c666 100644 --- a/src/medium/maxexp.h +++ b/src/medium/maxexp.h @@ -19,6 +19,10 @@ #if !defined(__MAXEXP_H) #define __MAXEXP_H +#if defined(_MSC_VER) +# include +#endif + MTS_NAMESPACE_BEGIN class MaxExpDist { diff --git a/src/mtsgui/mainwindow.cpp b/src/mtsgui/mainwindow.cpp index 8f202e40..84e9811b 100644 --- a/src/mtsgui/mainwindow.cpp +++ b/src/mtsgui/mainwindow.cpp @@ -1486,7 +1486,7 @@ void MainWindow::onExportDialogClose(int reason) { QFileDialog *dialog = static_cast(sender()); m_currentChild = NULL; - if (reason == QDialog::Accepted) { + if (reason == QDialog::Accepted) { QString fileName = dialog->selectedFiles().value(0); settings.setValue("fileDialogState", dialog->saveState()); exportImage(fileName); diff --git a/src/rfilters/gaussian.cpp b/src/rfilters/gaussian.cpp index a9cc046b..5ec166aa 100644 --- a/src/rfilters/gaussian.cpp +++ b/src/rfilters/gaussian.cpp @@ -52,8 +52,8 @@ public: Float eval(Float x) const { Float alpha = -1.0f / (2.0f * m_stddev*m_stddev); return std::max((Float) 0.0f, - std::exp(alpha * x * x) - - std::exp(alpha * m_radius * m_radius)); + math::fastexp(alpha * x * x) - + math::fastexp(alpha * m_radius * m_radius)); } std::string toString() const { diff --git a/src/shapes/serialized.cpp b/src/shapes/serialized.cpp index 279283fa..4c44a254 100644 --- a/src/shapes/serialized.cpp +++ b/src/shapes/serialized.cpp @@ -17,10 +17,17 @@ */ #include +#include #include #include #include #include +#include + +#include + +/// How many files to keep open in the cache, per thread +#define MTS_SERIALIZED_CACHE_SIZE 4 MTS_NAMESPACE_BEGIN @@ -152,9 +159,8 @@ public: /* Load the geometry */ Log(EInfo, "Loading shape %i from \"%s\" ..", shapeIndex, filePath.filename().string().c_str()); - ref stream = new FileStream(filePath, FileStream::EReadOnly); ref timer = new Timer(); - loadCompressed(stream, shapeIndex); + loadCompressed(filePath, shapeIndex); Log(EDebug, "Done (" SIZE_T_FMT " triangles, " SIZE_T_FMT " vertices, %i ms)", m_triangleCount, m_vertexCount, timer->getMilliseconds()); @@ -202,8 +208,92 @@ public: : TriMesh(stream, manager) { } 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 m_offsets; + ref m_fstream; + }; + + struct FileStreamCache : LRUCache, + boost::shared_ptr > { + + inline boost::shared_ptr get(const fs::path& path) { + bool dummy; + return LRUCache::get(path, dummy); + } + + FileStreamCache() : LRUCache(MTS_SERIALIZED_CACHE_SIZE, + &boost::make_shared) {} + }; + + /// 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 = cache->get(filePath); + Assert(meshLoader != NULL); + TriMesh::loadCompressed(meshLoader->seekStream((size_t) idx)); + } + + static ThreadLocal m_cache; }; +ThreadLocal SerializedMesh::m_cache; + MTS_IMPLEMENT_CLASS_S(SerializedMesh, false, TriMesh) MTS_EXPORT_PLUGIN(SerializedMesh, "Serialized mesh loader"); MTS_NAMESPACE_END diff --git a/src/subsurface/bluenoise.cpp b/src/subsurface/bluenoise.cpp index 86002ef5..20ff403c 100644 --- a/src/subsurface/bluenoise.cpp +++ b/src/subsurface/bluenoise.cpp @@ -218,7 +218,7 @@ void blueNoisePointSet(const Scene *scene, const std::vector &shapes, Cell &cell = cells[cellID]; int arrayIndex = cell.firstIndex + trial; - if (arrayIndex > (int) samples.size() || + if (arrayIndex >= (int) samples.size() || samples[arrayIndex].cellID != cellID || cell.sample != -1) continue;