| 
									
										
										
										
											2022-09-13 08:44:26 +02:00
										 |  |  | /* SPDX-License-Identifier: GPL-2.0-or-later */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** \file
 | 
					
						
							|  |  |  |  * \ingroup fn | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file contains common utilities for actually executing a lazy-function. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "BLI_parameter_pack_utils.hh"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "FN_lazy_function.hh"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace blender::fn::lazy_function { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Most basic implementation of #Params. It does not actually implement any logic for how to | 
					
						
							|  |  |  |  * retrieve inputs or set outputs. Instead, code using #BasicParams has to implement that. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class BasicParams : public Params { | 
					
						
							|  |  |  |  private: | 
					
						
							|  |  |  |   const Span<GMutablePointer> inputs_; | 
					
						
							|  |  |  |   const Span<GMutablePointer> outputs_; | 
					
						
							|  |  |  |   MutableSpan<std::optional<ValueUsage>> input_usages_; | 
					
						
							|  |  |  |   Span<ValueUsage> output_usages_; | 
					
						
							|  |  |  |   MutableSpan<bool> set_outputs_; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  public: | 
					
						
							|  |  |  |   BasicParams(const LazyFunction &fn, | 
					
						
							|  |  |  |               const Span<GMutablePointer> inputs, | 
					
						
							|  |  |  |               const Span<GMutablePointer> outputs, | 
					
						
							|  |  |  |               MutableSpan<std::optional<ValueUsage>> input_usages, | 
					
						
							|  |  |  |               Span<ValueUsage> output_usages, | 
					
						
							|  |  |  |               MutableSpan<bool> set_outputs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void *try_get_input_data_ptr_impl(const int index) const override; | 
					
						
							|  |  |  |   void *try_get_input_data_ptr_or_request_impl(const int index) override; | 
					
						
							|  |  |  |   void *get_output_data_ptr_impl(const int index) override; | 
					
						
							|  |  |  |   void output_set_impl(const int index) override; | 
					
						
							|  |  |  |   bool output_was_set_impl(const int index) const override; | 
					
						
							|  |  |  |   ValueUsage get_output_usage_impl(const int index) const override; | 
					
						
							|  |  |  |   void set_input_unused_impl(const int index) override; | 
					
						
							| 
									
										
										
										
											2022-09-20 10:59:12 +02:00
										 |  |  |   bool try_enable_multi_threading_impl() override; | 
					
						
							| 
									
										
										
										
											2022-09-13 08:44:26 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace detail { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Utility to implement #execute_lazy_function_eagerly. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | template<typename... Inputs, typename... Outputs, size_t... InIndices, size_t... OutIndices> | 
					
						
							|  |  |  | inline void execute_lazy_function_eagerly_impl( | 
					
						
							|  |  |  |     const LazyFunction &fn, | 
					
						
							|  |  |  |     UserData *user_data, | 
					
						
							|  |  |  |     std::tuple<Inputs...> &inputs, | 
					
						
							|  |  |  |     std::tuple<Outputs *...> &outputs, | 
					
						
							|  |  |  |     std::index_sequence<InIndices...> /* in_indices */, | 
					
						
							|  |  |  |     std::index_sequence<OutIndices...> /* out_indices */) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   constexpr size_t InputsNum = sizeof...(Inputs); | 
					
						
							|  |  |  |   constexpr size_t OutputsNum = sizeof...(Outputs); | 
					
						
							|  |  |  |   std::array<GMutablePointer, InputsNum> input_pointers; | 
					
						
							|  |  |  |   std::array<GMutablePointer, OutputsNum> output_pointers; | 
					
						
							|  |  |  |   std::array<std::optional<ValueUsage>, InputsNum> input_usages; | 
					
						
							|  |  |  |   std::array<ValueUsage, OutputsNum> output_usages; | 
					
						
							|  |  |  |   std::array<bool, OutputsNum> set_outputs; | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |       [&]() { | 
					
						
							|  |  |  |         constexpr size_t I = InIndices; | 
					
						
							| 
									
										
										
										
											2022-09-20 10:42:25 +02:00
										 |  |  |         /* Use `typedef` instead of `using` to work around a compiler bug. */ | 
					
						
							| 
									
										
										
										
											2022-09-15 09:33:57 +02:00
										 |  |  |         typedef Inputs T; | 
					
						
							| 
									
										
										
										
											2022-09-13 08:44:26 +02:00
										 |  |  |         const CPPType &type = CPPType::get<T>(); | 
					
						
							|  |  |  |         input_pointers[I] = {type, &std::get<I>(inputs)}; | 
					
						
							|  |  |  |       }(), | 
					
						
							|  |  |  |       ...); | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |       [&]() { | 
					
						
							|  |  |  |         constexpr size_t I = OutIndices; | 
					
						
							| 
									
										
										
										
											2022-09-20 10:42:25 +02:00
										 |  |  |         /* Use `typedef` instead of `using` to work around a compiler bug. */ | 
					
						
							| 
									
										
										
										
											2022-09-15 09:33:57 +02:00
										 |  |  |         typedef Outputs T; | 
					
						
							| 
									
										
										
										
											2022-09-13 08:44:26 +02:00
										 |  |  |         const CPPType &type = CPPType::get<T>(); | 
					
						
							|  |  |  |         output_pointers[I] = {type, std::get<I>(outputs)}; | 
					
						
							|  |  |  |       }(), | 
					
						
							|  |  |  |       ...); | 
					
						
							|  |  |  |   output_usages.fill(ValueUsage::Used); | 
					
						
							|  |  |  |   set_outputs.fill(false); | 
					
						
							|  |  |  |   LinearAllocator<> allocator; | 
					
						
							|  |  |  |   Context context; | 
					
						
							|  |  |  |   context.user_data = user_data; | 
					
						
							|  |  |  |   context.storage = fn.init_storage(allocator); | 
					
						
							|  |  |  |   BasicParams params{ | 
					
						
							|  |  |  |       fn, input_pointers, output_pointers, input_usages, output_usages, set_outputs}; | 
					
						
							|  |  |  |   fn.execute(params, context); | 
					
						
							|  |  |  |   fn.destruct_storage(context.storage); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace detail
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * In some cases (mainly for tests), the set of inputs and outputs for a lazy-function is known at | 
					
						
							|  |  |  |  * compile time and one just wants to compute the outputs based on the inputs, without any | 
					
						
							| 
									
										
										
										
											2022-09-13 18:00:44 +10:00
										 |  |  |  * laziness. | 
					
						
							| 
									
										
										
										
											2022-09-13 08:44:26 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This function does exactly that. It takes all inputs in a tuple and writes the outputs to points | 
					
						
							|  |  |  |  * provided in a second tuple. Since all inputs have to be provided, the lazy-function has to | 
					
						
							|  |  |  |  * compute all outputs. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | template<typename... Inputs, typename... Outputs> | 
					
						
							|  |  |  | inline void execute_lazy_function_eagerly(const LazyFunction &fn, | 
					
						
							|  |  |  |                                           UserData *user_data, | 
					
						
							|  |  |  |                                           std::tuple<Inputs...> inputs, | 
					
						
							|  |  |  |                                           std::tuple<Outputs *...> outputs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   BLI_assert(fn.inputs().size() == sizeof...(Inputs)); | 
					
						
							|  |  |  |   BLI_assert(fn.outputs().size() == sizeof...(Outputs)); | 
					
						
							|  |  |  |   detail::execute_lazy_function_eagerly_impl(fn, | 
					
						
							|  |  |  |                                              user_data, | 
					
						
							|  |  |  |                                              inputs, | 
					
						
							|  |  |  |                                              outputs, | 
					
						
							|  |  |  |                                              std::make_index_sequence<sizeof...(Inputs)>(), | 
					
						
							|  |  |  |                                              std::make_index_sequence<sizeof...(Outputs)>()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace blender::fn::lazy_function
 |