At ArrayFire, we constantly encounter projects that require OpenGL and run on a remote server that does not have a display.
In this blog, we have compiled a list of steps that users can use to run full profile OpenGL applications over SSH on remote systems without a display.

A few notes before we get started.
<ul>
	<li>The first part of the blog that shows the configuration of the xorg.conf file is limited to NVIDIA cards (with display).</li>
	<li>AMD cards support this capability without the modification of xorg.conf file. However, we have not been able to get a comprehensive list of supported devices.</li>
</ul>

<strong>Requirements</strong>:
You will need access to the remote system over SSH.
To run the tool, you will need libGL.so, libX11.so. Another tool I would recommend strongly is glewinfo. Most linux distributions ship this with the glew-utils package. An alternate to glewinfo is glxinfo which is present on all systems with X. You can substitute glewinfo with glxinfo in all the commands below if needed.

<strong>Part 1: Setting up X (NVIDIA Only)</strong>
To get X to running on NVIDIA cards, we need to make a changes to the xorg.conf file. Before making the change, make sure you create a back up of the current version on your system (name it xorg.conf.stable).
You can find a sample xorg.conf file <a href="https://github.com/arrayfire/glContext/blob/master/xorg.conf" title="xorg.conf" target="_blank">here</a>. The sample file is for a NVIDIA GTX 690. The specific things to notice are the use of "UseDisplayDevice" option under "Screen" and the "Virtual" option under "SubSection Display". You can use parts of this file to configure your own config file. Make sure the options listed in the sample file get listed in your config file as well.
Save and close the file.

Now run this command: <code># nvidia-xconfig -a --use-display-device=None --virtual=1280x1024</code>
Restart the system.

<strong>Note:</strong> To abort trying this, just copy xorg.conf.stable back to xorg.conf and restart.

<strong>Part 2: Start X (NVIDIA GeForce Only)</strong>

The following is only for GeForce cards. Quadro and Tesla cards can skip to Part 3.

On restart, run the command <code># /usr/bin/X :0 &</code>. Ideally, this command should give an output similar to:
<pre>
X.Org X Server 1.13.0
Release Date: 2012-09-05
X Protocol Version 11, Revision 0

<Some more output here>

Loading extension GLX
Loading extension NV-GLX
Loading extension NV-CONTROL
</pre>

This means X has started successfully on the virtual display.
If it fails, restart the system and try again. You can also look at /var/log/Xorg.0.log for the log of the failure.

<strong>Part 3: Initial Diagnosis</strong>
Once X has started successfully, run <code>echo $DISPLAY</code>. It is very likely that the output of this will be empty.
If you have glewinfo installed, run the following command <code>env DISPLAY=:0 glewinfo | less</code>.
The goal of this command is to run glewinfo having temporarily set DISPLAY to :0 (virtual display on remote system). If this command run's successfully, you should be able to see the graphics card on the remote system along with the full OpenGL profile. And now you are ready to deploy applications using X.
If this command fails for Quadro and Tesla cards, run the section in Part 2.

If you want to set DISPLAY for the entire session, run <code>export DISPLAY=:0</code>

<strong>Part 4: Deploying Off-Screen Rendering Applications on Remote System</strong>
This is where X becomes crucial. Tools like GLFW may or may not work on remote systems because of their dependence on Xrandr and other software. The trick is use X to create an OpenGL context, and the run everything using off-screen rendering using framebuffers and renderbuffers. I took the source to create an OpenGL context using X from <a href="http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)" title="OpenGL Context Creation Page" target="_blank"></a> and modified it slighly. Thanks to the folks at OpenGL.org for providing this. The source code with the changes I made can be found here: <a href="https://github.com/arrayfire/glContext/blob/master/glContext.hpp" target="_blank">glContext.hpp</a>.

Include this in your source code. To create context run createGLContext() and to delete the context run deleteGLContext().

I have specified that the minimum OpenGL version should be 4.4 with forward context enabled. You can modify this version by changing the values at lines 26 and 27.
The forward compatibility can be disabled at line 241 by changing line 241 to None.
If the application fails to create the specified version of the context, the application will exit.

At line 189, we create the window. The API description can be found <a href="http://tronche.com/gui/x/xlib/window/XCreateWindow.html" title="link" target="_blank">here</a>. "0, 0, 10, 10" specifies the top-left corner (0,0) and the width and height of the window (10, 10). Since the goal of this is to be used for off-screen rendering, the size of the window has no effect on the rendering.
Line 202 specifies the window title.

If everything goes successfully, you should see an output like this:
<pre>
glContext.hpp:297: GL Version  = 4.4.0 NVIDIA 331.79
glContext.hpp:298: GL Vendor   = NVIDIA Corporation
glContext.hpp:299: GL Renderer = GeForce GTX 690/PCIe/SSE2
glContext.hpp:300: GL Shader   = 4.40 NVIDIA via Cg compiler
</pre>

If you wish to use a lower minimum version, say 3.0, then the version provided by the output will reflect 3.0 since GL fetches version from the context.

To test the context creation as a stand alone, create a cpp file with the following contents (<a href="https://github.com/arrayfire/glContext/blob/master/glContext.cpp" title="glContext.cpp" target="_blank">glContext.cpp</a>):
<pre>
#include "glContext.hpp"

int main(int argc, char* argv[])
{
    createGLContext();
    deleteGLContext();
    return 0;
}
</pre>

Compile this with <strong>g++ -o gl glContext.cpp -lGL -lX11</strong> and run with <strong>./gl</strong> (make sure DISPLAY is :0). If this works successfully, any off-screen rendering code will work perfectly with this.
<strong>Note:</strong> If you use GLEW, make sure you include glew.h before including gl.h.

If you wish to know about remote OpenGL further or work with us on remote rendering, contact us at <a href="mailto:technical@arrayfire.com" >technical@arrayfire.com</a>.

Links:
OpenGL Context Creation Tutorial: <a href="http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)" title="http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)" target="_blank">http://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)</a>
ArrayFire glContext repo on Github: <a href="https://github.com/arrayfire/glContext" title="https://github.com/arrayfire/glContext" target="_blank">https://github.com/arrayfire/glContext</a>