Graphics card profiles

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.
This article describes content/features that may not yet be available in the latest stable version of FlightGear (2020.3).
You may need to install some extra components, use the latest development (Git) version or even rebuild FlightGear from source, possibly from a custom topic branch using special build settings: .

This feature is scheduled for FlightGear 2016.4. 30}% completed

If you'd like to learn more about getting your own ideas into FlightGear, check out Implementing new features for FlightGear.


This particular idea dates back to early 2016 when a few FlightGear contributors were discussing a reddit review of FlightGear on the forum which highlighted that FlightGear's default graphics settings didn't seem to fit/match the graphics hardware available, i.e. rather plain default settings compared to the powerful graphics card available.


FlightGear core deveoper Erik Hofman came up with the idea to preselect more appropriate default settings based on parsing various GL_* strings (in particular GL_VENDOR and GL_RENDERER), i.e. detect the video card and based on the GL renderer name turn on ALS (or not), turn on water shader (or not), turn on the 3d city shader (or not), etc.

At runtime, that data is already available in the form of properties that are copied to the property tree during initialization.


This approach is largely inspired by the way FlightGear already supports a variety of different joysticks and other I/O hardware using XML-configurable hardware profiles.

The defaults Preferences.xml configuration file would not be touched but based on the detected video card certain options would get enabled, making the first impression much more pleasant (for first time users). [1]

Autosave.xml configuration file would need to override any settings from such a standard gpu profile.

Furthermore, people agreed that it could be a good idea to make sure the configuration has at least 30fps at the default airport.

Also it would be a one time configuration for after a (re)install. The settings would be written to a local configuration afterwards so users could still tweak for their own liking.[2]

Other contributors, like Thorsten mentioned, that it does not seem to be a technical problem - to query a string from the property tree is easy, to write a routine which sets other properties based on what the string says is equally easy.

The problem is the meat of it - someone has to know what settings are likely to run on what graphics cards. And someone has to write the routine that does it. Then it's done. [3]

We can do that by setting /sim/rendering/* as required, i.e. loading pre-configured profiles from $FG_ROOT. For example, $FG_ROOT fgdata/gui/dialogs/about.xml and fgdata/gui/dialogs/rendering.xml are already accessing some of the GL* data via properties, e.g. to display the Intel warning, but also the GPU vendor info:

Thus, we would ideally introduce 3-4 "vendor"-specific folders and then introduce a PropertyList XML file where the RENDERER string is used to select a certain default configuration. For testing purposes, we could promote this features on the forum/website/IRC and facebook to get people involved with testing, and to provide feedback - so that we know what settings work well enough, using Nasal's http APIs, such feedback could even be gathered online. [4]

However, CPU/RAM and VRAM info is not currently available, but we have patches providing this sort of info [5]

  1. erik (Feb 7th, 2016). Re: Review of FG on reddit: xpost.
  2. erik (Feb 8th, 2016). Re: Review of FG on reddit: xpost.
  3. Thorsten (Feb 7th, 2016). Re: Review of FG on reddit: xpost.
  4. Hooray (Feb 8th, 2016). Re: Review of FG on reddit: xpost.
  5. Hooray (Feb 7th, 2016). Re: Review of FG on reddit: xpost.


At runtime, we are already running some heuristics to make certain features available selectively, or to enable end-users to provide better troubleshooting information:

      var vendor = getprop("/sim/rendering/gl-vendor");
      if (vendor != nil) {
        vendor =;
        if (find("intel", vendor) != -1) {
          setprop("/sim/gui/dialogs/rendering/shader-warning", 1);
        } else {
          setprop("/sim/gui/dialogs/rendering/shader-warning", 0);


It's basically a form of feature-scaling: Feature Scaling

We could expose most of the GL* info at the property tree level, and then use Nasal/propertylist rules (and/or SGConditions) to dynamically toggle features on/off during startup, i.e. sort of a better customiable initialization sequence.

The easiest way to implement something like this would be supporting "data overlays", i.e. in the form of fgfsrc/preferences.xml files that are stored in $FG_ROOT/Profiles and automatically overlaid over default startup options once a certain GL vendor/make and GPU model are detected, e.g. by having:

  • $FG_ROOT/GraphicsProfiles/NVIDIA
  • $FG_ROOT/GraphicsProfiles/AMD-ATI
  • $FG_ROOT/GraphicsProfiles/INTEL
  • $FG_ROOT/Graphics Profiles/MATROX
  • $FG_ROOT/GraphicsProfiles/Other

Each vendor-specific sub-folder could then contain graphics related xml overlays that will be loaded on top of the default startup options (i.e. those not customized/overriden by the user) - what that means is that the current hard-coded default settings in fg_init.cxx would need to be dynamically loaded during startup using the APIs from props_io.cxx.

The corresponding startup profiles could be either conventional PropertyList XML files, or support embedded Nasal/property-rule blocks to provide support for regex-matching etc.[1]

This is something where it would make sense to provide a benchmark and test various combination of settings on different systems and gather all results in a central database to determine a safe subset of settings that should work for most AMD/ATI, NVIDIA and Intel boards - note that the CrashRpt tool is already gathering feedback on crashes and sending that to a webserver, where it is stored in a database. [2]

We could provide a --disable-gpu-profiles parameter to resort to the old/standard behavior, or require people to explicitly enable the new system via something like --enable-gpu-profiles, i.e. trying to pick an appropriate set of standard settings.

The more difficult thing is fixing up subsystems that are not currently able to be reset/restarted with restarting FG as a whole Note that the rendering settings dialog will already detect the INTEL substring and show a warning if appropriate [3]

  1. Hooray (Feb 7th, 2016). Re: Review of FG on reddit: xpost.
  2. Hooray (Feb 7th, 2016). Re: Review of FG on reddit: xpost.
  3. Hooray (Feb 7th, 2016). Re: Review of FG on reddit: xpost.


Note  Erik committed a slightly different version of this [2]
Cquote1.png there is now an option in the Debug menu to 'Save Video Configuration' which creates a file with the correct name and the proper properties for inclusion in the Video section of FGData.
— erik (Mar 21st, 2016). Re: Per video card configuration.
(powered by Instant-Cquotes)

As of 03/2016, there's a patch which will basically apply the logic for loading the Preferences.xml configuration file to load another PropertyList XML File into the /sim/rendering branch of the FlightGear Property tree after the initialization of the OSG/OpenGL graphics context (GC) has finished.

This would make it possible to either hard-code heuristics in C++ space to load vendor/renderer-based files from a certain XML node, or delegate doing that to scripting space.


When preferences.xml, autosave.xml etc are loaded, the gl-vendor properties are not yet initialized, because that requires an actual GC (OSG graphics context), so that loading the XML overlay may need to be delayed or the initialization sequence changed accordingly.

We could also store a copy of the rendering specific state from autosave.xml and apply that within the GC::run() method, i.e. after loading graphics profiles there (straightforward).

Alternatively, it would be possible to delegate the control flow to Nasal space to handle loading suitable defaults for the detected vendor/renderer.

flightgear/src/GUI/gui.cxx (line 102)

// Operation for querying OpenGL parameters. This must be done in a
// valid OpenGL context, potentially in another thread.

struct GeneralInitOperation : public GraphicsContextOperation
        : GraphicsContextOperation(std::string("General init"))
    void run(osg::GraphicsContext* gc)
        SGPropertyNode* simRendering = fgGetNode("/sim/rendering");

        simRendering->setStringValue("gl-vendor", (char*) glGetString(GL_VENDOR));
        SG_LOG( SG_GENERAL, SG_INFO, glGetString(GL_VENDOR));

        simRendering->setStringValue("gl-renderer", (char*) glGetString(GL_RENDERER));

        simRendering->setStringValue("gl-version", (char*) glGetString(GL_VERSION));
        SG_LOG( SG_GENERAL, SG_INFO, glGetString(GL_VERSION));

        // Old hardware without support for OpenGL 2.0 does not support GLSL and
        // glGetString returns NULL for GL_SHADING_LANGUAGE_VERSION.
        // See
        const char* glsl_version = (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION);
        if( !glsl_version )
          glsl_version = "UNSUPPORTED";
        simRendering->setStringValue("gl-shading-language-version", glsl_version);
        SG_LOG( SG_GENERAL, SG_INFO, glsl_version);

        GLint tmp;
        glGetIntegerv( GL_MAX_TEXTURE_SIZE, &tmp );
        simRendering->setIntValue("max-texture-size", tmp);

        glGetIntegerv( GL_DEPTH_BITS, &tmp );
        simRendering->setIntValue("depth-buffer-bits", tmp);


Organization of Profiles


This could work pretty much like the joystick support - i.e. we would maintain PropertyList XML files in $FG_ROOT/GraphicsProfiles/VENDOR, and then use the GL_* strings to load a matching default profile, with CLI arguments being processed afterwards, i.e. to take precedence, so that defaults can be overridden.

For instance, see $FG_SRCflightgear/Main/fg_init.cxx (line 466) fgInitConfig():

bool loadDefaults = options->shouldLoadDefaultConfig();
if (loadDefaults) {
// Read global preferences from $FG_ROOT/preferences.xml
SG_LOG(SG_INPUT, SG_INFO, "Reading global preferences");
fgLoadProps("preferences.xml", globals->get_props());
SG_LOG(SG_INPUT, SG_INFO, "Finished Reading global preferences");

This is one of the places, where we could also run another fgLoadPRops() call to load rendering settings that are overlaid onto /sim/rendering

Technically, we are already using this approach to suport translations, which are also read/overlaid into the tree[1]

  1. Hooray (Feb 8th, 2016). Re: Review of FG on reddit: xpost.

Proof of Concept

Public domain This work has been released into the public domain by its author, FlightGear forum. This applies worldwide.
In some countries this may not be legally possible; if so:
FlightGear forum grants anyone the right to use this work for any purpose, without any conditions, unless such conditions are required by law.

Note  This is just intended to demonstrate the concept; this moves the loading of the gpu profile to the run() method of the GeneralInitOperation struct in GUI/gui.cxx, so that we can access the gl-vendor and gl-renderer information to proceed from here. To test this, use --prop:/sim/startup/enable-gpu-profiles=1

It would make more sense to restore a copy of any autosave.xml settings, i.e. those having the userarchive attribute set. To actually do some kind of heuristics processing, it would make sense to support SGConditions, PropertyRules and/or Nasal (at least temporarily), so that we can process a few properties and select appropriate defaults.

diff --git a/src/GUI/gui.cxx b/src/GUI/gui.cxx
index b8a8f33..59f6dbf 100644
--- a/src/GUI/gui.cxx
+++ b/src/GUI/gui.cxx
@@ -109,8 +109,31 @@ struct GeneralInitOperation : public GraphicsContextOperation
         : GraphicsContextOperation(std::string("General init"))
+    ~GeneralInitOperation() {
+	SG_LOG(SG_GL, SG_INFO, "GeneralInitOperation finished");
+    }
+    void applyGraphicsProfile() {
+    try {
+      	SGPropertyNode* rendering = fgGetNode("/sim/rendering");
+	std::string path = "Profiles/graphics.xml"; 
+	if(rendering) {
+		SG_LOG(SG_INPUT, SG_INFO, "Loading graphics profiles from:" <<path);
+		//TODO: in its current form, this will overwrite autosave stuff
+      		fgLoadProps(path.c_str(), rendering);
+		}
+	else SG_LOG(SG_INPUT, SG_ALERT, "Could not acquire handle to /sim/rendering for loading graphics profile from "<<path);
+      }
+      catch (const std::exception& e) {
+                SG_LOG(SG_INPUT, SG_INFO, "caught std::exception trying to read graphics profile");
+            } catch (...) {
+                SG_LOG(SG_INPUT, SG_INFO, "caught generic exception trying to read graphics profile");
+            }
+    } // of applyGraphicsProfile
     void run(osg::GraphicsContext* gc)
+	SG_LOG(SG_GL, SG_INFO, "OpenGL initialization");
         SGPropertyNode* simRendering = fgGetNode("/sim/rendering");
         simRendering->setStringValue("gl-vendor", (char*) glGetString(GL_VENDOR));
@@ -138,6 +161,12 @@ struct GeneralInitOperation : public GraphicsContextOperation
         glGetIntegerv( GL_DEPTH_BITS, &tmp );
         simRendering->setIntValue("depth-buffer-bits", tmp);
+	SG_LOG(SG_GL, SG_INFO, "OpenGL strings copied to property tree");
+	if(fgGetNode("/sim/startup")->getBoolValue("enable-gpu-profiles",true)) {
+		applyGraphicsProfile();
+	}
+	else SG_LOG(SG_GL, SG_INFO, "NOT applying standard gpu profiles!");
Note  This is to be put into $FG_ROOT/Profiles/graphics.xml (for now, this is just a copy of the /sim/rendering portion of preferences.xml )
<?xml version="1.0"?>
Graphics defaults for FlightGear property values.

Started February 2016: 
        <enabled type="bool">false</enabled>
        <show-buffers type="bool" userarchive="y">false</show-buffers>
        <ambient-occlusion type="bool" userarchive="y">false</ambient-occlusion>
        <ambient-occlusion-strength type="float" userarchive="y">0.6</ambient-occlusion-strength>
        <ambient-occlusion-buffers type="bool">true</ambient-occlusion-buffers>
        <bloom type="bool" userarchive="y">true</bloom>
        <bloom-strength type="float" userarchive="y">0.6</bloom-strength>
        <bloom-buffers type="bool">true</bloom-buffers>
        <night-vision type="bool">false</night-vision>
          <vignette type="bool">false</vignette>
          <inner-circle type="float" userarchive="y">0.8</inner-circle>
          <outer-circle type="float" userarchive="y">1.3</outer-circle>
          <color-shift type="bool">false</color-shift>
            <x type="float" userarchive="y">.393</x>
            <y type="float" userarchive="y">.769</y>
            <z type="float" userarchive="y">.189</z>
            <x type="float" userarchive="y">.349</x>
            <y type="float" userarchive="y">.686</y>
            <z type="float" userarchive="y">.168</z>
            <x type="float" userarchive="y">.272</x>
            <y type="float" userarchive="y">.534</y>
            <z type="float" userarchive="y">.131</z>
          <distortion type="bool">false</distortion>
            <x type="float" userarchive="y">0.0</x>
            <y type="float" userarchive="y">0.0</y>
            <z type="float" userarchive="y">1.0</z>
          <color-fringe type="bool">false</color-fringe>
          <color-fringe-factor type="float" userarchive="y">1.0</color-fringe-factor>
          <film-wear type="bool">false</film-wear>
        <exposure type="float" userarchive="y">1.0</exposure>
        <use-color-for-depth type="bool">false</use-color-for-depth>
        <no-16bit-buffer type="bool">false</no-16bit-buffer>
        <debug-buffer n="0">
          <enabled type="bool" userarchive="y">false</enabled>
          <name userarchive="y"/>
        <debug-buffer n="1">
          <enabled type="bool" userarchive="y">true</enabled>
          <name userarchive="y">spec-emis</name>
        <debug-buffer n="2">
          <enabled type="bool" userarchive="y">true</enabled>
          <name userarchive="y">normal</name>
        <debug-buffer n="3">
          <enabled type="bool" userarchive="y">true</enabled>
          <name userarchive="y">diffuse</name>
            <sky type="bool">true</sky>
            <ambient type="bool">true</ambient>
            <sunlight type="bool">true</sunlight>
            <lights type="bool">true</lights>
            <fog type="bool">true</fog>
            <debug type="bool">false</debug>
            <transparent type="bool">false</transparent>
      <debug type="bool">false</debug>
      <realism type="int">5</realism>
      <filtering type="int">8</filtering>
        <custom-settings type="bool" userarchive="y">false</custom-settings>
        <generic type="float" userarchive="y">1.0</generic>
        <landmass type="float" userarchive="y">1.0</landmass>
        <model type="float" userarchive="y">1.0</model>
        <contrails type="float" userarchive="y">1.0</contrails>
        <crop type="float" userarchive="y">1.0</crop>
        <skydome type="bool" userarchive="y">false</skydome>
        <transition type="float" userarchive="y">1.0</transition>
        <urban type="float" userarchive="y">1.0</urban>
        <water type="float" userarchive="y">1.0</water>
        <wind-effects type="float" userarchive="y">0.0</wind-effects>
        <forest type="float" userarchive="y">0.0</forest>
        <lights type="float" userarchive="y">1.0</lights>
        <quality-level-internal type="float" userarchive="y">1.0</quality-level-internal>
      <mie type="float" userarchive="y">0.003</mie>
      <rayleigh type="float" userarchive="y">0.0003</rayleigh>
      <dome-density type="float" userarchive="y">0.5</dome-density>
     Uncomment the above element to select OSG multi-threading mode.
     This may improve performance on multi-core, multi-CPU
     and/or multi-GPU systems. The recommended setting is AutomaticSelection.
     The alternatives are
      <plod-minimum-expiry-time-secs type="double" userarchive="y">180.0</plod-minimum-expiry-time-secs>
        <detailed type="double" userarchive="y">1500</detailed>
        <rough type="double" userarchive="y">9000</rough>
        <bare type="double" userarchive="y">30000</bare>
          ai-range-mode-pixel: true=use pixel size on screen, false=use distance from eyepoint 
          With /sim/rendering/static-lod/ai-range-mode-pixel set to true
          /sim/rendering/static-lod/ai-detailed now contains the minimum size of the
          model on the screen to be displayed. Values of 10-50 seem to make some sense.
        <ai-range-mode-pixel type="bool" userarchive="y">false</ai-range-mode-pixel>
        <ai-detailed type="double" userarchive="y">10000</ai-detailed>
        <!-- ai-bare type="double" userarchive="y">10000</ai-bare -->
        <ai-interior type="double" userarchive="y">50</ai-interior>
      <random-objects type="bool" userarchive="y">true</random-objects>
      <random-vegetation type="bool" userarchive="y">true</random-vegetation>
      <random-vegetation-shadows type="bool" userarchive="y">false</random-vegetation-shadows>
      <random-vegetation-normals type="bool" userarchive="y">false</random-vegetation-normals>
      <vegetation-density type="double" userarchive="y">1.0</vegetation-density>
      <random-buildings type="bool" userarchive="y">false</random-buildings>
      <building-density type="double" userarchive="y">1.0</building-density>
      <horizon-effect type="bool" userarchive="y">false</horizon-effect>
      <point-sprites type="bool" userarchive="y">true</point-sprites>
      <enhanced-lighting type="bool" userarchive="y">false</enhanced-lighting>
      <distance-attenuation type="bool" userarchive="y">false</distance-attenuation>
      <particles type="bool" userarchive="y">true</particles>
      <precipitation-gui-enable type="bool" userarchive="y">false</precipitation-gui-enable>
      <precipitation-enable type="bool" userarchive="y">false</precipitation-enable>
      <precipitation-aircraft-enable type="bool">true</precipitation-aircraft-enable>
        <!-- streaks coloring and transparency -->
        <min-light type="float">0.35</min-light>
        <streak-brightness-nearmost-layer type="float">0.9</streak-brightness-nearmost-layer>
        <streak-brightness-farmost-layer type="float">0.5</streak-brightness-farmost-layer>
        <!-- streak period as a function of the speed, decreases with speed -->
        <streak-period-max type="float">2.5</streak-period-max>
        <streak-period-change-per-kt type="float">0.005</streak-period-change-per-kt>
        <streak-period-min type="float">1.0</streak-period-min>
        <!-- streak length as a function of the speed, increases with speed -->
        <streak-length-min type="float">0.03</streak-length-min>
        <streak-length-change-per-kt type="float">0.0005</streak-length-change-per-kt>
        <streak-length-max type="float">0.1</streak-length-max>
        <!-- # of rain streaks at the 100.0% precipitation density -->
        <streak-count-min type="int">40</streak-count-min>
        <!-- lightest rain -->
        <streak-count-max type="int">190</streak-count-max>
        <!-- heaviest rain -->
        <!-- numbers over MAX_RAIN_SLICE in simgear/environment/visual_enviro.cxx
         will be ignored in the current implementation.  -->
        <!-- the precipitation cone geometry -->
        <cone-base-radius type="float">15.0</cone-base-radius>
        <cone-height type="float">30.0</cone-height>
      <lightning-enable type="bool" userarchive="y">false</lightning-enable>
      <specular-highlight type="bool" userarchive="y">false</specular-highlight>
      <bump-mapping type="bool" userarchive="y">false</bump-mapping>
      <clouds3d-enable type="bool" userarchive="y">false</clouds3d-enable>
      <clouds3d-vis-range type="float" userarchive="y">10000.0</clouds3d-vis-range>
      <clouds3d-detail-range type="float" userarchive="y">10000.0</clouds3d-detail-range>
      <clouds3d-density type="float" userarchive="y">0.25</clouds3d-density>
      <!-- legacy property to control drawing 'out the window', i.e
            non-cockpit + aircraft elements. Use draw-mask instead. -->
      <draw-otw type="bool">true</draw-otw>
        <terrain type="bool">true</terrain>
        <models type="bool">true</models>
        <aircraft type="bool">true</aircraft>
        <clouds type="bool">true</clouds>
      <shadows-ac type="bool" userarchive="y">false</shadows-ac>
      <shadows-ac-transp type="bool" userarchive="y">false</shadows-ac-transp>
      <shadows-ai type="bool" userarchive="y">false</shadows-ai>
      <shadows-to type="bool" userarchive="y">false</shadows-to>
      <shadows-debug type="bool" userarchive="y">false</shadows-debug>
        <enabled type="bool" userarchive="y">true</enabled>
        <debug type="bool" userarchive="y">false</debug>
        <map-size type="int" userarchive="y">2048</map-size>
        <num-cascades type="int" userarchive="y">4</num-cascades>
        <cascade-far-m n="0" type="float" userarchive="y">2.0</cascade-far-m>
        <cascade-far-m n="1" type="float" userarchive="y">8.0</cascade-far-m>
        <cascade-far-m n="2" type="float" userarchive="y">32.0</cascade-far-m>
        <cascade-far-m n="3" type="float" userarchive="y">128.0</cascade-far-m>
        <filtering type="int" userarchive="y">1</filtering>
      <shader-experimental type="bool" userarchive="y">false</shader-experimental>
      <shader-effects type="bool" userarchive="y">true</shader-effects>
      <fps-display type="bool" userarchive="y">false</fps-display>
      <frame-latency-display type="bool" userarchive="y">false</frame-latency-display>
      <on-screen-statistics type="int">0</on-screen-statistics>
      <glide-slope-tunnel type="bool" userarchive="y">false</glide-slope-tunnel>
        <enabled type="bool" userarchive="y">true</enabled>
          <blackout-onset-g type="double">3.5</blackout-onset-g>
          <blackout-complete-g type="double">5</blackout-complete-g>
          <redout-onset-g type="double">-2</redout-onset-g>
          <redout-complete-g type="double">-4</redout-complete-g>
        <enabled type="bool" userarchive="y">false</enabled>
        <rate-m-g type="double">0.005</rate-m-g>
        <eye-separation type="double" userarchive="y">0.05</eye-separation>
        <screen-distance type="double" userarchive="y">0.5</screen-distance>
        <stereo-mode type="string" userarchive="y">OFF</stereo-mode>
      <osg-notify-level type="string" userarchive="y">warn</osg-notify-level>
      <cache type="bool">true</cache>
      <use-vbos type="bool">false</use-vbos>
          <enabled-near type="bool">false</enabled-near>
          <enabled-far type="bool">false</enabled-far>
          <ratio type="double">0.01</ratio>
          <max-error type="double">3000.0</max-error>
          <max-length type="double">1000.0</max-length>
      <!-- OSG default is 300, but this means we burn lots of memory on no-longer
             visible trees / random buildings -->
      <max-paged-lod type="int">200</max-paged-lod>
        <use-searchlight type="bool">false</use-searchlight>
        <use-landing-light type="bool">false</use-landing-light>
        <use-alt-landing-light type="bool">false</use-alt-landing-light>
        <landing-light1-offset-deg type="float">0.0</landing-light1-offset-deg>
        <landing-light2-offset-deg type="float">0.0</landing-light2-offset-deg>
        <landing-light3-offset-deg type="float">0.0</landing-light3-offset-deg>
	<use-flashlight type="int">0</use-flashlight>
<!-- end of graphics.xml -->