DRI with X.org on multiple graphic adapters

Justus Winter 4winter@informatik.uni-hamburg.de

Preface

Motivation

As of the time of this writing (2006-04-05) X.org does not support DRI on multiple graphic adapters using the multi-head capabilities.

Unfortunately enabling xinerama, my favorite hot X feature, completely disables DRI. It is possible using a dual head graphic adapter and the proprietary driver of either NVIDIA or ATI to get an accelerated xinerama screen, but since I do not own a dual head card, this is not an option.

X.org supports DRI on the primary head on non-xinerama dual-head configurations. I went with this option for a while and enjoyed some games and opengl applications, but I felt betrayed. I paid a high price (disabling xinerama) and got only one accelerated head.

Overview

So I decided to exploit the multi-seat capabilities on recent (X11R6.9 and X11R7.0) X.org releases to offer an accelerated non xinerama multi-head setup.

The idea is to use two accelerated X instances and tie them together using x2x or synergy.

Warning

This document is not for the faint of heart. It requires some knowledge about your favorite operating system and its packet management. But if you are prepared to screw things up, this is definitively a interesting read for you :).

Let's start

Requirements

You will need I did this hack on Ubuntu Linux 6.06, but this is not at all distribution specific. It is not even linux specific, but I am not familiar with DRI on *BSD systems, so you are on your own.

Preparing for multi-seat configuration

Backup your xorg.conf and have this document nearby for reference :).

For each head you need one Monitor section, one Device, one Screen and one ServerLayout section.

Your primary head requires no modification at all, for each secondary head create the following entries:

xorg.conf

Section "Monitor"
	Identifier   "MonitorX"
	VendorName   "Apple"
	ModelName    "whatever"
EndSection

Section "Device"
	Driver      "tdfx"
	VendorName  "3Dfx Interactive, Inc."
	BoardName   "Voodoo 3"
	BusID       "PCI:0:11:0"
EndSection

Section "Screen"
	Identifier "ScreenX"
	Device     "CardX"
	Monitor    "MonitorX"
	DefaultDepth 16
	
	SubSection "Display"
		Depth     16
		Modes       "1024x768" "800x600" "640x480"
	EndSubSection
EndSection

Section "ServerLayout"
	Identifier      "seatX"
	Screen          0 "ScreenX" 0 0
	InputDevice	"VoidKeyboard"
	InputDevice	"VoidPointer"
EndSection
You also need to define one void keyboard and one void pointing device:

void events

Section "InputDevice"
	Identifier	"VoidKeyboard"
	Driver		"void"
	Option		"CoreKeyboard"
EndSection

Section "InputDevice"
	Identifier	"VoidPointer"
	Driver		"void"
	Option		"CorePointer"
EndSection
Using this void keyboard definition I encountered a strange thing. The second head was not receiving certain key scan-codes (I confirmed this using xev). In fact, the only keys working were the alphanumeric keys, space, escape, full stop etc. but things like ctrl, shift and return were missing. This might be a bug in X.org, not sure though. I ended up plugging a spare usb keyboard in and using this as void keyboard:

my spare keyboard

Section "InputDevice"
	Identifier	"VoidKeyboard"
	Driver		"evdev"
	Option		"Device"	"/dev/input/event3"
	Option		"CoreKeyboard"
	Option		"XkbRules"	"xorg"
	Option		"XkbModel"	"pc104"
	Option		"XkbLayout"	"us"
	Option      	"XkbOptions"    "ctrl:nocaps"
EndSection

Testing the multi-seat configuration

Get to your spare computer and login remotely via ssh. You are about to test your configuration and your console will most likely get screwed up in the process.

Start your heads in turn using the command

X -novtswitch -sharevts -layout seatN :0
Okay, your display is scrambled, your keyboard is useless, but all screens are working now and you are still with me? Great!

Glueing em together

Lets write some scripts that start all our heads and connect them using x2x or synergy.

startxms

#!/bin/sh

# startx is nicer, as it creates .Xauthority entries for us, but unfortunately
# its not working for me
COMMAND=xinit

# unfortunately I have to run this script as root, otherwise X bails out
# something about no permissions, which is funny, because X is set{u,g}id root
# so I just start this as root and su to the target user
USER=teythoon

# read about my glx adventures in the next chapter
switchGLX nvidia

$COMMAND /bin/su ${USER} -c /bin/sh /home/${USER}/.xinitrc.0 -- -config 
   xorg.conf.multiseat -novtswitch -sharevts -layout seat0 :0 &

# give it some time to start and switch back to xorg
sleep 10
switchGLX xorg
$COMMAND /bin/su ${USER} -c /bin/sh /home/${USER}/.xinitrc.1 -- -config 
   xorg.conf.multiseat -novtswitch -sharevts -layout seat1 :1 
Okay, what's going on? Essential we just run the X server and ~/.xinitrc.X as client. lets take a look at them.

.xinitrc.0

# this sets up head #0
export DISPLAY=:0
synergys --config ~/.synergy

# glx magic, see below
export LD_LIBRARY_PATH=/usr/lib.nvidia:${LD_LIBRARY_PATH}

# user specific stuff goes here

xscreensaver -no-splash &
# just start the normal xinitrc
. ~/.xinitrc
and

.xinitrc.1

# this sets up head #1
export DISPLAY=:1
synergyc --name second localhost

# user specific stuff goes here

# xscreensaver, conky and a windowmanager
xscreensaver -no-splash &
conky -d
enlightenment
You might want to remove the suid or sgid flag from your terminal to prevent it from clearing the LD_LIBRARY_PATH variable.

As you can see I am using synergy. x2x is even easier to set up, but it has fewer features (ie no screensaver synchronisation which is kind of interesting for this kind of setup).

Synergy also needs a small config file (europa is my hostname):

.synergy

section: screens
   europa:
   second:
end
section: links
   second:
       right = europa
   europa:
       left = second
end
Keep on reading, you are really close now...

GLX extension and libraries

My secondary graphic adapter is a TDFX Voodoo3 2000, direct rendering is provided by a driver in the main linux kernel tree. The GLX extension provided by X.org provides GLX to user application, which can take advantage of the DRI using the GLX client libraries provided by X.org.

My primary graphic adapter is a NVIDIA Geforce2 card, direct rendering is provided by NVIDIA's proprietary driver. Unfortunatly they also provide their own GLX extension for X and their own GLX client libraries.

The GLX extension module

On my system, the X.org extensions reside in /usr/lib/xorg/modules/extensions. In that directory I renamed the X.org extension libglx.so.xorg, copied the NVIDIA extension into that directory as libglx.so.nvidia and wrote that script to switch between them:

switchGLX

#!/bin/bash
cd /usr/lib/xorg/modules/extensions
rm libglx.so
ln -s libglx.so.
Using this script and the startxms script above I start first the X server with the NVIDIA extension, switch to X.org and start the next one.

If there is a more elegant way to tell X which extensions to use please let me know.

The client libraries

On my system the client libraries (libGLcore.so.1, libGL.so.1 and libGLU.so.1) are located in /usr/lib. I decided to let the X.org libs stay there and copied the NVIDIA libraries to /usr/lib.nvidia. Using the LD_LIBRARY_PATH environment variable those apps running on the primary screen now are happily using the NVIDIA libs, whereas those on the secondary screen are using the X.org libraries.

Where are we now?

We got a multihead setup with DRI working on both heads. We exceeded the possibilities of the current X.org code, but it is quite a hack and a lot of work.

Extending the X.org code to allow DRI on multiple heads would be the way to go. The ultimate goal is DRI accelerated xinerama, so if you got some time to waste, head over to X.org and start hacking :).

Downsides

There are a few disadvantages with this kind of setup:

References and Documentation