Skip to content

Commit 3e57470

Browse files
committed
Add ReadDiagnostics to Codecs
Signed-off-by: Dan Bailey <danbailey@ilm.com>
1 parent fc50e0a commit 3e57470

7 files changed

Lines changed: 228 additions & 17 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+
if (diagnostics) 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 = nullptr;
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: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,27 @@ Archive::copy() const
384384
}
385385

386386

387+
void
388+
Archive::enableReadDiagnostics()
389+
{
390+
if (!mReadDiagnostics) mReadDiagnostics = std::make_shared<ReadDiagnostics>();
391+
}
392+
393+
394+
void
395+
Archive::disableReadDiagnostics()
396+
{
397+
mReadDiagnostics.reset();
398+
}
399+
400+
401+
ReadDiagnostics*
402+
Archive::readDiagnostics() const
403+
{
404+
return mReadDiagnostics.get();
405+
}
406+
407+
387408
////////////////////////////////////////
388409

389410

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

899920

900921
GridBase::Ptr
901-
Archive::readGrid(const GridDescriptor& gd, std::istream& is, const io::ReadOptions& readOptions)
922+
Archive::readGrid(const GridDescriptor& gd, std::istream& is, const io::ReadOptions& readOptions, ReadDiagnostics* diagnostics)
902923
{
903924
// Read the compression settings for this grid and tag the stream with them
904925
// so that downstream functions can reference them.
@@ -964,13 +985,13 @@ Archive::readGrid(const GridDescriptor& gd, std::istream& is, const io::ReadOpti
964985
if (readOptions.readMode != io::ReadMode::TopologyOnly && !gd.isInstance()) {
965986
// read topology
966987
if (codec) {
967-
codec->readTopology(is, *codecData, readOptions);
988+
codec->readTopology(is, *codecData, readOptions, diagnostics);
968989
} else {
969990
grid->readTopology(is);
970991
}
971992
// read buffers
972993
if (codec) {
973-
codec->readBuffers(is, *codecData, readOptions);
994+
codec->readBuffers(is, *codecData, readOptions, diagnostics);
974995
} else {
975996
const auto& worldBBox = readOptions.clipBBox;
976997
const bool clip = worldBBox.isSorted();

openvdb/openvdb/io/Archive.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ 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 pointer to the diagnostics collector, or nullptr if disabled.
108+
ReadDiagnostics* readDiagnostics() const;
109+
101110
protected:
102111
/// @brief Return @c true if the input stream contains grid offsets
103112
/// that allow for random access or partial reading.
@@ -137,7 +146,7 @@ class OPENVDB_API Archive
137146
/// @brief Read in and create the grid represented by the given grid descriptor using the
138147
/// given input stream, using the provided options if given.
139148
static GridBase::Ptr readGrid(const GridDescriptor&, std::istream&,
140-
const io::ReadOptions& readOptions = io::ReadOptions{});
149+
const io::ReadOptions& readOptions = io::ReadOptions{}, ReadDiagnostics* = nullptr);
141150

142151
using NamedGridMap = std::map<Name /*uniqueName*/, GridBase::Ptr>;
143152

@@ -192,6 +201,8 @@ class OPENVDB_API Archive
192201
uint32_t mCompression;
193202
/// Flag indicating whether grid statistics metadata should be written
194203
bool mEnableGridStats;
204+
/// Diagnostics collector for read operations
205+
std::shared_ptr<ReadDiagnostics> mReadDiagnostics;
195206
}; // class Archive
196207

197208
} // namespace io

openvdb/openvdb/io/Codec.h

Lines changed: 28 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,30 @@ 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+
void addWarning(const std::string& context, const std::string& message) {
86+
std::lock_guard<std::mutex> lock(mMutex);
87+
mDiagnostics.push_back({DiagnosticSeverity::Warning, context, message});
88+
}
89+
void clear() {
90+
std::lock_guard<std::mutex> lock(mMutex);
91+
mDiagnostics.clear();
92+
}
93+
const std::vector<ReadDiagnostic>& diagnostics() const { return mDiagnostics; }
94+
bool empty() const { return mDiagnostics.empty(); }
95+
private:
96+
std::vector<ReadDiagnostic> mDiagnostics;
97+
mutable std::mutex mMutex;
98+
}; // struct ReadDiagnostics
99+
74100
/// Per-codec mutable data created by Codec::createData()
75101
/// Contains the grid and any codec-specific state
76102
struct OPENVDB_API CodecData
@@ -93,10 +119,10 @@ struct OPENVDB_API Codec
93119
virtual CodecData::Ptr createData() = 0;
94120

95121
/// Read the grid topology
96-
virtual void readTopology(std::istream&, CodecData&, const ReadOptions&) { }
122+
virtual void readTopology(std::istream&, CodecData&, const ReadOptions&, ReadDiagnostics*) { }
97123

98124
/// Read all data buffers for this grid
99-
virtual void readBuffers(std::istream&, CodecData&, const ReadOptions&) { }
125+
virtual void readBuffers(std::istream&, CodecData&, const ReadOptions&, ReadDiagnostics*) { }
100126

101127
/// Write the grid topology
102128
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, readDiagnostics());
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, readDiagnostics());
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, readDiagnostics());
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: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,147 @@ TEST_F(TestCodec, testBoolCodecIO)
213213
std::remove(rawPath.c_str());
214214
std::remove(codecPath.c_str());
215215
}
216+
217+
218+
TEST_F(TestCodec, testReadDiagnosticsStruct)
219+
{
220+
using namespace openvdb::io;
221+
222+
// Default-constructed: no diagnostics
223+
ReadDiagnostics diags;
224+
EXPECT_TRUE(diags.diagnostics().empty());
225+
226+
// addWarning accumulates entries
227+
diags.addWarning("grid_a", "something went wrong");
228+
ASSERT_EQ(diags.diagnostics().size(), size_t(1));
229+
EXPECT_EQ(diags.diagnostics()[0].severity, DiagnosticSeverity::Warning);
230+
EXPECT_EQ(diags.diagnostics()[0].context, std::string("grid_a"));
231+
EXPECT_EQ(diags.diagnostics()[0].message, std::string("something went wrong"));
232+
233+
diags.addWarning("grid_b", "another issue");
234+
EXPECT_EQ(diags.diagnostics().size(), size_t(2));
235+
236+
// clear() empties the vector
237+
diags.clear();
238+
EXPECT_TRUE(diags.diagnostics().empty());
239+
}
240+
241+
242+
TEST_F(TestCodec, testReadDiagnosticsArchiveAPI)
243+
{
244+
using namespace openvdb;
245+
using namespace openvdb::io;
246+
247+
openvdb::initialize();
248+
CodecRegistry::clear();
249+
io::internal::initialize();
250+
251+
BoolGrid::Ptr srcGrid = BoolGrid::create(false);
252+
srcGrid->setName("bool_grid");
253+
srcGrid->fill(CoordBBox(Coord(-5), Coord(5)), true, true);
254+
255+
const std::string codecPath = "testReadDiagnosticsArchiveAPI.vdb";
256+
257+
{
258+
io::File f(codecPath);
259+
f.write(GridPtrVec{srcGrid});
260+
}
261+
262+
// Without enableReadDiagnostics: readDiagnostics() returns nullptr
263+
{
264+
io::File f(codecPath);
265+
f.open();
266+
EXPECT_EQ(f.readDiagnostics(), nullptr);
267+
f.readGrid("bool_grid");
268+
EXPECT_EQ(f.readDiagnostics(), nullptr);
269+
f.close();
270+
}
271+
272+
// With enableReadDiagnostics: readDiagnostics() returns a pointer, normal read has no warnings
273+
{
274+
io::File f(codecPath);
275+
f.open();
276+
f.enableReadDiagnostics();
277+
EXPECT_NE(f.readDiagnostics(), nullptr);
278+
f.readGrid("bool_grid");
279+
EXPECT_NE(f.readDiagnostics(), nullptr);
280+
EXPECT_TRUE(f.readDiagnostics()->diagnostics().empty());
281+
f.close();
282+
}
283+
284+
// clearReadDiagnostics() empties the list but keeps collection active
285+
{
286+
io::File f(codecPath);
287+
f.open();
288+
f.enableReadDiagnostics();
289+
f.readGrid("bool_grid");
290+
f.readDiagnostics()->clear();
291+
EXPECT_NE(f.readDiagnostics(), nullptr);
292+
EXPECT_TRUE(f.readDiagnostics()->diagnostics().empty());
293+
f.close();
294+
}
295+
296+
// enableReadDiagnostics() is idempotent
297+
{
298+
io::File f(codecPath);
299+
f.open();
300+
f.enableReadDiagnostics();
301+
const io::ReadDiagnostics* ptr = f.readDiagnostics();
302+
f.enableReadDiagnostics();
303+
EXPECT_EQ(f.readDiagnostics(), ptr);
304+
f.close();
305+
}
306+
307+
// Cleanup
308+
CodecRegistry::clear();
309+
std::remove(codecPath.c_str());
310+
}
311+
312+
313+
TEST_F(TestCodec, testReadDiagnosticsGetGrids)
314+
{
315+
using namespace openvdb;
316+
using namespace openvdb::io;
317+
318+
openvdb::initialize();
319+
CodecRegistry::clear();
320+
io::internal::initialize();
321+
322+
BoolGrid::Ptr srcGrid = BoolGrid::create(false);
323+
srcGrid->setName("bool_grid");
324+
srcGrid->fill(CoordBBox(Coord(-5), Coord(5)), true, true);
325+
326+
const std::string codecPath = "testReadDiagnosticsGetGrids.vdb";
327+
328+
{
329+
io::File f(codecPath);
330+
f.write(GridPtrVec{srcGrid});
331+
}
332+
333+
// getGrids() with diagnostics enabled — normal read produces no warnings
334+
{
335+
io::File f(codecPath);
336+
f.open();
337+
f.enableReadDiagnostics();
338+
GridPtrVecPtr grids = f.getGrids();
339+
ASSERT_TRUE(grids && !grids->empty());
340+
EXPECT_NE(f.readDiagnostics(), nullptr);
341+
EXPECT_TRUE(f.readDiagnostics()->diagnostics().empty());
342+
f.close();
343+
}
344+
345+
// clearReadDiagnostics() between getGrids() calls resets state
346+
{
347+
io::File f(codecPath);
348+
f.open();
349+
f.enableReadDiagnostics();
350+
f.getGrids();
351+
f.readDiagnostics()->clear();
352+
EXPECT_TRUE(f.readDiagnostics()->diagnostics().empty());
353+
f.close();
354+
}
355+
356+
// Cleanup
357+
CodecRegistry::clear();
358+
std::remove(codecPath.c_str());
359+
}

0 commit comments

Comments
 (0)