//============================================================================ // 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. //============================================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { VTKM_CONT inline vtkm::cont::ArrayHandleConcatenate< vtkm::cont::ArrayHandleConstant, vtkm::cont::ArrayHandleView>> ShiftArrayHandleByOne(const vtkm::cont::ArrayHandle& array) { return vtkm::cont::make_ArrayHandleConcatenate( vtkm::cont::make_ArrayHandleConstant(0, 1), vtkm::cont::make_ArrayHandleView(array, 0, array.GetNumberOfValues() - 1)); } struct ReverseInputToOutputMapWorklet : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn outputStartIndices, FieldIn outputEndIndices, WholeArrayOut outputToInputMap, WholeArrayOut visit); using ExecutionSignature = void(_1, _2, _3, _4, InputIndex); using InputDomain = _2; template VTKM_EXEC void operator()(vtkm::Id outputStartIndex, vtkm::Id outputEndIndex, const OutputMapType& outputToInputMap, const VisitType& visit, vtkm::Id inputIndex) const { vtkm::IdComponent visitIndex = 0; for (vtkm::Id outputIndex = outputStartIndex; outputIndex < outputEndIndex; outputIndex++) { outputToInputMap.Set(outputIndex, inputIndex); visit.Set(outputIndex, visitIndex); visitIndex++; } } VTKM_CONT static void Run(const vtkm::cont::ArrayHandle& inputToOutputMap, const vtkm::cont::ArrayHandle& outputToInputMap, const vtkm::cont::ArrayHandle& visit, vtkm::cont::DeviceAdapterId device) { vtkm::worklet::DispatcherMapField dispatcher; dispatcher.SetDevice(device); dispatcher.Invoke( ShiftArrayHandleByOne(inputToOutputMap), inputToOutputMap, outputToInputMap, visit); } }; struct SubtractToVisitIndexWorklet : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn startsOfGroup, WholeArrayOut visit); using ExecutionSignature = void(InputIndex, _1, _2); using InputDomain = _1; template VTKM_EXEC void operator()(vtkm::Id inputIndex, vtkm::Id startOfGroup, const VisitType& visit) const { vtkm::IdComponent visitIndex = static_cast(inputIndex - startOfGroup); visit.Set(inputIndex, visitIndex); } }; } // anonymous namespace namespace vtkm { namespace worklet { namespace detail { struct ScatterCountingBuilder { template VTKM_CONT static void BuildArrays(vtkm::worklet::ScatterCounting* self, const CountArrayType& countArray, vtkm::cont::DeviceAdapterId device, bool saveInputToOutputMap) { VTKM_IS_ARRAY_HANDLE(CountArrayType); self->InputRange = countArray.GetNumberOfValues(); // The input to output map is actually built off by one. The first entry // is actually for the second value. The last entry is the total number of // output. This off-by-one is so that an upper bound find will work when // building the output to input map. Later we will either correct the // map or delete it. vtkm::cont::ArrayHandle inputToOutputMapOffByOne; vtkm::Id outputSize = vtkm::cont::Algorithm::ScanInclusive( device, vtkm::cont::make_ArrayHandleCast(countArray, vtkm::Id()), inputToOutputMapOffByOne); // We have implemented two different ways to compute the output to input // map. The first way is to use a binary search on each output index into // the input map. The second way is to schedule on each input and // iteratively fill all the output indices for that input. The first way is // faster for output sizes that are small relative to the input (typical in // Marching Cubes, for example) and also tends to be well load balanced. // The second way is faster for larger outputs (typical in triangulation, // for example). We will use the first method for small output sizes and // the second for large output sizes. Toying with this might be a good // place for optimization. if (outputSize < self->InputRange) { BuildOutputToInputMapWithFind(self, outputSize, device, inputToOutputMapOffByOne); } else { BuildOutputToInputMapWithIterate(self, outputSize, device, inputToOutputMapOffByOne); } if (saveInputToOutputMap) { // Since we are saving it, correct the input to output map. vtkm::cont::Algorithm::Copy( device, ShiftArrayHandleByOne(inputToOutputMapOffByOne), self->InputToOutputMap); } } VTKM_CONT static void BuildOutputToInputMapWithFind( vtkm::worklet::ScatterCounting* self, vtkm::Id outputSize, vtkm::cont::DeviceAdapterId device, vtkm::cont::ArrayHandle inputToOutputMapOffByOne) { vtkm::cont::ArrayHandleIndex outputIndices(outputSize); vtkm::cont::Algorithm::UpperBounds( device, inputToOutputMapOffByOne, outputIndices, self->OutputToInputMap); vtkm::cont::ArrayHandle startsOfGroups; // This find gives the index of the start of a group. vtkm::cont::Algorithm::LowerBounds( device, self->OutputToInputMap, self->OutputToInputMap, startsOfGroups); self->VisitArray.Allocate(outputSize); vtkm::worklet::DispatcherMapField dispatcher; dispatcher.SetDevice(device); dispatcher.Invoke(startsOfGroups, self->VisitArray); } VTKM_CONT static void BuildOutputToInputMapWithIterate( vtkm::worklet::ScatterCounting* self, vtkm::Id outputSize, vtkm::cont::DeviceAdapterId device, vtkm::cont::ArrayHandle inputToOutputMapOffByOne) { self->OutputToInputMap.Allocate(outputSize); self->VisitArray.Allocate(outputSize); ReverseInputToOutputMapWorklet::Run( inputToOutputMapOffByOne, self->OutputToInputMap, self->VisitArray, device); } template void operator()(const ArrayType& countArray, vtkm::cont::DeviceAdapterId device, bool saveInputToOutputMap, vtkm::worklet::ScatterCounting* self) { BuildArrays(self, countArray, device, saveInputToOutputMap); } }; } } } // namespace vtkm::worklet::detail void vtkm::worklet::ScatterCounting::BuildArrays(const VariantArrayHandleCount& countArray, vtkm::cont::DeviceAdapterId device, bool saveInputToOutputMap) { countArray.CastAndCall( vtkm::worklet::detail::ScatterCountingBuilder(), device, saveInputToOutputMap, this); }