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/fwd.h>
#include <mitsuba/render/fwd.h>
#include <mitsuba/core/math.h>
#include <mitsuba/core/object.h>
#include <mitsuba/core/ref.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>)
#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__); \

View File

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

View File

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

View File

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

View File

@ -189,6 +189,13 @@ template <typename T> inline TVector1<T> normalize(const TVector1<T> &v) {
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 {
#ifdef MTS_DEBUG
if (s == 0)
@ -380,6 +387,13 @@ template <typename T> inline TVector2<T> normalize(const TVector2<T> &v) {
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 {
#ifdef MTS_DEBUG
if (s == 0)
@ -582,6 +596,13 @@ template <typename T> inline TVector3<T> normalize(const TVector3<T> &v) {
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 {
#ifdef MTS_DEBUG
if (s == 0)
@ -781,6 +802,13 @@ template <typename T> inline TVector4<T> normalize(const TVector4<T> &v) {
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 {
#ifdef MTS_DEBUG
if (s == 0)

View File

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

View File

@ -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<size_t>& outOffsets);
/// Prepare internal tables for sampling uniformly wrt. area
void prepareSamplingTable();
protected:

View File

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

View File

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

View File

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

View File

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

View File

@ -17,16 +17,26 @@
*/
#include <mitsuba/core/tls.h>
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/unordered_map.hpp>
#include <set>
#include <boost/unordered_set.hpp>
#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__)
# include <pthread.h>
#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<void *, TLSEntry> Map;
/// boost multi-index element to act as replacement of map<Key,T>
template<typename T1, typename T2>
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<PerThreadData *> ptdGlobal;
/// Per-thread TLS entry map
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
boost::mutex ptdGlobalLock;
@ -79,10 +110,10 @@ struct ThreadLocalBase::ThreadLocalPrivate {
and clean up where necessary */
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) {
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);
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<void *, bool> 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<boost::mutex> guard(ptd->mutex);
TLSEntry &entry = ptd->map[this];
if (EXPECT_NOT_TAKEN(!entry.data)) {
boost::lock_guard<boost::recursive_mutex> 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<boost::mutex> lock(ptd->mutex);
boost::unique_lock<boost::recursive_mutex> 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<PerThreadData::seq_tag>(ptd->map).rbegin();
it != mi::get<PerThreadData::seq_tag>(ptd->map).rend(); ++it) {
TLSEntry &entry = it->second;
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) {
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;

View File

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

View File

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

View File

@ -206,7 +206,7 @@ struct path_to_python_str {
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());
}
};
@ -532,7 +532,7 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(fromXYZ_overloads, fromXYZ, 3, 4)
void export_core() {
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::object coreModule(

View File

@ -31,6 +31,7 @@
#include <mitsuba/core/fresolver.h>
#include <mitsuba/render/scene.h>
#include <boost/algorithm/string.hpp>
#include <boost/unordered_set.hpp>
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<CleanupFun> CleanupSet;
static PrimitiveThreadLocal<CleanupSet> __cleanup_tls;
SceneHandler::SceneHandler(const SAXParser *parser,
const ParameterMap &params, 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();

View File

@ -171,7 +171,6 @@ static void readHelper(Stream *stream, bool fileDoublePrecision,
}
}
void TriMesh::loadCompressed(Stream *_stream, int index) {
ref<Stream> stream = _stream;
@ -179,43 +178,12 @@ void TriMesh::loadCompressed(Stream *_stream, int index) {
Log(EError, "Tried to unserialize a shape from a stream, "
"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<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() {
if (m_positions)
delete[] m_positions;

View File

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

View File

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

View File

@ -17,10 +17,17 @@
*/
#include <mitsuba/render/trimesh.h>
#include <mitsuba/render/scenehandler.h>
#include <mitsuba/core/properties.h>
#include <mitsuba/core/fstream.h>
#include <mitsuba/core/fresolver.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
@ -152,9 +159,8 @@ public:
/* Load the geometry */
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();
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<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_EXPORT_PLUGIN(SerializedMesh, "Serialized mesh loader");
MTS_NAMESPACE_END

View File

@ -218,7 +218,7 @@ void blueNoisePointSet(const Scene *scene, const std::vector<Shape *> &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;