xref: /third_party/mesa3d/src/util/dag.c (revision bf215546)
1/*
2 * Copyright © 2019 Broadcom
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24#include "util/set.h"
25#include "util/dag.h"
26
27static void
28append_edge(struct dag_node *parent, struct dag_node *child, uintptr_t data)
29{
30   /* Remove the child as a DAG head. */
31   list_delinit(&child->link);
32
33   struct dag_edge edge = {
34      .child = child,
35      .data = data,
36   };
37
38   util_dynarray_append(&parent->edges, struct dag_edge, edge);
39   child->parent_count++;
40}
41
42/**
43 * Adds a directed edge from the parent node to the child.
44 *
45 * Both nodes should have been initialized with dag_init_node().  The edge
46 * list may contain multiple edges to the same child with different data.
47 */
48void
49dag_add_edge(struct dag_node *parent, struct dag_node *child, uintptr_t data)
50{
51   util_dynarray_foreach(&parent->edges, struct dag_edge, edge) {
52      if (edge->child == child && edge->data == data)
53         return;
54   }
55
56   append_edge(parent, child, data);
57}
58
59/**
60 * Adds a directed edge from the parent node to the child.
61 *
62 * Both nodes should have been initialized with dag_init_node(). If there is
63 * already an existing edge, the data is updated to the maximum of the
64 * previous data and the new data. This is useful if the data represents a
65 * delay.
66 */
67void
68dag_add_edge_max_data(struct dag_node *parent, struct dag_node *child,
69                      uintptr_t data)
70{
71   util_dynarray_foreach(&parent->edges, struct dag_edge, edge) {
72      if (edge->child == child) {
73         edge->data = MAX2(edge->data, data);
74         return;
75      }
76   }
77
78   append_edge(parent, child, data);
79}
80
81/* Removes a single edge from the graph, promoting the child to a DAG head.
82 *
83 * Note that calling this other than through dag_prune_head() means that you
84 * need to be careful when iterating the edges of remaining nodes for NULL
85 * children.
86 */
87void
88dag_remove_edge(struct dag *dag, struct dag_edge *edge)
89{
90   if (!edge->child)
91      return;
92
93   struct dag_node *child = edge->child;
94   child->parent_count--;
95   if (child->parent_count == 0)
96      list_addtail(&child->link, &dag->heads);
97
98   edge->child = NULL;
99   edge->data = 0;
100}
101
102/**
103 * Removes a DAG head from the graph, and moves any new dag heads into the
104 * heads list.
105 */
106void
107dag_prune_head(struct dag *dag, struct dag_node *node)
108{
109   assert(!node->parent_count);
110
111   list_delinit(&node->link);
112
113   util_dynarray_foreach(&node->edges, struct dag_edge, edge) {
114      dag_remove_edge(dag, edge);
115   }
116}
117
118/**
119 * Initializes DAG node (probably embedded in some other datastructure in the
120 * user).
121 */
122void
123dag_init_node(struct dag *dag, struct dag_node *node)
124{
125   util_dynarray_init(&node->edges, dag);
126   list_addtail(&node->link, &dag->heads);
127}
128
129struct dag_traverse_bottom_up_state {
130   struct set *seen;
131   void *data;
132};
133
134static void
135dag_traverse_bottom_up_node(struct dag_node *node,
136                            void (*cb)(struct dag_node *node,
137                                       void *data),
138                            struct dag_traverse_bottom_up_state *state)
139{
140   if (_mesa_set_search(state->seen, node))
141      return;
142
143   struct util_dynarray stack;
144   util_dynarray_init(&stack, NULL);
145
146   do {
147      assert(node);
148
149      while (node->edges.size != 0) {
150         util_dynarray_append(&stack, struct dag_node *, node);
151
152         /* Push unprocessed children onto stack in reverse order. Note that
153          * it's possible for any of the children nodes to already be on the
154          * stack.
155          */
156         util_dynarray_foreach_reverse(&node->edges, struct dag_edge, edge) {
157            if (!_mesa_set_search(state->seen, edge->child)) {
158               util_dynarray_append(&stack, struct dag_node *, edge->child);
159            }
160         }
161
162         /* Get last element pushed: either left-most child or current node.
163          * If it's the current node, that means that we've processed all its
164          * children already.
165          */
166         struct dag_node *top = util_dynarray_pop(&stack, struct dag_node *);
167         if (top == node)
168            break;
169         node = top;
170      }
171
172      /* Process the node */
173      cb(node, state->data);
174      _mesa_set_add(state->seen, node);
175
176      /* Find the next unprocessed node in the stack */
177      do {
178         node = NULL;
179         if (stack.size == 0)
180            break;
181
182         node = util_dynarray_pop(&stack, struct dag_node *);
183      } while (_mesa_set_search(state->seen, node));
184   } while (node);
185
186   util_dynarray_fini(&stack);
187}
188
189/**
190 * Walks the DAG from leaves to the root, ensuring that each node is only seen
191 * once its children have been, and each node is only traversed once.
192 */
193void
194dag_traverse_bottom_up(struct dag *dag, void (*cb)(struct dag_node *node,
195                                                   void *data), void *data)
196{
197   struct dag_traverse_bottom_up_state state = {
198      .seen = _mesa_pointer_set_create(NULL),
199      .data = data,
200   };
201
202   list_for_each_entry(struct dag_node, node, &dag->heads, link) {
203      dag_traverse_bottom_up_node(node, cb, &state);
204   }
205
206   ralloc_free(state.seen);
207}
208
209/**
210 * Creates an empty DAG datastructure.
211 */
212struct dag *
213dag_create(void *mem_ctx)
214{
215   struct dag *dag = rzalloc(mem_ctx, struct dag);
216
217   list_inithead(&dag->heads);
218
219   return dag;
220}
221
222