From 2fc636c276435a7e31aacb8ec535234bb4cea92d Mon Sep 17 00:00:00 2001 From: NAThompson Date: Mon, 11 May 2020 17:08:44 -0400 Subject: [PATCH 01/27] Test BOV reader. Fix slow WritePortal().Set(idx, val) loop. --- vtkm/io/BOVDataSetReader.h | 7 +- vtkm/io/testing/CMakeLists.txt | 1 + vtkm/io/testing/UnitTestBOVDataSetReader.cxx | 69 ++++++++++++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 vtkm/io/testing/UnitTestBOVDataSetReader.cxx diff --git a/vtkm/io/BOVDataSetReader.h b/vtkm/io/BOVDataSetReader.h index 95b7e28ac..c8981a800 100644 --- a/vtkm/io/BOVDataSetReader.h +++ b/vtkm/io/BOVDataSetReader.h @@ -214,8 +214,9 @@ private: std::vector buff; ReadBuffer(fName, nTuples, buff); var.Allocate(nTuples); + auto writePortal = var.WritePortal(); for (vtkm::Id i = 0; i < nTuples; i++) - var.WritePortal().Set(i, buff[(size_t)i]); + writePortal.Set(i, buff[(size_t)i]); } template @@ -228,12 +229,13 @@ private: var.Allocate(nTuples); vtkm::Vec v; + auto writePortal = var.WritePortal(); for (vtkm::Id i = 0; i < nTuples; i++) { v[0] = buff[static_cast(i * 3 + 0)]; v[1] = buff[static_cast(i * 3 + 1)]; v[2] = buff[static_cast(i * 3 + 2)]; - var.WritePortal().Set(i, v); + writePortal.Set(i, v); } } @@ -242,7 +244,6 @@ private: vtkm::cont::DataSet DataSet; }; } -} } // vtkm::io #endif // vtk_m_io_BOVReader_h diff --git a/vtkm/io/testing/CMakeLists.txt b/vtkm/io/testing/CMakeLists.txt index 39c832efe..b0e219f1c 100644 --- a/vtkm/io/testing/CMakeLists.txt +++ b/vtkm/io/testing/CMakeLists.txt @@ -9,6 +9,7 @@ ##============================================================================ set(unit_tests + UnitTestBOVDataSetReader.cxx UnitTestVTKDataSetReader.cxx UnitTestVTKDataSetWriter.cxx ) diff --git a/vtkm/io/testing/UnitTestBOVDataSetReader.cxx b/vtkm/io/testing/UnitTestBOVDataSetReader.cxx new file mode 100644 index 000000000..833d23d85 --- /dev/null +++ b/vtkm/io/testing/UnitTestBOVDataSetReader.cxx @@ -0,0 +1,69 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include +#include +#include + +namespace +{ + +inline vtkm::cont::DataSet readBOVDataSet(const char* fname) +{ + vtkm::cont::DataSet ds; + vtkm::io::BOVDataSetReader reader(fname); + try + { + ds = reader.ReadDataSet(); + } + catch (vtkm::io::ErrorIO& e) + { + std::string message("Error reading "); + message += fname; + message += ", "; + message += e.GetMessage(); + + VTKM_TEST_FAIL(message.c_str()); + } + + return ds; +} + +} // anonymous namespace + +void TestReadingBOVDataSet() +{ + std::string bovFile = vtkm::cont::testing::Testing::GetTestDataBasePath() + "/uniform/noise.bov"; + + auto const& ds = readBOVDataSet(bovFile.data()); + + VTKM_TEST_ASSERT(ds.GetNumberOfFields() == 1, "Incorrect number of fields"); + // See the .bov file: DATA SIZE: 50 50 50 + VTKM_TEST_ASSERT(ds.GetNumberOfPoints() == 50 * 50 * 50, "Incorrect number of points"); + VTKM_TEST_ASSERT(ds.GetCellSet().GetNumberOfPoints() == 50 * 50 * 50, + "Incorrect number of points (from cell set)"); + VTKM_TEST_ASSERT(ds.GetNumberOfCells() == 49 * 49 * 49, "Incorrect number of cells"); + // See the .bov file: VARIABLE: "var" + VTKM_TEST_ASSERT(ds.HasField("var"), "Should have field 'var', but does not."); + VTKM_TEST_ASSERT(ds.GetNumberOfFields() == 1, "There is only one field in noise.bov"); + VTKM_TEST_ASSERT(ds.GetNumberOfCoordinateSystems() == 1, + "There is only one coordinate system in noise.bov"); + + auto const& field = ds.GetField("var"); + // I'm pretty sure that all .bov files have their fields associated with points . . . + VTKM_TEST_ASSERT(field.GetAssociation() == vtkm::cont::Field::Association::POINTS, + "The field should be associated with points."); +} + + +int UnitTestBOVDataSetReader(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(TestReadingBOVDataSet, argc, argv); +} From d5906eccd96bc9e93b41993d4f85ecb84028c9fb Mon Sep 17 00:00:00 2001 From: Vicente Adolfo Bolea Sanchez Date: Tue, 12 May 2020 13:45:43 -0400 Subject: [PATCH 02/27] add ThreadIndicesTopologyMap optimized specializations This commit splits ThreadIndicesTopologyMap into two different specializations which can be instanciated with the tags: DefaultScatterAndMaskTag and CustomScatterAndMaskTag. These specialization will allow ThreadIndicesTopologyMap instances to avoid holding in memory InputIndex, OutputIndex and ThreadIndex variables when Mask = MaskNone and Scatter = ScatterIdentity which in this case are not needed since no transformation are done. Signed-off-by: Vicente Adolfo Bolea Sanchez --- vtkm/exec/arg/FetchExtrude.h | 25 ++- vtkm/exec/arg/FetchTagArrayTopologyMapIn.h | 10 +- vtkm/exec/arg/ThreadIndicesExtrude.h | 8 +- vtkm/exec/arg/ThreadIndicesTopologyMap.h | 196 ++++++++++++++++-- .../UnitTestFetchArrayTopologyMapIn.cxx | 8 +- vtkm/worklet/WorkletMapTopology.h | 55 +++-- vtkm/worklet/gradient/PointGradient.h | 5 +- 7 files changed, 247 insertions(+), 60 deletions(-) diff --git a/vtkm/exec/arg/FetchExtrude.h b/vtkm/exec/arg/FetchExtrude.h index 05bac2a33..4189328be 100644 --- a/vtkm/exec/arg/FetchExtrude.h +++ b/vtkm/exec/arg/FetchExtrude.h @@ -30,10 +30,11 @@ template struct Fetch { VTKM_SUPPRESS_EXEC_WARNINGS - template - VTKM_EXEC auto Load(const vtkm::exec::arg::ThreadIndicesTopologyMap< - vtkm::exec::ConnectivityExtrude>& indices, - const ExecObjectType&) const -> vtkm::Vec + template + VTKM_EXEC auto Load( + const vtkm::exec::arg::ThreadIndicesTopologyMap, + ScatterAndMaskMode>& indices, + const ExecObjectType&) const -> vtkm::Vec { // std::cout << "opimized fetch for point ids" << std::endl; const auto& xgcidx = indices.GetIndicesIncident(); @@ -50,9 +51,10 @@ struct Fetch - VTKM_EXEC auto Load(const vtkm::exec::arg::ThreadIndicesTopologyMap& indices, - const ExecObjectType&) const -> decltype(indices.GetIndicesIncident()) + template + VTKM_EXEC auto Load( + const vtkm::exec::arg::ThreadIndicesTopologyMap& indices, + const ExecObjectType&) const -> decltype(indices.GetIndicesIncident()) { return indices.GetIndicesIncident(); } @@ -133,10 +135,11 @@ struct Fetch - VTKM_EXEC auto Load(const vtkm::exec::arg::ThreadIndicesTopologyMap< - vtkm::exec::ReverseConnectivityExtrude>& indices, - const vtkm::exec::ArrayPortalExtrude& points) + template + VTKM_EXEC auto Load( + const vtkm::exec::arg::ThreadIndicesTopologyMap, + ScatterAndMaskMode>& indices, + const vtkm::exec::ArrayPortalExtrude& points) -> decltype(points.Get(indices.GetIndexLogical())) { // std::cout << "optimized fetch for point coordinates" << std::endl; diff --git a/vtkm/exec/arg/FetchTagArrayTopologyMapIn.h b/vtkm/exec/arg/FetchTagArrayTopologyMapIn.h index 28af1c867..60074269f 100644 --- a/vtkm/exec/arg/FetchTagArrayTopologyMapIn.h +++ b/vtkm/exec/arg/FetchTagArrayTopologyMapIn.h @@ -213,11 +213,11 @@ struct Fetch - VTKM_EXEC auto Load(const vtkm::exec::arg::ThreadIndicesTopologyMap< - vtkm::exec::ConnectivityExtrude>& indices, - const ExecObjectType& portal) - -> vtkm::Vec + template + VTKM_EXEC auto Load( + const vtkm::exec::arg::ThreadIndicesTopologyMap, + ScatterAndMaskMode>& indices, + const ExecObjectType& portal) -> vtkm::Vec { // std::cout << "opimized fetch for point values" << std::endl; const auto& xgcidx = indices.GetIndicesIncident(); diff --git a/vtkm/exec/arg/ThreadIndicesExtrude.h b/vtkm/exec/arg/ThreadIndicesExtrude.h index 21f9f1f5c..bcc95abbb 100644 --- a/vtkm/exec/arg/ThreadIndicesExtrude.h +++ b/vtkm/exec/arg/ThreadIndicesExtrude.h @@ -21,8 +21,8 @@ namespace arg { // Specialization for extrude types. -template -class ThreadIndicesTopologyMap> +template +class ThreadIndicesTopologyMap, ScatterAndMaskMode> { using ConnectivityType = vtkm::exec::ConnectivityExtrude; @@ -175,8 +175,8 @@ private: }; // Specialization for extrude types. -template -class ThreadIndicesTopologyMap> +template +class ThreadIndicesTopologyMap, ScatterAndMaskMode> { using ConnectivityType = vtkm::exec::ReverseConnectivityExtrude; diff --git a/vtkm/exec/arg/ThreadIndicesTopologyMap.h b/vtkm/exec/arg/ThreadIndicesTopologyMap.h index a32f85a0b..6c5fb6ffe 100644 --- a/vtkm/exec/arg/ThreadIndicesTopologyMap.h +++ b/vtkm/exec/arg/ThreadIndicesTopologyMap.h @@ -69,6 +69,17 @@ static inline VTKM_EXEC vtkm::Id2 Deflate(const vtkm::Id3& index, vtkm::Id2) } // namespace detail +/// \brief Uses spaces optimizations when using MaskNone and ScatterIdentity +/// +struct DefaultScatterAndMaskTag +{ +}; + +/// \brief Used for when not using MaskNone and ScatterIdentity. +/// +struct CustomScatterOrMaskTag +{ +}; /// \brief Container for thread indices in a topology map /// @@ -80,7 +91,7 @@ static inline VTKM_EXEC vtkm::Id2 Deflate(const vtkm::Id3& index, vtkm::Id2) /// This class is templated on the type that stores the connectivity (such /// as \c ConnectivityExplicit or \c ConnectivityStructured). /// -template +template class ThreadIndicesTopologyMap : public vtkm::exec::arg::ThreadIndicesBasic { using Superclass = vtkm::exec::arg::ThreadIndicesBasic; @@ -144,10 +155,11 @@ private: CellShapeTag CellShape; }; -// Specialization for structured connectivity types. +/// \brief Specialization for CustomScatterOrMaskTag template class ThreadIndicesTopologyMap< - vtkm::exec::ConnectivityStructured> + vtkm::exec::ConnectivityStructured, + CustomScatterOrMaskTag> { using ConnectivityType = vtkm::exec::ConnectivityStructured; @@ -160,15 +172,15 @@ public: vtkm::exec::ConnectivityStructured; VTKM_EXEC ThreadIndicesTopologyMap(vtkm::Id threadIndex, - vtkm::Id inIndex, + vtkm::Id inputIndex, vtkm::IdComponent visitIndex, - vtkm::Id outIndex, + vtkm::Id outputIndex, const ConnectivityType& connectivity) { this->ThreadIndex = threadIndex; - this->InputIndex = inIndex; + this->InputIndex = inputIndex; this->VisitIndex = visitIndex; - this->OutputIndex = outIndex; + this->OutputIndex = outputIndex; this->LogicalIndex = connectivity.FlatToLogicalToIndex(this->InputIndex); this->IndicesIncident = connectivity.GetIndices(this->LogicalIndex); this->CellShape = connectivity.GetCellShape(this->InputIndex); @@ -181,9 +193,6 @@ public: // This constructor handles multidimensional indices on one-to-one input-to-output auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType()); this->ThreadIndex = threadIndex1D; - this->InputIndex = threadIndex1D; - this->OutputIndex = threadIndex1D; - this->VisitIndex = 0; this->LogicalIndex = logicalIndex; this->IndicesIncident = connectivity.GetIndices(logicalIndex); this->CellShape = connectivity.GetCellShape(threadIndex1D); @@ -207,6 +216,7 @@ public: this->CellShape = connectivity.GetCellShape(threadIndex1D); } + /// \brief The index of the thread or work invocation. /// /// This index refers to which instance of the worklet is being invoked. Every invocation of the @@ -292,20 +302,176 @@ public: private: vtkm::Id ThreadIndex; - vtkm::Id InputIndex; vtkm::IdComponent VisitIndex; + LogicalIndexType LogicalIndex; + IndicesIncidentType IndicesIncident; + CellShapeTag CellShape; + vtkm::Id InputIndex; vtkm::Id OutputIndex; +}; + +/// \brief Specialization for DefaultScatterAndMaskTag +/// +/// It does not store VisitIndex, InputIndex and OutputIndex +/// since this is used only when Scatter is set as ScatterIdentity +/// and Mask is set as MaskNone which does not performs any transformation onto the +/// indices. +/// +template +class ThreadIndicesTopologyMap< + vtkm::exec::ConnectivityStructured, + DefaultScatterAndMaskTag> +{ + using ConnectivityType = + vtkm::exec::ConnectivityStructured; + +public: + using IndicesIncidentType = typename ConnectivityType::IndicesType; + using CellShapeTag = typename ConnectivityType::CellShapeTag; + using LogicalIndexType = typename ConnectivityType::SchedulingRangeType; + using Connectivity = ConnectivityType; + + VTKM_EXEC ThreadIndicesTopologyMap(vtkm::Id threadIndex, + vtkm::Id inputIndex, + vtkm::IdComponent vtkmNotUsed(visitIndex), + vtkm::Id vtkmNotUsed(outputIndex), + const ConnectivityType& connectivity) + { + this->ThreadIndex = threadIndex; + this->LogicalIndex = connectivity.FlatToLogicalToIndex(inputIndex); + this->IndicesIncident = connectivity.GetIndices(this->LogicalIndex); + this->CellShape = connectivity.GetCellShape(inputIndex); + } + + + + + VTKM_EXEC ThreadIndicesTopologyMap(const vtkm::Id3& threadIndex3D, + vtkm::Id threadIndex1D, + const ConnectivityType& connectivity) + { + // This constructor handles multidimensional indices on one-to-one input-to-output + auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType()); + this->ThreadIndex = threadIndex1D; + this->LogicalIndex = logicalIndex; + this->IndicesIncident = connectivity.GetIndices(logicalIndex); + this->CellShape = connectivity.GetCellShape(threadIndex1D); + } + + VTKM_EXEC ThreadIndicesTopologyMap(const vtkm::Id3& threadIndex3D, + vtkm::Id threadIndex1D, + vtkm::Id vtkmNotUsed(inIndex), + vtkm::IdComponent vtkmNotUsed(visitIndex), + vtkm::Id vtkmNotUsed(outIndex), + const ConnectivityType& connectivity) + { + // This constructor handles multidimensional indices on many-to-many input-to-output + auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType()); + this->ThreadIndex = threadIndex1D; + this->LogicalIndex = logicalIndex; + this->IndicesIncident = connectivity.GetIndices(logicalIndex); + this->CellShape = connectivity.GetCellShape(threadIndex1D); + } + + /// \brief The index of the thread or work invocation. + /// + /// This index refers to which instance of the worklet is being invoked. Every invocation of the + /// worklet has a unique thread index. This is also called the work index depending on the + /// context. + /// + VTKM_EXEC + vtkm::Id GetThreadIndex() const { return this->ThreadIndex; } + + /// \brief The logical index into the input domain. + /// + /// This is similar to \c GetIndex3D except the Vec size matches the actual + /// dimensions of the data. + /// + VTKM_EXEC + LogicalIndexType GetIndexLogical() const { return this->LogicalIndex; } + + /// \brief The index into the input domain. + /// + /// This index refers to the input element (array value, cell, etc.) that + /// this thread is being invoked for. This is the typical index used during + /// fetches. + /// + VTKM_EXEC + vtkm::Id GetInputIndex() const { return this->ThreadIndex; } + + /// \brief The 3D index into the input domain. + /// + /// Overloads the implementation in the base class to return the 3D index + /// for the input. + /// + VTKM_EXEC + vtkm::Id3 GetInputIndex3D() const { return detail::InflateTo3D(this->GetIndexLogical()); } + + /// \brief The index into the output domain. + /// + /// This index refers to the output element (array value, cell, etc.) that + /// this thread is creating. This is the typical index used during + /// Fetch::Store. + /// + VTKM_EXEC + vtkm::Id GetOutputIndex() const { return this->ThreadIndex; } + + /// \brief The visit index. + /// + /// When multiple output indices have the same input index, they are + /// distinguished using the visit index. + /// + VTKM_EXEC + vtkm::IdComponent GetVisitIndex() const { return 0; } + + /// \brief The indices of the incident elements. + /// + /// A topology map has "visited" and "incident" elements (e.g. points, cells, + /// etc). For each worklet invocation, there is exactly one visited element, + /// but there can be several incident elements. This method returns a + /// Vec-like object containing the indices to the incident elements. + /// + VTKM_EXEC + const IndicesIncidentType& GetIndicesIncident() const { return this->IndicesIncident; } + + /// \brief The input indices of the incident elements in pointer form. + /// + /// Returns the same object as GetIndicesIncident except that it returns a + /// pointer to the internally held object rather than a reference or copy. + /// Since the from indices can be a sizeable Vec (8 entries is common), it is + /// best not to have a bunch a copies. Thus, you can pass around a pointer + /// instead. However, care should be taken to make sure that this object does + /// not go out of scope, at which time the returned pointer becomes invalid. + /// + VTKM_EXEC + const IndicesIncidentType* GetIndicesIncidentPointer() const { return &this->IndicesIncident; } + + /// \brief The shape of the input cell. + /// + /// In topology maps that map from points to something, the indices make up + /// the structure of a cell. Although the shape tag is not technically and + /// index, it defines the meaning of the indices, so we put it here. (That + /// and this class is the only convenient place to store it.) + /// + VTKM_EXEC + CellShapeTag GetCellShape() const { return this->CellShape; } + +private: + vtkm::Id ThreadIndex; LogicalIndexType LogicalIndex; IndicesIncidentType IndicesIncident; CellShapeTag CellShape; }; -// Specialization for permuted structured connectivity types. + +/// \brief Specialization for permuted structured connectivity types. template class ThreadIndicesTopologyMap>> + PermutationPortal, + vtkm::exec::ConnectivityStructured>, + CustomScatterOrMaskTag> { using PermutedConnectivityType = vtkm::exec::ConnectivityPermutedVisitCellsWithPoints< PermutationPortal, diff --git a/vtkm/exec/arg/testing/UnitTestFetchArrayTopologyMapIn.cxx b/vtkm/exec/arg/testing/UnitTestFetchArrayTopologyMapIn.cxx index 1ab639ed1..db2d68364 100644 --- a/vtkm/exec/arg/testing/UnitTestFetchArrayTopologyMapIn.cxx +++ b/vtkm/exec/arg/testing/UnitTestFetchArrayTopologyMapIn.cxx @@ -87,7 +87,9 @@ struct FetchArrayTopologyMapInTests void TryInvocation(const Invocation& invocation) const { using ConnectivityType = typename Invocation::InputDomainType; - using ThreadIndicesType = vtkm::exec::arg::ThreadIndicesTopologyMap; + using ThreadIndicesType = + vtkm::exec::arg::ThreadIndicesTopologyMap; using FetchType = vtkm::exec::arg::Fetch; + using ThreadIndicesType = + vtkm::exec::arg::ThreadIndicesTopologyMap; vtkm::exec::arg::Fetch - VTKM_EXEC vtkm::exec::arg::ThreadIndicesTopologyMap GetThreadIndices( - vtkm::Id threadIndex, - const OutToInArrayType& outToIn, - const VisitArrayType& visit, - const ThreadToOutArrayType& threadToOut, - const InputDomainType& connectivity) const + VTKM_EXEC vtkm::exec::arg::ThreadIndicesTopologyMap + GetThreadIndices(vtkm::Id threadIndex, + const OutToInArrayType& outToIn, + const VisitArrayType& visit, + const ThreadToOutArrayType& threadToOut, + const InputDomainType& connectivity) const { const vtkm::Id outIndex = threadToOut.Get(threadIndex); - return vtkm::exec::arg::ThreadIndicesTopologyMap( + return vtkm::exec::arg::ThreadIndicesTopologyMap( threadIndex, outToIn.Get(outIndex), visit.Get(outIndex), outIndex, connectivity); } @@ -199,7 +201,10 @@ public: typename InputDomainType, bool S = IsScatterIdentity, bool M = IsMaskNone> - VTKM_EXEC EnableFnWhen> + VTKM_EXEC EnableFnWhen< + S && M, + vtkm::exec::arg::ThreadIndicesTopologyMap> GetThreadIndices(vtkm::Id threadIndex1D, const vtkm::Id3& threadIndex3D, const OutToInArrayType& vtkmNotUsed(outToIn), @@ -207,7 +212,8 @@ public: const ThreadToOutArrayType& vtkmNotUsed(threadToOut), const InputDomainType& connectivity) const { - return vtkm::exec::arg::ThreadIndicesTopologyMap( + return vtkm::exec::arg::ThreadIndicesTopologyMap( threadIndex3D, threadIndex1D, connectivity); } @@ -219,21 +225,26 @@ public: typename InputDomainType, bool S = IsScatterIdentity, bool M = IsMaskNone> - VTKM_EXEC EnableFnWhen> - GetThreadIndices(vtkm::Id threadIndex1D, - const vtkm::Id3& threadIndex3D, - const OutToInArrayType& outToIn, - const VisitArrayType& visit, - const ThreadToOutArrayType& threadToOut, - const InputDomainType& connectivity) const + VTKM_EXEC + EnableFnWhen> + GetThreadIndices(vtkm::Id threadIndex1D, + const vtkm::Id3& threadIndex3D, + const OutToInArrayType& outToIn, + const VisitArrayType& visit, + const ThreadToOutArrayType& threadToOut, + const InputDomainType& connectivity) const { const vtkm::Id outIndex = threadToOut.Get(threadIndex1D); - return vtkm::exec::arg::ThreadIndicesTopologyMap(threadIndex3D, - threadIndex1D, - outToIn.Get(outIndex), - visit.Get(outIndex), - outIndex, - connectivity); + return vtkm::exec::arg::ThreadIndicesTopologyMap( + threadIndex3D, + threadIndex1D, + outToIn.Get(outIndex), + visit.Get(outIndex), + outIndex, + connectivity); } }; diff --git a/vtkm/worklet/gradient/PointGradient.h b/vtkm/worklet/gradient/PointGradient.h index 8960c569c..033327bc9 100644 --- a/vtkm/worklet/gradient/PointGradient.h +++ b/vtkm/worklet/gradient/PointGradient.h @@ -50,7 +50,10 @@ struct PointGradient : public vtkm::worklet::WorkletVisitPointsWithCells const WholeFieldIn& inputField, GradientOutType& outputGradient) const { - using CellThreadIndices = vtkm::exec::arg::ThreadIndicesTopologyMap; + // Use optimized ThreadIndicesTopologyMap + using CellThreadIndices = + vtkm::exec::arg::ThreadIndicesTopologyMap; using ValueType = typename WholeFieldIn::ValueType; using CellShapeTag = typename CellSetInType::CellShapeTag; From d961c185acddc3ae4dc53cc2702f5a86a934cd64 Mon Sep 17 00:00:00 2001 From: NAThompson Date: Tue, 12 May 2020 15:43:40 -0400 Subject: [PATCH 03/27] Deprecate ReadPortal().Get pattern. --- vtkm/cont/CellLocatorUniformGrid.cxx | 5 +++-- vtkm/cont/CellSetExplicit.h | 1 + vtkm/cont/CellSetExplicit.hxx | 15 +++++++++++---- vtkm/cont/testing/TestingCellLocatorUniformBins.h | 9 ++++++--- vtkm/cont/testing/TestingDataSetExplicit.h | 6 ++++-- .../testing/UnitTestArrayHandleConcatenate.cxx | 6 ++++-- vtkm/cont/testing/UnitTestArrayHandleCounting.cxx | 10 +++++++--- vtkm/cont/testing/UnitTestArrayHandleImplicit.cxx | 7 ++++--- vtkm/cont/testing/UnitTestCellLocatorGeneral.cxx | 9 ++++++--- vtkm/cont/testing/UnitTestCellSet.cxx | 4 ++-- vtkm/cont/testing/UnitTestDataSetPermutation.cxx | 12 ++++++++---- .../testing/UnitTestContourFilterNormals.cxx | 8 +++++--- .../testing/UnitTestVertexClusteringFilter.cxx | 3 ++- vtkm/worklet/cosmotools/CosmoTools.h | 8 ++++---- vtkm/worklet/testing/UnitTestCellGradient.cxx | 9 ++++++--- vtkm/worklet/testing/UnitTestCosmoTools.cxx | 4 +++- 16 files changed, 76 insertions(+), 40 deletions(-) diff --git a/vtkm/cont/CellLocatorUniformGrid.cxx b/vtkm/cont/CellLocatorUniformGrid.cxx index 3310ee3fa..d39535d2a 100644 --- a/vtkm/cont/CellLocatorUniformGrid.cxx +++ b/vtkm/cont/CellLocatorUniformGrid.cxx @@ -54,9 +54,10 @@ void CellLocatorUniformGrid::Build() } UniformType uniformCoords = coords.GetData().Cast(); - this->Origin = uniformCoords.ReadPortal().GetOrigin(); + auto coordsPortal = uniformCoords.ReadPortal(); + this->Origin = coordsPortal.GetOrigin(); - vtkm::Vec3f spacing = uniformCoords.ReadPortal().GetSpacing(); + vtkm::Vec3f spacing = coordsPortal.GetSpacing(); vtkm::Vec3f unitLength; unitLength[0] = static_cast(this->PointDims[0] - 1); unitLength[1] = static_cast(this->PointDims[1] - 1); diff --git a/vtkm/cont/CellSetExplicit.h b/vtkm/cont/CellSetExplicit.h index 4e92fb1a3..7877a7726 100644 --- a/vtkm/cont/CellSetExplicit.h +++ b/vtkm/cont/CellSetExplicit.h @@ -186,6 +186,7 @@ public: VTKM_CONT vtkm::IdComponent GetNumberOfPointsInCell(vtkm::Id cellid) const override; VTKM_CONT void GetCellPointIds(vtkm::Id id, vtkm::Id* ptids) const override; + VTKM_CONT auto ShapesReadPortal() const; VTKM_CONT vtkm::UInt8 GetCellShape(vtkm::Id cellid) const override; template diff --git a/vtkm/cont/CellSetExplicit.hxx b/vtkm/cont/CellSetExplicit.hxx index 27789b8e1..7ff00e367 100644 --- a/vtkm/cont/CellSetExplicit.hxx +++ b/vtkm/cont/CellSetExplicit.hxx @@ -9,7 +9,7 @@ //============================================================================ #ifndef vtk_m_cont_CellSetExplicit_hxx #define vtk_m_cont_CellSetExplicit_hxx - +#include #include #include @@ -176,14 +176,21 @@ vtkm::IdComponent CellSetExplicit template VTKM_CONT +auto CellSetExplicit::ShapesReadPortal() const +{ + return this->Data->CellPointIds.Shapes.ReadPortal(); +} + +template +VTKM_CONT +VTKM_DEPRECATED(1.6, "Calling GetCellShape(cellid) is a performance bug. Call ShapesReadPortal() and loop over the .Get.") vtkm::UInt8 CellSetExplicit ::GetCellShape(vtkm::Id cellid) const { - // Looping over GetCellShape(cellid) is a performance bug. - // Don't know quite what to do about it right now. - return this->Data->CellPointIds.Shapes.ReadPortal().Get(cellid); + return this->ShapesReadPortal().Get(cellid); } + template template VTKM_CONT diff --git a/vtkm/cont/testing/TestingCellLocatorUniformBins.h b/vtkm/cont/testing/TestingCellLocatorUniformBins.h index 23e7e01c9..0380f277a 100644 --- a/vtkm/cont/testing/TestingCellLocatorUniformBins.h +++ b/vtkm/cont/testing/TestingCellLocatorUniformBins.h @@ -204,11 +204,14 @@ void TestCellLocator(const vtkm::Vec& dim, vtkm::Id number vtkm::worklet::DispatcherMapField dispatcher; dispatcher.Invoke(points, locator, cellIds, pcoords); + auto cellIdsPortal = cellIds.ReadPortal(); + auto expCellIdsPortal = expCellIds.ReadPortal(); + auto pcoordsPortal = pcoords.ReadPortal(); + auto expPCoordsPortal = expPCoords.ReadPortal(); for (vtkm::Id i = 0; i < numberOfPoints; ++i) { - VTKM_TEST_ASSERT(cellIds.ReadPortal().Get(i) == expCellIds.ReadPortal().Get(i), - "Incorrect cell ids"); - VTKM_TEST_ASSERT(test_equal(pcoords.ReadPortal().Get(i), expPCoords.ReadPortal().Get(i), 1e-3), + VTKM_TEST_ASSERT(cellIdsPortal.Get(i) == expCellIdsPortal.Get(i), "Incorrect cell ids"); + VTKM_TEST_ASSERT(test_equal(pcoordsPortal.Get(i), expPCoordsPortal.Get(i), 1e-3), "Incorrect parameteric coordinates"); } } diff --git a/vtkm/cont/testing/TestingDataSetExplicit.h b/vtkm/cont/testing/TestingDataSetExplicit.h index 469fd9e38..6d7896935 100644 --- a/vtkm/cont/testing/TestingDataSetExplicit.h +++ b/vtkm/cont/testing/TestingDataSetExplicit.h @@ -42,9 +42,10 @@ private: return false; } + auto ahPortal = ah.ReadPortal(); for (vtkm::Id i = 0; i < size; ++i) { - if (ah.ReadPortal().Get(i) != expected[i]) + if (ahPortal.Get(i) != expected[i]) { return false; } @@ -115,6 +116,7 @@ private: VTKM_TEST_ASSERT(conn.GetNumberOfValues() == connectivitySize, "Connectivity array wrong size."); vtkm::Id connectivityIndex = 0; + auto connPortal = conn.ReadPortal(); for (vtkm::Id pointIndex = 0; pointIndex < numPoints; pointIndex++) { vtkm::IdComponent numIncidentCells = correctNumIndices[pointIndex]; @@ -125,7 +127,7 @@ private: } for (vtkm::IdComponent cellIndex = 0; cellIndex < numIncidentCells; cellIndex++) { - vtkm::Id expectedCell = conn.ReadPortal().Get(connectivityIndex + cellIndex); + vtkm::Id expectedCell = connPortal.Get(connectivityIndex + cellIndex); std::set::iterator foundCell = correctIncidentCells.find(expectedCell); VTKM_TEST_ASSERT(foundCell != correctIncidentCells.end(), "An incident cell in the connectivity list is wrong or repeated."); diff --git a/vtkm/cont/testing/UnitTestArrayHandleConcatenate.cxx b/vtkm/cont/testing/UnitTestArrayHandleConcatenate.cxx index 329b5dae2..1becee061 100644 --- a/vtkm/cont/testing/UnitTestArrayHandleConcatenate.cxx +++ b/vtkm/cont/testing/UnitTestArrayHandleConcatenate.cxx @@ -36,9 +36,10 @@ void TestArrayHandleConcatenate() array5 = vtkm::cont::make_ArrayHandleConcatenate(array3, array4); } + auto array5Portal = array5.ReadPortal(); for (vtkm::Id index = 0; index < array5.GetNumberOfValues(); index++) { - std::cout << array5.ReadPortal().Get(index) << std::endl; + std::cout << array5Portal.Get(index) << std::endl; } } @@ -59,8 +60,9 @@ void TestConcatenateEmptyArray() ArrayConcat arrConc(arr2, arr1); ArrayConcat2 arrConc2(arrConc, arr3); + auto arrConc2Portal = arrConc2.ReadPortal(); for (vtkm::Id i = 0; i < arrConc2.GetNumberOfValues(); i++) - std::cout << arrConc2.ReadPortal().Get(i) << std::endl; + std::cout << arrConc2Portal.Get(i) << std::endl; } } // namespace UnitTestArrayHandleIndexNamespace diff --git a/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx b/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx index 4aca0a7eb..b5e975fa3 100644 --- a/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx +++ b/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx @@ -102,14 +102,17 @@ struct TemplatedTests "Counting array using raw array handle + tag has wrong size."); ValueType properValue = startingValue; + auto arrayConstPortal = arrayConst.ReadPortal(); + auto arrayMakePortal = arrayMake.ReadPortal(); + auto arrayHandlePortal = arrayHandle.ReadPortal(); for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) { - VTKM_TEST_ASSERT(arrayConst.ReadPortal().Get(index) == properValue, + VTKM_TEST_ASSERT(arrayConstPortal.Get(index) == properValue, "Counting array using constructor has unexpected value."); - VTKM_TEST_ASSERT(arrayMake.ReadPortal().Get(index) == properValue, + VTKM_TEST_ASSERT(arrayMakePortal.Get(index) == properValue, "Counting array using make has unexpected value."); - VTKM_TEST_ASSERT(arrayHandle.ReadPortal().Get(index) == properValue, + VTKM_TEST_ASSERT(arrayHandlePortal.Get(index) == properValue, "Counting array using raw array handle + tag has unexpected value."); properValue = properValue + step; } @@ -128,6 +131,7 @@ void TestArrayHandleCounting() TemplatedTests()(StringInt(10), StringInt(2)); } + } // namespace UnitTestArrayHandleCountingNamespace int UnitTestArrayHandleCounting(int argc, char* argv[]) diff --git a/vtkm/cont/testing/UnitTestArrayHandleImplicit.cxx b/vtkm/cont/testing/UnitTestArrayHandleImplicit.cxx index 13c75b77c..fa7d79f0f 100644 --- a/vtkm/cont/testing/UnitTestArrayHandleImplicit.cxx +++ b/vtkm/cont/testing/UnitTestArrayHandleImplicit.cxx @@ -42,12 +42,13 @@ struct ImplicitTests using ImplicitHandle = vtkm::cont::ArrayHandleImplicit; - ImplicitHandle implict = vtkm::cont::make_ArrayHandleImplicit(functor, ARRAY_SIZE); + ImplicitHandle implicit = vtkm::cont::make_ArrayHandleImplicit(functor, ARRAY_SIZE); //verify that the control portal works + auto implicitPortal = implicit.ReadPortal(); for (int i = 0; i < ARRAY_SIZE; ++i) { - const ValueType v = implict.ReadPortal().Get(i); + const ValueType v = implicitPortal.Get(i); const ValueType correct_value = functor(i); VTKM_TEST_ASSERT(v == correct_value, "Implicit Handle Failed"); } @@ -56,7 +57,7 @@ struct ImplicitTests vtkm::cont::Token token; using Device = vtkm::cont::DeviceAdapterTagSerial; using CEPortal = typename ImplicitHandle::template ExecutionTypes::PortalConst; - CEPortal execPortal = implict.PrepareForInput(Device(), token); + CEPortal execPortal = implicit.PrepareForInput(Device(), token); for (int i = 0; i < ARRAY_SIZE; ++i) { const ValueType v = execPortal.Get(i); diff --git a/vtkm/cont/testing/UnitTestCellLocatorGeneral.cxx b/vtkm/cont/testing/UnitTestCellLocatorGeneral.cxx index 737601b0e..c90fee88e 100644 --- a/vtkm/cont/testing/UnitTestCellLocatorGeneral.cxx +++ b/vtkm/cont/testing/UnitTestCellLocatorGeneral.cxx @@ -188,11 +188,14 @@ void TestWithDataSet(vtkm::cont::CellLocatorGeneral& locator, const vtkm::cont:: // CellLocatorGeneral is non-copyable. Pass it via a pointer. dispatcher.Invoke(points, &locator, cellIds, pcoords); + auto cellIdPortal = cellIds.ReadPortal(); + auto expCellIdsPortal = expCellIds.ReadPortal(); + auto pcoordsPortal = pcoords.ReadPortal(); + auto expPCoordsPortal = expPCoords.ReadPortal(); for (vtkm::Id i = 0; i < 128; ++i) { - VTKM_TEST_ASSERT(cellIds.ReadPortal().Get(i) == expCellIds.ReadPortal().Get(i), - "Incorrect cell ids"); - VTKM_TEST_ASSERT(test_equal(pcoords.ReadPortal().Get(i), expPCoords.ReadPortal().Get(i), 1e-3), + VTKM_TEST_ASSERT(cellIdPortal.Get(i) == expCellIdsPortal.Get(i), "Incorrect cell ids"); + VTKM_TEST_ASSERT(test_equal(pcoordsPortal.Get(i), expPCoordsPortal.Get(i), 1e-3), "Incorrect parameteric coordinates"); } diff --git a/vtkm/cont/testing/UnitTestCellSet.cxx b/vtkm/cont/testing/UnitTestCellSet.cxx index ef889a99a..733d1da00 100644 --- a/vtkm/cont/testing/UnitTestCellSet.cxx +++ b/vtkm/cont/testing/UnitTestCellSet.cxx @@ -117,13 +117,13 @@ void TestAgainstBaseLine(const vtkm::cont::CellSet& cellset, : PermutationArray.GetNumberOfValues(); VTKM_TEST_ASSERT(numCells == expectedNumCell, "Wrong number of cells"); + auto permutationPortal = PermutationArray.ReadPortal(); for (vtkm::Id i = 0; i < numCells; ++i) { VTKM_TEST_ASSERT(cellset.GetCellShape(i) == vtkm::CELL_SHAPE_HEXAHEDRON, "Wrong shape"); VTKM_TEST_ASSERT(cellset.GetNumberOfPointsInCell(i) == 8, "Wrong number of points-of-cell"); - vtkm::Id baseLineCellId = - (flag == IsPermutationCellSet::YES) ? PermutationArray.ReadPortal().Get(i) : i; + vtkm::Id baseLineCellId = (flag == IsPermutationCellSet::YES) ? permutationPortal.Get(i) : i; auto baseLinePointIds = baseLineStructure.GetPointsOfCell(baseLineCellId); vtkm::Id pointIds[8]; diff --git a/vtkm/cont/testing/UnitTestDataSetPermutation.cxx b/vtkm/cont/testing/UnitTestDataSetPermutation.cxx index 9d7df938c..4586d29da 100644 --- a/vtkm/cont/testing/UnitTestDataSetPermutation.cxx +++ b/vtkm/cont/testing/UnitTestDataSetPermutation.cxx @@ -32,9 +32,10 @@ bool TestArrayHandle(const vtkm::cont::ArrayHandle& ah, return false; } + auto ahPortal = ah.ReadPortal(); for (vtkm::Id i = 0; i < size; ++i) { - if (ah.ReadPortal().Get(i) != expected[i]) + if (ahPortal.Get(i) != expected[i]) { return false; } @@ -110,9 +111,10 @@ void TestDataSet_Explicit() //iterate same cell 4 times vtkm::Float32 expected[4] = { 30.1667f, 30.1667f, 30.1667f, 30.1667f }; + auto resultPortal = result.ReadPortal(); for (int i = 0; i < 4; ++i) { - VTKM_TEST_ASSERT(test_equal(result.ReadPortal().Get(i), expected[i]), + VTKM_TEST_ASSERT(test_equal(resultPortal.Get(i), expected[i]), "Wrong result for CellAverage worklet on explicit subset data"); } } @@ -145,9 +147,10 @@ void TestDataSet_Structured2D() dispatcher.Invoke(subset, dataSet.GetField("pointvar"), result); vtkm::Float32 expected[4] = { 40.1f, 40.1f, 40.1f, 40.1f }; + auto resultPortal = result.ReadPortal(); for (int i = 0; i < 4; ++i) { - VTKM_TEST_ASSERT(test_equal(result.ReadPortal().Get(i), expected[i]), + VTKM_TEST_ASSERT(test_equal(resultPortal.Get(i), expected[i]), "Wrong result for CellAverage worklet on 2d structured subset data"); } } @@ -180,9 +183,10 @@ void TestDataSet_Structured3D() dispatcher.Invoke(subset, dataSet.GetField("pointvar"), result); vtkm::Float32 expected[4] = { 70.2125f, 70.2125f, 70.2125f, 70.2125f }; + auto resultPortal = result.ReadPortal(); for (int i = 0; i < 4; ++i) { - VTKM_TEST_ASSERT(test_equal(result.ReadPortal().Get(i), expected[i]), + VTKM_TEST_ASSERT(test_equal(resultPortal.Get(i), expected[i]), "Wrong result for CellAverage worklet on 2d structured subset data"); } } diff --git a/vtkm/filter/testing/UnitTestContourFilterNormals.cxx b/vtkm/filter/testing/UnitTestContourFilterNormals.cxx index 5b2fc3a7e..c454d21c3 100644 --- a/vtkm/filter/testing/UnitTestContourFilterNormals.cxx +++ b/vtkm/filter/testing/UnitTestContourFilterNormals.cxx @@ -97,11 +97,12 @@ void TestNormals(const vtkm::cont::DataSet& dataset, bool structured) result.GetField("normals").GetData().CopyTo(normals); VTKM_TEST_ASSERT(normals.GetNumberOfValues() == numVerts, "Wrong number of values in normals field"); + auto normalsPortal = normals.ReadPortal(); for (vtkm::Id i = 0; i < numVerts; ++i) { - VTKM_TEST_ASSERT(test_equal(normals.ReadPortal().Get(i), expected[i], 0.001), + VTKM_TEST_ASSERT(test_equal(normalsPortal.Get(i), expected[i], 0.001), "Result (", - normals.ReadPortal().Get(i), + normalsPortal.Get(i), ") does not match expected value (", expected[i], ") vert ", @@ -124,9 +125,10 @@ void TestNormals(const vtkm::cont::DataSet& dataset, bool structured) result.GetField("normals").GetData().CopyTo(normals); VTKM_TEST_ASSERT(normals.GetNumberOfValues() == numVerts, "Wrong number of values in normals field"); + normalsPortal = normals.ReadPortal(); for (vtkm::Id i = 0; i < numVerts; ++i) { - VTKM_TEST_ASSERT(test_equal(normals.ReadPortal().Get(i), expected[i], 0.001), + VTKM_TEST_ASSERT(test_equal(normalsPortal.Get(i), expected[i], 0.001), "Result does not match expected values"); } } diff --git a/vtkm/filter/testing/UnitTestVertexClusteringFilter.cxx b/vtkm/filter/testing/UnitTestVertexClusteringFilter.cxx index 805d1587c..11ddccb55 100644 --- a/vtkm/filter/testing/UnitTestVertexClusteringFilter.cxx +++ b/vtkm/filter/testing/UnitTestVertexClusteringFilter.cxx @@ -73,9 +73,10 @@ void TestVertexClustering() auto pointArray = output.GetCoordinateSystem(0).GetData(); VTKM_TEST_ASSERT(pointArray.GetNumberOfValues() == output_points, "Number of output points mismatch"); + auto pointArrayPortal = pointArray.ReadPortal(); for (vtkm::Id i = 0; i < pointArray.GetNumberOfValues(); ++i) { - const PointType& p1 = pointArray.ReadPortal().Get(i); + const PointType& p1 = pointArrayPortal.Get(i); PointType p2 = vtkm::make_Vec(output_point[i][0], output_point[i][1], output_point[i][2]); VTKM_TEST_ASSERT(test_equal(p1, p2), "Point Array mismatch"); } diff --git a/vtkm/worklet/cosmotools/CosmoTools.h b/vtkm/worklet/cosmotools/CosmoTools.h index 282e2ad45..faa7da3a0 100644 --- a/vtkm/worklet/cosmotools/CosmoTools.h +++ b/vtkm/worklet/cosmotools/CosmoTools.h @@ -88,8 +88,6 @@ #include #include -//#define DEBUG_PRINT 1 - namespace { @@ -103,9 +101,10 @@ void DebugPrint(const char* msg, vtkm::cont::ArrayHandle& array) { vtkm::Id count = 20; count = std::min(count, array.GetNumberOfValues()); + auto portal = array.ReadPortal(); std::cout << std::setw(15) << msg << ": "; for (vtkm::Id i = 0; i < count; i++) - std::cout << std::setprecision(3) << std::setw(5) << array.ReadPortal().Get(i) << " "; + std::cout << std::setprecision(3) << std::setw(5) << portal.Get(i) << " "; std::cout << std::endl; } @@ -114,9 +113,10 @@ void DebugPrint(const char* msg, vtkm::cont::ArrayHandleReverse eg = expected_gradients[i]; - vtkm::Float64 d = extraOutput.Divergence.ReadPortal().Get(i); + vtkm::Float64 d = divergencePortal.Get(i); VTKM_TEST_ASSERT(test_equal((eg[0][0] + eg[1][1] + eg[2][2]), d), "Wrong result for Divergence on 3D uniform data"); vtkm::Vec3f_64 ev(eg[1][2] - eg[2][1], eg[2][0] - eg[0][2], eg[0][1] - eg[1][0]); - vtkm::Vec3f_64 v = extraOutput.Vorticity.ReadPortal().Get(i); + vtkm::Vec3f_64 v = vorticityPortal.Get(i); VTKM_TEST_ASSERT(test_equal(ev, v), "Wrong result for Vorticity on 3D uniform data"); } } @@ -220,9 +222,10 @@ void TestCellGradientExplicit() result = gradient.Run(dataSet.GetCellSet(), dataSet.GetCoordinateSystem(), input); vtkm::Vec3f_32 expected[2] = { { 10.f, 10.1f, 0.0f }, { 10.f, 10.1f, -0.0f } }; + auto resultPortal = result.ReadPortal(); for (int i = 0; i < 2; ++i) { - VTKM_TEST_ASSERT(test_equal(result.ReadPortal().Get(i), expected[i]), + VTKM_TEST_ASSERT(test_equal(resultPortal.Get(i), expected[i]), "Wrong result for CellGradient worklet on 3D explicit data"); } } diff --git a/vtkm/worklet/testing/UnitTestCosmoTools.cxx b/vtkm/worklet/testing/UnitTestCosmoTools.cxx index e569e8edb..71d9d7885 100644 --- a/vtkm/worklet/testing/UnitTestCosmoTools.cxx +++ b/vtkm/worklet/testing/UnitTestCosmoTools.cxx @@ -36,9 +36,11 @@ bool TestArrayHandle(const vtkm::cont::ArrayHandle& ah, return false; } + auto ahPortal = ah.ReadPortal(); + auto expectedPortal = expected.ReadPortal(); for (vtkm::Id i = 0; i < size; ++i) { - if (ah.ReadPortal().Get(i) != expected.ReadPortal().Get(i)) + if (ahPortal.Get(i) != expectedPortal.Get(i)) { return false; } From 331b80a783a2e2573203c5b475cf891dfb971621 Mon Sep 17 00:00:00 2001 From: NAThompson Date: Tue, 12 May 2020 16:02:16 -0400 Subject: [PATCH 04/27] More understandable VTKm options for symbol visibility. --- CMake/VTKmWrappers.cmake | 2 +- CMake/testing/VTKmTestWrappers.cmake | 2 +- CMakeLists.txt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMake/VTKmWrappers.cmake b/CMake/VTKmWrappers.cmake index 08c359053..7e1f1b728 100644 --- a/CMake/VTKmWrappers.cmake +++ b/CMake/VTKmWrappers.cmake @@ -378,7 +378,7 @@ function(vtkm_library) EXTENDS_VTKM DEVICE_SOURCES ${VTKm_LIB_DEVICE_SOURCES} ) - if(NOT VTKm_USE_DEFAULT_SYMBOL_VISIBILITY) + if(VTKm_HIDE_PRIVATE_SYMBOLS) set_property(TARGET ${lib_name} PROPERTY CUDA_VISIBILITY_PRESET "hidden") set_property(TARGET ${lib_name} PROPERTY CXX_VISIBILITY_PRESET "hidden") endif() diff --git a/CMake/testing/VTKmTestWrappers.cmake b/CMake/testing/VTKmTestWrappers.cmake index 5ea49b5b6..8ec6042f9 100644 --- a/CMake/testing/VTKmTestWrappers.cmake +++ b/CMake/testing/VTKmTestWrappers.cmake @@ -134,7 +134,7 @@ function(vtkm_unit_tests) endif() vtkm_add_target_information(${test_prog} DEVICE_SOURCES ${device_sources}) - if(NOT VTKm_USE_DEFAULT_SYMBOL_VISIBILITY) + if(VTKm_HIDE_PRIVATE_SYMBOLS) set_property(TARGET ${test_prog} PROPERTY CUDA_VISIBILITY_PRESET "hidden") set_property(TARGET ${test_prog} PROPERTY CXX_VISIBILITY_PRESET "hidden") endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 093c223a5..aa1b0aeaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,7 +118,7 @@ vtkm_option(VTKm_INSTALL_ONLY_LIBRARIES "install only vtk-m libraries and no hea # rather than exporting all symbols. This flag is added so that consumers # which require static builds can force all symbols on, which is something # VTK does. -vtkm_option(VTKm_USE_DEFAULT_SYMBOL_VISIBILITY "Don't explicitly hide symbols from libraries." OFF) +vtkm_option(VTKm_HIDE_PRIVATE_SYMBOLS "Hide symbols from libraries." ON) vtkm_option(BUILD_SHARED_LIBS "Build VTK-m with shared libraries" OFF) set(VTKm_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) @@ -136,7 +136,7 @@ mark_as_advanced( VTKm_ENABLE_LOGGING VTKm_NO_ASSERT VTKm_INSTALL_ONLY_LIBRARIES - VTKm_USE_DEFAULT_SYMBOL_VISIBILITY + VTKm_HIDE_PRIVATE_SYMBOLS VTKm_ENABLE_DEVELOPER_FLAGS VTKm_NO_INSTALL_README_LICENSE ) From 42786f6c18b7d2f07d8e66a599d17c752579443b Mon Sep 17 00:00:00 2001 From: NAThompson Date: Wed, 13 May 2020 12:01:24 -0400 Subject: [PATCH 05/27] Add vtkm_cont as PUBLIC dependency of vtkm_rendering in a flailing attempt to green up windows build. --- vtkm/rendering/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtkm/rendering/CMakeLists.txt b/vtkm/rendering/CMakeLists.txt index bed5b8a76..4f836aa31 100644 --- a/vtkm/rendering/CMakeLists.txt +++ b/vtkm/rendering/CMakeLists.txt @@ -138,7 +138,7 @@ vtkm_library( ) #----------------------------------------------------------------------------- -target_link_libraries(vtkm_rendering PUBLIC vtkm_filter vtkm_io) +target_link_libraries(vtkm_rendering PUBLIC vtkm_cont vtkm_filter vtkm_io) if(UNIX AND NOT APPLE) target_link_libraries(vtkm_rendering PRIVATE rt) endif() From 194c6f6b42a39437cc51034c934488b5fd3a4c20 Mon Sep 17 00:00:00 2001 From: NAThompson Date: Wed, 13 May 2020 12:31:25 -0400 Subject: [PATCH 06/27] Attempt to make UnitTestTimer fail less often. --- vtkm/cont/testing/UnitTestTimer.cxx | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/vtkm/cont/testing/UnitTestTimer.cxx b/vtkm/cont/testing/UnitTestTimer.cxx index 1f6c3ec91..a21f59a04 100644 --- a/vtkm/cont/testing/UnitTestTimer.cxx +++ b/vtkm/cont/testing/UnitTestTimer.cxx @@ -23,7 +23,7 @@ namespace using TimerTestDevices = vtkm::ListAppend>; -constexpr long long waitTimeMilliseconds = 100; +constexpr long long waitTimeMilliseconds = 5; constexpr vtkm::Float64 waitTimeSeconds = vtkm::Float64(waitTimeMilliseconds) / 1000; struct Waiter @@ -44,17 +44,10 @@ struct Waiter long long millisecondsToSleep = this->ExpectedTimeMilliseconds - elapsedMilliseconds; std::cout << " Sleeping for " << millisecondsToSleep << "ms (to " << expectedTimeSeconds - << "s)" << std::endl; + << "s)\n"; std::this_thread::sleep_for(std::chrono::milliseconds(millisecondsToSleep)); - VTKM_TEST_ASSERT(std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - this->Start) - .count() < - (this->ExpectedTimeMilliseconds + ((3 * waitTimeMilliseconds) / 4)), - "Internal test error: Sleep lasted longer than expected. System must be busy. " - "Might need to increase waitTimeMilliseconds."); - return expectedTimeSeconds; } }; @@ -63,15 +56,12 @@ void CheckTime(const vtkm::cont::Timer& timer, vtkm::Float64 expectedTime) { vtkm::Float64 elapsedTime = timer.GetElapsedTime(); VTKM_TEST_ASSERT( - elapsedTime > (expectedTime - 0.001), "Timer did not capture full wait. ", elapsedTime); - VTKM_TEST_ASSERT(elapsedTime < (expectedTime + waitTimeSeconds), - "Timer counted too far or system really busy. ", - elapsedTime); + elapsedTime > (expectedTime - 0.01), "Timer did not capture full wait. ", elapsedTime); } void DoTimerCheck(vtkm::cont::Timer& timer) { - std::cout << " Starting timer" << std::endl; + std::cout << " Starting timer\n"; timer.Start(); VTKM_TEST_ASSERT(timer.Started(), "Timer fails to track started status"); VTKM_TEST_ASSERT(!timer.Stopped(), "Timer fails to track non stopped status"); @@ -85,14 +75,14 @@ void DoTimerCheck(vtkm::cont::Timer& timer) CheckTime(timer, expectedTime); - std::cout << " Make sure timer is still running" << std::endl; + std::cout << " Make sure timer is still running\n"; VTKM_TEST_ASSERT(!timer.Stopped(), "Timer fails to track stopped status"); expectedTime = waiter.Wait(); CheckTime(timer, expectedTime); - std::cout << " Stop the timer" << std::endl; + std::cout << " Stop the timer\n"; timer.Stop(); VTKM_TEST_ASSERT(timer.Stopped(), "Timer fails to track stopped status"); @@ -100,7 +90,7 @@ void DoTimerCheck(vtkm::cont::Timer& timer) waiter.Wait(); // Do not advanced expected time - std::cout << " Check that timer legitimately stopped" << std::endl; + std::cout << " Check that timer legitimately stopped\n"; CheckTime(timer, expectedTime); } @@ -146,7 +136,7 @@ struct TimerCheckFunctor void DoTimerTest() { - std::cout << "Check default timer" << std::endl; + std::cout << "Check default timer\n"; vtkm::cont::Timer timer; DoTimerCheck(timer); From 1f8e2db7b2e8f590a21062feaa81a382e4e7b25d Mon Sep 17 00:00:00 2001 From: NAThompson Date: Wed, 13 May 2020 13:00:40 -0400 Subject: [PATCH 07/27] Remove unused variable from UnitTestTimer. --- vtkm/cont/testing/UnitTestTimer.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vtkm/cont/testing/UnitTestTimer.cxx b/vtkm/cont/testing/UnitTestTimer.cxx index a21f59a04..4c34bc73a 100644 --- a/vtkm/cont/testing/UnitTestTimer.cxx +++ b/vtkm/cont/testing/UnitTestTimer.cxx @@ -24,7 +24,6 @@ using TimerTestDevices = vtkm::ListAppend>; constexpr long long waitTimeMilliseconds = 5; -constexpr vtkm::Float64 waitTimeSeconds = vtkm::Float64(waitTimeMilliseconds) / 1000; struct Waiter { @@ -56,7 +55,7 @@ void CheckTime(const vtkm::cont::Timer& timer, vtkm::Float64 expectedTime) { vtkm::Float64 elapsedTime = timer.GetElapsedTime(); VTKM_TEST_ASSERT( - elapsedTime > (expectedTime - 0.01), "Timer did not capture full wait. ", elapsedTime); + elapsedTime > (expectedTime - 0.001), "Timer did not capture full wait. ", elapsedTime); } void DoTimerCheck(vtkm::cont::Timer& timer) From c18150d4b80555e114c9fe55b026008a0c7822ed Mon Sep 17 00:00:00 2001 From: NAThompson Date: Wed, 13 May 2020 14:08:50 -0400 Subject: [PATCH 08/27] Revert "Add vtkm_cont as PUBLIC dependency of vtkm_rendering in a flailing attempt to green up windows build." This reverts commit 42786f6c18b7d2f07d8e66a599d17c752579443b. --- vtkm/rendering/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtkm/rendering/CMakeLists.txt b/vtkm/rendering/CMakeLists.txt index 4f836aa31..bed5b8a76 100644 --- a/vtkm/rendering/CMakeLists.txt +++ b/vtkm/rendering/CMakeLists.txt @@ -138,7 +138,7 @@ vtkm_library( ) #----------------------------------------------------------------------------- -target_link_libraries(vtkm_rendering PUBLIC vtkm_cont vtkm_filter vtkm_io) +target_link_libraries(vtkm_rendering PUBLIC vtkm_filter vtkm_io) if(UNIX AND NOT APPLE) target_link_libraries(vtkm_rendering PRIVATE rt) endif() From 53c33e24ad4062c32d7160defbeea2d0f26fcc06 Mon Sep 17 00:00:00 2001 From: Robert Maynard Date: Wed, 13 May 2020 12:08:55 -0400 Subject: [PATCH 09/27] Assume NVCC has zero support for VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED --- vtkm/Deprecated.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vtkm/Deprecated.h b/vtkm/Deprecated.h index fe08a6c14..45493bbae 100644 --- a/vtkm/Deprecated.h +++ b/vtkm/Deprecated.h @@ -60,7 +60,9 @@ // [[deprecated]] is supported, then VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED will get defined. #ifndef VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED -#if __cplusplus >= 201402L && !defined(__NVCC__) +#if defined(__NVCC__) +// Currently nvcc has zero support deprecated attributes +#elif __cplusplus >= 201402L // C++14 and better supports [[deprecated]] // Except in these cases: @@ -68,7 +70,6 @@ #define VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED #elif defined(VTKM_GCC) - // GCC has supported [[deprecated]] since version 5.0, but using it on enum was not // supported until 6.0. So we have to make a special case to only use it for high // enough revisions. @@ -76,14 +77,14 @@ #define VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED #endif // Too old GCC -#elif defined(__has_cpp_attribute) && !defined(__NVCC__) +#elif defined(__has_cpp_attribute) #if __has_cpp_attribute(deprecated) // Compiler not fully C++14 compliant, but it reports to support [[deprecated]] #define VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED #endif // __has_cpp_attribute(deprecated) -#elif defined(VTKM_MSVC) && (_MSC_VER >= 1900) && !defined(__NVCC__) +#elif defined(VTKM_MSVC) && (_MSC_VER >= 1900) #define VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED From b59913fad3735e5b5b87b68313396f8403b60665 Mon Sep 17 00:00:00 2001 From: Robert Maynard Date: Wed, 13 May 2020 15:15:02 -0400 Subject: [PATCH 10/27] Log CDash build ids from each step of the build process This can help track down a weird CDash bugs caused by multiple in-flight builds that get canceled and result in bad CDash reporting --- .gitlab/ci/ctest_build.cmake | 9 ++++++++- .gitlab/ci/ctest_configure.cmake | 11 +++++++++-- .gitlab/ci/ctest_memcheck.cmake | 3 ++- .gitlab/ci/ctest_test.cmake | 3 ++- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.gitlab/ci/ctest_build.cmake b/.gitlab/ci/ctest_build.cmake index 3795d6d00..38a55c8c8 100644 --- a/.gitlab/ci/ctest_build.cmake +++ b/.gitlab/ci/ctest_build.cmake @@ -24,7 +24,14 @@ ctest_build(APPEND RETURN_VALUE build_result) if(NOT DEFINED ENV{GITLAB_CI_EMULATION}) - ctest_submit(PARTS Build) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15) + ctest_submit(PARTS Build BUILD_ID build_id) + message(STATUS "Build submission build_id: ${build_id}") + else() + ctest_submit(PARTS Build) + endif() + + endif() if (build_result) diff --git a/.gitlab/ci/ctest_configure.cmake b/.gitlab/ci/ctest_configure.cmake index e426ae0e6..6169dcbff 100644 --- a/.gitlab/ci/ctest_configure.cmake +++ b/.gitlab/ci/ctest_configure.cmake @@ -38,8 +38,15 @@ ctest_configure(APPEND # We can now submit because we've configured. if(NOT DEFINED ENV{GITLAB_CI_EMULATION}) - ctest_submit(PARTS Update) - ctest_submit(PARTS Configure) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15) + ctest_submit(PARTS Update BUILD_ID build_id) + message(STATUS "Update submission build_id: ${build_id}") + ctest_submit(PARTS Configure BUILD_ID build_id) + message(STATUS "Configure submission build_id: ${build_id}") + else() + ctest_submit(PARTS Update) + ctest_submit(PARTS Configure) + endif() endif() if (configure_result) diff --git a/.gitlab/ci/ctest_memcheck.cmake b/.gitlab/ci/ctest_memcheck.cmake index 529a57817..abf80a34c 100644 --- a/.gitlab/ci/ctest_memcheck.cmake +++ b/.gitlab/ci/ctest_memcheck.cmake @@ -48,7 +48,8 @@ ctest_memcheck( EXCLUDE "${test_exclusions}" DEFECT_COUNT defects) -ctest_submit(PARTS Memcheck) +ctest_submit(PARTS Memcheck BUILD_ID build_id) + message(STATUS "Memcheck submission build_id: ${build_id}") if (defects) message(FATAL_ERROR "Found ${defects} memcheck defects") diff --git a/.gitlab/ci/ctest_test.cmake b/.gitlab/ci/ctest_test.cmake index 9cf46e975..4ff74a4a9 100644 --- a/.gitlab/ci/ctest_test.cmake +++ b/.gitlab/ci/ctest_test.cmake @@ -35,7 +35,8 @@ ctest_test(APPEND ) if(NOT DEFINED ENV{GITLAB_CI_EMULATION}) - ctest_submit(PARTS Test) + ctest_submit(PARTS Test BUILD_ID build_id) + message(STATUS "Test submission build_id: ${build_id}") endif() if (test_result) From 24d022b02b34474b9e737a143d942cea0a586f89 Mon Sep 17 00:00:00 2001 From: nadavi Date: Fri, 22 Nov 2019 15:40:09 -0700 Subject: [PATCH 11/27] Implement and test ImageReader and ImageWriter capabilities in the io library --- docs/changelog/image_io.md | 32 ++++ vtkm/cont/testing/UnitTestLogging.cxx | 1 + vtkm/io/CMakeLists.txt | 24 ++- vtkm/io/DecodePNG.cxx | 1 - vtkm/io/ImageReader.h | 155 +++++++++++++++++++ vtkm/io/ImageReader.hxx | 176 +++++++++++++++++++++ vtkm/io/ImageWriter.h | 140 +++++++++++++++++ vtkm/io/ImageWriter.hxx | 173 +++++++++++++++++++++ vtkm/io/PixelTypes.h | 195 ++++++++++++++++++++++++ vtkm/io/PixelTypes.hxx | 85 +++++++++++ vtkm/io/reader/CMakeLists.txt | 4 +- vtkm/io/testing/CMakeLists.txt | 15 +- vtkm/io/testing/UnitTestImageWriter.cxx | 166 ++++++++++++++++++++ vtkm/io/testing/UnitTestPixelTypes.cxx | 176 +++++++++++++++++++++ vtkm/io/writer/CMakeLists.txt | 6 +- 15 files changed, 1338 insertions(+), 11 deletions(-) create mode 100644 docs/changelog/image_io.md create mode 100644 vtkm/io/ImageReader.h create mode 100644 vtkm/io/ImageReader.hxx create mode 100644 vtkm/io/ImageWriter.h create mode 100644 vtkm/io/ImageWriter.hxx create mode 100644 vtkm/io/PixelTypes.h create mode 100644 vtkm/io/PixelTypes.hxx create mode 100644 vtkm/io/testing/UnitTestImageWriter.cxx create mode 100644 vtkm/io/testing/UnitTestPixelTypes.cxx diff --git a/docs/changelog/image_io.md b/docs/changelog/image_io.md new file mode 100644 index 000000000..d4ca4d03c --- /dev/null +++ b/docs/changelog/image_io.md @@ -0,0 +1,32 @@ +# Implemented PNG/PPM image Readers/Writers + +The original implementation of writing image data was only performed as a +proxy through the Canvas rendering class. In order to implement true support +for image-based regression testing, this interface needed to be expanded upon +to support reading/writing arbitrary image data and storing it in a `vtkm::DataSet`. +Using the new `vtkm::io::PNGReader` and `vtkm::io::PPMReader` it is possible +to read data from files and Cavases directly and store them as a point field +in a 2D uniform `vtkm::DataSet` + +```cpp +auto reader = vtkm::io::PNGReader(); +auto imageDataSet = reader.ReadFromFile("read_image.png"); +``` + +Similarly, the new `vtkm::io::PNGWriter` and `vtkm::io::PPMWriter` make it possible +to write out a 2D uniform `vtkm::DataSet` directly to a file. + +```cpp +auto writer = vtkm::io::PNGWriter(); +writer.WriteToFile("write_image.png", imageDataSet); +``` + +If canvas data is to be written out, the reader provides a method for converting +a canvas's data to a `vtkm::DataSet`. + +```cpp +auto reader = vtkm::io::PNGReader(); +auto dataSet = reader.CreateImageDataSet(canvas); +auto writer = vtkm::io::PNGWriter(); +writer.WriteToFile("output.png", dataSet); +``` diff --git a/vtkm/cont/testing/UnitTestLogging.cxx b/vtkm/cont/testing/UnitTestLogging.cxx index 047173e3d..920e2bd18 100644 --- a/vtkm/cont/testing/UnitTestLogging.cxx +++ b/vtkm/cont/testing/UnitTestLogging.cxx @@ -97,6 +97,7 @@ void RunTests() int UnitTestLogging(int, char* []) { // Test that parameterless init works: + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Log before intialize"); vtkm::cont::InitLogging(); RunTests(); diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index e8dfb0465..7d8bc7660 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -13,6 +13,9 @@ set(headers ErrorIO.h DecodePNG.h EncodePNG.h + ImageReader.h + ImageWriter.h + PixelTypes.h VTKDataSetReader.h VTKDataSetReaderBase.h VTKPolyDataReader.h @@ -23,17 +26,28 @@ set(headers VTKDataSetWriter.h ) +set(template_sources + ImageReader.hxx + ImageWriter.hxx + PixelTypes.hxx +) + set(sources DecodePNG.cxx EncodePNG.cxx ) -vtkm_declare_headers(${headers}) +vtkm_declare_headers( + ${headers} + ${template_sources} +) -vtkm_library( NAME vtkm_io - SOURCES ${sources} - HEADERS ${headers} - ) +vtkm_library( + NAME vtkm_io + SOURCES ${sources} + HEADERS ${headers} + TEMPLATE_SOURCES ${template_sources} +) target_link_libraries(vtkm_io PUBLIC vtkm_cont PRIVATE vtkm_lodepng) diff --git a/vtkm/io/DecodePNG.cxx b/vtkm/io/DecodePNG.cxx index 8d89e15b9..fa1c71434 100644 --- a/vtkm/io/DecodePNG.cxx +++ b/vtkm/io/DecodePNG.cxx @@ -13,7 +13,6 @@ #include #include - VTKM_THIRDPARTY_PRE_INCLUDE #include VTKM_THIRDPARTY_POST_INCLUDE diff --git a/vtkm/io/ImageReader.h b/vtkm/io/ImageReader.h new file mode 100644 index 000000000..06d470edd --- /dev/null +++ b/vtkm/io/ImageReader.h @@ -0,0 +1,155 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_ImageReader_h +#define vtk_m_io_ImageReader_h + +#include +#include +#include + +namespace vtkm +{ + +// Forward Declare +namespace rendering +{ +class Canvas; +} // namespace rendering + +namespace io +{ + +/// \brief Manages reading, and loading data from images +/// +/// \c BaseImageReader implements methods for loading imaging data from a canvas or +/// ArrayHandle and storing that data in a vtkm::cont::DataSet. Image rgb values +/// are represented as a point field in a 2D uniform dataset. +/// +/// \c BaseImageReader can be constructed from a file, canvas, or ArrayHandle. It can +/// also be empy constructed and filled in with a dataset later. +/// +/// \c BaseImageReader implements virtual methods for reading files. Ideally, +/// these methods will be overriden in various subclasses to implement specific +/// functionality for reading data to specific image file-types. +/// +/// \c The templated type is used when Filling the ImageDataSet. +/// +class BaseImageReader +{ +public: + /// Constructs an emtpy BaseImageReader. + /// + BaseImageReader() = default; + explicit BaseImageReader(const vtkm::Id& maxColorValue) + : MaxColorValue(maxColorValue) + { + } + ~BaseImageReader() noexcept = default; + + /// Reads image data from a file. Meant to be implemented in overriden + /// image-specific classes + /// + virtual vtkm::cont::DataSet ReadFromFile(const std::string& fileName) = 0; + + /// Creates an ImageDataSet from a Canvas object + /// + vtkm::cont::DataSet CreateImageDataSet(const vtkm::rendering::Canvas& canvas); + + /// Creates an ImageDataSet from a RGBA 32bit float color buffer + /// Assumes the color buffer is stored in row-major ordering + /// + vtkm::cont::DataSet CreateImageDataSet(const vtkm::cont::ArrayHandle& colorBuffer, + const vtkm::Id& width, + const vtkm::Id& height); + + const std::string& GetPointFieldName() const { return this->PointFieldName; } + +protected: + vtkm::cont::DataSet InitializeImageDataSet(const vtkm::Id& width, const vtkm::Id& height); + + std::string PointFieldName = "pixel-data"; + vtkm::Id MaxColorValue{ 0 }; +}; + +/// \brief Manages reading images using the PNG format via lodepng +/// +/// \c PNGReader extends BaseImageReader and implements reading images in a valid +/// PNG format. It utilizes lodepng's decode file functions to read +/// PNG images that are automatically compressed to optimal sizes relative to +/// the actual bit complexity of the image. +/// +/// \c PNGReader will automatically upsample/downsample read image data +/// to a 16 bit RGB no matter how the image is compressed. It is up to the user to +/// decide the pixel format for input PNGs +class PNGReader : public BaseImageReader +{ + using Superclass = BaseImageReader; + +public: + using Superclass::Superclass; + PNGReader() = default; + ~PNGReader() noexcept = default; + + /// Reads PNG data from the provided file and stores it + /// as a 16bit RGB value + /// + vtkm::cont::DataSet ReadFromFile(const std::string& fileName) override; + + /// Reads PNG data from the provided file and stores it + /// according to the method's templated PixelType + /// + template + vtkm::cont::DataSet ReadFromFile(const std::string& fileName); +}; + + +/// \brief Manages reading images using the PNM format +/// +/// \c PNMImage extends BaseImage, and implements reading images from a +/// valid PNM format (for magic number P6). More details on the PNM +/// format can be found here: http://netpbm.sourceforge.net/doc/ppm.html +/// +/// When a file is read the parsed MagicNumber and MaxColorSize provided +/// are utilized to correctly parse the bits from the file +class PNMReader : public BaseImageReader +{ + using Superclass = BaseImageReader; + +public: + using Superclass::Superclass; + PNMReader() = default; + ~PNMReader() noexcept = default; + + /// Attempts to read the provided file into a DataSet object. + /// Will pull the image's MaxColorValue from the file and then Decode + /// with the appropriate RGB PixelType bit depth. + /// + vtkm::cont::DataSet ReadFromFile(const std::string& fileName) override; + +protected: + /// Reads image data from the provided inStream with the supplied width/height + /// Stores the data in a vector of PixelType which is converted to an DataSet + /// + template + vtkm::cont::DataSet DecodeFile(std::ifstream& inStream, + const vtkm::Id& width, + const vtkm::Id& height); + + // This is set to only work for P6 pnm image types for now (ie ppm) + std::string MagicNumber{ "P6" }; +}; + + +} // namespace io +} // namespace vtkm + +#include + +#endif diff --git a/vtkm/io/ImageReader.hxx b/vtkm/io/ImageReader.hxx new file mode 100644 index 000000000..60afc7f9b --- /dev/null +++ b/vtkm/io/ImageReader.hxx @@ -0,0 +1,176 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_ImageReader_hxx +#define vtk_m_io_ImageReader_hxx + +#include +#include +#include +#include +#include +#include + +VTKM_THIRDPARTY_PRE_INCLUDE +#include +VTKM_THIRDPARTY_POST_INCLUDE + +namespace vtkm +{ +namespace io +{ + +// Start BaseReaderImage Class Template Implementations +VTKM_CONT +vtkm::cont::DataSet BaseImageReader::CreateImageDataSet(const vtkm::rendering::Canvas& canvas) +{ + return this->CreateImageDataSet(canvas.GetColorBuffer(), canvas.GetWidth(), canvas.GetHeight()); +} + +VTKM_CONT +vtkm::cont::DataSet BaseImageReader::CreateImageDataSet( + const vtkm::cont::ArrayHandle& colorBuffer, + const vtkm::Id& width, + const vtkm::Id& height) +{ + vtkm::cont::ArrayHandle::ReadPortalType colorPortal = colorBuffer.ReadPortal(); + std::vector fieldData; + for (vtkm::Id yIndex = 0; yIndex < height; yIndex++) + { + for (vtkm::Id xIndex = 0; xIndex < width; xIndex++) + { + vtkm::Vec4f_32 tuple = colorPortal.Get(yIndex * width + xIndex); + fieldData.push_back(tuple); + } + } + auto dataSet = this->InitializeImageDataSet(width, height); + vtkm::cont::DataSetFieldAdd dsf; + dsf.AddPointField(dataSet, this->PointFieldName, fieldData); + return dataSet; +} + +VTKM_CONT +vtkm::cont::DataSet BaseImageReader::InitializeImageDataSet(const vtkm::Id& width, + const vtkm::Id& height) +{ + vtkm::cont::DataSetBuilderUniform dsb; + vtkm::Id2 dimensions(width, height); + return dsb.Create(dimensions); +} +// End BaseReaderImage Class Template Implementations + +// Start PNGReader Class Template Implementations +VTKM_CONT +vtkm::cont::DataSet PNGReader::ReadFromFile(const std::string& fileName) +{ + return this->ReadFromFile(fileName); +} + +VTKM_CONT +template +vtkm::cont::DataSet PNGReader::ReadFromFile(const std::string& fileName) +{ + unsigned char* imageData; + unsigned uwidth, uheight; + vtkm::Id width, height; + vtkm::png::lodepng_decode_file(&imageData, + &uwidth, + &uheight, + fileName.c_str(), + PixelType::PNG_COLOR_TYPE, + PixelType::BIT_DEPTH); + + width = static_cast(uwidth); + height = static_cast(uheight); + + // Fill in the data starting from the end (Images are read Top-Left to Bottom-Right, + // but are stored from Bottom-Left to Top-Right) + std::vector fieldData; + for (vtkm::Id yIndex = static_cast(height - 1); yIndex >= 0; yIndex--) + { + for (vtkm::Id xIndex = 0; xIndex < static_cast(width); xIndex++) + { + vtkm::Id index = static_cast(yIndex * width + xIndex); + fieldData.push_back(PixelType(imageData, index).ToVec4f()); + } + } + + auto dataSet = this->InitializeImageDataSet(width, height); + vtkm::cont::DataSetFieldAdd dsf; + dsf.AddPointField(dataSet, this->PointFieldName, fieldData); + + free(imageData); + return dataSet; +} +// End PNGReader Class Template Implementations + +// Start PNMReader Class Template Implementations +VTKM_CONT +vtkm::cont::DataSet PNMReader::ReadFromFile(const std::string& fileName) +{ + std::ifstream inStream(fileName.c_str(), std::ios_base::binary | std::ios_base::in); + vtkm::Id width; + vtkm::Id height; + std::string val; + + inStream >> val; + if (this->MagicNumber != val) + { + throw vtkm::cont::ErrorBadValue("MagicNumber: " + this->MagicNumber + " in file: " + fileName + + " did not match: " + val); + } + + inStream >> width >> height >> this->MaxColorValue; + inStream.get(); + + switch (this->MaxColorValue) + { + case 255: + return this->DecodeFile(inStream, width, height); + case 65535: + return this->DecodeFile(inStream, width, height); + default: + throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(this->MaxColorValue) + + " from file: " + fileName + " was not one of: {8, 16}"); + } +} + +VTKM_CONT +template +vtkm::cont::DataSet PNMReader::DecodeFile(std::ifstream& inStream, + const vtkm::Id& width, + const vtkm::Id& height) +{ + vtkm::UInt32 imageSize = static_cast(width * height * PixelType::BYTES_PER_PIXEL); + std::vector imageData(imageSize); + inStream.read((char*)imageData.data(), imageSize); + + // Fill in the data starting from the end (Images are read Top-Left to Bottom-Right, + // but are stored from Bottom-Left to Top-Right) + std::vector fieldData; + for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--) + { + for (vtkm::Id xIndex = 0; xIndex < width; xIndex++) + { + vtkm::Id index = yIndex * width + xIndex; + fieldData.push_back(PixelType(imageData.data(), index).ToVec4f()); + } + } + + auto dataSet = this->InitializeImageDataSet(width, height); + vtkm::cont::DataSetFieldAdd dsf; + dsf.AddPointField(dataSet, this->PointFieldName, fieldData); + return dataSet; +} +// End PNMReader Class Template Implementations + +} // namespace io +} // namespace vtkm + +#endif diff --git a/vtkm/io/ImageWriter.h b/vtkm/io/ImageWriter.h new file mode 100644 index 000000000..7ccc1284a --- /dev/null +++ b/vtkm/io/ImageWriter.h @@ -0,0 +1,140 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_ImageWriter_h +#define vtk_m_io_ImageWriter_h + +#include +#include +#include + +namespace vtkm +{ +namespace io +{ + +/// \brief Manages writing, and loading data from images +/// +/// \c BaseImageWriter implements methods for loading imaging data from a canvas or +/// ArrayHandle and storing that data in a vtkm::cont::DataSet. Image rgb values +/// are represented as a point field in a 2D uniform dataset. +/// +/// \c BaseImageWriter can be constructed from a file, canvas, or ArrayHandle. It can +/// also be empy constructed and filled in with a dataset later. +/// +/// \c BaseImageWriter implements virtual methods for writing files. Ideally, +/// these methods will be overriden in various subclasses to implement specific +/// functionality for writing data to specific image file-types. +/// +class BaseImageWriter +{ +public: + /// Constructs an emtpy BaseImageWriter. + /// + BaseImageWriter() = default; + explicit BaseImageWriter(const vtkm::Id& maxColorValue) + : MaxColorValue(maxColorValue) + { + } + ~BaseImageWriter() noexcept = default; + + /// Write and store ImageDataSet to a file. Meant to be implemented in + /// overriden image-specific classes + /// + virtual void WriteToFile(const std::string& fileName, + const vtkm::cont::DataSet& dataSet) const = 0; + + vtkm::Id GetImageWidth(vtkm::cont::DataSet dataSet) const; + vtkm::Id GetImageHeight(vtkm::cont::DataSet dataSet) const; + + const std::string& GetPointFieldName() const { return this->PointFieldName; } + void SetMaxColorValue(const vtkm::Id& maxColorValue) { this->MaxColorValue = maxColorValue; } + +protected: + std::string PointFieldName = "pixel-data"; + vtkm::Id MaxColorValue{ 0 }; +}; + +/// \brief Manages writing images using the PNM format +/// +/// \c PNMWriter extends BaseImageWriter, and implements writing images in a +/// valid PNM format (for magic number P6). More details on the PNM +/// format can be found here: http://netpbm.sourceforge.net/doc/ppm.html +/// +/// When a file is writen the MaxColorValue found in the file is used to +/// determine the PixelType required to stored PixelType is instead dependent +/// upon the read MaxColorValue obtained from the file +class PNMWriter : public BaseImageWriter +{ + using Superclass = BaseImageWriter; + +public: + using Superclass::Superclass; + PNMWriter() = default; + ~PNMWriter() noexcept = default; + + /// Attempts to write the ImageDataSet to a PNM file. The MaxColorValue + /// set in the file with either be selected from the stored MaxColorValue + /// member variable, or from the templated type if MaxColorValue hasn't been + /// set from a read file. + /// + void WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const override; + +protected: + /// Writes image data stored in ImageDataSet to the provided outStream + /// Casts the data to the provided PixelType + /// + template + void EncodeFile(std::ofstream& outStream, const vtkm::cont::DataSet& dataSet) const; + + // Currently only works with P6 PNM files (PPM) + std::string MagicNumber{ "P6" }; +}; + +/// \brief Manages writing images using the PNG format via lodepng +/// +/// \c PNGWriter extends BaseImageWriter and implements writing images in a valid +/// PNG format. It utilizes lodepng's encode file functions to write +/// PNG images that are automatically compressed to optimal sizes relative to +/// the actual bit complexity of the image. +/// +/// \c PNGImage will automatically upsample/downsample written image data +/// to the supplied templated PixelType. For example, it is possible to write +/// a 1-bit greyscale image into a 16bit RGB PNG object. It is up to the user to +/// decide the pixel format for output PNGs +class PNGWriter : public BaseImageWriter +{ + using Superclass = BaseImageWriter; + +public: + using Superclass::Superclass; + PNGWriter() = default; + ~PNGWriter() noexcept = default; + + /// Writes stored data matched to the class's templated type + /// to a file in PNG format. Relies upon the lodepng encoding + /// method to optimize compression and choose the best storage format. + /// + void WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const override; + + /// Writes stored data matched to the method's templated type + /// to a file in PNG format. Relies upon the lodepng encoding + /// method to optimize compression and choose the best storage format. + /// + template + void WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const; +}; + + +} // namespace io +} // namespace vtkm + +#include + +#endif diff --git a/vtkm/io/ImageWriter.hxx b/vtkm/io/ImageWriter.hxx new file mode 100644 index 000000000..9a056fe8c --- /dev/null +++ b/vtkm/io/ImageWriter.hxx @@ -0,0 +1,173 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_ImageWriter_hxx +#define vtk_m_io_ImageWriter_hxx + +#include +#include +#include +#include +#include + +VTKM_THIRDPARTY_PRE_INCLUDE +#include +VTKM_THIRDPARTY_POST_INCLUDE + +namespace vtkm +{ +namespace io +{ + +VTKM_CONT +vtkm::Id BaseImageWriter::GetImageWidth(vtkm::cont::DataSet dataSet) const +{ + if (dataSet.GetNumberOfCoordinateSystems() > 0) + { + // Add 1 since the Bounds are 0 indexed + return static_cast(dataSet.GetCoordinateSystem().GetBounds().X.Max) + 1; + } + return 0; +} + +VTKM_CONT +vtkm::Id BaseImageWriter::GetImageHeight(vtkm::cont::DataSet dataSet) const +{ + if (dataSet.GetNumberOfCoordinateSystems() > 0) + { + // Add 1 since the Bounds are 0 indexed + return static_cast(dataSet.GetCoordinateSystem().GetBounds().Y.Max) + 1; + } + return 0; +} + +VTKM_CONT +void PNMWriter::WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const +{ + if (!dataSet.HasField(this->PointFieldName)) + { + throw vtkm::cont::ErrorBadValue( + "No pixel data found in DataSet, cannot write without image data!"); + } + + std::ofstream outStream(fileName.c_str(), std::ios_base::binary | std::ios_base::out); + outStream << this->MagicNumber << std::endl + << this->GetImageWidth(dataSet) << " " << this->GetImageHeight(dataSet) << std::endl; + + switch (this->MaxColorValue) + { + case 0: + this->EncodeFile(outStream, dataSet); + break; + case 255: + this->EncodeFile(outStream, dataSet); + break; + case 65535: + this->EncodeFile(outStream, dataSet); + break; + default: + throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(this->MaxColorValue) + + " was not one of: {255, 65535}"); + } +} + +VTKM_CONT +template +void PNMWriter::EncodeFile(std::ofstream& outStream, const vtkm::cont::DataSet& dataSet) const +{ + outStream << PixelType::MAX_COLOR_VALUE << std::endl; + auto pixelField = dataSet.GetPointField(this->PointFieldName) + .GetData() + .template Cast>(); + auto pixelPortal = pixelField.ReadPortal(); + + vtkm::UInt32 imageSize = + static_cast(pixelField.GetNumberOfValues() * PixelType::BYTES_PER_PIXEL); + std::vector imageData(imageSize); + + // Write out the data starting from the end (Images are stored Bottom-Left to Top-Right, + // but are viewed from Top-Left to Bottom-Right) + vtkm::Id imageIndex = 0; + vtkm::Id imageHeight = this->GetImageHeight(dataSet); + vtkm::Id imageWidth = this->GetImageWidth(dataSet); + for (vtkm::Id yIndex = imageHeight - 1; yIndex >= 0; yIndex--) + { + for (vtkm::Id xIndex = 0; xIndex < imageWidth; xIndex++, imageIndex++) + { + vtkm::Id index = yIndex * imageWidth + xIndex; + PixelType(pixelPortal.Get(index)).FillImageAtIndexWithPixel(imageData.data(), imageIndex); + } + } + outStream.write((char*)imageData.data(), imageSize); + outStream.close(); +} + +VTKM_CONT +void PNGWriter::WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const +{ + switch (this->MaxColorValue) + { + case 0: + WriteToFile(fileName, dataSet); + break; + case 255: + WriteToFile(fileName, dataSet); + break; + case 65535: + WriteToFile(fileName, dataSet); + break; + default: + throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(this->MaxColorValue) + + " was not one of: {255, 65535}"); + } +} + +VTKM_CONT +template +void PNGWriter::WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const +{ + if (!dataSet.HasField(this->PointFieldName)) + { + throw vtkm::cont::ErrorBadValue( + "No pixel data found in DataSet, cannot write without image data!"); + } + + auto pixelField = dataSet.GetPointField(this->PointFieldName) + .GetData() + .template Cast>(); + auto pixelPortal = pixelField.ReadPortal(); + std::vector imageData(static_cast::size_type>( + pixelField.GetNumberOfValues() * PixelType::BYTES_PER_PIXEL)); + + // Write out the data starting from the end (Images are stored Bottom-Left to Top-Right, + // but are viewed from Top-Left to Bottom-Right) + vtkm::Id imageIndex = 0; + vtkm::Id imageHeight = this->GetImageHeight(dataSet); + vtkm::Id imageWidth = this->GetImageWidth(dataSet); + for (vtkm::Id yIndex = imageHeight - 1; yIndex >= 0; yIndex--) + { + for (vtkm::Id xIndex = 0; xIndex < imageWidth; xIndex++, imageIndex++) + { + vtkm::Id index = yIndex * imageWidth + xIndex; + PixelType(pixelPortal.Get(index)).FillImageAtIndexWithPixel(imageData.data(), imageIndex); + } + } + + vtkm::png::lodepng_encode_file(fileName.c_str(), + imageData.data(), + static_cast(imageWidth), + static_cast(imageHeight), + PixelType::PNG_COLOR_TYPE, + PixelType::BIT_DEPTH); +} + +} // namespace io +} // namespace vtkm + +#endif diff --git a/vtkm/io/PixelTypes.h b/vtkm/io/PixelTypes.h new file mode 100644 index 000000000..4b7ee0ab5 --- /dev/null +++ b/vtkm/io/PixelTypes.h @@ -0,0 +1,195 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_PixelTypes_h +#define vtk_m_io_PixelTypes_h + +#include +#include + +VTKM_THIRDPARTY_PRE_INCLUDE +#include +VTKM_THIRDPARTY_POST_INCLUDE + +namespace vtkm +{ +namespace io +{ + +// ---------------------------------------------------------------------- +// Define custom SFINAE structures to calculate the VTKM types associated +// with provided BitDepths +template +struct ComponentTypeFromSize +{ +}; + +template +struct ComponentTypeFromSize::type> +{ + using type = vtkm::UInt8; +}; +template +struct ComponentTypeFromSize::type> +{ + using type = vtkm::UInt16; +}; +// ---------------------------------------------------------------------- + +/// \brief Base type for more complex pixels (RGB, Greyscale, etc) that describes various values +/// such as bit-depth, channel width, bytes per pixel, and how various data should be polled +/// +/// \c BasePixel takes BitDepth and Channels as template parameters. BitDepth describes the number +/// of bits in the pixel, while Channels describes the multiple of bits that are available. +/// BasePixel extends vtkm::Vec. The ComponentType is pulled from the ComponentTypeFromSize +/// SFINAE struct defined above. This helps with optimizing the pixel size for a given +/// bit-depth. The Size is pulled from the Channels param. +/// +/// \c BasePixel requires BitDepths that are > 8 and powers of 2 at the moment. BitDepths of +/// 4, 2, or 1 bit are not correctly handled at the moment. +/// +/// \c BasePixel describes how to populate itself from an unsigned char pointer (assuming that +/// the data stored within the pointer matches the bit-depth and channels described by the +/// BasePixel type), and how to fill in data for an unsigned char pointer. This is mostly +/// useful in serialization and deserialization to various image formats. +/// +template +class BasePixel : public vtkm::Vec::type, Channels> +{ + static_assert(BitDepth >= 8, "BitDepth not >= 8"); + static_assert(!(BitDepth & (BitDepth - 1)), "BitDepth not a power of 2"); + +public: + using Superclass = vtkm::Vec::type, Channels>; + using ComponentType = typename Superclass::ComponentType; + using BaseType = BasePixel; + + static constexpr vtkm::IdComponent BIT_DEPTH = BitDepth; + static constexpr vtkm::IdComponent NUM_BYTES = BitDepth / 8; + static constexpr vtkm::IdComponent MAX_COLOR_VALUE = (1 << BitDepth) - 1; + static constexpr vtkm::IdComponent NUM_CHANNELS = Superclass::NUM_COMPONENTS; + static constexpr vtkm::IdComponent BYTES_PER_PIXEL = NUM_CHANNELS * NUM_BYTES; + + using Superclass::Superclass; + BasePixel() = default; + + /// Fills in this->Components by calling ConstructPixelFromImage. Requires + /// the base vec values to be zeroed out initially. + /// + BasePixel(const unsigned char* imageData, const vtkm::Id index) + : Superclass(0) + { + ConstructPixelFromImage(imageData, index); + } + + /// Calculates this difference between two pixels as a single value. + /// + virtual ComponentType Diff(const BaseType& pixel) const = 0; + + /// Generates a Vec4f_32 from the current data available in the pixel + /// + virtual vtkm::Vec4f_32 ToVec4f() const = 0; + + /// Implement the << operator for this class type. Will call the overloaded + /// print method for the subclassed type. + /// + friend std::ostream& operator<<(std::ostream& os, const BaseType& basePixel) + { + basePixel.print(os); + return os; + } + + /// Takes an output imageData pointer and in index to a location in that dataset + /// and fills in the pixel data at the location. Utilizes BIT_DEPTH and + /// NUM_CHANNELS to fill in multiple bytes worth of data if necessary. + /// + void FillImageAtIndexWithPixel(unsigned char* imageData, const vtkm::Id index); + +protected: + /// Takes an input imageData pointer and an index to a location in that dataset + /// and fills in this->Components correctly using the provided BIT_DEPTH and + /// NUM_CHANNELS. Does NOT 0 out the current Components. + /// + void ConstructPixelFromImage(const unsigned char* imageData, const vtkm::Id index); + + virtual void print(std::ostream& os) const = 0; +}; + + +template +class RGBPixel : public BasePixel +{ +public: + // RGB values are stored in a vtkm::Vec + using Superclass = BasePixel; + using ComponentType = typename Superclass::ComponentType; + static constexpr vtkm::png::LodePNGColorType PNG_COLOR_TYPE = + vtkm::png::LodePNGColorType::LCT_RGB; + + using Superclass::Superclass; + RGBPixel() = default; + RGBPixel(vtkm::Vec4f_32 tuple) + : Superclass(static_cast(tuple[0] * this->MAX_COLOR_VALUE), + static_cast(tuple[1] * this->MAX_COLOR_VALUE), + static_cast(tuple[2] * this->MAX_COLOR_VALUE)) + { + } + + ComponentType Diff(const Superclass& pixel) const override; + vtkm::Vec4f_32 ToVec4f() const override; + +protected: + void print(std::ostream& os) const override + { + os << "(" << (int)this->Components[0] << "," << (int)this->Components[1] << "," + << (int)this->Components[2] << ")"; + } +}; + +// Default types for 8 and 16 bit RGB pixels +using RGBPixel_8 = RGBPixel<8>; +using RGBPixel_16 = RGBPixel<16>; + +template +class GreyPixel : public BasePixel +{ +public: + // Grey values are stored in a vtkm::Vec + // Note: A vec of size 1 is used instead of just a `ComponentType` + // in order to simplify the pixel helper functions + using Superclass = BasePixel; + using ComponentType = typename Superclass::ComponentType; + static constexpr vtkm::png::LodePNGColorType PNG_COLOR_TYPE = + vtkm::png::LodePNGColorType::LCT_GREY; + + using Superclass::Superclass; + GreyPixel() = default; + GreyPixel(vtkm::Vec4f_32 tuple) + : Superclass( + static_cast((tuple[0] + tuple[1] + tuple[2]) * this->MAX_COLOR_VALUE / 3)) + { + } + + ComponentType Diff(const Superclass& pixel) const override; + vtkm::Vec4f_32 ToVec4f() const override; + +protected: + void print(std::ostream& os) const override { os << "(" << (int)this->Components[0] << ")"; } +}; + +// Default types for 8 and 16 bit Grey pixels +using GreyPixel_16 = GreyPixel<16>; +using GreyPixel_8 = GreyPixel<8>; + +} // namespace io +} // namespace vtkm + +#include + +#endif //vtk_m_io_PixelTypes_h diff --git a/vtkm/io/PixelTypes.hxx b/vtkm/io/PixelTypes.hxx new file mode 100644 index 000000000..4b7dcbe38 --- /dev/null +++ b/vtkm/io/PixelTypes.hxx @@ -0,0 +1,85 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_PixelTypes_hxx +#define vtk_m_io_PixelTypes_hxx + +#include +#include + +namespace vtkm +{ +namespace io +{ + +template +void BasePixel::FillImageAtIndexWithPixel(unsigned char* imageData, const vtkm::Id index) +{ + vtkm::Id initShift = BIT_DEPTH - 8; + for (vtkm::Id channel = 0; channel < NUM_CHANNELS; channel++) + { + for (vtkm::Id shift = initShift, i = 0; shift >= 0; shift -= 8, i++) + { + imageData[index * BYTES_PER_PIXEL + i + (channel * NUM_BYTES)] = + static_cast((this->Components[channel] & (0xff << shift)) >> shift); + } + } +} + +template +void BasePixel::ConstructPixelFromImage(const unsigned char* imageData, const vtkm::Id index) +{ + vtkm::Id initShift = BIT_DEPTH - 8; + for (vtkm::Id channel = 0; channel < NUM_CHANNELS; channel++) + { + for (vtkm::Id shift = initShift, i = 0; shift >= 0; shift -= 8, i++) + { + this->Components[channel] |= imageData[index * BYTES_PER_PIXEL + i + (channel * NUM_BYTES)] + << shift; + } + } +} + +template +typename RGBPixel::ComponentType RGBPixel::Diff(const Superclass& pixel) const +{ + return static_cast::ComponentType>(vtkm::Abs(this->Components[0] - pixel[0]) + + vtkm::Abs(this->Components[1] - pixel[1]) + + vtkm::Abs(this->Components[2] - pixel[2])); +} + +template +vtkm::Vec4f_32 RGBPixel::ToVec4f() const +{ + return vtkm::Vec4f_32(static_cast(this->Components[0]) / this->MAX_COLOR_VALUE, + static_cast(this->Components[1]) / this->MAX_COLOR_VALUE, + static_cast(this->Components[2]) / this->MAX_COLOR_VALUE, + 1); +} + +template +typename GreyPixel::ComponentType GreyPixel::Diff(const Superclass& pixel) const +{ + return static_cast::ComponentType>(vtkm::Abs(this->Components[0] - pixel[0])); +} + +template +vtkm::Vec4f_32 GreyPixel::ToVec4f() const +{ + return vtkm::Vec4f_32(static_cast(this->Components[0]) / this->MAX_COLOR_VALUE, + static_cast(this->Components[0]) / this->MAX_COLOR_VALUE, + static_cast(this->Components[0]) / this->MAX_COLOR_VALUE, + 1); +} + + +} // namespace io +} // namespace vtkm + +#endif //vtk_m_io_PixelTypes_h diff --git a/vtkm/io/reader/CMakeLists.txt b/vtkm/io/reader/CMakeLists.txt index 25047e830..2128ba0a7 100644 --- a/vtkm/io/reader/CMakeLists.txt +++ b/vtkm/io/reader/CMakeLists.txt @@ -13,4 +13,6 @@ set(headers VTKDataSetReader.h ) -vtkm_declare_headers(${headers}) +vtkm_declare_headers( + ${headers} +) diff --git a/vtkm/io/testing/CMakeLists.txt b/vtkm/io/testing/CMakeLists.txt index b0e219f1c..f17628c77 100644 --- a/vtkm/io/testing/CMakeLists.txt +++ b/vtkm/io/testing/CMakeLists.txt @@ -10,8 +10,19 @@ set(unit_tests UnitTestBOVDataSetReader.cxx + UnitTestPixelTypes.cxx UnitTestVTKDataSetReader.cxx UnitTestVTKDataSetWriter.cxx - ) +) -vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS) +vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS LIBRARIES vtkm_lodepng) + +if(NOT VTKm_ENABLE_RENDERING) + return() +endif() + +set(image_unit_tests + UnitTestImageWriter.cxx +) + +vtkm_unit_tests(NAME UnitTests_vtkm_io_image_testing SOURCES ${image_unit_tests} ALL_BACKENDS LIBRARIES vtkm_rendering vtkm_lodepng) diff --git a/vtkm/io/testing/UnitTestImageWriter.cxx b/vtkm/io/testing/UnitTestImageWriter.cxx new file mode 100644 index 000000000..dbca2f9a6 --- /dev/null +++ b/vtkm/io/testing/UnitTestImageWriter.cxx @@ -0,0 +1,166 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +using namespace vtkm::io; +using namespace vtkm::rendering; + +template +void TestFilledImage(vtkm::cont::DataSet& dataSet, + const std::string& fieldName, + const vtkm::rendering::Canvas& canvas) +{ + VTKM_TEST_ASSERT(dataSet.HasPointField(fieldName), "Point Field Not Found: " + fieldName); + + auto pointField = dataSet.GetPointField(fieldName); + VTKM_TEST_ASSERT(pointField.GetNumberOfValues() == canvas.GetWidth() * canvas.GetHeight(), + "wrong image dimensions"); + VTKM_TEST_ASSERT(pointField.GetData().template IsType>(), + "wrong ArrayHandle type"); + auto pixelPortal = + pointField.GetData().template Cast>().ReadPortal(); + + auto colorPortal = canvas.GetColorBuffer().ReadPortal(); + for (vtkm::Id y = 0; y < canvas.GetHeight(); y++) + { + std::ostringstream row; + row << "["; + for (vtkm::Id x = 0; x < canvas.GetWidth(); x++) + { + auto tuple = colorPortal.Get(y * canvas.GetWidth() + x); + auto pixelVec = PixelType(pixelPortal.Get(y * canvas.GetWidth() + x)); + std::ostringstream data; + data << pixelVec << ":" << PixelType(tuple) << std::endl; + VTKM_TEST_ASSERT(pixelVec == PixelType(tuple), + "Stored pixel did not match canvas value" + data.str()); + row << pixelVec << ","; + } + row << "]"; + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Row[" << y << "]" << row.str()); + } +} + +template +void TestCreateImageDataSet(BaseImageReader& reader, const vtkm::rendering::Canvas& canvas) +{ + auto dataSet = reader.CreateImageDataSet(canvas); + TestFilledImage(dataSet, reader.GetPointFieldName(), canvas); +} + +template +void TestReadAndWritePNG(const vtkm::rendering::Canvas& canvas, std::string image) +{ + auto pngReader = PNGReader(); + auto pngWriter = PNGWriter(); + vtkm::cont::DataSet dataSet; + bool throws = false; + try + { + pngWriter.WriteToFile(image, dataSet); + } + catch (const vtkm::cont::ErrorBadValue&) + { + throws = true; + } + VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data"); + + dataSet = pngReader.CreateImageDataSet(canvas); + pngWriter.WriteToFile(image, dataSet); + dataSet = pngReader.ReadFromFile(image); + pngWriter.WriteToFile(image, dataSet); + dataSet = pngReader.ReadFromFile(image); + TestFilledImage(dataSet, pngReader.GetPointFieldName(), canvas); +} + +template +void TestReadAndWritePNM(const vtkm::rendering::Canvas& canvas) +{ + using PixelType = RGBPixel; + PNMWriter ppmWriter((1 << BitDepth) - 1); + PNMReader ppmReader((1 << BitDepth) - 1); + vtkm::cont::DataSet dataSet; + bool throws = false; + try + { + ppmWriter.WriteToFile("ppmTestFile" + std::to_string(BitDepth) + "bit.ppm", dataSet); + } + catch (const vtkm::cont::ErrorBadValue&) + { + throws = true; + } + VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data"); + + dataSet = ppmReader.CreateImageDataSet(canvas); + ppmWriter.WriteToFile("ppmTestFile.ppm", dataSet); + dataSet = ppmReader.ReadFromFile("ppmTestFile.ppm"); + ppmWriter.WriteToFile("ppmTestFile2.ppm", dataSet); + dataSet = ppmReader.ReadFromFile("ppmTestFile2.ppm"); + TestFilledImage(dataSet, ppmReader.GetPointFieldName(), canvas); +} + + +void TestBaseImageMethods(const vtkm::rendering::Canvas& canvas) +{ + auto reader = PNGReader(); + TestCreateImageDataSet(reader, canvas); + TestCreateImageDataSet(reader, canvas); + TestCreateImageDataSet(reader, canvas); + TestCreateImageDataSet(reader, canvas); +} + +void TestPNMImage(const vtkm::rendering::Canvas& canvas) +{ + TestReadAndWritePNM<8>(canvas); + TestReadAndWritePNM<16>(canvas); +} + +void TestPNGImage(const vtkm::rendering::Canvas& canvas) +{ + TestReadAndWritePNG(canvas, "pngRGB8Test.png"); + TestReadAndWritePNG(canvas, "pngRGB16Test.png"); + TestReadAndWritePNG(canvas, "pngGrey8Test.png"); + TestReadAndWritePNG(canvas, "pngGrey16Test.png"); +} + +void TestImage() +{ + vtkm::rendering::Canvas canvas(16, 16); + canvas.SetBackgroundColor(vtkm::rendering::Color::red); + canvas.Initialize(); + canvas.Activate(); + canvas.Clear(); + // Line from top left to bottom right, ensures correct transposedness + canvas.AddLine(-0.9, 0.9, 0.9, -0.9, 2.0f, vtkm::rendering::Color::black); + vtkm::Bounds colorBarBounds(-0.8, -0.6, -0.8, 0.8, 0, 0); + canvas.AddColorBar(colorBarBounds, vtkm::cont::ColorTable("inferno"), false); + canvas.BlendBackground(); + canvas.SaveAs("baseline.ppm"); + + TestBaseImageMethods(canvas); + TestPNMImage(canvas); + TestPNGImage(canvas); +} +} + +int UnitTestImageWriter(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(TestImage, argc, argv); +} diff --git a/vtkm/io/testing/UnitTestPixelTypes.cxx b/vtkm/io/testing/UnitTestPixelTypes.cxx new file mode 100644 index 000000000..f734cb65f --- /dev/null +++ b/vtkm/io/testing/UnitTestPixelTypes.cxx @@ -0,0 +1,176 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include +#include + +#include + +using namespace vtkm::io; + +template +void TestPixelTypeOperations(const vtkm::UInt16& numPixels = 10) +{ + using ValType = typename PixelType::ComponentType; + const ValType numBytes = static_cast(PixelType::NUM_BYTES); + const ValType numChannels = static_cast(PixelType::NUM_CHANNELS); + + // Fill in the imageData through FillPixelData + std::vector imageData(numPixels * numBytes * numChannels); + std::vector pixelVector(numPixels); + for (ValType i = 0; i < numPixels; i++) + { + ValType pixelVal = 0; + for (ValType j = 0, shift = numBytes - 1; j < numBytes; shift--, j++) + { + pixelVal += (i + j) << (shift * 8); + } + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "pixelVal[" << i << "] = " << pixelVal); + + PixelType pixel(pixelVal); + pixelVector[i] = pixel; + pixel.FillImageAtIndexWithPixel(imageData.data(), i); + } + + // Test that the imageData values were set correctly + VTKM_TEST_ASSERT(static_cast(imageData.size()) == numPixels * numChannels * numBytes, + "Wrong number of elements"); + for (ValType j = 0; j < numBytes; j++) + { + for (ValType i = 0; i < numPixels; i++) + { + for (ValType k = numChannels * i; k < numChannels * i + numChannels; k++) + { + VTKM_TEST_ASSERT(imageData[k * numBytes + j] == i + j, + "Wrong value at index[" + std::to_string(k * numBytes + j) + "]: " + + std::to_string(imageData[k * numBytes + j]) + " != " + + std::to_string(i + j)); + } + } + } + + // Test that a pixel can be retreived from the filled out data vector + for (vtkm::Id i = 0; i < numPixels; i++) + { + VTKM_TEST_ASSERT(pixelVector[static_cast::size_type>(i)] == + PixelType(imageData.data(), i), + "Incorrect pixel value"); + } +} + +void TestDifferentPixelTypes() +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing 8 bit RGB"); + TestPixelTypeOperations(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing 8 bit Grey"); + TestPixelTypeOperations(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing 16 bit RGB"); + TestPixelTypeOperations(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing 16 bit Grey"); + TestPixelTypeOperations(); +} + +void TestGreyPixelConstructors() +{ + std::vector initData{ 1, 2 }; + + auto pixel_1 = GreyPixel_8(1); + auto pixel_2 = GreyPixel_8(1); + auto pixel_3 = GreyPixel_8(2); + auto pixel_4 = GreyPixel_8(initData.data(), 0); + auto pixel_5 = GreyPixel_8(initData.data(), 1); + auto pixel_6 = GreyPixel_16(initData.data(), 0); + + float color = 10.0f / GreyPixel_16::MAX_COLOR_VALUE; + auto pixel_7 = GreyPixel_16({ color, color, color, 5 }); + + VTKM_TEST_ASSERT(vtkm::UInt16(1) == pixel_1[0], "Type mis-match"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(0) == pixel_1.Diff(pixel_2), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(1) == pixel_1.Diff(pixel_3), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::Vec4f_32(1.0f / 255, 1.0f / 255, 1.0f / 255, 1) == pixel_1.ToVec4f(), + "Incorrect Conversion"); + VTKM_TEST_ASSERT(vtkm::Vec(1) == pixel_4, "Bad 1st value 8 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec(2) == pixel_5, "Bad 2nd value 8 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec(258) == pixel_6, "Bad 16 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec4f_32(258.0f / 65535, 258.0f / 65535, 258.0f / 65535, 1) == + pixel_6.ToVec4f(), + "Incorrect Conversion"); + VTKM_TEST_ASSERT(vtkm::Vec(10) == pixel_7, "Bad Vec4f_32 construction"); + + VTKM_TEST_ASSERT(GreyPixel<16>::BIT_DEPTH == 16, "Bad BitDepth"); + VTKM_TEST_ASSERT(GreyPixel<16>::NUM_BYTES == 2, "Bad NumBytes"); + VTKM_TEST_ASSERT(GreyPixel<16>::MAX_COLOR_VALUE == 65535, "Bad NumBytes"); + VTKM_TEST_ASSERT(GreyPixel<16>::NUM_CHANNELS == 1, "Bad NumChannels"); + VTKM_TEST_ASSERT(GreyPixel<16>::BYTES_PER_PIXEL == 2, "Wrong Pixel Byte distance"); + + // Shouldn't compile + // auto pixel_4 = RGBPixel_8(1, 1, 1); + // pixel_1.Diff(pixel_4); +} + +void TestRGBPixelConstructors() +{ + std::vector initData{ 1, 2, 3, 4, 5, 6 }; + + auto pixel_1 = RGBPixel_8(1, 1, 1); + auto pixel_2 = RGBPixel_8(1, 1, 1); + auto pixel_3 = RGBPixel_8(1); + auto pixel_4 = RGBPixel_8(2, 2, 2); + auto pixel_5 = RGBPixel_8(initData.data(), 0); + auto pixel_6 = RGBPixel_8(initData.data(), 1); + auto pixel_7 = RGBPixel_16(initData.data(), 0); + + float color = 10.0f / RGBPixel_16::MAX_COLOR_VALUE; + auto pixel_8 = RGBPixel_16({ color, color, color, 5 }); + + VTKM_TEST_ASSERT(vtkm::Vec3ui_8(1, 1, 1) == pixel_1, "Type mis-match"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(0) == pixel_1.Diff(pixel_2), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(0) == pixel_1.Diff(pixel_3), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(3) == pixel_1.Diff(pixel_4), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::Vec4f_32(1.0f / 255, 1.0f / 255, 1.0f / 255, 1) == pixel_1.ToVec4f(), + "Incorrect Conversion"); + VTKM_TEST_ASSERT(vtkm::Vec3ui_8(1, 2, 3) == pixel_5, "Bad 1st value 8 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec3ui_8(4, 5, 6) == pixel_6, "Bad 2nd value 8 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec3ui_16(258, 772, 1286) == pixel_7, "Bad 16 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec4f_32(258.0f / 65535, 772.0f / 65535, 1286.0f / 65535, 1) == + pixel_7.ToVec4f(), + "Incorrect Conversion"); + VTKM_TEST_ASSERT(vtkm::Vec(10, 10, 10) == pixel_8, "Bad Vec4f_32 construction"); + + VTKM_TEST_ASSERT(RGBPixel<16>::BIT_DEPTH == 16, "Bad BitDepth"); + VTKM_TEST_ASSERT(RGBPixel<16>::NUM_BYTES == 2, "Bad NumBytes"); + VTKM_TEST_ASSERT(RGBPixel<16>::MAX_COLOR_VALUE == 65535, "Bad NumBytes"); + VTKM_TEST_ASSERT(RGBPixel<16>::NUM_CHANNELS == 3, "Bad NumChannels"); + VTKM_TEST_ASSERT(RGBPixel<16>::BYTES_PER_PIXEL == 6, "Wrong Pixel Byte distance"); + + // Shouldn't compile + // auto pixel_8 = GreyPixel_8(1); + // pixel_1.Diff(pixel_8); +} + +void TestPixelTypes() +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing RGBPixel"); + TestRGBPixelConstructors(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing GreyPixel"); + TestGreyPixelConstructors(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing Pixel Types"); + TestDifferentPixelTypes(); +} + +int UnitTestPixelTypes(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(TestPixelTypes, argc, argv); +} diff --git a/vtkm/io/writer/CMakeLists.txt b/vtkm/io/writer/CMakeLists.txt index 1bebb0959..9d88e46f2 100644 --- a/vtkm/io/writer/CMakeLists.txt +++ b/vtkm/io/writer/CMakeLists.txt @@ -10,6 +10,8 @@ set(headers VTKDataSetWriter.h - ) +) -vtkm_declare_headers(${headers}) +vtkm_declare_headers( + ${headers} +) From 6fba7347a91c9737d5370962c0c0f3b8db22a796 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 14 May 2020 14:29:34 -0400 Subject: [PATCH 12/27] Mark pixel destructors as virtual to fix build error. --- vtkm/io/PixelTypes.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vtkm/io/PixelTypes.h b/vtkm/io/PixelTypes.h index 4b7ee0ab5..8a86d3c8d 100644 --- a/vtkm/io/PixelTypes.h +++ b/vtkm/io/PixelTypes.h @@ -111,6 +111,8 @@ public: /// void FillImageAtIndexWithPixel(unsigned char* imageData, const vtkm::Id index); + virtual ~BasePixel() = default; + protected: /// Takes an input imageData pointer and an index to a location in that dataset /// and fills in this->Components correctly using the provided BIT_DEPTH and @@ -144,6 +146,8 @@ public: ComponentType Diff(const Superclass& pixel) const override; vtkm::Vec4f_32 ToVec4f() const override; + virtual ~RGBPixel() = default; + protected: void print(std::ostream& os) const override { @@ -179,6 +183,8 @@ public: ComponentType Diff(const Superclass& pixel) const override; vtkm::Vec4f_32 ToVec4f() const override; + virtual ~GreyPixel() = default; + protected: void print(std::ostream& os) const override { os << "(" << (int)this->Components[0] << ")"; } }; From a55596b38c4e003a58589b2b042f96e94d77b618 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 14 May 2020 16:10:33 -0400 Subject: [PATCH 13/27] Reorder member functions. --- vtkm/io/PixelTypes.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vtkm/io/PixelTypes.h b/vtkm/io/PixelTypes.h index 8a86d3c8d..c6a7b9034 100644 --- a/vtkm/io/PixelTypes.h +++ b/vtkm/io/PixelTypes.h @@ -88,6 +88,8 @@ public: ConstructPixelFromImage(imageData, index); } + virtual ~BasePixel() = default; + /// Calculates this difference between two pixels as a single value. /// virtual ComponentType Diff(const BaseType& pixel) const = 0; @@ -111,8 +113,6 @@ public: /// void FillImageAtIndexWithPixel(unsigned char* imageData, const vtkm::Id index); - virtual ~BasePixel() = default; - protected: /// Takes an input imageData pointer and an index to a location in that dataset /// and fills in this->Components correctly using the provided BIT_DEPTH and @@ -143,11 +143,11 @@ public: { } + virtual ~RGBPixel() = default; + ComponentType Diff(const Superclass& pixel) const override; vtkm::Vec4f_32 ToVec4f() const override; - virtual ~RGBPixel() = default; - protected: void print(std::ostream& os) const override { @@ -180,11 +180,11 @@ public: { } + virtual ~GreyPixel() = default; + ComponentType Diff(const Superclass& pixel) const override; vtkm::Vec4f_32 ToVec4f() const override; - virtual ~GreyPixel() = default; - protected: void print(std::ostream& os) const override { os << "(" << (int)this->Components[0] << ")"; } }; From 9991179e6dcd27786438198894ac3b7c64259fcc Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 14 May 2020 18:10:32 -0400 Subject: [PATCH 14/27] Do not use auto return type; it's not supported until C++14. --- vtkm/cont/CellSetExplicit.h | 4 +++- vtkm/cont/CellSetExplicit.hxx | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/vtkm/cont/CellSetExplicit.h b/vtkm/cont/CellSetExplicit.h index 7877a7726..261ea25bf 100644 --- a/vtkm/cont/CellSetExplicit.h +++ b/vtkm/cont/CellSetExplicit.h @@ -186,7 +186,9 @@ public: VTKM_CONT vtkm::IdComponent GetNumberOfPointsInCell(vtkm::Id cellid) const override; VTKM_CONT void GetCellPointIds(vtkm::Id id, vtkm::Id* ptids) const override; - VTKM_CONT auto ShapesReadPortal() const; + VTKM_CONT typename vtkm::cont::ArrayHandle::ReadPortalType + ShapesReadPortal() const; + VTKM_CONT vtkm::UInt8 GetCellShape(vtkm::Id cellid) const override; template diff --git a/vtkm/cont/CellSetExplicit.hxx b/vtkm/cont/CellSetExplicit.hxx index 7ff00e367..da7ec8e99 100644 --- a/vtkm/cont/CellSetExplicit.hxx +++ b/vtkm/cont/CellSetExplicit.hxx @@ -176,7 +176,8 @@ vtkm::IdComponent CellSetExplicit template VTKM_CONT -auto CellSetExplicit::ShapesReadPortal() const +typename vtkm::cont::ArrayHandle::ReadPortalType +CellSetExplicit::ShapesReadPortal() const { return this->Data->CellPointIds.Shapes.ReadPortal(); } From 9aa350ee3df3343b1888fbc4ee90f281813b86e3 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 14 May 2020 18:13:03 -0400 Subject: [PATCH 15/27] Fix bad whitespace. --- vtkm/cont/CellSetExplicit.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtkm/cont/CellSetExplicit.hxx b/vtkm/cont/CellSetExplicit.hxx index da7ec8e99..815df2f8d 100644 --- a/vtkm/cont/CellSetExplicit.hxx +++ b/vtkm/cont/CellSetExplicit.hxx @@ -176,7 +176,7 @@ vtkm::IdComponent CellSetExplicit template VTKM_CONT -typename vtkm::cont::ArrayHandle::ReadPortalType +typename vtkm::cont::ArrayHandle::ReadPortalType CellSetExplicit::ShapesReadPortal() const { return this->Data->CellPointIds.Shapes.ReadPortal(); From f9d9b536d44e36b8f3d28cac88bc4859bd2b027d Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Thu, 14 May 2020 17:16:19 -0600 Subject: [PATCH 16/27] Fix conversion warnings in benchmarks and examples These are caused when using 32-bit Id's. Although there are some benchmarks that test for this, some code in benchmarking and examples is not covered by them. --- benchmarking/BenchmarkDeviceAdapter.cxx | 34 +++++++++---------- .../contour_tree_augmented/ContourTreeApp.cxx | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/benchmarking/BenchmarkDeviceAdapter.cxx b/benchmarking/BenchmarkDeviceAdapter.cxx index 637a91982..453ce57ff 100644 --- a/benchmarking/BenchmarkDeviceAdapter.cxx +++ b/benchmarking/BenchmarkDeviceAdapter.cxx @@ -404,7 +404,7 @@ template void BenchCopy(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -580,7 +580,7 @@ template void BenchFillArrayHandle(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -610,7 +610,7 @@ VTKM_BENCHMARK_TEMPLATES_OPTS(BenchFillArrayHandle, void BenchFillBitFieldBool(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numBits = numBytes * CHAR_BIT; const bool value = state.range(1) != 0; @@ -640,7 +640,7 @@ template void BenchFillBitFieldMask(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numBits = numBytes * CHAR_BIT; const WordType mask = static_cast(0x1); @@ -717,7 +717,7 @@ template void BenchReduce(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -752,10 +752,10 @@ void BenchReduceByKey(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); - const vtkm::Id percentKeys = state.range(1); + const vtkm::Id percentKeys = static_cast(state.range(1)); const vtkm::Id numKeys = std::max((numValues * percentKeys) / 100, vtkm::Id{ 1 }); { @@ -807,7 +807,7 @@ template void BenchScanExclusive(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -841,7 +841,7 @@ template void BenchScanExtended(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -875,7 +875,7 @@ template void BenchScanInclusive(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -909,7 +909,7 @@ template void BenchSort(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -950,7 +950,7 @@ void BenchSortByKey(benchmark::State& state) const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); - const vtkm::Id percentKeys = state.range(1); + const vtkm::Id percentKeys = static_cast(state.range(1)); const vtkm::Id numKeys = std::max((numValues * percentKeys) / 100, vtkm::Id{ 1 }); { @@ -1005,7 +1005,7 @@ template void BenchStableSortIndices(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -1042,10 +1042,10 @@ template void BenchStableSortIndicesUnique(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); - const vtkm::Id percentUnique = state.range(1); + const vtkm::Id percentUnique = static_cast(state.range(1)); const vtkm::Id numUnique = std::max((numValues * percentUnique) / 100, vtkm::Id{ 1 }); { @@ -1105,10 +1105,10 @@ template void BenchUnique(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); - const vtkm::Id percentUnique = state.range(1); + const vtkm::Id percentUnique = static_cast(state.range(1)); const vtkm::Id numUnique = std::max((numValues * percentUnique) / 100, vtkm::Id{ 1 }); { diff --git a/examples/contour_tree_augmented/ContourTreeApp.cxx b/examples/contour_tree_augmented/ContourTreeApp.cxx index a0037977e..056625c1f 100644 --- a/examples/contour_tree_augmented/ContourTreeApp.cxx +++ b/examples/contour_tree_augmented/ContourTreeApp.cxx @@ -130,7 +130,7 @@ public: } else { - return (it - this->mCLOptions.begin()); + return static_cast(it - this->mCLOptions.begin()); } } From 2858186dddf8fe8d9ccbd66a58b6c8d3ded2af10 Mon Sep 17 00:00:00 2001 From: dpugmire Date: Mon, 18 May 2020 16:42:37 -0400 Subject: [PATCH 17/27] Print statement was wrong for openMP --- examples/multi_backend/MultiDeviceGradient.hxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/multi_backend/MultiDeviceGradient.hxx b/examples/multi_backend/MultiDeviceGradient.hxx index 12d0daecf..39a43b624 100644 --- a/examples/multi_backend/MultiDeviceGradient.hxx +++ b/examples/multi_backend/MultiDeviceGradient.hxx @@ -84,7 +84,8 @@ void process_partition_openMP(RuntimeTaskQueue& queue) //Step 4. Notify the queue that we finished processing this task queue.completedTask(); - std::cout << "finished a partition on tbb (" << std::this_thread::get_id() << ")" << std::endl; + std::cout << "finished a partition on openMP (" << std::this_thread::get_id() << ")" + << std::endl; } } From bda8a1580b8a16f04cfc2bc4fbac81f10b446482 Mon Sep 17 00:00:00 2001 From: dpugmire Date: Mon, 18 May 2020 16:44:12 -0400 Subject: [PATCH 18/27] change comments as well. --- examples/multi_backend/MultiDeviceGradient.hxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/multi_backend/MultiDeviceGradient.hxx b/examples/multi_backend/MultiDeviceGradient.hxx index 39a43b624..36a592f3d 100644 --- a/examples/multi_backend/MultiDeviceGradient.hxx +++ b/examples/multi_backend/MultiDeviceGradient.hxx @@ -64,17 +64,17 @@ void process_partition_openMP(RuntimeTaskQueue& queue) { //Step 1. Set the device adapter to this thread to TBB. //This makes sure that any vtkm::filters used by our - //task operate only on TBB. The "global" thread tracker + //task operate only on openMP. The "global" thread tracker //is actually thread-local, so we can use that. // vtkm::cont::GetRuntimeDeviceTracker().ForceDevice(vtkm::cont::DeviceAdapterTagOpenMP{}); while (queue.hasTasks()) { - //Step 2. Get the task to run on TBB + //Step 2. Get the task to run on openMP auto task = queue.pop(); - //Step 3. Run the task on TBB. We check the validity + //Step 3. Run the task on openMP. We check the validity //of the task since we could be given an empty task //when the queue is empty and we are shutting down if (task != nullptr) From af5d363f421124da8b6f025af86a4e6ff64a9f80 Mon Sep 17 00:00:00 2001 From: Robert Maynard Date: Mon, 18 May 2020 17:06:25 -0400 Subject: [PATCH 19/27] Update sccache to latest master which now has full nvcc support --- .gitlab/ci/config/sccache.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/ci/config/sccache.sh b/.gitlab/ci/config/sccache.sh index f05b8e62d..8e6ce6dae 100755 --- a/.gitlab/ci/config/sccache.sh +++ b/.gitlab/ci/config/sccache.sh @@ -2,8 +2,8 @@ set -e -readonly version="nvcc_v3" -readonly sha256sum="d5b56dd9e7d4597f4a47a90d6327e30a259151b59b897607e1804d6d3513f491" +readonly version="nvcc_v4" +readonly sha256sum="260779b4a740fe8373d251d1e318541a98dd5cd2f8051eedd55227a5a852fdf7" readonly filename="sccache-0.2.14-$version-x86_64-unknown-linux-musl" readonly tarball="$filename.tar.gz" From 5d20506a5d3ce54780e812f39e4e8c53aa9265d0 Mon Sep 17 00:00:00 2001 From: Nick Thompson Date: Mon, 18 May 2020 22:25:29 -0400 Subject: [PATCH 20/27] Remove overly verbose log pring. --- vtkm/io/testing/UnitTestImageWriter.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/vtkm/io/testing/UnitTestImageWriter.cxx b/vtkm/io/testing/UnitTestImageWriter.cxx index dbca2f9a6..8be7521aa 100644 --- a/vtkm/io/testing/UnitTestImageWriter.cxx +++ b/vtkm/io/testing/UnitTestImageWriter.cxx @@ -54,7 +54,6 @@ void TestFilledImage(vtkm::cont::DataSet& dataSet, row << pixelVec << ","; } row << "]"; - VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Row[" << y << "]" << row.str()); } } From 72f1846bf19ad32c0a9e345645b162cb34a2aa21 Mon Sep 17 00:00:00 2001 From: dpugmire Date: Tue, 19 May 2020 09:40:44 -0400 Subject: [PATCH 21/27] missed a comment change. --- examples/multi_backend/MultiDeviceGradient.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/multi_backend/MultiDeviceGradient.hxx b/examples/multi_backend/MultiDeviceGradient.hxx index 36a592f3d..8ff601c92 100644 --- a/examples/multi_backend/MultiDeviceGradient.hxx +++ b/examples/multi_backend/MultiDeviceGradient.hxx @@ -62,7 +62,7 @@ void process_partition_tbb(RuntimeTaskQueue& queue) void process_partition_openMP(RuntimeTaskQueue& queue) { - //Step 1. Set the device adapter to this thread to TBB. + //Step 1. Set the device adapter to this thread to openMP. //This makes sure that any vtkm::filters used by our //task operate only on openMP. The "global" thread tracker //is actually thread-local, so we can use that. From 569adda002e04dcc9650fa298a9a126a1ad04417 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 10:37:36 -0600 Subject: [PATCH 22/27] Update changelogs There have been several new features that were merged without appropriate documentation in the changelogs. This adds some new changelogs for some of these new features. --- .../changelog/coordinate-transform-results.md | 13 ++++ docs/changelog/dataset-unique-field-names.md | 17 +++++ .../filter-specifies-own-field-types.md | 8 +++ docs/changelog/flying-edges.md | 15 ++++ docs/changelog/no-cell-op-errors.md | 72 +++++++++++++++++++ .../remove-opengl-rendering-classes.md | 13 ++++ 6 files changed, 138 insertions(+) create mode 100644 docs/changelog/coordinate-transform-results.md create mode 100644 docs/changelog/dataset-unique-field-names.md create mode 100644 docs/changelog/filter-specifies-own-field-types.md create mode 100644 docs/changelog/flying-edges.md create mode 100644 docs/changelog/no-cell-op-errors.md create mode 100644 docs/changelog/remove-opengl-rendering-classes.md diff --git a/docs/changelog/coordinate-transform-results.md b/docs/changelog/coordinate-transform-results.md new file mode 100644 index 000000000..124fb79cc --- /dev/null +++ b/docs/changelog/coordinate-transform-results.md @@ -0,0 +1,13 @@ +# Result DataSet of coordinate transform has its CoordinateSystem changed + +When you run one of the coordinate transform filters, +`CylindricalCoordinateTransform` or `SphericalCoordinateTransform`, the +transform coordiantes are placed as the first `CoordinateSystem` in the +returned `DataSet`. This means that after running this filter, the data +will be moved to this new coordinate space. + +Previously, the result of these filters was just placed in a named `Field` +of the output. This caused some confusion because the filter did not seem +to have any effect (unless you knew to modify the output data). Not using +the result as the coordinate system seems like a dubious use case (and not +hard to work around), so this is much better behavior. diff --git a/docs/changelog/dataset-unique-field-names.md b/docs/changelog/dataset-unique-field-names.md new file mode 100644 index 000000000..90a1f4af7 --- /dev/null +++ b/docs/changelog/dataset-unique-field-names.md @@ -0,0 +1,17 @@ +# DataSet now only allows unique field names + +When you add a `vtkm::cont::Field` to a `vtkm::cont::DataSet`, it now +requires every `Field` to have a unique name. When you attempt to add a +`Field` to a `DataSet` that already has a `Field` of the same name and +association, the old `Field` is removed and replaced with the new `Field`. + +You are allowed, however, to have two `Field`s with the same name but +different associations. For example, you could have a point `Field` named +"normals" and also have a cell `Field` named "normals" in the same +`DataSet`. + +This new behavior matches how VTK's data sets manage fields. + +The old behavior allowed you to add multiple `Field`s with the same name, +but it would be unclear which one you would get if you asked for a `Field` +by name. diff --git a/docs/changelog/filter-specifies-own-field-types.md b/docs/changelog/filter-specifies-own-field-types.md new file mode 100644 index 000000000..6dc50fab1 --- /dev/null +++ b/docs/changelog/filter-specifies-own-field-types.md @@ -0,0 +1,8 @@ +# Filters specify their own field types + +Previously, the policy specified which field types the filter should +operate on. The filter could remove some types, but it was not able to +add any types. + +This is backward. Instead, the filter should specify what types its +supports and the policy may cull out some of those. diff --git a/docs/changelog/flying-edges.md b/docs/changelog/flying-edges.md new file mode 100644 index 000000000..b174bbb37 --- /dev/null +++ b/docs/changelog/flying-edges.md @@ -0,0 +1,15 @@ +# Flying Edges + +Added the flying edges contouring algorithm to VTK-m. This algorithm only +works on structured grids, but operates much faster than the traditional +Marching Cubes algorithm. + +The speed of VTK-m's flying edges is comprable to VTK's running on the same +CPUs. VTK-m's implementation also works well on CUDA hardware. + +The Flying Edges algorithm was introduced in this paper: + +Schroeder, W.; Maynard, R. & Geveci, B. +"Flying edges: A high-performance scalable isocontouring algorithm." +Large Data Analysis and Visualization (LDAV), 2015. +DOI 10.1109/LDAV.2015.7348069 diff --git a/docs/changelog/no-cell-op-errors.md b/docs/changelog/no-cell-op-errors.md new file mode 100644 index 000000000..a9ed3608b --- /dev/null +++ b/docs/changelog/no-cell-op-errors.md @@ -0,0 +1,72 @@ +# Avoid raising errors when operating on cells + +Cell operations like interpolate and finding parametric coordinates can +fail under certain conditions. The previous behavior was to call +`RaiseError` on the worklet. By design, this would cause the worklet +execution to fail. However, that makes the worklet unstable for a conditin +that might be relatively common in data. For example, you wouldn't want a +large streamline worklet to fail just because one cell was not found +correctly. + +To work around this, many of the cell operations in the execution +environment have been changed to return an error code rather than raise an +error in the worklet. + +## Error Codes + +To support cell operations efficiently returning errors, a new enum named +`vtkm::ErrorCode` is available. This is the current implementation of +`ErrorCode`. + +``` cpp +enum class ErrorCode +{ + Success, + InvalidShapeId, + InvalidNumberOfPoints, + WrongShapeIdForTagType, + InvalidPointId, + InvalidEdgeId, + InvalidFaceId, + SolutionDidNotConverge, + MatrixFactorizationFailed, + DegenerateCellDetected, + MalformedCellDetected, + OperationOnEmptyCell, + CellNotFound, + + UnknownError +}; +``` + +A convenience function named `ErrorString` is provided to make it easy to +convert the `ErrorCode` to a descriptive string that can be placed in an +error. + +## New Calling Specification + +Previously, most execution environment functions took as an argument the +worklet calling the function. This made it possible to call `RaiseError` on +the worklet. The result of the operation was typically returned. For +example, here is how the _old_ version of interpolate was called. + +``` cpp +FieldType interpolatedValue = + vtkm::exec::CellInterpolate(fieldValues, pcoord, shape, worklet); +``` + +The worklet is now no longer passed to the function. It is no longer needed +because an error is never directly raised. Instead, an `ErrorCode` is +returned from the function. Because the `ErrorCode` is returned, the +computed result of the function is returned by passing in a reference to a +variable. This is usually placed as the last argument (where the worklet +used to be). here is the _new_ version of how interpolate is called. + +``` cpp +FieldType interpolatedValue; +vtkm::ErrorCode result = + vtkm::exec::CellInterpolate(fieldValues, pcoord, shape, interpolatedValue); +``` + +The success of the operation can be determined by checking that the +returned `ErrorCode` is equal to `vtkm::ErrorCode::Success`. diff --git a/docs/changelog/remove-opengl-rendering-classes.md b/docs/changelog/remove-opengl-rendering-classes.md new file mode 100644 index 000000000..e49c6c6f6 --- /dev/null +++ b/docs/changelog/remove-opengl-rendering-classes.md @@ -0,0 +1,13 @@ +# Removed OpenGL Rendering Classes + +When the rendering library was first built, OpenGL was used to implement +the components (windows, mappers, annotation, etc.). However, as the native +ray casting became viable, the majority of the work has focused on using +that. Since then, the original OpenGL classes have been largely ignored. + +It has for many months been determined that it is not work attempting to +maintain two different versions of the rendering libraries as features are +added and changed. Thus, the OpenGL classes have fallen out of date and did +not actually work. + +These classes have finally been officially removed. From 0612be9c5bbbbdc49854ae2c1438d0d88c6de525 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 12:27:01 -0600 Subject: [PATCH 23/27] Move VTKDataSetReaderBase code to vtkm_io Most of this code is not templated methods. Rather, it implements over several types to call templated functions, which creates quite a bit of code. Rather than have all code using a reader recompile the code, just compile it once and put it in a library. --- benchmarking/CMakeLists.txt | 2 +- examples/mesh_quality/CMakeLists.txt | 2 +- examples/particle_advection/CMakeLists.txt | 2 +- examples/redistribute_points/CMakeLists.txt | 2 +- examples/temporal_advection/CMakeLists.txt | 2 +- vtkm/filter/testing/CMakeLists.txt | 2 +- vtkm/io/CMakeLists.txt | 1 + vtkm/io/VTKDataSetReaderBase.cxx | 826 +++++++++++++++++++ vtkm/io/VTKDataSetReaderBase.h | 869 ++------------------ vtkm/io/VTKPolyDataReader.h | 1 + vtkm/io/testing/CMakeLists.txt | 16 +- 11 files changed, 912 insertions(+), 813 deletions(-) create mode 100644 vtkm/io/VTKDataSetReaderBase.cxx diff --git a/benchmarking/CMakeLists.txt b/benchmarking/CMakeLists.txt index a504d395e..bbfbd8c2a 100644 --- a/benchmarking/CMakeLists.txt +++ b/benchmarking/CMakeLists.txt @@ -52,7 +52,7 @@ set(VTKm_BENCHS_RANGE_UPPER_BOUNDARY 134217728 CACHE STRING "Biggest sample for mark_as_advanced(VTKm_BENCHS_RANGE_LOWER_BOUNDARY VTKm_BENCHS_RANGE_UPPER_BOUNDARY) foreach (benchmark ${benchmarks}) - add_benchmark(NAME ${benchmark} FILE ${benchmark}.cxx LIBS vtkm_source vtkm_filter) + add_benchmark(NAME ${benchmark} FILE ${benchmark}.cxx LIBS vtkm_source vtkm_filter vtkm_io) endforeach () target_compile_definitions(BenchmarkDeviceAdapter PUBLIC VTKm_BENCHS_RANGE_LOWER_BOUNDARY=${VTKm_BENCHS_RANGE_LOWER_BOUNDARY}) diff --git a/examples/mesh_quality/CMakeLists.txt b/examples/mesh_quality/CMakeLists.txt index 3987f3da2..6308b37b6 100644 --- a/examples/mesh_quality/CMakeLists.txt +++ b/examples/mesh_quality/CMakeLists.txt @@ -26,7 +26,7 @@ project(MeshQuality CXX) find_package(VTKm REQUIRED QUIET) add_executable(MeshQuality MeshQuality.cxx) -target_link_libraries(MeshQuality PRIVATE vtkm_filter) +target_link_libraries(MeshQuality PRIVATE vtkm_filter vtkm_io) if(TARGET vtkm::tbb) target_compile_definitions(MeshQuality PRIVATE BUILDING_TBB_VERSION) diff --git a/examples/particle_advection/CMakeLists.txt b/examples/particle_advection/CMakeLists.txt index 5c0b9a006..7866c56c6 100644 --- a/examples/particle_advection/CMakeLists.txt +++ b/examples/particle_advection/CMakeLists.txt @@ -14,7 +14,7 @@ project(ParticleAdvection CXX) find_package(VTKm REQUIRED QUIET) add_executable(Particle_Advection ParticleAdvection.cxx) -target_link_libraries(Particle_Advection PRIVATE vtkm_filter) +target_link_libraries(Particle_Advection PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Particle_Advection DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES ParticleAdvection.cxx) diff --git a/examples/redistribute_points/CMakeLists.txt b/examples/redistribute_points/CMakeLists.txt index 8a2ff67e9..59948d79f 100644 --- a/examples/redistribute_points/CMakeLists.txt +++ b/examples/redistribute_points/CMakeLists.txt @@ -13,7 +13,7 @@ project(RedistributePoints CXX) #Find the VTK-m package find_package(VTKm REQUIRED QUIET) add_executable(RedistributePoints RedistributePoints.cxx RedistributePoints.h) -target_link_libraries(RedistributePoints PRIVATE vtkm_filter) +target_link_libraries(RedistributePoints PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(RedistributePoints DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES RedistributePoints.cxx) diff --git a/examples/temporal_advection/CMakeLists.txt b/examples/temporal_advection/CMakeLists.txt index 027a24f7a..eeed9ff44 100644 --- a/examples/temporal_advection/CMakeLists.txt +++ b/examples/temporal_advection/CMakeLists.txt @@ -19,4 +19,4 @@ add_executable(Temporal_Advection TemporalAdvection.cxx) vtkm_add_target_information(Temporal_Advection DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES TemporalAdvection.cxx) -target_link_libraries(Temporal_Advection PRIVATE vtkm_filter) +target_link_libraries(Temporal_Advection PRIVATE vtkm_filter vtkm_io) diff --git a/vtkm/filter/testing/CMakeLists.txt b/vtkm/filter/testing/CMakeLists.txt index 72a572eeb..ab23e36be 100644 --- a/vtkm/filter/testing/CMakeLists.txt +++ b/vtkm/filter/testing/CMakeLists.txt @@ -69,7 +69,7 @@ set(unit_tests vtkm_unit_tests( SOURCES ${unit_tests} - LIBRARIES vtkm_filter vtkm_source + LIBRARIES vtkm_filter vtkm_source vtkm_io ALL_BACKENDS USE_VTKM_JOB_POOL ) diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index 7d8bc7660..32d9d9ae5 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -35,6 +35,7 @@ set(template_sources set(sources DecodePNG.cxx EncodePNG.cxx + VTKDataSetReaderBase.cxx ) vtkm_declare_headers( diff --git a/vtkm/io/VTKDataSetReaderBase.cxx b/vtkm/io/VTKDataSetReaderBase.cxx new file mode 100644 index 000000000..dcbace6e5 --- /dev/null +++ b/vtkm/io/VTKDataSetReaderBase.cxx @@ -0,0 +1,826 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace +{ + +inline void PrintVTKDataFileSummary(const vtkm::io::internal::VTKDataSetFile& df, std::ostream& out) +{ + out << "\tFile: " << df.FileName << std::endl; + out << "\tVersion: " << df.Version[0] << "." << df.Version[0] << std::endl; + out << "\tTitle: " << df.Title << std::endl; + out << "\tFormat: " << (df.IsBinary ? "BINARY" : "ASCII") << std::endl; + out << "\tDataSet type: " << vtkm::io::internal::DataSetStructureString(df.Structure) + << std::endl; +} + +// Since Fields and DataSets store data in the default VariantArrayHandle, convert +// the data to the closest type supported by default. The following will +// need to be updated if VariantArrayHandle or TypeListCommon changes. +template +struct ClosestCommonType +{ + using Type = T; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int64; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int64; +}; + +template +struct ClosestFloat +{ + using Type = T; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; + +template +vtkm::cont::VariantArrayHandle CreateVariantArrayHandle(const std::vector& vec) +{ + switch (vtkm::VecTraits::NUM_COMPONENTS) + { + case 1: + { + using CommonType = typename ClosestCommonType::Type; + constexpr bool not_same = !std::is_same::value; + if (not_same) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Type " << vtkm::io::internal::DataTypeName::Name() + << " is currently unsupported. Converting to " + << vtkm::io::internal::DataTypeName::Name() + << "."); + } + + vtkm::cont::ArrayHandle output; + output.Allocate(static_cast(vec.size())); + auto portal = output.WritePortal(); + for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) + { + portal.Set(i, static_cast(vec[static_cast(i)])); + } + + return vtkm::cont::VariantArrayHandle(output); + } + case 2: + case 3: + case 9: + { + constexpr auto numComps = vtkm::VecTraits::NUM_COMPONENTS; + + using InComponentType = typename vtkm::VecTraits::ComponentType; + using OutComponentType = typename ClosestFloat::Type; + using CommonType = vtkm::Vec; + constexpr bool not_same = !std::is_same::value; + if (not_same) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Type " << vtkm::io::internal::DataTypeName::Name() << "[" + << vtkm::VecTraits::GetNumberOfComponents(T()) + << "] " + << "is currently unsupported. Converting to " + << vtkm::io::internal::DataTypeName::Name() + << "[" + << numComps + << "]."); + } + + vtkm::cont::ArrayHandle output; + output.Allocate(static_cast(vec.size())); + auto portal = output.WritePortal(); + for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) + { + CommonType outval = CommonType(); + for (vtkm::IdComponent j = 0; j < numComps; ++j) + { + outval[j] = static_cast( + vtkm::VecTraits::GetComponent(vec[static_cast(i)], j)); + } + portal.Set(i, outval); + } + + return vtkm::cont::VariantArrayHandle(output); + } + default: + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Only 1, 2, 3, or 9 components supported. Skipping."); + return vtkm::cont::VariantArrayHandle(vtkm::cont::ArrayHandle()); + } + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +VTKDataSetReaderBase::VTKDataSetReaderBase(const char* fileName) + : DataFile(new internal::VTKDataSetFile) + , DataSet() + , Loaded(false) +{ + this->DataFile->FileName = fileName; +} + +VTKDataSetReaderBase::VTKDataSetReaderBase(const std::string& fileName) + : DataFile(new internal::VTKDataSetFile) + , DataSet() + , Loaded(false) +{ + this->DataFile->FileName = fileName; +} + +VTKDataSetReaderBase::~VTKDataSetReaderBase() +{ +} + +const vtkm::cont::DataSet& VTKDataSetReaderBase::ReadDataSet() +{ + if (!this->Loaded) + { + try + { + this->OpenFile(); + this->ReadHeader(); + this->Read(); + this->CloseFile(); + this->Loaded = true; + } + catch (std::ifstream::failure& e) + { + std::string message("IO Error: "); + throw vtkm::io::ErrorIO(message + e.what()); + } + } + + return this->DataSet; +} + +void VTKDataSetReaderBase::PrintSummary(std::ostream& out) const +{ + out << "VTKDataSetReader" << std::endl; + PrintVTKDataFileSummary(*this->DataFile.get(), out); + this->DataSet.PrintSummary(out); +} + +void VTKDataSetReaderBase::ReadPoints() +{ + std::string dataType; + std::size_t numPoints; + this->DataFile->Stream >> numPoints >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle points = + this->DoReadArrayVariant(vtkm::cont::Field::Association::POINTS, dataType, numPoints, 3); + + this->DataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", points)); +} + +void VTKDataSetReaderBase::ReadCells(vtkm::cont::ArrayHandle& connectivity, + vtkm::cont::ArrayHandle& numIndices) +{ + vtkm::Id numCells, numInts; + this->DataFile->Stream >> numCells >> numInts >> std::ws; + + connectivity.Allocate(numInts - numCells); + numIndices.Allocate(numCells); + + std::vector buffer(static_cast(numInts)); + this->ReadArray(buffer); + + vtkm::Int32* buffp = &buffer[0]; + auto connectivityPortal = connectivity.WritePortal(); + auto numIndicesPortal = numIndices.WritePortal(); + for (vtkm::Id i = 0, connInd = 0; i < numCells; ++i) + { + vtkm::IdComponent numInds = static_cast(*buffp++); + numIndicesPortal.Set(i, numInds); + for (vtkm::IdComponent j = 0; j < numInds; ++j, ++connInd) + { + connectivityPortal.Set(connInd, static_cast(*buffp++)); + } + } +} + +void VTKDataSetReaderBase::ReadShapes(vtkm::cont::ArrayHandle& shapes) +{ + std::string tag; + vtkm::Id numCells; + this->DataFile->Stream >> tag >> numCells >> std::ws; + internal::parseAssert(tag == "CELL_TYPES"); + + shapes.Allocate(numCells); + std::vector buffer(static_cast(numCells)); + this->ReadArray(buffer); + + vtkm::Int32* buffp = &buffer[0]; + auto shapesPortal = shapes.WritePortal(); + for (vtkm::Id i = 0; i < numCells; ++i) + { + shapesPortal.Set(i, static_cast(*buffp++)); + } +} + +void VTKDataSetReaderBase::ReadAttributes() +{ + if (this->DataFile->Stream.eof()) + { + return; + } + + vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY; + std::size_t size; + + std::string tag; + this->DataFile->Stream >> tag; + while (!this->DataFile->Stream.eof()) + { + if (tag == "POINT_DATA") + { + association = vtkm::cont::Field::Association::POINTS; + } + else if (tag == "CELL_DATA") + { + association = vtkm::cont::Field::Association::CELL_SET; + } + else + { + internal::parseAssert(false); + } + + this->DataFile->Stream >> size; + while (!this->DataFile->Stream.eof()) + { + this->DataFile->Stream >> tag; + if (tag == "SCALARS") + { + this->ReadScalars(association, size); + } + else if (tag == "COLOR_SCALARS") + { + this->ReadColorScalars(association, size); + } + else if (tag == "LOOKUP_TABLE") + { + this->ReadLookupTable(); + } + else if (tag == "VECTORS" || tag == "NORMALS") + { + this->ReadVectors(association, size); + } + else if (tag == "TEXTURE_COORDINATES") + { + this->ReadTextureCoordinates(association, size); + } + else if (tag == "TENSORS") + { + this->ReadTensors(association, size); + } + else if (tag == "FIELD") + { + this->ReadFields(association, size); + } + else + { + break; + } + } + } +} + +void VTKDataSetReaderBase::CloseFile() +{ + this->DataFile->Stream.close(); +} + +void VTKDataSetReaderBase::OpenFile() +{ + this->DataFile->Stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + try + { + this->DataFile->Stream.open(this->DataFile->FileName.c_str(), + std::ios_base::in | std::ios_base::binary); + } + catch (std::ifstream::failure&) + { + std::string message("could not open file \"" + this->DataFile->FileName + "\""); + throw vtkm::io::ErrorIO(message); + } +} + +void VTKDataSetReaderBase::ReadHeader() +{ + char vstring[] = "# vtk DataFile Version"; + const std::size_t vlen = sizeof(vstring); + + // Read version line + char vbuf[vlen]; + this->DataFile->Stream.read(vbuf, vlen - 1); + vbuf[vlen - 1] = '\0'; + if (std::string(vbuf) != std::string(vstring)) + { + throw vtkm::io::ErrorIO("Incorrect file format."); + } + + char dot; + this->DataFile->Stream >> this->DataFile->Version[0] >> dot >> this->DataFile->Version[1]; + // skip rest of the line + std::string skip; + std::getline(this->DataFile->Stream, skip); + + if ((this->DataFile->Version[0] > 4) || + (this->DataFile->Version[0] == 4 && this->DataFile->Version[1] > 2)) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Reader may not correctly read >v4.2 files. Reading version " + << this->DataFile->Version[0] + << "." + << this->DataFile->Version[1] + << ".\n"); + } + + // Read title line + std::getline(this->DataFile->Stream, this->DataFile->Title); + + // Read format line + this->DataFile->IsBinary = false; + std::string format; + this->DataFile->Stream >> format >> std::ws; + if (format == "BINARY") + { + this->DataFile->IsBinary = true; + } + else if (format != "ASCII") + { + throw vtkm::io::ErrorIO("Unsupported Format."); + } + + // Read structure line + std::string tag, structStr; + this->DataFile->Stream >> tag >> structStr >> std::ws; + internal::parseAssert(tag == "DATASET"); + + this->DataFile->Structure = vtkm::io::internal::DataSetStructureId(structStr); + if (this->DataFile->Structure == vtkm::io::internal::DATASET_UNKNOWN) + { + throw vtkm::io::ErrorIO("Unsupported DataSet type."); + } +} + + +void VTKDataSetReaderBase::AddField(const std::string& name, + vtkm::cont::Field::Association association, + vtkm::cont::VariantArrayHandle& data) +{ + if (data.GetNumberOfValues() > 0) + { + switch (association) + { + case vtkm::cont::Field::Association::POINTS: + case vtkm::cont::Field::Association::WHOLE_MESH: + this->DataSet.AddField(vtkm::cont::Field(name, association, data)); + break; + case vtkm::cont::Field::Association::CELL_SET: + this->DataSet.AddField(vtkm::cont::Field(name, association, data)); + break; + default: + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Not recording field '" << name << "' because it has an unknown association"); + break; + } + } +} + +void VTKDataSetReaderBase::ReadScalars(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName, dataType, lookupTableName; + vtkm::IdComponent numComponents = 1; + this->DataFile->Stream >> dataName >> dataType; + std::string tag; + this->DataFile->Stream >> tag; + if (tag != "LOOKUP_TABLE") + { + try + { + numComponents = std::stoi(tag); + } + catch (std::invalid_argument&) + { + internal::parseAssert(false); + } + this->DataFile->Stream >> tag; + } + + internal::parseAssert(tag == "LOOKUP_TABLE"); + this->DataFile->Stream >> lookupTableName >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadColorScalars(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for COLOR_SCALARS is not implemented. Skipping."); + + std::string dataName; + vtkm::IdComponent numComponents; + this->DataFile->Stream >> dataName >> numComponents >> std::ws; + std::string dataType = this->DataFile->IsBinary ? "unsigned_char" : "float"; + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadLookupTable() +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for LOOKUP_TABLE is not implemented. Skipping."); + + std::string dataName; + std::size_t numEntries; + this->DataFile->Stream >> dataName >> numEntries >> std::ws; + this->SkipArray(numEntries, vtkm::Vec()); +} + +void VTKDataSetReaderBase::ReadTextureCoordinates(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + vtkm::IdComponent numComponents; + std::string dataType; + this->DataFile->Stream >> dataName >> numComponents >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadVectors(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + std::string dataType; + this->DataFile->Stream >> dataName >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, 3); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadTensors(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + std::string dataType; + this->DataFile->Stream >> dataName >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, 9); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadFields(vtkm::cont::Field::Association association, + std::size_t expectedNumElements) +{ + std::string dataName; + vtkm::Id numArrays; + this->DataFile->Stream >> dataName >> numArrays >> std::ws; + for (vtkm::Id i = 0; i < numArrays; ++i) + { + std::size_t numTuples; + vtkm::IdComponent numComponents; + std::string arrayName, dataType; + this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; + if (numTuples == expectedNumElements) + { + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numTuples, numComponents); + this->AddField(arrayName, association, data); + } + else + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Field " << arrayName + << "'s size does not match expected number of elements. Skipping"); + } + } +} + +void VTKDataSetReaderBase::ReadGlobalFields(std::vector* visitBounds) +{ + std::string dataName; + vtkm::Id numArrays; + this->DataFile->Stream >> dataName >> numArrays >> std::ws; + for (vtkm::Id i = 0; i < numArrays; ++i) + { + std::size_t numTuples; + vtkm::IdComponent numComponents; + std::string arrayName, dataType; + this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; + if (arrayName == "avtOriginalBounds" && visitBounds) + { + visitBounds->resize(6); + internal::parseAssert(numComponents == 1 && numTuples == 6); + // parse the bounds and fill the bounds vector + this->ReadArray(*visitBounds); + } + else + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Support for global field " << arrayName << " not implemented. Skipping."); + this->DoSkipArrayVariant(dataType, numTuples, numComponents); + } + } +} + +class VTKDataSetReaderBase::SkipArrayVariant +{ +public: + SkipArrayVariant(VTKDataSetReaderBase* reader, std::size_t numElements) + : Reader(reader) + , NumElements(numElements) + { + } + + template + void operator()(T) const + { + this->Reader->SkipArray(this->NumElements, T()); + } + + template + void operator()(vtkm::IdComponent numComponents, T) const + { + this->Reader->SkipArray(this->NumElements * static_cast(numComponents), T()); + } + +protected: + VTKDataSetReaderBase* Reader; + std::size_t NumElements; +}; + +class VTKDataSetReaderBase::ReadArrayVariant : public SkipArrayVariant +{ +public: + ReadArrayVariant(VTKDataSetReaderBase* reader, + vtkm::cont::Field::Association association, + std::size_t numElements, + vtkm::cont::VariantArrayHandle& data) + : SkipArrayVariant(reader, numElements) + , Association(association) + , Data(&data) + { + } + + template + void operator()(T) const + { + std::vector buffer(this->NumElements); + this->Reader->ReadArray(buffer); + if ((this->Association != vtkm::cont::Field::Association::CELL_SET) || + (this->Reader->GetCellsPermutation().GetNumberOfValues() < 1)) + { + *this->Data = CreateVariantArrayHandle(buffer); + } + else + { + // If we are reading data associated with a cell set, we need to (sometimes) permute the + // data due to differences between VTK and VTK-m cell shapes. + auto permutation = this->Reader->GetCellsPermutation().ReadPortal(); + vtkm::Id outSize = permutation.GetNumberOfValues(); + std::vector permutedBuffer(static_cast(outSize)); + for (vtkm::Id outIndex = 0; outIndex < outSize; outIndex++) + { + std::size_t inIndex = static_cast(permutation.Get(outIndex)); + permutedBuffer[static_cast(outIndex)] = buffer[inIndex]; + } + *this->Data = CreateVariantArrayHandle(permutedBuffer); + } + } + + template + void operator()(vtkm::IdComponent numComponents, T) const + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Support for " << numComponents << " components not implemented. Skipping."); + SkipArrayVariant::operator()(numComponents, T()); + } + +private: + vtkm::cont::Field::Association Association; + vtkm::cont::VariantArrayHandle* Data; +}; + +void VTKDataSetReaderBase::DoSkipArrayVariant(std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents) +{ + // string is unsupported for SkipArrayVariant, so it requires some + // special handling + if (dataType == "string") + { + const vtkm::Id stringCount = numComponents * static_cast(numElements); + for (vtkm::Id i = 0; i < stringCount; ++i) + { + std::string trash; + this->DataFile->Stream >> trash; + } + } + else + { + vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); + vtkm::io::internal::SelectTypeAndCall( + typeId, numComponents, SkipArrayVariant(this, numElements)); + } +} + +vtkm::cont::VariantArrayHandle VTKDataSetReaderBase::DoReadArrayVariant( + vtkm::cont::Field::Association association, + std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents) +{ + // Create empty data to start so that the return can check if data were actually read + vtkm::cont::ArrayHandle empty; + vtkm::cont::VariantArrayHandle data(empty); + + vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); + vtkm::io::internal::SelectTypeAndCall( + typeId, numComponents, ReadArrayVariant(this, association, numElements, data)); + + return data; +} + +void VTKDataSetReaderBase::ReadArray(std::vector& buffer) +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Support for data type 'bit' is not implemented. Skipping."); + this->SkipArray(buffer.size(), vtkm::io::internal::DummyBitType()); + buffer.clear(); +} + +void VTKDataSetReaderBase::SkipArray(std::size_t numElements, + vtkm::io::internal::DummyBitType, + vtkm::IdComponent numComponents) +{ + if (this->DataFile->IsBinary) + { + numElements = (numElements + 7) / 8; + this->DataFile->Stream.seekg(static_cast(numElements), std::ios_base::cur); + } + else + { + for (std::size_t i = 0; i < numElements; ++i) + { + vtkm::UInt16 val; + this->DataFile->Stream >> val; + } + } + this->DataFile->Stream >> std::ws; + this->SkipArrayMetaData(numComponents); +} + +void VTKDataSetReaderBase::SkipArrayMetaData(vtkm::IdComponent numComponents) +{ + if (!this->DataFile->Stream.good()) + { + return; + } + + auto begining = this->DataFile->Stream.tellg(); + + std::string tag; + this->DataFile->Stream >> tag; + if (tag != "METADATA") + { + this->DataFile->Stream.seekg(begining); + return; + } + + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "METADATA is not supported. Attempting to Skip."); + + this->DataFile->Stream >> tag >> std::ws; + if (tag == "COMPONENT_NAMES") + { + std::string name; + for (vtkm::IdComponent i = 0; i < numComponents; ++i) + { + this->DataFile->Stream >> name >> std::ws; + } + } + else if (tag == "INFORMATION") + { + int numKeys = 0; + this->DataFile->Stream >> numKeys >> std::ws; + + // Skipping INFORMATION is tricky. The reader needs to be aware of the types of the + // information, which is not provided in the file. + // Here we will just skip until an empty line is found. + // However, if there are no keys, then there is nothing to read (and the stream tends + // to skip over empty lines. + if (numKeys > 0) + { + std::string line; + do + { + std::getline(this->DataFile->Stream, line); + } while (this->DataFile->Stream.good() && !line.empty()); + + // Eat any remaining whitespace after the INFORMATION to be ready to read the next token + this->DataFile->Stream >> std::ws; + } + } + else + { + internal::parseAssert(false); + } +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetReaderBase.h b/vtkm/io/VTKDataSetReaderBase.h index 70292392c..62a935315 100644 --- a/vtkm/io/VTKDataSetReaderBase.h +++ b/vtkm/io/VTKDataSetReaderBase.h @@ -10,25 +10,16 @@ #ifndef vtk_m_io_VTKDataSetReaderBase_h #define vtk_m_io_VTKDataSetReaderBase_h +#include +#include +#include +#include + #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include #include -#include -#include namespace vtkm { @@ -48,16 +39,6 @@ struct VTKDataSetFile std::ifstream Stream; }; -inline void PrintVTKDataFileSummary(const VTKDataSetFile& df, std::ostream& out) -{ - out << "\tFile: " << df.FileName << std::endl; - out << "\tVersion: " << df.Version[0] << "." << df.Version[0] << std::endl; - out << "\tTitle: " << df.Title << std::endl; - out << "\tFormat: " << (df.IsBinary ? "BINARY" : "ASCII") << std::endl; - out << "\tDataSet type: " << vtkm::io::internal::DataSetStructureString(df.Structure) - << std::endl; -} - inline void parseAssert(bool condition) { if (!condition) @@ -82,166 +63,6 @@ struct StreamIOType using Type = vtkm::UInt16; }; -// Since Fields and DataSets store data in the default VariantArrayHandle, convert -// the data to the closest type supported by default. The following will -// need to be updated if VariantArrayHandle or TypeListCommon changes. -template -struct ClosestCommonType -{ - using Type = T; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; - -template -struct ClosestFloat -{ - using Type = T; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; - -template -vtkm::cont::VariantArrayHandle CreateVariantArrayHandle(const std::vector& vec) -{ - switch (vtkm::VecTraits::NUM_COMPONENTS) - { - case 1: - { - using CommonType = typename ClosestCommonType::Type; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() - << " is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() - << "."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - portal.Set(i, static_cast(vec[static_cast(i)])); - } - - return vtkm::cont::VariantArrayHandle(output); - } - case 2: - case 3: - case 9: - { - constexpr auto numComps = vtkm::VecTraits::NUM_COMPONENTS; - - using InComponentType = typename vtkm::VecTraits::ComponentType; - using OutComponentType = typename ClosestFloat::Type; - using CommonType = vtkm::Vec; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() << "[" - << vtkm::VecTraits::GetNumberOfComponents(T()) - << "] " - << "is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() - << "[" - << numComps - << "]."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - CommonType outval = CommonType(); - for (vtkm::IdComponent j = 0; j < numComps; ++j) - { - outval[j] = static_cast( - vtkm::VecTraits::GetComponent(vec[static_cast(i)], j)); - } - portal.Set(i, outval); - } - - return vtkm::cont::VariantArrayHandle(output); - } - default: - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Only 1, 2, 3, or 9 components supported. Skipping."); - return vtkm::cont::VariantArrayHandle(vtkm::cont::ArrayHandle()); - } - } -} - inline vtkm::cont::DynamicCellSet CreateCellSetStructured(const vtkm::Id3& dim) { if (dim[0] > 1 && dim[1] > 1 && dim[2] > 1) @@ -272,564 +93,102 @@ inline vtkm::cont::DynamicCellSet CreateCellSetStructured(const vtkm::Id3& dim) } // namespace internal -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKDataSetReaderBase { +protected: + std::unique_ptr DataFile; + vtkm::cont::DataSet DataSet; + +private: + bool Loaded; + vtkm::cont::ArrayHandle CellsPermutation; + + friend class VTKDataSetReader; + public: - explicit VTKDataSetReaderBase(const char* fileName) - : DataFile(new internal::VTKDataSetFile) - , DataSet() - , Loaded(false) - { - this->DataFile->FileName = fileName; - } + explicit VTKM_CONT VTKDataSetReaderBase(const char* fileName); - explicit VTKDataSetReaderBase(const std::string& fileName) - : DataFile(new internal::VTKDataSetFile) - , DataSet() - , Loaded(false) - { - this->DataFile->FileName = fileName; - } + explicit VTKM_CONT VTKDataSetReaderBase(const std::string& fileName); - virtual ~VTKDataSetReaderBase() {} + virtual VTKM_CONT ~VTKDataSetReaderBase(); - const vtkm::cont::DataSet& ReadDataSet() - { - if (!this->Loaded) - { - try - { - this->OpenFile(); - this->ReadHeader(); - this->Read(); - this->CloseFile(); - this->Loaded = true; - } - catch (std::ifstream::failure& e) - { - std::string message("IO Error: "); - throw vtkm::io::ErrorIO(message + e.what()); - } - } + VTKDataSetReaderBase(const VTKDataSetReaderBase&) = delete; + void operator=(const VTKDataSetReaderBase&) = delete; - return this->DataSet; - } + const VTKM_CONT vtkm::cont::DataSet& ReadDataSet(); const vtkm::cont::DataSet& GetDataSet() const { return this->DataSet; } - virtual void PrintSummary(std::ostream& out) const - { - out << "VTKDataSetReader" << std::endl; - PrintVTKDataFileSummary(*this->DataFile.get(), out); - this->DataSet.PrintSummary(out); - } + virtual VTKM_CONT void PrintSummary(std::ostream& out) const; protected: - void ReadPoints() - { - std::string dataType; - std::size_t numPoints; - this->DataFile->Stream >> numPoints >> dataType >> std::ws; + VTKM_CONT void ReadPoints(); - vtkm::cont::VariantArrayHandle points = - this->DoReadArrayVariant(vtkm::cont::Field::Association::POINTS, dataType, numPoints, 3); + VTKM_CONT void ReadCells(vtkm::cont::ArrayHandle& connectivity, + vtkm::cont::ArrayHandle& numIndices); - this->DataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", points)); - } + VTKM_CONT void ReadShapes(vtkm::cont::ArrayHandle& shapes); - void ReadCells(vtkm::cont::ArrayHandle& connectivity, - vtkm::cont::ArrayHandle& numIndices) - { - vtkm::Id numCells, numInts; - this->DataFile->Stream >> numCells >> numInts >> std::ws; - - connectivity.Allocate(numInts - numCells); - numIndices.Allocate(numCells); - - std::vector buffer(static_cast(numInts)); - this->ReadArray(buffer); - - vtkm::Int32* buffp = &buffer[0]; - auto connectivityPortal = connectivity.WritePortal(); - auto numIndicesPortal = numIndices.WritePortal(); - for (vtkm::Id i = 0, connInd = 0; i < numCells; ++i) - { - vtkm::IdComponent numInds = static_cast(*buffp++); - numIndicesPortal.Set(i, numInds); - for (vtkm::IdComponent j = 0; j < numInds; ++j, ++connInd) - { - connectivityPortal.Set(connInd, static_cast(*buffp++)); - } - } - } - - void ReadShapes(vtkm::cont::ArrayHandle& shapes) - { - std::string tag; - vtkm::Id numCells; - this->DataFile->Stream >> tag >> numCells >> std::ws; - internal::parseAssert(tag == "CELL_TYPES"); - - shapes.Allocate(numCells); - std::vector buffer(static_cast(numCells)); - this->ReadArray(buffer); - - vtkm::Int32* buffp = &buffer[0]; - auto shapesPortal = shapes.WritePortal(); - for (vtkm::Id i = 0; i < numCells; ++i) - { - shapesPortal.Set(i, static_cast(*buffp++)); - } - } - - void ReadAttributes() - { - if (this->DataFile->Stream.eof()) - { - return; - } - - vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY; - std::size_t size; - - std::string tag; - this->DataFile->Stream >> tag; - while (!this->DataFile->Stream.eof()) - { - if (tag == "POINT_DATA") - { - association = vtkm::cont::Field::Association::POINTS; - } - else if (tag == "CELL_DATA") - { - association = vtkm::cont::Field::Association::CELL_SET; - } - else - { - internal::parseAssert(false); - } - - this->DataFile->Stream >> size; - while (!this->DataFile->Stream.eof()) - { - this->DataFile->Stream >> tag; - if (tag == "SCALARS") - { - this->ReadScalars(association, size); - } - else if (tag == "COLOR_SCALARS") - { - this->ReadColorScalars(association, size); - } - else if (tag == "LOOKUP_TABLE") - { - this->ReadLookupTable(); - } - else if (tag == "VECTORS" || tag == "NORMALS") - { - this->ReadVectors(association, size); - } - else if (tag == "TEXTURE_COORDINATES") - { - this->ReadTextureCoordinates(association, size); - } - else if (tag == "TENSORS") - { - this->ReadTensors(association, size); - } - else if (tag == "FIELD") - { - this->ReadFields(association, size); - } - else - { - break; - } - } - } - } + VTKM_CONT void ReadAttributes(); void SetCellsPermutation(const vtkm::cont::ArrayHandle& permutation) { this->CellsPermutation = permutation; } - vtkm::cont::ArrayHandle GetCellsPermutation() const { return this->CellsPermutation; } + VTKM_CONT vtkm::cont::ArrayHandle GetCellsPermutation() const + { + return this->CellsPermutation; + } - void TransferDataFile(VTKDataSetReaderBase& reader) + VTKM_CONT void TransferDataFile(VTKDataSetReaderBase& reader) { reader.DataFile.swap(this->DataFile); this->DataFile.reset(nullptr); } - virtual void CloseFile() { this->DataFile->Stream.close(); } + VTKM_CONT virtual void CloseFile(); + + VTKM_CONT virtual void Read() = 0; private: - void OpenFile() - { - this->DataFile->Stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); - try - { - this->DataFile->Stream.open(this->DataFile->FileName.c_str(), - std::ios_base::in | std::ios_base::binary); - } - catch (std::ifstream::failure&) - { - std::string message("could not open file \"" + this->DataFile->FileName + "\""); - throw vtkm::io::ErrorIO(message); - } - } - - void ReadHeader() - { - char vstring[] = "# vtk DataFile Version"; - const std::size_t vlen = sizeof(vstring); - - // Read version line - char vbuf[vlen]; - this->DataFile->Stream.read(vbuf, vlen - 1); - vbuf[vlen - 1] = '\0'; - if (std::string(vbuf) != std::string(vstring)) - { - throw vtkm::io::ErrorIO("Incorrect file format."); - } - - char dot; - this->DataFile->Stream >> this->DataFile->Version[0] >> dot >> this->DataFile->Version[1]; - // skip rest of the line - std::string skip; - std::getline(this->DataFile->Stream, skip); - - if ((this->DataFile->Version[0] > 4) || - (this->DataFile->Version[0] == 4 && this->DataFile->Version[1] > 2)) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Reader may not correctly read >v4.2 files. Reading version " - << this->DataFile->Version[0] - << "." - << this->DataFile->Version[1] - << ".\n"); - } - - // Read title line - std::getline(this->DataFile->Stream, this->DataFile->Title); - - // Read format line - this->DataFile->IsBinary = false; - std::string format; - this->DataFile->Stream >> format >> std::ws; - if (format == "BINARY") - { - this->DataFile->IsBinary = true; - } - else if (format != "ASCII") - { - throw vtkm::io::ErrorIO("Unsupported Format."); - } - - // Read structure line - std::string tag, structStr; - this->DataFile->Stream >> tag >> structStr >> std::ws; - internal::parseAssert(tag == "DATASET"); - - this->DataFile->Structure = vtkm::io::internal::DataSetStructureId(structStr); - if (this->DataFile->Structure == vtkm::io::internal::DATASET_UNKNOWN) - { - throw vtkm::io::ErrorIO("Unsupported DataSet type."); - } - } - - virtual void Read() = 0; - - void AddField(const std::string& name, - vtkm::cont::Field::Association association, - vtkm::cont::VariantArrayHandle& data) - { - if (data.GetNumberOfValues() > 0) - { - switch (association) - { - case vtkm::cont::Field::Association::POINTS: - case vtkm::cont::Field::Association::WHOLE_MESH: - this->DataSet.AddField(vtkm::cont::Field(name, association, data)); - break; - case vtkm::cont::Field::Association::CELL_SET: - this->DataSet.AddField(vtkm::cont::Field(name, association, data)); - break; - default: - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Not recording field '" << name << "' because it has an unknown association"); - break; - } - } - } - - void ReadScalars(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName, dataType, lookupTableName; - vtkm::IdComponent numComponents = 1; - this->DataFile->Stream >> dataName >> dataType; - std::string tag; - this->DataFile->Stream >> tag; - if (tag != "LOOKUP_TABLE") - { - try - { - numComponents = std::stoi(tag); - } - catch (std::invalid_argument&) - { - internal::parseAssert(false); - } - this->DataFile->Stream >> tag; - } - - internal::parseAssert(tag == "LOOKUP_TABLE"); - this->DataFile->Stream >> lookupTableName >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadColorScalars(vtkm::cont::Field::Association association, std::size_t numElements) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for COLOR_SCALARS is not implemented. Skipping."); - - std::string dataName; - vtkm::IdComponent numComponents; - this->DataFile->Stream >> dataName >> numComponents >> std::ws; - std::string dataType = this->DataFile->IsBinary ? "unsigned_char" : "float"; - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadLookupTable() - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for LOOKUP_TABLE is not implemented. Skipping."); - - std::string dataName; - std::size_t numEntries; - this->DataFile->Stream >> dataName >> numEntries >> std::ws; - this->SkipArray(numEntries, vtkm::Vec()); - } - - void ReadTextureCoordinates(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - vtkm::IdComponent numComponents; - std::string dataType; - this->DataFile->Stream >> dataName >> numComponents >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadVectors(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - std::string dataType; - this->DataFile->Stream >> dataName >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, 3); - this->AddField(dataName, association, data); - } - - void ReadTensors(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - std::string dataType; - this->DataFile->Stream >> dataName >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, 9); - this->AddField(dataName, association, data); - } - - void ReadFields(vtkm::cont::Field::Association association, std::size_t expectedNumElements) - { - std::string dataName; - vtkm::Id numArrays; - this->DataFile->Stream >> dataName >> numArrays >> std::ws; - for (vtkm::Id i = 0; i < numArrays; ++i) - { - std::size_t numTuples; - vtkm::IdComponent numComponents; - std::string arrayName, dataType; - this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; - if (numTuples == expectedNumElements) - { - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numTuples, numComponents); - this->AddField(arrayName, association, data); - } - else - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Field " << arrayName - << "'s size does not match expected number of elements. Skipping"); - } - } - } + VTKM_CONT void OpenFile(); + VTKM_CONT void ReadHeader(); + VTKM_CONT void AddField(const std::string& name, + vtkm::cont::Field::Association association, + vtkm::cont::VariantArrayHandle& data); + VTKM_CONT void ReadScalars(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadColorScalars(vtkm::cont::Field::Association association, + std::size_t numElements); + VTKM_CONT void ReadLookupTable(); + VTKM_CONT void ReadTextureCoordinates(vtkm::cont::Field::Association association, + std::size_t numElements); + VTKM_CONT void ReadVectors(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadTensors(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadFields(vtkm::cont::Field::Association association, + std::size_t expectedNumElements); protected: - void ReadGlobalFields(std::vector* visitBounds = nullptr) - { - std::string dataName; - vtkm::Id numArrays; - this->DataFile->Stream >> dataName >> numArrays >> std::ws; - for (vtkm::Id i = 0; i < numArrays; ++i) - { - std::size_t numTuples; - vtkm::IdComponent numComponents; - std::string arrayName, dataType; - this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; - if (arrayName == "avtOriginalBounds" && visitBounds) - { - visitBounds->resize(6); - internal::parseAssert(numComponents == 1 && numTuples == 6); - // parse the bounds and fill the bounds vector - this->ReadArray(*visitBounds); - } - else - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Support for global field " << arrayName << " not implemented. Skipping."); - this->DoSkipArrayVariant(dataType, numTuples, numComponents); - } - } - } + VTKM_CONT void ReadGlobalFields(std::vector* visitBounds = nullptr); private: - class SkipArrayVariant - { - public: - SkipArrayVariant(VTKDataSetReaderBase* reader, std::size_t numElements) - : Reader(reader) - , NumElements(numElements) - { - } - - template - void operator()(T) const - { - this->Reader->SkipArray(this->NumElements, T()); - } - - template - void operator()(vtkm::IdComponent numComponents, T) const - { - this->Reader->SkipArray(this->NumElements * static_cast(numComponents), T()); - } - - protected: - VTKDataSetReaderBase* Reader; - std::size_t NumElements; - }; - - class ReadArrayVariant : public SkipArrayVariant - { - public: - ReadArrayVariant(VTKDataSetReaderBase* reader, - vtkm::cont::Field::Association association, - std::size_t numElements, - vtkm::cont::VariantArrayHandle& data) - : SkipArrayVariant(reader, numElements) - , Association(association) - , Data(&data) - { - } - - template - void operator()(T) const - { - std::vector buffer(this->NumElements); - this->Reader->ReadArray(buffer); - if ((this->Association != vtkm::cont::Field::Association::CELL_SET) || - (this->Reader->GetCellsPermutation().GetNumberOfValues() < 1)) - { - *this->Data = internal::CreateVariantArrayHandle(buffer); - } - else - { - // If we are reading data associated with a cell set, we need to (sometimes) permute the - // data due to differences between VTK and VTK-m cell shapes. - auto permutation = this->Reader->GetCellsPermutation().ReadPortal(); - vtkm::Id outSize = permutation.GetNumberOfValues(); - std::vector permutedBuffer(static_cast(outSize)); - for (vtkm::Id outIndex = 0; outIndex < outSize; outIndex++) - { - std::size_t inIndex = static_cast(permutation.Get(outIndex)); - permutedBuffer[static_cast(outIndex)] = buffer[inIndex]; - } - *this->Data = internal::CreateVariantArrayHandle(permutedBuffer); - } - } - - template - void operator()(vtkm::IdComponent numComponents, T) const - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for " << numComponents << " components not implemented. Skipping."); - SkipArrayVariant::operator()(numComponents, T()); - } - - private: - vtkm::cont::Field::Association Association; - vtkm::cont::VariantArrayHandle* Data; - }; + class SkipArrayVariant; + class ReadArrayVariant; //Make the Array parsing methods protected so that derived classes //can call the methods. protected: - void DoSkipArrayVariant(std::string dataType, - std::size_t numElements, - vtkm::IdComponent numComponents) - { - // string is unsupported for SkipArrayVariant, so it requires some - // special handling - if (dataType == "string") - { - const vtkm::Id stringCount = numComponents * static_cast(numElements); - for (vtkm::Id i = 0; i < stringCount; ++i) - { - std::string trash; - this->DataFile->Stream >> trash; - } - } - else - { - vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); - vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, SkipArrayVariant(this, numElements)); - } - } - - vtkm::cont::VariantArrayHandle DoReadArrayVariant(vtkm::cont::Field::Association association, - std::string dataType, - std::size_t numElements, - vtkm::IdComponent numComponents) - { - // Create empty data to start so that the return can check if data were actually read - vtkm::cont::ArrayHandle empty; - vtkm::cont::VariantArrayHandle data(empty); - - vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); - vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, ReadArrayVariant(this, association, numElements, data)); - - return data; - } + VTKM_CONT void DoSkipArrayVariant(std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents); + VTKM_CONT vtkm::cont::VariantArrayHandle DoReadArrayVariant( + vtkm::cont::Field::Association association, + std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents); template - void ReadArray(std::vector& buffer) + VTKM_CONT void ReadArray(std::vector& buffer) { using ComponentType = typename vtkm::VecTraits::ComponentType; constexpr vtkm::IdComponent numComponents = vtkm::VecTraits::NUM_COMPONENTS; @@ -861,7 +220,8 @@ protected: } template - void ReadArray(std::vector>& buffer) + VTKM_CONT void ReadArray( + std::vector>& buffer) { VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for data type 'bit' is not implemented. Skipping."); @@ -869,13 +229,7 @@ protected: buffer.clear(); } - void ReadArray(std::vector& buffer) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for data type 'bit' is not implemented. Skipping."); - this->SkipArray(buffer.size(), vtkm::io::internal::DummyBitType()); - buffer.clear(); - } + VTKM_CONT void ReadArray(std::vector& buffer); template void SkipArray(std::size_t numElements, T) @@ -912,95 +266,12 @@ protected: NumComponents); } - void SkipArray(std::size_t numElements, - vtkm::io::internal::DummyBitType, - vtkm::IdComponent numComponents = 1) - { - if (this->DataFile->IsBinary) - { - numElements = (numElements + 7) / 8; - this->DataFile->Stream.seekg(static_cast(numElements), std::ios_base::cur); - } - else - { - for (std::size_t i = 0; i < numElements; ++i) - { - vtkm::UInt16 val; - this->DataFile->Stream >> val; - } - } - this->DataFile->Stream >> std::ws; - this->SkipArrayMetaData(numComponents); - } + VTKM_CONT void SkipArray(std::size_t numElements, + vtkm::io::internal::DummyBitType, + vtkm::IdComponent numComponents = 1); - void SkipArrayMetaData(vtkm::IdComponent numComponents) - { - if (!this->DataFile->Stream.good()) - { - return; - } - - auto begining = this->DataFile->Stream.tellg(); - - std::string tag; - this->DataFile->Stream >> tag; - if (tag != "METADATA") - { - this->DataFile->Stream.seekg(begining); - return; - } - - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "METADATA is not supported. Attempting to Skip."); - - this->DataFile->Stream >> tag >> std::ws; - if (tag == "COMPONENT_NAMES") - { - std::string name; - for (vtkm::IdComponent i = 0; i < numComponents; ++i) - { - this->DataFile->Stream >> name >> std::ws; - } - } - else if (tag == "INFORMATION") - { - int numKeys = 0; - this->DataFile->Stream >> numKeys >> std::ws; - - // Skipping INFORMATION is tricky. The reader needs to be aware of the types of the - // information, which is not provided in the file. - // Here we will just skip until an empty line is found. - // However, if there are no keys, then there is nothing to read (and the stream tends - // to skip over empty lines. - if (numKeys > 0) - { - std::string line; - do - { - std::getline(this->DataFile->Stream, line); - } while (this->DataFile->Stream.good() && !line.empty()); - - // Eat any remaining whitespace after the INFORMATION to be ready to read the next token - this->DataFile->Stream >> std::ws; - } - } - else - { - internal::parseAssert(false); - } - } - -protected: - std::unique_ptr DataFile; - vtkm::cont::DataSet DataSet; - -private: - bool Loaded; - vtkm::cont::ArrayHandle CellsPermutation; - - friend class VTKDataSetReader; + VTKM_CONT void SkipArrayMetaData(vtkm::IdComponent numComponents); }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // vtkm::io diff --git a/vtkm/io/VTKPolyDataReader.h b/vtkm/io/VTKPolyDataReader.h index 3629f62ca..dcc0623f7 100644 --- a/vtkm/io/VTKPolyDataReader.h +++ b/vtkm/io/VTKPolyDataReader.h @@ -11,6 +11,7 @@ #define vtk_m_io_VTKPolyDataReader_h #include +#include #include diff --git a/vtkm/io/testing/CMakeLists.txt b/vtkm/io/testing/CMakeLists.txt index f17628c77..33ee8762e 100644 --- a/vtkm/io/testing/CMakeLists.txt +++ b/vtkm/io/testing/CMakeLists.txt @@ -15,14 +15,14 @@ set(unit_tests UnitTestVTKDataSetWriter.cxx ) -vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS LIBRARIES vtkm_lodepng) +set(unit_test_libraries vtkm_lodepng vtkm_io) -if(NOT VTKm_ENABLE_RENDERING) - return() +if(VTKm_ENABLE_RENDERING) + set(unit_tests ${unit_tests} + UnitTestImageWriter.cxx + ) + + set(unit_test_libraries ${unit_test_libraries} vtkm_rendering) endif() -set(image_unit_tests - UnitTestImageWriter.cxx -) - -vtkm_unit_tests(NAME UnitTests_vtkm_io_image_testing SOURCES ${image_unit_tests} ALL_BACKENDS LIBRARIES vtkm_rendering vtkm_lodepng) +vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS LIBRARIES ${unit_test_libraries}) From 086439e71ec66f04b04c86eab73bb9fccc632fd9 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 13:04:12 -0600 Subject: [PATCH 24/27] Update variants of VTKDataSetReaders to compile into vtkm_io --- docs/changelog/vtk-io-in-library.md | 12 ++ vtkm/io/CMakeLists.txt | 6 + vtkm/io/VTKDataSetReader.cxx | 92 ++++++++++++++++ vtkm/io/VTKDataSetReader.h | 77 ++----------- vtkm/io/VTKPolyDataReader.cxx | 152 ++++++++++++++++++++++++++ vtkm/io/VTKPolyDataReader.h | 140 +----------------------- vtkm/io/VTKRectilinearGridReader.cxx | 95 ++++++++++++++++ vtkm/io/VTKRectilinearGridReader.h | 82 +------------- vtkm/io/VTKStructuredGridReader.cxx | 62 +++++++++++ vtkm/io/VTKStructuredGridReader.h | 46 +------- vtkm/io/VTKStructuredPointsReader.cxx | 79 +++++++++++++ vtkm/io/VTKStructuredPointsReader.h | 63 +---------- vtkm/io/VTKUnstructuredGridReader.cxx | 88 +++++++++++++++ vtkm/io/VTKUnstructuredGridReader.h | 70 +----------- 14 files changed, 615 insertions(+), 449 deletions(-) create mode 100644 docs/changelog/vtk-io-in-library.md create mode 100644 vtkm/io/VTKDataSetReader.cxx create mode 100644 vtkm/io/VTKPolyDataReader.cxx create mode 100644 vtkm/io/VTKRectilinearGridReader.cxx create mode 100644 vtkm/io/VTKStructuredGridReader.cxx create mode 100644 vtkm/io/VTKStructuredPointsReader.cxx create mode 100644 vtkm/io/VTKUnstructuredGridReader.cxx diff --git a/docs/changelog/vtk-io-in-library.md b/docs/changelog/vtk-io-in-library.md new file mode 100644 index 000000000..d6616813e --- /dev/null +++ b/docs/changelog/vtk-io-in-library.md @@ -0,0 +1,12 @@ +# Move VTK file readers and writers into vtkm_io + +The legacy VTK file reader and writer were created back when VTK-m was a +header-only library. Things have changed and we now compile quite a bit of +code into libraries. At this point, there is no reason why the VTK file +reader/writer should be any different. + +Thus, `VTKDataSetReader`, `VTKDataSetWriter`, and several supporting +classes are now compiled into the `vtk_io` library. + +As a side effect, code using VTK-m will need to link to `vtk_io` if they +are using any readers or writers. diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index 32d9d9ae5..a8d63aea4 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -35,7 +35,13 @@ set(template_sources set(sources DecodePNG.cxx EncodePNG.cxx + VTKDataSetReader.cxx VTKDataSetReaderBase.cxx + VTKPolyDataReader.cxx + VTKRectilinearGridReader.cxx + VTKStructuredGridReader.cxx + VTKStructuredPointsReader.cxx + VTKUnstructuredGridReader.cxx ) vtkm_declare_headers( diff --git a/vtkm/io/VTKDataSetReader.cxx b/vtkm/io/VTKDataSetReader.cxx new file mode 100644 index 000000000..a1fdbbdb9 --- /dev/null +++ b/vtkm/io/VTKDataSetReader.cxx @@ -0,0 +1,92 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include +#include +#include +#include +#include + +#include + +namespace vtkm +{ +namespace io +{ + +VTKDataSetReader::VTKDataSetReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKDataSetReader::VTKDataSetReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKDataSetReader::~VTKDataSetReader() +{ +} + +void VTKDataSetReader::PrintSummary(std::ostream& out) const +{ + if (this->Reader) + { + this->Reader->PrintSummary(out); + } + else + { + VTKDataSetReaderBase::PrintSummary(out); + } +} + +void VTKDataSetReader::CloseFile() +{ + if (this->Reader) + { + this->Reader->CloseFile(); + } + else + { + VTKDataSetReaderBase::CloseFile(); + } +} + +void VTKDataSetReader::Read() +{ + switch (this->DataFile->Structure) + { + case vtkm::io::internal::DATASET_STRUCTURED_POINTS: + this->Reader.reset(new VTKStructuredPointsReader("")); + break; + case vtkm::io::internal::DATASET_STRUCTURED_GRID: + this->Reader.reset(new VTKStructuredGridReader("")); + break; + case vtkm::io::internal::DATASET_RECTILINEAR_GRID: + this->Reader.reset(new VTKRectilinearGridReader("")); + break; + case vtkm::io::internal::DATASET_POLYDATA: + this->Reader.reset(new VTKPolyDataReader("")); + break; + case vtkm::io::internal::DATASET_UNSTRUCTURED_GRID: + this->Reader.reset(new VTKUnstructuredGridReader("")); + break; + default: + throw vtkm::io::ErrorIO("Unsupported DataSet type."); + } + + this->TransferDataFile(*this->Reader.get()); + this->Reader->Read(); + this->DataSet = this->Reader->GetDataSet(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetReader.h b/vtkm/io/VTKDataSetReader.h index 166c9466d..49e4b4537 100644 --- a/vtkm/io/VTKDataSetReader.h +++ b/vtkm/io/VTKDataSetReader.h @@ -11,86 +11,27 @@ #define vtk_m_io_VTKDataSetReader_h #include -#include -#include -#include -#include -#include - -#include namespace vtkm { namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKDataSetReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKDataSetReader : public VTKDataSetReaderBase { public: - explicit VTKDataSetReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + VTKM_CONT VTKDataSetReader(const char* fileName); + VTKM_CONT VTKDataSetReader(const std::string& fileName); + VTKM_CONT ~VTKDataSetReader() override; - explicit VTKDataSetReader(const std::string& fileName) - : VTKDataSetReaderBase(fileName) - { - } + VTKDataSetReader(const VTKDataSetReader&) = delete; + void operator=(const VTKDataSetReader&) = delete; - virtual void PrintSummary(std::ostream& out) const - { - if (this->Reader) - { - this->Reader->PrintSummary(out); - } - else - { - VTKDataSetReaderBase::PrintSummary(out); - } - } + VTKM_CONT void PrintSummary(std::ostream& out) const override; private: - virtual void CloseFile() - { - if (this->Reader) - { - this->Reader->CloseFile(); - } - else - { - VTKDataSetReaderBase::CloseFile(); - } - } - - virtual void Read() - { - switch (this->DataFile->Structure) - { - case vtkm::io::internal::DATASET_STRUCTURED_POINTS: - this->Reader.reset(new VTKStructuredPointsReader("")); - break; - case vtkm::io::internal::DATASET_STRUCTURED_GRID: - this->Reader.reset(new VTKStructuredGridReader("")); - break; - case vtkm::io::internal::DATASET_RECTILINEAR_GRID: - this->Reader.reset(new VTKRectilinearGridReader("")); - break; - case vtkm::io::internal::DATASET_POLYDATA: - this->Reader.reset(new VTKPolyDataReader("")); - break; - case vtkm::io::internal::DATASET_UNSTRUCTURED_GRID: - this->Reader.reset(new VTKUnstructuredGridReader("")); - break; - default: - throw vtkm::io::ErrorIO("Unsupported DataSet type."); - } - - this->TransferDataFile(*this->Reader.get()); - this->Reader->Read(); - this->DataSet = this->Reader->GetDataSet(); - } + VTKM_CONT void CloseFile() override; + VTKM_CONT void Read() override; std::unique_ptr Reader; }; diff --git a/vtkm/io/VTKPolyDataReader.cxx b/vtkm/io/VTKPolyDataReader.cxx new file mode 100644 index 000000000..e134d6160 --- /dev/null +++ b/vtkm/io/VTKPolyDataReader.cxx @@ -0,0 +1,152 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +namespace +{ + +template +inline vtkm::cont::ArrayHandle ConcatinateArrayHandles( + const std::vector>& arrays) +{ + vtkm::Id size = 0; + for (std::size_t i = 0; i < arrays.size(); ++i) + { + size += arrays[i].GetNumberOfValues(); + } + + vtkm::cont::ArrayHandle out; + out.Allocate(size); + + auto outp = vtkm::cont::ArrayPortalToIteratorBegin(out.WritePortal()); + for (std::size_t i = 0; i < arrays.size(); ++i) + { + std::copy(vtkm::cont::ArrayPortalToIteratorBegin(arrays[i].ReadPortal()), + vtkm::cont::ArrayPortalToIteratorEnd(arrays[i].ReadPortal()), + outp); + using DifferenceType = typename std::iterator_traits::difference_type; + std::advance(outp, static_cast(arrays[i].GetNumberOfValues())); + } + + return out; +} +} + +namespace vtkm +{ +namespace io +{ + +VTKPolyDataReader::VTKPolyDataReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKPolyDataReader::VTKPolyDataReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKPolyDataReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_POLYDATA) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + std::string tag; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read the points + internal::parseAssert(tag == "POINTS"); + this->ReadPoints(); + + vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); + + // Read the cellset + std::vector> connectivityArrays; + std::vector> numIndicesArrays; + std::vector shapesBuffer; + while (!this->DataFile->Stream.eof()) + { + vtkm::UInt8 shape = vtkm::CELL_SHAPE_EMPTY; + this->DataFile->Stream >> tag; + if (tag == "VERTICES") + { + shape = vtkm::io::internal::CELL_SHAPE_POLY_VERTEX; + } + else if (tag == "LINES") + { + shape = vtkm::io::internal::CELL_SHAPE_POLY_LINE; + } + else if (tag == "POLYGONS") + { + shape = vtkm::CELL_SHAPE_POLYGON; + } + else if (tag == "TRIANGLE_STRIPS") + { + shape = vtkm::io::internal::CELL_SHAPE_TRIANGLE_STRIP; + } + else + { + this->DataFile->Stream.seekg(-static_cast(tag.length()), std::ios_base::cur); + break; + } + + vtkm::cont::ArrayHandle cellConnectivity; + vtkm::cont::ArrayHandle cellNumIndices; + this->ReadCells(cellConnectivity, cellNumIndices); + + connectivityArrays.push_back(cellConnectivity); + numIndicesArrays.push_back(cellNumIndices); + shapesBuffer.insert( + shapesBuffer.end(), static_cast(cellNumIndices.GetNumberOfValues()), shape); + } + + vtkm::cont::ArrayHandle connectivity = ConcatinateArrayHandles(connectivityArrays); + vtkm::cont::ArrayHandle numIndices = ConcatinateArrayHandles(numIndicesArrays); + vtkm::cont::ArrayHandle shapes; + shapes.Allocate(static_cast(shapesBuffer.size())); + std::copy(shapesBuffer.begin(), + shapesBuffer.end(), + vtkm::cont::ArrayPortalToIteratorBegin(shapes.WritePortal())); + + vtkm::cont::ArrayHandle permutation; + vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); + this->SetCellsPermutation(permutation); + + if (vtkm::io::internal::IsSingleShape(shapes)) + { + vtkm::cont::CellSetSingleType<> cellSet; + cellSet.Fill( + numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); + this->DataSet.SetCellSet(cellSet); + } + else + { + auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); + vtkm::cont::CellSetExplicit<> cellSet; + cellSet.Fill(numPoints, shapes, connectivity, offsets); + this->DataSet.SetCellSet(cellSet); + } + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKPolyDataReader.h b/vtkm/io/VTKPolyDataReader.h index dcc0623f7..449c1747f 100644 --- a/vtkm/io/VTKPolyDataReader.h +++ b/vtkm/io/VTKPolyDataReader.h @@ -22,147 +22,15 @@ namespace vtkm namespace io { -namespace internal -{ - -template -inline vtkm::cont::ArrayHandle ConcatinateArrayHandles( - const std::vector>& arrays) -{ - vtkm::Id size = 0; - for (std::size_t i = 0; i < arrays.size(); ++i) - { - size += arrays[i].GetNumberOfValues(); - } - - vtkm::cont::ArrayHandle out; - out.Allocate(size); - - auto outp = vtkm::cont::ArrayPortalToIteratorBegin(out.WritePortal()); - for (std::size_t i = 0; i < arrays.size(); ++i) - { - std::copy(vtkm::cont::ArrayPortalToIteratorBegin(arrays[i].ReadPortal()), - vtkm::cont::ArrayPortalToIteratorEnd(arrays[i].ReadPortal()), - outp); - using DifferenceType = typename std::iterator_traits::difference_type; - std::advance(outp, static_cast(arrays[i].GetNumberOfValues())); - } - - return out; -} - -} // namespace internal - -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKPolyDataReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKPolyDataReader : public VTKDataSetReaderBase { public: - explicit VTKPolyDataReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKPolyDataReader(const char* fileName); + explicit VTKM_CONT VTKPolyDataReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_POLYDATA) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - std::string tag; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read the points - internal::parseAssert(tag == "POINTS"); - this->ReadPoints(); - - vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); - - // Read the cellset - std::vector> connectivityArrays; - std::vector> numIndicesArrays; - std::vector shapesBuffer; - while (!this->DataFile->Stream.eof()) - { - vtkm::UInt8 shape = vtkm::CELL_SHAPE_EMPTY; - this->DataFile->Stream >> tag; - if (tag == "VERTICES") - { - shape = vtkm::io::internal::CELL_SHAPE_POLY_VERTEX; - } - else if (tag == "LINES") - { - shape = vtkm::io::internal::CELL_SHAPE_POLY_LINE; - } - else if (tag == "POLYGONS") - { - shape = vtkm::CELL_SHAPE_POLYGON; - } - else if (tag == "TRIANGLE_STRIPS") - { - shape = vtkm::io::internal::CELL_SHAPE_TRIANGLE_STRIP; - } - else - { - this->DataFile->Stream.seekg(-static_cast(tag.length()), - std::ios_base::cur); - break; - } - - vtkm::cont::ArrayHandle cellConnectivity; - vtkm::cont::ArrayHandle cellNumIndices; - this->ReadCells(cellConnectivity, cellNumIndices); - - connectivityArrays.push_back(cellConnectivity); - numIndicesArrays.push_back(cellNumIndices); - shapesBuffer.insert( - shapesBuffer.end(), static_cast(cellNumIndices.GetNumberOfValues()), shape); - } - - vtkm::cont::ArrayHandle connectivity = - internal::ConcatinateArrayHandles(connectivityArrays); - vtkm::cont::ArrayHandle numIndices = - internal::ConcatinateArrayHandles(numIndicesArrays); - vtkm::cont::ArrayHandle shapes; - shapes.Allocate(static_cast(shapesBuffer.size())); - std::copy(shapesBuffer.begin(), - shapesBuffer.end(), - vtkm::cont::ArrayPortalToIteratorBegin(shapes.WritePortal())); - - vtkm::cont::ArrayHandle permutation; - vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); - this->SetCellsPermutation(permutation); - - if (vtkm::io::internal::IsSingleShape(shapes)) - { - vtkm::cont::CellSetSingleType<> cellSet; - cellSet.Fill( - numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); - this->DataSet.SetCellSet(cellSet); - } - else - { - auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); - vtkm::cont::CellSetExplicit<> cellSet; - cellSet.Fill(numPoints, shapes, connectivity, offsets); - this->DataSet.SetCellSet(cellSet); - } - - // Read points and cell attributes - this->ReadAttributes(); - } + void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKRectilinearGridReader.cxx b/vtkm/io/VTKRectilinearGridReader.cxx new file mode 100644 index 000000000..9dcf3a5ca --- /dev/null +++ b/vtkm/io/VTKRectilinearGridReader.cxx @@ -0,0 +1,95 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +namespace vtkm +{ +namespace io +{ + +VTKRectilinearGridReader::VTKRectilinearGridReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKRectilinearGridReader::VTKRectilinearGridReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKRectilinearGridReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_RECTILINEAR_GRID) + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + std::string tag; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read structured grid specific meta-data + internal::parseAssert(tag == "DIMENSIONS"); + vtkm::Id3 dim; + this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + + //Read the points. + std::string dataType; + std::size_t numPoints[3]; + vtkm::cont::VariantArrayHandle X, Y, Z; + + // Always read coordinates as vtkm::FloatDefault + std::string readDataType = vtkm::io::internal::DataTypeName::Name(); + + this->DataFile->Stream >> tag >> numPoints[0] >> dataType >> std::ws; + if (tag != "X_COORDINATES") + throw vtkm::io::ErrorIO("X_COORDINATES tag not found"); + X = this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[0], 1); + + this->DataFile->Stream >> tag >> numPoints[1] >> dataType >> std::ws; + if (tag != "Y_COORDINATES") + throw vtkm::io::ErrorIO("Y_COORDINATES tag not found"); + Y = this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[1], 1); + + this->DataFile->Stream >> tag >> numPoints[2] >> dataType >> std::ws; + if (tag != "Z_COORDINATES") + throw vtkm::io::ErrorIO("Z_COORDINATES tag not found"); + Z = this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[2], 1); + + if (dim != vtkm::Id3(static_cast(numPoints[0]), + static_cast(numPoints[1]), + static_cast(numPoints[2]))) + throw vtkm::io::ErrorIO("DIMENSIONS not equal to number of points"); + + vtkm::cont::ArrayHandleCartesianProduct, + vtkm::cont::ArrayHandle, + vtkm::cont::ArrayHandle> + coords; + + vtkm::cont::ArrayHandle Xc, Yc, Zc; + X.CopyTo(Xc); + Y.CopyTo(Yc); + Z.CopyTo(Zc); + coords = vtkm::cont::make_ArrayHandleCartesianProduct(Xc, Yc, Zc); + vtkm::cont::CoordinateSystem coordSys("coordinates", coords); + this->DataSet.AddCoordinateSystem(coordSys); + + this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKRectilinearGridReader.h b/vtkm/io/VTKRectilinearGridReader.h index 92a5c6e05..37c681c28 100644 --- a/vtkm/io/VTKRectilinearGridReader.h +++ b/vtkm/io/VTKRectilinearGridReader.h @@ -17,89 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKRectilinearGridReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKRectilinearGridReader : public VTKDataSetReaderBase { public: - explicit VTKRectilinearGridReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKRectilinearGridReader(const char* fileName); + explicit VTKM_CONT VTKRectilinearGridReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_RECTILINEAR_GRID) - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - std::string tag; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read structured grid specific meta-data - internal::parseAssert(tag == "DIMENSIONS"); - vtkm::Id3 dim; - this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - - //Read the points. - std::string dataType; - std::size_t numPoints[3]; - vtkm::cont::VariantArrayHandle X, Y, Z; - - // Always read coordinates as vtkm::FloatDefault - std::string readDataType = vtkm::io::internal::DataTypeName::Name(); - - this->DataFile->Stream >> tag >> numPoints[0] >> dataType >> std::ws; - if (tag != "X_COORDINATES") - throw vtkm::io::ErrorIO("X_COORDINATES tag not found"); - X = - this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[0], 1); - - this->DataFile->Stream >> tag >> numPoints[1] >> dataType >> std::ws; - if (tag != "Y_COORDINATES") - throw vtkm::io::ErrorIO("Y_COORDINATES tag not found"); - Y = - this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[1], 1); - - this->DataFile->Stream >> tag >> numPoints[2] >> dataType >> std::ws; - if (tag != "Z_COORDINATES") - throw vtkm::io::ErrorIO("Z_COORDINATES tag not found"); - Z = - this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[2], 1); - - if (dim != vtkm::Id3(static_cast(numPoints[0]), - static_cast(numPoints[1]), - static_cast(numPoints[2]))) - throw vtkm::io::ErrorIO("DIMENSIONS not equal to number of points"); - - vtkm::cont::ArrayHandleCartesianProduct, - vtkm::cont::ArrayHandle, - vtkm::cont::ArrayHandle> - coords; - - vtkm::cont::ArrayHandle Xc, Yc, Zc; - X.CopyTo(Xc); - Y.CopyTo(Yc); - Z.CopyTo(Zc); - coords = vtkm::cont::make_ArrayHandleCartesianProduct(Xc, Yc, Zc); - vtkm::cont::CoordinateSystem coordSys("coordinates", coords); - this->DataSet.AddCoordinateSystem(coordSys); - - this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredGridReader.cxx b/vtkm/io/VTKStructuredGridReader.cxx new file mode 100644 index 000000000..76b7d2101 --- /dev/null +++ b/vtkm/io/VTKStructuredGridReader.cxx @@ -0,0 +1,62 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +namespace vtkm +{ +namespace io +{ + +VTKStructuredGridReader::VTKStructuredGridReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKStructuredGridReader::VTKStructuredGridReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKStructuredGridReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_GRID) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + std::string tag; + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read structured grid specific meta-data + internal::parseAssert(tag == "DIMENSIONS"); + vtkm::Id3 dim; + this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + + this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); + + // Read the points + this->DataFile->Stream >> tag; + internal::parseAssert(tag == "POINTS"); + this->ReadPoints(); + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredGridReader.h b/vtkm/io/VTKStructuredGridReader.h index b37e98301..745cef2b7 100644 --- a/vtkm/io/VTKStructuredGridReader.h +++ b/vtkm/io/VTKStructuredGridReader.h @@ -17,53 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKStructuredGridReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKStructuredGridReader : public VTKDataSetReaderBase { public: - explicit VTKStructuredGridReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKStructuredGridReader(const char* fileName); + explicit VTKM_CONT VTKStructuredGridReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_GRID) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - std::string tag; - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read structured grid specific meta-data - internal::parseAssert(tag == "DIMENSIONS"); - vtkm::Id3 dim; - this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - - this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); - - // Read the points - this->DataFile->Stream >> tag; - internal::parseAssert(tag == "POINTS"); - this->ReadPoints(); - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredPointsReader.cxx b/vtkm/io/VTKStructuredPointsReader.cxx new file mode 100644 index 000000000..1293da374 --- /dev/null +++ b/vtkm/io/VTKStructuredPointsReader.cxx @@ -0,0 +1,79 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +namespace vtkm +{ +namespace io +{ + +VTKStructuredPointsReader::VTKStructuredPointsReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKStructuredPointsReader::VTKStructuredPointsReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKStructuredPointsReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_POINTS) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + std::string tag; + + // Read structured points specific meta-data + vtkm::Id3 dim; + vtkm::Vec3f_32 origin, spacing; + + //Two ways the file can describe the dimensions. The proper way is by + //using the DIMENSIONS keyword, but VisIt written VTK files spicify data + //bounds instead, as a FIELD + std::vector visitBounds; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(&visitBounds); + this->DataFile->Stream >> tag; + } + if (visitBounds.empty()) + { + internal::parseAssert(tag == "DIMENSIONS"); + this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + this->DataFile->Stream >> tag; + } + + internal::parseAssert(tag == "SPACING"); + this->DataFile->Stream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; + if (!visitBounds.empty()) + { + //now with spacing and physical bounds we can back compute the dimensions + dim[0] = static_cast((visitBounds[1] - visitBounds[0]) / spacing[0]); + dim[1] = static_cast((visitBounds[3] - visitBounds[2]) / spacing[1]); + dim[2] = static_cast((visitBounds[5] - visitBounds[4]) / spacing[2]); + } + + this->DataFile->Stream >> tag >> origin[0] >> origin[1] >> origin[2] >> std::ws; + internal::parseAssert(tag == "ORIGIN"); + + this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); + this->DataSet.AddCoordinateSystem( + vtkm::cont::CoordinateSystem("coordinates", dim, origin, spacing)); + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredPointsReader.h b/vtkm/io/VTKStructuredPointsReader.h index 17d786380..2e998f96f 100644 --- a/vtkm/io/VTKStructuredPointsReader.h +++ b/vtkm/io/VTKStructuredPointsReader.h @@ -17,70 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKStructuredPointsReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKStructuredPointsReader : public VTKDataSetReaderBase { public: - explicit VTKStructuredPointsReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKStructuredPointsReader(const char* fileName); + explicit VTKM_CONT VTKStructuredPointsReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_POINTS) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - std::string tag; - - // Read structured points specific meta-data - vtkm::Id3 dim; - vtkm::Vec3f_32 origin, spacing; - - //Two ways the file can describe the dimensions. The proper way is by - //using the DIMENSIONS keyword, but VisIt written VTK files spicify data - //bounds instead, as a FIELD - std::vector visitBounds; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(&visitBounds); - this->DataFile->Stream >> tag; - } - if (visitBounds.empty()) - { - internal::parseAssert(tag == "DIMENSIONS"); - this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - this->DataFile->Stream >> tag; - } - - internal::parseAssert(tag == "SPACING"); - this->DataFile->Stream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; - if (!visitBounds.empty()) - { - //now with spacing and physical bounds we can back compute the dimensions - dim[0] = static_cast((visitBounds[1] - visitBounds[0]) / spacing[0]); - dim[1] = static_cast((visitBounds[3] - visitBounds[2]) / spacing[1]); - dim[2] = static_cast((visitBounds[5] - visitBounds[4]) / spacing[2]); - } - - this->DataFile->Stream >> tag >> origin[0] >> origin[1] >> origin[2] >> std::ws; - internal::parseAssert(tag == "ORIGIN"); - - this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); - this->DataSet.AddCoordinateSystem( - vtkm::cont::CoordinateSystem("coordinates", dim, origin, spacing)); - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKUnstructuredGridReader.cxx b/vtkm/io/VTKUnstructuredGridReader.cxx new file mode 100644 index 000000000..0fb69cacb --- /dev/null +++ b/vtkm/io/VTKUnstructuredGridReader.cxx @@ -0,0 +1,88 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include + +namespace vtkm +{ +namespace io +{ + +VTKUnstructuredGridReader::VTKUnstructuredGridReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKUnstructuredGridReader::VTKUnstructuredGridReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKUnstructuredGridReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_UNSTRUCTURED_GRID) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + std::string tag; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read the points + internal::parseAssert(tag == "POINTS"); + this->ReadPoints(); + + vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); + + // Read the cellset + vtkm::cont::ArrayHandle connectivity; + vtkm::cont::ArrayHandle numIndices; + vtkm::cont::ArrayHandle shapes; + + this->DataFile->Stream >> tag; + internal::parseAssert(tag == "CELLS"); + + this->ReadCells(connectivity, numIndices); + this->ReadShapes(shapes); + + vtkm::cont::ArrayHandle permutation; + vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); + this->SetCellsPermutation(permutation); + + //DRP + if (false) //vtkm::io::internal::IsSingleShape(shapes)) + { + vtkm::cont::CellSetSingleType<> cellSet; + cellSet.Fill( + numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); + this->DataSet.SetCellSet(cellSet); + } + else + { + auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); + vtkm::cont::CellSetExplicit<> cellSet; + cellSet.Fill(numPoints, shapes, connectivity, offsets); + this->DataSet.SetCellSet(cellSet); + } + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} diff --git a/vtkm/io/VTKUnstructuredGridReader.h b/vtkm/io/VTKUnstructuredGridReader.h index bee4e06f8..4d60e1d39 100644 --- a/vtkm/io/VTKUnstructuredGridReader.h +++ b/vtkm/io/VTKUnstructuredGridReader.h @@ -17,77 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKUnstructuredGridReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKUnstructuredGridReader : public VTKDataSetReaderBase { public: - explicit VTKUnstructuredGridReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKUnstructuredGridReader(const char* fileName); + explicit VTKM_CONT VTKUnstructuredGridReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_UNSTRUCTURED_GRID) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - std::string tag; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read the points - internal::parseAssert(tag == "POINTS"); - this->ReadPoints(); - - vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); - - // Read the cellset - vtkm::cont::ArrayHandle connectivity; - vtkm::cont::ArrayHandle numIndices; - vtkm::cont::ArrayHandle shapes; - - this->DataFile->Stream >> tag; - internal::parseAssert(tag == "CELLS"); - - this->ReadCells(connectivity, numIndices); - this->ReadShapes(shapes); - - vtkm::cont::ArrayHandle permutation; - vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); - this->SetCellsPermutation(permutation); - - //DRP - if (false) //vtkm::io::internal::IsSingleShape(shapes)) - { - vtkm::cont::CellSetSingleType<> cellSet; - cellSet.Fill( - numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); - this->DataSet.SetCellSet(cellSet); - } - else - { - auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); - vtkm::cont::CellSetExplicit<> cellSet; - cellSet.Fill(numPoints, shapes, connectivity, offsets); - this->DataSet.SetCellSet(cellSet); - } - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io From 8657a9b3c88c5223ad02ebc05799f3a648dcf457 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 13:29:04 -0600 Subject: [PATCH 25/27] Update VTKDataSetWriter to compile into vtkm_io --- examples/clipping/CMakeLists.txt | 2 +- examples/hello_worklet/CMakeLists.txt | 2 +- examples/tetrahedra/CMakeLists.txt | 4 +- vtkm/io/CMakeLists.txt | 3 +- vtkm/io/VTKDataSetReaderBase.h | 3 - vtkm/io/VTKDataSetWriter.cxx | 407 ++++++++++++++++++++++++++ vtkm/io/VTKDataSetWriter.h | 388 +----------------------- vtkm/io/internal/VTKDataSetTypes.h | 4 + 8 files changed, 422 insertions(+), 391 deletions(-) create mode 100644 vtkm/io/VTKDataSetWriter.cxx diff --git a/examples/clipping/CMakeLists.txt b/examples/clipping/CMakeLists.txt index 6f26353ce..46bc5bf18 100644 --- a/examples/clipping/CMakeLists.txt +++ b/examples/clipping/CMakeLists.txt @@ -14,7 +14,7 @@ project(Clipping CXX) find_package(VTKm REQUIRED QUIET) add_executable(Clipping Clipping.cxx) -target_link_libraries(Clipping PRIVATE vtkm_filter) +target_link_libraries(Clipping PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Clipping DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS diff --git a/examples/hello_worklet/CMakeLists.txt b/examples/hello_worklet/CMakeLists.txt index 806f69b57..eff9dfcae 100644 --- a/examples/hello_worklet/CMakeLists.txt +++ b/examples/hello_worklet/CMakeLists.txt @@ -14,7 +14,7 @@ project(HelloWorklet CXX) find_package(VTKm REQUIRED QUIET) add_executable(HelloWorklet HelloWorklet.cxx) -target_link_libraries(HelloWorklet PRIVATE vtkm_filter) +target_link_libraries(HelloWorklet PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(HelloWorklet DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS diff --git a/examples/tetrahedra/CMakeLists.txt b/examples/tetrahedra/CMakeLists.txt index c894daee5..7bd8a4e67 100644 --- a/examples/tetrahedra/CMakeLists.txt +++ b/examples/tetrahedra/CMakeLists.txt @@ -14,10 +14,10 @@ project(Tetrahedra CXX) find_package(VTKm REQUIRED QUIET) add_executable(Tetrahedralize Tetrahedralize.cxx) -target_link_libraries(Tetrahedralize PRIVATE vtkm_filter) +target_link_libraries(Tetrahedralize PRIVATE vtkm_filter vtkm_io) add_executable(Triangulate Triangulate.cxx) -target_link_libraries(Triangulate PRIVATE vtkm_filter) +target_link_libraries(Triangulate PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Tetrahedralize Triangulate DROP_UNUSED_SYMBOLS diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index a8d63aea4..7b3e48d84 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -18,12 +18,12 @@ set(headers PixelTypes.h VTKDataSetReader.h VTKDataSetReaderBase.h + VTKDataSetWriter.h VTKPolyDataReader.h VTKRectilinearGridReader.h VTKStructuredGridReader.h VTKStructuredPointsReader.h VTKUnstructuredGridReader.h - VTKDataSetWriter.h ) set(template_sources @@ -37,6 +37,7 @@ set(sources EncodePNG.cxx VTKDataSetReader.cxx VTKDataSetReaderBase.cxx + VTKDataSetWriter.cxx VTKPolyDataReader.cxx VTKRectilinearGridReader.cxx VTKStructuredGridReader.cxx diff --git a/vtkm/io/VTKDataSetReaderBase.h b/vtkm/io/VTKDataSetReaderBase.h index 62a935315..245d46eb0 100644 --- a/vtkm/io/VTKDataSetReaderBase.h +++ b/vtkm/io/VTKDataSetReaderBase.h @@ -275,7 +275,4 @@ protected: } } // vtkm::io -VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::ColorChannel8) -VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::DummyBitType) - #endif // vtk_m_io_VTKDataSetReaderBase_h diff --git a/vtkm/io/VTKDataSetWriter.cxx b/vtkm/io/VTKDataSetWriter.cxx new file mode 100644 index 000000000..6f3eac1a3 --- /dev/null +++ b/vtkm/io/VTKDataSetWriter.cxx @@ -0,0 +1,407 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct OutputPointsFunctor +{ +private: + std::ostream& out; + + template + VTKM_CONT void Output(const PortalType& portal) const + { + for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) + { + const int VTKDims = 3; // VTK files always require 3 dims for points + + using ValueType = typename PortalType::ValueType; + using VecType = typename vtkm::VecTraits; + + const ValueType& value = portal.Get(index); + + vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); + for (vtkm::IdComponent c = 0; c < numComponents && c < VTKDims; c++) + { + out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); + } + for (vtkm::IdComponent c = numComponents; c < VTKDims; c++) + { + out << " 0"; + } + out << '\n'; + } + } + +public: + VTKM_CONT + OutputPointsFunctor(std::ostream& o) + : out(o) + { + } + + template + VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const + { + this->Output(array.ReadPortal()); + } +}; + +struct OutputFieldFunctor +{ +private: + std::ostream& out; + + template + VTKM_CONT void Output(const PortalType& portal) const + { + for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) + { + using ValueType = typename PortalType::ValueType; + using VecType = typename vtkm::VecTraits; + + const ValueType& value = portal.Get(index); + + vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); + for (vtkm::IdComponent c = 0; c < numComponents; c++) + { + out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); + } + out << '\n'; + } + } + +public: + VTKM_CONT + OutputFieldFunctor(std::ostream& o) + : out(o) + { + } + + template + VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const + { + this->Output(array.ReadPortal()); + } +}; + +class GetDataTypeName +{ +public: + GetDataTypeName(std::string& name) + : Name(&name) + { + } + + template + void operator()(const ArrayHandleType&) const + { + using DataType = typename vtkm::VecTraits::ComponentType; + *this->Name = vtkm::io::internal::DataTypeName::Name(); + } + +private: + std::string* Name; +}; + +void WritePoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + ///\todo: support other coordinate systems + int cindex = 0; + auto cdata = dataSet.GetCoordinateSystem(cindex).GetData(); + + vtkm::Id npoints = cdata.GetNumberOfValues(); + out << "POINTS " << npoints << " " << vtkm::io::internal::DataTypeName::Name() + << " " << '\n'; + + OutputPointsFunctor{ out }(cdata); +} + +template +void WriteExplicitCells(std::ostream& out, const CellSetType& cellSet) +{ + vtkm::Id nCells = cellSet.GetNumberOfCells(); + + vtkm::Id conn_length = 0; + for (vtkm::Id i = 0; i < nCells; ++i) + { + conn_length += 1 + cellSet.GetNumberOfPointsInCell(i); + } + + out << "CELLS " << nCells << " " << conn_length << '\n'; + + for (vtkm::Id i = 0; i < nCells; ++i) + { + vtkm::cont::ArrayHandle ids; + vtkm::Id nids = cellSet.GetNumberOfPointsInCell(i); + cellSet.GetIndices(i, ids); + out << nids; + auto IdPortal = ids.ReadPortal(); + for (int j = 0; j < nids; ++j) + out << " " << IdPortal.Get(j); + out << '\n'; + } + + out << "CELL_TYPES " << nCells << '\n'; + for (vtkm::Id i = 0; i < nCells; ++i) + { + vtkm::Id shape = cellSet.GetCellShape(i); + out << shape << '\n'; + } +} + +void WriteVertexCells(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + vtkm::Id nCells = dataSet.GetCoordinateSystem(0).GetNumberOfPoints(); + + out << "CELLS " << nCells << " " << nCells * 2 << '\n'; + for (int i = 0; i < nCells; i++) + { + out << "1 " << i << '\n'; + } + out << "CELL_TYPES " << nCells << '\n'; + for (int i = 0; i < nCells; i++) + { + out << vtkm::CELL_SHAPE_VERTEX << '\n'; + } +} + +void WritePointFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + bool wrote_header = false; + for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) + { + const vtkm::cont::Field field = dataSet.GetField(f); + + if (field.GetAssociation() != vtkm::cont::Field::Association::POINTS) + { + continue; + } + + vtkm::Id npoints = field.GetNumberOfValues(); + int ncomps = field.GetData().GetNumberOfComponents(); + if (ncomps > 4) + { + continue; + } + + if (!wrote_header) + { + out << "POINT_DATA " << npoints << '\n'; + wrote_header = true; + } + + std::string typeName; + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + GetDataTypeName(typeName)); + std::string name = field.GetName(); + for (auto& c : name) + { + if (std::isspace(c)) + { + c = '_'; + } + } + out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; + out << "LOOKUP_TABLE default" << '\n'; + + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + OutputFieldFunctor(out)); + } +} + +void WriteCellFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + bool wrote_header = false; + for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) + { + const vtkm::cont::Field field = dataSet.GetField(f); + if (!field.IsFieldCell()) + { + continue; + } + + + vtkm::Id ncells = field.GetNumberOfValues(); + int ncomps = field.GetData().GetNumberOfComponents(); + if (ncomps > 4) + continue; + + if (!wrote_header) + { + out << "CELL_DATA " << ncells << '\n'; + wrote_header = true; + } + + std::string typeName; + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + GetDataTypeName(typeName)); + + std::string name = field.GetName(); + for (auto& c : name) + { + if (std::isspace(c)) + { + c = '_'; + } + } + + out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; + out << "LOOKUP_TABLE default" << '\n'; + + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + OutputFieldFunctor(out)); + } +} + +void WriteDataSetAsPoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + out << "DATASET UNSTRUCTURED_GRID" << '\n'; + WritePoints(out, dataSet); + WriteVertexCells(out, dataSet); +} + +template +void WriteDataSetAsUnstructured(std::ostream& out, + const vtkm::cont::DataSet& dataSet, + const CellSetType& cellSet) +{ + out << "DATASET UNSTRUCTURED_GRID" << '\n'; + WritePoints(out, dataSet); + WriteExplicitCells(out, cellSet); +} + +template +void WriteDataSetAsStructured(std::ostream& out, + const vtkm::cont::DataSet& dataSet, + const vtkm::cont::CellSetStructured& cellSet) +{ + ///\todo: support uniform/rectilinear + out << "DATASET STRUCTURED_GRID" << '\n'; + + auto pointDimensions = cellSet.GetPointDimensions(); + using VTraits = vtkm::VecTraits; + + out << "DIMENSIONS "; + out << VTraits::GetComponent(pointDimensions, 0) << " "; + out << (DIM > 1 ? VTraits::GetComponent(pointDimensions, 1) : 1) << " "; + out << (DIM > 2 ? VTraits::GetComponent(pointDimensions, 2) : 1) << " "; + + WritePoints(out, dataSet); +} + +void Write(std::ostream& out, const vtkm::cont::DataSet& dataSet, bool just_points = false) +{ + // The Paraview parser cannot handle scientific notation: + out << std::fixed; + out << "# vtk DataFile Version 3.0" << '\n'; + out << "vtk output" << '\n'; + out << "ASCII" << '\n'; + + if (just_points) + { + WriteDataSetAsPoints(out, dataSet); + WritePointFields(out, dataSet); + } + else + { + vtkm::cont::DynamicCellSet cellSet = dataSet.GetCellSet(); + if (cellSet.IsType>()) + { + WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + // these function just like explicit cell sets + WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); + } + else + { + throw vtkm::cont::ErrorBadType("Could not determine type to write out."); + } + + WritePointFields(out, dataSet); + WriteCellFields(out, dataSet); + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +VTKDataSetWriter::VTKDataSetWriter(const char* fileName) + : FileName(fileName) +{ +} + +VTKDataSetWriter::VTKDataSetWriter(const std::string& fileName) + : FileName(fileName) +{ +} + +void VTKDataSetWriter::WriteDataSet(const vtkm::cont::DataSet& dataSet, bool just_points) const +{ + if (dataSet.GetNumberOfCoordinateSystems() < 1) + { + throw vtkm::cont::ErrorBadValue( + "DataSet has no coordinate system, which is not supported by VTK file format."); + } + try + { + std::ofstream fileStream(this->FileName.c_str(), std::fstream::trunc); + Write(fileStream, dataSet, just_points); + fileStream.close(); + } + catch (std::ofstream::failure& error) + { + throw vtkm::io::ErrorIO(error.what()); + } +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetWriter.h b/vtkm/io/VTKDataSetWriter.h index 946ac5c3d..e666f0b7d 100644 --- a/vtkm/io/VTKDataSetWriter.h +++ b/vtkm/io/VTKDataSetWriter.h @@ -10,400 +10,22 @@ #ifndef vtk_m_io_DataSetWriter_h #define vtk_m_io_DataSetWriter_h -#include -#include - -#include -#include -#include #include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include +#include namespace vtkm { namespace io { -namespace detail -{ -struct OutputPointsFunctor -{ -private: - std::ostream& out; - - template - VTKM_CONT void Output(const PortalType& portal) const - { - for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) - { - const int VTKDims = 3; // VTK files always require 3 dims for points - - using ValueType = typename PortalType::ValueType; - using VecType = typename vtkm::VecTraits; - - const ValueType& value = portal.Get(index); - - vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); - for (vtkm::IdComponent c = 0; c < numComponents && c < VTKDims; c++) - { - out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); - } - for (vtkm::IdComponent c = numComponents; c < VTKDims; c++) - { - out << " 0"; - } - out << '\n'; - } - } - -public: - VTKM_CONT - OutputPointsFunctor(std::ostream& o) - : out(o) - { - } - - template - VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const - { - this->Output(array.ReadPortal()); - } -}; - -struct OutputFieldFunctor -{ -private: - std::ostream& out; - - template - VTKM_CONT void Output(const PortalType& portal) const - { - for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) - { - using ValueType = typename PortalType::ValueType; - using VecType = typename vtkm::VecTraits; - - const ValueType& value = portal.Get(index); - - vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); - for (vtkm::IdComponent c = 0; c < numComponents; c++) - { - out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); - } - out << '\n'; - } - } - -public: - VTKM_CONT - OutputFieldFunctor(std::ostream& o) - : out(o) - { - } - - template - VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const - { - this->Output(array.ReadPortal()); - } -}; - -class GetDataTypeName +struct VTKM_IO_EXPORT VTKDataSetWriter { public: - GetDataTypeName(std::string& name) - : Name(&name) - { - } + VTKM_CONT VTKDataSetWriter(const char* fileName); + VTKM_CONT VTKDataSetWriter(const std::string& fileName); - template - void operator()(const ArrayHandleType&) const - { - using DataType = typename vtkm::VecTraits::ComponentType; - *this->Name = vtkm::io::internal::DataTypeName::Name(); - } - -private: - std::string* Name; -}; - -} // namespace detail - -struct VTKDataSetWriter -{ -private: - static void WritePoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - ///\todo: support other coordinate systems - int cindex = 0; - auto cdata = dataSet.GetCoordinateSystem(cindex).GetData(); - - vtkm::Id npoints = cdata.GetNumberOfValues(); - out << "POINTS " << npoints << " " - << vtkm::io::internal::DataTypeName::Name() << " " << '\n'; - - detail::OutputPointsFunctor{ out }(cdata); - } - - template - static void WriteExplicitCells(std::ostream& out, const CellSetType& cellSet) - { - vtkm::Id nCells = cellSet.GetNumberOfCells(); - - vtkm::Id conn_length = 0; - for (vtkm::Id i = 0; i < nCells; ++i) - { - conn_length += 1 + cellSet.GetNumberOfPointsInCell(i); - } - - out << "CELLS " << nCells << " " << conn_length << '\n'; - - for (vtkm::Id i = 0; i < nCells; ++i) - { - vtkm::cont::ArrayHandle ids; - vtkm::Id nids = cellSet.GetNumberOfPointsInCell(i); - cellSet.GetIndices(i, ids); - out << nids; - auto IdPortal = ids.ReadPortal(); - for (int j = 0; j < nids; ++j) - out << " " << IdPortal.Get(j); - out << '\n'; - } - - out << "CELL_TYPES " << nCells << '\n'; - for (vtkm::Id i = 0; i < nCells; ++i) - { - vtkm::Id shape = cellSet.GetCellShape(i); - out << shape << '\n'; - } - } - - static void WriteVertexCells(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - vtkm::Id nCells = dataSet.GetCoordinateSystem(0).GetNumberOfPoints(); - - out << "CELLS " << nCells << " " << nCells * 2 << '\n'; - for (int i = 0; i < nCells; i++) - { - out << "1 " << i << '\n'; - } - out << "CELL_TYPES " << nCells << '\n'; - for (int i = 0; i < nCells; i++) - { - out << vtkm::CELL_SHAPE_VERTEX << '\n'; - } - } - - static void WritePointFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - bool wrote_header = false; - for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) - { - const vtkm::cont::Field field = dataSet.GetField(f); - - if (field.GetAssociation() != vtkm::cont::Field::Association::POINTS) - { - continue; - } - - vtkm::Id npoints = field.GetNumberOfValues(); - int ncomps = field.GetData().GetNumberOfComponents(); - if (ncomps > 4) - { - continue; - } - - if (!wrote_header) - { - out << "POINT_DATA " << npoints << '\n'; - wrote_header = true; - } - - std::string typeName; - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::GetDataTypeName(typeName)); - std::string name = field.GetName(); - for (auto& c : name) - { - if (std::isspace(c)) - { - c = '_'; - } - } - out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; - out << "LOOKUP_TABLE default" << '\n'; - - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::OutputFieldFunctor(out)); - } - } - - static void WriteCellFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - bool wrote_header = false; - for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) - { - const vtkm::cont::Field field = dataSet.GetField(f); - if (!field.IsFieldCell()) - { - continue; - } - - - vtkm::Id ncells = field.GetNumberOfValues(); - int ncomps = field.GetData().GetNumberOfComponents(); - if (ncomps > 4) - continue; - - if (!wrote_header) - { - out << "CELL_DATA " << ncells << '\n'; - wrote_header = true; - } - - std::string typeName; - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::GetDataTypeName(typeName)); - - std::string name = field.GetName(); - for (auto& c : name) - { - if (std::isspace(c)) - { - c = '_'; - } - } - - out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; - out << "LOOKUP_TABLE default" << '\n'; - - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::OutputFieldFunctor(out)); - } - } - - static void WriteDataSetAsPoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - out << "DATASET UNSTRUCTURED_GRID" << '\n'; - WritePoints(out, dataSet); - WriteVertexCells(out, dataSet); - } - - template - static void WriteDataSetAsUnstructured(std::ostream& out, - const vtkm::cont::DataSet& dataSet, - const CellSetType& cellSet) - { - out << "DATASET UNSTRUCTURED_GRID" << '\n'; - WritePoints(out, dataSet); - WriteExplicitCells(out, cellSet); - } - - template - static void WriteDataSetAsStructured(std::ostream& out, - const vtkm::cont::DataSet& dataSet, - const vtkm::cont::CellSetStructured& cellSet) - { - ///\todo: support uniform/rectilinear - out << "DATASET STRUCTURED_GRID" << '\n'; - - auto pointDimensions = cellSet.GetPointDimensions(); - using VTraits = vtkm::VecTraits; - - out << "DIMENSIONS "; - out << VTraits::GetComponent(pointDimensions, 0) << " "; - out << (DIM > 1 ? VTraits::GetComponent(pointDimensions, 1) : 1) << " "; - out << (DIM > 2 ? VTraits::GetComponent(pointDimensions, 2) : 1) << " "; - - WritePoints(out, dataSet); - } - - static void Write(std::ostream& out, const vtkm::cont::DataSet& dataSet, bool just_points = false) - { - // The Paraview parser cannot handle scientific notation: - out << std::fixed; - out << "# vtk DataFile Version 3.0" << '\n'; - out << "vtk output" << '\n'; - out << "ASCII" << '\n'; - - if (just_points) - { - WriteDataSetAsPoints(out, dataSet); - WritePointFields(out, dataSet); - } - else - { - vtkm::cont::DynamicCellSet cellSet = dataSet.GetCellSet(); - if (cellSet.IsType>()) - { - WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - // these function just like explicit cell sets - WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); - } - else - { - throw vtkm::cont::ErrorBadType("Could not determine type to write out."); - } - - WritePointFields(out, dataSet); - WriteCellFields(out, dataSet); - } - } - -public: - VTKM_CONT - explicit VTKDataSetWriter(const std::string& filename) - : FileName(filename) - { - } - - VTKM_CONT - void WriteDataSet(const vtkm::cont::DataSet& dataSet, bool just_points = false) const - { - if (dataSet.GetNumberOfCoordinateSystems() < 1) - { - throw vtkm::cont::ErrorBadValue( - "DataSet has no coordinate system, which is not supported by VTK file format."); - } - try - { - std::ofstream fileStream(this->FileName.c_str(), std::fstream::trunc); - this->Write(fileStream, dataSet, just_points); - fileStream.close(); - } - catch (std::ofstream::failure& error) - { - throw vtkm::io::ErrorIO(error.what()); - } - } + VTKM_CONT void WriteDataSet(const vtkm::cont::DataSet& dataSet, bool just_points = false) const; private: std::string FileName; diff --git a/vtkm/io/internal/VTKDataSetTypes.h b/vtkm/io/internal/VTKDataSetTypes.h index 8210ff197..30e9ece20 100644 --- a/vtkm/io/internal/VTKDataSetTypes.h +++ b/vtkm/io/internal/VTKDataSetTypes.h @@ -11,6 +11,7 @@ #define vtk_m_io_internal_VTKDataSetTypes_h #include +#include #include #include @@ -238,4 +239,7 @@ inline void SelectTypeAndCall(DataType dtype, } } // namespace vtkm::io::internal +VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::ColorChannel8) +VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::DummyBitType) + #endif // vtk_m_io_internal_VTKDataSetTypes_h From dce576bd11a619fa9afed360516c2593989c722b Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 14:31:14 -0600 Subject: [PATCH 26/27] Add BOVDataSetReader implementation to vtkm_io --- docs/changelog/vtk-io-in-library.md | 5 +- vtkm/io/BOVDataSetReader.cxx | 254 +++++++++++++++++++ vtkm/io/BOVDataSetReader.h | 223 +--------------- vtkm/io/CMakeLists.txt | 1 + vtkm/io/testing/UnitTestBOVDataSetReader.cxx | 1 + 5 files changed, 266 insertions(+), 218 deletions(-) create mode 100644 vtkm/io/BOVDataSetReader.cxx diff --git a/docs/changelog/vtk-io-in-library.md b/docs/changelog/vtk-io-in-library.md index d6616813e..37ed2f4f9 100644 --- a/docs/changelog/vtk-io-in-library.md +++ b/docs/changelog/vtk-io-in-library.md @@ -6,7 +6,8 @@ code into libraries. At this point, there is no reason why the VTK file reader/writer should be any different. Thus, `VTKDataSetReader`, `VTKDataSetWriter`, and several supporting -classes are now compiled into the `vtk_io` library. +classes are now compiled into the `vtkm_io` library. Also similarly updated +`BOVDataSetReader` for good measure. -As a side effect, code using VTK-m will need to link to `vtk_io` if they +As a side effect, code using VTK-m will need to link to `vtkm_io` if they are using any readers or writers. diff --git a/vtkm/io/BOVDataSetReader.cxx b/vtkm/io/BOVDataSetReader.cxx new file mode 100644 index 000000000..d66498692 --- /dev/null +++ b/vtkm/io/BOVDataSetReader.cxx @@ -0,0 +1,254 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include +#include +#include + +#include + +namespace +{ + +enum class DataFormat +{ + ByteData, + ShortData, + IntegerData, + FloatData, + DoubleData +}; + +template +void ReadBuffer(const std::string& fName, const vtkm::Id& sz, std::vector& buff) +{ + FILE* fp = fopen(fName.c_str(), "rb"); + size_t readSize = static_cast(sz); + if (fp == nullptr) + { + throw vtkm::io::ErrorIO("Unable to open data file: " + fName); + } + buff.resize(readSize); + size_t nread = fread(&buff[0], sizeof(T), readSize, fp); + if (nread != readSize) + { + throw vtkm::io::ErrorIO("Data file read failed: " + fName); + } + fclose(fp); +} + +template +void ReadScalar(const std::string& fName, const vtkm::Id& nTuples, vtkm::cont::ArrayHandle& var) +{ + std::vector buff; + ReadBuffer(fName, nTuples, buff); + var.Allocate(nTuples); + auto writePortal = var.WritePortal(); + for (vtkm::Id i = 0; i < nTuples; i++) + { + writePortal.Set(i, buff[static_cast(i)]); + } +} + +template +void ReadVector(const std::string& fName, + const vtkm::Id& nTuples, + vtkm::cont::ArrayHandle>& var) +{ + std::vector buff; + ReadBuffer(fName, nTuples * 3, buff); + + var.Allocate(nTuples); + vtkm::Vec v; + auto writePortal = var.WritePortal(); + for (vtkm::Id i = 0; i < nTuples; i++) + { + v[0] = buff[static_cast(i * 3 + 0)]; + v[1] = buff[static_cast(i * 3 + 1)]; + v[2] = buff[static_cast(i * 3 + 2)]; + writePortal.Set(i, v); + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +BOVDataSetReader::BOVDataSetReader(const char* fileName) + : FileName(fileName) + , Loaded(false) + , DataSet() +{ +} + +BOVDataSetReader::BOVDataSetReader(const std::string& fileName) + : FileName(fileName) + , Loaded(false) + , DataSet() +{ +} + +const vtkm::cont::DataSet& BOVDataSetReader::ReadDataSet() +{ + try + { + this->LoadFile(); + } + catch (std::ifstream::failure& e) + { + std::string message("IO Error: "); + throw vtkm::io::ErrorIO(message + e.what()); + } + return this->DataSet; +} + +void BOVDataSetReader::LoadFile() +{ + if (this->Loaded) + return; + + std::ifstream stream(this->FileName); + if (stream.fail()) + throw vtkm::io::ErrorIO("Failed to open file: " + this->FileName); + + DataFormat dataFormat = DataFormat::ByteData; + std::string bovFile, line, token, options, variableName; + vtkm::Id numComponents = 1; + vtkm::Id3 dim; + vtkm::Vec3f origin(0, 0, 0); + vtkm::Vec3f spacing(1, 1, 1); + bool spacingSet = false; + + while (stream.good()) + { + std::getline(stream, line); + if (line.size() == 0 || line[0] == '#') + continue; + //std::cout<<"::"<> bovFile >> std::ws; + } + else if (token.find("DATA") != std::string::npos && token.find("SIZE") != std::string::npos) + { + strStream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + } + else if (token.find("BRICK") != std::string::npos && token.find("ORIGIN") != std::string::npos) + { + strStream >> origin[0] >> origin[1] >> origin[2] >> std::ws; + } + + //DRP + else if (token.find("BRICK") != std::string::npos && token.find("SIZE") != std::string::npos) + { + strStream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; + spacingSet = true; + } + else if (token.find("DATA") != std::string::npos && token.find("FORMAT") != std::string::npos) + { + std::string opt; + strStream >> opt >> std::ws; + if (opt.find("FLOAT") != std::string::npos || opt.find("REAL") != std::string::npos) + dataFormat = DataFormat::FloatData; + else if (opt.find("DOUBLE") != std::string::npos) + dataFormat = DataFormat::DoubleData; + else + throw vtkm::io::ErrorIO("Unsupported data type: " + token); + } + else if (token.find("DATA") != std::string::npos && + token.find("COMPONENTS") != std::string::npos) + { + strStream >> numComponents >> std::ws; + if (numComponents != 1 && numComponents != 3) + throw vtkm::io::ErrorIO("Unsupported number of components"); + } + else if (token.find("VARIABLE") != std::string::npos && + token.find("PALETTE") == std::string::npos) + { + strStream >> variableName >> std::ws; + if (variableName[0] == '"') + variableName = variableName.substr(1, variableName.size() - 2); + } + } + + if (spacingSet) + { + spacing[0] = (spacing[0]) / static_cast(dim[0] - 1); + spacing[1] = (spacing[1]) / static_cast(dim[1] - 1); + spacing[2] = (spacing[2]) / static_cast(dim[2] - 1); + } + + std::string fullPathDataFile; + std::size_t pos = FileName.rfind("/"); + if (pos != std::string::npos) + { + std::string baseDir; + baseDir = this->FileName.substr(0, pos); + fullPathDataFile = baseDir + "/" + bovFile; + } + else + fullPathDataFile = bovFile; + + + vtkm::cont::DataSetBuilderUniform dataSetBuilder; + vtkm::cont::DataSetFieldAdd dsf; + this->DataSet = dataSetBuilder.Create(dim, origin, spacing); + + vtkm::Id numTuples = dim[0] * dim[1] * dim[2]; + if (numComponents == 1) + { + if (dataFormat == DataFormat::FloatData) + { + vtkm::cont::ArrayHandle var; + ReadScalar(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + else if (dataFormat == DataFormat::DoubleData) + { + vtkm::cont::ArrayHandle var; + ReadScalar(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + } + else if (numComponents == 3) + { + if (dataFormat == DataFormat::FloatData) + { + vtkm::cont::ArrayHandle var; + ReadVector(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + else if (dataFormat == DataFormat::DoubleData) + { + vtkm::cont::ArrayHandle var; + ReadVector(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + } + + this->Loaded = true; +} +} +} // namespace vtkm::io diff --git a/vtkm/io/BOVDataSetReader.h b/vtkm/io/BOVDataSetReader.h index c8981a800..dbd2147c5 100644 --- a/vtkm/io/BOVDataSetReader.h +++ b/vtkm/io/BOVDataSetReader.h @@ -10,234 +10,25 @@ #ifndef vtk_m_io_BOVDataSetReader_h #define vtk_m_io_BOVDataSetReader_h -#include #include -#include -#include -#include + +#include namespace vtkm { namespace io { -class BOVDataSetReader +class VTKM_IO_EXPORT BOVDataSetReader { public: - BOVDataSetReader(const char* fileName) - : FileName(fileName) - , Loaded(false) - , DataSet() - { - } - BOVDataSetReader(const std::string& fileName) - : FileName(fileName) - , Loaded(false) - , DataSet() - { - } + VTKM_CONT BOVDataSetReader(const char* fileName); + VTKM_CONT BOVDataSetReader(const std::string& fileName); - const vtkm::cont::DataSet& ReadDataSet() - { - try - { - LoadFile(); - } - catch (std::ifstream::failure& e) - { - std::string message("IO Error: "); - throw vtkm::io::ErrorIO(message + e.what()); - } - return this->DataSet; - } + VTKM_CONT const vtkm::cont::DataSet& ReadDataSet(); private: - using DataFormat = enum { ByteData, ShortData, IntegerData, FloatData, DoubleData }; - - void LoadFile() - { - if (this->Loaded) - return; - - std::ifstream stream(this->FileName); - if (stream.fail()) - throw vtkm::io::ErrorIO("Failed to open file: " + this->FileName); - - DataFormat dataFormat = ByteData; - std::string bovFile, line, token, options, variableName; - vtkm::Id numComponents = 1; - vtkm::Id3 dim; - vtkm::Vec3f origin(0, 0, 0); - vtkm::Vec3f spacing(1, 1, 1); - bool spacingSet = false; - - while (stream.good()) - { - std::getline(stream, line); - if (line.size() == 0 || line[0] == '#') - continue; - //std::cout<<"::"<> bovFile >> std::ws; - } - else if (token.find("DATA") != std::string::npos && token.find("SIZE") != std::string::npos) - { - strStream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - } - else if (token.find("BRICK") != std::string::npos && - token.find("ORIGIN") != std::string::npos) - { - strStream >> origin[0] >> origin[1] >> origin[2] >> std::ws; - } - - //DRP - else if (token.find("BRICK") != std::string::npos && token.find("SIZE") != std::string::npos) - { - strStream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; - spacingSet = true; - } - else if (token.find("DATA") != std::string::npos && token.find("FORMAT") != std::string::npos) - { - std::string opt; - strStream >> opt >> std::ws; - if (opt.find("FLOAT") != std::string::npos || opt.find("REAL") != std::string::npos) - dataFormat = FloatData; - else if (opt.find("DOUBLE") != std::string::npos) - dataFormat = DoubleData; - else - throw vtkm::io::ErrorIO("Unsupported data type: " + token); - } - else if (token.find("DATA") != std::string::npos && - token.find("COMPONENTS") != std::string::npos) - { - strStream >> numComponents >> std::ws; - if (numComponents != 1 && numComponents != 3) - throw vtkm::io::ErrorIO("Unsupported number of components"); - } - else if (token.find("VARIABLE") != std::string::npos && - token.find("PALETTE") == std::string::npos) - { - strStream >> variableName >> std::ws; - if (variableName[0] == '"') - variableName = variableName.substr(1, variableName.size() - 2); - } - } - - if (spacingSet) - { - spacing[0] = (spacing[0]) / static_cast(dim[0] - 1); - spacing[1] = (spacing[1]) / static_cast(dim[1] - 1); - spacing[2] = (spacing[2]) / static_cast(dim[2] - 1); - } - - std::string fullPathDataFile; - std::size_t pos = FileName.rfind("/"); - if (pos != std::string::npos) - { - std::string baseDir; - baseDir = this->FileName.substr(0, pos); - fullPathDataFile = baseDir + "/" + bovFile; - } - else - fullPathDataFile = bovFile; - - - vtkm::cont::DataSetBuilderUniform dataSetBuilder; - vtkm::cont::DataSetFieldAdd dsf; - this->DataSet = dataSetBuilder.Create(dim, origin, spacing); - - vtkm::Id numTuples = dim[0] * dim[1] * dim[2]; - if (numComponents == 1) - { - if (dataFormat == FloatData) - { - vtkm::cont::ArrayHandle var; - ReadScalar(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - else if (dataFormat == DoubleData) - { - vtkm::cont::ArrayHandle var; - ReadScalar(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - } - else if (numComponents == 3) - { - if (dataFormat == FloatData) - { - vtkm::cont::ArrayHandle var; - ReadVector(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - else if (dataFormat == DoubleData) - { - vtkm::cont::ArrayHandle var; - ReadVector(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - } - - this->Loaded = true; - } - - template - void ReadBuffer(const std::string& fName, const vtkm::Id& sz, std::vector& buff) - { - FILE* fp = fopen(fName.c_str(), "rb"); - size_t readSize = static_cast(sz); - if (fp == nullptr) - throw vtkm::io::ErrorIO("Unable to open data file: " + fName); - buff.resize(readSize); - size_t nread = fread(&buff[0], sizeof(T), readSize, fp); - if (nread != readSize) - throw vtkm::io::ErrorIO("Data file read failed: " + fName); - fclose(fp); - } - - template - void ReadScalar(const std::string& fName, - const vtkm::Id& nTuples, - vtkm::cont::ArrayHandle& var) - { - std::vector buff; - ReadBuffer(fName, nTuples, buff); - var.Allocate(nTuples); - auto writePortal = var.WritePortal(); - for (vtkm::Id i = 0; i < nTuples; i++) - writePortal.Set(i, buff[(size_t)i]); - } - - template - void ReadVector(const std::string& fName, - const vtkm::Id& nTuples, - vtkm::cont::ArrayHandle>& var) - { - std::vector buff; - ReadBuffer(fName, nTuples * 3, buff); - - var.Allocate(nTuples); - vtkm::Vec v; - auto writePortal = var.WritePortal(); - for (vtkm::Id i = 0; i < nTuples; i++) - { - v[0] = buff[static_cast(i * 3 + 0)]; - v[1] = buff[static_cast(i * 3 + 1)]; - v[2] = buff[static_cast(i * 3 + 2)]; - writePortal.Set(i, v); - } - } + VTKM_CONT void LoadFile(); std::string FileName; bool Loaded; diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index 7b3e48d84..99db550d4 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -33,6 +33,7 @@ set(template_sources ) set(sources + BOVDataSetReader.cxx DecodePNG.cxx EncodePNG.cxx VTKDataSetReader.cxx diff --git a/vtkm/io/testing/UnitTestBOVDataSetReader.cxx b/vtkm/io/testing/UnitTestBOVDataSetReader.cxx index 833d23d85..f1af8c023 100644 --- a/vtkm/io/testing/UnitTestBOVDataSetReader.cxx +++ b/vtkm/io/testing/UnitTestBOVDataSetReader.cxx @@ -11,6 +11,7 @@ #include #include #include +#include namespace { From 05fcda6c7624e710af191460e47958354a91af2e Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 19 May 2020 15:04:08 -0600 Subject: [PATCH 27/27] Compile IO library with device compilers Because the IO classes use ArrayHandle, they have to be compiled with device compilers (even though they do not directly produce any device code). This is a dumb requirement, but it is a requirement nonetheless. --- vtkm/io/CMakeLists.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index 99db550d4..6c9eaeedb 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -33,9 +33,16 @@ set(template_sources ) set(sources - BOVDataSetReader.cxx DecodePNG.cxx EncodePNG.cxx +) + +# TODO: None of these codes actually use a device. Rather, they access ArrayHandle, and we +# currently need to ensure that ArrayHandle is correctly compiled for all devices. This is +# kind of silly, so hopefully sometime in the future you will no longer need to compile for +# devices for ArrayHandle, and this requirement will go away. +set(device_sources + BOVDataSetReader.cxx VTKDataSetReader.cxx VTKDataSetReaderBase.cxx VTKDataSetWriter.cxx @@ -44,7 +51,7 @@ set(sources VTKStructuredGridReader.cxx VTKStructuredPointsReader.cxx VTKUnstructuredGridReader.cxx -) + ) vtkm_declare_headers( ${headers} @@ -54,6 +61,7 @@ vtkm_declare_headers( vtkm_library( NAME vtkm_io SOURCES ${sources} + DEVICE_SOURCES ${device_sources} HEADERS ${headers} TEMPLATE_SOURCES ${template_sources} )