Skip to content

Commit c292c28

Browse files
committed
Converting reader definition to be able to load half-grids from stored float-grids
1 parent 1b0fa91 commit c292c28

13 files changed

Lines changed: 285 additions & 30 deletions

File tree

openvdb/openvdb/io/Archive.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ struct StreamMetadata::Impl
212212
bool mDelayedLoadMeta = DelayedLoadMetadata::isRegisteredType();
213213
uint64_t mLeaf = 0;
214214
uint32_t mTest = 0; // for testing only
215+
std::string mDesiredScalarType = "";
216+
SharedPtr<ConvertingReaderBase> mConvertingReader;
215217
}; // struct StreamMetadata
216218

217219

@@ -279,6 +281,12 @@ MetaMap& StreamMetadata::gridMetadata() { return mImpl->mGridMet
279281
const MetaMap& StreamMetadata::gridMetadata() const { return mImpl->mGridMetadata; }
280282
uint32_t StreamMetadata::__test() const { return mImpl->mTest; }
281283

284+
const std::string& StreamMetadata::desiredScalarType() const { return mImpl->mDesiredScalarType; }
285+
void StreamMetadata::setDesiredScalarType(std::string t) { mImpl->mDesiredScalarType = t; }
286+
287+
const ConvertingReaderBase* StreamMetadata::convertingReader() const {return mImpl->mConvertingReader.get();}
288+
void StreamMetadata::setConvertingReader(SharedPtr<ConvertingReaderBase> t) {mImpl->mConvertingReader = t;}
289+
282290
StreamMetadata::AuxDataMap& StreamMetadata::auxData() { return mImpl->mAuxData; }
283291
const StreamMetadata::AuxDataMap& StreamMetadata::auxData() const { return mImpl->mAuxData; }
284292

@@ -1186,6 +1194,17 @@ Archive::isDelayedLoadingEnabled()
11861194
}
11871195

11881196

1197+
Name Archive::conversionToString(Archive::ScalarConversion conv)
1198+
{
1199+
switch (conv)
1200+
{
1201+
case Archive::ScalarConversion::None:
1202+
return "";
1203+
case Archive::ScalarConversion::Half:
1204+
return typeNameAsString<math::half>();
1205+
}
1206+
}
1207+
11891208
namespace {
11901209

11911210
struct NoBBox {};
@@ -1223,6 +1242,7 @@ doReadGrid(GridBase::Ptr grid, const GridDescriptor& gd, std::istream& is, const
12231242
streamMetadata.reset(new StreamMetadata);
12241243
}
12251244
streamMetadata->setHalfFloat(grid->saveFloatAsHalf());
1245+
streamMetadata->setConvertingReader(gd.convertingReader());
12261246
io::setStreamMetadataPtr(is, streamMetadata, /*transfer=*/false);
12271247

12281248
io::setGridClass(is, GRID_UNKNOWN);

openvdb/openvdb/io/Archive.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class OPENVDB_API Archive
3434
using Ptr = SharedPtr<Archive>;
3535
using ConstPtr = SharedPtr<const Archive>;
3636

37+
enum class ScalarConversion { None, Half };
38+
3739
static const uint32_t DEFAULT_COMPRESSION_FLAGS;
3840

3941
Archive();
@@ -98,6 +100,8 @@ class OPENVDB_API Archive
98100
/// to disable delayed loading unconditionally.
99101
static bool isDelayedLoadingEnabled();
100102

103+
static Name conversionToString(ScalarConversion conv);
104+
101105
protected:
102106
/// @brief Return @c true if the input stream contains grid offsets
103107
/// that allow for random access or partial reading.

openvdb/openvdb/io/Compression.h

Lines changed: 200 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,197 @@ struct HalfReader</*IsReal=*/true, T> {
320320
};
321321

322322

323+
struct ConvertingReaderBase
324+
{
325+
using Ptr = SharedPtr<ConvertingReaderBase>;
326+
327+
virtual ~ConvertingReaderBase() = default;
328+
};
329+
330+
template<typename ValueT>
331+
struct ConvertingReader: ConvertingReaderBase
332+
{
333+
virtual void read(
334+
std::istream& is, ValueT* data, Index count, uint32_t compression,
335+
DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0),
336+
bool fromHalf = false) const = 0;
337+
338+
virtual void read(std::istream& is, ValueT& data) const = 0;
339+
340+
virtual void seekElement(std::istream& is, int offset, std::ios_base::seekdir dir) const = 0;
341+
342+
static const ConvertingReader& get(std::istream& is);
343+
};
344+
345+
template<typename ValueT, typename TValueFrom>
346+
struct TypedConvertingReader: ConvertingReader<ValueT>
347+
{
348+
using HalfT = typename RealToHalf<TValueFrom>::HalfT;
349+
350+
static constexpr bool do_conversion = !std::is_same<ValueT, TValueFrom>::value;
351+
static constexpr bool from_half_flag_is_relevant = RealToHalf<TValueFrom>::isReal;
352+
353+
template<typename TRead>
354+
static void readHelper(
355+
std::istream& is, ValueT* data, Index count, uint32_t compression,
356+
DelayedLoadMetadata* metadata, size_t metadataOffset)
357+
{
358+
if constexpr (std::is_same<ValueT, TRead>::value)
359+
{
360+
// either reads or skips through compressed data (count == 0) or it
361+
// is seeking (data == nullptr)
362+
io::readData(is, data, count, compression, metadata, metadataOffset);
363+
}
364+
else
365+
{
366+
// reading
367+
if (data && count > 0)
368+
{
369+
std::vector<TRead> buffer(count);
370+
io::readData(is, buffer.data(), count, compression, metadata, metadataOffset);
371+
std::copy(buffer.begin(), buffer.end(), data);
372+
}
373+
// either skips through compressed data (count == 0) or it is
374+
// seeking (data == nullptr)
375+
else
376+
{
377+
// reinterpret_cast<TRead*>(data) might produce not-aligned
378+
// pointer, but io::readData will not write to it, so it should
379+
// be safe. We must not skip this statement, nor we can pass
380+
// nullptr instead of data, it causes failures in the testsuite.
381+
// Now it behaves exactly as before.
382+
io::readData(is, reinterpret_cast<TRead*>(data), count,
383+
compression, metadata, metadataOffset);
384+
}
385+
}
386+
}
387+
388+
virtual void read(
389+
std::istream& is, ValueT* data, Index count, uint32_t compression,
390+
DelayedLoadMetadata* metadata = nullptr, size_t metadataOffset = size_t(0),
391+
bool fromHalf = false) const override
392+
{
393+
// this if-statement is here to maintain the original logic when
394+
// HalfReader<T> is being used. It can't be removed as it breaks bunch
395+
// of tests in the testsuite.
396+
if (fromHalf && from_half_flag_is_relevant && count < 1)
397+
return;
398+
399+
// due to std::vector<bool> being weird and not fitting the overall
400+
// templating idea here (it doesn't have data() method), the following
401+
// will fail to do non-identity conversion for bool-typed data. But for
402+
// bool-typed data there is just identity at the moment.
403+
if constexpr (from_half_flag_is_relevant)
404+
{
405+
if (fromHalf)
406+
{
407+
readHelper<HalfT>(is, data, count, compression, metadata, metadataOffset);
408+
}
409+
else
410+
{
411+
readHelper<TValueFrom>(is, data, count, compression, metadata, metadataOffset);
412+
}
413+
}
414+
else
415+
{
416+
readHelper<TValueFrom>(is, data, count, compression, metadata, metadataOffset);
417+
}
418+
}
419+
420+
virtual void read(std::istream& is, ValueT& data) const override
421+
{
422+
if constexpr (do_conversion)
423+
{
424+
TValueFrom buffer;
425+
is.read(reinterpret_cast<char*>(&buffer), /*bytes=*/sizeof(TValueFrom));
426+
data = buffer;
427+
}
428+
else
429+
{
430+
is.read(reinterpret_cast<char*>(&data), /*bytes=*/sizeof(ValueT));
431+
}
432+
}
433+
434+
virtual void seekElement(std::istream& is, int offset, std::ios_base::seekdir dir) const override
435+
{
436+
if constexpr (do_conversion)
437+
{
438+
is.seekg(/*bytes=*/sizeof(TValueFrom) * offset, dir);
439+
}
440+
else
441+
{
442+
is.seekg(/*bytes=*/sizeof(ValueT) * offset, dir);
443+
}
444+
}
445+
};
446+
447+
template<typename ValueT>
448+
inline const ConvertingReader<ValueT>& ConvertingReader<ValueT>::get(std::istream& is)
449+
{
450+
SharedPtr<io::StreamMetadata> meta = io::getStreamMetadataPtr(is);
451+
452+
auto ptr = static_cast<const ConvertingReader*>(meta->convertingReader());
453+
if(ptr)
454+
{
455+
return *ptr;
456+
}
457+
auto ptr2 = new TypedConvertingReader<ValueT, ValueT>();
458+
meta->setConvertingReader(Ptr(ptr2));
459+
return *ptr2;
460+
}
461+
462+
struct ConvertingReaderFactory
463+
{
464+
using ReaderPtr = ConvertingReaderBase::Ptr;
465+
466+
struct FactoryEntry
467+
{
468+
ReaderPtr reader;
469+
Name new_grid_value_type;
470+
};
471+
472+
private:
473+
template<typename T1, typename T2>
474+
static Name getTypeName()
475+
{
476+
return Name(typeNameAsString<T1>()) + Name(typeNameAsString<T2>());
477+
}
478+
479+
template<typename T1, typename T2, typename T3>
480+
static auto getMapEntry()
481+
{
482+
return std::pair
483+
{
484+
getTypeName<T1, T2>(),
485+
FactoryEntry { ReaderPtr(new TypedConvertingReader<T3, T1>()), typeNameAsString<T3>()}
486+
};
487+
}
488+
489+
public:
490+
static FactoryEntry create(const Name& grid_value_type, const Name& desired_scalar_type)
491+
{
492+
static std::unordered_map<Name, FactoryEntry> type_map =
493+
{
494+
getMapEntry<float, Half, Half>(),
495+
getMapEntry<double, Half, Half>(),
496+
getMapEntry<Vec2f, Half, Vec2H>(),
497+
getMapEntry<Vec2d, Half, Vec2H>(),
498+
getMapEntry<Vec3f, Half, Vec3H>(),
499+
getMapEntry<Vec3d, Half, Vec3H>(),
500+
};
501+
502+
if (auto it = type_map.find(grid_value_type + desired_scalar_type); it != type_map.end())
503+
{
504+
return it->second;
505+
}
506+
else
507+
{
508+
return {nullptr, ""};
509+
}
510+
}
511+
};
512+
513+
323514
template<typename T>
324515
inline size_t
325516
writeDataSize(const T *data, Index count, uint32_t compression)
@@ -474,6 +665,9 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
474665
const bool seek = (destBuf == nullptr);
475666
OPENVDB_ASSERT(!seek || (!meta || meta->seekable()));
476667

668+
// converting reader for reading grid values
669+
auto& convertingReader = io::ConvertingReader<ValueT>::get(is);
670+
477671
// Get delayed load metadata if it exists
478672

479673
DelayedLoadMetadata::Ptr delayLoadMeta;
@@ -512,16 +706,16 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
512706
{
513707
// Read one of at most two distinct inactive values.
514708
if (seek) {
515-
is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
709+
convertingReader.seekElement(is, 1, std::ios_base::cur);
516710
} else {
517-
is.read(reinterpret_cast<char*>(&inactiveVal0), /*bytes=*/sizeof(ValueT));
711+
convertingReader.read(is, inactiveVal0);
518712
}
519713
if (metadata == MASK_AND_TWO_INACTIVE_VALS) {
520714
// Read the second of two distinct inactive values.
521715
if (seek) {
522-
is.seekg(/*bytes=*/sizeof(ValueT), std::ios_base::cur);
716+
convertingReader.seekElement(is, 1, std::ios_base::cur);
523717
} else {
524-
is.read(reinterpret_cast<char*>(&inactiveVal1), /*bytes=*/sizeof(ValueT));
718+
convertingReader.read(is, inactiveVal1);
525719
}
526720
}
527721
}
@@ -558,13 +752,8 @@ readCompressedValues(std::istream& is, ValueT* destBuf, Index destCount,
558752
}
559753

560754
// Read in the buffer.
561-
if (fromHalf) {
562-
HalfReader<RealToHalf<ValueT>::isReal, ValueT>::read(
563-
is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get(), leafIndex);
564-
} else {
565-
readData<ValueT>(
566-
is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get(), leafIndex);
567-
}
755+
convertingReader.read(
756+
is, (seek ? nullptr : tempBuf), tempCount, compression, delayLoadMeta.get(), leafIndex, fromHalf);
568757

569758
// If mask compression is enabled and the number of active values read into
570759
// the temp buffer is smaller than the size of the destination buffer,

openvdb/openvdb/io/File.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,9 +280,9 @@ File::isOpen() const
280280

281281
bool
282282
#ifdef OPENVDB_USE_DELAYED_LOADING
283-
File::open(bool delayLoad, const MappedFile::Notifier& notifier)
283+
File::open(bool delayLoad, const MappedFile::Notifier& notifier, ScalarConversion conversion)
284284
#else
285-
File::open(bool /*delayLoad = true*/)
285+
File::open(bool /*delayLoad = true*/, ScalarConversion conversion)
286286
#endif // OPENVDB_USE_DELAYED_LOADING
287287
{
288288
if (isOpen()) {
@@ -367,6 +367,7 @@ File::open(bool /*delayLoad = true*/)
367367
// and other metadata.
368368
mImpl->mStreamMetadata.reset(new StreamMetadata);
369369
mImpl->mStreamMetadata->setSeekable(true);
370+
mImpl->mStreamMetadata->setDesiredScalarType(conversionToString(conversion));
370371
io::setStreamMetadataPtr(inputStream(), mImpl->mStreamMetadata, /*transfer=*/false);
371372
Archive::setFormatVersion(inputStream());
372373
Archive::setLibraryVersion(inputStream());

openvdb/openvdb/io/File.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ class OPENVDB_API File: public Archive
6464
/// @throw IoError if the file is not a valid VDB file.
6565
/// @return @c true if the file's UUID has changed since it was last read.
6666
/// @see setCopyMaxBytes
67-
bool open(bool delayLoad = true, const MappedFile::Notifier& = MappedFile::Notifier());
67+
bool open(bool delayLoad = true, const MappedFile::Notifier& = MappedFile::Notifier(),
68+
ScalarConversion conversion = ScalarConversion::None);
6869
#else
69-
bool open(bool /*delayLoad*/ = false);
70+
bool open(bool /*delayLoad*/ = false, ScalarConversion conversion = ScalarConversion::None);
7071
#endif
7172

7273
/// Return @c true if the file has been opened for reading.

openvdb/openvdb/io/GridDescriptor.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
#include <openvdb/Exceptions.h>
77
#include <openvdb/util/Name.h>
8+
#include <openvdb/io/Compression.h>
89
#include <sstream>
10+
#include <iostream>
911

1012

1113
namespace openvdb {
@@ -94,6 +96,26 @@ GridDescriptor::read(std::istream &is)
9496
}
9597
// else
9698
GridBase::Ptr grid = GridBase::createGrid(mGridType);
99+
100+
// change the type of grid if it is desired and possible
101+
Name desired_scalar_type = io::getStreamMetadataPtr(is)->desiredScalarType();
102+
Name grid_value_type = grid->valueType();
103+
104+
auto [reader, new_value_type] = io::ConvertingReaderFactory::create(grid_value_type, desired_scalar_type);
105+
if (reader)
106+
{
107+
Name newGridType = mGridType;
108+
newGridType.replace(newGridType.find(grid_value_type), grid_value_type.length(), new_value_type);
109+
110+
// Create the grid of the type if it has been registered.
111+
if (GridBase::isRegistered(newGridType)) {
112+
mGridType = newGridType;
113+
mConvertingReader = reader;
114+
grid = GridBase::createGrid(mGridType);
115+
}
116+
}
117+
118+
// TODO: store reader into grid/gd
97119
if (grid) grid->setSaveFloatAsHalf(mSaveFloatAsHalf);
98120

99121
// Read in the offsets.

0 commit comments

Comments
 (0)