Skip to content

Commit 732392b

Browse files
committed
Add ReadDiagnostics to Codecs
Signed-off-by: Dan Bailey <danbailey@ilm.com>
1 parent 5daaf27 commit 732392b

7 files changed

Lines changed: 189 additions & 16 deletions

File tree

openvdb/openvdb/codecs/BoolCodec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ struct OPENVDB_API BoolCodec : public TopologyCodec<GridT>
110110

111111
static inline std::string name() { return GridT::gridType(); }
112112

113-
void readBuffers(std::istream& is, io::CodecData& data, const io::ReadOptions& options) final
113+
void readBuffers(std::istream& is, io::CodecData& data, const io::ReadOptions& options, io::ReadDiagnostics&) final
114114
{
115115
GridT& grid = static_cast<GridT&>(*data.grid);
116116

openvdb/openvdb/codecs/TopologyCodec.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,22 @@ struct ReadTopologyOp
107107
using LeafT = typename TreeT::LeafNodeType;
108108
using StorageValueT = typename StorageTreeT::ValueType;
109109

110-
ReadTopologyOp(std::istream& _is, bool _saveFloatAsHalf)
110+
ReadTopologyOp(std::istream& _is, bool _saveFloatAsHalf, io::ReadDiagnostics& _diagnostics,
111+
const std::string& _gridName)
111112
: is(_is)
112-
, saveFloatAsHalf(_saveFloatAsHalf) { }
113+
, saveFloatAsHalf(_saveFloatAsHalf)
114+
, diagnostics(_diagnostics)
115+
, gridName(_gridName) { }
113116

114117
void operator()(RootT& root)
115118
{
116119
using ChildT = typename RootT::ChildNodeType;
117120

118121
int32_t bufferCount;
119122
is.read(reinterpret_cast<char*>(&bufferCount), sizeof(int32_t));
120-
if (bufferCount != 1) OPENVDB_LOG_WARN("multi-buffer trees are no longer supported");
123+
if (bufferCount != 1) {
124+
diagnostics.addWarning(gridName, "multi-buffer trees are no longer supported");
125+
}
121126

122127
// Delete the existing tree.
123128
root.clear();
@@ -216,6 +221,8 @@ struct ReadTopologyOp
216221
std::istream& is;
217222
bool saveFloatAsHalf;
218223
ValueT background;
224+
io::ReadDiagnostics& diagnostics;
225+
std::string gridName;
219226
}; // struct ReadTopologyOp
220227

221228
template <typename TreeT>
@@ -264,14 +271,14 @@ void setTilesToBackground(TreeT& tree)
264271

265272
// Free-standing function for read case (supports type conversion via StorageGridT)
266273
template<typename GridT, typename StorageGridT = GridT>
267-
void topologyCodecReadTopology(GridBase& gridBase, std::istream& is, const io::ReadOptions& options)
274+
void topologyCodecReadTopology(GridBase& gridBase, std::istream& is, const io::ReadOptions& options, io::ReadDiagnostics& diagnostics)
268275
{
269276
io::checkFormatVersion(is);
270277

271278
GridT& grid = static_cast<GridT&>(gridBase);
272279
grid.tree().clearAllAccessors();
273280

274-
internal::ReadTopologyOp<typename GridT::TreeType, typename StorageGridT::TreeType> readTopologyOp(is, grid.saveFloatAsHalf());
281+
internal::ReadTopologyOp<typename GridT::TreeType, typename StorageGridT::TreeType> readTopologyOp(is, grid.saveFloatAsHalf(), diagnostics, grid.getName());
275282
readTopologyOp(grid.tree().root());
276283

277284
if (options.readMode == io::ReadMode::TopologyOnly) {
@@ -307,9 +314,10 @@ struct OPENVDB_API TopologyCodec : public io::Codec
307314
return data;
308315
}
309316

310-
void readTopology(std::istream& is, io::CodecData& data, const io::ReadOptions& options) final
317+
void readTopology(std::istream& is, io::CodecData& data, const io::ReadOptions& options,
318+
io::ReadDiagnostics& diagnostics) final
311319
{
312-
internal::topologyCodecReadTopology<GridT, StorageGridT>(*data.grid, is, options);
320+
internal::topologyCodecReadTopology<GridT, StorageGridT>(*data.grid, is, options, diagnostics);
313321
}
314322

315323
void writeTopology(std::ostream& os, const GridBase& gridBase, const io::WriteOptions&) final

openvdb/openvdb/io/Archive.cc

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,35 @@ Archive::copy() const
384384
}
385385

386386

387+
void
388+
Archive::enableReadDiagnostics()
389+
{
390+
mReadDiagnostics.enable();
391+
}
392+
393+
394+
void
395+
Archive::disableReadDiagnostics()
396+
{
397+
mReadDiagnostics.disable();
398+
mReadDiagnostics.clear();
399+
}
400+
401+
402+
const ReadDiagnostics&
403+
Archive::readDiagnostics() const
404+
{
405+
return mReadDiagnostics;
406+
}
407+
408+
409+
void
410+
Archive::clearReadDiagnostics()
411+
{
412+
mReadDiagnostics.clear();
413+
}
414+
415+
387416
////////////////////////////////////////
388417

389418

@@ -898,7 +927,7 @@ Archive::connectInstance(const GridDescriptor& gd, const NamedGridMap& grids) co
898927

899928

900929
GridBase::Ptr
901-
Archive::readGrid(const GridDescriptor& gd, std::istream& is, const io::ReadOptions& readOptions)
930+
Archive::readGrid(const GridDescriptor& gd, std::istream& is, const io::ReadOptions& readOptions, ReadDiagnostics& diagnostics)
902931
{
903932
// Read the compression settings for this grid and tag the stream with them
904933
// so that downstream functions can reference them.
@@ -964,13 +993,13 @@ Archive::readGrid(const GridDescriptor& gd, std::istream& is, const io::ReadOpti
964993
if (readOptions.readMode != io::ReadMode::TopologyOnly && !gd.isInstance()) {
965994
// read topology
966995
if (codec) {
967-
codec->readTopology(is, *codecData, readOptions);
996+
codec->readTopology(is, *codecData, readOptions, diagnostics);
968997
} else {
969998
grid->readTopology(is);
970999
}
9711000
// read buffers
9721001
if (codec) {
973-
codec->readBuffers(is, *codecData, readOptions);
1002+
codec->readBuffers(is, *codecData, readOptions, diagnostics);
9741003
} else {
9751004
const auto& worldBBox = readOptions.clipBBox;
9761005
const bool clip = worldBBox.isSorted();
@@ -987,6 +1016,14 @@ Archive::readGrid(const GridDescriptor& gd, std::istream& is, const io::ReadOpti
9871016
}
9881017

9891018

1019+
GridBase::Ptr
1020+
Archive::readGrid(const GridDescriptor& gd, std::istream& is, const io::ReadOptions& readOptions)
1021+
{
1022+
ReadDiagnostics nullDiagnostics;
1023+
return readGrid(gd, is, readOptions, nullDiagnostics);
1024+
}
1025+
1026+
9901027
////////////////////////////////////////
9911028

9921029

openvdb/openvdb/io/Archive.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ class OPENVDB_API Archive
9898
/// @brief Return @c false (delayed loading has been removed).
9999
static bool isDelayedLoadingEnabled() { return false; }
100100

101+
/// @brief Enable collection of read diagnostics (warnings, etc.) during I/O operations.
102+
void enableReadDiagnostics();
103+
104+
/// @brief Disable collection of read diagnostics.
105+
void disableReadDiagnostics();
106+
107+
/// @brief Return a const reference to the diagnostics collector.
108+
const ReadDiagnostics& readDiagnostics() const;
109+
110+
/// @brief Clear the diagnostics list while keeping collection active.
111+
void clearReadDiagnostics();
112+
101113
protected:
102114
/// @brief Return @c true if the input stream contains grid offsets
103115
/// that allow for random access or partial reading.
@@ -136,6 +148,8 @@ class OPENVDB_API Archive
136148

137149
/// @brief Read in and create the grid represented by the given grid descriptor using the
138150
/// given input stream, using the provided options if given.
151+
static GridBase::Ptr readGrid(const GridDescriptor&, std::istream&,
152+
const io::ReadOptions& readOptions, ReadDiagnostics& diagnostics);
139153
static GridBase::Ptr readGrid(const GridDescriptor&, std::istream&,
140154
const io::ReadOptions& readOptions = io::ReadOptions{});
141155

@@ -173,6 +187,9 @@ class OPENVDB_API Archive
173187
const io::WriteOptions& writeOptions = io::WriteOptions{}) const;
174188
//@}
175189

190+
/// Diagnostics collector for read operations (always valid; enabled/disabled via flag)
191+
mutable ReadDiagnostics mReadDiagnostics;
192+
176193
private:
177194
friend class ::TestFile;
178195

openvdb/openvdb/io/Codec.h

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <unordered_map>
99
#include <memory>
1010
#include <iostream>
11+
#include <vector>
12+
#include <mutex>
1113

1214
#include <openvdb/Types.h>
1315

@@ -71,6 +73,49 @@ struct OPENVDB_API WriteOptions
7173
{
7274
}; // struct WriteOptions
7375

76+
enum class DiagnosticSeverity { Warning };
77+
78+
struct ReadDiagnostic {
79+
DiagnosticSeverity severity;
80+
std::string context;
81+
std::string message;
82+
};
83+
84+
struct OPENVDB_API ReadDiagnostics {
85+
ReadDiagnostics() = default;
86+
ReadDiagnostics(const ReadDiagnostics& other) {
87+
std::lock_guard<std::mutex> lock(other.mMutex);
88+
mEnabled = other.mEnabled;
89+
mDiagnostics = other.mDiagnostics;
90+
}
91+
ReadDiagnostics& operator=(const ReadDiagnostics& other) {
92+
if (this != &other) {
93+
std::scoped_lock lock(mMutex, other.mMutex);
94+
mEnabled = other.mEnabled;
95+
mDiagnostics = other.mDiagnostics;
96+
}
97+
return *this;
98+
}
99+
void addWarning(const std::string& context, const std::string& message) {
100+
if (!mEnabled) return;
101+
std::lock_guard<std::mutex> lock(mMutex);
102+
mDiagnostics.push_back({DiagnosticSeverity::Warning, context, message});
103+
}
104+
void clear() {
105+
std::lock_guard<std::mutex> lock(mMutex);
106+
mDiagnostics.clear();
107+
}
108+
void enable() { mEnabled = true; }
109+
void disable() { mEnabled = false; }
110+
bool enabled() const { return mEnabled; }
111+
const std::vector<ReadDiagnostic>& diagnostics() const { return mDiagnostics; }
112+
bool empty() const { return mDiagnostics.empty(); }
113+
private:
114+
bool mEnabled = false;
115+
std::vector<ReadDiagnostic> mDiagnostics;
116+
mutable std::mutex mMutex;
117+
}; // struct ReadDiagnostics
118+
74119
/// Per-codec mutable data created by Codec::createData()
75120
/// Contains the grid and any codec-specific state
76121
struct OPENVDB_API CodecData
@@ -93,10 +138,10 @@ struct OPENVDB_API Codec
93138
virtual CodecData::Ptr createData() = 0;
94139

95140
/// Read the grid topology
96-
virtual void readTopology(std::istream&, CodecData&, const ReadOptions&) { }
141+
virtual void readTopology(std::istream&, CodecData&, const ReadOptions&, ReadDiagnostics&) { }
97142

98143
/// Read all data buffers for this grid
99-
virtual void readBuffers(std::istream&, CodecData&, const ReadOptions&) { }
144+
virtual void readBuffers(std::istream&, CodecData&, const ReadOptions&, ReadDiagnostics&) { }
100145

101146
/// Write the grid topology
102147
virtual void writeTopology(std::ostream&, const GridBase&, const WriteOptions&) { }

openvdb/openvdb/io/File.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ File::getGrids(const io::ReadOptions& readOptions) const
319319
const GridDescriptor& gd = i->second;
320320
// Seek to the grid in the file.
321321
gd.seekToGrid(inputStream());
322-
GridBase::Ptr grid = Archive::readGrid(gd, inputStream(), readOptions);
322+
GridBase::Ptr grid = Archive::readGrid(gd, inputStream(), readOptions, mReadDiagnostics);
323323
ret->push_back(grid);
324324
namedGrids[gd.uniqueName()] = grid;
325325
}
@@ -477,7 +477,7 @@ File::readGrid(const Name& name, const io::ReadOptions& readOptions)
477477
OPENVDB_ASSERT(inputHasGridOffsets());
478478
// Seek to the grid in the file.
479479
gd.seekToGrid(inputStream());
480-
grid = Archive::readGrid(gd, inputStream(), readOptions);
480+
grid = Archive::readGrid(gd, inputStream(), readOptions, mReadDiagnostics);
481481

482482
if (gd.isInstance()) {
483483
/// @todo Refactor to share code with Archive::connectInstance()?
@@ -493,7 +493,7 @@ File::readGrid(const Name& name, const io::ReadOptions& readOptions)
493493
GridBase::Ptr parent;
494494
OPENVDB_ASSERT(inputHasGridOffsets());
495495
parentIt->second.seekToGrid(inputStream());
496-
parent = Archive::readGrid(parentIt->second, inputStream(), readOptions);
496+
parent = Archive::readGrid(parentIt->second, inputStream(), readOptions, mReadDiagnostics);
497497
if (parent) grid->setTree(parent->baseTreePtr());
498498
}
499499
return grid;
@@ -592,6 +592,7 @@ File::endName() const
592592
return File::NameIterator(mGridDescriptors.end());
593593
}
594594

595+
595596
} // namespace io
596597
} // namespace OPENVDB_VERSION_NAME
597598
} // namespace openvdb

openvdb/openvdb/unittest/TestCodec.cc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,71 @@ TEST_F(TestCodec, testCodecRegistry)
7777
}
7878

7979

80+
TEST_F(TestCodec, testReadDiagnostics)
81+
{
82+
using namespace openvdb;
83+
using namespace openvdb::io;
84+
85+
// ReadDiagnostics struct: disabled by default, addWarning is a no-op until enabled
86+
{
87+
ReadDiagnostics diags;
88+
EXPECT_FALSE(diags.enabled());
89+
diags.addWarning("grid_a", "something went wrong");
90+
EXPECT_TRUE(diags.diagnostics().empty());
91+
92+
diags.enable();
93+
diags.addWarning("grid_a", "something went wrong");
94+
ASSERT_EQ(diags.diagnostics().size(), size_t(1));
95+
EXPECT_EQ(diags.diagnostics()[0].severity, DiagnosticSeverity::Warning);
96+
97+
diags.clear();
98+
EXPECT_TRUE(diags.diagnostics().empty());
99+
}
100+
101+
CodecRegistry::clear();
102+
openvdb::io::internal::initialize();
103+
104+
// Archive API and getGrids() with diagnostics
105+
106+
BoolGrid::Ptr srcGrid = BoolGrid::create(false);
107+
srcGrid->setName("bool_grid");
108+
srcGrid->fill(CoordBBox(Coord(-5), Coord(5)), true, true);
109+
110+
const std::string codecPath = "testReadDiagnostics.vdb";
111+
{
112+
io::File f(codecPath);
113+
f.write(GridPtrVec{srcGrid});
114+
}
115+
116+
// Disabled by default; enabling produces no warnings on a clean read
117+
{
118+
io::File f(codecPath);
119+
f.open();
120+
EXPECT_FALSE(f.readDiagnostics().enabled());
121+
f.enableReadDiagnostics();
122+
EXPECT_TRUE(f.readDiagnostics().enabled());
123+
f.readGrid("bool_grid");
124+
EXPECT_TRUE(f.readDiagnostics().diagnostics().empty());
125+
f.close();
126+
}
127+
128+
// clearReadDiagnostics() resets entries but keeps diagnostics enabled
129+
{
130+
io::File f(codecPath);
131+
f.open();
132+
f.enableReadDiagnostics();
133+
GridPtrVecPtr grids = f.getGrids();
134+
ASSERT_TRUE(grids && !grids->empty());
135+
f.clearReadDiagnostics();
136+
EXPECT_TRUE(f.readDiagnostics().enabled());
137+
EXPECT_TRUE(f.readDiagnostics().diagnostics().empty());
138+
f.close();
139+
}
140+
141+
std::remove(codecPath.c_str());
142+
}
143+
144+
80145
template <typename GridT>
81146
void testIOImpl(
82147
const std::string& gridName,

0 commit comments

Comments
 (0)