tornavis/source/blender/functions/FN_lazy_function_execute.hh

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

123 lines
4.4 KiB
C++
Raw Normal View History

Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
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;
};
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;
typedef Inputs T;
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
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;
typedef Outputs T;
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
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 10:00:44 +02:00
* laziness.
Geometry Nodes: new evaluation system This refactors the geometry nodes evaluation system. No changes for the user are expected. At a high level the goals are: * Support using geometry nodes outside of the geometry nodes modifier. * Support using the evaluator infrastructure for other purposes like field evaluation. * Support more nodes, especially when many of them are disabled behind switch nodes. * Support doing preprocessing on node groups. For more details see T98492. There are fairly detailed comments in the code, but here is a high level overview for how it works now: * There is a new "lazy-function" system. It is similar in spirit to the multi-function system but with different goals. Instead of optimizing throughput for highly parallelizable work, this system is designed to compute only the data that is actually necessary. What data is necessary can be determined dynamically during evaluation. Many lazy-functions can be composed in a graph to form a new lazy-function, which can again be used in a graph etc. * Each geometry node group is converted into a lazy-function graph prior to evaluation. To evaluate geometry nodes, one then just has to evaluate that graph. Node groups are no longer inlined into their parents. Next steps for the evaluation system is to reduce the use of threads in some situations to avoid overhead. Many small node groups don't benefit from multi-threading at all. This is much easier to do now because not everything has to be inlined in one huge node tree anymore. Differential Revision: https://developer.blender.org/D15914
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