ProgramGraph::get_node() must not return null, changed to a reference

This commit is contained in:
Marc Gilleron 2022-04-07 20:35:11 +01:00
parent 7517ec9a10
commit 96224c2231
4 changed files with 86 additions and 91 deletions

View File

@ -68,36 +68,36 @@ ProgramGraph::Node *ProgramGraph::create_node(uint32_t type_id, uint32_t id) {
}
void ProgramGraph::remove_node(uint32_t node_id) {
Node *node = get_node(node_id);
Node &node = get_node(node_id);
// Remove input connections
for (uint32_t dst_port_index = 0; dst_port_index < node->inputs.size(); ++dst_port_index) {
const Port &p = node->inputs[dst_port_index];
for (uint32_t dst_port_index = 0; dst_port_index < node.inputs.size(); ++dst_port_index) {
const Port &p = node.inputs[dst_port_index];
for (auto it = p.connections.begin(); it != p.connections.end(); ++it) {
const PortLocation src = *it;
Node *src_node = get_node(src.node_id);
uint32_t i = src_node->find_output_connection(src.port_index, PortLocation{ node_id, dst_port_index });
Node &src_node = get_node(src.node_id);
uint32_t i = src_node.find_output_connection(src.port_index, PortLocation{ node_id, dst_port_index });
CRASH_COND(i == NULL_INDEX);
std::vector<PortLocation> &connections = src_node->outputs[src.port_index].connections;
std::vector<PortLocation> &connections = src_node.outputs[src.port_index].connections;
connections.erase(connections.begin() + i);
}
}
// Remove output connections
for (uint32_t src_port_index = 0; src_port_index < node->outputs.size(); ++src_port_index) {
const Port &p = node->outputs[src_port_index];
for (uint32_t src_port_index = 0; src_port_index < node.outputs.size(); ++src_port_index) {
const Port &p = node.outputs[src_port_index];
for (auto it = p.connections.begin(); it != p.connections.end(); ++it) {
const PortLocation dst = *it;
Node *dst_node = get_node(dst.node_id);
uint32_t i = dst_node->find_input_connection(PortLocation{ node_id, src_port_index }, dst.port_index);
Node &dst_node = get_node(dst.node_id);
uint32_t i = dst_node.find_input_connection(PortLocation{ node_id, src_port_index }, dst.port_index);
CRASH_COND(i == NULL_INDEX);
std::vector<PortLocation> &connections = dst_node->inputs[dst.port_index].connections;
std::vector<PortLocation> &connections = dst_node.inputs[dst.port_index].connections;
connections.erase(connections.begin() + i);
}
}
_nodes.erase(node_id);
memdelete(node);
memdelete(&node);
}
void ProgramGraph::clear() {
@ -110,13 +110,13 @@ void ProgramGraph::clear() {
}
bool ProgramGraph::is_connected(PortLocation src, PortLocation dst) const {
const Node *src_node = get_node(src.node_id);
const Node *dst_node = get_node(dst.node_id);
if (src_node->find_output_connection(src.port_index, dst) != NULL_INDEX) {
CRASH_COND(dst_node->find_input_connection(src, dst.port_index) == NULL_INDEX);
const Node &src_node = get_node(src.node_id);
const Node &dst_node = get_node(dst.node_id);
if (src_node.find_output_connection(src.port_index, dst) != NULL_INDEX) {
CRASH_COND(dst_node.find_input_connection(src, dst.port_index) == NULL_INDEX);
return true;
} else {
CRASH_COND(dst_node->find_input_connection(src, dst.port_index) != NULL_INDEX);
CRASH_COND(dst_node.find_input_connection(src, dst.port_index) != NULL_INDEX);
return false;
}
}
@ -137,33 +137,33 @@ bool ProgramGraph::can_connect(PortLocation src, PortLocation dst) const {
if (is_valid_connection(src, dst)) {
return false;
}
const Node *dst_node = get_node(dst.node_id);
const Node &dst_node = get_node(dst.node_id);
// There can be only one connection from a source to a destination
return dst_node->inputs[dst.port_index].connections.size() == 0;
return dst_node.inputs[dst.port_index].connections.size() == 0;
}
void ProgramGraph::connect(PortLocation src, PortLocation dst) {
ERR_FAIL_COND(is_connected(src, dst));
ERR_FAIL_COND(has_path(dst.node_id, src.node_id));
Node *src_node = get_node(src.node_id);
Node *dst_node = get_node(dst.node_id);
Node &src_node = get_node(src.node_id);
Node &dst_node = get_node(dst.node_id);
ERR_FAIL_COND_MSG(
dst_node->inputs[dst.port_index].connections.size() != 0, "Destination node's port is already connected");
src_node->outputs[src.port_index].connections.push_back(dst);
dst_node->inputs[dst.port_index].connections.push_back(src);
dst_node.inputs[dst.port_index].connections.size() != 0, "Destination node's port is already connected");
src_node.outputs[src.port_index].connections.push_back(dst);
dst_node.inputs[dst.port_index].connections.push_back(src);
}
bool ProgramGraph::disconnect(PortLocation src, PortLocation dst) {
Node *src_node = get_node(src.node_id);
Node *dst_node = get_node(dst.node_id);
uint32_t src_i = src_node->find_output_connection(src.port_index, dst);
Node &src_node = get_node(src.node_id);
Node &dst_node = get_node(dst.node_id);
uint32_t src_i = src_node.find_output_connection(src.port_index, dst);
if (src_i == NULL_INDEX) {
return false;
}
uint32_t dst_i = dst_node->find_input_connection(src, dst.port_index);
uint32_t dst_i = dst_node.find_input_connection(src, dst.port_index);
CRASH_COND(dst_i == NULL_INDEX);
std::vector<PortLocation> &src_connections = src_node->outputs[src.port_index].connections;
std::vector<PortLocation> &dst_connections = dst_node->inputs[dst.port_index].connections;
std::vector<PortLocation> &src_connections = src_node.outputs[src.port_index].connections;
std::vector<PortLocation> &dst_connections = dst_node.inputs[dst.port_index].connections;
src_connections.erase(src_connections.begin() + src_i);
dst_connections.erase(dst_connections.begin() + dst_i);
return true;
@ -191,12 +191,12 @@ bool ProgramGraph::is_output_port_valid(PortLocation loc) const {
return true;
}
ProgramGraph::Node *ProgramGraph::get_node(uint32_t id) const {
ProgramGraph::Node &ProgramGraph::get_node(uint32_t id) const {
auto it = _nodes.find(id);
CRASH_COND(it == _nodes.end());
Node *node = it->second;
CRASH_COND(node == nullptr);
return node;
return *node;
}
ProgramGraph::Node *ProgramGraph::try_get_node(uint32_t id) const {
@ -214,15 +214,15 @@ bool ProgramGraph::has_path(uint32_t p_src_node_id, uint32_t p_dst_node_id) cons
nodes_to_process.push_back(p_src_node_id);
while (nodes_to_process.size() > 0) {
const Node *node = get_node(nodes_to_process.back());
const Node &node = get_node(nodes_to_process.back());
nodes_to_process.pop_back();
visited_nodes.insert(node->id);
visited_nodes.insert(node.id);
uint32_t nodes_to_process_begin = nodes_to_process.size();
// Find destinations
for (uint32_t oi = 0; oi < node->outputs.size(); ++oi) {
const Port &p = node->outputs[oi];
for (uint32_t oi = 0; oi < node.outputs.size(); ++oi) {
const Port &p = node.outputs[oi];
for (auto cit = p.connections.begin(); cit != p.connections.end(); ++cit) {
PortLocation dst = *cit;
if (dst.node_id == p_dst_node_id) {
@ -257,12 +257,12 @@ void ProgramGraph::find_dependencies(std::vector<uint32_t> nodes_to_process, std
std::unordered_set<uint32_t> visited_nodes;
while (nodes_to_process.size() > 0) {
const Node *node = get_node(nodes_to_process.back());
const Node &node = get_node(nodes_to_process.back());
uint32_t nodes_to_process_begin = nodes_to_process.size();
// Find ancestors
for (uint32_t ii = 0; ii < node->inputs.size(); ++ii) {
const Port &p = node->inputs[ii];
for (uint32_t ii = 0; ii < node.inputs.size(); ++ii) {
const Port &p = node.inputs[ii];
for (auto cit = p.connections.begin(); cit != p.connections.end(); ++cit) {
const PortLocation src = *cit;
// A node can have two connections to the same destination node
@ -278,19 +278,19 @@ void ProgramGraph::find_dependencies(std::vector<uint32_t> nodes_to_process, std
if (nodes_to_process_begin == nodes_to_process.size()) {
// No ancestor to visit, process the node
out_order.push_back(node->id);
visited_nodes.insert(node->id);
out_order.push_back(node.id);
visited_nodes.insert(node.id);
nodes_to_process.pop_back();
}
}
}
void ProgramGraph::find_immediate_dependencies(uint32_t node_id, std::vector<uint32_t> &deps) const {
const Node *node = get_node(node_id);
const Node &node = get_node(node_id);
const size_t begin = deps.size();
for (uint32_t ii = 0; ii < node->inputs.size(); ++ii) {
const Port &p = node->inputs[ii];
for (uint32_t ii = 0; ii < node.inputs.size(); ++ii) {
const Port &p = node.inputs[ii];
for (auto cit = p.connections.begin(); cit != p.connections.end(); ++cit) {
const PortLocation src = *cit;
@ -314,14 +314,14 @@ void ProgramGraph::find_depth_first(uint32_t start_node_id, std::vector<uint32_t
nodes_to_process.push_back(start_node_id);
while (nodes_to_process.size() > 0) {
const Node *node = get_node(nodes_to_process.back());
const Node &node = get_node(nodes_to_process.back());
nodes_to_process.pop_back();
uint32_t nodes_to_process_begin = nodes_to_process.size();
order.push_back(node->id);
visited_nodes.insert(node->id);
order.push_back(node.id);
visited_nodes.insert(node.id);
for (uint32_t oi = 0; oi < node->outputs.size(); ++oi) {
const Port &p = node->outputs[oi];
for (uint32_t oi = 0; oi < node.outputs.size(); ++oi) {
const Port &p = node.outputs[oi];
for (auto cit = p.connections.begin(); cit != p.connections.end(); ++cit) {
PortLocation dst = *cit;
if (range_contains(nodes_to_process, dst.node_id, nodes_to_process_begin, nodes_to_process.size())) {

View File

@ -59,8 +59,7 @@ public:
};
Node *create_node(uint32_t type_id, uint32_t id = NULL_ID);
// TODO Return a reference, this function is not allowed to fail
Node *get_node(uint32_t id) const;
Node &get_node(uint32_t id) const;
Node *try_get_node(uint32_t id) const;
void remove_node(uint32_t id);
void clear();

View File

@ -116,10 +116,9 @@ void VoxelGeneratorGraph::get_connections(std::vector<ProgramGraph::Connection>
bool VoxelGeneratorGraph::try_get_connection_to(
ProgramGraph::PortLocation dst, ProgramGraph::PortLocation &out_src) const {
const ProgramGraph::Node *node = _graph.get_node(dst.node_id);
CRASH_COND(node == nullptr);
CRASH_COND(dst.port_index >= node->inputs.size());
const ProgramGraph::Port &port = node->inputs[dst.port_index];
const ProgramGraph::Node &node = _graph.get_node(dst.node_id);
CRASH_COND(dst.port_index >= node.inputs.size());
const ProgramGraph::Port &port = node.inputs[dst.port_index];
if (port.connections.size() == 0) {
return false;
}
@ -850,9 +849,8 @@ static bool has_output_type(
const VoxelGraphRuntime &runtime, const ProgramGraph &graph, VoxelGeneratorGraph::NodeTypeID node_type_id) {
for (unsigned int other_output_index = 0; other_output_index < runtime.get_output_count(); ++other_output_index) {
const VoxelGraphRuntime::OutputInfo output = runtime.get_output_info(other_output_index);
const ProgramGraph::Node *node = graph.get_node(output.node_id);
ERR_CONTINUE(node == nullptr);
if (node->type_id == VoxelGeneratorGraph::NODE_OUTPUT_WEIGHT) {
const ProgramGraph::Node &node = graph.get_node(output.node_id);
if (node.type_id == VoxelGeneratorGraph::NODE_OUTPUT_WEIGHT) {
return true;
}
}
@ -876,12 +874,10 @@ VoxelGraphRuntime::CompilationResult VoxelGeneratorGraph::compile() {
// Extra steps
for (unsigned int output_index = 0; output_index < runtime.get_output_count(); ++output_index) {
const VoxelGraphRuntime::OutputInfo output = runtime.get_output_info(output_index);
const ProgramGraph::Node *node = _graph.get_node(output.node_id);
ERR_FAIL_COND_V(node == nullptr, VoxelGraphRuntime::CompilationResult());
const ProgramGraph::Node &node = _graph.get_node(output.node_id);
// TODO Allow specifying max count in VoxelGraphNodeDB so we can make some of these checks more generic
switch (node->type_id) {
switch (node.type_id) {
case NODE_OUTPUT_SDF:
if (r->sdf_output_buffer_index != -1) {
VoxelGraphRuntime::CompilationResult error;
@ -904,8 +900,8 @@ VoxelGraphRuntime::CompilationResult VoxelGeneratorGraph::compile() {
error.node_id = output.node_id;
return error;
}
CRASH_COND(node->params.size() == 0);
const int layer_index = node->params[0];
CRASH_COND(node.params.size() == 0);
const int layer_index = node.params[0];
if (layer_index < 0) {
// Should not be allowed by the UI, but who knows
VoxelGraphRuntime::CompilationResult error;
@ -1409,7 +1405,7 @@ Ref<Resource> VoxelGeneratorGraph::duplicate(bool p_subresources) const {
static Dictionary get_graph_as_variant_data(const ProgramGraph &graph) {
Dictionary nodes_data;
graph.for_each_node_id([&graph, &nodes_data](uint32_t node_id) {
const ProgramGraph::Node *node = graph.get_node(node_id);
const ProgramGraph::Node *node = graph.try_get_node(node_id);
ERR_FAIL_COND(node == nullptr);
Dictionary node_data;
@ -1474,6 +1470,7 @@ static Dictionary get_graph_as_variant_data(const ProgramGraph &graph) {
Dictionary data;
data["nodes"] = nodes_data;
data["connections"] = connections_data;
data["version"] = 2;
return data;
}

View File

@ -192,9 +192,9 @@ static uint32_t expand_node(ProgramGraph &graph, const ExpressionParser::Node &e
static VoxelGraphRuntime::CompilationResult expand_expression_node(ProgramGraph &graph, uint32_t original_node_id,
ProgramGraph::PortLocation &expanded_output_port, std::vector<uint32_t> &expanded_nodes) {
VOXEL_PROFILE_SCOPE();
const ProgramGraph::Node *original_node = graph.get_node(original_node_id);
CRASH_COND(original_node->params.size() == 0);
const String code = original_node->params[0];
const ProgramGraph::Node &original_node = graph.get_node(original_node_id);
CRASH_COND(original_node.params.size() == 0);
const String code = original_node.params[0];
const CharString code_utf8 = code.utf8();
Span<const ExpressionParser::Function> functions =
@ -239,14 +239,14 @@ static VoxelGraphRuntime::CompilationResult expand_expression_node(ProgramGraph
const ToConnect tc = to_connect[i];
unsigned int original_port_index;
if (!original_node->find_input_port_by_name(tc.var_name, original_port_index)) {
if (!original_node.find_input_port_by_name(tc.var_name, original_port_index)) {
VoxelGraphRuntime::CompilationResult result;
result.success = false;
result.node_id = original_node_id;
result.message = "Could not resolve expression variable from input ports";
return result;
}
const ProgramGraph::Port &original_port = original_node->inputs[original_port_index];
const ProgramGraph::Port &original_port = original_node.inputs[original_port_index];
for (unsigned int j = 0; j < original_port.connections.size(); ++j) {
const ProgramGraph::PortLocation src = original_port.connections[j];
graph.connect(src, tc.dst);
@ -351,8 +351,8 @@ VoxelGraphRuntime::CompilationResult VoxelGraphRuntime::_compile(const ProgramGr
if (!debug) {
// Exclude debug nodes
unordered_remove_if(terminal_nodes, [&graph](uint32_t node_id) {
const ProgramGraph::Node *node = graph.get_node(node_id);
const VoxelGraphNodeDB::NodeType &type = VoxelGraphNodeDB::get_singleton()->get_type(node->type_id);
const ProgramGraph::Node &node = graph.get_node(node_id);
const VoxelGraphNodeDB::NodeType &type = VoxelGraphNodeDB::get_singleton()->get_type(node.type_id);
return type.debug_only;
});
}
@ -372,11 +372,11 @@ VoxelGraphRuntime::CompilationResult VoxelGraphRuntime::_compile(const ProgramGr
for (size_t i = 0; i < order.size(); ++i) {
const uint32_t node_id = order[i];
const ProgramGraph::Node *node = graph.get_node(node_id);
const ProgramGraph::Node &node = graph.get_node(node_id);
bool depends_on_y = false;
if (node->type_id == VoxelGeneratorGraph::NODE_INPUT_Y) {
if (node.type_id == VoxelGeneratorGraph::NODE_INPUT_Y) {
nodes_depending_on_y.insert(node_id);
depends_on_y = true;
}
@ -481,12 +481,11 @@ VoxelGraphRuntime::CompilationResult VoxelGraphRuntime::_compile(const ProgramGr
// Run through each node in order, and turn them into program instructions
for (size_t order_index = 0; order_index < order.size(); ++order_index) {
const uint32_t node_id = order[order_index];
const ProgramGraph::Node *node = graph.get_node(node_id);
const VoxelGraphNodeDB::NodeType &type = type_db.get_type(node->type_id);
const ProgramGraph::Node &node = graph.get_node(node_id);
const VoxelGraphNodeDB::NodeType &type = type_db.get_type(node.type_id);
CRASH_COND(node == nullptr);
CRASH_COND(node->inputs.size() != type.inputs.size());
CRASH_COND(node->outputs.size() != type.outputs.size());
CRASH_COND(node.inputs.size() != type.inputs.size());
CRASH_COND(node.outputs.size() != type.outputs.size());
if (order_index == xzy_start_index) {
_program.xzy_start_op_address = operations.size();
@ -503,11 +502,11 @@ VoxelGraphRuntime::CompilationResult VoxelGraphRuntime::_compile(const ProgramGr
node_id_to_dependency_graph.insert(std::make_pair(node_id, dg_node_index));
// We still hardcode some of the nodes. Maybe we can abstract them too one day.
switch (node->type_id) {
switch (node.type_id) {
case VoxelGeneratorGraph::NODE_CONSTANT: {
CRASH_COND(type.outputs.size() != 1);
CRASH_COND(type.params.size() != 1);
const uint16_t a = mem.add_constant(node->params[0].operator float());
const uint16_t a = mem.add_constant(node.params[0].operator float());
_program.output_port_addresses[ProgramGraph::PortLocation{ node_id, 0 }] = a;
// Technically not an input or an output, but is a dependency regardless so treat it like an input
dg_node.is_input = true;
@ -537,14 +536,14 @@ VoxelGraphRuntime::CompilationResult VoxelGraphRuntime::_compile(const ProgramGr
// Add actual operation
CRASH_COND(node->type_id > 0xff);
CRASH_COND(node.type_id > 0xff);
if (order_index == xzy_start_index) {
_program.default_execution_map.xzy_start_index = _program.default_execution_map.operation_adresses.size();
}
_program.default_execution_map.operation_adresses.push_back(operations.size());
operations.push_back(node->type_id);
operations.push_back(node.type_id);
// Inputs and outputs use a convention so we can have generic code for them.
// Parameters are more specific, and may be affected by alignment so better just do them by hand
@ -553,14 +552,14 @@ VoxelGraphRuntime::CompilationResult VoxelGraphRuntime::_compile(const ProgramGr
for (size_t j = 0; j < type.inputs.size(); ++j) {
uint16_t a;
if (node->inputs[j].connections.size() == 0) {
if (node.inputs[j].connections.size() == 0) {
// No input, default it
CRASH_COND(j >= node->default_inputs.size());
float defval = node->default_inputs[j];
CRASH_COND(j >= node.default_inputs.size());
float defval = node.default_inputs[j];
a = mem.add_constant(defval);
} else {
ProgramGraph::PortLocation src_port = node->inputs[j].connections[0];
ProgramGraph::PortLocation src_port = node.inputs[j].connections[0];
const uint16_t *aptr = _program.output_port_addresses.getptr(src_port);
// Previous node ports must have been registered
CRASH_COND(aptr == nullptr);
@ -597,9 +596,9 @@ VoxelGraphRuntime::CompilationResult VoxelGraphRuntime::_compile(const ProgramGr
// Get params, copy resources when used, and hold a reference to them
std::vector<Variant> params_copy;
params_copy.resize(node->params.size());
for (size_t i = 0; i < node->params.size(); ++i) {
Variant v = node->params[i];
params_copy.resize(node.params.size());
for (size_t i = 0; i < node.params.size(); ++i) {
Variant v = node.params[i];
if (v.get_type() == Variant::OBJECT) {
Ref<Resource> res = v;
@ -639,7 +638,7 @@ VoxelGraphRuntime::CompilationResult VoxelGraphRuntime::_compile(const ProgramGr
}
if (type.category == VoxelGraphNodeDB::CATEGORY_OUTPUT) {
CRASH_COND(node->outputs.size() != 1);
CRASH_COND(node.outputs.size() != 1);
if (_program.outputs_count == _program.outputs.size()) {
CompilationResult result;