ArrayHandleVirtual.h 18.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
//============================================================================
//  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 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
//  Copyright 2014 UT-Battelle, LLC.
//  Copyright 2014 Los Alamos National Security.
//
//  Under the terms of Contract DE-NA0003525 with NTESS,
//  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 vtk_m_cont_ArrayHandleVirtual_h
#define vtk_m_cont_ArrayHandleVirtual_h

#include <vtkm/cont/vtkm_cont_export.h>

#include <vtkm/cont/ArrayHandle.h>
26
#include <vtkm/cont/ArrayHandleUniformPointCoordinates.h>
27 28
#include <vtkm/cont/internal/DeviceAdapterTag.h>

29
#include <vtkm/cont/StorageAny.h>
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
#include <vtkm/cont/StorageVirtual.h>

#include <memory>

namespace vtkm
{
namespace cont
{

/// Specialization of ArrayHandle for virtual storage.
template <typename T>
class VTKM_ALWAYS_EXPORT ArrayHandle<T, ::vtkm::cont::StorageTagVirtual>
  : public vtkm::cont::internal::ArrayHandleBase
{
public:
  using StorageTag = vtkm::cont::StorageTagVirtual;
  using StorageType = vtkm::cont::internal::Storage<void, vtkm::cont::StorageTagVirtual>;

  using ValueType = T;

  using PortalControl = vtkm::ArrayPortalRef<T>;
  using PortalConstControl = vtkm::ArrayPortalRef<T>;

  template <typename Device>
  struct ExecutionTypes
  {
    using Portal = vtkm::ArrayPortalRef<T>;
    using PortalConst = vtkm::ArrayPortalRef<T>;
  };

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

  ///Construct a invalid ArrayHandleVirtual that has nullptr storage
  VTKM_CONT ArrayHandle()
    : Storage(nullptr)
  {
  }

  ///Construct a valid ArrayHandleVirtual from an existing ArrayHandle
  ///that doesn't derive from ArrayHandleVirtual.
  ///Note left non-explicit to allow:
  ///
  /// std::vector<vtkm::cont::ArrayHandleVirtual<vtkm::Float64>> vectorOfArrays;
  /// //Make basic array.
  /// vtkm::cont::ArrayHandle<vtkm::Float64> basicArray;
  ///  //Fill basicArray...
  /// vectorOfArrays.push_back(basicArray);
  ///
  /// // Make fancy array.
  /// vtkm::cont::ArrayHandleCounting<vtkm::Float64> fancyArray(-1.0, 0.1, ARRAY_SIZE);
  /// vectorOfArrays.push_back(fancyArray);


  template <typename S>
  ArrayHandle(const vtkm::cont::ArrayHandle<T, S>& ah)
    : Storage(std::make_shared<vtkm::cont::StorageAny<T, S>>(ah))
  {
    using is_base = std::is_base_of<vtkm::cont::StorageVirtual, S>;
    static_assert(!is_base::value, "Wrong specialization for ArrayHandleVirtual selected");
  }

  ///Copy an existing ArrayHandleVirtual into this instance
  ArrayHandle(const ArrayHandle<T, vtkm::cont::StorageTagVirtual>& src) = default;
92

93 94 95 96 97 98

  /// virtual destructor, as required to make sure derived classes that
  /// might have member variables are properly cleaned up.
  //
  virtual ~ArrayHandle() = default;

99

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
  ///Move existing shared_ptr of vtkm::cont::StorageVirtual to be
  ///owned by this ArrayHandleVirtual.
  ///This is generally how derived class construct a valid ArrayHandleVirtual
  template <typename DerivedStorage>
  explicit ArrayHandle(std::shared_ptr<DerivedStorage>&& storage) noexcept
    : Storage(std::move(storage))
  {
    using is_base = std::is_base_of<vtkm::cont::StorageVirtual, DerivedStorage>;
    static_assert(is_base::value,
                  "Storage for ArrayHandleVirtual needs to derive from vtkm::cont::StorageVirual");
  }

  ///Move existing unique_ptr of vtkm::cont::StorageVirtual to be
  ///owned by this ArrayHandleVirtual.
  ///This is how a derived class construct a valid ArrayHandleVirtual
  template <typename DerivedStorage>
  explicit ArrayHandle(std::unique_ptr<DerivedStorage>&& storage) noexcept
    : Storage(std::move(storage))
  {
    using is_base = std::is_base_of<vtkm::cont::StorageVirtual, DerivedStorage>;
    static_assert(is_base::value,
                  "Storage for ArrayHandleVirtual needs to derive from vtkm::cont::StorageVirual");
  }

  ///move from one virtual array handle to another
  ArrayHandle(ArrayHandle<T, vtkm::cont::StorageTagVirtual>&& src) noexcept
    : Storage(std::move(src.Storage))
  {
  }

130 131 132 133 134 135 136
  ///move from one a non-virtual array handle to virtual array handle
  template <typename S>
  ArrayHandle(ArrayHandle<T, S>&& src) noexcept
    : Storage(std::make_shared<vtkm::cont::StorageAny<T, S>>(std::move(src)))
  {
  }

137 138
  VTKM_CONT ArrayHandle<T, vtkm::cont::StorageTagVirtual>& operator=(
    const ArrayHandle<T, vtkm::cont::StorageTagVirtual>& src) = default;
139 140 141 142 143 144 145
  template <typename S>
  VTKM_CONT ArrayHandle<T, vtkm::cont::StorageTagVirtual>& operator=(const ArrayHandle<T, S>& src)
  {
    this->Storage = std::make_shared<vtkm::cont::StorageAny<T, S>>(src);
    return *this;
  }

146 147 148 149 150 151
  VTKM_CONT ArrayHandle<T, vtkm::cont::StorageTagVirtual>& operator=(
    ArrayHandle<T, vtkm::cont::StorageTagVirtual>&& src) noexcept
  {
    this->Storage = std::move(src.Storage);
    return *this;
  }
152 153 154 155 156 157 158
  template <typename S>
  VTKM_CONT ArrayHandle<T, vtkm::cont::StorageTagVirtual>& operator=(
    ArrayHandle<T, S>&& src) noexcept
  {
    this->Storage = std::make_shared<vtkm::cont::StorageAny<T, S>>(std::move(src));
    return *this;
  }
159

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
  /// Like a pointer, two \c ArrayHandles are considered equal if they point
  /// to the same location in memory.
  ///
  VTKM_CONT
  bool operator==(const ArrayHandle<T, StorageTag>& rhs) const
  {
    return (this->Storage == rhs.Storage);
  }

  VTKM_CONT
  bool operator!=(const ArrayHandle<T, StorageTag>& rhs) const
  {
    return (this->Storage != rhs.Storage);
  }

  template <typename VT, typename ST>
  VTKM_CONT bool operator==(const ArrayHandle<VT, ST>&) const
  {
    return false; // different valuetype and/or storage
  }

  template <typename VT, typename ST>
  VTKM_CONT bool operator!=(const ArrayHandle<VT, ST>&) const
  {
    return true; // different valuetype and/or storage
  }

187
  /// Returns true if this array matches the type passed in.
188 189 190 191 192 193
  ///
  template <typename ArrayHandleType>
  VTKM_CONT bool IsType() const
  {
    VTKM_IS_ARRAY_HANDLE(ArrayHandleType);

194 195 196 197
    //We need to determine if we are checking that `ArrayHandleType`
    //is a virtual array handle since that is an easy check.
    //Or if we have to go ask the storage if they are holding
    //
198
    using ST = typename ArrayHandleType::StorageTag;
199
    using is_base = std::is_same<vtkm::cont::StorageTagVirtual, ST>;
200 201 202 203 204 205 206 207

    //Determine if the Value type of the virtual and ArrayHandleType
    //are the same. This an easy compile time check, and doesn't need
    // to be done at runtime.
    using VT = typename ArrayHandleType::ValueType;
    using same_value_type = std::is_same<T, VT>;

    return this->IsSameType<ArrayHandleType>(same_value_type{}, is_base{});
208 209
  }

210 211 212 213 214 215 216 217 218 219 220 221 222 223
  /// Returns this array cast to the given \c ArrayHandle type. Throws \c
  /// ErrorBadType if the cast does not work. Use \c IsType
  /// to check if the cast can happen.
  ///
  template <typename ArrayHandleType>
  VTKM_CONT ArrayHandleType Cast() const
  {
    VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
    //We need to determine if we are checking that `ArrayHandleType`
    //is a virtual array handle since that is an easy check.
    //Or if we have to go ask the storage if they are holding
    //
    using ST = typename ArrayHandleType::StorageTag;
    using is_base = std::is_same<vtkm::cont::StorageTagVirtual, ST>;
224 225 226 227 228 229 230 231

    //Determine if the Value type of the virtual and ArrayHandleType
    //are the same. This an easy compile time check, and doesn't need
    // to be done at runtime.
    using VT = typename ArrayHandleType::ValueType;
    using same_value_type = std::is_same<T, VT>;

    return this->CastToType<ArrayHandleType>(same_value_type{}, is_base{});
232 233
  }

234 235 236 237 238 239 240 241 242
  /// Returns a new instance of an ArrayHandleVirtual with the same storage
  ///
  VTKM_CONT ArrayHandle<T, ::vtkm::cont::StorageTagVirtual> NewInstance() const
  {
    return (this->Storage)
      ? ArrayHandle<T, ::vtkm::cont::StorageTagVirtual>(this->Storage->NewInstance())
      : ArrayHandle<T, ::vtkm::cont::StorageTagVirtual>();
  }

243 244 245 246 247 248 249 250 251
  /// Returns a view on the internal storage of the ArrayHandleVirtual
  ///
  VTKM_CONT const StorageType* GetStorage() const { return this->Storage.get(); }

  /// Get the array portal of the control array.
  /// Since worklet invocations are asynchronous and this routine is a synchronization point,
  /// exceptions maybe thrown for errors from previously executed worklets.
  ///
  PortalControl GetPortalControl()
252 253
  {
    return make_ArrayPortalRef(
254
      static_cast<const vtkm::ArrayPortalVirtual<T>*>(this->Storage->GetPortalControl()),
255 256 257
      this->GetNumberOfValues());
  }

258 259 260 261 262
  /// Get the array portal of the control array.
  /// Since worklet invocations are asynchronous and this routine is a synchronization point,
  /// exceptions maybe thrown for errors from previously executed worklets.
  ///
  PortalConstControl GetPortalConstControl() const
263
  {
264 265 266
    return make_ArrayPortalRef(
      static_cast<const vtkm::ArrayPortalVirtual<T>*>(this->Storage->GetPortalConstControl()),
      this->GetNumberOfValues());
267 268
  }

269 270
  /// Returns the number of entries in the array.
  ///
271 272
  vtkm::Id GetNumberOfValues() const { return this->Storage->GetNumberOfValues(); }

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
  /// \brief Allocates an array large enough to hold the given number of values.
  ///
  /// The allocation may be done on an already existing array, but can wipe out
  /// any data already in the array. This method can throw
  /// ErrorBadAllocation if the array cannot be allocated or
  /// ErrorBadValue if the allocation is not feasible (for example, the
  /// array storage is read-only).
  ///
  VTKM_CONT
  void Allocate(vtkm::Id numberOfValues) { return this->Storage->Allocate(numberOfValues); }

  /// \brief Reduces the size of the array without changing its values.
  ///
  /// This method allows you to resize the array without reallocating it. The
  /// number of entries in the array is changed to \c numberOfValues. The data
  /// in the array (from indices 0 to \c numberOfValues - 1) are the same, but
  /// \c numberOfValues must be equal or less than the preexisting size
  /// (returned from GetNumberOfValues). That is, this method can only be used
  /// to shorten the array, not lengthen.
  void Shrink(vtkm::Id numberOfValues) { return this->Storage->Shrink(numberOfValues); }

294 295 296 297 298 299 300 301 302
  /// Releases any resources being used in the execution environment (that are
  /// not being shared by the control environment).
  ///
  void ReleaseResourcesExecution() { return this->Storage->ReleaseResourcesExecution(); }

  /// Releases all resources in both the control and execution environments.
  ///
  void ReleaseResources() { return this->Storage->ReleaseResources(); }

303 304 305 306 307
  /// Prepares this array to be used as an input to an operation in the
  /// execution environment. If necessary, copies data to the execution
  /// environment. Can throw an exception if this array does not yet contain
  /// any data. Returns a portal that can be used in code running in the
  /// execution environment.
308
  ///
309 310 311 312 313 314 315
  /// Return a ArrayPortalRef that wraps the real virtual portal. We need a stack object for
  /// the following reasons:
  /// 1. Device Adapter algorithms only support const AH<T,S>& and not const AH<T,S>*
  /// 2. Devices will want to get the length of a portal before execution, but for CUDA
  ///  we can't ask this information of the portal as it only valid on the device, instead
  ///  we have to store this information also in the ref wrapper
  vtkm::ArrayPortalRef<T> PrepareForInput(vtkm::cont::DeviceAdapterId devId) const
316 317
  {
    return make_ArrayPortalRef(
318
      static_cast<const vtkm::ArrayPortalVirtual<T>*>(this->Storage->PrepareForInput(devId)),
319 320 321
      this->GetNumberOfValues());
  }

322 323 324 325 326 327
  /// Prepares (allocates) this array to be used as an output from an operation
  /// in the execution environment. The internal state of this class is set to
  /// have valid data in the execution array with the assumption that the array
  /// will be filled soon (i.e. before any other methods of this object are
  /// called). Returns a portal that can be used in code running in the
  /// execution environment.
328
  ///
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
  vtkm::ArrayPortalRef<T> PrepareForOutput(vtkm::Id numberOfValues,
                                           vtkm::cont::DeviceAdapterId devId)
  {
    return make_ArrayPortalRef(static_cast<const vtkm::ArrayPortalVirtual<T>*>(
                                 this->Storage->PrepareForOutput(numberOfValues, devId)),
                               numberOfValues);
  }

  /// Prepares this array to be used in an in-place operation (both as input
  /// and output) in the execution environment. If necessary, copies data to
  /// the execution environment. Can throw an exception if this array does not
  /// yet contain any data. Returns a portal that can be used in code running
  /// in the execution environment.
  ///
  vtkm::ArrayPortalRef<T> PrepareForInPlace(vtkm::cont::DeviceAdapterId devId)
344 345
  {
    return make_ArrayPortalRef(
346
      static_cast<const vtkm::ArrayPortalVirtual<T>*>(this->Storage->PrepareForInPlace(devId)),
347 348 349
      this->GetNumberOfValues());
  }

350 351 352 353 354 355
  /// Returns the DeviceAdapterId for the current device. If there is no device
  /// with an up-to-date copy of the data, VTKM_DEVICE_ADAPTER_UNDEFINED is
  /// returned.
  VTKM_CONT
  DeviceAdapterId GetDeviceAdapterId() const { return this->Storage->GetDeviceAdapterId(); }

356 357
protected:
  std::shared_ptr<StorageType> Storage = nullptr;
358 359 360

private:
  template <typename ArrayHandleType>
361 362 363 364 365 366 367 368 369 370 371 372 373
  inline bool IsSameType(std::false_type, std::true_type) const
  {
    return false;
  }
  template <typename ArrayHandleType>
  inline bool IsSameType(std::false_type, std::false_type) const
  {
    return false;
  }

  template <typename ArrayHandleType>
  inline bool IsSameType(std::true_type vtkmNotUsed(valueTypesMatch),
                         std::true_type vtkmNotUsed(inheritsFromArrayHandleVirtual)) const
374 375
  {
    //All classes that derive from ArrayHandleVirtual have virtual methods so we can use
376 377
    //typeid directly.
    //needs optimizations based on platform. !OSX can use typeid
378 379 380 381 382 383 384 385 386 387 388 389 390 391
    auto casted = dynamic_cast<const ArrayHandleType*>(this);
    return casted != nullptr;
  }

  template <typename ArrayHandleType>
  inline bool IsSameType(std::true_type vtkmNotUsed(valueTypesMatch),
                         std::false_type vtkmNotUsed(notFromArrayHandleVirtual)) const
  {
    if (!this->Storage)
    {
      return false;
    }
    using S = typename ArrayHandleType::StorageTag;
    return this->Storage->template IsType<vtkm::cont::StorageAny<T, S>>();
392 393 394
  }

  template <typename ArrayHandleType>
395 396 397 398 399 400 401
  inline ArrayHandleType CastToType(std::false_type vtkmNotUsed(valueTypesMatch),
                                    std::true_type vtkmNotUsed(notFromArrayHandleVirtual)) const
  {
    VTKM_LOG_CAST_FAIL(*this, ArrayHandleType);
    throwFailedDynamicCast("ArrayHandleVirtual", vtkm::cont::TypeName<ArrayHandleType>());
    return ArrayHandleType{};
  }
402 403

  template <typename ArrayHandleType>
404 405 406 407 408 409 410 411 412 413 414 415
  inline ArrayHandleType CastToType(std::false_type vtkmNotUsed(valueTypesMatch),
                                    std::false_type vtkmNotUsed(notFromArrayHandleVirtual)) const
  {
    VTKM_LOG_CAST_FAIL(*this, ArrayHandleType);
    throwFailedDynamicCast("ArrayHandleVirtual", vtkm::cont::TypeName<ArrayHandleType>());
    return ArrayHandleType{};
  }

  template <typename ArrayHandleType>
  inline ArrayHandleType CastToType(
    std::true_type vtkmNotUsed(valueTypesMatch),
    std::true_type vtkmNotUsed(inheritsFromArrayHandleVirtual)) const
416
  {
417 418 419 420 421 422 423 424 425 426
    //All classes that derive from ArrayHandleVirtual have virtual methods so we can use
    //dynamic_cast directly
    const ArrayHandleType* derived = dynamic_cast<const ArrayHandleType*>(this);
    if (!derived)
    {
      VTKM_LOG_CAST_FAIL(*this, ArrayHandleType);
      throwFailedDynamicCast("ArrayHandleVirtual", vtkm::cont::TypeName<ArrayHandleType>());
    }
    VTKM_LOG_CAST_SUCC(*this, derived);
    return *derived;
427
  }
428 429

  template <typename ArrayHandleType>
430 431
  ArrayHandleType CastToType(std::true_type vtkmNotUsed(valueTypesMatch),
                             std::false_type vtkmNotUsed(notFromArrayHandleVirtual)) const;
432 433
};

434 435
//=============================================================================
// Better name for the type
436 437 438 439
template <typename T>
using ArrayHandleVirtual = vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagVirtual>;


440 441 442 443 444 445 446 447 448
//=============================================================================
/// A convenience function for creating an ArrayHandleVirtual.
template <typename T, typename S>
VTKM_CONT vtkm::cont::ArrayHandleVirtual<T> make_ArrayHandleVirtual(
  const vtkm::cont::ArrayHandle<T, S>& ah)
{
  return vtkm::cont::ArrayHandleVirtual<T>(ah);
}

449 450 451
//=============================================================================
// Free function casting helpers

452 453
/// Returns true if \c virtHandle matches the type of ArrayHandleType.
///
454 455 456 457 458 459
template <typename ArrayHandleType, typename T>
VTKM_CONT inline bool IsType(const vtkm::cont::ArrayHandleVirtual<T>& virtHandle)
{
  return virtHandle.template IsType<ArrayHandleType>();
}

460 461 462 463
/// Returns \c virtHandle cast to the given \c ArrayHandle type. Throws \c
/// ErrorBadType if the cast does not work. Use \c IsType
/// to check if the cast can happen.
///
464 465 466 467 468 469
template <typename ArrayHandleType, typename T>
VTKM_CONT inline ArrayHandleType Cast(const vtkm::cont::ArrayHandleVirtual<T>& virtHandle)
{
  return virtHandle.template Cast<ArrayHandleType>();
}

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
//=============================================================================
// Specializations of CastAndCall to help make sure ArrayHandleVirtual
// holding a ArrayHandleUniformPointCoordinates works properly
template <typename Functor, typename... Args>
void CastAndCall(vtkm::cont::ArrayHandleVirtual<vtkm::Vec<vtkm::FloatDefault, 3>> coords,
                 Functor&& f,
                 Args&&... args)
{
  using HandleType = ArrayHandleUniformPointCoordinates;
  using T = typename HandleType::ValueType;
  using S = typename HandleType::StorageTag;
  if (coords.IsType<HandleType>())
  {
    const vtkm::cont::StorageVirtual* storage = coords.GetStorage();
    auto* any = storage->Cast<vtkm::cont::StorageAny<T, S>>();
    f(any->GetHandle(), std::forward<Args>(args)...);
  }
  else
  {
    f(coords, std::forward<Args>(args)...);
  }
}

493 494


495 496 497 498 499 500 501 502 503 504 505 506 507
//=============================================================================
// Specializations of serialization related classes
template <typename T>
struct TypeString<vtkm::cont::ArrayHandleVirtual<T>>
{
  static VTKM_CONT const std::string& Get()
  {
    static std::string name = "AH_Virtual<" + TypeString<T>::Get() + ">";
    return name;
  }
};
}
} //namespace vtkm::cont
508 509


510
#endif //vtk_m_cont_ArrayHandleVirtual_h