ArrayHandle.h 18.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
//============================================================================
//  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.
11
//  Copyright 2014 Los Alamos National Security.
12 13 14 15 16 17 18 19
//
//  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.
//============================================================================
20 21
#ifndef vtk_m_cont_ArrayHandle_h
#define vtk_m_cont_ArrayHandle_h
22 23 24 25 26

#include <vtkm/Types.h>

#include <vtkm/cont/Assert.h>
#include <vtkm/cont/ErrorControlBadValue.h>
27
#include <vtkm/cont/ErrorControlInternal.h>
28
#include <vtkm/cont/Storage.h>
29
#include <vtkm/cont/StorageBasic.h>
30

31 32 33 34
#include <vtkm/cont/internal/ArrayHandleExecutionManager.h>
#include <vtkm/cont/internal/DeviceAdapterTag.h>

#include <boost/concept_check.hpp>
35
#include <boost/mpl/not.hpp>
36 37
#include <boost/smart_ptr/scoped_ptr.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
38
#include <boost/type_traits/is_base_of.hpp>
39 40 41 42 43 44

#include <vector>

namespace vtkm {
namespace cont {

45 46
namespace internal {

Kenneth Moreland's avatar
Kenneth Moreland committed
47 48 49 50 51 52 53 54
/// \brief Base class of all ArrayHandle classes.
///
/// This is an empty class that is used to check if something is an \c
/// ArrayHandle class (or at least something that behaves exactly like one).
/// The \c ArrayHandle template class inherits from this.
///
class ArrayHandleBase {  };

55 56 57 58 59
/// Checks to see if the given type and storage can form a valid array handle
/// (some storage objects cannot support all types). This check is compatable
/// with the Boost meta-template programming library (MPL). It contains a
/// typedef named type that is either boost::mpl::true_ or boost::mpl::false_.
/// Both of these have a typedef named value with the respective boolean value.
60
///
61
template<typename T, typename StorageTag>
62 63 64
struct IsValidArrayHandle {
  typedef typename boost::mpl::not_<
    typename boost::is_base_of<
65 66
      vtkm::cont::internal::UndefinedStorage,
      vtkm::cont::internal::Storage<T,StorageTag>
67 68 69 70
      >::type
    >::type type;
};

Kenneth Moreland's avatar
Kenneth Moreland committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
/// Checks to see if the given object is an array handle. This check is
/// compatiable with the Boost meta-template programming library (MPL). It
/// contains a typedef named type that is eitehr boost::mpl::true_ or
/// boost::mpl::false_. Both of these have a typedef named value with the
/// respective boolean value.
///
/// Unlike \c IsValidArrayHandle, if an \c ArrayHandle is used with this
/// class, then it must be created by the compiler and therefore must already
/// be valid. Where \c IsValidArrayHandle is used when you know something is
/// an \c ArrayHandle but you are not sure if the \c StorageTag is valid, this
/// class is used to ensure that a given type is an \c ArrayHandle. It is
/// used internally in the VTKM_IS_ARRAY_HANDLE macro.
///
template<typename T>
struct ArrayHandleCheck
{
  typedef typename boost::is_base_of<
      ::vtkm::cont::internal::ArrayHandleBase, T>::type type;
};

#define VTKM_IS_ARRAY_HANDLE(type) \
  BOOST_MPL_ASSERT(( ::vtkm::cont::internal::ArrayHandleCheck<type> ))

94
} // namespace internal
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

/// \brief Manages an array-worth of data.
///
/// \c ArrayHandle manages as array of data that can be manipulated by VTKm
/// algorithms. The \c ArrayHandle may have up to two copies of the array, one
/// for the control environment and one for the execution environment, although
/// depending on the device and how the array is being used, the \c ArrayHandle
/// will only have one copy when possible.
///
/// An ArrayHandle can be constructed one of two ways. Its default construction
/// creates an empty, unallocated array that can later be allocated and filled
/// either by the user or a VTKm algorithm. The \c ArrayHandle can also be
/// constructed with iterators to a user's array. In this case the \c
/// ArrayHandle will keep a reference to this array but may drop it if the
/// array is reallocated.
///
/// \c ArrayHandle behaves like a shared smart pointer in that when it is copied
/// each copy holds a reference to the same array.  These copies are reference
/// counted so that when all copies of the \c ArrayHandle are destroyed, any
/// allocated memory is released.
///
116
///
117 118
template<
    typename T,
119
    typename StorageTag_ = VTKM_DEFAULT_STORAGE_TAG>
Kenneth Moreland's avatar
Kenneth Moreland committed
120
class ArrayHandle : public internal::ArrayHandleBase
121 122
{
private:
123 124
  typedef vtkm::cont::internal::Storage<T,StorageTag_> StorageType;
  typedef vtkm::cont::internal::ArrayHandleExecutionManagerBase<T,StorageTag_>
125 126 127
      ExecutionManagerType;
public:
  typedef T ValueType;
128 129 130
  typedef StorageTag_ StorageTag;
  typedef typename StorageType::PortalType PortalControl;
  typedef typename StorageType::PortalConstType PortalConstControl;
131
  template <typename DeviceAdapterTag>
132 133
  struct ExecutionTypes
  {
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    typedef typename ExecutionManagerType
        ::template ExecutionTypes<DeviceAdapterTag>::Portal Portal;
    typedef typename ExecutionManagerType
        ::template ExecutionTypes<DeviceAdapterTag>::PortalConst PortalConst;
  };

  /// Constructs an empty ArrayHandle. Typically used for output or
  /// intermediate arrays that will be filled by a VTKm algorithm.
  ///
  VTKM_CONT_EXPORT ArrayHandle() : Internals(new InternalStruct)
  {
    this->Internals->ControlArrayValid = false;
    this->Internals->ExecutionArrayValid = false;
  }

149 150 151
  /// Special constructor for subclass specializations that need to set the
  /// initial state of the control array. When this constructor is used, it
  /// is assumed that the control array is valid.
152
  ///
153
  ArrayHandle(const StorageType &storage)
154 155
    : Internals(new InternalStruct)
  {
156 157
    this->Internals->ControlArray = storage;
    this->Internals->ControlArrayValid = true;
158 159 160 161 162 163 164 165
    this->Internals->ExecutionArrayValid = false;
  }

  /// Get the array portal of the control array.
  ///
  VTKM_CONT_EXPORT PortalControl GetPortalControl()
  {
    this->SyncControlArray();
166
    if (this->Internals->ControlArrayValid)
167
    {
168 169 170
      // If the user writes into the iterator we return, then the execution
      // array will become invalid. Play it safe and release the execution
      // resources. (Use the const version to preserve the execution array.)
171
      this->ReleaseResourcesExecutionInternal();
172
      return this->Internals->ControlArray.GetPortal();
173
    }
174
    else
175
    {
176 177
      throw vtkm::cont::ErrorControlInternal(
            "ArrayHandle::SyncControlArray did not make control array valid.");
178
    }
179 180 181 182 183 184 185
  }

  /// Get the array portal of the control array.
  ///
  VTKM_CONT_EXPORT PortalConstControl GetPortalConstControl() const
  {
    this->SyncControlArray();
186
    if (this->Internals->ControlArrayValid)
187
    {
188
      return this->Internals->ControlArray.GetPortalConst();
189
    }
190
    else
191
    {
192 193
      throw vtkm::cont::ErrorControlInternal(
            "ArrayHandle::SyncControlArray did not make control array valid.");
194
    }
195 196 197 198 199 200
  }

  /// Returns the number of entries in the array.
  ///
  VTKM_CONT_EXPORT vtkm::Id GetNumberOfValues() const
  {
201
    if (this->Internals->ControlArrayValid)
202
    {
203
      return this->Internals->ControlArray.GetNumberOfValues();
204
    }
205
    else if (this->Internals->ExecutionArrayValid)
206
    {
207
      return this->Internals->ExecutionArray->GetNumberOfValues();
208
    }
209
    else
210
    {
211
      return 0;
212
    }
213 214
  }

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
  /// \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
  /// ErrorControlOutOfMemory if the array cannot be allocated or
  /// ErrorControlBadValue if the allocation is not feasible (for example, the
  /// array storage is read-only).
  ///
  VTKM_CONT_EXPORT
  void Allocate(vtkm::Id numberOfValues)
  {
    this->ReleaseResourcesExecutionInternal();
    this->Internals->ControlArray.Allocate(numberOfValues);
    this->Internals->ControlArrayValid = true;
  }

231 232 233 234 235 236 237 238 239 240 241 242 243
  /// \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)
  {
    vtkm::Id originalNumberOfValues = this->GetNumberOfValues();

    if (numberOfValues < originalNumberOfValues)
244
    {
245
      if (this->Internals->ControlArrayValid)
246
      {
247
        this->Internals->ControlArray.Shrink(numberOfValues);
248
      }
249
      if (this->Internals->ExecutionArrayValid)
250
      {
251 252
        this->Internals->ExecutionArray->Shrink(numberOfValues);
      }
253
    }
254
    else if (numberOfValues == originalNumberOfValues)
255
    {
256
      // Nothing to do.
257
    }
258
    else // numberOfValues > originalNumberOfValues
259
    {
260
      throw vtkm::cont::ErrorControlBadValue(
261 262
        "ArrayHandle::Shrink cannot be used to grow array.");
    }
263 264 265 266 267 268 269 270 271

    VTKM_ASSERT_CONT(this->GetNumberOfValues() == numberOfValues);
  }

  /// Releases any resources being used in the execution environment (that are
  /// not being shared by the control environment).
  ///
  VTKM_CONT_EXPORT void ReleaseResourcesExecution()
  {
272 273 274 275 276
    // Save any data in the execution environment by making sure it is synced
    // with the control environment.
    this->SyncControlArray();

    this->ReleaseResourcesExecutionInternal();
277 278 279 280 281 282
  }

  /// Releases all resources in both the control and execution environments.
  ///
  VTKM_CONT_EXPORT void ReleaseResources()
  {
283
    this->ReleaseResourcesExecutionInternal();
284 285

    if (this->Internals->ControlArrayValid)
286
    {
287 288
      this->Internals->ControlArray.ReleaseResources();
      this->Internals->ControlArrayValid = false;
289
    }
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
  }

  /// 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.
  ///
  template<typename DeviceAdapterTag>
  VTKM_CONT_EXPORT
  typename ExecutionTypes<DeviceAdapterTag>::PortalConst
  PrepareForInput(DeviceAdapterTag) const
  {
    VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);

305 306
    if (!this->Internals->ControlArrayValid
        && !this->Internals->ExecutionArrayValid)
307
    {
308
      throw vtkm::cont::ErrorControlBadValue(
309 310
        "ArrayHandle has no data when PrepareForInput called.");
    }
311 312 313 314 315 316 317 318 319

    this->PrepareForDevice(DeviceAdapterTag());
    typename ExecutionTypes<DeviceAdapterTag>::PortalConst portal =
        this->Internals->ExecutionArray->PrepareForInput(
          !this->Internals->ExecutionArrayValid, DeviceAdapterTag());

    this->Internals->ExecutionArrayValid = true;

    return portal;
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
  }

  /// 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.
  ///
  template<typename DeviceAdapterTag>
  VTKM_CONT_EXPORT
  typename ExecutionTypes<DeviceAdapterTag>::Portal
  PrepareForOutput(vtkm::Id numberOfValues, DeviceAdapterTag)
  {
    VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);

    // Invalidate any control arrays.
    // Should the control array resource be released? Probably not a good
    // idea when shared with execution.
    this->Internals->ControlArrayValid = false;

    this->PrepareForDevice(DeviceAdapterTag());
342 343 344
    typename ExecutionTypes<DeviceAdapterTag>::Portal portal =
        this->Internals->ExecutionArray->PrepareForOutput(numberOfValues,
                                                          DeviceAdapterTag());
345 346 347 348 349 350 351 352 353 354 355 356

    // We are assuming that the calling code will fill the array using the
    // iterators we are returning, so go ahead and mark the execution array as
    // having valid data. (A previous version of this class had a separate call
    // to mark the array as filled, but that was onerous to call at the the
    // right time and rather pointless since it is basically always the case
    // that the array is going to be filled before anything else. In this
    // implementation the only access to the array is through the iterators
    // returned from this method, so you would have to work to invalidate this
    // assumption anyway.)
    this->Internals->ExecutionArrayValid = true;

357
    return portal;
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
  }

  /// 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.
  ///
  template<typename DeviceAdapterTag>
  VTKM_CONT_EXPORT
  typename ExecutionTypes<DeviceAdapterTag>::Portal
  PrepareForInPlace(DeviceAdapterTag)
  {
    VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);

373 374
    if (!this->Internals->ControlArrayValid
        && !this->Internals->ExecutionArrayValid)
375
    {
376
      throw vtkm::cont::ErrorControlBadValue(
377 378
        "ArrayHandle has no data when PrepareForInput called.");
    }
379

380 381 382 383 384 385 386
    this->PrepareForDevice(DeviceAdapterTag());
    typename ExecutionTypes<DeviceAdapterTag>::Portal portal =
        this->Internals->ExecutionArray->PrepareForInPlace(
          !this->Internals->ExecutionArrayValid, DeviceAdapterTag());

    this->Internals->ExecutionArrayValid = true;

387 388 389 390 391
    // Invalidate any control arrays since their data will become invalid when
    // the execution data is overwritten. Don't actually release the control
    // array. It may be shared as the execution array.
    this->Internals->ControlArrayValid = false;

392
    return portal;
393 394
  }

395
// private:
396 397
  struct InternalStruct
  {
398
    StorageType ControlArray;
399 400 401 402
    bool ControlArrayValid;

    boost::scoped_ptr<
      vtkm::cont::internal::ArrayHandleExecutionManagerBase<
403
        ValueType,StorageTag> > ExecutionArray;
404 405 406
    bool ExecutionArrayValid;
  };

407
  ArrayHandle(boost::shared_ptr<InternalStruct> i)
408 409 410 411 412 413 414 415 416 417 418 419 420
    : Internals(i)
  { }

  /// Gets this array handle ready to interact with the given device. If the
  /// array handle has already interacted with this device, then this method
  /// does nothing. Although the internal state of this class can change, the
  /// method is declared const because logically the data does not.
  ///
  template<typename DeviceAdapterTag>
  VTKM_CONT_EXPORT
  void PrepareForDevice(DeviceAdapterTag) const
  {
    if (this->Internals->ExecutionArray != NULL)
421
    {
422
      if (this->Internals->ExecutionArray->IsDeviceAdapter(DeviceAdapterTag()))
423
      {
424 425
        // Already have manager for correct device adapter. Nothing to do.
        return;
426
      }
427
      else
428
      {
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
        // Have the wrong manager. Delete the old one and create a new one
        // of the right type. (BTW, it would be possible for the array handle
        // to hold references to execution arrays on multiple devices. However,
        // there is not a clear use case for that yet and it is unclear what
        // the behavior of "dirty" arrays should be, so it is not currently
        // implemented.)
        this->SyncControlArray();
        // Need to change some state that does not change the logical state from
        // an external point of view.
        InternalStruct *internals
            = const_cast<InternalStruct*>(this->Internals.get());
        internals->ExecutionArray.reset();
        internals->ExecutionArrayValid = false;
        }
      }

    VTKM_ASSERT_CONT(this->Internals->ExecutionArray == NULL);
446
    VTKM_ASSERT_CONT(!this->Internals->ExecutionArrayValid);
447 448 449 450 451 452
    // Need to change some state that does not change the logical state from
    // an external point of view.
    InternalStruct *internals
        = const_cast<InternalStruct*>(this->Internals.get());
    internals->ExecutionArray.reset(
          new vtkm::cont::internal::ArrayHandleExecutionManager<
453
            T, StorageTag, DeviceAdapterTag>(&internals->ControlArray));
454 455 456 457 458 459 460 461 462 463
  }

  /// Synchronizes the control array with the execution array. If either the
  /// user array or control array is already valid, this method does nothing
  /// (because the data is already available in the control environment).
  /// Although the internal state of this class can change, the method is
  /// declared const because logically the data does not.
  ///
  VTKM_CONT_EXPORT void SyncControlArray() const
  {
464
    if (!this->Internals->ControlArrayValid)
465
    {
466 467 468
      // Need to change some state that does not change the logical state from
      // an external point of view.
      InternalStruct *internals
469
        = const_cast<InternalStruct*>(this->Internals.get());
470 471
      if (this->Internals->ExecutionArrayValid)
      {
472
        internals->ExecutionArray->RetrieveOutputData(&internals->ControlArray);
473 474 475 476 477 478 479 480 481 482
        internals->ControlArrayValid = true;
      }
      else
      {
        // This array is in the null state (there is nothing allocated), but
        // the calling function wants to do something with the array. Put this
        // class into a valid state by allocating an array of size 0.
        internals->ControlArray.Allocate(0);
        internals->ControlArrayValid = true;
      }
483
    }
484 485
  }

486 487 488 489 490 491 492 493 494 495
  VTKM_CONT_EXPORT
  void ReleaseResourcesExecutionInternal()
  {
    if (this->Internals->ExecutionArrayValid)
    {
      this->Internals->ExecutionArray->ReleaseResources();
      this->Internals->ExecutionArrayValid = false;
    }
  }

496 497 498
  boost::shared_ptr<InternalStruct> Internals;
};

499
/// A convenience function for creating an ArrayHandle from a standard C array.
500 501 502
///
template<typename T>
VTKM_CONT_EXPORT
503 504 505
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>
make_ArrayHandle(const T *array,
                 vtkm::Id length)
506
{
507 508 509 510 511
  typedef vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>
      ArrayHandleType;
  typedef vtkm::cont::internal::Storage<T, vtkm::cont::StorageTagBasic>
      StorageType;
  return ArrayHandleType(StorageType(array, length));
512 513 514 515 516
}

/// A convenience function for creating an ArrayHandle from an std::vector.
///
template<typename T,
517
         typename Allocator>
518
VTKM_CONT_EXPORT
519
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>
520 521
make_ArrayHandle(const std::vector<T,Allocator> &array)
{
522
  return make_ArrayHandle(&array.front(), static_cast<vtkm::Id>(array.size()));
523 524 525 526 527
}

}
}

528
#endif //vtk_m_cont_ArrayHandle_h