main sync #3

Merged
Patrick Busch merged 318 commits from blender/blender:main into main 2023-03-17 15:52:21 +01:00
5 changed files with 143 additions and 173 deletions
Showing only changes of commit 258678916f - Show all commits

View File

@ -345,11 +345,6 @@ typedef struct bNodeType {
/* Execute a geometry node. */ /* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute; NodeGeometryExecFunction geometry_node_execute;
/**
* If true, the geometry nodes evaluator can call the execute function multiple times to improve
* performance by specifying required data in one call and using it for calculations in another.
*/
bool geometry_node_execute_supports_laziness;
/* Declares which sockets the node has. */ /* Declares which sockets the node has. */
NodeDeclareFunction declare; NodeDeclareFunction declare;

View File

@ -194,8 +194,6 @@ class GeoNodeExecParams {
/** /**
* Returns true when the output has to be computed. * Returns true when the output has to be computed.
* Nodes that support laziness could use the #lazy_output_is_required variant to possibly avoid
* some computations.
*/ */
bool output_is_required(StringRef identifier) const bool output_is_required(StringRef identifier) const
{ {
@ -203,29 +201,6 @@ class GeoNodeExecParams {
return params_.get_output_usage(index) != lf::ValueUsage::Unused; return params_.get_output_usage(index) != lf::ValueUsage::Unused;
} }
/**
* Tell the evaluator that a specific input is required.
* This returns true when the input will only be available in the next execution.
* False is returned if the input is available already.
* This can only be used when the node supports laziness.
*/
bool lazy_require_input(StringRef identifier)
{
const int index = this->get_input_index(identifier);
return params_.try_get_input_data_ptr_or_request(index) == nullptr;
}
/**
* Asks the evaluator if a specific output is required right now. If this returns false, the
* value might still need to be computed later.
* This can only be used when the node supports laziness.
*/
bool lazy_output_is_required(StringRef identifier)
{
const int index = this->get_output_index(identifier);
return params_.get_output_usage(index) == lf::ValueUsage::Used;
}
/** /**
* Get the node that is currently being executed. * Get the node that is currently being executed.
*/ */

View File

@ -221,6 +221,8 @@ class GeometryNodesLazyFunctionLogger : public fn::lazy_function::GraphExecutor:
const lf::Context &context) const override; const lf::Context &context) const override;
}; };
std::unique_ptr<LazyFunction> get_switch_node_lazy_function(const bNode &node);
/** /**
* Tells the lazy-function graph evaluator which nodes have side effects based on the current * Tells the lazy-function graph evaluator which nodes have side effects based on the current
* context. For example, the same viewer node can have side effects in one context, but not in * context. For example, the same viewer node can have side effects in one context, but not in

View File

@ -5,14 +5,9 @@
#include "UI_interface.h" #include "UI_interface.h"
#include "UI_resources.h" #include "UI_resources.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_material.h"
#include "NOD_socket_search_link.hh" #include "NOD_socket_search_link.hh"
#include "FN_multi_function_signature.hh" #include "FN_field_cpp_type.hh"
namespace blender::nodes::node_geo_switch_cc { namespace blender::nodes::node_geo_switch_cc {
@ -149,149 +144,128 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
} }
} }
template<typename T> void switch_fields(GeoNodeExecParams &params, const StringRef suffix) class LazyFunctionForSwitchNode : public LazyFunction {
public:
LazyFunctionForSwitchNode(const bNode &node)
{ {
if (params.lazy_require_input("Switch")) { const NodeSwitch &storage = node_storage(node);
const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.input_type);
const bNodeSocketType *socket_type = nullptr;
for (const bNodeSocket *socket : node.output_sockets()) {
if (socket->type == data_type) {
socket_type = socket->typeinfo;
break;
}
}
BLI_assert(socket_type != nullptr);
const CPPType &cpp_type = *socket_type->geometry_nodes_cpp_type;
inputs_.append_as("Condition", CPPType::get<ValueOrField<bool>>());
inputs_.append_as("False", cpp_type, lf::ValueUsage::Maybe);
inputs_.append_as("True", cpp_type, lf::ValueUsage::Maybe);
outputs_.append_as("Value", cpp_type);
}
void execute_impl(lf::Params &params, const lf::Context & /*context*/) const override
{
const ValueOrField<bool> condition = params.get_input<ValueOrField<bool>>(0);
if (condition.is_field()) {
Field<bool> condition_field = condition.as_field();
if (condition_field.node().depends_on_input()) {
this->execute_field(condition.as_field(), params);
return;
}
const bool condition_bool = fn::evaluate_constant_field(condition_field);
this->execute_single(condition_bool, params);
return;
}
this->execute_single(condition.as_value(), params);
}
static constexpr int false_input_index = 1;
static constexpr int true_input_index = 2;
void execute_single(const bool condition, lf::Params &params) const
{
const int input_to_forward = condition ? true_input_index : false_input_index;
const int input_to_ignore = condition ? false_input_index : true_input_index;
params.set_input_unused(input_to_ignore);
void *value_to_forward = params.try_get_input_data_ptr_or_request(input_to_forward);
if (value_to_forward == nullptr) {
/* Try again when the value is available. */
return; return;
} }
const std::string name_false = "False" + suffix; const CPPType &type = *outputs_[0].type;
const std::string name_true = "True" + suffix; void *output_ptr = params.get_output_data_ptr(0);
const std::string name_output = "Output" + suffix; type.move_construct(value_to_forward, output_ptr);
params.output_set(0);
}
Field<bool> switches_field = params.get_input<Field<bool>>("Switch"); void execute_field(Field<bool> condition, lf::Params &params) const
if (switches_field.node().depends_on_input()) { {
/* The switch has to be incorporated into the field. Both inputs have to be evaluated. */ /* When the condition is a non-constant field, we need both inputs. */
const bool require_false = params.lazy_require_input(name_false); void *false_value_or_field = params.try_get_input_data_ptr_or_request(false_input_index);
const bool require_true = params.lazy_require_input(name_true); void *true_value_or_field = params.try_get_input_data_ptr_or_request(true_input_index);
if (require_false | require_true) { if (ELEM(nullptr, false_value_or_field, true_value_or_field)) {
/* Try again when inputs are available. */
return; return;
} }
Field<T> falses_field = params.extract_input<Field<T>>(name_false); const CPPType &type = *outputs_[0].type;
Field<T> trues_field = params.extract_input<Field<T>>(name_true); const fn::ValueOrFieldCPPType &value_or_field_type = *fn::ValueOrFieldCPPType::get_from_self(
type);
const CPPType &value_type = value_or_field_type.value;
const MultiFunction &switch_multi_function = this->get_switch_multi_function(value_type);
GField false_field = value_or_field_type.as_field(false_value_or_field);
GField true_field = value_or_field_type.as_field(true_value_or_field);
GField output_field{FieldOperation::Create(
switch_multi_function,
{std::move(condition), std::move(false_field), std::move(true_field)})};
void *output_ptr = params.get_output_data_ptr(0);
value_or_field_type.construct_from_field(output_ptr, std::move(output_field));
params.output_set(0);
}
const MultiFunction &get_switch_multi_function(const CPPType &type) const
{
const MultiFunction *switch_multi_function = nullptr;
type.to_static_type_tag<float, int, bool, float3, ColorGeometry4f, std::string>(
[&](auto type_tag) {
using T = typename decltype(type_tag)::type;
if constexpr (std::is_void_v<T>) {
BLI_assert_unreachable();
}
else {
static auto switch_fn = mf::build::SI3_SO<bool, T, T, T>( static auto switch_fn = mf::build::SI3_SO<bool, T, T, T>(
"Switch", [](bool condition, const T &false_value, const T &true_value) { "Switch", [](const bool condition, const T &false_value, const T &true_value) {
return condition ? true_value : false_value; return condition ? true_value : false_value;
}); });
switch_multi_function = &switch_fn;
auto switch_op = std::make_shared<FieldOperation>(FieldOperation(
std::move(switch_fn),
{std::move(switches_field), std::move(falses_field), std::move(trues_field)}));
params.set_output(name_output, Field<T>(switch_op, 0));
}
else {
/* The switch input is constant, so just evaluate and forward one of the inputs. */
const bool switch_value = fn::evaluate_constant_field(switches_field);
if (switch_value) {
params.set_input_unused(name_false);
if (params.lazy_require_input(name_true)) {
return;
}
params.set_output(name_output, params.extract_input<Field<T>>(name_true));
}
else {
params.set_input_unused(name_true);
if (params.lazy_require_input(name_false)) {
return;
}
params.set_output(name_output, params.extract_input<Field<T>>(name_false));
}
}
}
template<typename T> void switch_no_fields(GeoNodeExecParams &params, const StringRef suffix)
{
if (params.lazy_require_input("Switch_001")) {
return;
}
bool switch_value = params.get_input<bool>("Switch_001");
const std::string name_false = "False" + suffix;
const std::string name_true = "True" + suffix;
const std::string name_output = "Output" + suffix;
if (switch_value) {
params.set_input_unused(name_false);
if (params.lazy_require_input(name_true)) {
return;
}
params.set_output(name_output, params.extract_input<T>(name_true));
}
else {
params.set_input_unused(name_true);
if (params.lazy_require_input(name_false)) {
return;
}
params.set_output(name_output, params.extract_input<T>(name_false));
}
}
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeSwitch &storage = node_storage(params.node());
const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.input_type);
switch (data_type) {
case SOCK_FLOAT: {
switch_fields<float>(params, "");
break;
}
case SOCK_INT: {
switch_fields<int>(params, "_001");
break;
}
case SOCK_BOOLEAN: {
switch_fields<bool>(params, "_002");
break;
}
case SOCK_VECTOR: {
switch_fields<float3>(params, "_003");
break;
}
case SOCK_RGBA: {
switch_fields<ColorGeometry4f>(params, "_004");
break;
}
case SOCK_STRING: {
switch_fields<std::string>(params, "_005");
break;
}
case SOCK_GEOMETRY: {
switch_no_fields<GeometrySet>(params, "_006");
break;
}
case SOCK_OBJECT: {
switch_no_fields<Object *>(params, "_007");
break;
}
case SOCK_COLLECTION: {
switch_no_fields<Collection *>(params, "_008");
break;
}
case SOCK_TEXTURE: {
switch_no_fields<Tex *>(params, "_009");
break;
}
case SOCK_MATERIAL: {
switch_no_fields<Material *>(params, "_010");
break;
}
case SOCK_IMAGE: {
switch_no_fields<Image *>(params, "_011");
break;
}
default:
BLI_assert_unreachable();
break;
} }
});
BLI_assert(switch_multi_function != nullptr);
return *switch_multi_function;
} }
};
} // namespace blender::nodes::node_geo_switch_cc } // namespace blender::nodes::node_geo_switch_cc
namespace blender::nodes {
std::unique_ptr<LazyFunction> get_switch_node_lazy_function(const bNode &node)
{
using namespace node_geo_switch_cc;
BLI_assert(node.type == GEO_NODE_SWITCH);
return std::make_unique<LazyFunctionForSwitchNode>(node);
}
} // namespace blender::nodes
void register_node_type_geo_switch() void register_node_type_geo_switch()
{ {
namespace file_ns = blender::nodes::node_geo_switch_cc; namespace file_ns = blender::nodes::node_geo_switch_cc;
@ -303,8 +277,6 @@ void register_node_type_geo_switch()
ntype.initfunc = file_ns::node_init; ntype.initfunc = file_ns::node_init;
ntype.updatefunc = file_ns::node_update; ntype.updatefunc = file_ns::node_update;
node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.geometry_node_execute_supports_laziness = true;
ntype.gather_link_search_ops = file_ns::node_gather_link_searches; ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
ntype.draw_buttons = file_ns::node_layout; ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype); nodeRegisterType(&ntype);

View File

@ -73,10 +73,7 @@ static void lazy_function_interface_from_node(const bNode &node,
Vector<lf::Output> &r_outputs) Vector<lf::Output> &r_outputs)
{ {
const bool is_muted = node.is_muted(); const bool is_muted = node.is_muted();
const bool supports_laziness = node.typeinfo->geometry_node_execute_supports_laziness || const lf::ValueUsage input_usage = lf::ValueUsage::Used;
node.is_group();
const lf::ValueUsage input_usage = supports_laziness ? lf::ValueUsage::Maybe :
lf::ValueUsage::Used;
for (const bNodeSocket *socket : node.input_sockets()) { for (const bNodeSocket *socket : node.input_sockets()) {
if (!socket->is_available()) { if (!socket->is_available()) {
continue; continue;
@ -1331,6 +1328,10 @@ struct GeometryNodesLazyFunctionGraphBuilder {
this->handle_viewer_node(*bnode); this->handle_viewer_node(*bnode);
break; break;
} }
case GEO_NODE_SWITCH: {
this->handle_switch_node(*bnode);
break;
}
default: { default: {
if (node_type->geometry_node_execute) { if (node_type->geometry_node_execute) {
this->handle_geometry_node(*bnode); this->handle_geometry_node(*bnode);
@ -1572,6 +1573,31 @@ struct GeometryNodesLazyFunctionGraphBuilder {
mapping_->viewer_node_map.add(&bnode, &lf_node); mapping_->viewer_node_map.add(&bnode, &lf_node);
} }
void handle_switch_node(const bNode &bnode)
{
std::unique_ptr<LazyFunction> lazy_function = get_switch_node_lazy_function(bnode);
lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
lf_graph_info_->functions.append(std::move(lazy_function));
int input_index = 0;
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
if (bsocket->is_available()) {
lf::InputSocket &lf_socket = lf_node.input(input_index);
input_socket_map_.add(bsocket, &lf_socket);
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
input_index++;
}
}
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
if (bsocket->is_available()) {
lf::OutputSocket &lf_socket = lf_node.output(0);
output_socket_map_.add(bsocket, &lf_socket);
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
break;
}
}
}
void handle_undefined_node(const bNode &bnode) void handle_undefined_node(const bNode &bnode)
{ {
Vector<const bNodeSocket *> used_outputs; Vector<const bNodeSocket *> used_outputs;