VTK/OpenGL Driver Information

From KitwarePublic
Jump to navigationJump to search

With VTK's extensive use of OpenGL extensions and broad cross platform support it's inevitable that occasionally driver bugs are encountered for specific combinations of GPU, GL driver, and operating system. This page describes additions to vtkOpenGLExtenstionManager for uniformly dealing with OpenGL driver bugs in such a way that they may be easily located and re-evaluated periodically. For example when new drivers are released.

What Constitutes a Driver Bug?

A rendering bug should be considered an OpenGL driver bug when a VTK class fails to render properly and the following conditions are met:

  • rendering fails on specific a specific combination of device, platform, or driver version, but succeeds on a wide variety of other such combinations.
  • the class makes use of OpenGL functions as documented in the OpenGL spec for the version associated with the rendering context used by VTK.
  • no OpenGL errors are reported by VTK's GL error checking macros or errors are reported that do not match the behavior required by the OpenGL spec.

Verifying these conditions can require some substantial effort in reproducing the bug and reading both the source code and OpenGL spec. However, it's worth looking closely at the problematic code skeptically. For example some drivers, such as NVidia's, are generously fault tolerant and run cleanly on code that's not following the GL spec to the letter. In this case the bug is in the VTK code but it looks like a driver bug because it runs cleanly on one vendors hardware. For common features or popular GPU's it's worth the effort to see if the problem can be avoided by a slight refactoring rather than disabling the feature altogether, even when the bug is in the driver.

Identifying a Specific Rendering Device or Driver

Once a bug is classified as a driver bug the feature should be disabled or a warning reported when it's used with the problematic platform, system, or driver combination. In VTK 6.1 the following additions to the extension manager were added to simplify this. Note that these functions report information about the OpenGL functionality provided by driver as opposed to the functionality supported or requested by VTK and they should only be used for driver identification. The internal data supporting these methods is attained during the managers initialization in vtkOpenGLExtensionManager::Update which must be called once prior to their use. When using the manager associated with the render window, this is not an issue as initialization is triggered internally during the first render.

<source lang="cpp">

 // Description:
 // Return the driver's version parts. This may be used for
 // fine grained feature testing.
 virtual int GetDriverVersionMajor();
 virtual int GetDriverVersionMinor();
 virtual int GetDriverVersionPatch();
 // Description:
 // Get GL API version that the driver provides. This is
 // often different than the GL version that VTK recognizes
 // so only use this for identifying a specific driver.
 virtual int GetDriverGLVersionMajor();
 virtual int GetDriverGLVersionMinor();
 virtual int GetDriverGLVersionPatch();
 // Description:
 // Test's for common implementors of rendering drivers. This may be used for
 // fine grained feature testing. Note: DriverIsMesa succeeds for OS Mesa,
 // use DriverGLRendererIsOSMessa to differentiate.
 virtual bool DriverIsATI();
 virtual bool DriverIsNvidia();
 virtual bool DriverIsIntel();
 virtual bool DriverIsMesa();
 virtual bool DriverIsMicrosoft();
 // Description:
 // Test for a specific driver version.
 virtual bool DriverVersionIs(int major);
 virtual bool DriverVersionIs(int major, int minor);
 virtual bool DriverVersionIs(int major, int minor, int patch);
 // Description:
 // Test for driver version greater than or equal
 // to the named version.
 virtual bool DriverVersionAtLeast(int major);
 virtual bool DriverVersionAtLeast(int major, int minor);
 virtual bool DriverVersionAtLeast(int major, int minor, int patch);
 // Description:
 // Test for the driver's GL version as reported in
 // its GL_VERSION string. This is intended for driver
 // identification only, use ExtensionSuppported
 // to test for VTK support of a specific GL version.
 virtual bool DriverGLVersionIs(int major, int minor, int patch);
 virtual bool DriverGLVersionIs(int major, int minor);
 // Description:
 // Test for a specific renderer. This could be used
 // in some cases to identify the graphics card or
 // specific driver. Use HasToken to prevent false
 // matches eg. avoid GeForce4 matching GeForce400
 virtual bool DriverGLRendererIs(const char *str);
 virtual bool DriverGLRendererHas(const char *str);
 virtual bool DriverGLRendererHasToken(const char *str);
 // Description:
 // Test for Mesa's offscreen renderer.
 virtual bool DriverGLRendererIsOSMesa();
 // Description:
 // Get the OpenGL version, vendor and renderer strings. These can
 // be used to idnetify a specific driver.
 virtual const char *GetDriverGLVendor();
 virtual const char *GetDriverGLVersion();
 virtual const char *GetDriverGLRenderer();

</source>

Disabling a Feature

Rendering classes that require OpenGL extensions should provide a public IsSupported method that can be used at run time to check for driver compatibility. This is also where driver related bugs may be detected and reported. Note that this method should run silently whether or not the requisite features are supported.

<source lang="cpp">

 // Description:
 // Return true when the required GL extensions are present and
 // the class may be safely used.
 bool vtkSomeClass::IsSupported(vtkRenderWindow *renWin);

</source>

Periodically Reevaluating the Bug

When a feature is disabled because of a driver bug it's important that the feature not remain disabled indefinitely. It's important to periodically reevaluate the situation as new hardware and drivers are released. Use of the extension manager's IgnoreDriverBugs flag facilitates this. When set driver bugs are ignored and a warning message will be displayed as the classes are used. The logic in the class's IsSupported method identifying the problematic driver should check of the ignore flag and allow the feature to be used when it's set. The flag can be enabled globally from the CMakeCache variable VTK_IGNORE_GLDRIVER_BUGS. When enabled at run time the string literal passed in GetIgnoreDriverBugs call will be reported in the warning stream. During VTK's ctests this will appear in the tests output which makes dashboard evaluation easier.

<source lang="cpp">

 // Description:
 // When set known driver bugs are ignored during driver feature
 // detection. This is used to evaluate the status of a new driver
 // release to see if the bugs have been fixed. The function takes
 // a description argument which, is sent to VTK's warning stream
 // when the ignore flag is set. This makes the test output searchable
 // for tests which have problems with certain drivers. The CMakeLists
 // variable VTK_IGNORE_GLDRIVER_BUGS can be used to set this at
 // build time. Default OFF.
 bool GetIgnoreDriverBugs(const char *description);
 vtkSetMacro(IgnoreDriverBugs, bool);
 vtkBooleanMacro(IgnoreDriverBugs, bool);

</source>

Example

Putting all of the above together, here is an example from vtkDepthPeelingPass::CheckSupport

<source lang="cpp">

 ...
     // force alpha blending
     // Mesa does not support true linking of shaders (VTK bug 8135)
     // and Mesa 7.2 just crashes during the try-compile.
     // os mesa 9.1.4 some tests fail
     // ATI Radeon HD XXXXX on Windows chokes on PROXY_TEXTURE_RECTANGLE_ARB
     // memory querries however if those are not used all the tests pass.
     // ATI Radeon HD on Mac OSX PROXY_TEXTURE_RECTANGLE_ARB are fine but
     // TestTranslucentLUTDepthPeeling fails. So leave it disabled on Apple
     int driver_support
       = (!(extensions->DriverIsATI()
       && (extensions->GetDriverGLVersionMajor() < 3))
       && !extensions->DriverIsMesa())
       || extensions->GetIgnoreDriverBugs("ATI and Mesa depth peeling bugs");
     this->IsSupported =
       supports_depth_texture &&
       supports_shadow &&
       supports_blend_func_separate &&
       supports_shadow_funcs &&
       supports_vertex_shader &&
       supports_fragment_shader &&
       supports_shader_objects &&
       supports_occlusion_query &&
       supports_multitexture &&
       supports_GL_ARB_texture_rectangle &&
       supports_edge_clamp &&
       supportsAtLeast8AlphaBits &&
       driver_support;
     if(this->IsSupported)
       {
       // use depth peeling
       ...
       }
     else
       {
       // falback to depth sort
       ...
       }
 ...

</source>


and later in the same class there's an example of a slight refactoring that allows the feature to be used on popular devices rather than disabling the feature:

<source lang="cpp">

 ...
 if ( extensions->DriverIsATI()
    && !extensions->GetIgnoreDriverBugs("ATI proxy query bug.") )
   {
   // The ATI Radeon HD drivers currently choke on the proxy
   // query, but depth peeling has been confirmed to work. For
   // those driver fall back on the weaker max texture size
   // check.
   ...
   }
 else
   {
   // Not a buggy ATI driver, it's OK to make the proxy query.
   ...
   }

</source>