How Does Shade Work?
The biggest hurdle to overcome with the creation of Shade was how to make a scene graph that works on both a stand alone computer and a cluster, and allow for an application developed on a single computer to be moved to a distributed tiled display with no extra coding required by the application author. Below is a brief overview of a couple of techniques used to create Shade.
Distributed Object Programming
Everytime an Object's internal state is changed, this change needs to be replicated across the network to all the computers involved in the rendering process. The following goals need to be met.
- An Object requires a unique identifier so the message can be directed to it
- The number of message types needs to scale to any number of messages, and can be any size
- If the library is meant for a stand alone computer there should be no performance hit in creating an application
The first goal is simple enough. Everytime an Object is created it is given a unique ID, which is just an unsigned integer. IDs begin with 1 so an ID of 0 indicates a NULL Object.
The second goal required use of the Boost PreProcessor library and the Boost MPI library. Two types of things need to be communicated across the network. Primitives and Objects. Primitives are C++ types, STL collections, and mathematical constructs defined by Shade. These constructs are sent through Boost MPI and are serialized using the Boost Serialization library and sent across the network. Since Objects are replicated across the network, their IDs are all that need to be communicated.Sending of Primitives and Objects is wrapped in a series of Macros that create the communication hooks to create a distributed system all at compile time.
// Excerts from ShadeShaderEffect.cpp /** Implements an MPI Primitive Object Message */ SHADE_REGISTER_MPI_MESSAGE_OP (ShaderEffect, setTexture, Texture, int); /** Implements an MPI Primitive Object Message */ SHADE_IMPLEMENT_MPI_MESSAGE_OP(ShaderEffect, setTexture, Texture, int); void ShaderEffect::setTexture(TexturePtr texture_, int unit) { textures[unit] = texture_; SHADE_SEND_MPI_MESSAGE_OP(ShaderEffect, setTexture, texture_.get(), unit); }
The third goal is met due to the use of Macros. When compiled into an MPI library, Shade expands these Macros and all the MPI calls are added. Otherwise these Macros expand to nothing and there is no performance hit since no extra code is being created.
A Virtual Window
Windowing systems give the user the ability to resize windows. If the position of the widgets within the window were hard coded to be at a specific position resizing would fail. The window would be stretched and would not look right. With graphics programming a window can be resized or a user might want to run at a higher resolution, so rather than thinking in terms of pixels its better to think of a percentage of the windows dimensions.

Shade treats the Window as having a length of 1.0 and a width of 1.0. And this idea can be extended to a tiled display.
Each window opened on the tiled display contains internal information saying what portion of the window that it is responsible for drawing. Then during rendering that portion is calculated and drawn. So as long as the physical dimensions of the window is the same of the virtual window the rendering is identical.
In fact multiple viewports can be defined and applied to the virtual window and the window is drawn properly.