Contrary to Windows, the X11 video mode setup is slightly more complicated thanks to a number of configuration options available to the user. This page here is supposed to collect information about the options to set different video modes, including code snippets and so on, to get a working windowed and fullscreen X11 mode setup working for NeL.
With cheap TFT screens and most graphics cards offering 2+ CRTCs (these things which create the video signals), dual screen setups become more and more common, and should be at least supported in the way that NeL recognizes such setups and knows how to handle fullscreen modes with such setups. A feature use is support of multiple OpenGL contexts, so games made with NeL can make use of all the monitors.
Extensions which can be used
- XF86VidMode, allows setting of CRTC settings, without touching the underlying framebuffer size. Panning for modes where CRTC settings are smaller than framebuffer size. What happens for CRTC settings beyond the framebuffer size?
- Xinerama, which maps an X screen into multiple virtual screens. That means your multiple monitors act like views to the same X screen, but possibly different locations in it.
- XRandR, which from version 1.2 on replaces both XF86VidMode and Xinerama. According to it's documentation to be prefered over XF86VidMode and Xinerama, if available. Allows for independent settings of CRTCs and framebuffer size(s).
- GLX, being the only mandantory extension to run NeL3D based applications in X at all. It's the interface between OpenGL and X, providing the OpenGL context.
Things to take care of
- Conflicting terms: Xinerama as well as X define the term "screen", which has slightly different meanings for each. XRandR defines "CRTC"s, these things which actually produce the signals for the "output"s like CRTs, TFTs or even TV screens. From here on "screen" will refer to a rectangle part of a framebuffer, which is usually shown on an output device of some sort.
- Not all screens are GLX enabled. The method to detect screens and such has to take care of that and only return the screens which actually can handle GLX. Note: nVidia returns GL/X being disabled, yet
glxgearsruns just fine on it. Broken? - There should be a way to have the user define exactly where to position NeL windows, for both windowed and fullscreen case.
- Let the window manager to decide about the window placement, but make sure that it goes to the screen which has GLX enabled.
- There is maybe no need to handle screens beyond Xinerama, the user can define the screen placement via the DISPLAY environment variable (e.g. :0.1 for screen #1, instead of :0.0). Note: For multi-context support we need to handle DISPLAY on our own, eventually.
- What to do if no user config exists and we have multiple GLX enabled screens thanks to Xinerama? Pick the first? (Xinerama doesn't allow for different screen depths, so it's probably useless to bother about it, the real problem are fullscreen resolutions which the user either want on the biggest, smallest or whatever screen ... move that to game config maybe?).
- Different versions of the extensions. For example, XRandR added "native" multi-screen support with 1.2, lower versions probably have to rely on Xinerama.
- Bugs. I've stumbled into a number of weird bugs in relation to the nVidia driver.
Coding hints
- Availability of headers / libs for extensions has to be done with CMake
- Usage of extensions has to be done in the code, so we have kind of 2 guards around each extension access: (a)
#ifdef's to include/exclude code and (b)if's to cover the actual usage of an extension. Note: I think the#ifdefpart will be skipped.
A prototype
- Code can be accessed via SVN: http://nel.svn.sourceforge.net/svnroot/nel/personal/spex/fullscreentest
I didn't save the chat logs; anyway, I decided to go all the way for the fullscreen mode detection and mode switching instead of doing it in a "barely runs" kind of way. This will result in something "bigger". To see if it works, how it works, and if all the ideas surrounding the actual mode detection and switching works out, the decision to create a prototype for this has been made. If this prototype works and the underwent the code review, the relevant code will be moved into the NeL OpenGL driver.
Following immediate goals were planned for the prototype:
- The different ways to handle fullscreen support should be modularized.
- The modules should offer some way of configurability. That way, if the application programmers take care of it, will allow users to mess with the fullscreen handling a bit, in case they run into problems/bugs/this kinda stuff.
- The already existing
GfxModeclass (actually struct) is going to be used, but may have to get a couple additional attributes. - User interface via Qt. The prototype will not rely on a really complicated user interface, but it can be used to get a first look at Qt and maybe check into the Qt+OpenGL integration, which will prove to be useful later on (see below).
... and after that
In a second step, the NeL OpenGL driver should be split from everything OpenGL context related; that includes the actual context setup but also the GUI system specific handling, like creating windows, fullscreen mode switching and eventually input handling. If this split will actually go "deep" or just in how the code is organized is left open to discussion, still. However, it would allow a faster adaption to new GUI systems / ways of how to obtain an OpenGL context, i.e. we still lack a native MacOSX solution for this. Also, this "framework" could be used in new OpenGL drivers, which is a strong point for a rather clean cut between OpenGL driver and context setup framework.
Together with the splitup another very important feature is waiting to get included: Copy&Paste. Games which have chat functionality really should support Copy&Paste with the underlying system, mainly used for URLs, but also other means.
dlopen() versus compile time linking
There was a lengthy discussion about the problem of either linking the X11 extensions to the OpenGL driver or using dlopen() to access these.
The advantage of the linking case is obvious: All the overhead added by dlopen() just falls flat. In return it adds dependencies which need to be resolved, otherwise the application will not even start (to be more precise: the OpenGL driver module will fail to load). Modern Desktop Linux systems most probably have all these extension libraries installed; I can't say anything about other Unix systems, though.
For dlopen() speaks the runtime dependency, and the fact that if loading an extension library fails, that you can either work around it (i.e. Xinerama is missing) or just disable the screen mode "plugin" (which may result in a completely disabled screen mode selection). Downside is the apparent coding overhead introduced by this way of accessing libraries.
The decision is set towards the dlopen() case, and the current prototype has this feature included already.
ModeManager's
ModeManager's encapsulate the different ways of how to read and set mode information. One of them has been implmented already, the others are still "work in progress":
- Xinerama + XF86VidMode (most of it is done, needs some more configuration possibilities)
- XRandR, although I'm thinking about adding Xinerama to that one, too (at least if XRandR is <v1.2)
- "fake" manager, which just returns user specified coordinates for the "fullscreen" windows.
Aspect "plugins"
While reading through documentation to the NV-CONTROL X11 extension (nVidia propietary driver specific extension), the idea to have something like a ModeManager crosscutting "aspect" applied to them, so you can use whatever ModeManager you like, and still have special abilities of the gfx card available (i.e. Antialiasing settings, enforced sync on vertical retrace and so on).
Maybe these things don't need to have anything to do with ModeManager}}s, but they are at least related to a certain degree, beside the {{GfxMode struct contains one of these settings already (antialiasing).
Xext wrappers
Following the line of thought for "aspect plugins", I stumbled over the problem of having to use certain X11 extensions in different ModeManager s. To save writing the same code over and over, the idea for C++ wrapped X11 extensions was born, bound together by The One Ring, err, a extension wrapper factory/manager. Each extension wrapper and each ModeManager will have access to this factory/manager, bypassing the need to create a singleton (phew).
struct GfxMode changes
Thus who took a deeper dive into the graphics code of NeL3D probably stumbled over it already: The GfxMode struct (= class). It will need additional attributes to cover the required information:
Userdata
By design of this struct, screen modes are defined by their resolution and (computed) vertical refresh rate. The problem about this approach is, that everytime a mode has to be set, the full list of available modes needs to be retrieved again, because GfxMode doesn't contain the "native" mode information. Userdata will allow to reference such information from within the GfxMode struct.
ModeManager reference
GfxMode structs are provided by the mode switcher "plugins". To know which "plugin" is responsible for handling the data of a specific GfxMode, a reference to the respective "plugin" needs to be kept. In the prototype this attribute is called "manager".
Screen
The screen we are refering to. Screen numbering is somewhat of an issue, because different extensions may number/order them differently. This problem is not yet covered.
Position of the screen
With Xinerama, but maybe also with XRandR, screens may have positions on the "virtual" framebuffer distinct from (0,0). This information is stored in the "screen position" attributes (i.e. screen_pos_x and screen_pos_y or something along that line). The prototype doesn't contain these attributes, yet.
Implementation problems
- nVidia with their "remarkable driver" (quote int-e/bf) managed to mess up their "TwinView" mode seriously:
int-e took the time to look up the "Value in failed request" (which obviously should be a 1, because
GLX extension supported. version 1.3 RANDR extension supported. version 1.2 XINERAMA extension supported. version 1.1 XINERAMA is active. XVidMode extension supported. version 2.2 found Xinerama screen 0: screenNum 0, size 1600x1200, at 0,0 Screen #0, Mode 0: width 2880 height 1200 freq 85 # validated: -2 XRandR info Screen #0, size 0: width 2880 height 1200 rate 50 resource with 1 crtcs, 1 outputs and 1 modes crtc #0 id 429: 2880 x 1200 @ (0, 0), mode 431, with 1 outputs: 430 output #0 id 430: 'default' supported modes: 431 mode #0 id 431: 2880 x 1200 @ 50.00 '2880x1200' screen #0 supports GLX found Xinerama screen 1: screenNum 1, size 1280x1024, at 1600,0 X Error of failed request: BadValue (integer parameter out of range for operation) Major opcode of failed request: 134 (XFree86-VidModeExtension) Minor opcode of failed request: 6 (XF86VidModeGetAllModeLines) Value in failed request: 0x1a6 Serial number of failed request: 37 Current serial number in output stream: 37XF86VidModeGetAllModeLinesgot called there with screenNum=1 out of the Xinerama information (which is btw. the only extension returning correct information, having a 1600x1200 TFT and a 1280x1024 CRT right of it). Also, the XRandR refresh rate informations are totally odd (my CRT runs at 85 Hz usually, xrandr returns 50 Hz for it, similar to the 50 Hz shown above). Way to go, nVidia! - nVidia also returns weird XF86-XVidMode modes, some of them with like 200+ Hz refresh rates. After checking the values more closely it turns out the total amount of vertical lines is like half of what it should be ... interlaced modes?
- The XRandR implementation requires for a halfway correct implementation access to X11 events. The X11 event loop is handled in (
unix_event_emitter.cpp) already, but doesn't really offer any generic way to "plug" own event handlers into it.