//============================================================================ // 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 { namespace filter { namespace internal { template struct SupportsPreExecute { template ().PreExecute( std::declval(), std::declval>()))> static std::true_type has(int); template static std::false_type has(...); using type = decltype(has(0)); }; template struct SupportsPostExecute { template ().PostExecute( std::declval(), std::declval(), std::declval>()))> static std::true_type has(int); template static std::false_type has(...); using type = decltype(has(0)); }; template struct SupportsPrepareForExecution { template ().PrepareForExecution( std::declval(), std::declval>()))> static std::true_type has(int); template static std::false_type has(...); using type = decltype(has(0)); }; template struct SupportsMapFieldOntoOutput { template ().MapFieldOntoOutput( std::declval(), std::declval(), std::declval>()))> static std::true_type has(int); template static std::false_type has(...); using type = decltype(has(0)); }; //-------------------------------------------------------------------------------- template void CallPreExecuteInternal(std::true_type, Derived* self, Args&&... args) { return self->PreExecute(std::forward(args)...); } //-------------------------------------------------------------------------------- template void CallPreExecuteInternal(std::false_type, Derived*, Args&&...) { } //-------------------------------------------------------------------------------- template void CallPreExecute(Derived* self, const InputType& input, const vtkm::filter::PolicyBase& policy) { using call_supported_t = typename SupportsPreExecute::type; CallPreExecuteInternal(call_supported_t(), self, input, policy); } //-------------------------------------------------------------------------------- template void CallMapFieldOntoOutputInternal(std::true_type, Derived* self, const vtkm::cont::DataSet& input, vtkm::cont::DataSet& output, const vtkm::filter::PolicyBase& policy) { for (vtkm::IdComponent cc = 0; cc < input.GetNumberOfFields(); ++cc) { auto field = input.GetField(cc); if (self->GetFieldsToPass().IsFieldSelected(field)) { self->MapFieldOntoOutput(output, field, policy); } } } //-------------------------------------------------------------------------------- template void CallMapFieldOntoOutputInternal(std::false_type, Derived* self, const vtkm::cont::DataSet& input, vtkm::cont::DataSet& output, const vtkm::filter::PolicyBase&) { // no MapFieldOntoOutput method is present. In that case, we simply copy the // requested input fields to the output. for (vtkm::IdComponent cc = 0; cc < input.GetNumberOfFields(); ++cc) { auto field = input.GetField(cc); if (self->GetFieldsToPass().IsFieldSelected(field)) { output.AddField(field); } } } //-------------------------------------------------------------------------------- template void CallMapFieldOntoOutput(Derived* self, const vtkm::cont::DataSet& input, vtkm::cont::DataSet& output, vtkm::filter::PolicyBase policy) { using call_supported_t = typename SupportsMapFieldOntoOutput::type; CallMapFieldOntoOutputInternal(call_supported_t(), self, input, output, policy); } //-------------------------------------------------------------------------------- // forward declare. template InputType CallPrepareForExecution(Derived* self, const InputType& input, const vtkm::filter::PolicyBase& policy); //-------------------------------------------------------------------------------- template InputType CallPrepareForExecutionInternal(std::true_type, Derived* self, const InputType& input, const vtkm::filter::PolicyBase& policy) { return self->PrepareForExecution(input, policy); } template void RunFilter(Derived* self, const vtkm::filter::PolicyBase& policy, vtkm::filter::DataSetQueue& input, vtkm::filter::DataSetQueue& output) { auto filterClone = static_cast(self->Clone()); std::pair task; while (input.GetTask(task)) { auto outDS = CallPrepareForExecution(filterClone, task.second, policy); CallMapFieldOntoOutput(filterClone, task.second, outDS, policy); output.Push(std::make_pair(task.first, std::move(outDS))); } vtkm::cont::Algorithm::Synchronize(); delete filterClone; } //-------------------------------------------------------------------------------- // specialization for PartitionedDataSet input when `PrepareForExecution` is not provided // by the subclass. we iterate over blocks and execute for each block // individually. template vtkm::cont::PartitionedDataSet CallPrepareForExecutionInternal( std::false_type, Derived* self, const vtkm::cont::PartitionedDataSet& input, const vtkm::filter::PolicyBase& policy) { vtkm::cont::PartitionedDataSet output; if (self->GetRunMultiThreadedFilter()) { vtkm::filter::DataSetQueue inputQueue(input); vtkm::filter::DataSetQueue outputQueue; vtkm::Id numThreads = self->DetermineNumberOfThreads(input); //Run 'numThreads' filters. std::vector> futures(static_cast(numThreads)); for (std::size_t i = 0; i < static_cast(numThreads); i++) { auto f = std::async(std::launch::async, RunFilter, self, policy, std::ref(inputQueue), std::ref(outputQueue)); futures[i] = std::move(f); } for (auto& f : futures) f.get(); //Get results from the outputQueue. output = outputQueue.Get(); } else { for (const auto& inBlock : input) { vtkm::cont::DataSet outBlock = CallPrepareForExecution(self, inBlock, policy); CallMapFieldOntoOutput(self, inBlock, outBlock, policy); output.AppendPartition(outBlock); } } return output; } //-------------------------------------------------------------------------------- template InputType CallPrepareForExecution(Derived* self, const InputType& input, const vtkm::filter::PolicyBase& policy) { using call_supported_t = typename SupportsPrepareForExecution::type; return CallPrepareForExecutionInternal(call_supported_t(), self, input, policy); } //-------------------------------------------------------------------------------- template void CallPostExecuteInternal(std::true_type, Derived* self, const InputType& input, InputType& output, const vtkm::filter::PolicyBase& policy) { self->PostExecute(input, output, policy); } //-------------------------------------------------------------------------------- template void CallPostExecuteInternal(std::false_type, Derived*, Args&&...) { } //-------------------------------------------------------------------------------- template void CallPostExecute(Derived* self, const InputType& input, InputType& output, const vtkm::filter::PolicyBase& policy) { using call_supported_t = typename SupportsPostExecute::type; CallPostExecuteInternal(call_supported_t(), self, input, output, policy); } } //---------------------------------------------------------------------------- template inline VTKM_CONT Filter::Filter() : Invoke() , FieldsToPass(vtkm::filter::FieldSelection::MODE_ALL) { } //---------------------------------------------------------------------------- template inline VTKM_CONT Filter::~Filter() { } //---------------------------------------------------------------------------- template inline VTKM_CONT vtkm::cont::DataSet Filter::Execute(const vtkm::cont::DataSet& input) { Derived* self = static_cast(this); vtkm::cont::PartitionedDataSet output = self->Execute(vtkm::cont::PartitionedDataSet(input)); if (output.GetNumberOfPartitions() > 1) { throw vtkm::cont::ErrorFilterExecution("Expecting at most 1 block."); } return output.GetNumberOfPartitions() == 1 ? output.GetPartition(0) : vtkm::cont::DataSet(); } template inline VTKM_CONT vtkm::cont::PartitionedDataSet Filter::Execute( const vtkm::cont::PartitionedDataSet& input) { VTKM_LOG_SCOPE(vtkm::cont::LogLevel::Perf, "Filter (%d partitions): '%s'", (int)input.GetNumberOfPartitions(), vtkm::cont::TypeToString().c_str()); Derived* self = static_cast(this); vtkm::filter::PolicyDefault policy; // Call `void Derived::PreExecute(input, policy)`, if defined. internal::CallPreExecute(self, input, policy); // Call `PrepareForExecution` (which should probably be renamed at some point) vtkm::cont::PartitionedDataSet output = internal::CallPrepareForExecution(self, input, policy); // Call `Derived::PostExecute(input, output, policy)` if defined. internal::CallPostExecute(self, input, output, policy); return output; } template inline VTKM_CONT vtkm::Id Filter::DetermineNumberOfThreads( const vtkm::cont::PartitionedDataSet& input) { vtkm::Id numDS = input.GetNumberOfPartitions(); //Aribitrary constants. const vtkm::Id threadsPerGPU = 8; const vtkm::Id threadsPerCPU = 4; vtkm::Id availThreads = 1; auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); if (tracker.CanRunOn(vtkm::cont::DeviceAdapterTagCuda{})) availThreads = threadsPerGPU; else if (tracker.CanRunOn(vtkm::cont::DeviceAdapterTagKokkos{})) { //Kokkos doesn't support threading on the CPU. #ifdef VTKM_KOKKOS_CUDA availThreads = threadsPerGPU; #else availThreads = 1; #endif } else if (tracker.CanRunOn(vtkm::cont::DeviceAdapterTagSerial{})) availThreads = 1; else availThreads = threadsPerCPU; vtkm::Id numThreads = std::min(numDS, availThreads); return numThreads; } template inline VTKM_CONT vtkm::cont::PartitionedDataSet Filter::ExecuteThreaded( const vtkm::cont::PartitionedDataSet& input, vtkm::Id vtkmNotUsed(numThreads)) { VTKM_LOG_SCOPE(vtkm::cont::LogLevel::Perf, "Filter (%d partitions): '%s'", (int)input.GetNumberOfPartitions(), vtkm::cont::TypeToString().c_str()); Derived* self = static_cast(this); vtkm::filter::PolicyDefault policy; // Call `void Derived::PreExecute(input, policy)`, if defined. internal::CallPreExecute(self, input, policy); // Call `PrepareForExecution` (which should probably be renamed at some point) vtkm::cont::PartitionedDataSet output = internal::CallPrepareForExecution(self, input, policy); // Call `Derived::PostExecute(input, output, policy)` if defined. internal::CallPostExecute(self, input, output, policy); return output; } //---------------------------------------------------------------------------- template template VTKM_CONT vtkm::cont::DataSet Filter::Execute( const vtkm::cont::DataSet& input, vtkm::filter::PolicyBase policy) { Derived* self = static_cast(this); VTKM_DEPRECATED_SUPPRESS_BEGIN vtkm::cont::PartitionedDataSet output = self->Execute(vtkm::cont::PartitionedDataSet(input), policy); VTKM_DEPRECATED_SUPPRESS_END if (output.GetNumberOfPartitions() > 1) { throw vtkm::cont::ErrorFilterExecution("Expecting at most 1 block."); } return output.GetNumberOfPartitions() == 1 ? output.GetPartition(0) : vtkm::cont::DataSet(); } //---------------------------------------------------------------------------- template template VTKM_CONT vtkm::cont::PartitionedDataSet Filter::Execute( const vtkm::cont::PartitionedDataSet& input, vtkm::filter::PolicyBase policy) { VTKM_LOG_SCOPE(vtkm::cont::LogLevel::Perf, "Filter (%d partitions): '%s'", (int)input.GetNumberOfPartitions(), vtkm::cont::TypeToString().c_str()); Derived* self = static_cast(this); // Call `void Derived::PreExecute(input, policy)`, if defined. internal::CallPreExecute(self, input, policy); // Call `PrepareForExecution` (which should probably be renamed at some point) vtkm::cont::PartitionedDataSet output = internal::CallPrepareForExecution(self, input, policy); // Call `Derived::PostExecute(input, output, policy)` if defined. internal::CallPostExecute(self, input, output, policy); return output; } //---------------------------------------------------------------------------- template template inline VTKM_CONT void Filter::MapFieldsToPass( const vtkm::cont::DataSet& input, vtkm::cont::DataSet& output, vtkm::filter::PolicyBase policy) { Derived* self = static_cast(this); for (vtkm::IdComponent cc = 0; cc < input.GetNumberOfFields(); ++cc) { auto field = input.GetField(cc); if (this->GetFieldsToPass().IsFieldSelected(field)) { internal::CallMapFieldOntoOutput(self, output, field, policy); } } } } }