Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
363 lines
11 KiB
C++
363 lines
11 KiB
C++
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup depsgraph
|
|
*
|
|
* Implementation of tools for debugging the depsgraph
|
|
*/
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
#include "DEG_depsgraph_build.h"
|
|
#include "DEG_depsgraph_debug.h"
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
#include "intern/debug/deg_debug.h"
|
|
#include "intern/depsgraph.h"
|
|
#include "intern/depsgraph_relation.h"
|
|
#include "intern/depsgraph_type.h"
|
|
#include "intern/node/deg_node_component.h"
|
|
#include "intern/node/deg_node_id.h"
|
|
#include "intern/node/deg_node_time.h"
|
|
|
|
namespace deg = blender::deg;
|
|
|
|
void DEG_debug_flags_set(Depsgraph *depsgraph, int flags)
|
|
{
|
|
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
|
deg_graph->debug.flags = flags;
|
|
}
|
|
|
|
int DEG_debug_flags_get(const Depsgraph *depsgraph)
|
|
{
|
|
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
|
return deg_graph->debug.flags;
|
|
}
|
|
|
|
void DEG_debug_name_set(Depsgraph *depsgraph, const char *name)
|
|
{
|
|
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
|
deg_graph->debug.name = name;
|
|
}
|
|
|
|
const char *DEG_debug_name_get(Depsgraph *depsgraph)
|
|
{
|
|
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
|
return deg_graph->debug.name.c_str();
|
|
}
|
|
|
|
bool DEG_debug_compare(const Depsgraph *graph1, const Depsgraph *graph2)
|
|
{
|
|
BLI_assert(graph1 != nullptr);
|
|
BLI_assert(graph2 != nullptr);
|
|
const deg::Depsgraph *deg_graph1 = reinterpret_cast<const deg::Depsgraph *>(graph1);
|
|
const deg::Depsgraph *deg_graph2 = reinterpret_cast<const deg::Depsgraph *>(graph2);
|
|
if (deg_graph1->operations.size() != deg_graph2->operations.size()) {
|
|
return false;
|
|
}
|
|
/* TODO(sergey): Currently we only do real stupid check,
|
|
* which is fast but which isn't 100% reliable.
|
|
*
|
|
* Would be cool to make it more robust, but it's good enough
|
|
* for now. Also, proper graph check is actually NP-complex
|
|
* problem. */
|
|
return true;
|
|
}
|
|
|
|
bool DEG_debug_graph_relations_validate(Depsgraph *graph,
|
|
Main *bmain,
|
|
Scene *scene,
|
|
ViewLayer *view_layer)
|
|
{
|
|
Depsgraph *temp_depsgraph = DEG_graph_new(bmain, scene, view_layer, DEG_get_mode(graph));
|
|
bool valid = true;
|
|
DEG_graph_build_from_view_layer(temp_depsgraph);
|
|
if (!DEG_debug_compare(temp_depsgraph, graph)) {
|
|
fprintf(stderr, "ERROR! Depsgraph wasn't tagged for update when it should have!\n");
|
|
BLI_assert_msg(0, "This should not happen!");
|
|
valid = false;
|
|
}
|
|
DEG_graph_free(temp_depsgraph);
|
|
return valid;
|
|
}
|
|
|
|
bool DEG_debug_consistency_check(Depsgraph *graph)
|
|
{
|
|
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
|
/* Validate links exists in both directions. */
|
|
for (deg::OperationNode *node : deg_graph->operations) {
|
|
for (deg::Relation *rel : node->outlinks) {
|
|
int counter1 = 0;
|
|
for (deg::Relation *tmp_rel : node->outlinks) {
|
|
if (tmp_rel == rel) {
|
|
counter1++;
|
|
}
|
|
}
|
|
int counter2 = 0;
|
|
for (deg::Relation *tmp_rel : rel->to->inlinks) {
|
|
if (tmp_rel == rel) {
|
|
counter2++;
|
|
}
|
|
}
|
|
if (counter1 != counter2) {
|
|
printf(
|
|
"Relation exists in outgoing direction but not in "
|
|
"incoming (%d vs. %d).\n",
|
|
counter1,
|
|
counter2);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (deg::OperationNode *node : deg_graph->operations) {
|
|
for (deg::Relation *rel : node->inlinks) {
|
|
int counter1 = 0;
|
|
for (deg::Relation *tmp_rel : node->inlinks) {
|
|
if (tmp_rel == rel) {
|
|
counter1++;
|
|
}
|
|
}
|
|
int counter2 = 0;
|
|
for (deg::Relation *tmp_rel : rel->from->outlinks) {
|
|
if (tmp_rel == rel) {
|
|
counter2++;
|
|
}
|
|
}
|
|
if (counter1 != counter2) {
|
|
printf("Relation exists in incoming direction but not in outcoming (%d vs. %d).\n",
|
|
counter1,
|
|
counter2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Validate node valency calculated in both directions. */
|
|
for (deg::OperationNode *node : deg_graph->operations) {
|
|
node->num_links_pending = 0;
|
|
node->custom_flags = 0;
|
|
}
|
|
|
|
for (deg::OperationNode *node : deg_graph->operations) {
|
|
if (node->custom_flags) {
|
|
printf("Node %s is twice in the operations!\n", node->identifier().c_str());
|
|
return false;
|
|
}
|
|
for (deg::Relation *rel : node->outlinks) {
|
|
if (rel->to->type == deg::NodeType::OPERATION) {
|
|
deg::OperationNode *to = (deg::OperationNode *)rel->to;
|
|
BLI_assert(to->num_links_pending < to->inlinks.size());
|
|
++to->num_links_pending;
|
|
}
|
|
}
|
|
node->custom_flags = 1;
|
|
}
|
|
|
|
for (deg::OperationNode *node : deg_graph->operations) {
|
|
int num_links_pending = 0;
|
|
for (deg::Relation *rel : node->inlinks) {
|
|
if (rel->from->type == deg::NodeType::OPERATION) {
|
|
num_links_pending++;
|
|
}
|
|
}
|
|
if (node->num_links_pending != num_links_pending) {
|
|
printf("Valency mismatch: %s, %u != %d\n",
|
|
node->identifier().c_str(),
|
|
node->num_links_pending,
|
|
num_links_pending);
|
|
printf("Number of inlinks: %d\n", int(node->inlinks.size()));
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* ------------------------------------------------ */
|
|
|
|
void DEG_stats_simple(const Depsgraph *graph,
|
|
size_t *r_outer,
|
|
size_t *r_operations,
|
|
size_t *r_relations)
|
|
{
|
|
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
|
|
|
/* number of operations */
|
|
if (r_operations) {
|
|
/* All operations should be in this list, allowing us to count the total
|
|
* number of nodes. */
|
|
*r_operations = deg_graph->operations.size();
|
|
}
|
|
|
|
/* Count number of outer nodes and/or relations between these. */
|
|
if (r_outer || r_relations) {
|
|
size_t tot_outer = 0;
|
|
size_t tot_rels = 0;
|
|
|
|
for (deg::IDNode *id_node : deg_graph->id_nodes) {
|
|
tot_outer++;
|
|
for (deg::ComponentNode *comp_node : id_node->components.values()) {
|
|
tot_outer++;
|
|
for (deg::OperationNode *op_node : comp_node->operations) {
|
|
tot_rels += op_node->inlinks.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
deg::TimeSourceNode *time_source = deg_graph->find_time_source();
|
|
if (time_source != nullptr) {
|
|
tot_rels += time_source->inlinks.size();
|
|
}
|
|
|
|
if (r_relations) {
|
|
*r_relations = tot_rels;
|
|
}
|
|
if (r_outer) {
|
|
*r_outer = tot_outer;
|
|
}
|
|
}
|
|
}
|
|
|
|
static deg::string depsgraph_name_for_logging(Depsgraph *depsgraph)
|
|
{
|
|
const char *name = DEG_debug_name_get(depsgraph);
|
|
if (name[0] == '\0') {
|
|
return "";
|
|
}
|
|
return "[" + deg::string(name) + "]: ";
|
|
}
|
|
|
|
void DEG_debug_print_begin(Depsgraph *depsgraph)
|
|
{
|
|
fprintf(stdout, "%s", depsgraph_name_for_logging(depsgraph).c_str());
|
|
}
|
|
|
|
void DEG_debug_print_eval(Depsgraph *depsgraph,
|
|
const char *function_name,
|
|
const char *object_name,
|
|
const void *object_address)
|
|
{
|
|
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
|
return;
|
|
}
|
|
fprintf(stdout,
|
|
"%s%s on %s %s(%p)%s\n",
|
|
depsgraph_name_for_logging(depsgraph).c_str(),
|
|
function_name,
|
|
object_name,
|
|
deg::color_for_pointer(object_address).c_str(),
|
|
object_address,
|
|
deg::color_end().c_str());
|
|
fflush(stdout);
|
|
}
|
|
|
|
void DEG_debug_print_eval_subdata(Depsgraph *depsgraph,
|
|
const char *function_name,
|
|
const char *object_name,
|
|
const void *object_address,
|
|
const char *subdata_comment,
|
|
const char *subdata_name,
|
|
const void *subdata_address)
|
|
{
|
|
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
|
return;
|
|
}
|
|
fprintf(stdout,
|
|
"%s%s on %s %s(%p)%s %s %s %s(%p)%s\n",
|
|
depsgraph_name_for_logging(depsgraph).c_str(),
|
|
function_name,
|
|
object_name,
|
|
deg::color_for_pointer(object_address).c_str(),
|
|
object_address,
|
|
deg::color_end().c_str(),
|
|
subdata_comment,
|
|
subdata_name,
|
|
deg::color_for_pointer(subdata_address).c_str(),
|
|
subdata_address,
|
|
deg::color_end().c_str());
|
|
fflush(stdout);
|
|
}
|
|
|
|
void DEG_debug_print_eval_subdata_index(Depsgraph *depsgraph,
|
|
const char *function_name,
|
|
const char *object_name,
|
|
const void *object_address,
|
|
const char *subdata_comment,
|
|
const char *subdata_name,
|
|
const void *subdata_address,
|
|
const int subdata_index)
|
|
{
|
|
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
|
return;
|
|
}
|
|
fprintf(stdout,
|
|
"%s%s on %s %s(%p)%s %s %s[%d] %s(%p)%s\n",
|
|
depsgraph_name_for_logging(depsgraph).c_str(),
|
|
function_name,
|
|
object_name,
|
|
deg::color_for_pointer(object_address).c_str(),
|
|
object_address,
|
|
deg::color_end().c_str(),
|
|
subdata_comment,
|
|
subdata_name,
|
|
subdata_index,
|
|
deg::color_for_pointer(subdata_address).c_str(),
|
|
subdata_address,
|
|
deg::color_end().c_str());
|
|
fflush(stdout);
|
|
}
|
|
|
|
void DEG_debug_print_eval_parent_typed(Depsgraph *depsgraph,
|
|
const char *function_name,
|
|
const char *object_name,
|
|
const void *object_address,
|
|
const char *parent_comment,
|
|
const char *parent_name,
|
|
const void *parent_address)
|
|
{
|
|
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
|
return;
|
|
}
|
|
fprintf(stdout,
|
|
"%s%s on %s %s(%p) [%s] %s %s %s(%p)%s\n",
|
|
depsgraph_name_for_logging(depsgraph).c_str(),
|
|
function_name,
|
|
object_name,
|
|
deg::color_for_pointer(object_address).c_str(),
|
|
object_address,
|
|
deg::color_end().c_str(),
|
|
parent_comment,
|
|
parent_name,
|
|
deg::color_for_pointer(parent_address).c_str(),
|
|
parent_address,
|
|
deg::color_end().c_str());
|
|
fflush(stdout);
|
|
}
|
|
|
|
void DEG_debug_print_eval_time(Depsgraph *depsgraph,
|
|
const char *function_name,
|
|
const char *object_name,
|
|
const void *object_address,
|
|
float time)
|
|
{
|
|
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
|
return;
|
|
}
|
|
fprintf(stdout,
|
|
"%s%s on %s %s(%p)%s at time %f\n",
|
|
depsgraph_name_for_logging(depsgraph).c_str(),
|
|
function_name,
|
|
object_name,
|
|
deg::color_for_pointer(object_address).c_str(),
|
|
object_address,
|
|
deg::color_end().c_str(),
|
|
time);
|
|
fflush(stdout);
|
|
}
|