FlowVR-render proposes an advanced framework for distributed rendering. FlowVR-render is built on top of FlowVR. It is based on viewer modules that send primitives to renderer modules using a specific protocol. Viewers describe the graphics properties of objects to be rendered while renderers are in charge of computing these graphical representations.
By analogy to OpenGL, a viewer is similar to an OpenGL client application while the renderer takes the role of the OpenGL graphics-card driver. However, there are several important differences:
A scene is built from one or several viewer modules that are in charge of issuing primitives to the renderers. Usually a viewer is dedicated to one type of objects. To add a new object in a scene, it just requires to add the corresponding viewer to the application.
Viewers can run asynchronously, using FlowVR greedy patterns, enabling to include in the same scene objects that are not updated at the same rate.
Each primitive contains the description of a batch of polygons (points, lignes or triangles) and their rendering parameters (color, position, etc.). It uses resources like shaders, coordinates and other attributes via a vertex buffer, element indices via an index buffer and optionnally one or several textures.
Each primitive also stores its bounding box. This information is required to optimize communications using frustum culling. It is useful to specify this information in the primitive as it is costly to recover from the other parameters (especially when using shaders that can change vertex positions).
A name can be specified for each primitive for identification in error messages, visual selection feedback, or filters based on name patterns.
Some rendering algorithms require primitives to be processed in a given order. FlowVR Render provides a mechanism to enforce this ordering by associating each primitive with an order value. This number defines a partial ordering between primitives. A primitive with a lower order value must be rendered before a primitive with a higher value. Primitives with the same value can be rendered in any order. Different values will mainly be used when rendering order can affect the final image like for transparent primitives or user-interface overlays. For a given order value, renderers can re-order primitives to improve performance. For instance, primitives can be sorted front-to-back to take advantage of fast Z-culling, primitives with similar parameters such as textures and shaders can be gathered to reduce state switching overheads.
Large datasets such as textures or vertex attributes are often used by several primitives in the scene, sometimes across several viewers. To avoid duplicating them in each primitive we define resources. A resource can encapsulate a shader, texture, vertex buffer or index buffer. It is identified by an unique ID. Each primitive refers to the IDs of all the resources it uses. Notice that it introduces a one-way dependency between primitives and resources, but not between primitives.
For a viewer, many of the parameters do not change from one iteration to the other. To avoid re-issuing primitives that did not changed, the protocol relies on a retained like mode. Renderers maintain the current description of the scene, and viewers describe the changes to this description. Each change is encapsulated into a chunk. At each frame, a viewer sends one message containing a list of chunks. A chunk can correspond to a:
This protocol is purely one-way, from viewers to renderers. In particular, IDs are generated by the viewers and not the renderers. This property is useful when the viewers and renderers are not tightly synchronized (for example storing messages on disk and replaying them later).
The renderer is a FlowVR metamodule called flowvr-renderer. It comes with its .desc file: desc/flowvr-renderer.desc.xml.
This metamodule can spawn several renderer that can be synchronized (genlock, datalock).
Users should not have to modify the renderer.
A renderer opens an OpenGL windows where it renders primitives according to the messages received from viewers on its input port scene.
Notice that with NVIDIA graphics cards, it is possible to activate the antialiasing (usefull for screen shots or videos) using the nvidia-settings utility or setting the environment variable __GL_FSAA_MODE to 4 or 5.
When the rendering windows get the focus, flowvr-renderer can capture the mouse and keyboard events. We list below the different behaviors that some keys enable to control.
The F1 key enables to switch between the following different modes:
If no viewer control the camera (the camera is a special resource ), it can be controlled from the keyboard and mouse:
Other active keys are:
Several command line arguments enable to control the behavior of the renderer. To get the list of the ones available tape in:
In particular it enables to control the background (-black, -white and -b background.png), to tell the renderer to record a video (-video), and specify the display configuration, which can either be a file or a direct specification of the window size. The following section will detail this.
When FlowVR Render is used in a distributed rendering setup, where several instances are rendering a part of the image, it is necessary to specify which instance render which part, in other word what is the view frustum of each renderer instance.
If the screen setup is a simple tiling of each image in a grid without overlapping, as shown in figure , then the dimension of each screen as well as the number of rows and columns as @resxxresy[@nxxny[@aspect]], where resx and resx is the resolution of each screen, nx and ny the number of screens horizontally and vertically (defaulting to 1x1, i.e. a single screen), and optionally aspect the aspect ratio of the whole display area (only necessary of the pixels are not squares).
For other more generic configurations, a file is used to describe the parameters of each renderer. This description is much more powerful, as it can describe arbitrary screen transformations, as well as blending masks for smooth overlaps, and stereo setups. This file only contains general parameters and points to external files storing the parameters (view frustum transform, blending mask) for each renderer. The file is formated as a simple text file, each line containing the name of a parameter followed by its value. The following description is geared toward a display-wall setup, where several projectors, each controlled by a renderer, are projecting a small image on a big common screen. We use two coordinate systems: the local projector-space, corresponding to the pixels of the image of each projector, and the global screen-space, which is where the final complete image is specified.
The parameters in the display configuration file are:
After the global parameters, the file contain a line for each renderer instance with the following format: hostname win_x0 win_y0 win_nx win_ny view, where hostname identifies the renderer instance, win_* specify the corrdinate of the rendering window, and view contains the eye of this view (L or R) and the projector row and column number (yxx). So for example for a 2x2 non-stereo display wall, the top-left projector would be L0x0, the top-right L0x1, the bottom-left L1x1, and the bottom-right L1x1.
The view specification is used to find the files describing the projector's transform matrix, multiplicative and additive masks. The file format patterns are:
These files are not intended to be created by hand but instead to be created procedurally, either from a high-level description of the setup, or from measurements (i.e. calibration data of the projectors). In the base FlowVR Render distribution, the flowvr-gendispconfig is provided to generate configuration of display-walls with projectors aligned on a grid with a specific vertical and horizontal overlap between projectors. Its command line format is :
A renderer has the following output ports:
A renderer has the following output ports:
Multiple renderers can be synchronized for driving multi-projector or multi-display environments.
Datalock need to be ensured for the keyboard and mouse events.
Actually, launching one flowvr-renderer metamodule starts two modules (two intermingled loops running in the same process). The rendering module, called render, and a swaplock module, called swaplock . This last module locks the buffer swap to the occurence of a beginIt event (only when this port is active, i.e. connected).
When swaplock is not required (i.e. a single display, or multiple separate displays) or when it is directly handled by the graphics cards then this module is not used.
To create a classical swaplock setup where all renderers are synchronized, then a barrier must link all the swaplock modules. Assuming the Visu is the flowvr-renderer metamodule, then the following perl code will create the necessary connections:
As another example it is possible to link all renderers to a fixed frequency filter to control their refresh rate:
Building upon these examples it is possible to design applications with different swaplock groups and controllable update frequencies
Swaplock does make sense only if it is associated with a proper datalock, meaning that all renderers should render the same data at each frame. Datalock usually depends on the application, but some important considerations on the renderer level must be taken care of. Three data streams are used by renderers:
To resolve the first two all renderers' dt and keysmouse input ports must be connected to the same source. This source can for example be the events from the first renderer's window:
The last data stream is the scene itself. For this case many different schemes can be envisionned depending on the application. There is however two cases that are the most common and from which most other schemes can be constructed:
The first case is handled in src/perl/render-fifo.pl. The basic idea is to use a Merge filter on each renderer to collect messages broadcasted from all viewers. The datalock is handled implicitely as all connections are FIFO, hence all renderer with receive the same messages at each frame.
The second case is handled in src/perl/render-greedy.pl. The basic idea is the same as previously, except a coherent greedy is inserted before the merge filter. This Greedy synchronizor will decide which messages should be forwarded to the renderer, and this decision is unique for all renderers to ensure proper datalock.
A viewer is a FlowVR metamodule with a specific scene output port. To ease programming a viewer, like declaring the scene, preparing chunks and changing primitives' parameters, FlowVR Render provides several classes that we detail in the following. FlowVR Render also comes with some basic viewers that we found useful for many applications.
Each viewer should have its FlowVR .desc file. In particular this .desc should include the scene output port. See the share/flowvr-render/examples/something
FlowVR Render comes with several basic viewers located in src/viewers for the source code and modules/desc for their .desc files. Refer to these directories to get the full list of available viewers. The main ones are:
We present in the following the FlowVR Render specific functions to program a viewer. We do not detail the classical FlowVR functions for metamodule programming. Refer to theFlowVR documentation if required.
A viewer usually follows the following steps:
Notice it is adviced to set the shader primitives first, as most of the resources depend on them.
The doc/html/chunkwriter_8h-source.html file (source code at include/render/chunkwriter.h) contains most of the functions specific to viewer programming.
![]() |
A viewer is a FlowVR metamodule with a specific scene output port that can be declared as follow:
Beside the default stamps defined for all metamodule messages, a viewer declare a scratch stamp for its scene port. It has a 1 or 0 value depending if the message contains chunks or not.
IDs for resources and primitives are generated using the following function from flowvr-base/include/moduleapi.h:
This function ensures the ID returned is unique through the whole application.
The camera has a predefined ID ID_CAMERA.
A viewer loads its data files (shaders, buffers, textures) :
By default FLOWVR_DATA_PATH gives access to the data directory . Update this variable value, if you create new data repositories.
To create a resource:
To load a resource from a file:
To generate a resource in a procedural way:
To update a resource:
To suppress a resource:
It is also possible to save a texture to a file with:
For instance the sphere1.cpp example only set a mesh for the sphere using:
The sphere2.cpp example additionally loads a texture from the file given in argument (set in the examplles/sphere2/net/sphere2.mml.xml file) or a default texture otherwise (a chessboard):
To create a primitive:
To delete a primitive:
To move or rotate a primitive:
Both sphere examples just declare one primitive:
The sphere2.cpp example uses rotatePrimitive to rotate the sphere at each iteration by an angle of it degrees around the Vec3f(0,0,1) axis (default rotation axis):
There are 4 functions for handling the parameters of a primitive depending on the type of parameter:
These functions take as parameters:
The sphere2.cpp example uses this parameter to add the previously loaded vertex shader to the sphere primitive:
The sphere2.cpp example uses this parameter to add the previously loaded pixel shader to the sphere primitive:
Both sphere examples example uses this parameter to :
The sphere2.cpp example uses this parameter to :
Both sphere examples example uses this parameter to :
The sphere2.cpp example uses this parameter to :
Both sphere examples example uses this parameter to add the previously loaded vertex buffer (sphere mesh) to the sphere primitive:
The sphere2.cpp example uses this parameter to :
All Cg uniform parameters (see http://developer.nvidia.com/object/cg_toolkit.html) of a shader can be controlled through the PARAMVSHADER or PARAMPSHADER parameter type.
Both examples
The sphere2.cpp example additionally use the mapscale from the vertex shader used to modify texture scaling. It enables to repeat twice the texture on the sphere if it is closed to a square texture (otherwize strong distortions would appear):
All primitives expose the following parameters that can be controlled through the parameter type PARAMOPENGL.
Notice that names and values are case-sensitives.
|
Once all set the message can be sent using the ChunkWriter::put function with the SceneOutputPort as parameter.
All examples are gathered into the examples directory. Go into this directory to get an up-to-date list.
To compile example go into the corresponding directory and execute:
To run an example on a single machine, launch a FlowVR daemon:
These simple examples are detailed into previous sections. Coming with the sphere2 example, another Makefile (share/flowvr-render/examples/sphere2/Makefile-multi.mak) allows the user to display this example among several windows. Only the network description file changes (share/flowvr-render/examples/sphere2/net/sphere2-multi.mml.xml).
A coupling between a particule simulation and a point viewer. The main difference between particles1 and 2 is that instead of display a pixel for each particle, it displays a textured sprite. This is done by the mean of another pixel and vertex shaders (see share/flowvr-render/examples/particles2, share/flowvr-render/data/shaders/sprite_p.cg and share/flowvr-render/data/shaders/sprite_v.cg)
It also features blending between particles.
These 2 examples uses a ray-casting shader for volume rendering. The volume1 example uses the share/flowvr-render/data/shaders/volume_p.cg based on a volume function taking a position parameter and returning the color of the volume at this position. In this example the function returne a constant value. The fire example the value returned is based on a noise and a flame texture (share/flowvr-render/data/shaders/volume_fire_p.cg).
These examples are based on the NVIDIA presentation http://download.nvidia.com/developer/presentations/2005/GDC/Sponsored_Day/GDC_2005_VolumeRenderingForGames.pdf
2009-04-06