Checking if your Graphics software runs on GPU

What are we asking exactly?

This is actually not that easy to phrase. If you see anything on a screen, your software does make use of some graphics processing unit – but it is more likely you’re interested in particular capabilities of the GPU you’re running on. The device capabilities range wildly and the full details comprise hundreds and hundreds of fields in structs retrievable e.g. by DirectX API. One can –

  1. Choose a a few (or single) capabilities that are of interest and query only them,
  2. Make do with a vendor – i.e., ‘I’m running on an nVidia card’
  3. Use some abstraction of GPU ‘level’, that is hopefully available from one of the API sets.

In my own scenario the HW platform is controlled and the GPU choice is limited to (a) an integrated Intel graphics processor, (b) a known nVidia card. So my code choices were (2) and (3) – see more below.

If you have a GPU, why wouldn’t it be used?

If you have a GPU on your computer and your software wants to use it, why wouldn’t it be able to? The two reasons I came across are –

  1. Erroneous connectivity
    1. When you plug your monitor to a (desktop) motherboard socket and not a graphics card socket, some systems do not use the graphics card,
    2. When your laptop is mounted on a docking station (USB docking station in particular), sometimes the laptop’s motherboard takes the wrong decision.
  2. nVidia Optimus (1-paragraph survey, technical details)
  3. Is an attempt by nVidia to save laptop battery life by turning off the GPU when (they think) you’re not using it. On an Optimus-enabled laptop if you right click your desktop and choose ‘NVIDIA control panel/Manage 3D settings’, you’d be able to indirectly see and select the graphics output to use:

    Now according to the whitepaper a switch to the discrete NVIDIA GPU is triggered by DX, DXVA and CUDA calls – but no OpenGL calls. One does come across online complaints, however, that CUDA calls do not trigger the switching mechanism.

Each of these is solvable, but all I wanted was a way for the software to warn about such situations and prompt the user for action.

Native solutions

The task of querying the underlying device is a basic one, and every reasonable platform that makes use of the GPU offers such API.

  1. If you’re using DirectX – Create a DX device, then try to use it to call IDXGIAdapter::GetDesc. Check if the Device creation fails or DXGI_ADAPTER_DESC.VendorID is not nVidia. Check this example on MSDN.
  2. If you’re using CUDA – it has API to enumerate and query available devices – essentially, cudaGetDeviceProperties. One usage example is here.
  3. If you’re using OpenGL – create a dummy window and call glGetString(GL_VENDOR) to detect the OpenGL implementation used.

I’m sure there are also OpenCL, Vulkan, DirectCompute (etc. etc.) APIs for that, but that should be enough. Our SW operates on OpenGL so I went with the 3rd option and here’s a shameless copy/past of the code snippet I used, by Daniel Cornel:

bool VerifyRunningOnNvidia()
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = L"DummyWindow";
RegisterClass(&wc);
HWND hWnd = CreateWindow(L"DummyWindow", NULL, 0, 0, 0, 256, 256, NULL, NULL, GetModuleHandle(NULL), NULL);

HDC hDC = GetDC(hWnd);
PIXELFORMATDESCRIPTOR pfd = { 0 };
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
int pixelFormat = ChoosePixelFormat(hDC, &pfd);
SetPixelFormat(hDC, pixelFormat, &pfd);
HGLRC hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);

// Check the device information. Vendor chould be Intel for the integrated GPU
// and NVIDIA for the discrete GPU
const char* GpuVendor = (const char*)glGetString(GL_VENDOR);

bool IsNvidia = 0 == strcmp(GpuVendor, "NVIDIA Corporation");

// Destroy the OpenGL context
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);

return IsNvidia;
}

Managed solution

Enter System.Windows.Media.RenderCapability class, and specifically its Tier member. It provides a 3-values abstraction of the GPU ‘level’, which I expect should be enough for all but AAA game developers. Some of the descriptions given seem specific to the context of WPF (“…graphics features of WPF will use hardware acceleration…”) but truth is it suffices as a basic GPU query for all needs. The mapping to DirectX versions seems rather heuristic, and if you care about it there’s a good chance WPF is not the tool for you in the first place.

As an added bonus WPF provides an event that digs deep enough into the OS to give you a hook to respond to Render Tier change – which (afaik) is more than any other graphics framework provides. Such a change might occur if the user re-plugs the monitor to a different socket at runtime, or docks/undocks his laptop.

Here’s the relevant code snippet:

using System.Windows.Media;

...
void SomeEarlyInit()
{
...
    CheckRenderTier(null, null);
    RenderCapability.TierChanged += CheckRenderTier;
...
}

private void CheckRenderTier(object sender, EventArgs e)
{
    int renderingTier = RenderCapability.Tier >> 16;
    if (renderingTier < 2)
        MessageBox.Show("Graphics card inaccessible. Application requires an active GPU to function properly", "Warning");
}
Advertisement
This entry was posted in DirectX, Win32. Bookmark the permalink.

1 Response to Checking if your Graphics software runs on GPU

  1. Tomer Gal says:

    Hi Ofek,
    As for OpenCL, in OpenCL you query for the platform(Intel, NVidia, AMD, etc) and then query for the device, usually according to its type (CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_CPU, etc).
    So, in OpenCL the developer specifically chooses on which device (and platform) the code will run.

    Regards,
    Tomer Gal

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s