1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //
6 //  This software is distributed WITHOUT ANY WARRANTY; without even
7 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 //  PURPOSE.  See the above copyright notice for more information.
9 //============================================================================
10 #ifndef vtk_m_cont_ArrayHandleGroupVecVariable_h
11 #define vtk_m_cont_ArrayHandleGroupVecVariable_h
12 
13 #include <vtkm/cont/Algorithm.h>
14 #include <vtkm/cont/ArrayGetValues.h>
15 #include <vtkm/cont/ArrayHandle.h>
16 #include <vtkm/cont/ArrayHandleCast.h>
17 #include <vtkm/cont/ArrayPortal.h>
18 #include <vtkm/cont/ErrorBadValue.h>
19 #include <vtkm/cont/RuntimeDeviceTracker.h>
20 #include <vtkm/cont/TryExecute.h>
21 
22 #include <vtkm/Assert.h>
23 #include <vtkm/VecFromPortal.h>
24 
25 namespace vtkm
26 {
27 namespace internal
28 {
29 
30 template <typename ComponentsPortalType, typename OffsetsPortalType>
31 class VTKM_ALWAYS_EXPORT ArrayPortalGroupVecVariable
32 {
33 public:
34   using ComponentType = typename std::remove_const<typename ComponentsPortalType::ValueType>::type;
35   using ValueType = vtkm::VecFromPortal<ComponentsPortalType>;
36 
37   VTKM_SUPPRESS_EXEC_WARNINGS
38   VTKM_EXEC_CONT
ArrayPortalGroupVecVariable()39   ArrayPortalGroupVecVariable()
40     : ComponentsPortal()
41     , OffsetsPortal()
42   {
43   }
44 
45   VTKM_SUPPRESS_EXEC_WARNINGS
46   VTKM_EXEC_CONT
ArrayPortalGroupVecVariable(const ComponentsPortalType & componentsPortal,const OffsetsPortalType & offsetsPortal)47   ArrayPortalGroupVecVariable(const ComponentsPortalType& componentsPortal,
48                               const OffsetsPortalType& offsetsPortal)
49     : ComponentsPortal(componentsPortal)
50     , OffsetsPortal(offsetsPortal)
51   {
52   }
53 
54   /// Copy constructor for any other ArrayPortalConcatenate with a portal type
55   /// that can be copied to this portal type. This allows us to do any type
56   /// casting that the portals do (like the non-const to const cast).
57   VTKM_SUPPRESS_EXEC_WARNINGS
58   template <typename OtherComponentsPortalType, typename OtherOffsetsPortalType>
ArrayPortalGroupVecVariable(const ArrayPortalGroupVecVariable<OtherComponentsPortalType,OtherOffsetsPortalType> & src)59   VTKM_EXEC_CONT ArrayPortalGroupVecVariable(
60     const ArrayPortalGroupVecVariable<OtherComponentsPortalType, OtherOffsetsPortalType>& src)
61     : ComponentsPortal(src.GetComponentsPortal())
62     , OffsetsPortal(src.GetOffsetsPortal())
63   {
64   }
65 
66   VTKM_SUPPRESS_EXEC_WARNINGS
67   VTKM_EXEC_CONT
GetNumberOfValues()68   vtkm::Id GetNumberOfValues() const { return this->OffsetsPortal.GetNumberOfValues() - 1; }
69 
70   VTKM_SUPPRESS_EXEC_WARNINGS
71   VTKM_EXEC_CONT
Get(vtkm::Id index)72   ValueType Get(vtkm::Id index) const
73   {
74     vtkm::Id offsetIndex = this->OffsetsPortal.Get(index);
75     vtkm::Id nextOffsetIndex = this->OffsetsPortal.Get(index + 1);
76 
77     return ValueType(this->ComponentsPortal,
78                      static_cast<vtkm::IdComponent>(nextOffsetIndex - offsetIndex),
79                      offsetIndex);
80   }
81 
82   VTKM_SUPPRESS_EXEC_WARNINGS
83   VTKM_EXEC_CONT
Set(vtkm::Id vtkmNotUsed (index),const ValueType & vtkmNotUsed (value))84   void Set(vtkm::Id vtkmNotUsed(index), const ValueType& vtkmNotUsed(value)) const
85   {
86     // The ValueType (VecFromPortal) operates on demand. Thus, if you set
87     // something in the value, it has already been passed to the array. Perhaps
88     // we should check to make sure that the value used matches the location
89     // you are trying to set in the array, but we don't do that.
90   }
91 
92   VTKM_SUPPRESS_EXEC_WARNINGS
93   VTKM_EXEC_CONT
GetComponentsPortal()94   const ComponentsPortalType& GetComponentsPortal() const { return this->ComponentsPortal; }
95 
96   VTKM_SUPPRESS_EXEC_WARNINGS
97   VTKM_EXEC_CONT
GetOffsetsPortal()98   const OffsetsPortalType& GetOffsetsPortal() const { return this->OffsetsPortal; }
99 
100 private:
101   ComponentsPortalType ComponentsPortal;
102   OffsetsPortalType OffsetsPortal;
103 };
104 
105 }
106 } // namespace vtkm::internal
107 
108 namespace vtkm
109 {
110 namespace cont
111 {
112 
113 template <typename ComponentsStorageTag, typename OffsetsStorageTag>
114 struct VTKM_ALWAYS_EXPORT StorageTagGroupVecVariable
115 {
116 };
117 
118 namespace internal
119 {
120 
121 template <typename ComponentsPortal, typename ComponentsStorageTag, typename OffsetsStorageTag>
122 class Storage<vtkm::VecFromPortal<ComponentsPortal>,
123               vtkm::cont::StorageTagGroupVecVariable<ComponentsStorageTag, OffsetsStorageTag>>
124 {
125   using ComponentType = typename ComponentsPortal::ValueType;
126   using ComponentsStorage = vtkm::cont::internal::Storage<ComponentType, ComponentsStorageTag>;
127   using OffsetsStorage = vtkm::cont::internal::Storage<vtkm::Id, OffsetsStorageTag>;
128 
129   VTKM_STATIC_ASSERT_MSG(
130     (std::is_same<ComponentsPortal, typename ComponentsStorage::WritePortalType>::value),
131     "Used invalid ComponentsPortal type with expected ComponentsStorageTag.");
132 
133   template <typename Buff>
ComponentsBuffers(Buff * buffers)134   VTKM_CONT static Buff* ComponentsBuffers(Buff* buffers)
135   {
136     return buffers;
137   }
138 
139   template <typename Buff>
OffsetsBuffers(Buff * buffers)140   VTKM_CONT static Buff* OffsetsBuffers(Buff* buffers)
141   {
142     return buffers + ComponentsStorage::GetNumberOfBuffers();
143   }
144 
145 public:
146   VTKM_STORAGE_NO_RESIZE;
147 
148   using ReadPortalType =
149     vtkm::internal::ArrayPortalGroupVecVariable<typename ComponentsStorage::ReadPortalType,
150                                                 typename OffsetsStorage::ReadPortalType>;
151   using WritePortalType =
152     vtkm::internal::ArrayPortalGroupVecVariable<typename ComponentsStorage::WritePortalType,
153                                                 typename OffsetsStorage::ReadPortalType>;
154 
GetNumberOfBuffers()155   VTKM_CONT static vtkm::IdComponent GetNumberOfBuffers()
156   {
157     return ComponentsStorage::GetNumberOfBuffers() + OffsetsStorage::GetNumberOfBuffers();
158   }
159 
GetNumberOfValues(const vtkm::cont::internal::Buffer * buffers)160   VTKM_CONT static vtkm::Id GetNumberOfValues(const vtkm::cont::internal::Buffer* buffers)
161   {
162     return OffsetsStorage::GetNumberOfValues(OffsetsBuffers(buffers)) - 1;
163   }
164 
CreateReadPortal(const vtkm::cont::internal::Buffer * buffers,vtkm::cont::DeviceAdapterId device,vtkm::cont::Token & token)165   VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers,
166                                                    vtkm::cont::DeviceAdapterId device,
167                                                    vtkm::cont::Token& token)
168   {
169     return ReadPortalType(
170       ComponentsStorage::CreateReadPortal(ComponentsBuffers(buffers), device, token),
171       OffsetsStorage::CreateReadPortal(OffsetsBuffers(buffers), device, token));
172   }
173 
CreateWritePortal(vtkm::cont::internal::Buffer * buffers,vtkm::cont::DeviceAdapterId device,vtkm::cont::Token & token)174   VTKM_CONT static WritePortalType CreateWritePortal(vtkm::cont::internal::Buffer* buffers,
175                                                      vtkm::cont::DeviceAdapterId device,
176                                                      vtkm::cont::Token& token)
177   {
178     return WritePortalType(
179       ComponentsStorage::CreateWritePortal(ComponentsBuffers(buffers), device, token),
180       OffsetsStorage::CreateReadPortal(OffsetsBuffers(buffers), device, token));
181   }
182 
CreateBuffers(const vtkm::cont::ArrayHandle<ComponentType,ComponentsStorageTag> & componentsArray,const vtkm::cont::ArrayHandle<vtkm::Id,OffsetsStorageTag> & offsetsArray)183   VTKM_CONT static std::vector<vtkm::cont::internal::Buffer> CreateBuffers(
184     const vtkm::cont::ArrayHandle<ComponentType, ComponentsStorageTag>& componentsArray,
185     const vtkm::cont::ArrayHandle<vtkm::Id, OffsetsStorageTag>& offsetsArray)
186   {
187     std::vector<vtkm::cont::internal::Buffer> destBuffer(
188       static_cast<std::size_t>(GetNumberOfBuffers()));
189     auto destIter = destBuffer.begin();
190 
191     destIter =
192       std::copy_n(componentsArray.GetBuffers(), ComponentsStorage::GetNumberOfBuffers(), destIter);
193     destIter =
194       std::copy_n(offsetsArray.GetBuffers(), OffsetsStorage::GetNumberOfBuffers(), destIter);
195 
196     return destBuffer;
197   }
198 
GetComponentsArray(const vtkm::cont::internal::Buffer * buffers)199   VTKM_CONT static vtkm::cont::ArrayHandle<ComponentType, ComponentsStorageTag> GetComponentsArray(
200     const vtkm::cont::internal::Buffer* buffers)
201   {
202     return vtkm::cont::ArrayHandle<ComponentType, ComponentsStorageTag>(ComponentsBuffers(buffers));
203   }
204 
GetOffsetsArray(const vtkm::cont::internal::Buffer * buffers)205   VTKM_CONT static vtkm::cont::ArrayHandle<vtkm::Id, OffsetsStorageTag> GetOffsetsArray(
206     const vtkm::cont::internal::Buffer* buffers)
207   {
208     return vtkm::cont::ArrayHandle<vtkm::Id, OffsetsStorageTag>(OffsetsBuffers(buffers));
209   }
210 };
211 
212 } // namespace internal
213 
214 /// \brief Fancy array handle that groups values into vectors of different sizes.
215 ///
216 /// It is sometimes the case that you need to run a worklet with an input or
217 /// output that has a different number of values per instance. For example, the
218 /// cells of a CellCetExplicit can have different numbers of points in each
219 /// cell. If inputting or outputting cells of this type, each instance of the
220 /// worklet might need a \c Vec of a different length. This fance array handle
221 /// takes an array of values and an array of offsets and groups the consecutive
222 /// values in Vec-like objects. The values are treated as tightly packed, so
223 /// that each Vec contains the values from one offset to the next. The last
224 /// value contains values from the last offset to the end of the array.
225 ///
226 /// For example, if you have an array handle with the 9 values
227 /// 0,1,2,3,4,5,6,7,8 an offsets array handle with the 4 values 0,4,6,9 and give
228 /// them to an \c ArrayHandleGroupVecVariable, you get an array that looks like
229 /// it contains three values of Vec-like objects with the data [0,1,2,3],
230 /// [4,5], and [6,7,8].
231 ///
232 /// Note that this version of \c ArrayHandle breaks some of the assumptions
233 /// about \c ArrayHandle a little bit. Typically, there is exactly one type for
234 /// every value in the array, and this value is also the same between the
235 /// control and execution environment. However, this class uses \c
236 /// VecFromPortal it implement a Vec-like class that has a variable number of
237 /// values, and this type can change between control and execution
238 /// environments.
239 ///
240 /// The offsets array is often derived from a list of sizes for each of the
241 /// entries. You can use the convenience function \c
242 /// ConvertNumComponentsToOffsets to take an array of sizes (i.e. the number of
243 /// components for each entry) and get an array of offsets needed for \c
244 /// ArrayHandleGroupVecVariable.
245 ///
246 template <typename ComponentsArrayHandleType, typename OffsetsArrayHandleType>
247 class ArrayHandleGroupVecVariable
248   : public vtkm::cont::ArrayHandle<
249       vtkm::VecFromPortal<typename ComponentsArrayHandleType::WritePortalType>,
250       vtkm::cont::StorageTagGroupVecVariable<typename ComponentsArrayHandleType::StorageTag,
251                                              typename OffsetsArrayHandleType::StorageTag>>
252 {
253   VTKM_IS_ARRAY_HANDLE(ComponentsArrayHandleType);
254   VTKM_IS_ARRAY_HANDLE(OffsetsArrayHandleType);
255 
256   VTKM_STATIC_ASSERT_MSG(
257     (std::is_same<vtkm::Id, typename OffsetsArrayHandleType::ValueType>::value),
258     "ArrayHandleGroupVecVariable's offsets array must contain vtkm::Id values.");
259 
260 public:
261   VTKM_ARRAY_HANDLE_SUBCLASS(
262     ArrayHandleGroupVecVariable,
263     (ArrayHandleGroupVecVariable<ComponentsArrayHandleType, OffsetsArrayHandleType>),
264     (vtkm::cont::ArrayHandle<
265       vtkm::VecFromPortal<typename ComponentsArrayHandleType::WritePortalType>,
266       vtkm::cont::StorageTagGroupVecVariable<typename ComponentsArrayHandleType::StorageTag,
267                                              typename OffsetsArrayHandleType::StorageTag>>));
268 
269   using ComponentType = typename ComponentsArrayHandleType::ValueType;
270 
271 private:
272   using StorageType = vtkm::cont::internal::Storage<ValueType, StorageTag>;
273 
274 public:
275   VTKM_CONT
ArrayHandleGroupVecVariable(const ComponentsArrayHandleType & componentsArray,const OffsetsArrayHandleType & offsetsArray)276   ArrayHandleGroupVecVariable(const ComponentsArrayHandleType& componentsArray,
277                               const OffsetsArrayHandleType& offsetsArray)
278     : Superclass(StorageType::CreateBuffers(componentsArray, offsetsArray))
279   {
280   }
281 
GetComponentsArray()282   VTKM_CONT ComponentsArrayHandleType GetComponentsArray() const
283   {
284     return StorageType::GetComponentsArray(this->GetBuffers());
285   }
286 
GetOffsetsArray()287   VTKM_CONT OffsetsArrayHandleType GetOffsetsArray() const
288   {
289     return StorageType::GetOffsetsArray(this->GetBuffers());
290   }
291 };
292 
293 /// \c make_ArrayHandleGroupVecVariable is convenience function to generate an
294 /// ArrayHandleGroupVecVariable. It takes in an ArrayHandle of values and an
295 /// array handle of offsets and returns an array handle with consecutive
296 /// entries grouped in a Vec.
297 ///
298 template <typename ComponentsArrayHandleType, typename OffsetsArrayHandleType>
299 VTKM_CONT vtkm::cont::ArrayHandleGroupVecVariable<ComponentsArrayHandleType, OffsetsArrayHandleType>
make_ArrayHandleGroupVecVariable(const ComponentsArrayHandleType & componentsArray,const OffsetsArrayHandleType & offsetsArray)300 make_ArrayHandleGroupVecVariable(const ComponentsArrayHandleType& componentsArray,
301                                  const OffsetsArrayHandleType& offsetsArray)
302 {
303   return vtkm::cont::ArrayHandleGroupVecVariable<ComponentsArrayHandleType, OffsetsArrayHandleType>(
304     componentsArray, offsetsArray);
305 }
306 
307 /// \c ConvertNumComponentsToOffsets takes an array of Vec sizes (i.e. the number of components in
308 /// each Vec) and returns an array of offsets to a packed array of such Vecs. The resulting array
309 /// can be used with \c ArrayHandleGroupVecVariable.
310 ///
311 /// \param numComponentsArray the input array that specifies the number of components in each group
312 /// Vec.
313 ///
314 /// \param offsetsArray (optional) the output \c ArrayHandle, which must have a value type of \c
315 /// vtkm::Id. If the output \c ArrayHandle is not given, it is returned.
316 ///
317 /// \param componentsArraySize (optional) a reference to a \c vtkm::Id and is filled with the expected
318 /// size of the component values array.
319 ///
320 /// \param device (optional) specifies the device on which to run the conversion.
321 ///
322 template <typename NumComponentsArrayType, typename OffsetsStorage>
323 VTKM_CONT void ConvertNumComponentsToOffsets(
324   const NumComponentsArrayType& numComponentsArray,
325   vtkm::cont::ArrayHandle<vtkm::Id, OffsetsStorage>& offsetsArray,
326   vtkm::Id& componentsArraySize,
327   vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny())
328 {
329   using namespace vtkm::cont;
330   VTKM_IS_ARRAY_HANDLE(NumComponentsArrayType);
331 
332   VTKM_LOG_SCOPE_FUNCTION(vtkm::cont::LogLevel::Perf);
333 
334   Algorithm::ScanExtended(device, make_ArrayHandleCast<vtkm::Id>(numComponentsArray), offsetsArray);
335 
336   componentsArraySize = ArrayGetValue(offsetsArray.GetNumberOfValues() - 1, offsetsArray);
337 }
338 
339 template <typename NumComponentsArrayType, typename OffsetsStorage>
340 VTKM_CONT void ConvertNumComponentsToOffsets(
341   const NumComponentsArrayType& numComponentsArray,
342   vtkm::cont::ArrayHandle<vtkm::Id, OffsetsStorage>& offsetsArray,
343   vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny())
344 {
345   VTKM_IS_ARRAY_HANDLE(NumComponentsArrayType);
346 
347   VTKM_LOG_SCOPE_FUNCTION(vtkm::cont::LogLevel::Perf);
348 
349   vtkm::cont::Algorithm::ScanExtended(
350     device, vtkm::cont::make_ArrayHandleCast<vtkm::Id>(numComponentsArray), offsetsArray);
351 }
352 
353 template <typename NumComponentsArrayType>
354 VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Id> ConvertNumComponentsToOffsets(
355   const NumComponentsArrayType& numComponentsArray,
356   vtkm::Id& componentsArraySize,
357   vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny())
358 {
359   VTKM_IS_ARRAY_HANDLE(NumComponentsArrayType);
360 
361   vtkm::cont::ArrayHandle<vtkm::Id> offsetsArray;
362   vtkm::cont::ConvertNumComponentsToOffsets(
363     numComponentsArray, offsetsArray, componentsArraySize, device);
364   return offsetsArray;
365 }
366 
367 template <typename NumComponentsArrayType>
368 VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Id> ConvertNumComponentsToOffsets(
369   const NumComponentsArrayType& numComponentsArray,
370   vtkm::cont::DeviceAdapterId device = vtkm::cont::DeviceAdapterTagAny())
371 {
372   VTKM_IS_ARRAY_HANDLE(NumComponentsArrayType);
373 
374   vtkm::Id dummy;
375   return vtkm::cont::ConvertNumComponentsToOffsets(numComponentsArray, dummy, device);
376 }
377 }
378 } // namespace vtkm::cont
379 
380 //=============================================================================
381 // Specializations of worklet arguments using ArrayHandleGropuVecVariable
382 #include <vtkm/exec/arg/FetchTagArrayDirectOutArrayHandleGroupVecVariable.h>
383 
384 //=============================================================================
385 // Specializations of serialization related classes
386 /// @cond SERIALIZATION
387 namespace vtkm
388 {
389 namespace cont
390 {
391 
392 template <typename SAH, typename OAH>
393 struct SerializableTypeString<vtkm::cont::ArrayHandleGroupVecVariable<SAH, OAH>>
394 {
395   static VTKM_CONT const std::string& Get()
396   {
397     static std::string name = "AH_GroupVecVariable<" + SerializableTypeString<SAH>::Get() + "," +
398       SerializableTypeString<OAH>::Get() + ">";
399     return name;
400   }
401 };
402 
403 template <typename SP, typename SST, typename OST>
404 struct SerializableTypeString<
405   vtkm::cont::ArrayHandle<vtkm::VecFromPortal<SP>,
406                           vtkm::cont::StorageTagGroupVecVariable<SST, OST>>>
407   : SerializableTypeString<
408       vtkm::cont::ArrayHandleGroupVecVariable<vtkm::cont::ArrayHandle<typename SP::ValueType, SST>,
409                                               vtkm::cont::ArrayHandle<vtkm::Id, OST>>>
410 {
411 };
412 }
413 } // vtkm::cont
414 
415 namespace mangled_diy_namespace
416 {
417 
418 template <typename SAH, typename OAH>
419 struct Serialization<vtkm::cont::ArrayHandleGroupVecVariable<SAH, OAH>>
420 {
421 private:
422   using Type = vtkm::cont::ArrayHandleGroupVecVariable<SAH, OAH>;
423   using BaseType = vtkm::cont::ArrayHandle<typename Type::ValueType, typename Type::StorageTag>;
424 
425 public:
426   static VTKM_CONT void save(BinaryBuffer& bb, const BaseType& obj)
427   {
428     vtkmdiy::save(bb, Type(obj).GetComponentsArray());
429     vtkmdiy::save(bb, Type(obj).GetOffsetsArray());
430   }
431 
432   static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj)
433   {
434     SAH src;
435     OAH off;
436 
437     vtkmdiy::load(bb, src);
438     vtkmdiy::load(bb, off);
439 
440     obj = vtkm::cont::make_ArrayHandleGroupVecVariable(src, off);
441   }
442 };
443 
444 template <typename SP, typename SST, typename OST>
445 struct Serialization<vtkm::cont::ArrayHandle<vtkm::VecFromPortal<SP>,
446                                              vtkm::cont::StorageTagGroupVecVariable<SST, OST>>>
447   : Serialization<
448       vtkm::cont::ArrayHandleGroupVecVariable<vtkm::cont::ArrayHandle<typename SP::ValueType, SST>,
449                                               vtkm::cont::ArrayHandle<vtkm::Id, OST>>>
450 {
451 };
452 } // diy
453 /// @endcond SERIALIZATION
454 
455 #endif //vtk_m_cont_ArrayHandleGroupVecVariable_h
456