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>(); } };