Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions openvdb/openvdb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ set(OPENVDB_LIBRARY_POINTS_INCLUDE_FILES
points/PointConversion.h
points/PointCount.h
points/PointDataGrid.h
points/PointDataIO.h
points/PointDelete.h
points/PointGroup.h
points/PointMask.h
Expand Down
30 changes: 25 additions & 5 deletions openvdb/openvdb/Grid.h
Original file line number Diff line number Diff line change
Expand Up @@ -1181,14 +1181,34 @@ struct TreeAdapter<tree::ValueAccessor<_TreeType> >
////////////////////////////////////////


namespace points {

template<typename T, Index Log2Dim> class PointDataLeafNode;

/// @brief Type trait that evaluates to true only for @c PointDataLeafNode instantiations.
template<typename T>
struct IsPointDataLeafNode : std::false_type {};

template<typename T, Index Log2Dim>
struct IsPointDataLeafNode<PointDataLeafNode<T, Log2Dim>> : std::true_type {};

} // namespace points


/// @brief Metafunction that specifies whether a given leaf node, tree, or grid type
/// requires multiple passes to read and write voxel data
/// @details Multi-pass I/O allows one to optimize the data layout of leaf nodes
/// for certain access patterns during delayed loading.
/// @sa io::MultiPass
/// requires multiple passes to read and write voxel data.
/// @details Multi-pass I/O allows leaf nodes to optimize their serialization layout
/// for delayed-load access patterns. Only @c PointDataLeafNode supports multi-pass I/O.
/// Defining a custom leaf node that inherits @c io::PointDataGridMultiPass is no longer permitted.
/// @sa points::IsPointDataLeafNode
template<typename LeafNodeType>
struct HasMultiPassIO {
static const bool value = std::is_base_of<io::MultiPass, LeafNodeType>::value;
static_assert(
!std::is_base_of<io::PointDataGridMultiPass, LeafNodeType>::value
|| points::IsPointDataLeafNode<LeafNodeType>::value,
"Only PointDataLeafNode may inherit from io::PointDataGridMultiPass; "
"use points::IsPointDataLeafNode to test for multi-pass I/O support.");
static const bool value = points::IsPointDataLeafNode<LeafNodeType>::value;
};

// Partial specialization for Tree types
Expand Down
6 changes: 3 additions & 3 deletions openvdb/openvdb/io/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ std::ostream& operator<<(std::ostream&, const StreamMetadata::AuxDataMap&);
////////////////////////////////////////


/// @brief Leaf nodes that require multi-pass I/O must inherit from this struct.
/// @sa Grid::hasMultiPassIO()
struct MultiPass {};
/// @brief Tag for multi-pass I/O. Only points::PointDataLeafNode may inherit from this.
/// @sa Grid::hasMultiPassIO(), points::IsPointDataLeafNode
struct PointDataGridMultiPass {};


////////////////////////////////////////
Expand Down
2 changes: 0 additions & 2 deletions openvdb/openvdb/points/AttributeArray.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ AttributeArray::AttributeArray(const AttributeArray& rhs, const tbb::spin_mutex:
: mIsUniform(rhs.mIsUniform)
, mFlags(rhs.mFlags)
, mUsePagedRead(rhs.mUsePagedRead)
, mOutOfCore(rhs.mOutOfCore.load())
, mPageHandle()
{
if (mFlags & PARTIALREAD) mCompressedBytes = rhs.mCompressedBytes;
Expand All @@ -78,7 +77,6 @@ AttributeArray::operator=(const AttributeArray& rhs)
mIsUniform = rhs.mIsUniform;
mFlags = rhs.mFlags;
mUsePagedRead = rhs.mUsePagedRead;
mOutOfCore.store(rhs.mOutOfCore);
if (mFlags & PARTIALREAD) mCompressedBytes = rhs.mCompressedBytes;
else if (rhs.mPageHandle) mPageHandle = rhs.mPageHandle->copy();
else mPageHandle.reset();
Expand Down
8 changes: 3 additions & 5 deletions openvdb/openvdb/points/AttributeArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,12 @@ class OPENVDB_API AttributeArray
mutable tbb::spin_mutex mMutex;
uint8_t mFlags = 0;
uint8_t mUsePagedRead = 0;
#if OPENVDB_ABI_VERSION_NUMBER < 14
std::atomic<Index32> mOutOfCore{0}; // interpreted as bool
#endif
/// used for out-of-core, paged reading
union {
compression::PageHandle::Ptr mPageHandle;
std::unique_ptr<compression::PageHandle> mPageHandle;
size_t mCompressedBytes;
};
}; // class AttributeArray
Expand Down Expand Up @@ -1535,8 +1537,6 @@ TypedAttributeArray<ValueType_, Codec_>::readBuffers(std::istream& is)
OPENVDB_THROW(IoError, "Cannot read paged AttributeArray buffers.");
}

tbb::spin_mutex::scoped_lock lock(mMutex);

this->deallocate();

uint8_t bloscCompressed(0);
Expand Down Expand Up @@ -1586,8 +1586,6 @@ TypedAttributeArray<ValueType_, Codec_>::readPagedBuffers(compression::PagedInpu

OPENVDB_ASSERT(mPageHandle);

tbb::spin_mutex::scoped_lock lock(mMutex);

this->deallocate();

is.read(mPageHandle, std::streamsize(mPageHandle->size()), false);
Expand Down
132 changes: 3 additions & 129 deletions openvdb/openvdb/points/PointDataGrid.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,140 +31,14 @@
#include <utility> // std::pair, std::make_pair
#include <vector>

#include <openvdb/points/PointDataIO.h> // io::readCompressedValues(), io::writeCompressedValues(), io::writeCompressedValuesSize()

class TestPointDataLeaf;

namespace openvdb {
OPENVDB_USE_VERSION_NAMESPACE
namespace OPENVDB_VERSION_NAME {

namespace io
{

/// @brief openvdb::io::readCompressedValues specialized on PointDataIndex32 arrays to
/// ignore the value mask, use a larger block size and use 16-bit size instead of 64-bit
template<>
inline void
readCompressedValues( std::istream& is, PointDataIndex32* destBuf, Index destCount,
const util::NodeMask<3>& /*valueMask*/, bool /*fromHalf*/)
{
using compression::bloscDecompress;

const bool seek = destBuf == nullptr;

const size_t destBytes = destCount*sizeof(PointDataIndex32);
const size_t maximumBytes = std::numeric_limits<uint16_t>::max();
if (destBytes >= maximumBytes) {
OPENVDB_THROW(openvdb::IoError, "Cannot read more than " <<
maximumBytes << " bytes in voxel values.")
}

uint16_t bytes16;

const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(is);

if (seek && meta) {
// buffer size temporarily stored in the StreamMetadata pass
// to avoid having to perform an expensive disk read for 2-bytes
bytes16 = static_cast<uint16_t>(meta->pass());
// seek over size of the compressed buffer
is.seekg(sizeof(uint16_t), std::ios_base::cur);
}
else {
// otherwise read from disk
is.read(reinterpret_cast<char*>(&bytes16), sizeof(uint16_t));
}

if (bytes16 == std::numeric_limits<uint16_t>::max()) {
// read or seek uncompressed data
if (seek) {
is.seekg(destBytes, std::ios_base::cur);
}
else {
is.read(reinterpret_cast<char*>(destBuf), destBytes);
}
}
else {
// read or seek uncompressed data
if (seek) {
is.seekg(int(bytes16), std::ios_base::cur);
}
else {
// decompress into the destination buffer
std::unique_ptr<char[]> bloscBuffer(new char[int(bytes16)]);
is.read(bloscBuffer.get(), bytes16);
std::unique_ptr<char[]> buffer = bloscDecompress( bloscBuffer.get(),
destBytes,
/*resize=*/false);
std::memcpy(destBuf, buffer.get(), destBytes);
}
}
}

/// @brief openvdb::io::writeCompressedValues specialized on PointDataIndex32 arrays to
/// ignore the value mask, use a larger block size and use 16-bit size instead of 64-bit
template<>
inline void
writeCompressedValues( std::ostream& os, const PointDataIndex32* srcBuf, Index srcCount,
const util::NodeMask<3>& /*valueMask*/,
const util::NodeMask<3>& /*childMask*/, bool /*toHalf*/)
{
using compression::bloscCompress;

const size_t srcBytes = srcCount*sizeof(PointDataIndex32);
const size_t maximumBytes = std::numeric_limits<uint16_t>::max();
if (srcBytes >= maximumBytes) {
OPENVDB_THROW(openvdb::IoError, "Cannot write more than " <<
maximumBytes << " bytes in voxel values.")
}

const char* charBuffer = reinterpret_cast<const char*>(srcBuf);

size_t compressedBytes;
std::unique_ptr<char[]> buffer = bloscCompress( charBuffer, srcBytes,
compressedBytes, /*resize=*/false);

if (compressedBytes > 0) {
auto bytes16 = static_cast<uint16_t>(compressedBytes); // clamp to 16-bit unsigned integer
os.write(reinterpret_cast<const char*>(&bytes16), sizeof(uint16_t));
os.write(reinterpret_cast<const char*>(buffer.get()), compressedBytes);
}
else {
auto bytes16 = static_cast<uint16_t>(maximumBytes); // max value indicates uncompressed
os.write(reinterpret_cast<const char*>(&bytes16), sizeof(uint16_t));
os.write(reinterpret_cast<const char*>(srcBuf), srcBytes);
}
}

template <typename T>
inline void
writeCompressedValuesSize(std::ostream& os, const T* srcBuf, Index srcCount)
{
using compression::bloscCompressedSize;

const size_t srcBytes = srcCount*sizeof(T);
const size_t maximumBytes = std::numeric_limits<uint16_t>::max();
if (srcBytes >= maximumBytes) {
OPENVDB_THROW(openvdb::IoError, "Cannot write more than " <<
maximumBytes << " bytes in voxel values.")
}

const char* charBuffer = reinterpret_cast<const char*>(srcBuf);

// calculate voxel buffer size after compression
size_t compressedBytes = bloscCompressedSize(charBuffer, srcBytes);

if (compressedBytes > 0) {
auto bytes16 = static_cast<uint16_t>(compressedBytes); // clamp to 16-bit unsigned integer
os.write(reinterpret_cast<const char*>(&bytes16), sizeof(uint16_t));
}
else {
auto bytes16 = static_cast<uint16_t>(maximumBytes); // max value indicates uncompressed
os.write(reinterpret_cast<const char*>(&bytes16), sizeof(uint16_t));
}
}

} // namespace io


// forward declaration
namespace tree {
Expand Down Expand Up @@ -233,7 +107,7 @@ prefetch(PointDataTreeT&, bool /*position*/ = true, bool /*otherAttributes*/ = t


template <typename T, Index Log2Dim>
class PointDataLeafNode : public tree::LeafNode<T, Log2Dim>, io::MultiPass {
class PointDataLeafNode : public tree::LeafNode<T, Log2Dim>, io::PointDataGridMultiPass {

public:
using LeafNodeType = PointDataLeafNode<T, Log2Dim>;
Expand Down
Loading
Loading