From c2101b8ffcdd82587ef65bde2b29c6d7f3613ab1 Mon Sep 17 00:00:00 2001 From: Robert Maynard Date: Tue, 11 Feb 2014 12:34:56 -0500 Subject: [PATCH] Add in a serial device adapter and required supporting classes. We now can verify that the array handle is usable by a device adapter. --- vtkm/cont/ArrayHandleCounting.h | 144 ++ vtkm/cont/CMakeLists.txt | 2 + vtkm/cont/DeviceAdapter.h | 73 + vtkm/cont/DeviceAdapterSerial.h | 27 + vtkm/cont/internal/CMakeLists.txt | 7 + vtkm/cont/internal/DeviceAdapterAlgorithm.h | 404 +++++ .../internal/DeviceAdapterAlgorithmGeneral.h | 993 ++++++++++++ .../internal/DeviceAdapterAlgorithmSerial.h | 190 +++ vtkm/cont/internal/DeviceAdapterError.h | 32 + vtkm/cont/internal/DeviceAdapterTag.h | 5 + vtkm/cont/internal/testing/CMakeLists.txt | 2 +- vtkm/cont/testing/CMakeLists.txt | 5 + vtkm/cont/testing/TestingDeviceAdapter.h | 1330 +++++++++++++++++ .../testing/UnitTestArrayHandleCounting.cxx | 125 ++ ...itTestDeviceAdapterAlgorithmDependency.cxx | 57 + .../UnitTestDeviceAdapterAlgorithmGeneral.cxx | 106 ++ .../testing/UnitTestDeviceAdapterSerial.cxx | 31 + vtkm/exec/internal/ErrorMessageBuffer.h | 94 ++ vtkm/exec/internal/WorkletBase.h | 64 + vtkm/testing/CMakeLists.txt | 1 + vtkm/testing/UnitTestVectorTraits.cxx | 63 + vtkm/testing/VectorTraitsTests.h | 157 ++ 22 files changed, 3911 insertions(+), 1 deletion(-) create mode 100644 vtkm/cont/ArrayHandleCounting.h create mode 100644 vtkm/cont/DeviceAdapter.h create mode 100644 vtkm/cont/DeviceAdapterSerial.h create mode 100644 vtkm/cont/internal/DeviceAdapterAlgorithm.h create mode 100644 vtkm/cont/internal/DeviceAdapterAlgorithmGeneral.h create mode 100644 vtkm/cont/internal/DeviceAdapterAlgorithmSerial.h create mode 100644 vtkm/cont/internal/DeviceAdapterError.h create mode 100644 vtkm/cont/testing/TestingDeviceAdapter.h create mode 100644 vtkm/cont/testing/UnitTestArrayHandleCounting.cxx create mode 100644 vtkm/cont/testing/UnitTestDeviceAdapterAlgorithmDependency.cxx create mode 100644 vtkm/cont/testing/UnitTestDeviceAdapterAlgorithmGeneral.cxx create mode 100644 vtkm/cont/testing/UnitTestDeviceAdapterSerial.cxx create mode 100644 vtkm/exec/internal/ErrorMessageBuffer.h create mode 100644 vtkm/exec/internal/WorkletBase.h create mode 100644 vtkm/testing/UnitTestVectorTraits.cxx create mode 100644 vtkm/testing/VectorTraitsTests.h diff --git a/vtkm/cont/ArrayHandleCounting.h b/vtkm/cont/ArrayHandleCounting.h new file mode 100644 index 000000000..f77e5d6b2 --- /dev/null +++ b/vtkm/cont/ArrayHandleCounting.h @@ -0,0 +1,144 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_cont_ArrayHandleCounting_h +#define vtkm_cont_ArrayHandleCounting_h + +#include +#include +#include + +namespace vtkm { +namespace cont { + +namespace internal { + +/// \brief An implicit array portal that returns an counting value. +template +class ArrayPortalCounting +{ +public: + typedef CountingValueType ValueType; + + VTKM_EXEC_CONT_EXPORT + ArrayPortalCounting() : + StartingValue(), + NumberOfValues(0) + { } + + VTKM_EXEC_CONT_EXPORT + ArrayPortalCounting(ValueType startingValue, vtkm::Id numValues) : + StartingValue(startingValue), + NumberOfValues(numValues) + { } + + template + VTKM_EXEC_CONT_EXPORT + ArrayPortalCounting(const ArrayPortalCounting &src) + : StartingValue(src.StartingValue), + NumberOfValues(src.NumberOfValues) + { } + + template + VTKM_EXEC_CONT_EXPORT + ArrayPortalCounting &operator=( + const ArrayPortalCounting &src) + { + this->StartingValue = src.StartingValue; + this->NumberOfValues = src.NumberOfValues; + return *this; + } + + VTKM_EXEC_CONT_EXPORT + vtkm::Id GetNumberOfValues() const { return this->NumberOfValues; } + + VTKM_EXEC_CONT_EXPORT + ValueType Get(vtkm::Id index) const { return StartingValue+index; } + + typedef vtkm::cont::internal::IteratorFromArrayPortal< + ArrayPortalCounting < CountingValueType> > IteratorType; + + VTKM_CONT_EXPORT + IteratorType GetIteratorBegin() const + { + return IteratorType(*this); + } + + VTKM_CONT_EXPORT + IteratorType GetIteratorEnd() const + { + return IteratorType(*this, this->NumberOfValues); + } + +private: + CountingValueType StartingValue; + vtkm::Id NumberOfValues; +}; + +/// A convenience class that provides a typedef to the appropriate tag for +/// a counting array container. +template +struct ArrayHandleCountingTraits +{ + typedef vtkm::cont::ArrayContainerControlTagImplicit< + vtkm::cont::internal::ArrayPortalCounting > Tag; +}; + +} // namespace internal + +/// ArrayHandleCountings is a specialization of ArrayHandle. By default it +/// contains a increment value, that is increment for each step between zero +/// and the passed in length +template +class ArrayHandleCounting + : public vtkm::cont::ArrayHandle < + CountingValueType, + typename internal::ArrayHandleCountingTraits::Tag + > +{ + typedef vtkm::cont::ArrayHandle < + CountingValueType, + typename internal::ArrayHandleCountingTraits::Tag + > Superclass; +public: + + ArrayHandleCounting(CountingValueType startingValue, vtkm::Id length) + :Superclass(typename Superclass::PortalConstControl(startingValue, length)) + { + } + + ArrayHandleCounting():Superclass() {} +}; + +/// A convenience function for creating an ArrayHandleCounting. It takes the +/// value to start counting from and and the number of times to increment. +template +VTKM_CONT_EXPORT +vtkm::cont::ArrayHandleCounting +make_ArrayHandleCounting(CountingValueType startingValue, + vtkm::Id length) +{ + return vtkm::cont::ArrayHandleCounting(startingValue, + length); +} + +} +} // namespace vtkm::cont + +#endif //vtkm_cont_ArrayHandleCounting_h diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index 5ef6fa4e3..f56518d16 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -27,6 +27,8 @@ set(headers ArrayHandle.h ArrayPortal.h Assert.h + DeviceAdapter.h + DeviceAdapterSerial.h Error.h ErrorControl.h ErrorControlAssert.h diff --git a/vtkm/cont/DeviceAdapter.h b/vtkm/cont/DeviceAdapter.h new file mode 100644 index 000000000..1ffa6f505 --- /dev/null +++ b/vtkm/cont/DeviceAdapter.h @@ -0,0 +1,73 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_cont_DeviceAdapter_h +#define vtkm_cont_DeviceAdapter_h + +// These are listed in non-alphabetical order because this is the conceptual +// order in which the sub-files are loaded. (But the compile should still +// succeed of the order is changed.) + +#include +#include +#include + +namespace vtkm { +namespace cont { + +#ifdef VTKM_DOXYGEN_ONLY +/// \brief A tag specifying the interface between the control and execution environments. +/// +/// A DeviceAdapter tag specifies a set of functions and classes that provide +/// mechanisms to run algorithms on a type of parallel device. The tag +/// DeviceAdapterTag___ does not actually exist. Rather, this documentation is +/// provided to describe the interface for a DeviceAdapter. Loading the +/// dax/cont/DeviceAdapter.h header file will set a default device adapter +/// appropriate for the current compile environment. You can specify the +/// default device adapter by first setting the \c VTKM_DEVICE_ADAPTER macro. +/// Valid values for \c VTKM_DEVICE_ADAPTER are the following: +/// +/// \li \c VTKM_DEVICE_ADAPTER_SERIAL Runs all algorithms in serial. Can be +/// helpful for debugging. +/// \li \c VTKM_DEVICE_ADAPTER_CUDA Dispatches and runs algorithms on a GPU +/// using CUDA. Must be compiling with a CUDA compiler (nvcc). +/// \li \c VTKM_DEVICE_ADAPTER_OPENMP Dispatches an algorithm over multiple +/// CPU cores using OpenMP compiler directives. Must be compiling with an +/// OpenMP-compliant compiler with OpenMP pragmas enabled. +/// \li \c VTKM_DEVICE_ADAPTER_TBB Dispatches and runs algorithms on multiple +/// threads using the Intel Threading Building Blocks (TBB) libraries. Must +/// have the TBB headers available and the resulting code must be linked with +/// the TBB libraries. +/// +/// See the ArrayManagerExecution.h and DeviceAdapterAlgorithm.h files for +/// documentation on all the functions and classes that must be +/// overloaded/specialized to create a new device adapter. +/// +struct DeviceAdapterTag___ { }; +#endif //VTKM_DOXYGEN_ONLY + +namespace internal { + +} // namespace internal + +} +} // namespace vtkm::cont + + +#endif //vtkm_cont_DeviceAdapter_h diff --git a/vtkm/cont/DeviceAdapterSerial.h b/vtkm/cont/DeviceAdapterSerial.h new file mode 100644 index 000000000..e0ee147ad --- /dev/null +++ b/vtkm/cont/DeviceAdapterSerial.h @@ -0,0 +1,27 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_cont_DeviceAdapterSerial_h +#define vtkm_cont_DeviceAdapterSerial_h + +#include +#include +#include + +#endif //vtkm_cont_DeviceAdapterSerial_h diff --git a/vtkm/cont/internal/CMakeLists.txt b/vtkm/cont/internal/CMakeLists.txt index dfba17db9..d1b445f87 100644 --- a/vtkm/cont/internal/CMakeLists.txt +++ b/vtkm/cont/internal/CMakeLists.txt @@ -26,6 +26,13 @@ set(headers ArrayPortalFromIterators.h ArrayPortalShrink.h ArrayTransfer.h + DeviceAdapterAlgorithm.h + DeviceAdapterAlgorithmGeneral.h + DeviceAdapterAlgorithmSerial.h + DeviceAdapterError.h + DeviceAdapterTag.h + DeviceAdapterTagSerial.h + IteratorFromArrayPortal.h ) vtkm_declare_headers(${headers}) diff --git a/vtkm/cont/internal/DeviceAdapterAlgorithm.h b/vtkm/cont/internal/DeviceAdapterAlgorithm.h new file mode 100644 index 000000000..1735172fb --- /dev/null +++ b/vtkm/cont/internal/DeviceAdapterAlgorithm.h @@ -0,0 +1,404 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_cont_internal_DeviceAdapterAlgorithm_h +#define vtkm_cont_internal_DeviceAdapterAlgorithm_h + +#include + +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#endif + +namespace vtkm { +namespace cont { + +/// \brief Struct containing device adapter algorithms. +/// +/// This struct, templated on the device adapter tag, comprises static methods +/// that implement the algorithms provided by the device adapter. The default +/// struct is not implemented. Device adapter implementations must specialize +/// the template. +/// +template +struct DeviceAdapterAlgorithm +#ifdef VTKM_DOXYGEN_ONLY +{ + /// \brief Copy the contents of one ArrayHandle to another + /// + /// Copies the contents of \c input to \c output. The array \c to will be + /// allocated to the appropriate size. + /// + template + VTKM_CONT_EXPORT static void Copy( + const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle &output); + + /// \brief Output is the first index in input for each item in values that wouldn't alter the ordering of input + /// + /// LowerBounds is a vectorized search. From each value in \c values it finds + /// the first place the item can be inserted in the ordered \c input array and + /// stores the index in \c output. + /// + /// \par Requirements: + /// \arg \c input must already be sorted + /// + template + VTKM_CONT_EXPORT static void LowerBounds( + const vtkm::cont::ArrayHandle& input, + const vtkm::cont::ArrayHandle& values, + vtkm::cont::ArrayHandle& output); + + /// \brief Output is the first index in input for each item in values that wouldn't alter the ordering of input + /// + /// LowerBounds is a vectorized search. From each value in \c values it finds + /// the first place the item can be inserted in the ordered \c input array and + /// stores the index in \c output. Uses the custom comparison functor to + /// determine the correct location for each item. + /// + /// \par Requirements: + /// \arg \c input must already be sorted + /// + template + VTKM_CONT_EXPORT static void LowerBounds( + const vtkm::cont::ArrayHandle& input, + const vtkm::cont::ArrayHandle& values, + vtkm::cont::ArrayHandle& output, + Compare comp); + + /// \brief A special version of LowerBounds that does an in place operation. + /// + /// This version of lower bounds performs an in place operation where each + /// value in the \c values_output array is replaced by the index in \c input + /// where it occurs. Because this is an in place operation, the type of the + /// arrays is limited to vtkm::Id. + /// + template + VTKM_CONT_EXPORT static void LowerBounds( + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& values_output); + + /// \brief Compute an inclusive prefix sum operation on the input ArrayHandle. + /// + /// Computes an inclusive prefix sum operation on the \c input ArrayHandle, + /// storing the results in the \c output ArrayHandle. InclusiveScan is + /// similiar to the stl partial sum function, exception that InclusiveScan + /// doesn't do a serial sumnation. This means that if you have defined a + /// custom plus operator for T it must be associative, or you will get + /// inconsistent results. When the input and output ArrayHandles are the same + /// ArrayHandle the operation will be done inplace. + /// + /// \return The total sum. + /// + template + VTKM_CONT_EXPORT static T ScanInclusive( + const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle& output); + + /// \brief Compute an exclusive prefix sum operation on the input ArrayHandle. + /// + /// Computes an exclusive prefix sum operation on the \c input ArrayHandle, + /// storing the results in the \c output ArrayHandle. ExclusiveScan is + /// similiar to the stl partial sum function, exception that ExclusiveScan + /// doesn't do a serial sumnation. This means that if you have defined a + /// custom plus operator for T it must be associative, or you will get + /// inconsistent results. When the input and output ArrayHandles are the same + /// ArrayHandle the operation will be done inplace. + /// + /// \return The total sum. + /// + template + VTKM_CONT_EXPORT static T ScanExclusive( + const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle& output); + + /// \brief Schedule many instances of a function to run on concurrent threads. + /// + /// Calls the \c functor on several threads. This is the function used in the + /// control environment to spawn activity in the execution environment. \c + /// functor is a function-like object that can be invoked with the calling + /// specification functor(vtkm::Id index). It also has a method called + /// from the control environment to establish the error reporting buffer with + /// the calling specification functor.SetErrorMessageBuffer(const + /// vtkm::exec::internal::ErrorMessageBuffer &errorMessage). This object + /// can be stored in the functor's state such that if RaiseError is called on + /// it in the execution environment, an ErrorExecution will be thrown from + /// Schedule. + /// + /// The argument of the invoked functor uniquely identifies the thread or + /// instance of the invocation. There should be one invocation for each index + /// in the range [0, \c numInstances]. + /// + template + VTKM_CONT_EXPORT static void Schedule(Functor functor, + vtkm::Id numInstances); + + /// \brief Schedule many instances of a function to run on concurrent threads. + /// + /// Calls the \c functor on several threads. This is the function used in the + /// control environment to spawn activity in the execution environment. \c + /// functor is a function-like object that can be invoked with the calling + /// specification functor(vtkm::Id3 index) or functor(vtkm::Id + /// index). It also has a method called from the control environment to + /// establish the error reporting buffer with the calling specification + /// functor.SetErrorMessageBuffer(const + /// vtkm::exec::internal::ErrorMessageBuffer &errorMessage). This object + /// can be stored in the functor's state such that if RaiseError is called on + /// it in the execution environment, an ErrorExecution will be thrown from + /// Schedule. + /// + /// The argument of the invoked functor uniquely identifies the thread or + /// instance of the invocation. It is at the device adapter's discretion + /// whether to schedule on 1D or 3D indices, so the functor should have an + /// operator() overload for each index type. If 3D indices are used, there is + /// one invocation for every i, j, k value between [0, 0, 0] and \c rangeMax. + /// If 1D indices are used, this Schedule behaves as if Schedule(functor, + /// rangeMax[0]*rangeMax[1]*rangeMax[2]) were called. + /// + template + VTKM_CONT_EXPORT static void Schedule(Functor functor, + vtkm::Id3 rangeMax); + + /// \brief Unstable ascending sort of input array. + /// + /// Sorts the contents of \c values so that they in ascending value. Doesn't + /// guarantee stability + /// + template + VTKM_CONT_EXPORT static void Sort( + vtkm::cont::ArrayHandle &values); + + /// \brief Unstable ascending sort of input array. + /// + /// Sorts the contents of \c values so that they in ascending value based + /// on the custom compare functor. + /// + template + VTKM_CONT_EXPORT static void Sort( + vtkm::cont::ArrayHandle &values, + Compare comp); + + /// \brief Performs stream compaction to remove unwanted elements in the input array. Output becomes the index values of input that are valid. + /// + /// Calls the parallel primitive function of stream compaction on the \c + /// input to remove unwanted elements. The result of the stream compaction is + /// placed in \c output. The \c input values are used as the stream + /// compaction stencil while \c input indices are used as the values to place + /// into \c ouput. The size of \c output will be modified after this call as + /// we can't know the number of elements that will be removed by the stream + /// compaction algorithm. + /// + template + VTKM_CONT_EXPORT static void StreamCompact( + const vtkm::cont::ArrayHandle &stencil, + vtkm::cont::ArrayHandle &output); + + /// \brief Performs stream compaction to remove unwanted elements in the input array. + /// + /// Calls the parallel primitive function of stream compaction on the \c + /// input to remove unwanted elements. The result of the stream compaction is + /// placed in \c output. The values in \c stencil are used as the stream + /// compaction stencil while \c input values are placed into \c ouput. The + /// size of \c output will be modified after this call as we can't know the + /// number of elements that will be removed by the stream compaction + /// algorithm. + /// + template + VTKM_CONT_EXPORT static void StreamCompact( + const vtkm::cont::ArrayHandle &input, + const vtkm::cont::ArrayHandle &stencil, + vtkm::cont::ArrayHandle &output); + + /// \brief Completes any asynchronous operations running on the device. + /// + /// Waits for any asynchronous operations running on the device to complete. + /// + VTKM_CONT_EXPORT static void Synchronize(); + + /// \brief Reduce an array to only the unique values it contains + /// + /// Removes all duplicate values in \c values that are adjacent to each + /// other. Which means you should sort the input array unless you want + /// duplicate values that aren't adjacent. Note the values array size might + /// be modified by this operation. + /// + template + VTKM_CONT_EXPORT static void Unique( + vtkm::cont::ArrayHandle& values); + + /// \brief Reduce an array to only the unique values it contains + /// + /// Removes all duplicate values in \c values that are adjacent to each + /// other. Which means you should sort the input array unless you want + /// duplicate values that aren't adjacent. Note the values array size might + /// be modified by this operation. + /// + /// Uses the custom binary predicate Comparison to determine if something + /// is unique. The predicate must return true if the two items are the same. + /// + template + VTKM_CONT_EXPORT static void Unique( + vtkm::cont::ArrayHandle& values, + Compare comp); + + /// \brief Output is the last index in input for each item in values that wouldn't alter the ordering of input + /// + /// UpperBounds is a vectorized search. From each value in \c values it finds + /// the last place the item can be inserted in the ordered \c input array and + /// stores the index in \c output. + /// + /// \par Requirements: + /// \arg \c input must already be sorted + /// + template + VTKM_CONT_EXPORT static void UpperBounds( + const vtkm::cont::ArrayHandle& input, + const vtkm::cont::ArrayHandle& values, + vtkm::cont::ArrayHandle& output); + + /// \brief Output is the last index in input for each item in values that wouldn't alter the ordering of input + /// + /// LowerBounds is a vectorized search. From each value in \c values it finds + /// the last place the item can be inserted in the ordered \c input array and + /// stores the index in \c output. Uses the custom comparison functor to + /// determine the correct location for each item. + /// + /// \par Requirements: + /// \arg \c input must already be sorted + /// + template + VTKM_CONT_EXPORT static void UpperBounds( + const vtkm::cont::ArrayHandle& input, + const vtkm::cont::ArrayHandle& values, + vtkm::cont::ArrayHandle& output, + Compare comp); + + /// \brief A special version of UpperBounds that does an in place operation. + /// + /// This version of lower bounds performs an in place operation where each + /// value in the \c values_output array is replaced by the last index in + /// \c input where it occurs. Because this is an in place operation, the type + /// of the arrays is limited to vtkm::Id. + /// + template + VTKM_CONT_EXPORT static void UpperBounds( + const vtkm::cont::ArrayHandle& input, + vtkm::cont::ArrayHandle& values_output); +}; +#else // VTKM_DOXYGEN_ONLY + ; +#endif //VTKM_DOXYGEN_ONLY + +/// \brief Class providing a device-specific timer. +/// +/// The class provide the actual implementation used by vtkm::cont::Timer. +/// A default implementation is provided but device adapters should provide +/// one (in conjunction with DeviceAdapterAlgorithm) where appropriate. The +/// interface for this class is exactly the same as vtkm::cont::Timer. +/// +template +class DeviceAdapterTimerImplementation +{ +public: + /// When a timer is constructed, all threads are synchronized and the + /// current time is marked so that GetElapsedTime returns the number of + /// seconds elapsed since the construction. + VTKM_CONT_EXPORT DeviceAdapterTimerImplementation() + { + this->Reset(); + } + + /// Resets the timer. All further calls to GetElapsedTime will report the + /// number of seconds elapsed since the call to this. This method + /// synchronizes all asynchronous operations. + /// + VTKM_CONT_EXPORT void Reset() + { + this->StartTime = this->GetCurrentTime(); + } + + /// Returns the elapsed time in seconds between the construction of this + /// class or the last call to Reset and the time this function is called. The + /// time returned is measured in wall time. GetElapsedTime may be called any + /// number of times to get the progressive time. This method synchronizes all + /// asynchronous operations. + /// + VTKM_CONT_EXPORT vtkm::Scalar GetElapsedTime() + { + TimeStamp currentTime = this->GetCurrentTime(); + + vtkm::Scalar elapsedTime; + elapsedTime = currentTime.Seconds - this->StartTime.Seconds; + elapsedTime += ((currentTime.Microseconds - this->StartTime.Microseconds) + /vtkm::Scalar(1000000)); + + return elapsedTime; + } + struct TimeStamp { + vtkm::internal::Int64Type Seconds; + vtkm::internal::Int64Type Microseconds; + }; + TimeStamp StartTime; + + VTKM_CONT_EXPORT TimeStamp GetCurrentTime() + { + vtkm::cont::DeviceAdapterAlgorithm + ::Synchronize(); + + TimeStamp retval; +#ifdef _WIN32 + timeb currentTime; + ::ftime(¤tTime); + retval.Seconds = currentTime.time; + retval.Microseconds = 1000*currentTime.millitm; +#else + timeval currentTime; + gettimeofday(¤tTime, NULL); + retval.Seconds = currentTime.tv_sec; + retval.Microseconds = currentTime.tv_usec; +#endif + return retval; + } +}; + +} +} // namespace vtkm::cont + + +//----------------------------------------------------------------------------- +// These includes are intentionally placed here after the declaration of the +// DeviceAdapterAlgorithm template prototype, which all the implementations +// need. + +#if VTKM_DEVICE_ADAPTER == VTKM_DEVICE_ADAPTER_SERIAL +#include +// #elif VTKM_DEVICE_ADAPTER == VTKM_DEVICE_ADAPTER_CUDA +// #include +// #elif VTKM_DEVICE_ADAPTER == VTKM_DEVICE_ADAPTER_OPENMP +// #include +// #elif VTKM_DEVICE_ADAPTER == VTKM_DEVICE_ADAPTER_TBB +// #include +#endif + +#endif //vtkm_cont_DeviceAdapterAlgorithm_h diff --git a/vtkm/cont/internal/DeviceAdapterAlgorithmGeneral.h b/vtkm/cont/internal/DeviceAdapterAlgorithmGeneral.h new file mode 100644 index 000000000..af495ce73 --- /dev/null +++ b/vtkm/cont/internal/DeviceAdapterAlgorithmGeneral.h @@ -0,0 +1,993 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_cont_internal_DeviceAdapterAlgorithmGeneral_h +#define vtkm_cont_internal_DeviceAdapterAlgorithmGeneral_h + +#include +#include +#include + +#include +#include + +#include + +namespace +{ +/// Predicate that takes a single argument \c x, and returns +/// True if it isn't the identity of the Type \p T. +template +struct not_default_constructor +{ + VTKM_EXEC_CONT_EXPORT bool operator()(const T &x) + { + return (x != T()); + } +}; + +} + +namespace vtkm { +namespace cont { +namespace internal { + +/// \brief +/// +/// This struct provides algorithms that implement "general" device adapter +/// algorithms. If a device adapter provides implementations for Schedule, +/// and Synchronize, the rest of the algorithms can be implemented by calling +/// these functions. +/// +/// It should be noted that we recommend that you also implement Sort, +/// ScanInclusive, and ScanExclusive for improved performance. +/// +/// An easy way to implement the DeviceAdapterAlgorithm specialization is to +/// subclass this and override the implementation of methods as necessary. +/// As an example, the code would look something like this. +/// +/// \code{.cpp} +/// template<> +/// struct DeviceAdapterAlgorithm +/// : DeviceAdapterAlgorithmGeneral, +/// DeviceAdapterTagFoo> +/// { +/// template +/// VTKM_CONT_EXPORT static void Schedule(Functor functor, +/// vtkm::Id numInstances) +/// { +/// ... +/// } +/// +/// template +/// VTKM_CONT_EXPORT static void Schedule(Functor functor, +/// vtkm::Id3 maxRange) +/// { +/// ... +/// } +/// +/// VTKM_CONT_EXPORT static void Synchronize() +/// { +/// ... +/// } +/// }; +/// \endcode +/// +/// You might note that DeviceAdapterAlgorithmGeneral has two template +/// parameters that are redundant. Although the first parameter, the class for +/// the actual DeviceAdapterAlgorithm class containing Schedule, and +/// Synchronize is the same as DeviceAdapterAlgorithm, it is +/// made a separate template parameter to avoid a recursive dependence between +/// DeviceAdapterAlgorithmGeneral.h and DeviceAdapterAlgorithm.h +/// +template +struct DeviceAdapterAlgorithmGeneral +{ + //-------------------------------------------------------------------------- + // Get Execution Value + // This method is used internally to get a single element from the execution + // array. Might want to expose this and/or allow actual device adapter + // implementations to provide one. +private: + template + VTKM_CONT_EXPORT + static T GetExecutionValue(const vtkm::cont::ArrayHandle &input, + vtkm::Id index) + { + typedef vtkm::cont::ArrayHandle InputArrayType; + typedef vtkm::cont::ArrayHandle + OutputArrayType; + + OutputArrayType output; + + CopyKernel< + typename InputArrayType::template ExecutionTypes::PortalConst, + typename OutputArrayType::template ExecutionTypes::Portal> + kernel(input.PrepareForInput(DeviceAdapterTag()), + output.PrepareForOutput(1, DeviceAdapterTag()), + index); + + DerivedAlgorithm::Schedule(kernel, 1); + + return output.GetPortalConstControl().Get(0); + } + + //-------------------------------------------------------------------------- + // Copy +private: + template + struct CopyKernel { + InputPortalType InputPortal; + OutputPortalType OutputPortal; + vtkm::Id InputOffset; + vtkm::Id OutputOffset; + + VTKM_CONT_EXPORT + CopyKernel(InputPortalType inputPortal, + OutputPortalType outputPortal, + vtkm::Id inputOffset = 0, + vtkm::Id outputOffset = 0) + : InputPortal(inputPortal), + OutputPortal(outputPortal), + InputOffset(inputOffset), + OutputOffset(outputOffset) + { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const { + this->OutputPortal.Set( + index + this->OutputOffset, + this->InputPortal.Get(index + this->InputOffset)); + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + +public: + template + VTKM_CONT_EXPORT static void Copy(const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle &output) + { + vtkm::Id arraySize = input.GetNumberOfValues(); + + CopyKernel< + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::Portal> + kernel(input.PrepareForInput(DeviceAdapterTag()), + output.PrepareForOutput(arraySize, DeviceAdapterTag())); + + DerivedAlgorithm::Schedule(kernel, arraySize); + } + + //-------------------------------------------------------------------------- + // Lower Bounds +private: + template + struct LowerBoundsKernel { + InputPortalType InputPortal; + ValuesPortalType ValuesPortal; + OutputPortalType OutputPortal; + + VTKM_CONT_EXPORT + LowerBoundsKernel(InputPortalType inputPortal, + ValuesPortalType valuesPortal, + OutputPortalType outputPortal) + : InputPortal(inputPortal), + ValuesPortal(valuesPortal), + OutputPortal(outputPortal) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const { + // This method assumes that (1) InputPortalType can return working + // iterators in the execution environment and that (2) methods not + // specified with VTKM_EXEC_EXPORT (such as the STL algorithms) can be + // called from the execution environment. Neither one of these is + // necessarily true, but it is true for the current uses of this general + // function and I don't want to compete with STL if I don't have to. + + typename InputPortalType::IteratorType resultPos = + std::lower_bound(this->InputPortal.GetIteratorBegin(), + this->InputPortal.GetIteratorEnd(), + this->ValuesPortal.Get(index)); + + vtkm::Id resultIndex = + static_cast( + std::distance(this->InputPortal.GetIteratorBegin(), resultPos)); + this->OutputPortal.Set(index, resultIndex); + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + + template + struct LowerBoundsComparisonKernel { + InputPortalType InputPortal; + ValuesPortalType ValuesPortal; + OutputPortalType OutputPortal; + Compare CompareFunctor; + + VTKM_CONT_EXPORT + LowerBoundsComparisonKernel(InputPortalType inputPortal, + ValuesPortalType valuesPortal, + OutputPortalType outputPortal, + Compare comp) + : InputPortal(inputPortal), + ValuesPortal(valuesPortal), + OutputPortal(outputPortal), + CompareFunctor(comp) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const { + // This method assumes that (1) InputPortalType can return working + // iterators in the execution environment and that (2) methods not + // specified with VTKM_EXEC_EXPORT (such as the STL algorithms) can be + // called from the execution environment. Neither one of these is + // necessarily true, but it is true for the current uses of this general + // function and I don't want to compete with STL if I don't have to. + + typename InputPortalType::IteratorType resultPos = + std::lower_bound(this->InputPortal.GetIteratorBegin(), + this->InputPortal.GetIteratorEnd(), + this->ValuesPortal.Get(index), + this->CompareFunctor); + + vtkm::Id resultIndex = + static_cast( + std::distance(this->InputPortal.GetIteratorBegin(), resultPos)); + this->OutputPortal.Set(index, resultIndex); + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + + +public: + template + VTKM_CONT_EXPORT static void LowerBounds( + const vtkm::cont::ArrayHandle &input, + const vtkm::cont::ArrayHandle &values, + vtkm::cont::ArrayHandle &output) + { + vtkm::Id arraySize = values.GetNumberOfValues(); + + LowerBoundsKernel< + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::Portal> + kernel(input.PrepareForInput(DeviceAdapterTag()), + values.PrepareForInput(DeviceAdapterTag()), + output.PrepareForOutput(arraySize, DeviceAdapterTag())); + + DerivedAlgorithm::Schedule(kernel, arraySize); + } + + template + VTKM_CONT_EXPORT static void LowerBounds( + const vtkm::cont::ArrayHandle &input, + const vtkm::cont::ArrayHandle &values, + vtkm::cont::ArrayHandle &output, + Compare comp) + { + vtkm::Id arraySize = values.GetNumberOfValues(); + + LowerBoundsComparisonKernel< + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::Portal, + Compare> + kernel(input.PrepareForInput(DeviceAdapterTag()), + values.PrepareForInput(DeviceAdapterTag()), + output.PrepareForOutput(arraySize, DeviceAdapterTag()), + comp); + + DerivedAlgorithm::Schedule(kernel, arraySize); + } + + template + VTKM_CONT_EXPORT static void LowerBounds( + const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle &values_output) + { + DeviceAdapterAlgorithmGeneral< + DerivedAlgorithm,DeviceAdapterTag>::LowerBounds(input, + values_output, + values_output); + } + + //-------------------------------------------------------------------------- + // Scan Exclusive +private: + template + struct SetConstantKernel + { + typedef typename PortalType::ValueType ValueType; + PortalType Portal; + ValueType Value; + + VTKM_CONT_EXPORT + SetConstantKernel(const PortalType &portal, ValueType value) + : Portal(portal), Value(value) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const + { + this->Portal.Set(index, this->Value); + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + +public: + template + VTKM_CONT_EXPORT static T ScanExclusive( + const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle& output) + { + typedef vtkm::cont::ArrayHandle + TempArrayType; + typedef vtkm::cont::ArrayHandle OutputArrayType; + + TempArrayType inclusiveScan; + T result = DerivedAlgorithm::ScanInclusive(input, inclusiveScan); + + vtkm::Id numValues = inclusiveScan.GetNumberOfValues(); + if (numValues < 1) + { + return result; + } + + typedef typename TempArrayType::template ExecutionTypes + ::PortalConst SrcPortalType; + SrcPortalType srcPortal = inclusiveScan.PrepareForInput(DeviceAdapterTag()); + + typedef typename OutputArrayType::template ExecutionTypes + ::Portal DestPortalType; + DestPortalType destPortal = output.PrepareForOutput(numValues, + DeviceAdapterTag()); + + // Set first value in output (always 0). + DerivedAlgorithm::Schedule( + SetConstantKernel(destPortal,0), 1); + // Shift remaining values over by one. + DerivedAlgorithm::Schedule( + CopyKernel(srcPortal, + destPortal, + 0, + 1), + numValues - 1); + + return result; + } + + //-------------------------------------------------------------------------- + // Scan Inclusive +private: + template + struct ScanKernel : vtkm::exec::internal::WorkletBase + { + PortalType Portal; + vtkm::Id Stride; + vtkm::Id Offset; + vtkm::Id Distance; + + VTKM_CONT_EXPORT + ScanKernel(const PortalType &portal, vtkm::Id stride, vtkm::Id offset) + : Portal(portal), + Stride(stride), + Offset(offset), + Distance(stride/2) + { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const + { + typedef typename PortalType::ValueType ValueType; + + vtkm::Id leftIndex = this->Offset + index*this->Stride; + vtkm::Id rightIndex = leftIndex + this->Distance; + + if (rightIndex < this->Portal.GetNumberOfValues()) + { + ValueType leftValue = this->Portal.Get(leftIndex); + ValueType rightValue = this->Portal.Get(rightIndex); + this->Portal.Set(rightIndex, leftValue+rightValue); + } + } + }; + +public: + template + VTKM_CONT_EXPORT static T ScanInclusive( + const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle& output) + { + typedef typename + vtkm::cont::ArrayHandle + ::template ExecutionTypes::Portal PortalType; + + DerivedAlgorithm::Copy(input, output); + + vtkm::Id numValues = output.GetNumberOfValues(); + if (numValues < 1) + { + return 0; + } + + PortalType portal = output.PrepareForInPlace(DeviceAdapterTag()); + + vtkm::Id stride; + for (stride = 2; stride-1 < numValues; stride *= 2) + { + ScanKernel kernel(portal, stride, stride/2 - 1); + DerivedAlgorithm::Schedule(kernel, numValues/stride); + } + + // Do reverse operation on odd indices. Start at stride we were just at. + for (stride /= 2; stride > 1; stride /= 2) + { + ScanKernel kernel(portal, stride, stride - 1); + DerivedAlgorithm::Schedule(kernel, numValues/stride); + } + + return GetExecutionValue(output, numValues-1); + } + + //-------------------------------------------------------------------------- + // Sort +private: + template + struct BitonicSortMergeKernel : vtkm::exec::internal::WorkletBase + { + PortalType Portal; + CompareType Compare; + vtkm::Id GroupSize; + + VTKM_CONT_EXPORT + BitonicSortMergeKernel(const PortalType &portal, + const CompareType &compare, + vtkm::Id groupSize) + : Portal(portal), Compare(compare), GroupSize(groupSize) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const { + typedef typename PortalType::ValueType ValueType; + + vtkm::Id groupIndex = index%this->GroupSize; + vtkm::Id blockSize = 2*this->GroupSize; + vtkm::Id blockIndex = index/this->GroupSize; + + vtkm::Id lowIndex = blockIndex * blockSize + groupIndex; + vtkm::Id highIndex = lowIndex + this->GroupSize; + + if (highIndex < this->Portal.GetNumberOfValues()) + { + ValueType lowValue = this->Portal.Get(lowIndex); + ValueType highValue = this->Portal.Get(highIndex); + if (this->Compare(highValue, lowValue)) + { + this->Portal.Set(highIndex, lowValue); + this->Portal.Set(lowIndex, highValue); + } + } + } + }; + + template + struct BitonicSortCrossoverKernel : vtkm::exec::internal::WorkletBase + { + PortalType Portal; + CompareType Compare; + vtkm::Id GroupSize; + + VTKM_CONT_EXPORT + BitonicSortCrossoverKernel(const PortalType &portal, + const CompareType &compare, + vtkm::Id groupSize) + : Portal(portal), Compare(compare), GroupSize(groupSize) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const { + typedef typename PortalType::ValueType ValueType; + + vtkm::Id groupIndex = index%this->GroupSize; + vtkm::Id blockSize = 2*this->GroupSize; + vtkm::Id blockIndex = index/this->GroupSize; + + vtkm::Id lowIndex = blockIndex*blockSize + groupIndex; + vtkm::Id highIndex = blockIndex*blockSize + (blockSize - groupIndex - 1); + + if (highIndex < this->Portal.GetNumberOfValues()) + { + ValueType lowValue = this->Portal.Get(lowIndex); + ValueType highValue = this->Portal.Get(highIndex); + if (this->Compare(highValue, lowValue)) + { + this->Portal.Set(highIndex, lowValue); + this->Portal.Set(lowIndex, highValue); + } + } + } + }; + + struct DefaultCompareFunctor + { + + template + VTKM_EXEC_EXPORT + bool operator()(const T& first, const T& second) const + { + return first < second; + } + }; + +public: + template + VTKM_CONT_EXPORT static void Sort( + vtkm::cont::ArrayHandle &values, + CompareType compare) + { + typedef typename vtkm::cont::ArrayHandle ArrayType; + typedef typename ArrayType::template ExecutionTypes + ::Portal PortalType; + + vtkm::Id numValues = values.GetNumberOfValues(); + if (numValues < 2) { return; } + + PortalType portal = values.PrepareForInPlace(DeviceAdapterTag()); + + vtkm::Id numThreads = 1; + while (numThreads < numValues) { numThreads *= 2; } + numThreads /= 2; + + typedef BitonicSortMergeKernel MergeKernel; + typedef BitonicSortCrossoverKernel CrossoverKernel; + + for (vtkm::Id crossoverSize = 1; + crossoverSize < numValues; + crossoverSize *= 2) + { + DerivedAlgorithm::Schedule(CrossoverKernel(portal,compare,crossoverSize), + numThreads); + for (vtkm::Id mergeSize = crossoverSize/2; mergeSize > 0; mergeSize /= 2) + { + DerivedAlgorithm::Schedule(MergeKernel(portal,compare,mergeSize), + numThreads); + } + } + } + + template + VTKM_CONT_EXPORT static void Sort( + vtkm::cont::ArrayHandle &values) + { + DerivedAlgorithm::Sort(values, DefaultCompareFunctor()); + } + + //-------------------------------------------------------------------------- + // Stream Compact +private: + template + struct StencilToIndexFlagKernel + { + typedef typename StencilPortalType::ValueType StencilValueType; + StencilPortalType StencilPortal; + OutputPortalType OutputPortal; + + VTKM_CONT_EXPORT + StencilToIndexFlagKernel(StencilPortalType stencilPortal, + OutputPortalType outputPortal) + : StencilPortal(stencilPortal), OutputPortal(outputPortal) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const + { + StencilValueType value = this->StencilPortal.Get(index); + bool flag = not_default_constructor()(value); + this->OutputPortal.Set(index, flag ? 1 : 0); + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + + template + struct CopyIfKernel + { + InputPortalType InputPortal; + StencilPortalType StencilPortal; + IndexPortalType IndexPortal; + OutputPortalType OutputPortal; + + VTKM_CONT_EXPORT + CopyIfKernel(InputPortalType inputPortal, + StencilPortalType stencilPortal, + IndexPortalType indexPortal, + OutputPortalType outputPortal) + : InputPortal(inputPortal), + StencilPortal(stencilPortal), + IndexPortal(indexPortal), + OutputPortal(outputPortal) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const + { + typedef typename StencilPortalType::ValueType StencilValueType; + StencilValueType stencilValue = this->StencilPortal.Get(index); + if (not_default_constructor()(stencilValue)) + { + vtkm::Id outputIndex = this->IndexPortal.Get(index); + + typedef typename OutputPortalType::ValueType OutputValueType; + OutputValueType value = this->InputPortal.Get(index); + + this->OutputPortal.Set(outputIndex, value); + } + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + +public: + + template + VTKM_CONT_EXPORT static void StreamCompact( + const vtkm::cont::ArrayHandle& input, + const vtkm::cont::ArrayHandle& stencil, + vtkm::cont::ArrayHandle& output) + { + VTKM_ASSERT_CONT(input.GetNumberOfValues() == stencil.GetNumberOfValues()); + vtkm::Id arrayLength = stencil.GetNumberOfValues(); + + typedef vtkm::cont::ArrayHandle< + vtkm::Id, vtkm::cont::ArrayContainerControlTagBasic> IndexArrayType; + IndexArrayType indices; + + typedef typename vtkm::cont::ArrayHandle + ::template ExecutionTypes::PortalConst + StencilPortalType; + StencilPortalType stencilPortal = + stencil.PrepareForInput(DeviceAdapterTag()); + + typedef typename IndexArrayType + ::template ExecutionTypes::Portal IndexPortalType; + IndexPortalType indexPortal = + indices.PrepareForOutput(arrayLength, DeviceAdapterTag()); + + StencilToIndexFlagKernel< + StencilPortalType, IndexPortalType> indexKernel(stencilPortal, + indexPortal); + + DerivedAlgorithm::Schedule(indexKernel, arrayLength); + + vtkm::Id outArrayLength = DerivedAlgorithm::ScanExclusive(indices, indices); + + typedef typename vtkm::cont::ArrayHandle + ::template ExecutionTypes::PortalConst + InputPortalType; + InputPortalType inputPortal = input.PrepareForInput(DeviceAdapterTag()); + + typedef typename vtkm::cont::ArrayHandle + ::template ExecutionTypes::Portal OutputPortalType; + OutputPortalType outputPortal = + output.PrepareForOutput(outArrayLength, DeviceAdapterTag()); + + CopyIfKernel< + InputPortalType, + StencilPortalType, + IndexPortalType, + OutputPortalType>copyKernel(inputPortal, + stencilPortal, + indexPortal, + outputPortal); + DerivedAlgorithm::Schedule(copyKernel, arrayLength); + } + + template + VTKM_CONT_EXPORT static void StreamCompact( + const vtkm::cont::ArrayHandle &stencil, + vtkm::cont::ArrayHandle &output) + { + typedef vtkm::cont::ArrayHandleCounting CountingHandleType; + + CountingHandleType input = + vtkm::cont::make_ArrayHandleCounting(vtkm::Id(0), + stencil.GetNumberOfValues()); + DerivedAlgorithm::StreamCompact(input, stencil, output); + } + + //-------------------------------------------------------------------------- + // Unique +private: + template + struct ClassifyUniqueKernel { + InputPortalType InputPortal; + StencilPortalType StencilPortal; + + VTKM_CONT_EXPORT + ClassifyUniqueKernel(InputPortalType inputPortal, + StencilPortalType stencilPortal) + : InputPortal(inputPortal), StencilPortal(stencilPortal) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const { + typedef typename StencilPortalType::ValueType ValueType; + if (index == 0) + { + // Always copy first value. + this->StencilPortal.Set(index, ValueType(1)); + } + else + { + ValueType flag = ValueType(this->InputPortal.Get(index-1) + != this->InputPortal.Get(index)); + this->StencilPortal.Set(index, flag); + } + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + + template + struct ClassifyUniqueComparisonKernel { + InputPortalType InputPortal; + StencilPortalType StencilPortal; + Compare CompareFunctor; + + VTKM_CONT_EXPORT + ClassifyUniqueComparisonKernel(InputPortalType inputPortal, + StencilPortalType stencilPortal, + Compare comp): + InputPortal(inputPortal), + StencilPortal(stencilPortal), + CompareFunctor(comp) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const { + typedef typename StencilPortalType::ValueType ValueType; + if (index == 0) + { + // Always copy first value. + this->StencilPortal.Set(index, ValueType(1)); + } + else + { + //comparison predicate returns true when they match + const bool same = !(this->CompareFunctor(this->InputPortal.Get(index-1), + this->InputPortal.Get(index))); + ValueType flag = ValueType(same); + this->StencilPortal.Set(index, flag); + } + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + +public: + template + VTKM_CONT_EXPORT static void Unique( + vtkm::cont::ArrayHandle &values) + { + vtkm::cont::ArrayHandle + stencilArray; + vtkm::Id inputSize = values.GetNumberOfValues(); + + ClassifyUniqueKernel< + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::Portal> + classifyKernel(values.PrepareForInput(DeviceAdapterTag()), + stencilArray.PrepareForOutput(inputSize, DeviceAdapterTag())); + DerivedAlgorithm::Schedule(classifyKernel, inputSize); + + vtkm::cont::ArrayHandle + outputArray; + + DerivedAlgorithm::StreamCompact(values, stencilArray, outputArray); + + DerivedAlgorithm::Copy(outputArray, values); + } + + template + VTKM_CONT_EXPORT static void Unique( + vtkm::cont::ArrayHandle &values, + Compare comp) + { + vtkm::cont::ArrayHandle + stencilArray; + vtkm::Id inputSize = values.GetNumberOfValues(); + + ClassifyUniqueComparisonKernel< + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::Portal, + Compare> + classifyKernel(values.PrepareForInput(DeviceAdapterTag()), + stencilArray.PrepareForOutput(inputSize, DeviceAdapterTag()), + comp); + DerivedAlgorithm::Schedule(classifyKernel, inputSize); + + vtkm::cont::ArrayHandle + outputArray; + + DerivedAlgorithm::StreamCompact(values, stencilArray, outputArray); + + DerivedAlgorithm::Copy(outputArray, values); + } + + //-------------------------------------------------------------------------- + // Upper bounds +private: + template + struct UpperBoundsKernel { + InputPortalType InputPortal; + ValuesPortalType ValuesPortal; + OutputPortalType OutputPortal; + + VTKM_CONT_EXPORT + UpperBoundsKernel(InputPortalType inputPortal, + ValuesPortalType valuesPortal, + OutputPortalType outputPortal) + : InputPortal(inputPortal), + ValuesPortal(valuesPortal), + OutputPortal(outputPortal) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const { + // This method assumes that (1) InputPortalType can return working + // iterators in the execution environment and that (2) methods not + // specified with VTKM_EXEC_EXPORT (such as the STL algorithms) can be + // called from the execution environment. Neither one of these is + // necessarily true, but it is true for the current uses of this general + // function and I don't want to compete with STL if I don't have to. + + typename InputPortalType::IteratorType resultPos = + std::upper_bound(this->InputPortal.GetIteratorBegin(), + this->InputPortal.GetIteratorEnd(), + this->ValuesPortal.Get(index)); + + vtkm::Id resultIndex = + static_cast( + std::distance(this->InputPortal.GetIteratorBegin(), resultPos)); + this->OutputPortal.Set(index, resultIndex); + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + + + template + struct UpperBoundsKernelComparisonKernel { + InputPortalType InputPortal; + ValuesPortalType ValuesPortal; + OutputPortalType OutputPortal; + Compare CompareFunctor; + + VTKM_CONT_EXPORT + UpperBoundsKernelComparisonKernel(InputPortalType inputPortal, + ValuesPortalType valuesPortal, + OutputPortalType outputPortal, + Compare comp) + : InputPortal(inputPortal), + ValuesPortal(valuesPortal), + OutputPortal(outputPortal), + CompareFunctor(comp) { } + + VTKM_EXEC_EXPORT + void operator()(vtkm::Id index) const { + // This method assumes that (1) InputPortalType can return working + // iterators in the execution environment and that (2) methods not + // specified with VTKM_EXEC_EXPORT (such as the STL algorithms) can be + // called from the execution environment. Neither one of these is + // necessarily true, but it is true for the current uses of this general + // function and I don't want to compete with STL if I don't have to. + + typename InputPortalType::IteratorType resultPos = + std::upper_bound(this->InputPortal.GetIteratorBegin(), + this->InputPortal.GetIteratorEnd(), + this->ValuesPortal.Get(index), + this->CompareFunctor); + + vtkm::Id resultIndex = + static_cast( + std::distance(this->InputPortal.GetIteratorBegin(), resultPos)); + this->OutputPortal.Set(index, resultIndex); + } + + VTKM_CONT_EXPORT + void SetErrorMessageBuffer(const vtkm::exec::internal::ErrorMessageBuffer &) + { } + }; + +public: + template + VTKM_CONT_EXPORT static void UpperBounds( + const vtkm::cont::ArrayHandle &input, + const vtkm::cont::ArrayHandle &values, + vtkm::cont::ArrayHandle &output) + { + vtkm::Id arraySize = values.GetNumberOfValues(); + + UpperBoundsKernel< + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::Portal> + kernel(input.PrepareForInput(DeviceAdapterTag()), + values.PrepareForInput(DeviceAdapterTag()), + output.PrepareForOutput(arraySize, DeviceAdapterTag())); + + DerivedAlgorithm::Schedule(kernel, arraySize); + } + + template + VTKM_CONT_EXPORT static void UpperBounds( + const vtkm::cont::ArrayHandle &input, + const vtkm::cont::ArrayHandle &values, + vtkm::cont::ArrayHandle &output, + Compare comp) + { + vtkm::Id arraySize = values.GetNumberOfValues(); + + UpperBoundsKernelComparisonKernel< + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::PortalConst, + typename vtkm::cont::ArrayHandle::template ExecutionTypes::Portal, + Compare> + kernel(input.PrepareForInput(DeviceAdapterTag()), + values.PrepareForInput(DeviceAdapterTag()), + output.PrepareForOutput(arraySize, DeviceAdapterTag()), + comp); + + DerivedAlgorithm::Schedule(kernel, arraySize); + } + + template + VTKM_CONT_EXPORT static void UpperBounds( + const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle &values_output) + { + DeviceAdapterAlgorithmGeneral::UpperBounds(input, values_output, values_output); + } + +}; + + +} +} +} // namespace vtkm::cont::internal + +#endif //vtkm_cont_internal_DeviceAdapterAlgorithmGeneral_h diff --git a/vtkm/cont/internal/DeviceAdapterAlgorithmSerial.h b/vtkm/cont/internal/DeviceAdapterAlgorithmSerial.h new file mode 100644 index 000000000..3444103e4 --- /dev/null +++ b/vtkm/cont/internal/DeviceAdapterAlgorithmSerial.h @@ -0,0 +1,190 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_cont_internal_DeviceAdapterAlgorithmSerial_h +#define vtkm_cont_internal_DeviceAdapterAlgorithmSerial_h + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +namespace vtkm { +namespace cont { + +template<> +struct DeviceAdapterAlgorithm : + vtkm::cont::internal::DeviceAdapterAlgorithmGeneral< + DeviceAdapterAlgorithm, + vtkm::cont::DeviceAdapterTagSerial> +{ +private: + typedef vtkm::cont::DeviceAdapterTagSerial Device; + +public: + template + VTKM_CONT_EXPORT static T ScanInclusive( + const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle& output) + { + typedef typename vtkm::cont::ArrayHandle + ::template ExecutionTypes::Portal PortalOut; + typedef typename vtkm::cont::ArrayHandle + ::template ExecutionTypes::PortalConst PortalIn; + + vtkm::Id numberOfValues = input.GetNumberOfValues(); + + PortalIn inputPortal = input.PrepareForInput(Device()); + PortalOut outputPortal = output.PrepareForOutput(numberOfValues, Device()); + + if (numberOfValues <= 0) { return 0; } + + std::partial_sum(inputPortal.GetIteratorBegin(), + inputPortal.GetIteratorEnd(), + outputPortal.GetIteratorBegin()); + + // Return the value at the last index in the array, which is the full sum. + return outputPortal.Get(numberOfValues - 1); + } + + template + VTKM_CONT_EXPORT static T ScanExclusive( + const vtkm::cont::ArrayHandle &input, + vtkm::cont::ArrayHandle& output) + { + typedef typename vtkm::cont::ArrayHandle + ::template ExecutionTypes::Portal PortalOut; + typedef typename vtkm::cont::ArrayHandle + ::template ExecutionTypes::PortalConst PortalIn; + + vtkm::Id numberOfValues = input.GetNumberOfValues(); + + PortalIn inputPortal = input.PrepareForInput(Device()); + PortalOut outputPortal = output.PrepareForOutput(numberOfValues, Device()); + + if (numberOfValues <= 0) { return 0; } + + std::partial_sum(inputPortal.GetIteratorBegin(), + inputPortal.GetIteratorEnd(), + outputPortal.GetIteratorBegin()); + + T fullSum = outputPortal.Get(numberOfValues - 1); + + // Shift right by one + std::copy_backward(outputPortal.GetIteratorBegin(), + outputPortal.GetIteratorEnd()-1, + outputPortal.GetIteratorEnd()); + outputPortal.Set(0, 0); + return fullSum; + } + +private: + // This runs in the execution environment. + template + class ScheduleKernel + { + public: + ScheduleKernel(const FunctorType &functor) + : Functor(functor) { } + + //needed for when calling from schedule on a range + VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const + { + this->Functor(index); + } + + private: + const FunctorType Functor; + }; + +public: + template + VTKM_CONT_EXPORT static void Schedule(Functor functor, + vtkm::Id numInstances) + { + const vtkm::Id MESSAGE_SIZE = 1024; + char errorString[MESSAGE_SIZE]; + errorString[0] = '\0'; + vtkm::exec::internal::ErrorMessageBuffer + errorMessage(errorString, MESSAGE_SIZE); + + functor.SetErrorMessageBuffer(errorMessage); + + DeviceAdapterAlgorithm::ScheduleKernel kernel(functor); + + std::for_each( + ::boost::counting_iterator(0), + ::boost::counting_iterator(numInstances), + kernel); + + if (errorMessage.IsErrorRaised()) + { + throw vtkm::cont::ErrorExecution(errorString); + } + } + + template + VTKM_CONT_EXPORT + static void Schedule(FunctorType functor, vtkm::Id3 rangeMax) + { + DeviceAdapterAlgorithm::Schedule(functor, + rangeMax[0] * rangeMax[1] * rangeMax[2] ); + } + + template + VTKM_CONT_EXPORT static void Sort(vtkm::cont::ArrayHandle& values) + { + typedef typename vtkm::cont::ArrayHandle + ::template ExecutionTypes::Portal PortalType; + + PortalType arrayPortal = values.PrepareForInPlace(Device()); + std::sort(arrayPortal.GetIteratorBegin(), arrayPortal.GetIteratorEnd()); + } + + template + VTKM_CONT_EXPORT static void Sort(vtkm::cont::ArrayHandle& values, + Compare comp) + { + typedef typename vtkm::cont::ArrayHandle + ::template ExecutionTypes::Portal PortalType; + + PortalType arrayPortal = values.PrepareForInPlace(Device()); + std::sort(arrayPortal.GetIteratorBegin(), arrayPortal.GetIteratorEnd(),comp); + } + + VTKM_CONT_EXPORT static void Synchronize() + { + // Nothing to do. This device is serial and has no asynchronous operations. + } + +}; + +} +} // namespace vtkm::cont + +#endif //vtkm_cont_internal_DeviceAdapterAlgorithmSerial_h diff --git a/vtkm/cont/internal/DeviceAdapterError.h b/vtkm/cont/internal/DeviceAdapterError.h new file mode 100644 index 000000000..e251f80a9 --- /dev/null +++ b/vtkm/cont/internal/DeviceAdapterError.h @@ -0,0 +1,32 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_cont_internal_DeviceAdapterError_h +#define vtkm_cont_internal_DeviceAdapterError_h + +#include + +/// This is an invalid DeviceAdapter. The point of this class is to include the +/// header file to make this invalid class the default DeviceAdapter. From that +/// point, you have to specify an appropriate DeviceAdapter or else get a +/// compile error. +/// +VTKM_CREATE_DEVICE_ADAPTER(Error); + +#endif //vtkm_cont_internal_DeviceAdapterError_h diff --git a/vtkm/cont/internal/DeviceAdapterTag.h b/vtkm/cont/internal/DeviceAdapterTag.h index abd840cf9..f755831fa 100644 --- a/vtkm/cont/internal/DeviceAdapterTag.h +++ b/vtkm/cont/internal/DeviceAdapterTag.h @@ -91,6 +91,11 @@ struct DeviceAdapterTagCheck #include #define VTKM_DEFAULT_DEVICE_ADAPTER_TAG ::vtkm::cont::DeviceAdapterTagSerial +#elif VTKM_DEVICE_ADAPTER == VTKM_DEVICE_ADAPTER_ERROR + +#include +#define VTKM_DEFAULT_DEVICE_ADAPTER_TAG ::vtkm::cont::DeviceAdapterTagError + #elif (VTKM_DEVICE_ADAPTER == VTKM_DEVICE_ADAPTER_UNDEFINED) || !defined(VTKM_DEVICE_ADAPTER) #ifndef VTKM_DEFAULT_DEVICE_ADAPTER_TAG diff --git a/vtkm/cont/internal/testing/CMakeLists.txt b/vtkm/cont/internal/testing/CMakeLists.txt index 018c46855..9ab49343b 100644 --- a/vtkm/cont/internal/testing/CMakeLists.txt +++ b/vtkm/cont/internal/testing/CMakeLists.txt @@ -22,6 +22,6 @@ set(unit_tests UnitTestArrayManagerExecutionShareWithControl.cxx UnitTestArrayPortalFromIterators.cxx - # UnitTestIteratorFromArrayPortal.cxx + UnitTestIteratorFromArrayPortal.cxx ) vtkm_unit_tests(SOURCES ${unit_tests}) diff --git a/vtkm/cont/testing/CMakeLists.txt b/vtkm/cont/testing/CMakeLists.txt index 4424f84fa..782a32928 100644 --- a/vtkm/cont/testing/CMakeLists.txt +++ b/vtkm/cont/testing/CMakeLists.txt @@ -20,6 +20,7 @@ set(headers Testing.h + TestingDeviceAdapter.h ) vtkm_declare_headers(${headers}) @@ -28,7 +29,11 @@ set(unit_tests UnitTestArrayContainerControlBasic.cxx UnitTestArrayContainerControlImplicit.cxx UnitTestArrayHandle.cxx + UnitTestArrayHandleCounting.cxx UnitTestContTesting.cxx + UnitTestDeviceAdapterAlgorithmDependency.cxx + UnitTestDeviceAdapterAlgorithmGeneral.cxx + UnitTestDeviceAdapterSerial.cxx ) vtkm_unit_tests(SOURCES ${unit_tests}) diff --git a/vtkm/cont/testing/TestingDeviceAdapter.h b/vtkm/cont/testing/TestingDeviceAdapter.h new file mode 100644 index 000000000..92a8af564 --- /dev/null +++ b/vtkm/cont/testing/TestingDeviceAdapter.h @@ -0,0 +1,1330 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_cont_testing_TestingDeviceAdapter_h +#define vtkm_cont_testing_TestingDeviceAdapter_h + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +namespace vtkm { +namespace cont { +namespace testing { + +namespace +{ + struct SortLess + { + template + VTKM_EXEC_CONT_EXPORT bool operator()(const T& a,const T& b) const + { + typedef typename vtkm::TypeTraits::DimensionalityTag Dimensionality; + return this->compare(a,b,Dimensionality()); + } + template + VTKM_EXEC_CONT_EXPORT bool compare(const T& a,const T& b, + vtkm::TypeTraitsScalarTag) const + { + return a < b; + } + template + VTKM_EXEC_CONT_EXPORT bool compare(const T& a,const T& b, + vtkm::TypeTraitsVectorTag) const + { + enum{SIZE = vtkm::VectorTraits::NUM_COMPONENTS}; + bool valid = true; + for(unsigned int i=0; i < SIZE && valid; ++i) + { + valid = a[i] < b[i]; + } + return valid; + } + }; + + struct SortGreater + { + template + VTKM_EXEC_CONT_EXPORT bool operator()(const T& a,const T& b) const + { + typedef typename vtkm::TypeTraits::DimensionalityTag Dimensionality; + return this->compare(a,b,Dimensionality()); + } + template + VTKM_EXEC_CONT_EXPORT bool compare(const T& a,const T& b, + vtkm::TypeTraitsScalarTag) const + { + return a > b; + } + template + VTKM_EXEC_CONT_EXPORT bool compare(const T& a,const T& b, + vtkm::TypeTraitsVectorTag) const + { + enum{SIZE = vtkm::VectorTraits::NUM_COMPONENTS}; + bool valid = true; + for(unsigned int i=0; i < SIZE && valid; ++i) + { + valid = a[i] > b[i]; + } + return valid; + } + }; +} + +#define ERROR_MESSAGE "Got an error." +#define ARRAY_SIZE 500 +#define OFFSET 1000 +#define DIM 64 + +/// This class has a single static member, Run, that tests the templated +/// DeviceAdapter for conformance. +/// +template +struct TestingDeviceAdapter +{ +private: + typedef vtkm::cont::ArrayContainerControlTagBasic ArrayContainerControlTag; + + typedef vtkm::cont::ArrayHandle + IdArrayHandle; + + typedef vtkm::cont::ArrayHandle + ScalarArrayHandle; + + typedef vtkm::cont::internal::ArrayManagerExecution< + vtkm::Id, ArrayContainerControlTag, DeviceAdapterTag> + IdArrayManagerExecution; + + typedef vtkm::cont::internal::ArrayContainerControl IdContainer; + + typedef typename IdArrayHandle::template ExecutionTypes + ::Portal IdPortalType; + typedef typename IdArrayHandle::template ExecutionTypes + ::PortalConst IdPortalConstType; + + typedef vtkm::cont::ArrayHandle + Vector3ArrayHandle; + + typedef vtkm::cont::DeviceAdapterAlgorithm + Algorithm; + +public: + // Cuda kernels have to be public (in Cuda 4.0). + + struct CopyArrayKernel + { + VTKM_CONT_EXPORT + CopyArrayKernel(const IdPortalConstType &input, + const IdPortalType &output) + : InputArray(input), OutputArray(output) { } + + VTKM_EXEC_EXPORT void operator()( + vtkm::Id index, + const vtkm::exec::internal::ErrorMessageBuffer &) const + { + this->OutputArray.Set(index, this->InputArray.Get(index)); + } + + VTKM_CONT_EXPORT void SetErrorMessageBuffer( + const vtkm::exec::internal::ErrorMessageBuffer &) { } + + IdPortalConstType InputArray; + IdPortalType OutputArray; + }; + + struct ClearArrayKernel + { + VTKM_CONT_EXPORT + ClearArrayKernel(const IdPortalType &array) : Array(array) { } + + VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const + { + this->Array.Set(index, OFFSET); + } + + VTKM_CONT_EXPORT void SetErrorMessageBuffer( + const vtkm::exec::internal::ErrorMessageBuffer &) { } + + IdPortalType Array; + }; + + struct ClearArrayMapKernel //: public vtkm::exec::WorkletMapField + { + + // typedef void ControlSignature(Field(Out)); + // typedef void ExecutionSignature(_1); + + template + VTKM_EXEC_EXPORT void operator()(T& value) const + { + value = OFFSET; + } + }; + + struct AddArrayKernel + { + VTKM_CONT_EXPORT + AddArrayKernel(const IdPortalType &array) : Array(array) { } + + VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const + { + this->Array.Set(index, this->Array.Get(index) + index); + } + + VTKM_CONT_EXPORT void SetErrorMessageBuffer( + const vtkm::exec::internal::ErrorMessageBuffer &) { } + + IdPortalType Array; + }; + + struct OneErrorKernel + { + VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const + { + if (index == ARRAY_SIZE/2) + { + this->ErrorMessage.RaiseError(ERROR_MESSAGE); + } + } + + VTKM_CONT_EXPORT void SetErrorMessageBuffer( + const vtkm::exec::internal::ErrorMessageBuffer &errorMessage) + { + this->ErrorMessage = errorMessage; + } + + vtkm::exec::internal::ErrorMessageBuffer ErrorMessage; + }; + + struct AllErrorKernel + { + VTKM_EXEC_EXPORT void operator()(vtkm::Id vtkmNotUsed(index)) const + { + this->ErrorMessage.RaiseError(ERROR_MESSAGE); + } + + VTKM_CONT_EXPORT void SetErrorMessageBuffer( + const vtkm::exec::internal::ErrorMessageBuffer &errorMessage) + { + this->ErrorMessage = errorMessage; + } + + vtkm::exec::internal::ErrorMessageBuffer ErrorMessage; + }; + + struct OffsetPlusIndexKernel + { + VTKM_CONT_EXPORT + OffsetPlusIndexKernel(const IdPortalType &array) : Array(array) { } + + VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const + { + this->Array.Set(index, OFFSET + index); + } + + VTKM_CONT_EXPORT void SetErrorMessageBuffer( + const vtkm::exec::internal::ErrorMessageBuffer &) { } + + IdPortalType Array; + }; + + struct MarkOddNumbersKernel + { + VTKM_CONT_EXPORT + MarkOddNumbersKernel(const IdPortalType &array) : Array(array) { } + + VTKM_EXEC_EXPORT void operator()(vtkm::Id index) const + { + this->Array.Set(index, index%2); + } + + VTKM_CONT_EXPORT void SetErrorMessageBuffer( + const vtkm::exec::internal::ErrorMessageBuffer &) { } + + IdPortalType Array; + }; + + struct NGMult //: public vtkm::exec::WorkletMapField + { + // typedef void ControlSignature(Field(In), Field(In), Field(Out)); + // typedef _3 ExecutionSignature(_1, _2); + + template + VTKM_EXEC_EXPORT T operator()(T a, T b) const + { + return a * b; + } + }; + + struct NGNoOp //: public vtkm::exec::WorkletMapField + { + // typedef void ControlSignature(Field(In), Field(Out)); + // typedef _2 ExecutionSignature(_1); + + template + VTKM_EXEC_EXPORT T operator()(T a) const + { + return a; + } + }; + + struct FuseAll + { + template + VTKM_EXEC_EXPORT bool operator()(const T&, const T&) const + { + //binary predicates for unique return true if they are the same + return true; + } + }; + + +private: + + template + static VTKM_CONT_EXPORT + vtkm::cont::ArrayHandle + MakeArrayHandle(const T *array, vtkm::Id length) + { + return vtkm::cont::make_ArrayHandle(array, + length, + ArrayContainerControlTagBasic()); + } + + template + static VTKM_CONT_EXPORT + vtkm::cont::ArrayHandle + MakeArrayHandle(const std::vector& array) + { + return vtkm::cont::make_ArrayHandle(array, + ArrayContainerControlTagBasic()); + } + + static VTKM_CONT_EXPORT void TestDeviceAdapterTag() + { + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing device adapter tag" << std::endl; + + typedef vtkm::cont::internal::DeviceAdapterTraits Traits; + typedef vtkm::cont::internal::DeviceAdapterTraits< + vtkm::cont::DeviceAdapterTagError> ErrorTraits; + + VTKM_TEST_ASSERT(Traits::GetId() == Traits::GetId(), + "Device adapter Id does not equal itself."); + VTKM_TEST_ASSERT(Traits::GetId() != ErrorTraits::GetId(), + "Device adapter Id not distinguishable from others."); + } + + // Note: this test does not actually test to make sure the data is available + // in the execution environment. It tests to make sure data gets to the array + // and back, but it is possible that the data is not available in the + // execution environment. + static VTKM_CONT_EXPORT void TestArrayManagerExecution() + { + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing ArrayManagerExecution" << std::endl; + + typedef vtkm::cont::internal::ArrayManagerExecution< + vtkm::Scalar,ArrayContainerControlTagBasic,DeviceAdapterTag> + ArrayManagerExecution; + + // Create original input array. + vtkm::Scalar inputArray[ARRAY_SIZE*2]; + for (vtkm::Id index = 0; index < ARRAY_SIZE*2; index++) + { + inputArray[index] = index; + } + ::vtkm::cont::internal::ArrayPortalFromIterators + inputPortal(inputArray, inputArray+ARRAY_SIZE*2); + ArrayManagerExecution inputManager; + inputManager.LoadDataForInput( + ::vtkm::cont::internal::ArrayPortalFromIterators(inputPortal)); + + // Change size. + inputManager.Shrink(ARRAY_SIZE); + + // Copy array back. + vtkm::Scalar outputArray[ARRAY_SIZE]; + inputManager.CopyInto(outputArray); + + // Check array. + for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) + { + VTKM_TEST_ASSERT(outputArray[index] == index, + "Did not get correct values from array."); + } + } + + static VTKM_CONT_EXPORT void TestOutOfMemory() + { + // Only test out of memory with 64 bit ids. If there are 32 bit ids on + // a 64 bit OS (common), it is simply too hard to get a reliable allocation + // that is too much memory. +#ifdef VTKM_USE_64BIT_IDS + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing Out of Memory" << std::endl; + try + { + std::cout << "Do array allocation that should fail." << std::endl; + vtkm::cont::internal::ArrayManagerExecution< + vtkm::Vector4,ArrayContainerControlTagBasic,DeviceAdapterTag> + bigManager; + vtkm::cont::internal::ArrayContainerControl< + vtkm::Vector4, ArrayContainerControlTagBasic> supportArray; + const vtkm::Id bigSize = 0x7FFFFFFFFFFFFFFFLL; + bigManager.AllocateArrayForOutput(supportArray, bigSize); + // It does not seem reasonable to get here. The previous call should fail. + VTKM_TEST_FAIL("A ridiculously sized allocation succeeded. Either there " + "was a failure that was not reported but should have been " + "or the width of vtkm::Id is not large enough to express all " + "array sizes."); + } + catch (vtkm::cont::ErrorControlOutOfMemory error) + { + std::cout << "Got the expected error: " << error.GetMessage() << std::endl; + } +#else + std::cout << "--------- Skiping out of memory test" << std::endl; +#endif + } + +// VTKM_CONT_EXPORT static void TestTimer() +// { +// std::cout << "-------------------------------------------" << std::endl; +// std::cout << "Testing Timer" << std::endl; + +// vtkm::cont::Timer timer; + +// #ifndef _WIN32 +// sleep(1); +// #else +// Sleep(1000); +// #endif + +// vtkm::Scalar elapsedTime = timer.GetElapsedTime(); + +// std::cout << "Elapsed time: " << elapsedTime << std::endl; + +// VTKM_TEST_ASSERT(elapsedTime > 0.999, +// "Timer did not capture full second wait."); +// VTKM_TEST_ASSERT(elapsedTime < 2.0, +// "Timer counted too far or system really busy."); +// } + + static VTKM_CONT_EXPORT void TestAlgorithmSchedule() + { + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing single value Scheduling with vtkm::Id" << std::endl; + + { + std::cout << "Allocating execution array" << std::endl; + IdContainer container; + IdArrayManagerExecution manager; + manager.AllocateArrayForOutput(container, 1); + + std::cout << "Running clear." << std::endl; + Algorithm::Schedule(ClearArrayKernel(manager.GetPortal()), 1); + + std::cout << "Running add." << std::endl; + Algorithm::Schedule(AddArrayKernel(manager.GetPortal()), 1); + + std::cout << "Checking results." << std::endl; + manager.RetrieveOutputData(container); + + for (vtkm::Id index = 0; index < 1; index++) + { + vtkm::Scalar value = container.GetPortalConst().Get(index); + VTKM_TEST_ASSERT(value == index + OFFSET, + "Got bad value for single value scheduled kernel."); + } + } //release memory + + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing Schedule with vtkm::Id" << std::endl; + + { + std::cout << "Allocating execution array" << std::endl; + IdContainer container; + IdArrayManagerExecution manager; + manager.AllocateArrayForOutput(container, ARRAY_SIZE); + + std::cout << "Running clear." << std::endl; + Algorithm::Schedule(ClearArrayKernel(manager.GetPortal()), ARRAY_SIZE); + + std::cout << "Running add." << std::endl; + Algorithm::Schedule(AddArrayKernel(manager.GetPortal()), ARRAY_SIZE); + + std::cout << "Checking results." << std::endl; + manager.RetrieveOutputData(container); + + for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) + { + vtkm::Id value = container.GetPortalConst().Get(index); + VTKM_TEST_ASSERT(value == index + OFFSET, + "Got bad value for scheduled kernels."); + } + } //release memory + + //verify that the schedule call works with id3 + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing Schedule with vtkm::Id3" << std::endl; + + { + std::cout << "Allocating execution array" << std::endl; + IdContainer container; + IdArrayManagerExecution manager; + vtkm::Id DIM_SIZE = std::pow(ARRAY_SIZE, 1/3.0f); + manager.AllocateArrayForOutput(container, + DIM_SIZE * DIM_SIZE * DIM_SIZE); + vtkm::Id3 maxRange(DIM_SIZE); + + std::cout << "Running clear." << std::endl; + Algorithm::Schedule(ClearArrayKernel(manager.GetPortal()), maxRange); + + std::cout << "Running add." << std::endl; + Algorithm::Schedule(AddArrayKernel(manager.GetPortal()), maxRange); + + std::cout << "Checking results." << std::endl; + manager.RetrieveOutputData(container); + + const vtkm::Id maxId = DIM_SIZE * DIM_SIZE * DIM_SIZE; + for (vtkm::Id index = 0; index < maxId; index++) + { + vtkm::Id value = container.GetPortalConst().Get(index); + VTKM_TEST_ASSERT(value == index + OFFSET, + "Got bad value for scheduled vtkm::Id3 kernels."); + } + } //release memory + } + + // static VTKM_CONT_EXPORT void TestDispatcher() + // { + // std::cout << "-------------------------------------------" << std::endl; + // std::cout << "Testing vtkm::cont::Dispatcher* classes" << std::endl; + + // std::cout << "Testing vtkm::cont::Dispatcher with array of size 1" << std::endl; + + // std::vector singleElement; singleElement.push_back(1234); + // IdArrayHandle hSingleElement = MakeArrayHandle(singleElement); + // IdArrayHandle hResult; + + // vtkm::cont::DispatcherMapField< NGNoOp, DeviceAdapterTag > dispatcherNoOp; + // dispatcherNoOp.Invoke( hSingleElement, hResult ); + + // // output + // std::cout << "hResult.GetNumberOfValues(): " << hResult.GetNumberOfValues() << std::endl; + // for (vtkm::Id i = 0; i < hResult.GetNumberOfValues(); ++i) + // { + // std::cout << hResult.GetPortalConstControl().Get(i) << ","; + // } + // std::cout << std::endl; + + // // assert + // VTKM_TEST_ASSERT( + // hSingleElement.GetNumberOfValues() == hResult.GetNumberOfValues(), + // "out handle of single scheduling is wrong size"); + // VTKM_TEST_ASSERT(singleElement[0] == 1234, + // "output of single scheduling is incorrect"); + + // std::vector field(ARRAY_SIZE); + // for (vtkm::Id i = 0; i < ARRAY_SIZE; i++) + // { + // field[i]=i; + // } + // ScalarArrayHandle fieldHandle = MakeArrayHandle(field); + // ScalarArrayHandle multHandle; + + // std::cout << "Running NG Multiply worklet with two handles" << std::endl; + + // vtkm::cont::DispatcherMapField< NGMult, DeviceAdapterTag > dispatcherMult; + // dispatcherMult.Invoke( fieldHandle, fieldHandle, multHandle ); + + // typename ScalarArrayHandle::PortalConstControl multPortal = + // multHandle.GetPortalConstControl(); + + // for (vtkm::Id i = 0; i < ARRAY_SIZE; i++) + // { + // vtkm::Scalar squareValue = multPortal.Get(i); + // vtkm::Scalar squareTrue = field[i]*field[i]; + // VTKM_TEST_ASSERT(test_equal(squareValue, squareTrue), + // "Got bad multiply result"); + // } + + // std::cout << "Running NG Multiply worklet with handle and constant" << std::endl; + // dispatcherMult.Invoke(4.0f,fieldHandle, multHandle); + // multPortal = multHandle.GetPortalConstControl(); + + // for (vtkm::Id i = 0; i < ARRAY_SIZE; i++) + // { + // vtkm::Scalar squareValue = multPortal.Get(i); + // vtkm::Scalar squareTrue = field[i]*4.0f; + // VTKM_TEST_ASSERT(test_equal(squareValue, squareTrue), + // "Got bad multiply result"); + // } + + + // std::cout << "Testing Schedule on Subset" << std::endl; + // std::vector fullField(ARRAY_SIZE); + // std::vector subSetLookup(ARRAY_SIZE/2); + // for (vtkm::Id i = 0; i < ARRAY_SIZE; i++) + // { + // field[i]=i; + // if(i%2==0) + // { + // subSetLookup[i/2]=i; + // } + // } + + // IdArrayHandle subSetLookupHandle = MakeArrayHandle(subSetLookup); + // ScalarArrayHandle fullFieldHandle = MakeArrayHandle(fullField); + + // std::cout << "Running clear on subset." << std::endl; + // vtkm::cont::DispatcherMapField< ClearArrayMapKernel, + // DeviceAdapterTag > dispatcherClear; + // dispatcherClear.Invoke( + // make_Permutation(subSetLookupHandle,fullFieldHandle,ARRAY_SIZE)); + + // for (vtkm::Id index = 0; index < ARRAY_SIZE; index+=2) + // { + // vtkm::Id value = fullFieldHandle.GetPortalConstControl().Get(index); + // VTKM_TEST_ASSERT(value == OFFSET, + // "Got bad value for subset scheduled kernel."); + // } + // } + + static VTKM_CONT_EXPORT void TestStreamCompact() + { + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing Stream Compact" << std::endl; + + //test the version of compact that takes in input and uses it as a stencil + //and uses the index of each item as the value to place in the result vector + IdArrayHandle array; + IdArrayHandle result; + + //construct the index array + + Algorithm::Schedule( + MarkOddNumbersKernel(array.PrepareForOutput(ARRAY_SIZE, + DeviceAdapterTag())), + ARRAY_SIZE); + + Algorithm::StreamCompact(array, result); + VTKM_TEST_ASSERT(result.GetNumberOfValues() == array.GetNumberOfValues()/2, + "result of compacation has an incorrect size"); + + for (vtkm::Id index = 0; index < result.GetNumberOfValues(); index++) + { + const vtkm::Id value = result.GetPortalConstControl().Get(index); + VTKM_TEST_ASSERT(value == (index*2)+1, + "Incorrect value in compaction results."); + } + } + + static VTKM_CONT_EXPORT void TestStreamCompactWithStencil() + { + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing Stream Compact with stencil" << std::endl; + + IdArrayHandle array; + IdArrayHandle stencil; + IdArrayHandle result; + + //construct the index array + Algorithm::Schedule( + OffsetPlusIndexKernel(array.PrepareForOutput(ARRAY_SIZE, + DeviceAdapterTag())), + ARRAY_SIZE); + Algorithm::Schedule( + MarkOddNumbersKernel(stencil.PrepareForOutput(ARRAY_SIZE, + DeviceAdapterTag())), + ARRAY_SIZE); + + Algorithm::StreamCompact(array,stencil,result); + VTKM_TEST_ASSERT(result.GetNumberOfValues() == array.GetNumberOfValues()/2, + "result of compacation has an incorrect size"); + + for (vtkm::Id index = 0; index < result.GetNumberOfValues(); index++) + { + const vtkm::Id value = result.GetPortalConstControl().Get(index); + VTKM_TEST_ASSERT(value == (OFFSET + (index*2)+1), + "Incorrect value in compaction result."); + } + } + + static VTKM_CONT_EXPORT void TestOrderedUniqueValues() + { + std::cout << "-------------------------------------------------" << std::endl; + std::cout << "Testing Sort, Unique, LowerBounds and UpperBounds" << std::endl; + vtkm::Id testData[ARRAY_SIZE]; + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + testData[i]= OFFSET+(i % 50); + } + IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); + + IdArrayHandle handle; + IdArrayHandle handle1; + IdArrayHandle temp; + Algorithm::Copy(input,temp); + Algorithm::Sort(temp); + Algorithm::Unique(temp); + + //verify lower and upper bounds work + Algorithm::LowerBounds(temp,input,handle); + Algorithm::UpperBounds(temp,input,handle1); + + // Check to make sure that temp was resized correctly during Unique. + // (This was a discovered bug at one point.) + temp.GetPortalConstControl(); // Forces copy back to control. + temp.ReleaseResourcesExecution(); // Make sure not counting on execution. + VTKM_TEST_ASSERT( + temp.GetNumberOfValues() == 50, + "Unique did not resize array (or size did not copy to control)."); + + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + vtkm::Id value = handle.GetPortalConstControl().Get(i); + vtkm::Id value1 = handle1.GetPortalConstControl().Get(i); + VTKM_TEST_ASSERT(value == i % 50, "Got bad value (LowerBounds)"); + VTKM_TEST_ASSERT(value1 >= i % 50, "Got bad value (UpperBounds)"); + } + + std::cout << "Testing Sort, Unique, LowerBounds and UpperBounds with random values" + << std::endl; + //now test it works when the id are not incrementing + const vtkm::Id RANDOMDATA_SIZE = 6; + vtkm::Id randomData[RANDOMDATA_SIZE]; + randomData[0]=500; // 2 (lower), 3 (upper) + randomData[1]=955; // 3 (lower), 4 (upper) + randomData[2]=955; // 3 (lower), 4 (upper) + randomData[3]=120; // 0 (lower), 1 (upper) + randomData[4]=320; // 1 (lower), 2 (upper) + randomData[5]=955; // 3 (lower), 4 (upper) + + //change the control structure under the handle + input = MakeArrayHandle(randomData, RANDOMDATA_SIZE); + Algorithm::Copy(input,handle); + VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE, + "Handle incorrect size after setting new control data"); + + Algorithm::Copy(input,handle1); + VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE, + "Handle incorrect size after setting new control data"); + + Algorithm::Copy(handle,temp); + VTKM_TEST_ASSERT(temp.GetNumberOfValues() == RANDOMDATA_SIZE, + "Copy failed"); + Algorithm::Sort(temp); + Algorithm::Unique(temp); + Algorithm::LowerBounds(temp,handle); + Algorithm::UpperBounds(temp,handle1); + + VTKM_TEST_ASSERT(handle.GetNumberOfValues() == RANDOMDATA_SIZE, + "LowerBounds returned incorrect size"); + + std::copy(handle.GetPortalConstControl().GetIteratorBegin(), + handle.GetPortalConstControl().GetIteratorEnd(), + randomData); + VTKM_TEST_ASSERT(randomData[0] == 2, "Got bad value - LowerBounds"); + VTKM_TEST_ASSERT(randomData[1] == 3, "Got bad value - LowerBounds"); + VTKM_TEST_ASSERT(randomData[2] == 3, "Got bad value - LowerBounds"); + VTKM_TEST_ASSERT(randomData[3] == 0, "Got bad value - LowerBounds"); + VTKM_TEST_ASSERT(randomData[4] == 1, "Got bad value - LowerBounds"); + VTKM_TEST_ASSERT(randomData[5] == 3, "Got bad value - LowerBounds"); + + VTKM_TEST_ASSERT(handle1.GetNumberOfValues() == RANDOMDATA_SIZE, + "UppererBounds returned incorrect size"); + + std::copy(handle1.GetPortalConstControl().GetIteratorBegin(), + handle1.GetPortalConstControl().GetIteratorEnd(), + randomData); + VTKM_TEST_ASSERT(randomData[0] == 3, "Got bad value - UpperBound"); + VTKM_TEST_ASSERT(randomData[1] == 4, "Got bad value - UpperBound"); + VTKM_TEST_ASSERT(randomData[2] == 4, "Got bad value - UpperBound"); + VTKM_TEST_ASSERT(randomData[3] == 1, "Got bad value - UpperBound"); + VTKM_TEST_ASSERT(randomData[4] == 2, "Got bad value - UpperBound"); + VTKM_TEST_ASSERT(randomData[5] == 4, "Got bad value - UpperBound"); + } + + static VTKM_CONT_EXPORT void TestSortWithComparisonObject() + { + std::cout << "-------------------------------------------------" << std::endl; + std::cout << "Sort with comparison object" << std::endl; + vtkm::Id testData[ARRAY_SIZE]; + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + testData[i]= OFFSET+((ARRAY_SIZE-i) % 50); + } + IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); + + IdArrayHandle sorted; + IdArrayHandle comp_sorted; + + Algorithm::Copy(input,sorted); + Algorithm::Copy(input,comp_sorted); + + //Validate the standard sort is correct + Algorithm::Sort(sorted); + + for (vtkm::Id i = 0; i < ARRAY_SIZE-1; ++i) + { + vtkm::Id sorted1 = sorted.GetPortalConstControl().Get(i); + vtkm::Id sorted2 = sorted.GetPortalConstControl().Get(i+1); +// std::cout << sorted1 << " <= " << sorted2 << std::endl; + VTKM_TEST_ASSERT(sorted1 <= sorted2, "Values not properly sorted."); + } + + //Validate the sort, and SortGreater are inverse + Algorithm::Sort(comp_sorted,SortGreater()); + + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + vtkm::Id sorted1 = sorted.GetPortalConstControl().Get(i); + vtkm::Id sorted2 = comp_sorted.GetPortalConstControl().Get(ARRAY_SIZE - (i + 1)); +// std::cout << sorted1 << "==" << sorted2 << std::endl; + VTKM_TEST_ASSERT(sorted1 == sorted2, + "Got bad sort values when using SortGreater"); + } + + Algorithm::Sort(comp_sorted,SortLess()); + + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + vtkm::Id sorted1 = sorted.GetPortalConstControl().Get(i); + vtkm::Id sorted2 = comp_sorted.GetPortalConstControl().Get(i); + VTKM_TEST_ASSERT(sorted1 == sorted2, + "Got bad sort values when using SortLesser"); + } + } + + // static VTKM_CONT_EXPORT void TestSortByKey() + // { + // std::cout << "-------------------------------------------------" << std::endl; + // std::cout << "Sort by keys" << std::endl; + + // vtkm::Id testKeys[ARRAY_SIZE]; + // vtkm::Vector3 testValues[ARRAY_SIZE]; + + // vtkm::Vector3 grad(1.0,1.0,1.0); + // for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + // { + // testKeys[i] = ARRAY_SIZE - i; + // testValues[i] = vtkm::Vector3(i); + // } + + // IdArrayHandle keys = MakeArrayHandle(testKeys, ARRAY_SIZE); + // Vector3ArrayHandle values = MakeArrayHandle(testValues, ARRAY_SIZE); + + // IdArrayHandle sorted_keys; + // Vector3ArrayHandle sorted_values; + + // Algorithm::Copy(keys,sorted_keys); + // Algorithm::Copy(values,sorted_values); + + // Algorithm::SortByKey(sorted_keys,sorted_values); + // for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + // { + // //keys should be sorted from 1 to ARRAY_SIZE + // //values should be sorted from (ARRAY_SIZE-1) to 0 + // vtkm::Scalar sorted_value = + // sorted_values.GetPortalConstControl().Get(i)[0]; + // vtkm::Id sorted_key = sorted_keys.GetPortalConstControl().Get(i); + + // VTKM_TEST_ASSERT( (sorted_key == (i+1)) , "Got bad SortByKeys key"); + // VTKM_TEST_ASSERT( (sorted_value == (ARRAY_SIZE-1-i)), + // "Got bad SortByKeys value"); + // } + + // // this will return everything back to what it was before sorting + // Algorithm::SortByKey(sorted_keys,sorted_values,SortGreater()); + // for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + // { + // //keys should be sorted from ARRAY_SIZE to 1 + // //values should be sorted from 0 to (ARRAY_SIZE-1) + // vtkm::Scalar sorted_value = + // sorted_values.GetPortalConstControl().Get(i)[0]; + // vtkm::Id sorted_key = sorted_keys.GetPortalConstControl().Get(i); + + // VTKM_TEST_ASSERT( (sorted_key == (ARRAY_SIZE-i)), + // "Got bad SortByKeys key"); + // VTKM_TEST_ASSERT( (sorted_value == i), + // "Got bad SortByKeys value"); + // } + + // //this is here to verify we can sort by vtkm::Tuples + // Algorithm::SortByKey(sorted_values,sorted_keys); + // for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + // { + // //keys should be sorted from ARRAY_SIZE to 1 + // //values should be sorted from 0 to (ARRAY_SIZE-1) + // vtkm::Scalar sorted_value = + // sorted_values.GetPortalConstControl().Get(i)[0]; + // vtkm::Id sorted_key = sorted_keys.GetPortalConstControl().Get(i); + + // VTKM_TEST_ASSERT( (sorted_key == (ARRAY_SIZE-i)), + // "Got bad SortByKeys key"); + // VTKM_TEST_ASSERT( (sorted_value == i), + // "Got bad SortByKeys value"); + // } + // } + + static VTKM_CONT_EXPORT void TestLowerBoundsWithComparisonObject() + { + std::cout << "-------------------------------------------------" << std::endl; + std::cout << "Testing LowerBounds with comparison object" << std::endl; + vtkm::Id testData[ARRAY_SIZE]; + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + testData[i]= OFFSET+(i % 50); + } + IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); + + IdArrayHandle temp; + Algorithm::Copy(input,temp); + Algorithm::Sort(temp); + Algorithm::Unique(temp); + + IdArrayHandle handle; + //verify lower bounds work + Algorithm::LowerBounds(temp,input,handle,SortLess()); + + // Check to make sure that temp was resized correctly during Unique. + // (This was a discovered bug at one point.) + temp.GetPortalConstControl(); // Forces copy back to control. + temp.ReleaseResourcesExecution(); // Make sure not counting on execution. + VTKM_TEST_ASSERT( + temp.GetNumberOfValues() == 50, + "Unique did not resize array (or size did not copy to control)."); + + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + vtkm::Id value = handle.GetPortalConstControl().Get(i); + VTKM_TEST_ASSERT(value == i % 50, "Got bad LowerBounds value with SortLess"); + } + } + + + static VTKM_CONT_EXPORT void TestUpperBoundsWithComparisonObject() + { + std::cout << "-------------------------------------------------" << std::endl; + std::cout << "Testing UpperBounds with comparison object" << std::endl; + vtkm::Id testData[ARRAY_SIZE]; + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + testData[i]= OFFSET+(i % 50); + } + IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); + + IdArrayHandle temp; + Algorithm::Copy(input,temp); + Algorithm::Sort(temp); + Algorithm::Unique(temp); + + IdArrayHandle handle; + //verify upper bounds work + Algorithm::UpperBounds(temp,input,handle,SortLess()); + + // Check to make sure that temp was resized correctly during Unique. + // (This was a discovered bug at one point.) + temp.GetPortalConstControl(); // Forces copy back to control. + temp.ReleaseResourcesExecution(); // Make sure not counting on execution. + VTKM_TEST_ASSERT( + temp.GetNumberOfValues() == 50, + "Unique did not resize array (or size did not copy to control)."); + + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + vtkm::Id value = handle.GetPortalConstControl().Get(i); + VTKM_TEST_ASSERT(value == (i % 50)+1, "Got bad UpperBounds value with SortLess"); + } + } + + static VTKM_CONT_EXPORT void TestUniqueWithComparisonObject() + { + std::cout << "-------------------------------------------------" << std::endl; + std::cout << "Testing Unique with comparison object" << std::endl; + vtkm::Id testData[ARRAY_SIZE]; + for(vtkm::Id i=0; i < ARRAY_SIZE; ++i) + { + testData[i]= OFFSET+(i % 50); + } + IdArrayHandle input = MakeArrayHandle(testData, ARRAY_SIZE); + + IdArrayHandle temp; + Algorithm::Copy(input,temp); + Algorithm::Sort(temp); + Algorithm::Unique(temp, FuseAll()); + + // Check to make sure that temp was resized correctly during Unique. + // (This was a discovered bug at one point.) + temp.GetPortalConstControl(); // Forces copy back to control. + temp.ReleaseResourcesExecution(); // Make sure not counting on execution. + std::cout << "temp size: " << temp.GetNumberOfValues() << std::endl; + VTKM_TEST_ASSERT( + temp.GetNumberOfValues() == 1, + "Unique did not resize array (or size did not copy to control)."); + + vtkm::Id value = temp.GetPortalConstControl().Get(0); + VTKM_TEST_ASSERT(value == OFFSET, "Got bad unique value"); + } + + static VTKM_CONT_EXPORT void TestScanInclusive() + { + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing Inclusive Scan" << std::endl; + + //construct the index array + IdArrayHandle array; + Algorithm::Schedule( + ClearArrayKernel(array.PrepareForOutput(ARRAY_SIZE, + DeviceAdapterTag())), + ARRAY_SIZE); + + //we know have an array whose sum is equal to OFFSET * ARRAY_SIZE, + //let's validate that + vtkm::Id sum = Algorithm::ScanInclusive(array, array); + VTKM_TEST_ASSERT(sum == OFFSET * ARRAY_SIZE, + "Got bad sum from Inclusive Scan"); + + //each value should be equal to the Triangle Number of that index + //ie 1, 3, 6, 10, 15, 21 ... + vtkm::Id partialSum = 0; + vtkm::Id triangleNumber = 0; + for(unsigned int i=0, pos=1; i < ARRAY_SIZE; ++i, ++pos) + { + const vtkm::Id value = array.GetPortalConstControl().Get(i); + partialSum += value; + triangleNumber = ((pos*(pos+1))/2); + VTKM_TEST_ASSERT(partialSum == triangleNumber * OFFSET, + "Incorrect partial sum"); + } + } + + static VTKM_CONT_EXPORT void TestScanExclusive() + { + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing Exclusive Scan" << std::endl; + + //construct the index array + IdArrayHandle array; + Algorithm::Schedule( + ClearArrayKernel(array.PrepareForOutput(ARRAY_SIZE, + DeviceAdapterTag())), + ARRAY_SIZE); + + // we know have an array whose sum = (OFFSET * ARRAY_SIZE), + // let's validate that + vtkm::Id sum = Algorithm::ScanExclusive(array, array); + + VTKM_TEST_ASSERT(sum == (OFFSET * ARRAY_SIZE), + "Got bad sum from Exclusive Scan"); + + //each value should be equal to the Triangle Number of that index + //ie 0, 1, 3, 6, 10, 15, 21 ... + vtkm::Id partialSum = 0; + vtkm::Id triangleNumber = 0; + for(unsigned int i=0, pos=0; i < ARRAY_SIZE; ++i, ++pos) + { + const vtkm::Id value = array.GetPortalConstControl().Get(i); + partialSum += value; + triangleNumber = ((pos*(pos+1))/2); + VTKM_TEST_ASSERT(partialSum == triangleNumber * OFFSET, + "Incorrect partial sum"); + } + } + + static VTKM_CONT_EXPORT void TestErrorExecution() + { + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing Exceptions in Execution Environment" << std::endl; + + std::cout << "Generating one error." << std::endl; + std::string message; + try + { + Algorithm::Schedule(OneErrorKernel(), ARRAY_SIZE); + } + catch (vtkm::cont::ErrorExecution error) + { + std::cout << "Got expected error: " << error.GetMessage() << std::endl; + message = error.GetMessage(); + } + VTKM_TEST_ASSERT(message == ERROR_MESSAGE, + "Did not get expected error message."); + + std::cout << "Generating lots of errors." << std::endl; + message = ""; + try + { + Algorithm::Schedule(AllErrorKernel(), ARRAY_SIZE); + } + catch (vtkm::cont::ErrorExecution error) + { + std::cout << "Got expected error: " << error.GetMessage() << std::endl; + message = error.GetMessage(); + } + VTKM_TEST_ASSERT(message == ERROR_MESSAGE, + "Did not get expected error message."); + } + + // template + // static VTKM_CONT_EXPORT void TestWorkletMapField() + // { + // std::cout << "-------------------------------------------" << std::endl; + // std::cout << "Testing basic map field worklet" << std::endl; + + // //use a scoped pointer that constructs and fills a grid of the + // //right type + // vtkm::cont::testing::TestGrid + // grid(DIM); + + // vtkm::Vector3 trueGradient = vtkm::make_Vector3(1.0, 1.0, 1.0); + + // std::vector field(grid->GetNumberOfPoints()); + // std::cout << "Number of Points in the grid: " + // << grid->GetNumberOfPoints() + // << std::endl; + // for (vtkm::Id pointIndex = 0; + // pointIndex < grid->GetNumberOfPoints(); + // pointIndex++) + // { + // vtkm::Vector3 coordinates = grid.GetPointCoordinates(pointIndex); + // field[pointIndex] = vtkm::dot(coordinates, trueGradient); + // } + // ScalarArrayHandle fieldHandle = MakeArrayHandle(field); + + // ScalarArrayHandle squareHandle; + + // std::cout << "Running Square worklet" << std::endl; + // vtkm::cont::DispatcherMapField dispatcher; + // dispatcher.Invoke(fieldHandle, squareHandle); + + // typename ScalarArrayHandle::PortalConstControl squarePortal = + // squareHandle.GetPortalConstControl(); + + // std::cout << "Checking result" << std::endl; + // for (vtkm::Id pointIndex = 0; + // pointIndex < grid->GetNumberOfPoints(); + // pointIndex++) + // { + // vtkm::Scalar squareValue = squarePortal.Get(pointIndex); + // vtkm::Scalar squareTrue = field[pointIndex]*field[pointIndex]; + // VTKM_TEST_ASSERT(test_equal(squareValue, squareTrue), + // "Got bad square"); + // } + // } + + // template + // static VTKM_CONT_EXPORT void TestWorkletFieldMapError() + // { + // std::cout << "-------------------------------------------" << std::endl; + // std::cout << "Testing map field worklet error" << std::endl; + + // vtkm::cont::testing::TestGrid + // grid(DIM); + + // std::cout << "Running field map worklet that errors" << std::endl; + // bool gotError = false; + // try + // { + // vtkm::cont::DispatcherMapField< vtkm::worklet::testing::FieldMapError, + // DeviceAdapterTag> dispatcher; + // dispatcher.Invoke( grid.GetRealGrid().GetPointCoordinates() ); + // } + // catch (vtkm::cont::ErrorExecution error) + // { + // std::cout << "Got expected ErrorExecution object." << std::endl; + // std::cout << error.GetMessage() << std::endl; + // gotError = true; + // } + + // VTKM_TEST_ASSERT(gotError, "Never got the error thrown."); + // } + + // template + // static VTKM_CONT_EXPORT void TestWorkletMapCell() + // { + // std::cout << "-------------------------------------------" << std::endl; + // std::cout << "Testing basic map cell worklet" << std::endl; + + // if (vtkm::CellTraits::TOPOLOGICAL_DIMENSIONS < 3) + // { + // std::cout << "Skipping. Too hard to check gradient " + // << "on cells with topological dimension < 3" << std::endl; + // } + // else + // { + // // Calling a separate Impl function because the CUDA compiler is good + // // enough to optimize the if statement as a constant expression and + // // then complains about unreachable statements after a return. + // TestWorkletMapCellImpl(); + // } + // } + + // template + // static VTKM_CONT_EXPORT void TestWorkletMapCellImpl() + // { + // vtkm::cont::testing::TestGrid + // grid(DIM); + + // vtkm::Vector3 trueGradient = vtkm::make_Vector3(1.0, 1.0, 1.0); + + // std::vector field(grid->GetNumberOfPoints()); + // for (vtkm::Id pointIndex = 0; + // pointIndex < grid->GetNumberOfPoints(); + // pointIndex++) + // { + // vtkm::Vector3 coordinates = grid.GetPointCoordinates(pointIndex); + // field[pointIndex] = vtkm::dot(coordinates, trueGradient); + // } + // ScalarArrayHandle fieldHandle = MakeArrayHandle(field); + + // Vector3ArrayHandle gradientHandle; + + // std::cout << "Running CellGradient worklet" << std::endl; + + // vtkm::cont::DispatcherMapCell< vtkm::worklet::CellGradient, + // DeviceAdapterTag> dispatcher; + // dispatcher.Invoke(grid.GetRealGrid(), + // grid->GetPointCoordinates(), + // fieldHandle, + // gradientHandle); + + // typename Vector3ArrayHandle::PortalConstControl gradientPortal = + // gradientHandle.GetPortalConstControl(); + + // std::cout << "Checking result" << std::endl; + // for (vtkm::Id cellIndex = 0; + // cellIndex < grid->GetNumberOfCells(); + // cellIndex++) + // { + // vtkm::Vector3 gradientValue = gradientPortal.Get(cellIndex); + // VTKM_TEST_ASSERT(test_equal(gradientValue, trueGradient), + // "Got bad gradient"); + // } + // } + + // template + // static VTKM_CONT_EXPORT void TestWorkletCellMapError() + // { + // std::cout << "-------------------------------------------" << std::endl; + // std::cout << "Testing map cell worklet error" << std::endl; + + // vtkm::cont::testing::TestGrid + // grid(DIM); + + // std::cout << "Running cell map worklet that errors" << std::endl; + // bool gotError = false; + // try + // { + // vtkm::cont::DispatcherMapCell< vtkm::worklet::testing::CellMapError, + // DeviceAdapterTag> dispatcher; + // dispatcher.Invoke( grid.GetRealGrid() ); + // } + // catch (vtkm::cont::ErrorExecution error) + // { + // std::cout << "Got expected ErrorExecution object." << std::endl; + // std::cout << error.GetMessage() << std::endl; + // gotError = true; + // } + + // VTKM_TEST_ASSERT(gotError, "Never got the error thrown."); + // } + + // struct TestWorklets + // { + // template + // VTKM_CONT_EXPORT void operator()(const GridType&) const + // { + // TestWorkletMapField(); + // TestWorkletFieldMapError(); + // TestWorkletMapCell(); + // TestWorkletCellMapError(); + // } + // }; + + struct TestAll + { + VTKM_CONT_EXPORT void operator()() const + { + std::cout << "Doing DeviceAdapter tests" << std::endl; + TestArrayManagerExecution(); + TestOutOfMemory(); + // TestTimer(); + + TestAlgorithmSchedule(); + TestErrorExecution(); + TestScanInclusive(); + TestScanExclusive(); + TestSortWithComparisonObject(); + // TestSortByKey(); + TestLowerBoundsWithComparisonObject(); + TestUpperBoundsWithComparisonObject(); + TestUniqueWithComparisonObject(); + TestOrderedUniqueValues(); //tests Copy, LowerBounds, Sort, Unique + // TestDispatcher(); + TestStreamCompactWithStencil(); + TestStreamCompact(); + + + // std::cout << "Doing Worklet tests with all grid type" << std::endl; + // vtkm::cont::testing::GridTesting::TryAllGridTypes( + // TestWorklets(), ArrayContainerControlTagBasic()); + } + }; + +public: + + /// Run a suite of tests to check to see if a DeviceAdapter properly supports + /// all members and classes required for driving Dax algorithms. Returns an + /// error code that can be returned from the main function of a test. + /// + static VTKM_CONT_EXPORT int Run() + { + return vtkm::cont::testing::Testing::Run(TestAll()); + } +}; + +#undef ERROR_MESSAGE +#undef ARRAY_SIZE +#undef OFFSET +#undef DIM + +} +} +} // namespace vtkm::cont::testing + +#endif //vtkm_cont_testing_TestingDeviceAdapter_h diff --git a/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx b/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx new file mode 100644 index 000000000..188caa289 --- /dev/null +++ b/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx @@ -0,0 +1,125 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ + +#define VTKM_DEVICE_ADAPTER VTKM_DEVICE_ADAPTER_SERIAL + +#include + +#include + +namespace { + +const vtkm::Id ARRAY_SIZE = 10; + +//increments by two instead of one wrapper +template +struct CountByTwo +{ + CountByTwo(): Value() {} + explicit CountByTwo(T t): Value(t) {} + + bool operator==(const T& other) const + { return Value == other; } + + bool operator==(const CountByTwo& other) const + { return Value == other.Value; } + + CountByTwo operator+(vtkm::Id count) const + { return CountByTwo(Value+(count*2)); } + + CountByTwo& operator++() + { ++Value; ++Value; return *this; } + + friend std::ostream& operator<< (std::ostream& os, const CountByTwo& obj) + { os << obj.Value; return os; } + T Value; +}; + + + +template< typename ValueType> +struct TemplatedTests +{ + typedef vtkm::cont::ArrayHandleCounting ArrayHandleType; + + typedef vtkm::cont::ArrayHandle::Tag> + ArrayHandleType2; + + typedef typename ArrayHandleType::PortalConstControl PortalType; + + void operator()( const ValueType startingValue ) + { + ArrayHandleType arrayConst(startingValue, ARRAY_SIZE); + + ArrayHandleType arrayMake = vtkm::cont::make_ArrayHandleCounting(startingValue,ARRAY_SIZE); + + ArrayHandleType2 arrayHandle = + ArrayHandleType2(PortalType(startingValue, ARRAY_SIZE)); + + VTKM_TEST_ASSERT(arrayConst.GetNumberOfValues() == ARRAY_SIZE, + "Counting array using constructor has wrong size."); + + VTKM_TEST_ASSERT(arrayMake.GetNumberOfValues() == ARRAY_SIZE, + "Counting array using make has wrong size."); + + VTKM_TEST_ASSERT(arrayHandle.GetNumberOfValues() == ARRAY_SIZE, + "Counting array using raw array handle + tag has wrong size."); + + ValueType properValue = startingValue; + for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) + { + VTKM_TEST_ASSERT(arrayConst.GetPortalConstControl().Get(index) == properValue, + "Counting array using constructor has unexpected value."); + VTKM_TEST_ASSERT(arrayMake.GetPortalConstControl().Get(index) == properValue, + "Counting array using make has unexpected value."); + + VTKM_TEST_ASSERT(arrayHandle.GetPortalConstControl().Get(index) == properValue, + "Counting array using raw array handle + tag has unexpected value."); + ++properValue; + } + } +}; + +struct TestFunctor +{ + template + void operator()(const T t) + { + TemplatedTests tests; + tests(t); + } +}; + +void TestArrayHandleCounting() +{ + TestFunctor()(vtkm::Id(0)); + TestFunctor()(vtkm::Scalar(0)); + TestFunctor()( CountByTwo(12) ); + TestFunctor()( CountByTwo(1.2f) ); +} + + +} // annonymous namespace + +int UnitTestArrayHandleCounting(int, char *[]) +{ + return vtkm::cont::testing::Testing::Run(TestArrayHandleCounting); +} diff --git a/vtkm/cont/testing/UnitTestDeviceAdapterAlgorithmDependency.cxx b/vtkm/cont/testing/UnitTestDeviceAdapterAlgorithmDependency.cxx new file mode 100644 index 000000000..dc71c8dd1 --- /dev/null +++ b/vtkm/cont/testing/UnitTestDeviceAdapterAlgorithmDependency.cxx @@ -0,0 +1,57 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ + +// This tests a previous problem where code templated on the device adapter and +// used one of the device adapter algorithms (for example, the dispatcher) had +// to be declared after any device adapter it was ever used with. + +#define VTKM_DEVICE_ADAPTER VTKM_DEVICE_ADAPTER_ERROR + +#include + +#include + +#include + +// Important for this test! +//This file must be included after ArrayHandle.h +#include + +namespace { + +struct ExampleWorklet +{ + template + void operator()(T vtkmNotUsed(v)) const { } +}; + +void CheckPostDefinedDeviceAdapter() +{ + // Nothing to really check. If this compiles, then the test is probably + // successful. + vtkm::cont::ArrayHandle test; +} + +} // anonymous namespace + +int UnitTestDeviceAdapterAlgorithmDependency(int, char *[]) +{ + return vtkm::cont::testing::Testing::Run(CheckPostDefinedDeviceAdapter); +} diff --git a/vtkm/cont/testing/UnitTestDeviceAdapterAlgorithmGeneral.cxx b/vtkm/cont/testing/UnitTestDeviceAdapterAlgorithmGeneral.cxx new file mode 100644 index 000000000..2b7dd4a8a --- /dev/null +++ b/vtkm/cont/testing/UnitTestDeviceAdapterAlgorithmGeneral.cxx @@ -0,0 +1,106 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ + +// This test makes sure that the algorithms specified in +// DeviceAdapterAlgorithmGeneral.h are working correctly. It does this by +// creating a test device adapter that uses the serial device adapter for the +// base schedule/scan/sort algorithms and using the general algorithms for +// everything else. Because this test is based of the serial device adapter, +// make sure that UnitTestDeviceAdapterSerial is working before trying to debug +// this one. + +#define VTKM_DEVICE_ADAPTER VTKM_DEVICE_ADAPTER_ERROR + +#include +#include + +#include + +VTKM_CREATE_DEVICE_ADAPTER(TestAlgorithmGeneral); + +namespace vtkm { +namespace cont { + +template<> +struct DeviceAdapterAlgorithm< + vtkm::cont::DeviceAdapterTagTestAlgorithmGeneral> : + vtkm::cont::internal::DeviceAdapterAlgorithmGeneral< + DeviceAdapterAlgorithm< + vtkm::cont::DeviceAdapterTagTestAlgorithmGeneral>, + vtkm::cont::DeviceAdapterTagTestAlgorithmGeneral> +{ +private: + typedef vtkm::cont::DeviceAdapterAlgorithm< + vtkm::cont::DeviceAdapterTagSerial> Algorithm; + + typedef vtkm::cont::DeviceAdapterTagTestAlgorithmGeneral + DeviceAdapterTagTestAlgorithmGeneral; + +public: + + template + VTKM_CONT_EXPORT static void Schedule(Functor functor, + vtkm::Id numInstances) + { + Algorithm::Schedule(functor, numInstances); + } + + template + VTKM_CONT_EXPORT static void Schedule(Functor functor, + vtkm::Id3 rangeMax) + { + Algorithm::Schedule(functor, rangeMax); + } + + VTKM_CONT_EXPORT static void Synchronize() + { + Algorithm::Synchronize(); + } +}; + +namespace internal { + +template +class ArrayManagerExecution + + : public vtkm::cont::internal::ArrayManagerExecution + +{ +public: + typedef vtkm::cont::internal::ArrayManagerExecution + + Superclass; + typedef typename Superclass::ValueType ValueType; + typedef typename Superclass::PortalType PortalType; + typedef typename Superclass::PortalConstType PortalConstType; +}; + + +} +} +} // namespace vtkm::cont::testing + +int UnitTestDeviceAdapterAlgorithmGeneral(int, char *[]) +{ + return vtkm::cont::testing::TestingDeviceAdapter + ::Run(); +} diff --git a/vtkm/cont/testing/UnitTestDeviceAdapterSerial.cxx b/vtkm/cont/testing/UnitTestDeviceAdapterSerial.cxx new file mode 100644 index 000000000..14d448e81 --- /dev/null +++ b/vtkm/cont/testing/UnitTestDeviceAdapterSerial.cxx @@ -0,0 +1,31 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ + +#define VTKM_DEVICE_ADAPTER VTKM_DEVICE_ADAPTER_ERROR + +#include + +#include + +int UnitTestDeviceAdapterSerial(int, char *[]) +{ + return vtkm::cont::testing::TestingDeviceAdapter + ::Run(); +} diff --git a/vtkm/exec/internal/ErrorMessageBuffer.h b/vtkm/exec/internal/ErrorMessageBuffer.h new file mode 100644 index 000000000..d286c62b1 --- /dev/null +++ b/vtkm/exec/internal/ErrorMessageBuffer.h @@ -0,0 +1,94 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_exec_internal_ErrorMessageBuffer_h +#define vtkm_exec_internal_ErrorMessageBuffer_h + +#include + +namespace vtkm { +namespace exec { +namespace internal { + +/// Used to hold an error in the execution environment until the parallel +/// execution can complete. This is to be used in conjunction with a +/// DeviceAdapter's Schedule function to implement errors in execution +/// environments that cannot throw errors. This string should be global to all +/// threads. If the first entry in the string is '\0' (the C string +/// terminator), then we consider it as no error. Otherwise, the array contains +/// the string describing the error. +/// +/// Before scheduling worklets, the global array should be cleared to have no +/// error. This can only be reliably done by the device adapter. +/// +class ErrorMessageBuffer +{ +public: + VTKM_EXEC_CONT_EXPORT ErrorMessageBuffer() + : MessageBuffer(), MessageBufferSize(0) { } + + VTKM_EXEC_CONT_EXPORT + ErrorMessageBuffer(char *messageBuffer, vtkm::Id bufferSize) + : MessageBuffer(messageBuffer), MessageBufferSize(bufferSize) { } + + VTKM_EXEC_EXPORT void RaiseError(const char *message) const + { + // Only raise the error if one has not been raised yet. This check is not + // guaranteed to work across threads. However, chances are that if two or + // more threads simultaneously pass this test, they will be writing the + // same error, which is fine. Even in the much less likely case that two + // threads simultaneously write different error messages, the worst case is + // that you get a mangled message. That's not good (and it's what we are + // trying to avoid), but it's not critical. + if (this->IsErrorRaised()) { return; } + + // Safely copy message into array. + for (vtkm::Id index = 0; index < this->MessageBufferSize; index++) + { + this->MessageBuffer[index] = message[index]; + if (message[index] == '\0') { break; } + } + + // Make sure message is null terminated. + this->MessageBuffer[this->MessageBufferSize-1] = '\0'; + } + + VTKM_EXEC_CONT_EXPORT bool IsErrorRaised() const + { + if (this->MessageBufferSize > 0) + { + return (this->MessageBuffer[0] != '\0'); + } + else + { + // If there is no buffer set, then always report an error. + return true; + } + } + +private: + char *MessageBuffer; + vtkm::Id MessageBufferSize; +}; + +} +} +} // namespace vtkm::exec::internal + +#endif // vtkm_exec_internal_ErrorMessageBuffer_h diff --git a/vtkm/exec/internal/WorkletBase.h b/vtkm/exec/internal/WorkletBase.h new file mode 100644 index 000000000..ab1a89c14 --- /dev/null +++ b/vtkm/exec/internal/WorkletBase.h @@ -0,0 +1,64 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_exec_WorkletBase_h +#define vtkm_exec_WorkletBase_h + +#include + +#include + +namespace vtkm { +namespace exec { +namespace internal { + +/// Base class for all worklet classes. Worklet classes are subclasses and a +/// operator() const is added to implement an algorithm in Dax. Different +/// worklets have different calling semantics. +/// +class WorkletBase +{ +public: + VTKM_EXEC_CONT_EXPORT WorkletBase() { } + + VTKM_EXEC_EXPORT void RaiseError(const char *message) const + { + this->ErrorMessage.RaiseError(message); + } + + /// Set the error message buffer so that running algorithms can report + /// errors. This is supposed to be set by the dispatcher. This method may be + /// replaced as the execution semantics change. + /// + VTKM_CONT_EXPORT void SetErrorMessageBuffer( + const vtkm::exec::internal::ErrorMessageBuffer &buffer) + { + this->ErrorMessage = buffer; + } + +private: + vtkm::exec::internal::ErrorMessageBuffer ErrorMessage; +}; + + +} +} +} // namespace vtkm::exec::internal + +#endif //vtkm_exec_WorkletBase_h diff --git a/vtkm/testing/CMakeLists.txt b/vtkm/testing/CMakeLists.txt index bac278c93..5e69e0130 100644 --- a/vtkm/testing/CMakeLists.txt +++ b/vtkm/testing/CMakeLists.txt @@ -29,6 +29,7 @@ set(unit_tests UnitTestTesting.cxx UnitTestTypes.cxx UnitTestTypeTraits.cxx + UnitTestVectorTraits.cxx ) VTKM_unit_tests(SOURCES ${unit_tests}) diff --git a/vtkm/testing/UnitTestVectorTraits.cxx b/vtkm/testing/UnitTestVectorTraits.cxx new file mode 100644 index 000000000..79999e508 --- /dev/null +++ b/vtkm/testing/UnitTestVectorTraits.cxx @@ -0,0 +1,63 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#include + +#include + +namespace { + +static const vtkm::Id MAX_VECTOR_SIZE = 5; +static const vtkm::Id VectorInit[MAX_VECTOR_SIZE] = { 42, 54, 67, 12, 78 }; + +struct TestVectorTypeFunctor +{ + template void operator()(const T&) const { + typedef vtkm::VectorTraits Traits; + VTKM_TEST_ASSERT(Traits::NUM_COMPONENTS <= MAX_VECTOR_SIZE, + "Need to update test for larger vectors."); + T vector; + for (int index = 0; index < Traits::NUM_COMPONENTS; index++) + { + Traits::SetComponent(vector, index, VectorInit[index]); + } + vtkm::testing::TestVectorType(vector); + } +}; + +void TestVectorTraits() +{ + TestVectorTypeFunctor test; + vtkm::testing::Testing::TryAllTypes(test); + std::cout << "vtkm::Tuple" << std::endl; + test(vtkm::Tuple()); + + vtkm::testing::TestVectorComponentsTag(); + vtkm::testing::TestVectorComponentsTag(); + vtkm::testing::TestVectorComponentsTag(); + vtkm::testing::TestScalarComponentsTag(); + vtkm::testing::TestScalarComponentsTag(); +} + +} // anonymous namespace + +int UnitTestVectorTraits(int, char *[]) +{ + return vtkm::testing::Testing::Run(TestVectorTraits); +} diff --git a/vtkm/testing/VectorTraitsTests.h b/vtkm/testing/VectorTraitsTests.h new file mode 100644 index 000000000..a9c4dd26b --- /dev/null +++ b/vtkm/testing/VectorTraitsTests.h @@ -0,0 +1,157 @@ +//============================================================================ +// 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. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtkm_testing_VectorTraitsTest_h +#define vtkm_testing_VectorTraitsTest_h + +#include + +#include + +#include + +#include + +namespace vtkm { +namespace testing { + +namespace detail { + +inline void CompareDimensionalityTags(vtkm::TypeTraitsScalarTag, + vtkm::VectorTraitsTagSingleComponent) +{ + // If we are here, everything is fine. +} +inline void CompareDimensionalityTags(vtkm::TypeTraitsVectorTag, + vtkm::VectorTraitsTagMultipleComponents) +{ + // If we are here, everything is fine. +} + +/// Compares some manual arithmetic through type traits to arithmetic with +/// the Tuple class. +template +static void TestVectorTypeImpl( + const typename boost::remove_const::type &vector) +{ + typedef typename vtkm::VectorTraits Traits; + typedef typename Traits::ComponentType ComponentType; + static const int NUM_COMPONENTS = Traits::NUM_COMPONENTS; + typedef typename boost::remove_const::type NonConstT; + + { + NonConstT result; + const ComponentType multiplier = 4; + for (int i = 0; i < NUM_COMPONENTS; i++) + { + Traits::SetComponent(result, i, multiplier*Traits::GetComponent(vector, i)); + } + VTKM_TEST_ASSERT(test_equal(Traits::ToTuple(result), + multiplier*Traits::ToTuple(vector)), + "Got bad result for scalar multiple"); + } + + { + NonConstT result; + const ComponentType multiplier = 7; + for (int i = 0; i < NUM_COMPONENTS; i++) + { + Traits::GetComponent(result, i) + = multiplier * Traits::GetComponent(vector, i); + } + VTKM_TEST_ASSERT(test_equal(Traits::ToTuple(result), + multiplier*Traits::ToTuple(vector)), + "Got bad result for scalar multiple"); + } + + { + ComponentType result = 0; + for (int i = 0; i < NUM_COMPONENTS; i++) + { + ComponentType component + = Traits::GetComponent(vector, i); + result += component * component; + } + VTKM_TEST_ASSERT( + test_equal(result, + vtkm::dot(Traits::ToTuple(vector), Traits::ToTuple(vector))), + "Got bad result for dot product"); + } + + // This will fail to compile if the tags are wrong. + detail::CompareDimensionalityTags( + typename vtkm::TypeTraits::DimensionalityTag(), + typename vtkm::VectorTraits::HasMultipleComponents()); +} + +inline void CheckVectorComponentsTag(vtkm::VectorTraitsTagMultipleComponents) +{ + // If we are running here, everything is fine. +} + +} // namespace detail + +/// Checks to make sure that the HasMultipleComponents tag is actually for +/// multiple components. Should only be called for vector classes that actually +/// have multiple components. +/// +template +inline void TestVectorComponentsTag() +{ + // This will fail to compile if the tag is wrong + // (i.e. not vtkm::VectorTraitsTagMultipleComponents) + detail::CheckVectorComponentsTag( + typename vtkm::VectorTraits::HasMultipleComponents()); +} + +namespace detail { + +inline void CheckScalarComponentsTag(vtkm::VectorTraitsTagSingleComponent) +{ + // If we are running here, everything is fine. +} + +} // namespace detail + +/// Compares some manual arithmetic through type traits to arithmetic with +/// the Tuple class. +template +static void TestVectorType(const T &vector) +{ + detail::TestVectorTypeImpl(vector); + detail::TestVectorTypeImpl(vector); +} + +/// Checks to make sure that the HasMultipleComponents tag is actually for a +/// single component. Should only be called for "vector" classes that actually +/// have only a single component (that is, are really scalars). +/// +template +inline void TestScalarComponentsTag() +{ + // This will fail to compile if the tag is wrong + // (i.e. not vtkm::VectorTraitsTagSingleComponent) + detail::CheckScalarComponentsTag( + typename vtkm::VectorTraits::HasMultipleComponents()); +} + +} +} // namespace vtkm::testing + +#endif //vtkm_testing_VectorTraitsTest_h