At runtime TryExecute supports a specific deviceId to execute on.

Instead of always using the first enabled device, now TryExecute
can be told which device at runtime to use.
This commit is contained in:
Robert Maynard 2018-07-25 11:11:05 -04:00
parent b9082cd803
commit 554bc3d369
27 changed files with 495 additions and 343 deletions

@ -0,0 +1,31 @@
# TryExecuteOnDevice allows for runtime selection of which device to execute on
VTK-m now offers `vtkm::cont::TryExecuteOnDevice` to allow for the user to select
which device to execute a function on at runtime. The original `vtkm::cont::TryExecute`
used the first valid device, which meant users had to modify the runtime state
through the `RuntimeTracker` which was verbose and unwieldy.
Here is an example of how you can execute a function on the device that an array handle was last executed
on:
```cpp
struct ArrayCopyFunctor
{
template <typename Device, typename InArray, typename OutArray>
VTKM_CONT bool operator()(Device, const InArray& src, OutArray& dest)
{
vtkm::cont::DeviceAdapterAlgorithm<Device>::Copy(src, dest);
return true;
}
};
template<typename T, typename InStorage, typename OutStorage>
void SmartCopy(const vtkm::cont::ArrayHandle<T, InStorage>& src, vtkm::cont::ArrayHandle<T, OutStorage>& dest)
{
bool success = vtkm::cont::TryExecuteOnDevice(devId, ArrayCopyFunctor(), src, dest);
if (!success)
{
vtkm::cont::TryExecute(ArrayCopyFunctor(), src, dest);
}
}
```

@ -0,0 +1,41 @@
#DeviceAdapterId has becomes a real constexpr type and not an alias to vtkm::UInt8
As part of the ability to support `vtkm::cont::TryExecuteOnDevice` VTK-m has made the
DeviceAdapterId a real constexpr type instead of a vtkm::UInt8.
The benefits of a real type are as follows:
- Easier to add functionality like range verification, which previously had
to be located in each user of `DeviceAdapterId`
- In ability to have ambiguous arguments. Previously it wasn't perfectly clear
what a method parameter of `vtkm::UInt8` represented. Was it actually the
DeviceAdapterId or something else?
- Ability to add subclasses that represent things such as Undefined, Error, or Any.
The implementation of DeviceAdapterId is:
```cpp
struct DeviceAdapterId
{
constexpr explicit DeviceAdapterId(vtkm::Int8 id)
: Value(id)
{
}
constexpr bool operator==(DeviceAdapterId other) const { return this->Value == other.Value; }
constexpr bool operator!=(DeviceAdapterId other) const { return this->Value != other.Value; }
constexpr bool operator<(DeviceAdapterId other) const { return this->Value < other.Value; }
constexpr bool IsValueValid() const
{
return this->Value > 0 && this->Value < VTKM_MAX_DEVICE_ADAPTER_ID;
}
constexpr vtkm::Int8 GetValue() const { return this->Value; }
private:
vtkm::Int8 Value;
};
```

@ -0,0 +1,45 @@
# DeviceAdapterTags are usable for runtime device selection
VTK-m DeviceAdapterTags now are both a compile time representation of which device to use, and
also the runtime representation of that device. Previously the runtime representation was handled
by `vtkm::cont::DeviceAdapterId`. This was done by making `DeviceAdapterTag`'s' a constexpr type that
inherits from the constexpr `vtkm::cont::DeviceAdapterId` type.
At at ten thousand foot level this change means that in general instead of using `vtkm::cont::DeviceAdapterTraits<DeviceTag>`
you can simply use `DeviceTag`, or an instance of if `DeviceTag runtimeDeviceId;`.
Previously if you wanted to get the runtime representation of a device you would do the following:
```cpp
template<typename DeviceTag>
vtkm::cont::DeviceAdapterId getDeviceId()
{
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceTag>;
return Traits::GetId();
}
...
vtkm::cont::DeviceAdapterId runtimeId = getDeviceId<DeviceTag>();
```
Now with the updates you could do the following.
```cpp
vtkm::cont::DeviceAdapterId runtimeId = DeviceTag();
```
More importantly this conversion is unnecessary as you can pass instances `DeviceAdapterTags` into methods or functions
that want `vtkm::cont::DeviceAdapterId` as they are that type!
Previously if you wanted to see if a DeviceAdapter was enabled you would the following:
```cpp
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceTag>;
constexpr auto isValid = std::integral_constant<bool, Traits::Valid>();
```
Now you would do:
```cpp
constexpr auto isValid = std::integral_constant<bool, DeviceTag::IsEnabled>();
```
So why did VTK-m make these changes?
That is a good question, and the answer for that is two fold. The VTK-m project is working better support for ArraysHandles that leverage runtime polymorphism (aka virtuals), and the ability to construct `vtkm::worklet::Dispatchers` without specifying
the explicit device they should run on. Both of these designs push more of the VTK-m logic to operate at runtime rather than compile time. This changes are designed to allow for consistent object usage between runtime and compile time instead of having
to convert between compile time and runtime types.

@ -59,43 +59,13 @@ VTKM_CONT void ArrayCopy(const vtkm::cont::ArrayHandle<InValueType, InStorage>&
namespace detail
{
template <typename InValueType, typename InStorage, typename OutValueType, typename OutStorage>
struct ArrayCopyFunctor
{
using InArrayHandleType = vtkm::cont::ArrayHandle<InValueType, InStorage>;
InArrayHandleType InputArray;
using OutArrayHandleType = vtkm::cont::ArrayHandle<OutValueType, OutStorage>;
OutArrayHandleType OutputArray;
bool OnlyUseCurrentInputDevice;
VTKM_CONT
ArrayCopyFunctor(const InArrayHandleType& input,
OutArrayHandleType& output,
bool onlyUseCurrentInputDevice)
: InputArray(input)
, OutputArray(output)
, OnlyUseCurrentInputDevice(onlyUseCurrentInputDevice)
{
}
template <typename Device>
VTKM_CONT bool operator()(Device)
template <typename Device, typename InArray, typename OutArray>
VTKM_CONT bool operator()(Device, const InArray& input, OutArray& output)
{
VTKM_IS_DEVICE_ADAPTER_TAG(Device);
if (this->OnlyUseCurrentInputDevice &&
(vtkm::cont::DeviceAdapterTraits<Device>::GetId() != this->InputArray.GetDeviceAdapterId()))
{
// We were asked to only copy on the device that already has the data from the input array.
// This is not that device, so return without copying.
return false;
}
vtkm::cont::ArrayCopy(this->InputArray, this->OutputArray, Device());
vtkm::cont::ArrayCopy(input, output, Device());
return true;
}
};
@ -116,28 +86,19 @@ VTKM_CONT void ArrayCopy(
vtkm::cont::ArrayHandle<OutValueType, OutStorage>& destination,
vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker())
{
bool isCopied = false;
detail::ArrayCopyFunctor<InValueType, InStorage, OutValueType, OutStorage> functor(
source, destination, true);
detail::ArrayCopyFunctor functor;
// First pass, only use source's already loaded device.
isCopied = vtkm::cont::TryExecute(functor, tracker);
if (isCopied)
{
return;
bool isCopied = vtkm::cont::TryExecuteOnDevice(
source.GetDeviceAdapterId(), functor, tracker, source, destination);
if (!isCopied)
{ // Second pass, use any available device.
isCopied = vtkm::cont::TryExecute(functor, tracker, source, destination);
}
// Second pass, use any available device.
functor.OnlyUseCurrentInputDevice = false;
isCopied = vtkm::cont::TryExecute(functor, tracker);
if (isCopied)
{
return;
if (!isCopied)
{ // If after the second pass, still not valid through an exception
throw vtkm::cont::ErrorExecution("Failed to run ArrayCopy on any device.");
}
// If we are here, then we just failed to copy.
throw vtkm::cont::ErrorExecution("Failed to run ArrayCopy on any device.");
}
}
} // namespace vtkm::cont

@ -498,7 +498,7 @@ public:
{
return this->Internals->ExecutionArrayValid
? this->Internals->ExecutionArray->GetDeviceAdapterId()
: VTKM_DEVICE_ADAPTER_UNDEFINED;
: DeviceAdapterIdUndefined{};
}
struct VTKM_ALWAYS_EXPORT InternalStruct

@ -273,45 +273,46 @@ private:
struct PrepareForInputFunctor
{
template <typename DeviceAdapter>
VTKM_CONT void operator()(DeviceAdapter,
VTKM_CONT void operator()(DeviceAdapter device,
CoordinatesArrayHandle* instance,
PortalConst& ret) const
{
auto portal = instance->Array.PrepareForInput(DeviceAdapter());
auto portal = instance->Array.PrepareForInput(device);
instance->DevicePortalHandle.Reset(new CoordinatesPortalConst<decltype(portal)>(portal),
true,
vtkm::ListTagBase<DeviceAdapter>());
ret = PortalConst(portal.GetNumberOfValues(),
instance->DevicePortalHandle.PrepareForExecution(DeviceAdapter()));
instance->DevicePortalHandle.PrepareForExecution(device));
}
};
struct PrepareForOutputFunctor
{
template <typename DeviceAdapter>
VTKM_CONT void operator()(DeviceAdapter,
VTKM_CONT void operator()(DeviceAdapter device,
CoordinatesArrayHandle* instance,
vtkm::Id numberOfValues,
Portal& ret) const
{
auto portal = instance->Array.PrepareForOutput(numberOfValues, DeviceAdapter());
auto portal = instance->Array.PrepareForOutput(numberOfValues, device);
instance->DevicePortalHandle.Reset(
new CoordinatesPortal<decltype(portal)>(portal), true, vtkm::ListTagBase<DeviceAdapter>());
ret =
Portal(numberOfValues, instance->DevicePortalHandle.PrepareForExecution(DeviceAdapter()));
ret = Portal(numberOfValues, instance->DevicePortalHandle.PrepareForExecution(device));
}
};
struct PrepareForInPlaceFunctor
{
template <typename DeviceAdapter>
VTKM_CONT void operator()(DeviceAdapter, CoordinatesArrayHandle* instance, Portal& ret) const
VTKM_CONT void operator()(DeviceAdapter device,
CoordinatesArrayHandle* instance,
Portal& ret) const
{
auto portal = instance->Array.PrepareForInPlace(DeviceAdapter());
auto portal = instance->Array.PrepareForInPlace(device);
instance->DevicePortalHandle.Reset(
new CoordinatesPortal<decltype(portal)>(portal), true, vtkm::ListTagBase<DeviceAdapter>());
ret = Portal(instance->Array.GetNumberOfValues(),
instance->DevicePortalHandle.PrepareForExecution(DeviceAdapter()));
instance->DevicePortalHandle.PrepareForExecution(device));
}
};
@ -384,20 +385,19 @@ public:
VTKM_CONT
PortalConstExecution PrepareForInput(bool)
{
return this->Array->PrepareForInput(vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId());
return this->Array->PrepareForInput(DeviceAdapter());
}
VTKM_CONT
PortalExecution PrepareForInPlace(bool)
{
return this->Array->PrepareForInPlace(vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId());
return this->Array->PrepareForInPlace(DeviceAdapter());
}
VTKM_CONT
PortalExecution PrepareForOutput(vtkm::Id numberOfValues)
{
return this->Array->PrepareForOutput(numberOfValues,
vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId());
return this->Array->PrepareForOutput(numberOfValues, DeviceAdapter());
}
VTKM_CONT

@ -67,10 +67,9 @@ public:
}
template <typename DeviceAdapter>
VTKM_CONT const vtkm::exec::CellLocator* PrepareForExecution(DeviceAdapter) const
VTKM_CONT const vtkm::exec::CellLocator* PrepareForExecution(DeviceAdapter device) const
{
vtkm::cont::DeviceAdapterId deviceId = vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId();
return PrepareForExecutionImpl(deviceId).PrepareForExecution(DeviceAdapter());
return PrepareForExecutionImpl(device).PrepareForExecution(device);
}
protected:

@ -666,17 +666,6 @@ public:
/// This pointer is only valid as long as the ColorTable is unmodified
const vtkm::exec::ColorTableBase* PrepareForExecution(vtkm::cont::DeviceAdapterId deviceId) const;
/// \brief returns a virtual object pointer of the exec color table
///
/// This pointer is only valid as long as the ColorTable is unmodified
template <typename DeviceAdapter>
const vtkm::exec::ColorTableBase* PrepareForExecution(DeviceAdapter) const
{
auto deviceId = vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId();
return this->PrepareForExecution(deviceId);
}
/// \brief returns the modified count for the virtual object handle of the exec color table
///
/// The modified count allows consumers of a shared color table to keep track

@ -55,10 +55,9 @@ public:
}
template <typename DeviceAdapter>
VTKM_CONT const vtkm::exec::PointLocator* PrepareForExecution(DeviceAdapter) const
VTKM_CONT const vtkm::exec::PointLocator* PrepareForExecution(DeviceAdapter device) const
{
vtkm::cont::DeviceAdapterId deviceId = vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId();
return PrepareForExecutionImp(deviceId).PrepareForExecution(DeviceAdapter());
return PrepareForExecutionImp(device).PrepareForExecution(device);
}
//VTKM_CONT virtual const vtkm::exec::PointLocator*

@ -34,8 +34,6 @@
#include <sstream>
#include <thread>
#define VTKM_MAX_DEVICE_ADAPTER_ID 8
namespace vtkm
{
namespace cont
@ -66,10 +64,10 @@ VTKM_CONT
void RuntimeDeviceTracker::CheckDevice(vtkm::cont::DeviceAdapterId deviceId,
const vtkm::cont::DeviceAdapterNameType& deviceName) const
{
if ((deviceId < 0) || (deviceId >= VTKM_MAX_DEVICE_ADAPTER_ID))
if (!deviceId.IsValueValid())
{
std::stringstream message;
message << "Device '" << deviceName << "' has invalid ID of " << deviceId;
message << "Device '" << deviceName << "' has invalid ID of " << (int)deviceId.GetValue();
throw vtkm::cont::ErrorBadValue(message.str());
}
}
@ -79,7 +77,7 @@ bool RuntimeDeviceTracker::CanRunOnImpl(vtkm::cont::DeviceAdapterId deviceId,
const vtkm::cont::DeviceAdapterNameType& deviceName) const
{
this->CheckDevice(deviceId, deviceName);
return this->Internals->RuntimeValid[deviceId];
return this->Internals->RuntimeValid[deviceId.GetValue()];
}
VTKM_CONT
@ -88,7 +86,7 @@ void RuntimeDeviceTracker::SetDeviceState(vtkm::cont::DeviceAdapterId deviceId,
bool state)
{
this->CheckDevice(deviceId, deviceName);
this->Internals->RuntimeValid[deviceId] = state;
this->Internals->RuntimeValid[deviceId.GetValue()] = state;
}
namespace
@ -105,9 +103,9 @@ struct VTKM_NEVER_EXPORT RuntimeDeviceTrackerResetFunctor
}
template <typename Device>
VTKM_CONT void operator()(Device)
VTKM_CONT void operator()(Device device)
{
this->Tracker.ResetDevice(Device());
this->Tracker.ResetDevice(device);
}
};
}
@ -152,7 +150,7 @@ void RuntimeDeviceTracker::ForceDeviceImpl(vtkm::cont::DeviceAdapterId deviceId,
std::fill_n(this->Internals->RuntimeValid, VTKM_MAX_DEVICE_ADAPTER_ID, false);
this->Internals->RuntimeValid[deviceId] = runtimeExists;
this->Internals->RuntimeValid[deviceId.GetValue()] = runtimeExists;
}
VTKM_CONT

@ -62,10 +62,10 @@ public:
/// machine.
///
template <typename DeviceAdapterTag>
VTKM_CONT bool CanRunOn(DeviceAdapterTag) const
VTKM_CONT bool CanRunOn(DeviceAdapterTag device) const
{
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag>;
return this->CanRunOnImpl(Traits::GetId(), Traits::GetName());
return this->CanRunOnImpl(device, Traits::GetName());
}
/// Report a failure to allocate memory on a device, this will flag the
@ -73,10 +73,11 @@ public:
/// the filter.
///
template <typename DeviceAdapterTag>
VTKM_CONT void ReportAllocationFailure(DeviceAdapterTag, const vtkm::cont::ErrorBadAllocation&)
VTKM_CONT void ReportAllocationFailure(DeviceAdapterTag device,
const vtkm::cont::ErrorBadAllocation&)
{
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag>;
this->SetDeviceState(Traits::GetId(), Traits::GetName(), false);
this->SetDeviceState(device, Traits::GetName(), false);
}
/// Report a failure to allocate memory on a device, this will flag the
@ -93,10 +94,10 @@ public:
//@{
/// Report a ErrorBadDevice failure and flag the device as unusable.
template <typename DeviceAdapterTag>
VTKM_CONT void ReportBadDeviceFailure(DeviceAdapterTag, const vtkm::cont::ErrorBadDevice&)
VTKM_CONT void ReportBadDeviceFailure(DeviceAdapterTag device, const vtkm::cont::ErrorBadDevice&)
{
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag>;
this->SetDeviceState(Traits::GetId(), Traits::GetName(), false);
this->SetDeviceState(device, Traits::GetName(), false);
}
VTKM_CONT void ReportBadDeviceFailure(vtkm::cont::DeviceAdapterId deviceId,
@ -111,11 +112,11 @@ public:
/// caused by reported failures
///
template <typename DeviceAdapterTag>
VTKM_CONT void ResetDevice(DeviceAdapterTag)
VTKM_CONT void ResetDevice(DeviceAdapterTag device)
{
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag>;
vtkm::cont::RuntimeDeviceInformation<DeviceAdapterTag> runtimeDevice;
this->SetDeviceState(Traits::GetId(), Traits::GetName(), runtimeDevice.Exists());
this->SetDeviceState(device, Traits::GetName(), runtimeDevice.Exists());
}
/// Reset the tracker to its default state for default devices.
@ -177,10 +178,10 @@ public:
/// Use \c ResetDevice to turn the device back on (if it is supported).
///
template <typename DeviceAdapterTag>
VTKM_CONT void DisableDevice(DeviceAdapterTag)
VTKM_CONT void DisableDevice(DeviceAdapterTag device)
{
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag>;
this->SetDeviceState(Traits::GetId(), Traits::GetName(), false);
this->SetDeviceState(device, Traits::GetName(), false);
}
/// \brief Disable all devices except the specified one.
@ -196,11 +197,11 @@ public:
/// exist on the system.
///
template <typename DeviceAdapterTag>
VTKM_CONT void ForceDevice(DeviceAdapterTag)
VTKM_CONT void ForceDevice(DeviceAdapterTag device)
{
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag>;
vtkm::cont::RuntimeDeviceInformation<DeviceAdapterTag> runtimeDevice;
this->ForceDeviceImpl(Traits::GetId(), Traits::GetName(), runtimeDevice.Exists());
this->ForceDeviceImpl(device, Traits::GetName(), runtimeDevice.Exists());
}
private:

@ -37,13 +37,14 @@ VTKM_CONT_EXPORT void HandleTryExecuteException(vtkm::cont::DeviceAdapterId,
vtkm::cont::RuntimeDeviceTracker&);
template <typename DeviceTag, typename Functor, typename... Args>
bool TryExecuteIfValid(std::true_type,
DeviceTag tag,
Functor&& f,
vtkm::cont::RuntimeDeviceTracker& tracker,
Args&&... args)
inline bool TryExecuteIfValid(std::true_type,
DeviceTag tag,
Functor&& f,
vtkm::cont::DeviceAdapterId devId,
vtkm::cont::RuntimeDeviceTracker& tracker,
Args&&... args)
{
if (tracker.CanRunOn(tag))
if ((tag == devId || devId == DeviceAdapterIdAny()) && tracker.CanRunOn(tag))
{
try
{
@ -52,7 +53,7 @@ bool TryExecuteIfValid(std::true_type,
catch (...)
{
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceTag>;
detail::HandleTryExecuteException(Traits::GetId(), Traits::GetName(), tracker);
detail::HandleTryExecuteException(tag, Traits::GetName(), tracker);
}
}
@ -61,11 +62,12 @@ bool TryExecuteIfValid(std::true_type,
}
template <typename DeviceTag, typename Functor, typename... Args>
bool TryExecuteIfValid(std::false_type,
DeviceTag,
Functor&&,
vtkm::cont::RuntimeDeviceTracker&,
Args&&...)
inline bool TryExecuteIfValid(std::false_type,
DeviceTag,
Functor&&,
vtkm::cont::DeviceAdapterId,
vtkm::cont::RuntimeDeviceTracker&,
Args&&...)
{
return false;
}
@ -73,18 +75,19 @@ bool TryExecuteIfValid(std::false_type,
struct TryExecuteWrapper
{
template <typename DeviceTag, typename Functor, typename... Args>
void operator()(DeviceTag tag,
Functor&& f,
vtkm::cont::RuntimeDeviceTracker& tracker,
bool& ran,
Args&&... args) const
inline void operator()(DeviceTag tag,
Functor&& f,
vtkm::cont::DeviceAdapterId devId,
vtkm::cont::RuntimeDeviceTracker& tracker,
bool& ran,
Args&&... args) const
{
if (!ran)
{
using DeviceTraits = vtkm::cont::DeviceAdapterTraits<DeviceTag>;
ran = TryExecuteIfValid(std::integral_constant<bool, DeviceTraits::Valid>(),
ran = TryExecuteIfValid(std::integral_constant<bool, DeviceTag::IsEnabled>(),
tag,
std::forward<Functor>(f),
devId,
std::forward<decltype(tracker)>(tracker),
std::forward<Args>(args)...);
}
@ -92,29 +95,40 @@ struct TryExecuteWrapper
};
template <typename Functor, typename DeviceList, typename... Args>
VTKM_CONT bool TryExecuteImpl(Functor&& functor,
std::true_type,
std::true_type,
vtkm::cont::RuntimeDeviceTracker& tracker,
DeviceList list,
Args&&... args)
inline bool TryExecuteImpl(vtkm::cont::DeviceAdapterId devId,
Functor&& functor,
std::true_type,
std::true_type,
vtkm::cont::RuntimeDeviceTracker& tracker,
DeviceList list,
Args&&... args)
{
bool success = false;
detail::TryExecuteWrapper task;
vtkm::ListForEach(
task, list, std::forward<Functor>(functor), tracker, success, std::forward<Args>(args)...);
TryExecuteWrapper task;
vtkm::ListForEach(task,
list,
std::forward<Functor>(functor),
devId,
tracker,
success,
std::forward<Args>(args)...);
return success;
}
template <typename Functor, typename... Args>
VTKM_CONT bool TryExecuteImpl(Functor&& functor, std::false_type, std::false_type, Args&&... args)
inline bool TryExecuteImpl(vtkm::cont::DeviceAdapterId devId,
Functor&& functor,
std::false_type,
std::false_type,
Args&&... args)
{
bool success = false;
auto tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker();
detail::TryExecuteWrapper task;
TryExecuteWrapper task;
vtkm::ListForEach(task,
VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG(),
std::forward<Functor>(functor),
devId,
tracker,
success,
std::forward<Args>(args)...);
@ -122,13 +136,15 @@ VTKM_CONT bool TryExecuteImpl(Functor&& functor, std::false_type, std::false_typ
}
template <typename Functor, typename Arg1, typename... Args>
VTKM_CONT bool TryExecuteImpl(Functor&& functor,
std::true_type t,
std::false_type,
Arg1&& arg1,
Args&&... args)
inline bool TryExecuteImpl(vtkm::cont::DeviceAdapterId devId,
Functor&& functor,
std::true_type t,
std::false_type,
Arg1&& arg1,
Args&&... args)
{
return TryExecuteImpl(std::forward<Functor>(functor),
return TryExecuteImpl(devId,
std::forward<Functor>(functor),
t,
t,
std::forward<Arg1>(arg1),
@ -137,14 +153,16 @@ VTKM_CONT bool TryExecuteImpl(Functor&& functor,
}
template <typename Functor, typename Arg1, typename... Args>
VTKM_CONT bool TryExecuteImpl(Functor&& functor,
std::false_type,
std::true_type t,
Arg1&& arg1,
Args&&... args)
inline bool TryExecuteImpl(vtkm::cont::DeviceAdapterId devId,
Functor&& functor,
std::false_type,
std::true_type t,
Arg1&& arg1,
Args&&... args)
{
auto tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker();
return TryExecuteImpl(std::forward<Functor>(functor),
return TryExecuteImpl(devId,
std::forward<Functor>(functor),
t,
t,
tracker,
@ -155,9 +173,125 @@ VTKM_CONT bool TryExecuteImpl(Functor&& functor,
} // namespace detail
///@{
/// \brief Try to execute a functor on a list of devices until one succeeds.
/// \brief Try to execute a functor on a specific device selected at runtime.
///
/// This function takes a functor and a \c DeviceAdapterId which represents a
/// specific device to attempt to run on at runtime. It also optionally accepts
/// the following parameters:
/// - A set of devices to compile support for
/// - \c RuntimeDeviceTracker which holds which devices have been enabled at runtime, and
/// records any functor execution failures
///
/// It then iterates over the set of devices finding which one matches the provided
/// adapter Id and is also enabled in the runtime. The function will return true
/// only if the device adapter was valid, and the task was successfully run.
/// The optional \c RuntimeDeviceTracker allows for monitoring for certain
/// failures across calls to TryExecute and skip trying devices with a history of failure.
///
/// The TryExecuteOnDevice is also able to perfectly forward arbitrary arguments onto the functor.
/// These arguments must be placed after the optional \c RuntimeDeviceTracker, and device adapter
/// list and will passed to the functor in the same order as listed.
///
/// The functor must implement the function call operator ( \c operator() ) with a return type of
/// \c bool and that is \c true if the execution succeeds, \c false if it fails. If an exception
/// is thrown from the functor, then the execution is assumed to have failed. The functor call
/// operator must also take at least one argument being the required \c DeviceAdapterTag to use.
///
/// \code{.cpp}
/// struct TryCallExample
/// {
/// template<typename DeviceList>
/// bool operator()(DeviceList tags, int) const
/// {
/// return true;
/// }
/// };
///
///
/// // Execute only on the device which corresponds to devId
/// // Will not execute all if devId is
/// vtkm::cont::TryExecuteOnDevice(devId, TryCallExample(), int{42});
///
/// // Executing on a specific deviceID with a runtime tracker
/// auto tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker();
/// vtkm::cont::TryExecute(devId, TryCallExample(), tracker, int{42});
///
/// \endcode
///
/// This function returns \c true if the functor succeeded on a device,
/// \c false otherwise.
///
/// If no device list is specified, then \c VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG
/// is used.
///
/// If no \c RuntimeDeviceTracker specified, then \c GetGlobalRuntimeDeviceTracker()
/// is used.
template <typename Functor>
VTKM_CONT bool TryExecuteOnDevice(vtkm::cont::DeviceAdapterId devId, Functor&& functor)
{
//we haven't been passed either a runtime tracker or a device list
return detail::TryExecuteImpl(
devId, std::forward<Functor>(functor), std::false_type{}, std::false_type{});
}
template <typename Functor, typename Arg1>
VTKM_CONT bool TryExecuteOnDevice(vtkm::cont::DeviceAdapterId devId, Functor&& functor, Arg1&& arg1)
{
//determine if we are being passed a device adapter or runtime tracker as our argument
using is_deviceAdapter = typename std::is_base_of<vtkm::detail::ListRoot, Arg1>::type;
using is_tracker = typename std::is_base_of<vtkm::cont::RuntimeDeviceTracker,
typename std::remove_reference<Arg1>::type>::type;
return detail::TryExecuteImpl(devId,
std::forward<Functor>(functor),
is_tracker{},
is_deviceAdapter{},
std::forward<Arg1>(arg1));
}
template <typename Functor, typename Arg1, typename Arg2, typename... Args>
VTKM_CONT bool TryExecuteOnDevice(vtkm::cont::DeviceAdapterId devId,
Functor&& functor,
Arg1&& arg1,
Arg2&& arg2,
Args&&... args)
{
//So arg1 can be runtime or device adapter
//if arg1 is runtime, we need to see if arg2 is device adapter
using is_arg1_tracker =
typename std::is_base_of<vtkm::cont::RuntimeDeviceTracker,
typename std::remove_reference<Arg1>::type>::type;
using is_arg2_devicelist = typename std::is_base_of<vtkm::detail::ListRoot, Arg2>::type;
//We now know what of three states we are currently at
using has_runtime_and_deviceAdapter =
brigand::bool_<is_arg1_tracker::value && is_arg2_devicelist::value>;
using has_just_runtime = brigand::bool_<is_arg1_tracker::value && !is_arg2_devicelist::value>;
using has_just_devicelist = typename std::is_base_of<vtkm::detail::ListRoot, Arg1>::type;
//With this information we can now compute if we have a runtime tracker and/or
//the device adapter and enable the correct flags
using first_true =
brigand::bool_<has_runtime_and_deviceAdapter::value || has_just_runtime::value>;
using second_true =
brigand::bool_<has_runtime_and_deviceAdapter::value || has_just_devicelist::value>;
return detail::TryExecuteImpl(devId,
functor,
first_true{},
second_true{},
std::forward<Arg1>(arg1),
std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
//@} //block doxygen all TryExecuteOnDevice functions
///@{
/// \brief Try to execute a functor on a set of devices until one succeeds.
///
/// This function takes a functor and optionally the following:
/// - A set of devices to compile support for
/// - \c RuntimeDeviceTracker which holds which devices have been enabled at runtime, and
/// records any functor execution failures
///
/// This function takes a functor and optionally a list of devices and \c RuntimeDeviceTracker.
/// It then tries to run the functor for each device (in the order given in the list) until the
/// execution succeeds. The optional \c RuntimeDeviceTracker allows for monitoring for certain
/// failures across calls to TryExecute and skip trying devices with a history of failure.
@ -182,7 +316,7 @@ VTKM_CONT bool TryExecuteImpl(Functor&& functor,
/// };
///
///
/// // Executing without a runtime tracker or device list
/// // Executing without a runtime tracker, deviceId, or device list
/// vtkm::cont::TryExecute(TryCallExample(), int{42});
///
/// // Executing with a runtime tracker
@ -206,53 +340,13 @@ VTKM_CONT bool TryExecuteImpl(Functor&& functor,
///
/// If no \c RuntimeDeviceTracker specified, then \c GetGlobalRuntimeDeviceTracker()
/// is used.
template <typename Functor>
VTKM_CONT bool TryExecute(Functor&& functor)
template <typename Functor, typename... Args>
VTKM_CONT bool TryExecute(Functor&& functor, Args&&... args)
{
//we haven't been passed either a runtime tracker or a device list
return detail::TryExecuteImpl(functor, std::false_type{}, std::false_type{});
return TryExecuteOnDevice(
vtkm::cont::DeviceAdapterIdAny(), std::forward<Functor>(functor), std::forward<Args>(args)...);
}
template <typename Functor, typename Arg1>
VTKM_CONT bool TryExecute(Functor&& functor, Arg1&& arg1)
{
//determine if we are being passed a device adapter or runtime tracker as our argument
using is_deviceAdapter = typename std::is_base_of<vtkm::detail::ListRoot, Arg1>::type;
using is_tracker = typename std::is_base_of<vtkm::cont::RuntimeDeviceTracker,
typename std::remove_reference<Arg1>::type>::type;
return detail::TryExecuteImpl(
functor, is_tracker{}, is_deviceAdapter{}, std::forward<Arg1>(arg1));
}
template <typename Functor, typename Arg1, typename Arg2, typename... Args>
VTKM_CONT bool TryExecute(Functor&& functor, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
//So arg1 can be runtime or device adapter
//if arg1 is runtime, we need to see if arg2 is device adapter
using is_arg1_tracker =
typename std::is_base_of<vtkm::cont::RuntimeDeviceTracker,
typename std::remove_reference<Arg1>::type>::type;
using is_arg2_devicelist = typename std::is_base_of<vtkm::detail::ListRoot, Arg2>::type;
//We now know what of three states we are currently at
using has_runtime_and_deviceAdapter =
brigand::bool_<is_arg1_tracker::value && is_arg2_devicelist::value>;
using has_just_runtime = brigand::bool_<is_arg1_tracker::value && !is_arg2_devicelist::value>;
using has_just_devicelist = typename std::is_base_of<vtkm::detail::ListRoot, Arg1>::type;
//With this information we can now compute if we have a runtime tracker and/or
//the device adapter and enable the correct flags
using first_true =
brigand::bool_<has_runtime_and_deviceAdapter::value || has_just_runtime::value>;
using second_true =
brigand::bool_<has_runtime_and_deviceAdapter::value || has_just_devicelist::value>;
return detail::TryExecuteImpl(functor,
first_true{},
second_true{},
std::forward<Arg1>(arg1),
std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
//@} //block doxygen all TryExecute functions
}

@ -30,8 +30,6 @@
#include <array>
#include <type_traits>
#define VTKM_MAX_DEVICE_ADAPTER_ID 8
namespace vtkm
{
namespace cont
@ -114,7 +112,6 @@ public:
/// Release all the execution side resources
VTKM_CONT void ReleaseExecutionResources() { this->Internals->ReleaseExecutionResources(); }
/// Get a valid \c VirtualBaseType* with the current control side state for \c deviceId.
/// VirtualObjectHandle and the returned pointer are analogous to ArrayHandle and Portal
/// The returned pointer will be invalidated if:
@ -129,21 +126,14 @@ public:
throw vtkm::cont::ErrorBadValue("No target object bound");
}
if (deviceId < 0 || deviceId >= VTKM_MAX_DEVICE_ADAPTER_ID)
{
std::string msg = "An invalid device adapter ID of " + std::to_string(deviceId) +
"was used when trying to construct a virtual object. The valid range is between " +
"0 and " + std::to_string(VTKM_MAX_DEVICE_ADAPTER_ID) + ")";
throw vtkm::cont::ErrorBadType(msg);
}
if (!this->Internals->Current || this->Internals->Current->GetDeviceId() != deviceId)
{
if (!this->Internals->Transfers[static_cast<std::size_t>(deviceId)])
if (!this->Internals->Transfers[static_cast<std::size_t>(deviceId.GetValue())])
{
std::string msg = "The device adapter ID of " + std::to_string(deviceId) +
" was not part of the set of valid adapters when this virtual object was constructed " +
"or last binded to a set of device adapters.";
std::string msg = "VTK-m was asked to transfer an object for execution on DeviceAdapter " +
std::to_string(deviceId.GetValue()) +
". It can't as this VirtualObjectHandle was not constructed/bound with this "
"DeviceAdapter in the list of valid DeviceAdapters.";
throw vtkm::cont::ErrorBadType(msg);
}
@ -152,25 +142,12 @@ public:
this->Internals->Current->ReleaseResources();
}
this->Internals->Current =
this->Internals->Transfers[static_cast<std::size_t>(deviceId)].get();
this->Internals->Transfers[static_cast<std::size_t>(deviceId.GetValue())].get();
}
return this->Internals->Current->PrepareForExecution();
}
/// Get a valid \c VirtualBaseType* with the current control side state for \c DeviceAdapter.
/// VirtualObjectHandle and the returned pointer are analogous to ArrayHandle and Portal
/// The returned pointer will be invalidated if:
/// 1. A new pointer is requested for a different DeviceAdapter
/// 2. VirtualObjectHandle is destroyed
/// 3. Reset or ReleaseResources is called
///
template <typename DeviceAdapter>
VTKM_CONT const VirtualBaseType* PrepareForExecution(DeviceAdapter) const
{
using DeviceInfo = vtkm::cont::DeviceAdapterTraits<DeviceAdapter>;
return this->PrepareForExecution(DeviceInfo::GetId());
}
private:
class TransferInterface
@ -194,10 +171,7 @@ private:
{
}
VTKM_CONT vtkm::cont::DeviceAdapterId GetDeviceId() const override
{
return vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId();
}
VTKM_CONT vtkm::cont::DeviceAdapterId GetDeviceId() const override { return DeviceAdapter(); }
VTKM_CONT const VirtualBaseType* PrepareForExecution() override
{
@ -220,21 +194,24 @@ private:
struct CreateTransferInterface
{
template <typename DeviceAdapter>
VTKM_CONT void operator()(DeviceAdapter,
VTKM_CONT void operator()(DeviceAdapter device,
std::unique_ptr<TransferInterface>* transfers,
const VirtualDerivedType* virtualObject) const
{
using DeviceInfo = vtkm::cont::DeviceAdapterTraits<DeviceAdapter>;
if (DeviceInfo::GetId() < 0 || DeviceInfo::GetId() >= VTKM_MAX_DEVICE_ADAPTER_ID)
if (!device.IsValueValid())
{
std::string msg = "Device '" + DeviceInfo::GetName() + "' has invalid ID of " +
std::to_string(DeviceInfo::GetId()) + "(VTKM_MAX_DEVICE_ADAPTER_ID = " +
std::to_string(VTKM_MAX_DEVICE_ADAPTER_ID) + ")";
std::string msg =
"VTK-m is unable to construct a VirtualObjectHandle for execution on DeviceAdapter" +
DeviceInfo::GetName() + "[id=" + std::to_string(device.GetValue()) +
"]. This is generally caused by either asking for execution on a DeviceAdapter that "
"wasn't compiled into VTK-m. In the case of CUDA it can also be caused by accidentally "
"compiling source files as C++ files instead of CUDA.";
throw vtkm::cont::ErrorBadType(msg);
}
using TransferImpl = TransferInterfaceImpl<VirtualDerivedType, DeviceAdapter>;
transfers[DeviceInfo::GetId()].reset(new TransferImpl(virtualObject));
transfers[device.GetValue()].reset(new TransferImpl(virtualObject));
}
};
@ -277,6 +254,4 @@ private:
}
} // vtkm::cont
#undef VTKM_MAX_DEVICE_ADAPTER_ID
#endif // vtk_m_cont_VirtualObjectHandle_h

@ -250,7 +250,7 @@ bool ArrayHandleImpl::PrepareForDevice(DeviceAdapterId devId, vtkm::UInt64 sizeO
DeviceAdapterId ArrayHandleImpl::GetDeviceAdapterId() const
{
return this->ExecutionArrayValid ? this->ExecutionInterface->GetDeviceId()
: VTKM_DEVICE_ADAPTER_UNDEFINED;
: DeviceAdapterIdUndefined{};
}

@ -234,10 +234,9 @@ ArrayHandle<T, StorageTagBasic>::PrepareForInPlace(DeviceAdapterTag device)
template <typename T>
template <typename DeviceAdapterTag>
void ArrayHandle<T, StorageTagBasic>::PrepareForDevice(DeviceAdapterTag) const
void ArrayHandle<T, StorageTagBasic>::PrepareForDevice(DeviceAdapterTag device) const
{
DeviceAdapterId devId = DeviceAdapterTraits<DeviceAdapterTag>::GetId();
bool needToRealloc = this->Internals->PrepareForDevice(devId, sizeof(T));
bool needToRealloc = this->Internals->PrepareForDevice(device, sizeof(T));
if (needToRealloc)
{
this->Internals->ExecutionInterface =

@ -152,9 +152,9 @@ public:
void ReleaseResources() { this->ReleaseResourcesImpl(); }
template <typename DeviceAdapter>
VTKM_CONT bool IsDeviceAdapter(DeviceAdapter) const
VTKM_CONT bool IsDeviceAdapter(DeviceAdapter device) const
{
return this->IsDeviceAdapterImpl(vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId());
return this->IsDeviceAdapterImpl(device);
}
VTKM_CONT
@ -255,16 +255,10 @@ protected:
void ReleaseResourcesImpl() { this->Transfer.ReleaseResources(); }
VTKM_CONT
bool IsDeviceAdapterImpl(const DeviceAdapterId& id) const
{
return id == vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId();
}
bool IsDeviceAdapterImpl(const DeviceAdapterId& id) const { return id == DeviceAdapter(); }
VTKM_CONT
DeviceAdapterId GetDeviceAdapterIdImpl() const
{
return vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId();
}
DeviceAdapterId GetDeviceAdapterIdImpl() const { return DeviceAdapter(); }
private:
ArrayTransferType Transfer;

@ -37,10 +37,10 @@ class ExecuteIfValidDeviceTag
{
private:
template <typename DeviceAdapter>
using EnableIfValid = std::enable_if<vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::Valid>;
using EnableIfValid = std::enable_if<DeviceAdapter::IsEnabled>;
template <typename DeviceAdapter>
using EnableIfInvalid = std::enable_if<!vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::Valid>;
using EnableIfInvalid = std::enable_if<!DeviceAdapter::IsEnabled>;
public:
explicit ExecuteIfValidDeviceTag(const FunctorType& functor)
@ -99,7 +99,7 @@ public:
bool& status,
Args&&... args) const
{
if (vtkm::cont::DeviceAdapterTraits<DeviceAdapter>::GetId() == deviceId)
if (device == deviceId)
{
VTKM_ASSERT(status == false);
this->Functor(device, std::forward<Args>(args)...);
@ -126,8 +126,8 @@ VTKM_CONT void FindDeviceAdapterTagAndCall(vtkm::cont::DeviceAdapterId deviceId,
ForEachValidDevice(devices, wrapped, deviceId, status, std::forward<Args>(args)...);
if (!status)
{
std::string msg =
"Device with id " + std::to_string(deviceId) + " is either not in the list or is invalid";
std::string msg = "Device with id " + std::to_string(deviceId.GetValue()) +
" is either not in the list or is invalid";
throw vtkm::cont::ErrorBadDevice(msg);
}
}

@ -33,13 +33,56 @@
#define VTKM_DEVICE_ADAPTER_CUDA 2
#define VTKM_DEVICE_ADAPTER_TBB 3
#define VTKM_DEVICE_ADAPTER_OPENMP 4
//VTKM_DEVICE_ADAPTER_TestAlgorithmGeneral 7
#define VTKM_MAX_DEVICE_ADAPTER_ID 8
#define VTKM_DEVICE_ADAPTER_ANY 127
namespace vtkm
{
namespace cont
{
using DeviceAdapterId = vtkm::Int8;
struct DeviceAdapterId
{
constexpr explicit DeviceAdapterId(vtkm::Int8 id)
: Value(id)
{
}
constexpr bool operator==(DeviceAdapterId other) const { return this->Value == other.Value; }
constexpr bool operator!=(DeviceAdapterId other) const { return this->Value != other.Value; }
constexpr bool operator<(DeviceAdapterId other) const { return this->Value < other.Value; }
constexpr bool IsValueValid() const
{
return this->Value > 0 && this->Value < VTKM_MAX_DEVICE_ADAPTER_ID;
}
constexpr vtkm::Int8 GetValue() const { return this->Value; }
private:
vtkm::Int8 Value;
};
// Represents when using TryExecute that the functor
// can be executed on any device instead of a specific
// one
struct DeviceAdapterIdAny : DeviceAdapterId
{
constexpr DeviceAdapterIdAny()
: DeviceAdapterId(127)
{
}
};
struct DeviceAdapterIdUndefined : DeviceAdapterId
{
constexpr DeviceAdapterIdUndefined()
: DeviceAdapterId(VTKM_DEVICE_ADAPTER_UNDEFINED)
{
}
};
using DeviceAdapterNameType = std::string;
template <typename DeviceAdapter>
@ -55,15 +98,18 @@ struct DeviceAdapterTraits;
{ \
namespace cont \
{ \
struct VTKM_ALWAYS_EXPORT DeviceAdapterTag##Name \
struct VTKM_ALWAYS_EXPORT DeviceAdapterTag##Name : DeviceAdapterId \
{ \
constexpr DeviceAdapterTag##Name() \
: DeviceAdapterId(Id) \
{ \
} \
static constexpr bool IsEnabled = true; \
}; \
template <> \
struct DeviceAdapterTraits<vtkm::cont::DeviceAdapterTag##Name> \
{ \
static DeviceAdapterId GetId() { return DeviceAdapterId(Id); } \
static DeviceAdapterNameType GetName() { return DeviceAdapterNameType(#Name); } \
static constexpr bool Valid = true; \
}; \
} \
}
@ -76,15 +122,18 @@ struct DeviceAdapterTraits;
{ \
namespace cont \
{ \
struct VTKM_ALWAYS_EXPORT DeviceAdapterTag##Name \
struct VTKM_ALWAYS_EXPORT DeviceAdapterTag##Name : DeviceAdapterId \
{ \
constexpr DeviceAdapterTag##Name() \
: DeviceAdapterId(Id) \
{ \
} \
static constexpr bool IsEnabled = false; \
}; \
template <> \
struct DeviceAdapterTraits<vtkm::cont::DeviceAdapterTag##Name> \
{ \
static DeviceAdapterId GetId() { return DeviceAdapterId(Id); } \
static DeviceAdapterNameType GetName() { return DeviceAdapterNameType(#Name); } \
static constexpr bool Valid = false; \
}; \
} \
}
@ -95,7 +144,8 @@ struct DeviceAdapterTraits;
/// elsewhere in the code when a mistake is made.)
///
#define VTKM_IS_DEVICE_ADAPTER_TAG(tag) \
VTKM_STATIC_ASSERT_MSG(::vtkm::cont::DeviceAdapterTraits<tag>::Valid, \
"Provided type is not a valid VTK-m device adapter tag.")
static_assert(std::is_base_of<vtkm::cont::DeviceAdapterId, tag>::value && \
!std::is_same<vtkm::cont::DeviceAdapterId, tag>::value, \
"Provided type is not a valid VTK-m device adapter tag.")
#endif //vtk_m_cont_internal_DeviceAdapterTag_h

@ -25,8 +25,7 @@ namespace cont
{
VTKM_CONT bool DeviceAdapterRuntimeDetector<vtkm::cont::DeviceAdapterTagOpenMP>::Exists() const
{
using DeviceAdapterTraits = vtkm::cont::DeviceAdapterTraits<vtkm::cont::DeviceAdapterTagOpenMP>;
return DeviceAdapterTraits::Valid;
return vtkm::cont::DeviceAdapterTagOpenMP::IsEnabled;
}
}
}

@ -25,8 +25,7 @@ namespace cont
{
VTKM_CONT bool DeviceAdapterRuntimeDetector<vtkm::cont::DeviceAdapterTagSerial>::Exists() const
{
using DeviceAdapterTraits = vtkm::cont::DeviceAdapterTraits<vtkm::cont::DeviceAdapterTagSerial>;
return DeviceAdapterTraits::Valid;
return vtkm::cont::DeviceAdapterTagSerial::IsEnabled;
}
}
}

@ -24,8 +24,7 @@ namespace cont
{
VTKM_CONT bool DeviceAdapterRuntimeDetector<vtkm::cont::DeviceAdapterTagTBB>::Exists() const
{
using DeviceAdapterTraits = vtkm::cont::DeviceAdapterTraits<vtkm::cont::DeviceAdapterTagTBB>;
return DeviceAdapterTraits::Valid;
return vtkm::cont::DeviceAdapterTagTBB::IsEnabled;
}
}
}

@ -27,7 +27,6 @@
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/cont/ArrayHandleExtractComponent.h>
#include <vtkm/cont/serial/DeviceAdapterSerial.h>
#include <vtkm/cont/testing/Testing.h>
#include <algorithm>
@ -406,7 +405,7 @@ private:
VTKM_TEST_ASSERT(a1 == a2, "Shallow copied array not equal.");
VTKM_TEST_ASSERT(!(a1 != a2), "Shallow copied array not equal.");
a1.PrepareForInPlace(DeviceAdapterTagSerial());
a1.PrepareForInPlace(DeviceAdapterTag());
VTKM_TEST_ASSERT(a1 == a2, "Shallow copied array not equal.");
VTKM_TEST_ASSERT(!(a1 != a2), "Shallow copied array not equal.");
}

@ -471,14 +471,17 @@ private:
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing device adapter tag" << std::endl;
constexpr DeviceAdapterTag deviceTag;
constexpr vtkm::cont::DeviceAdapterTagError errorTag;
VTKM_TEST_ASSERT(deviceTag.GetValue() == deviceTag.GetValue(),
"Device adapter Id does not equal itself.");
VTKM_TEST_ASSERT(deviceTag.GetValue() != errorTag.GetValue(),
"Device adapter Id not distinguishable from others.");
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag>;
using ErrorTraits = vtkm::cont::DeviceAdapterTraits<vtkm::cont::DeviceAdapterTagError>;
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.");
VTKM_TEST_ASSERT(Traits::GetName() == Traits::GetName(),
"Device adapter Name does not equal itself.");
VTKM_TEST_ASSERT(Traits::GetName() != ErrorTraits::GetName(),

@ -116,11 +116,11 @@ struct ExecutionPortalFactoryBasic<T, DeviceAdapterTagTestAlgorithmGeneral>
{
using Superclass = ExecutionPortalFactoryBasicShareWithControl<T>;
using typename Superclass::ValueType;
using typename Superclass::PortalType;
using typename Superclass::PortalConstType;
using Superclass::CreatePortal;
using Superclass::CreatePortalConst;
using typename Superclass::PortalConstType;
using typename Superclass::PortalType;
using typename Superclass::ValueType;
};
template <>

@ -38,7 +38,7 @@ void detect_if_exists(DeviceAdapterTag tag)
{
using DeviceAdapterTraits = vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag>;
std::cout << "testing runtime support for " << DeviceAdapterTraits::GetName() << std::endl;
DoesExist<DeviceAdapterTraits::Valid> exist;
DoesExist<tag.IsEnabled> exist;
exist.Exist(tag);
}

@ -38,25 +38,40 @@ namespace interop
namespace detail
{
template <typename ArrayT>
struct PrepareForInteropFunctor
struct TransferToOpenGL
{
ArrayT& Array;
PrepareForInteropFunctor(ArrayT& array)
: Array(array)
template <typename DeviceAdapterTag, typename ValueType, typename StorageTag>
VTKM_CONT bool operator()(DeviceAdapterTag,
const vtkm::cont::ArrayHandle<ValueType, StorageTag>& handle,
BufferState& state) const
{
}
template <typename DeviceAdapterTag>
bool operator()(DeviceAdapterTag) const
{
using Traits = vtkm::cont::DeviceAdapterTraits<DeviceAdapterTag>;
this->Array.PrepareForInput(DeviceAdapterTag());
return this->Array.GetDeviceAdapterId() == Traits::GetId();
vtkm::interop::internal::TransferToOpenGL<ValueType, DeviceAdapterTag> toGL(state);
return toGL.Transfer(handle);
}
};
}
/// \brief Manages transferring an ArrayHandle to opengl .
///
/// \c TransferToOpenGL manages to transfer the contents of an ArrayHandle
/// to OpenGL as efficiently as possible. Will use the given \p state to determine
/// what buffer handle to use, and the type to bind the buffer handle too.
/// Lastly state also holds on to per backend resources that allow for efficient
/// updating to open gl.
///
/// This function keeps the buffer as the active buffer of the input type.
///
///
template <typename ValueType, class StorageTag, class DeviceAdapterTag>
VTKM_CONT void TransferToOpenGL(vtkm::cont::ArrayHandle<ValueType, StorageTag> handle,
BufferState& state,
DeviceAdapterTag)
{
vtkm::interop::internal::TransferToOpenGL<ValueType, DeviceAdapterTag> toGL(state);
toGL.Transfer(handle);
}
/// \brief Manages transferring an ArrayHandle to opengl .
///
/// \c TransferToOpenGL manages to transfer the contents of an ArrayHandle
@ -70,60 +85,22 @@ struct PrepareForInteropFunctor
///
/// This function will throw exceptions if the transfer wasn't possible
///
template <typename ValueType, class StorageTag, class DeviceAdapterTag>
VTKM_CONT void TransferToOpenGL(vtkm::cont::ArrayHandle<ValueType, StorageTag> handle,
BufferState& state,
DeviceAdapterTag)
{
vtkm::interop::internal::TransferToOpenGL<ValueType, DeviceAdapterTag> toGL(state);
return toGL.Transfer(handle);
}
/// Dispatch overload for TransferToOpenGL that deduces the DeviceAdapter for
/// the given ArrayHandle.
///
/// \overload
///
template <typename ValueType, class StorageTag>
template <typename ValueType, typename StorageTag>
VTKM_CONT void TransferToOpenGL(vtkm::cont::ArrayHandle<ValueType, StorageTag> handle,
BufferState& state)
{
vtkm::cont::DeviceAdapterId devId = handle.GetDeviceAdapterId();
if (devId == VTKM_DEVICE_ADAPTER_UNDEFINED)
bool success = vtkm::cont::TryExecuteOnDevice(devId, TransferToOpenGL, handle, state);
if (!success)
{
using ArrayT = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
using Functor = detail::PrepareForInteropFunctor<ArrayT>;
// Undefined device means that the array is not in an execution environment.
// In this case, call PrepareForInput using the devices in the tracker
// to move the data onto a device. This is required for a CUDA usecase
// where a device pointer is set as control memory to reuse an already
// allocated buffer. PrepareForInput on CUDA will detect this and set the
// execution pointer to match the control pointer.
vtkm::cont::TryExecute(Functor(handle));
devId = handle.GetDeviceAdapterId();
//Generally we are here because the devId is undefined
//or for some reason the last executed device is now disabled
success = vtkm::cont::TryExecute(TransferToOpenGL, handle, state);
}
switch (devId)
if (!success)
{
case VTKM_DEVICE_ADAPTER_SERIAL:
TransferToOpenGL(handle, state, vtkm::cont::DeviceAdapterTagSerial());
break;
#ifdef VTKM_ENABLE_TBB
case VTKM_DEVICE_ADAPTER_TBB:
TransferToOpenGL(handle, state, vtkm::cont::DeviceAdapterTagTBB());
break;
#endif
#ifdef VTKM_CUDA
case VTKM_DEVICE_ADAPTER_CUDA:
TransferToOpenGL(handle, state, vtkm::cont::DeviceAdapterTagCuda());
break;
#endif
default:
throw vtkm::cont::ErrorBadValue("Unknown device id.");
throw vtkm::cont::ErrorBadValue("Unknown device id.");
}
}
}

@ -213,10 +213,10 @@ public:
//set up stack size for cuda environment
#ifdef VTKM_CUDA
using DeviceAdapterTraits = vtkm::cont::DeviceAdapterTraits<DeviceAdapter>;
constexpr DeviceAdapter deviceId;
std::size_t stackSizeBackup;
(void)stackSizeBackup;
if (DeviceAdapterTraits::GetId() == VTKM_DEVICE_ADAPTER_CUDA)
if (deviceId.GetValue() == VTKM_DEVICE_ADAPTER_CUDA)
{
cudaDeviceGetLimit(&stackSizeBackup, cudaLimitStackSize);
cudaDeviceSetLimit(cudaLimitStackSize, 1024 * 16);
@ -230,7 +230,7 @@ public:
qc_Handle, pointId_Handle, splitId_Handle, coordi_Handle, nnId_Handle, nnDis_Handle);
#ifdef VTKM_CUDA
if (DeviceAdapterTraits::GetId() == VTKM_DEVICE_ADAPTER_CUDA)
if (deviceId.GetValue() == VTKM_DEVICE_ADAPTER_CUDA)
{
cudaDeviceSetLimit(cudaLimitStackSize, stackSizeBackup);
}