Shade: Basic Concepts
Shortly after first writing about Shade I recieved some comments requesting more information about the tool and how it works. The short answer is “poorly” — it’s still very much in the “proof-of-concept” stage. You can create shaders with it, but I haven’t built up a solid feature library and there are a lot of usability bugs. This is why I haven’t made the tool available yet, and don’t plan on doing so any time soon. I can, however, talk about it — I’ve prepared a series of posts that I will publish over the next few days that explore how Shade operates.
Shade is, primarily, a graph editor. The underlying framework contains a Graph class (and some supporting classes) that provide a basic graph container represented as a collection of vertices and a collection of edges between each vertex. Custom user data can be attached to the graph elements at various points: each vertex can hold user data, and each edge can hold user data for the edge itself, as well as user data associated with the edge’s origin and destination (edges are directional).
The Graph collection provides a foundation for the GraphControl class, which is a GUI control that allows construction and/or manipulation of a graph and is what the user interacts with when they actually use Shade. GraphControl is slightly more strict with its graph representation; basic Graph vertices have no restrictions on incoming or outgoing edge counts, but GraphControl vertices must specify a maximum for both. This is so the control can render the appropriate number of “sockets” on each graph vertex, regardless of whether those sockets are connected by an edge.

The above screenshot of a simple shader graph in Shade illustrates the look of the GraphControl. The sockets on the left of each graph vertex represent inputs, while those on the right represent outputs. Dull red lines represent edges. Each vertex’s user data field contains a reference to a Feature, a class which describes an isolated package of shader functionality and the inputs and outputs of that functionality. The Feature is also used to tell the control how many sockets to create and what to label them.
GraphControl also provides a mechanism to associate custom “data editors” with the control. These editors are associated with a particular type of user data. Any graph vertex that contains user data of the appropriate type gets an instance of the data editor embedded into that vertex’s representation. In the screenshot, the preview of the texture and the associated controls are provided by just such a customized editor.
That is essentially all there is to the user-interface aspect of Shade. The real interesting stuff comes in the compilation process. In brief, compilation in Shade is a two-pass operation. First, a topological sort of the graph is performed. This provides an ordering of the graph vertices that we can traverse linearly with the assurance that at an given vertex V, we have seen and processed all of the vertices upon which V’s inputs depend. Being able to make this assumption greatly simplifies the compiler’s code.
The second pass is the actual traversal of the sorted vertices, where the bulk of the processing happens. The semantic type system is leveraged to identify which parameters need to have automatic conversions inserted between them in order for their types to agree, which parameters need to be “forwarded” from one execution context to another (for example, parameters written in the vertex shader stage but read in the pixel shader stage), and which parameters will serve as the actual shader inputs and outputs. Thoughout the entire process, Shade must carefully manage shader resources so as not to exhaust them or produce code that will not compile when fed to the actual HLSL compiler.
The later articles in this mini-series will cover the above points in detail. Until then, feel free to send in any questions you might have.
