merge with Edgar's latest changes

metadata
Wenzel Jakob 2013-01-27 17:53:08 -05:00
commit 82255028d6
16 changed files with 288 additions and 78 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

@ -310,6 +310,31 @@ 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

@ -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;
@ -108,19 +139,24 @@ struct ThreadLocalBase::ThreadLocalPrivate {
#else #else
PerThreadData *ptd = ptdLocal; PerThreadData *ptd = ptdLocal;
#endif #endif
if(EXPECT_NOT_TAKEN(!ptd)) {
throw std::runtime_error("null per-thread data");
}
/* This is an uncontended thread-local lock (i.e. not to worry) */ void *data;
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;
/* This is the first access from this thread */ } else {
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 +227,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

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

@ -171,7 +171,6 @@ 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;
@ -179,43 +178,12 @@ void TriMesh::loadCompressed(Stream *_stream, int index) {
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,92 @@ 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 triange
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] = static_cast<size_t>(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] = 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

@ -21,6 +21,12 @@
#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 +158,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 +207,88 @@ 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&>) {}
};
/// 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);
}
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;