The Psnode Interface and Data Structures

Eric Chan
February 25, 2002

$Author: ericchan $
$Date: 2002/06/17 21:00:04 $

----------------------------------------------------------------------

History

06.17.2002
- Small tweaks and reformatting.

03.15.2002
- Added utility ps_read / ps_write routines.
- Added ps_size.
- Added ps_num_saved.

03.04.2002
- psnodes are now created with IDs.

03.03.2002
- Changed semantics of [map] function so that computing cost is not
  necessary if second argument is NULL.
- Added [costvalue] function.
- Added [n_refs] parameter to [recompute] function.

02.25.2002
- Added [link] field.
- Added [frag] function.

11.29.2001
- Initial draft.

----------------------------------------------------------------------

Source Files

The relevant source files are psnode.c, psnode.h.

----------------------------------------------------------------------

Interface and Function Descriptions

The psnode.h header file exports:

  int pass_split (psnode_t *root, psfuncs_t *funcs,
                  int *out_cost);

The pass_split function is given a DAG [root] and a set of function
pointers [funcs].  It solves the pass splitting problem for the given
DAG and may use the function pointers to do so.  It return non-zero on
success, zero on failure.  On success, [out_cost] contains the cost of
the decomposition; otherwise, the contents of [out_cost] are
undefined.

A few callback functions are needed.

    int (*map) (psnode_t *root, void **cost);

The map function takes a set of DAG nodes and tries to compile them to
a single pass.  It processes the root node of the DAG, and recursively
any children not marked as saved.  The root node itself will be marked
as saved if and only if it is not the root node of the entire
computation.  The map function returns zero on failure, non-zero on
success.  It also returns an opaque pointer to data representing the
cost of the compiled pass, or NULL if the map operation failed.  NULL
may be passed to [cost] to indicate that map need not compute cost
information.  This function is equivalent to the VALID callback
described in Section 4 of the RDS paper.

    int (*rsrc_model)(void *cost);

The rsrc_model heuristic function is a measure of the resources
consumed by a pass.  The function must obey this rule: if pass A
consumes strictly fewer resources than pass B, then rsrc_model(A) <
rsrc_model(B).

    int (*cost_model)(psnode_t *root);

The cost_model heuristic function is a measure of the quality of a
pass decomposition.  The function must obey this rule: if
decomposition A is superior to decomposition B, then cost_model(A) <
cost_model(B).  This function is equivalent to the COST callback
described in Section 4 of the RDS paper.

    int (*recompute) (void *cost, int n_refs);

The recompute heuristic function returns non-zero if the pass
associated with cost should be recomputed, zero otherwise.  The
parameter [n_refs] contains the number of times this cost is used in
subsequent computation.  This function is equivalent to the RECOMPUTE
callback described in Section 4 of the RDS paper.

    void (*freecost) (void *cost);

The freecost function deallocates the memory associated with the
specified cost.

    int (*frag) (psnode_t *node);

The frag function returns non-zero if [node] represents a per-fragment
operation.  It returns zero if [node] represents a non-per-fragment
operation, such as a per-vertex operation, a constant value, or a
texture reference.

----------------------------------------------------------------------

Data Structure

  typedef struct psnode_s
  {
      int n_refs;
      struct psnode_s **refs;

      int n_kids;
      struct psnode_s **kids;

      struct psnode_s *link;

      void *data;
      int saved;
      int visited;
      int id;
  } psnode_t;


  typedef struct psfuncs_s
  {
      int (*map)(psnode_t *root, void **cost);
      int (*rsrc_model)(void *cost);
      int (*cost_model)(psnode_t *root);
      int (*recompute)(void *cost, int n_refs);
      void (*freecost)(void *cost);
      int (*frag)(psnode_t *node);
  } psfuncs_t;

Compiler back ends are responsible for correctly constructing a DAG
using the psnode_t data structure.

n_refs is the number of parent nodes.  It is always zero for the root
node of the DAG.

n_kids is the number of children nodes.  It is always zero for a leaf
node of the DAG.

refs is an array of n_refs elements.  Each element is a pointer to a
parent node.

kids is an array of n_kids elements.  Each element is a pointer to a
child node.

link is a pointer to another psnode.  All nodes in the DAG are
connected via this link pointer in topological order.  This means that
if B is a descendant of A, then B occurs in the list after A, i.e. by
recursively following A->link, you will always find B.  The last node
in the list has its link field set to NULL.  Thus, this code will
traverse the entire DAG in topological order, visiting each element
once and only once:

   for (elem = root; elem; elem = elem->link)
      ;

visited is a flag reserved for the pass split algorithms.  The visited
field is always initalized to zero by the compiler back ends.  After
initialization, compiler back ends may not modify this field.

saved is a flag reserved for the pass split algorithms.  It is set by
the pass split algorithm to 1 if the node is the root of a new pass.
The saved field is always initialized to zero by the compiler back
ends.  After initialization, compiler back ends may not modify this
field.

id is a field reserved for the pass split algorithms.  At creation, a
psnode is assigned a unique non-negative integer id in the range 0
... N-1, where N is the number of nodes in the DAG.  The id values are
assigned in topological order.  Specifically, this means that A->id <
B->id if and only if B is a descendant of A.  The root is always
assigned the id 0.

data is a field reserved for the compiler back ends.  It is intended
to allow the compiler to maintain back end-specific information.

----------------------------------------------------------------------

Utility Functions

   void ps_unvisit_all (psnode_t *root);

ps_unvisit_all sets the [visited] flag to 0 for each node given the
root of the DAG.  The argument must be the root of the entire DAG.

   void ps_unsave_all (psnode_t *root);

ps_unsave_all sets the [saved] flag to 0 for each node given the root
of the DAG.  The argument must be the root of the entire DAG.

   psnode_t **ps_create_map (psnode_t *root, int *out_size);

ps_create_map creates a map from integer IDs to psnode pointers.
Recall that the [root] node itself always has ID 0.  Given this code:

   psnode_t **map = ps_create_map(dag, &size);

then for any node P in the dag, it is always true that map[P->id] ==
P.

   void ps_delete_dag (psnode_t *root);

Frees all memory associated with the DAG.  Should only be called only
by the compiler back ends, since the compiler was responsible for
allocating the DAG initially.

   int ps_size (psnode_t *root);

Returns the number of nodes in the dag.

   int ps_num_saved (psnode_t *root);

Returns the number of nodes in the dag that are marked as saved.

   int ps_write (psnode_t *root, char *filename,
                 char *usr_string);

Writes the decomposition of the given dag to the file whose name is
[filename].  A custom string (containing arbitrary information) can be
stored with the decomposition, passed via [usr_string].  If no such
string is needed, NULL can be provided.  The file is ASCII and
human-readable but should not be modified manually.

   int ps_read (psnode_t *root, char *filename,
                char *out_usr_string);

Sets the decomposition of the given dag to match the decomposition
described in file [filename].  If the decomposition file stores a
custom user string, it will be written to the parameter
[out_usr_string].  A maximum of 255 characters will be written to
[out_usr_string].  Note that it does not make sense to restore a
shader's decomposition from another shader's decomposition file.
ps_read will issue warnings and abort if it detects inconsistencies.

   void ps_dump (psnode_t *root, char *filename,
                 draw_ps_node_fn_t draw_node_fn,
                 draw_ps_edge_fn_t draw_edge_fn);

The ps_dump function is used for visualizing shaders / DAGs via the
Graphviz software package from AT&T Research.  See drawdag.txt for
more information.
