From 4d02add0e0d14607fc64315a606803197cff3abc Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 7 Nov 2023 16:58:31 -0500 Subject: [PATCH] Enable new instances of unknown arrays with dynamic sizes `UnknownArrayHandle` allows you to create a new instance of a compatible array so that when receiving an array of unknown type, a place to put the output can be created. However, these methods only worked if the number of components in each value could be determined statically at compile time. However, there are some special `ArrayHandle`s that can define the number of components at runtime. In this case, the `ArrayHandle` would throw an exception if `NewInstanceBasic` or `NewInstanceFloatBasic` was called. Although rare, this condition could happen when, for example, an array was extracted from an `UnknownArrayHandle` with `ExtractArrayFromComponents` or with `CastAndCallWithExtractedArray` and then the resulting array was passed to a function with arrays passed with `UnknownArrayHandle` such as `ArrayCopy`. --- .../new-instance-variable-components.md | 16 +++++ vtkm/cont/UnknownArrayHandle.cxx | 5 +- vtkm/cont/UnknownArrayHandle.h | 59 ++++++++++++------- vtkm/cont/testing/UnitTestArrayCopy.cxx | 38 ++++++++++++ .../testing/UnitTestUnknownArrayHandle.cxx | 12 ++++ 5 files changed, 107 insertions(+), 23 deletions(-) create mode 100644 docs/changelog/new-instance-variable-components.md diff --git a/docs/changelog/new-instance-variable-components.md b/docs/changelog/new-instance-variable-components.md new file mode 100644 index 000000000..90c43fd75 --- /dev/null +++ b/docs/changelog/new-instance-variable-components.md @@ -0,0 +1,16 @@ +# Enable new instances of unknown arrays with dynamic sizes + +`UnknownArrayHandle` allows you to create a new instance of a compatible +array so that when receiving an array of unknown type, a place to put the +output can be created. However, these methods only worked if the number of +components in each value could be determined statically at compile time. + +However, there are some special `ArrayHandle`s that can define the number +of components at runtime. In this case, the `ArrayHandle` would throw an +exception if `NewInstanceBasic` or `NewInstanceFloatBasic` was called. + +Although rare, this condition could happen when, for example, an array was +extracted from an `UnknownArrayHandle` with `ExtractArrayFromComponents` or +with `CastAndCallWithExtractedArray` and then the resulting array was +passed to a function with arrays passed with `UnknownArrayHandle` such as +`ArrayCopy`. diff --git a/vtkm/cont/UnknownArrayHandle.cxx b/vtkm/cont/UnknownArrayHandle.cxx index 21037bc99..80644f480 100644 --- a/vtkm/cont/UnknownArrayHandle.cxx +++ b/vtkm/cont/UnknownArrayHandle.cxx @@ -219,7 +219,7 @@ VTKM_CONT UnknownArrayHandle UnknownArrayHandle::NewInstanceBasic() const } if (this->Container) { - newArray.Container = this->Container->NewInstanceBasic(); + newArray.Container = this->Container->NewInstanceBasic(this->Container->ArrayHandlePointer); } return newArray; } @@ -237,7 +237,8 @@ VTKM_CONT UnknownArrayHandle UnknownArrayHandle::NewInstanceFloatBasic() const UnknownArrayHandle newArray; if (this->Container) { - newArray.Container = this->Container->NewInstanceFloatBasic(); + newArray.Container = + this->Container->NewInstanceFloatBasic(this->Container->ArrayHandlePointer); } return newArray; } diff --git a/vtkm/cont/UnknownArrayHandle.h b/vtkm/cont/UnknownArrayHandle.h index 4370294c4..534e1cb06 100644 --- a/vtkm/cont/UnknownArrayHandle.h +++ b/vtkm/cont/UnknownArrayHandle.h @@ -231,7 +231,7 @@ struct VTKM_CONT_EXPORT UnknownAHContainer using NewInstanceType = void*(); NewInstanceType* NewInstance; - using NewInstanceBasicType = std::shared_ptr(); + using NewInstanceBasicType = std::shared_ptr(void*); NewInstanceBasicType* NewInstanceBasic; NewInstanceBasicType* NewInstanceFloatBasic; @@ -307,39 +307,56 @@ private: explicit UnknownAHContainer(const vtkm::cont::ArrayHandle& array); }; -template -std::shared_ptr UnknownAHNewInstanceBasic(vtkm::VecTraitsTagSizeStatic) +template +std::shared_ptr UnknownAHNewInstanceBasic(void*, vtkm::VecTraitsTagSizeStatic) { return UnknownAHContainer::Make(vtkm::cont::ArrayHandleBasic{}); } -template -std::shared_ptr UnknownAHNewInstanceBasic(vtkm::VecTraitsTagSizeVariable) +template +std::shared_ptr UnknownAHNewInstanceBasic(void* mem, + vtkm::VecTraitsTagSizeVariable) { - throw vtkm::cont::ErrorBadType("Cannot create a basic array container from with ValueType of " + - vtkm::cont::TypeToString()); + vtkm::IdComponent numComponents = UnknownAHNumberOfComponentsFlat(mem); + if (numComponents < 1) + { + // Array can have an inconsistent number of components. Cannot be represented by basic array. + throw vtkm::cont::ErrorBadType("Cannot create a basic array from array with ValueType of " + + vtkm::cont::TypeToString()); + } + using ComponentType = typename vtkm::VecTraits::BaseComponentType; + return UnknownAHContainer::Make(vtkm::cont::ArrayHandleRuntimeVec(numComponents)); } -template -std::shared_ptr UnknownAHNewInstanceBasic() +template +std::shared_ptr UnknownAHNewInstanceBasic(void* mem) { - return UnknownAHNewInstanceBasic(typename vtkm::VecTraits::IsSizeStatic{}); + return UnknownAHNewInstanceBasic(mem, typename vtkm::VecTraits::IsSizeStatic{}); } -template -std::shared_ptr UnknownAHNewInstanceFloatBasic(vtkm::VecTraitsTagSizeStatic) +template +std::shared_ptr UnknownAHNewInstanceFloatBasic(void*, + vtkm::VecTraitsTagSizeStatic) { using FloatT = typename vtkm::VecTraits::template ReplaceBaseComponentType; return UnknownAHContainer::Make(vtkm::cont::ArrayHandleBasic{}); } -template -std::shared_ptr UnknownAHNewInstanceFloatBasic(vtkm::VecTraitsTagSizeVariable) +template +std::shared_ptr UnknownAHNewInstanceFloatBasic(void* mem, + vtkm::VecTraitsTagSizeVariable) { - throw vtkm::cont::ErrorBadType("Cannot create a basic array container from with ValueType of " + - vtkm::cont::TypeToString()); + vtkm::IdComponent numComponents = UnknownAHNumberOfComponentsFlat(mem); + if (numComponents < 1) + { + // Array can have an inconsistent number of components. Cannot be represented by basic array. + throw vtkm::cont::ErrorBadType("Cannot create a basic array from array with ValueType of " + + vtkm::cont::TypeToString()); + } + return UnknownAHContainer::Make( + vtkm::cont::ArrayHandleRuntimeVec(numComponents)); } -template -std::shared_ptr UnknownAHNewInstanceFloatBasic() +template +std::shared_ptr UnknownAHNewInstanceFloatBasic(void* mem) { - return UnknownAHNewInstanceFloatBasic(typename vtkm::VecTraits::IsSizeStatic{}); + return UnknownAHNewInstanceFloatBasic(mem, typename vtkm::VecTraits::IsSizeStatic{}); } template @@ -352,8 +369,8 @@ inline UnknownAHContainer::UnknownAHContainer(const vtkm::cont::ArrayHandle) , Buffers(detail::UnknownAHBuffers) , NewInstance(detail::UnknownAHNewInstance) - , NewInstanceBasic(detail::UnknownAHNewInstanceBasic) - , NewInstanceFloatBasic(detail::UnknownAHNewInstanceFloatBasic) + , NewInstanceBasic(detail::UnknownAHNewInstanceBasic) + , NewInstanceFloatBasic(detail::UnknownAHNewInstanceFloatBasic) , NumberOfValues(detail::UnknownAHNumberOfValues) , NumberOfComponents(detail::UnknownAHNumberOfComponents) , NumberOfComponentsFlat(detail::UnknownAHNumberOfComponentsFlat) diff --git a/vtkm/cont/testing/UnitTestArrayCopy.cxx b/vtkm/cont/testing/UnitTestArrayCopy.cxx index 9ca8d59dc..2ed5e08e6 100644 --- a/vtkm/cont/testing/UnitTestArrayCopy.cxx +++ b/vtkm/cont/testing/UnitTestArrayCopy.cxx @@ -254,6 +254,44 @@ void TryCopy() ARRAY_SIZE)); } + { + std::cout << "runtime vec size -> runtime vec size (different type)" << std::endl; + using ComponentType = typename VTraits::BaseComponentType; + using SourceType = typename VTraits::template ReplaceComponentType; + vtkm::cont::ArrayHandle staticVecArray = MakeInputArray(); + vtkm::cont::ArrayHandleRuntimeVec input = + vtkm::cont::make_ArrayHandleRuntimeVec(staticVecArray); + vtkm::cont::ArrayHandleRuntimeVec output(input.GetNumberOfComponents()); + vtkm::cont::ArrayCopy(input, output); + // Convert the arrays back to static vec sizes for comparison, because TestValues + // uses a device array copy that may not work on runtime vec sizes. + TestValues(staticVecArray, + output.template AsArrayHandleBasic>()); + } + + { + std::cout << "basic -> recombined vec" << std::endl; + using ComponentType = typename VTraits::BaseComponentType; + vtkm::cont::ArrayHandle input = MakeInputArray(); + vtkm::cont::ArrayHandle output; + auto recombinedVec = + vtkm::cont::UnknownArrayHandle{ output }.ExtractArrayFromComponents(); + vtkm::cont::ArrayCopy(input, recombinedVec); + TestValues(input, output); + } + + { + std::cout << "basic -> recombined vec (different type)" << std::endl; + using SourceType = typename VTraits::template ReplaceComponentType; + using ComponentType = typename VTraits::BaseComponentType; + vtkm::cont::ArrayHandle input = MakeInputArray(); + vtkm::cont::ArrayHandle output; + auto recombinedVec = + vtkm::cont::UnknownArrayHandle{ output }.ExtractArrayFromComponents(); + vtkm::cont::ArrayCopy(input, recombinedVec); + TestValues(input, output); + } + // Test the copy methods in UnknownArrayHandle. Although this would be appropriate in // UnitTestUnknownArrayHandle, it is easier to test copies here. { diff --git a/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx b/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx index 6b9f8045c..fa66c6f2a 100644 --- a/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx +++ b/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx @@ -479,6 +479,18 @@ struct CheckExtractedArray auto extractedData = extractedPortal.Get(valueIndex); VTKM_TEST_ASSERT(test_equal(originalData, extractedData)); } + + // Make sure an extracted array stuffed back into an UnknownArrayHandle works. + // This can happen when working with an extracted array that is passed to functions + // that are implemented with UnknownArrayHandle. + vtkm::cont::UnknownArrayHandle unknownArray{ extractedArray }; + + using ComponentType = + typename vtkm::VecTraits::BaseComponentType; + vtkm::cont::UnknownArrayHandle newBasic = unknownArray.NewInstanceBasic(); + newBasic.AsArrayHandle>(); + vtkm::cont::UnknownArrayHandle newFloat = unknownArray.NewInstanceFloatBasic(); + newFloat.AsArrayHandle>(); } };