panfrost: Precompute scoreboard dependents
authorAlyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Fri, 12 Jul 2019 21:48:34 +0000 (14:48 -0700)
committerAlyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Fri, 12 Jul 2019 23:22:15 +0000 (16:22 -0700)
Mali job dependency graphs, at least for GLES3.0, have the special
property that a given node will only have at most a single dependent.
This allows us to efficiently precompute the dependent array and
replace an inner loop's O(N) search with an O(1) lookup, bringing the
algorithmic complexity of scoreboarding from O(N^2) to O(N).

Signed-off-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
src/gallium/drivers/panfrost/pan_scoreboard.c

index ed3f42271d422bac3b249021bf802399c39ab673..63463daf0b4f5953bbabdce456ccda977814dee1 100644 (file)
@@ -366,7 +366,40 @@ panfrost_scoreboard_link_batch(struct panfrost_job *batch)
         BITSET_WORD *edge_removal_1 = calloc(sz, 1);
         BITSET_WORD *edge_removal_2 = calloc(sz, 1);
 
-        /* We compute no_incoming by traversing the batch. */
+        /* We compute no_incoming by traversing the batch. Simultaneously, we
+         * would like to keep track of a parity-reversed version of the
+         * dependency graph. Dependency indices are 16-bit and in practice (for
+         * ES3.0, at least), we can guarantee a given node will be depended on
+         * by no more than one other nodes. P.f:
+         *
+         * Proposition: Given a node N of type T, no more than one other node
+         * depends on N.
+         *
+         * If type is SET_VALUE: The only dependency added against us is from
+         * the first tiler job, so there is 1 dependent.
+         *
+         * If type is VERTEX: If there is a tiler node, that tiler node depends
+         * on us; if there is not (transform feedback), nothing depends on us.
+         * Therefore there is at most 1 dependent.
+         *
+         * If type is TILER: If there is another TILER job in succession, that
+         * node depends on us. No other job type depends on us. Therefore there
+         * is at most 1 dependent.
+         *
+         * If type is FRAGMENT: This type cannot be in a primary chain, so it
+         * is irrelevant. Just for kicks, nobody would depend on us, so there
+         * are zero dependents, so it holds anyway.
+         *
+         * TODO: Revise this logic for ES3.1 and above. This result may not
+         * hold for COMPUTE/FUSED/GEOMETRY jobs; we might need to special case
+         * those. Can FBO dependencies be expressed within a chain?
+         * ---
+         *
+         * Point is, we only need to hold a single dependent, which is a pretty
+         * helpful result.
+         */
+
+        unsigned *dependents = calloc(node_count, sizeof(unsigned));
 
         for (unsigned i = 0; i < node_count; ++i) {
                 struct mali_job_descriptor_header *node = DESCRIPTOR_FOR_NODE(i);
@@ -374,8 +407,23 @@ panfrost_scoreboard_link_batch(struct panfrost_job *batch)
                 unsigned dep_1 = node->job_dependency_index_1;
                 unsigned dep_2 = node->job_dependency_index_2;
 
+                /* Record no_incoming info for this node */
+
                 if (!(dep_1 || dep_2))
                         BITSET_SET(no_incoming, i);
+
+                /* Record this node as the dependent of each of its
+                 * dependencies */
+
+                if (dep_1) {
+                        assert(!dependents[dep_1 - 1]);
+                        dependents[dep_1 - 1] = i;
+                }
+
+                if (dep_2) {
+                        assert(!dependents[dep_2 - 1]);
+                        dependents[dep_2 - 1] = i;
+                }
         }
 
         /* No next_job fields are set at the beginning, so L is implciitly the
@@ -415,8 +463,10 @@ panfrost_scoreboard_link_batch(struct panfrost_job *batch)
 
                 tail = n;
 
-                /* Scan dependencies */
-                for (unsigned node_m = 0; node_m < node_count; ++node_m) {
+                /* Grab the dependent, if there is one */
+                unsigned node_m = dependents[node_n];
+
+                if (node_m) {
                         struct mali_job_descriptor_header *m =
                                 DESCRIPTOR_FOR_NODE(node_m);
 
@@ -439,7 +489,7 @@ panfrost_scoreboard_link_batch(struct panfrost_job *batch)
                                 dep_2 = 0;
                         } else {
                                 /* This node has no relevant dependencies */
-                                continue;
+                                assert(0);
                         }
 
                         /* Are there edges left? If not, add us to S */