From a2c3c80ce0e8d763cdcd5132e5a3009d615d6292 Mon Sep 17 00:00:00 2001 From: Manish Mathai Date: Fri, 6 Aug 2021 11:59:59 -0700 Subject: [PATCH] Make perlin generation as a `vtkm::source::Source` source Making it a `vtkm::source::Source` makes it easier to use in other benchmarks or tests. --- benchmarking/BenchmarkInSitu.cxx | 220 ++++--------------------------- vtkm/source/CMakeLists.txt | 2 + vtkm/source/PerlinNoise.cxx | 209 +++++++++++++++++++++++++++++ vtkm/source/PerlinNoise.h | 61 +++++++++ 4 files changed, 295 insertions(+), 197 deletions(-) create mode 100644 vtkm/source/PerlinNoise.cxx create mode 100644 vtkm/source/PerlinNoise.h diff --git a/benchmarking/BenchmarkInSitu.cxx b/benchmarking/BenchmarkInSitu.cxx index 5287a6c7b..a35ed1e14 100644 --- a/benchmarking/BenchmarkInSitu.cxx +++ b/benchmarking/BenchmarkInSitu.cxx @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -28,7 +27,6 @@ #include #include -#include #include #include #include @@ -43,6 +41,7 @@ #include #include +#include namespace { @@ -73,163 +72,6 @@ enum class RenderingMode Volume = 3, }; -struct PerlinNoise3DWorklet : public vtkm::worklet::WorkletVisitPointsWithCells -{ - using ControlSignature = void(CellSetIn, FieldInPoint, WholeArrayIn, FieldOut noise); - using ExecutionSignature = void(_2, _3, _4); - - VTKM_CONT PerlinNoise3DWorklet(vtkm::Id repeat) - : Repeat(repeat) - { - } - - // Adapted from https://adrianb.io/2014/08/09/perlinnoise.html - // Archive link: https://web.archive.org/web/20210329174559/https://adrianb.io/2014/08/09/perlinnoise.html - template - VTKM_EXEC void operator()(const PointVecType& pos, const PermsPortal& perms, OutType& noise) const - { - vtkm::Id xi = static_cast(pos[0]) % this->Repeat; - vtkm::Id yi = static_cast(pos[1]) % this->Repeat; - vtkm::Id zi = static_cast(pos[2]) % this->Repeat; - vtkm::FloatDefault xf = pos[0] - xi; - vtkm::FloatDefault yf = pos[1] - yi; - vtkm::FloatDefault zf = pos[2] - zi; - vtkm::FloatDefault u = this->Fade(xf); - vtkm::FloatDefault v = this->Fade(yf); - vtkm::FloatDefault w = this->Fade(zf); - - vtkm::Id aaa, aba, aab, abb, baa, bba, bab, bbb; - aaa = perms[perms[perms[xi] + yi] + zi]; - aba = perms[perms[perms[xi] + this->Increment(yi)] + zi]; - aab = perms[perms[perms[xi] + yi] + this->Increment(zi)]; - abb = perms[perms[perms[xi] + this->Increment(yi)] + this->Increment(zi)]; - baa = perms[perms[perms[this->Increment(xi)] + yi] + zi]; - bba = perms[perms[perms[this->Increment(xi)] + this->Increment(yi)] + zi]; - bab = perms[perms[perms[this->Increment(xi)] + yi] + this->Increment(zi)]; - bbb = perms[perms[perms[this->Increment(xi)] + this->Increment(yi)] + this->Increment(zi)]; - - vtkm::FloatDefault x1, x2, y1, y2; - x1 = vtkm::Lerp(this->Gradient(aaa, xf, yf, zf), this->Gradient(baa, xf - 1, yf, zf), u); - x2 = - vtkm::Lerp(this->Gradient(aba, xf, yf - 1, zf), this->Gradient(bba, xf - 1, yf - 1, zf), u); - y1 = vtkm::Lerp(x1, x2, v); - - x1 = - vtkm::Lerp(this->Gradient(aab, xf, yf, zf - 1), this->Gradient(bab, xf - 1, yf, zf - 1), u); - x2 = vtkm::Lerp( - this->Gradient(abb, xf, yf - 1, zf - 1), this->Gradient(bbb, xf - 1, yf - 1, zf - 1), u); - y2 = vtkm::Lerp(x1, x2, v); - - noise = (vtkm::Lerp(y1, y2, w) + OutType(1.0f)) * OutType(0.5f); - } - - VTKM_EXEC vtkm::FloatDefault Fade(vtkm::FloatDefault t) const - { - return t * t * t * (t * (t * 6 - 15) + 10); - } - - VTKM_EXEC vtkm::Id Increment(vtkm::Id n) const { return (n + 1) % this->Repeat; } - - VTKM_EXEC vtkm::FloatDefault Gradient(vtkm::Id hash, - vtkm::FloatDefault x, - vtkm::FloatDefault y, - vtkm::FloatDefault z) const - { - switch (hash & 0xF) - { - case 0x0: - return x + y; - case 0x1: - return -x + y; - case 0x2: - return x - y; - case 0x3: - return -x - y; - case 0x4: - return x + z; - case 0x5: - return -x + z; - case 0x6: - return x - z; - case 0x7: - return -x - z; - case 0x8: - return y + z; - case 0x9: - return -y + z; - case 0xA: - return y - z; - case 0xB: - return -y - z; - case 0xC: - return y + x; - case 0xD: - return -y + z; - case 0xE: - return y - x; - case 0xF: - return -y - z; - default: - return 0; // never happens - } - } - - vtkm::Id Repeat; -}; - -class PerlinNoise3DGenerator : public vtkm::filter::FilterField -{ -public: - VTKM_CONT PerlinNoise3DGenerator(vtkm::IdComponent tableSize, vtkm::Id seed) - : TableSize(tableSize) - , Seed(seed) - { - this->GeneratePermutations(); - this->SetUseCoordinateSystemAsField(true); - } - - template - VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input, - const FieldType&, - const vtkm::filter::FieldMetadata& fieldMetadata, - vtkm::filter::PolicyBase) - { - vtkm::cont::ArrayHandle noiseArray; - PerlinNoise3DWorklet worklet{ this->TableSize }; - this->Invoke(worklet, input.GetCellSet(), input.GetCoordinateSystem(), this->P, noiseArray); - - return vtkm::filter::CreateResult(input, noiseArray, "PerlinNoise3D", fieldMetadata); - } - -protected: - VTKM_CONT void GeneratePermutations() - { - std::mt19937_64 rng; - rng.seed(this->Seed); - std::uniform_int_distribution distribution(0, this->TableSize - 1); - - vtkm::cont::ArrayHandle perms; - perms.Allocate(this->TableSize); - auto permsPortal = perms.WritePortal(); - for (auto i = 0; i < permsPortal.GetNumberOfValues(); ++i) - { - permsPortal.Set(i, distribution(rng)); - } - this->P.Allocate(2 * this->TableSize); - auto pPortal = this->P.WritePortal(); - for (auto i = 0; i < pPortal.GetNumberOfValues(); ++i) - { - pPortal.Set(i, permsPortal.Get(i % this->TableSize)); - } - } - - -private: - vtkm::IdComponent TableSize; - vtkm::Id Seed; - vtkm::cont::ArrayHandle P; -}; - std::vector ExtractDataSets(const vtkm::cont::PartitionedDataSet& partitions) { return partitions.GetPartitions(); @@ -241,21 +83,16 @@ std::vector ExtractDataSets(vtkm::cont::DataSet& dataSet) return std::vector{ dataSet }; } -void BuildInputDataSet(uint32_t cycle, - bool isStructured, - bool isMultiBlock, - vtkm::Id dims, - vtkm::FloatDefault spacing = 0.1f) +void BuildInputDataSet(uint32_t cycle, bool isStructured, bool isMultiBlock, vtkm::Id dim) { vtkm::cont::PartitionedDataSet partitionedInputDataSet; vtkm::cont::DataSet inputDataSet; - PointScalarsName = "PerlinNoise3D"; - PointVectorsName = "PerlinNoise3DGradient"; + PointScalarsName = "perlinnoise"; + PointVectorsName = "perlinnoisegrad"; // Generate uniform dataset(s) - const vtkm::Id3 dataSetDims{ dims, dims, dims }; - const vtkm::Vec3f dataSetSpacing{ spacing, spacing, spacing }; + const vtkm::Id3 dims{ dim, dim, dim }; if (isMultiBlock) { for (auto i = 0; i < 2; ++i) @@ -264,33 +101,22 @@ void BuildInputDataSet(uint32_t cycle, { for (auto k = 0; k < 2; ++k) { - const vtkm::Vec3f dataSetOrigin{ (dims - 1) * spacing * i, - (dims - 1) * spacing * j, - (dims - 1) * spacing * k }; - vtkm::cont::DataSetBuilderUniform dataSetBuilder; - vtkm::cont::DataSet uniformDataSet = - dataSetBuilder.Create(dataSetDims, dataSetOrigin, dataSetSpacing); - partitionedInputDataSet.AppendPartition(uniformDataSet); + const vtkm::Vec3f origin{ static_cast(i), + static_cast(j), + static_cast(k) }; + const vtkm::source::PerlinNoise noise{ dims, + origin, + static_cast(cycle) }; + const auto dataset = noise.Execute(); + partitionedInputDataSet.AppendPartition(dataset); } } } } else { - const vtkm::Vec3f dataSetOrigin{ 0.0f, 0.0f, 0.0f }; - vtkm::cont::DataSetBuilderUniform dataSetBuilder; - inputDataSet = dataSetBuilder.Create(dataSetDims, dataSetOrigin, dataSetSpacing); - } - - // Generate Perlin Noise point scalar field - PerlinNoise3DGenerator fieldGenerator(dims, cycle); - if (isMultiBlock) - { - partitionedInputDataSet = fieldGenerator.Execute(partitionedInputDataSet); - } - else - { - inputDataSet = fieldGenerator.Execute(inputDataSet); + const vtkm::source::PerlinNoise noise{ dims, static_cast(cycle) }; + inputDataSet = noise.Execute(); } // Generate Perln Noise Gradient point vector field @@ -465,7 +291,7 @@ void BenchContour(::benchmark::State& state) vtkm::cont::Timer inputGenTimer{ device }; inputGenTimer.Start(); - BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim, DEFAULT_SPACING); + BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim); inputGenTimer.Stop(); vtkm::filter::Contour filter; @@ -629,7 +455,7 @@ void BenchStreamlines(::benchmark::State& state) vtkm::cont::Timer inputGenTimer{ device }; inputGenTimer.Start(); - BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim, DEFAULT_SPACING); + BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim); inputGenTimer.Stop(); vtkm::filter::Streamline streamline; @@ -749,7 +575,7 @@ void BenchSlice(::benchmark::State& state) vtkm::cont::Timer inputGenTimer{ device }; inputGenTimer.Start(); - BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim, DEFAULT_SPACING); + BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim); inputGenTimer.Stop(); vtkm::filter::Slice filter; @@ -843,7 +669,7 @@ void BenchMeshRendering(::benchmark::State& state) vtkm::cont::Timer writeTimer{ device }; inputGenTimer.Start(); - BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim, DEFAULT_SPACING); + BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim); inputGenTimer.Stop(); vtkm::cont::Timer totalTimer{ device }; @@ -905,7 +731,7 @@ void BenchVolumeRendering(::benchmark::State& state) vtkm::cont::Timer inputGenTimer{ device }; inputGenTimer.Start(); - BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim, DEFAULT_SPACING); + BuildInputDataSet(cycle, isStructured, isMultiBlock, DataSetDim); inputGenTimer.Stop(); vtkm::cont::Timer totalTimer{ device }; @@ -941,7 +767,7 @@ void BenchVolumeRenderingGenerator(::benchmark::internal::Benchmark* bm) { bm->ArgNames({ "Cycle", "IsMultiBlock" }); - std::vector isMultiBlocks{ false, /*true*/ }; + std::vector isMultiBlocks{ false }; for (uint32_t cycle = 1; cycle <= DEFAULT_NUM_CYCLES; ++cycle) { for (auto& isMultiBlock : isMultiBlocks) @@ -1027,7 +853,7 @@ void ParseBenchmarkOptions(int& argc, char** argv) "size", Arg::Number, " -s, --size \tSpecify dataset dimension and " - "dataset with NxNxN dimensions and 0.1 spacing is created. " + "dataset with NxNxN dimensions is created. " "If not specified, N=128" }); usage.push_back({ IMAGE_SIZE, 0, @@ -1146,4 +972,4 @@ int main(int argc, char* argv[]) } VTKM_EXECUTE_BENCHMARKS(argc, args.data()); -} +} \ No newline at end of file diff --git a/vtkm/source/CMakeLists.txt b/vtkm/source/CMakeLists.txt index 88c576b2c..03de54190 100644 --- a/vtkm/source/CMakeLists.txt +++ b/vtkm/source/CMakeLists.txt @@ -13,6 +13,7 @@ set(headers Source.h Tangle.h Wavelet.h + PerlinNoise.h ) set(device_sources @@ -20,6 +21,7 @@ set(device_sources Source.cxx Tangle.cxx Wavelet.cxx + PerlinNoise.cxx ) vtkm_library(NAME vtkm_source diff --git a/vtkm/source/PerlinNoise.cxx b/vtkm/source/PerlinNoise.cxx new file mode 100644 index 000000000..03847536b --- /dev/null +++ b/vtkm/source/PerlinNoise.cxx @@ -0,0 +1,209 @@ +//============================================================================ +// 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 vtkm +{ +namespace source +{ +namespace perlin +{ +struct PerlinNoiseWorklet : public vtkm::worklet::WorkletVisitPointsWithCells +{ + using ControlSignature = void(CellSetIn, FieldInPoint, WholeArrayIn, FieldOut noise); + using ExecutionSignature = void(_2, _3, _4); + + VTKM_CONT PerlinNoiseWorklet(vtkm::Id repeat) + : Repeat(repeat) + { + } + + // Adapted from https://adrianb.io/2014/08/09/perlinnoise.html + // Archive link: https://web.archive.org/web/20210329174559/https://adrianb.io/2014/08/09/perlinnoise.html + template + VTKM_EXEC void operator()(const PointVecType& pos, const PermsPortal& perms, OutType& noise) const + { + vtkm::Id xi = static_cast(pos[0]) % this->Repeat; + vtkm::Id yi = static_cast(pos[1]) % this->Repeat; + vtkm::Id zi = static_cast(pos[2]) % this->Repeat; + vtkm::FloatDefault xf = pos[0] - xi; + vtkm::FloatDefault yf = pos[1] - yi; + vtkm::FloatDefault zf = pos[2] - zi; + vtkm::FloatDefault u = this->Fade(xf); + vtkm::FloatDefault v = this->Fade(yf); + vtkm::FloatDefault w = this->Fade(zf); + + vtkm::Id aaa, aba, aab, abb, baa, bba, bab, bbb; + aaa = perms[perms[perms[xi] + yi] + zi]; + aba = perms[perms[perms[xi] + this->Increment(yi)] + zi]; + aab = perms[perms[perms[xi] + yi] + this->Increment(zi)]; + abb = perms[perms[perms[xi] + this->Increment(yi)] + this->Increment(zi)]; + baa = perms[perms[perms[this->Increment(xi)] + yi] + zi]; + bba = perms[perms[perms[this->Increment(xi)] + this->Increment(yi)] + zi]; + bab = perms[perms[perms[this->Increment(xi)] + yi] + this->Increment(zi)]; + bbb = perms[perms[perms[this->Increment(xi)] + this->Increment(yi)] + this->Increment(zi)]; + + vtkm::FloatDefault x1, x2, y1, y2; + x1 = vtkm::Lerp(this->Gradient(aaa, xf, yf, zf), this->Gradient(baa, xf - 1, yf, zf), u); + x2 = + vtkm::Lerp(this->Gradient(aba, xf, yf - 1, zf), this->Gradient(bba, xf - 1, yf - 1, zf), u); + y1 = vtkm::Lerp(x1, x2, v); + + x1 = + vtkm::Lerp(this->Gradient(aab, xf, yf, zf - 1), this->Gradient(bab, xf - 1, yf, zf - 1), u); + x2 = vtkm::Lerp( + this->Gradient(abb, xf, yf - 1, zf - 1), this->Gradient(bbb, xf - 1, yf - 1, zf - 1), u); + y2 = vtkm::Lerp(x1, x2, v); + + noise = (vtkm::Lerp(y1, y2, w) + OutType(1.0f)) * OutType(0.5f); + } + + VTKM_EXEC vtkm::FloatDefault Fade(vtkm::FloatDefault t) const + { + return t * t * t * (t * (t * 6 - 15) + 10); + } + + VTKM_EXEC vtkm::Id Increment(vtkm::Id n) const { return (n + 1) % this->Repeat; } + + VTKM_EXEC vtkm::FloatDefault Gradient(vtkm::Id hash, + vtkm::FloatDefault x, + vtkm::FloatDefault y, + vtkm::FloatDefault z) const + { + switch (hash & 0xF) + { + case 0x0: + return x + y; + case 0x1: + return -x + y; + case 0x2: + return x - y; + case 0x3: + return -x - y; + case 0x4: + return x + z; + case 0x5: + return -x + z; + case 0x6: + return x - z; + case 0x7: + return -x - z; + case 0x8: + return y + z; + case 0x9: + return -y + z; + case 0xA: + return y - z; + case 0xB: + return -y - z; + case 0xC: + return y + x; + case 0xD: + return -y + z; + case 0xE: + return y - x; + case 0xF: + return -y - z; + default: + return 0; // never happens + } + } + + vtkm::Id Repeat; +}; + +class PerlinNoiseField : public vtkm::filter::FilterField +{ +public: + VTKM_CONT PerlinNoiseField(vtkm::IdComponent tableSize, vtkm::Id seed) + : TableSize(tableSize) + , Seed(seed) + { + this->GeneratePermutations(); + this->SetUseCoordinateSystemAsField(true); + } + + template + VTKM_CONT vtkm::cont::DataSet DoExecute( + const vtkm::cont::DataSet& input, + const FieldType& vtkmNotUsed(field), + const vtkm::filter::FieldMetadata& fieldMetadata, + vtkm::filter::PolicyBase vtkmNotUsed(policy)) + { + vtkm::cont::ArrayHandle noise; + PerlinNoiseWorklet worklet{ this->TableSize }; + this->Invoke( + worklet, input.GetCellSet(), input.GetCoordinateSystem(), this->Permutations, noise); + + return vtkm::filter::CreateResult(input, noise, this->GetOutputFieldName(), fieldMetadata); + } + +private: + VTKM_CONT void GeneratePermutations() + { + std::mt19937_64 rng; + rng.seed(this->Seed); + std::uniform_int_distribution distribution(0, this->TableSize - 1); + + vtkm::cont::ArrayHandle perms; + perms.Allocate(this->TableSize); + auto permsPortal = perms.WritePortal(); + for (auto i = 0; i < permsPortal.GetNumberOfValues(); ++i) + { + permsPortal.Set(i, distribution(rng)); + } + this->Permutations.Allocate(2 * this->TableSize); + auto permutations = this->Permutations.WritePortal(); + for (auto i = 0; i < permutations.GetNumberOfValues(); ++i) + { + permutations.Set(i, permsPortal.Get(i % this->TableSize)); + } + } + + vtkm::IdComponent TableSize; + vtkm::Id Seed; + vtkm::cont::ArrayHandle Permutations; +}; +} // namespace perlin + +vtkm::cont::DataSet PerlinNoise::Execute() const +{ + VTKM_LOG_SCOPE_FUNCTION(vtkm::cont::LogLevel::Perf); + + vtkm::cont::DataSet dataSet; + const vtkm::Id3 pdims{ this->Dims + vtkm::Id3{ 1, 1, 1 } }; + const vtkm::Vec3f spacing(1.0f / static_cast(this->Dims[0]), + 1.0f / static_cast(this->Dims[1]), + 1.0f / static_cast(this->Dims[2])); + + + vtkm::cont::CellSetStructured<3> cellSet; + cellSet.SetPointDimensions(pdims); + dataSet.SetCellSet(cellSet); + vtkm::cont::ArrayHandleUniformPointCoordinates coordinates(pdims, this->Origin, spacing); + dataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", coordinates)); + + auto tableSize = static_cast( + vtkm::Max(this->Dims[0], vtkm::Max(this->Dims[1], this->Dims[2]))); + perlin::PerlinNoiseField noiseGenerator(tableSize, this->Seed); + noiseGenerator.SetOutputFieldName("perlinnoise"); + dataSet = noiseGenerator.Execute(dataSet); + + return dataSet; +} + +} // namespace source +} // namespace vtkm diff --git a/vtkm/source/PerlinNoise.h b/vtkm/source/PerlinNoise.h new file mode 100644 index 000000000..5a53dd046 --- /dev/null +++ b/vtkm/source/PerlinNoise.h @@ -0,0 +1,61 @@ +//============================================================================ +// 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_source_PerlinNoise_h +#define vtk_m_source_PerlinNoise_h + +#include + +namespace vtkm +{ +namespace source +{ +/** + * @brief The PerlinNoise source creates a uniform dataset. + * + * This class generates a uniform grid dataset with a tileable perlin + * noise scalar point field. + * + * The Execute method creates a complete structured dataset that have a + * scalar point field named 'perlinnoise'. +**/ +class VTKM_SOURCE_EXPORT PerlinNoise final : public vtkm::source::Source +{ +public: + ///Construct a PerlinNoise with Cell Dimensions + VTKM_CONT + PerlinNoise(vtkm::Id3 dims, vtkm::IdComponent seed) + : PerlinNoise(dims, vtkm::Vec3f(0), seed) + { + } + + VTKM_CONT + PerlinNoise(vtkm::Id3 dims, vtkm::Vec3f origin, vtkm::IdComponent seed) + : Dims(dims) + , Origin(origin) + , Seed(seed) + { + } + + vtkm::IdComponent GetSeed() const { return this->Seed; } + + void SetSeed(vtkm::IdComponent seed) { this->Seed = seed; } + + vtkm::cont::DataSet Execute() const override; + +private: + vtkm::Id3 Dims; + vtkm::Vec3f Origin; + vtkm::IdComponent Seed; +}; +} //namespace source +} //namespace vtkm + +#endif //vtk_m_source_Tangle_h