Bino supports multi-display output via the Equalizer framework.

This is how it works:


Building Bino with Equalizer Support
====================================

First, install Equalizer 0.9.1. See <http://www.equalizergraphics.com/>.
Verify that it works by running the included eqHello example.

Then, build Bino with Equalizer support. You have to use the
'--with-equalizer' configure option and additionally tell the configure
script where to find Equalizer, since version 0.9.1 cannot be detected
automatically yet:

$ ./configure --with-equalizer \
  libEqualizer_CFLAGS="-I/path/to/equalizer/include" \
  libEqualizer_LIBS="-L/path/to/equalizer/lib -leq"

Now you need an Equalizer configuration file for your display setup.


Configuring Equalizer and Starting Bino
=======================================

Bino needs a two-dimensional Equalizer canvas (= combined screen area),
subdivided into segments (= single display areas). For example, if you have
two projectors that project onto a 2m x 1m screen side-by-side, then your
canvas is 2m x 1m large, and you have two segments: the first segment fills
the left half of the canvas, and the second segment fills the right half.

Next, Equalizer needs to know how to render into each segment. For this
purpose, you define several hierarchical objects: nodes (= processes, possibly
on different systems), pipes (= graphics cards), windows (= output windows
with OpenGL contexts), and channels (= parts of windows). The video output
happens at the channel level: each channel is assigned to one segment of the
canvas. Most probably you just have one fullscreen window per pipe, and a
single output channel per window.

Note that one node is special: the application node, which is the node that
you initially start (the other nodes are started automatically by Equalizer).
The application node is called 'appNode' in the Equalizer configuration, and
Bino will play audio only on the application node. All video output is then
synchronized to this audio output.

Once you have your configuration file (examples are given below), you can
check if it works correctly using the eqHello example:
$ eqHello --eq-config configuration.eqc

Once you made sure that this works, you can start Bino using this command:
$ bino -o equalizer --eq-config configuration.eqc video.mp4

Note that all your nodes need access to the video file using the same name, so
a shared filesystem is helpful if you use multiple systems.

To play live video from a webcam or TV card, you can set up a streaming server
using ffserver (part of FFmpeg) or vlc, and then give the appropriate URL to
Bino. You can use multicast to stream the video to multiple systems
efficiently.

The ouput mode '-o equalizer-3d' allows to configure non-planar projections.
Bino projects the video onto a virtual screen in 3D space. The screen is
located in the distance of the biggest front-facing segment, and sized to fill
the wall optimally. By configuring the output segments accordingly, various
advanced display configurations can be used, e.g. displays rotated around the
Z axis by an arbitrary angle or non-planar screens.


Example Configurations
======================

1. Simple 2D video output
-------------------------

In this example, you have a 2m x 1m screen and two projectors: one for the
left half of the screen, and one for the right half. The two projectors are
connected to two graphics cards on the same system.

In this situation, you have one node with two pipes, and each pipe has a
fullscreen window with a single output channel. The first output channel is
assigned to the left segment, and the second output channel is assigned to the
right channel. The resulting configuration looks like this:

server
{
    config
    {
	appNode
        {
	    pipe { device 0 window { attributes { hint_fullscreen ON } channel { name "left" }}}
	    pipe { device 1 window { attributes { hint_fullscreen ON } channel { name "right" }}}
	}
        observer {}
        layout { view { observer 0 }}
        canvas
        {
            layout 0
            wall
            {
                bottom_left  [ 0.0  0.0 -1 ]
                bottom_right [ 2.0  0.0 -1 ]
                top_left     [ 0.0  1.0 -1 ]
            }
            segment { channel "left" viewport [ 0.0 0.0 0.5 1.0 ] }
            segment { channel "right" viewport [ 0.5 0.0 0.5 1.0 ] }
        }
	compound
        {
            compound { channel ( view 0 segment 0 ) swapbarrier {} }
            compound { channel ( view 0 segment 1 ) swapbarrier {} }
        }
    }
}

2. 3D video output across multiple systems
------------------------------------------

In the following example, you have a 4m x 3m screen for 3D projection via
passive stereo (e.g. polarization). You have two systems, "render1" and
"render2", each equipped with two graphics cards. The two cards on "render1"
generate two images for the left half of the screen: one for the left eye view
and one for the right eye view. The two cards on "render2" generate left and
right view for the right half of the screen. Additionally, you have a system
called "master" which has a sound card and should display a small control
window.

This setup is very similar to the situation shown in multi-display-vrlab.jpg.

The configuration looks like this:

server
{
    connection { hostname "master" }
    config
    {
	appNode
        {
            connection { hostname "master" }
	    pipe { window { viewport [ 100 100 400 300 ] channel { name "control" }}}
	}
	node
	{
            connection { hostname "render1" }
	    pipe { device 0 window { attributes { hint_fullscreen ON } channel { name "render1left" }}}
	    pipe { device 1 window { attributes { hint_fullscreen ON } channel { name "render1right" }}}
	}
	node
	{
            connection { hostname "render2" }
	    pipe { device 0 window { attributes { hint_fullscreen ON } channel { name "render2left" }}}
	    pipe { device 1 window { attributes { hint_fullscreen ON } channel { name "render2right" }}}
	}
        observer {}
        layout { view { observer 0 }}
        canvas
        {
            layout 0
            wall
            {
                bottom_left  [ 0.0 0.0 -1 ]
                bottom_right [ 4.0 0.0 -1 ]
                top_left     [ 0.0 3.0 -1 ]
            }
            segment { channel "render1left"  viewport [ 0.0 0.0 0.5 1.0 ] }
            segment { channel "render1right" viewport [ 0.0 0.0 0.5 1.0 ] }
            segment { channel "render2left"  viewport [ 0.5 0.0 0.5 1.0 ] }
            segment { channel "render2right" viewport [ 0.5 0.0 0.5 1.0 ] }
	    segment { channel "control"    viewport [ 0.0 0.0 1.0 1.0 ] }
        }
	compound
        {
            compound { eye [ LEFT  ] channel ( view 0 segment 0 ) swapbarrier {} }
            compound { eye [ RIGHT ] channel ( view 0 segment 1 ) swapbarrier {} }
            compound { eye [ LEFT  ] channel ( view 0 segment 2 ) swapbarrier {} }
            compound { eye [ RIGHT ] channel ( view 0 segment 3 ) swapbarrier {} }
            compound {               channel ( view 0 segment 4 ) swapbarrier {} }
        }
    }
}

3. Non-planar Displays (equalizer-3d mode)
------------------------------------------

The 'equalizer-3d' mode allows to set up arbitrary-oriented screens using
either the wall-based or projection-based 3D frustum descriptions.

In this example we set up two 16:10 displays side-by-side which have been
rotated around their Z axis by 1.3 degrees radians (~74 degrees).  The image
multi-display-rotated.jpg illustrates this setup. Other setups include
distortion-correct projection for curved screens, or arbitrarily-placed
screens in a 3D space.

First, we rotate a normally-aligned screen by 1.3 degrees and output the
result:

    eq::Matrix4f matrix(eq::Matrix4f::IDENTITY);
    matrix.rotate(1.3f, eq::Vector3f::FORWARD);
    wall.bottomLeft = matrix * wall.bottomLeft;
    wall.bottomRight = matrix * wall.bottomRight;
    wall.topLeft = matrix * wall.topLeft;
    std::cout << wall << std::endl;

yields a rotated screen centered on the origin:

    bottom_left  [   -0.69578     0.6371         -1 ]
    bottom_right [   -0.26778    -0.9046         -1 ]
    top_left     [    0.26778     0.9046         -1 ]

This screen has to be moved along the X-axis for the left and right screen by
0.5195m, which places the edges of the screen on the origin. The resulting
wall descriptions are used for the left and right segment, as shown in the
configuration below.

The configuration references two GPUs full-screen output. By changing the node
resource section, the ouputs may be mapped to two computers. When disabling
the fullscreen mode and setting 'device 0' for the second pipe, two windows
simulate this setup on a single machine.

global
{
    EQ_WINDOW_IATTR_HINT_FULLSCREEN ON
}

server
{
    config
    {
        appNode
        {
            pipe
            {
                device 0
                window
                {
                    viewport [ .215 .5 .4 .4 ]
                    channel { name "channel1" }
                }
            }
            pipe
            {
                device 1
                window
                {
                    viewport [ .285 .1 .4 .4 ]
                    attributes{ hint_drawable window }
                    channel { name "channel2" }
                }
            }
        }

        layout { view{ }}
        canvas
        {
            layout   0

            segment
            {
                channel  "channel1"
                wall
                {
                    bottom_left  [   -1.21528     0.6371         -1 ]
                    bottom_right [   -0.78728    -0.9046         -1 ]
                    top_left     [   -0.25172     0.9046         -1 ]
                }
            }
            segment
            {
                channel  "channel2"
                wall
                {
                    bottom_left  [   -0.17628     0.6371         -1 ]
                    bottom_right [    0.25172    -0.9046         -1 ]
                    top_left     [    0.78728     0.9046         -1 ]
                }
            }
        }

        compound
        {
            compound
            {
                channel ( segment 0 )
                swapbarrier{}
            }
            compound
            {
                channel ( segment 1 )
                swapbarrier{}
            }
        }
    }
}
