forked from blender/blender
main sync #3
@ -345,11 +345,6 @@ typedef struct bNodeType {
|
||||
|
||||
/* Execute a geometry node. */
|
||||
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. */
|
||||
NodeDeclareFunction declare;
|
||||
|
@ -194,8 +194,6 @@ class GeoNodeExecParams {
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
@ -203,29 +201,6 @@ class GeoNodeExecParams {
|
||||
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.
|
||||
*/
|
||||
|
@ -221,6 +221,8 @@ class GeometryNodesLazyFunctionLogger : public fn::lazy_function::GraphExecutor:
|
||||
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
|
||||
* context. For example, the same viewer node can have side effects in one context, but not in
|
||||
|
@ -5,14 +5,9 @@
|
||||
#include "UI_interface.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 "FN_multi_function_signature.hh"
|
||||
#include "FN_field_cpp_type.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_switch_cc {
|
||||
|
||||
@ -149,149 +144,128 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void switch_fields(GeoNodeExecParams ¶ms, const StringRef suffix)
|
||||
{
|
||||
if (params.lazy_require_input("Switch")) {
|
||||
class LazyFunctionForSwitchNode : public LazyFunction {
|
||||
public:
|
||||
LazyFunctionForSwitchNode(const bNode &node)
|
||||
{
|
||||
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 ¶ms, 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 ¶ms) 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;
|
||||
}
|
||||
|
||||
const std::string name_false = "False" + suffix;
|
||||
const std::string name_true = "True" + suffix;
|
||||
const std::string name_output = "Output" + suffix;
|
||||
const CPPType &type = *outputs_[0].type;
|
||||
void *output_ptr = params.get_output_data_ptr(0);
|
||||
type.move_construct(value_to_forward, output_ptr);
|
||||
params.output_set(0);
|
||||
}
|
||||
|
||||
Field<bool> switches_field = params.get_input<Field<bool>>("Switch");
|
||||
if (switches_field.node().depends_on_input()) {
|
||||
/* The switch has to be incorporated into the field. Both inputs have to be evaluated. */
|
||||
const bool require_false = params.lazy_require_input(name_false);
|
||||
const bool require_true = params.lazy_require_input(name_true);
|
||||
if (require_false | require_true) {
|
||||
void execute_field(Field<bool> condition, lf::Params ¶ms) const
|
||||
{
|
||||
/* When the condition is a non-constant field, we need both inputs. */
|
||||
void *false_value_or_field = params.try_get_input_data_ptr_or_request(false_input_index);
|
||||
void *true_value_or_field = params.try_get_input_data_ptr_or_request(true_input_index);
|
||||
if (ELEM(nullptr, false_value_or_field, true_value_or_field)) {
|
||||
/* Try again when inputs are available. */
|
||||
return;
|
||||
}
|
||||
|
||||
Field<T> falses_field = params.extract_input<Field<T>>(name_false);
|
||||
Field<T> trues_field = params.extract_input<Field<T>>(name_true);
|
||||
const CPPType &type = *outputs_[0].type;
|
||||
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>(
|
||||
"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;
|
||||
});
|
||||
|
||||
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));
|
||||
switch_multi_function = &switch_fn;
|
||||
}
|
||||
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;
|
||||
});
|
||||
BLI_assert(switch_multi_function != nullptr);
|
||||
return *switch_multi_function;
|
||||
}
|
||||
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 ¶ms, 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // 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()
|
||||
{
|
||||
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.updatefunc = file_ns::node_update;
|
||||
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.draw_buttons = file_ns::node_layout;
|
||||
nodeRegisterType(&ntype);
|
||||
|
@ -73,10 +73,7 @@ static void lazy_function_interface_from_node(const bNode &node,
|
||||
Vector<lf::Output> &r_outputs)
|
||||
{
|
||||
const bool is_muted = node.is_muted();
|
||||
const bool supports_laziness = node.typeinfo->geometry_node_execute_supports_laziness ||
|
||||
node.is_group();
|
||||
const lf::ValueUsage input_usage = supports_laziness ? lf::ValueUsage::Maybe :
|
||||
lf::ValueUsage::Used;
|
||||
const lf::ValueUsage input_usage = lf::ValueUsage::Used;
|
||||
for (const bNodeSocket *socket : node.input_sockets()) {
|
||||
if (!socket->is_available()) {
|
||||
continue;
|
||||
@ -1331,6 +1328,10 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
this->handle_viewer_node(*bnode);
|
||||
break;
|
||||
}
|
||||
case GEO_NODE_SWITCH: {
|
||||
this->handle_switch_node(*bnode);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (node_type->geometry_node_execute) {
|
||||
this->handle_geometry_node(*bnode);
|
||||
@ -1572,6 +1573,31 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
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)
|
||||
{
|
||||
Vector<const bNodeSocket *> used_outputs;
|
||||
|
Loading…
Reference in New Issue
Block a user