Advertisement

How to use a specific adapter for fullscreen applications?

Started by September 24, 2018 05:55 PM
6 comments, last by ROGRat 6 years, 3 months ago

Some computer configurations have multiple GPUs, e.g. gaming laptop with an Intel HD Graphics chip and a GeForce or Radeon chip. When enumerating through the available adapters on a computer, the Intel chip is the first one in most cases. However, I want to use the best adapter for my game. So I wrote the code below to create my swap chain:


protected void CreateDevice(Size clientSize, IntPtr outputHandle)
{
    ProcessLogger.Instance.StartFunction(this, "CreateDevice");

    // Set swap chain flags, DXGI format and default refresh rate.
    _swapChainFlags = SharpDX.DXGI.SwapChainFlags.None;
    _dxgiFormat = SharpDX.DXGI.Format.R8G8B8A8_UNorm;
    SharpDX.DXGI.Rational refreshRate = new SharpDX.DXGI.Rational(60, 1);

    // Get proper video adapter and create device and swap chain.
    using (var factory = new SharpDX.DXGI.Factory1())
    {
        SharpDX.DXGI.Adapter adapter = GetAdapter(factory);
        if (adapter != null)
        {
            ProcessLogger.Instance.Write(String.Format("Selected adapter: {0}", adapter.Description.Description));

            // Get refresh rate.
            refreshRate = GetRefreshRate(adapter, _dxgiFormat, refreshRate);
            ProcessLogger.Instance.Write(String.Format("Selected refresh rate = {0}/{1} ({2})", refreshRate.Numerator, refreshRate.Denominator, refreshRate.Numerator / refreshRate.Denominator));

            // Create Device and SwapChain
            ProcessLogger.Instance.Write("Create device.");
            _device = new SharpDX.Direct3D11.Device(adapter, SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport, new SharpDX.Direct3D.FeatureLevel[]
            {
                SharpDX.Direct3D.FeatureLevel.Level_10_1
            });

            ProcessLogger.Instance.Write("Create swap chain.");
            _swapChain = new SharpDX.DXGI.SwapChain(factory, _device, GetSwapChainDescription(clientSize, outputHandle, refreshRate));

            ProcessLogger.Instance.Write("Store device context.");
            _deviceContext = _device.ImmediateContext;
        }
    }

    ProcessLogger.Instance.EndFunction(this, "CreateDevice");
}

For this function to work properly, I have to select the proper adapter in GetAdapter. Here is what it looks like:


private SharpDX.DXGI.Adapter GetAdapter(SharpDX.DXGI.Factory1 factory)
{
    List<SharpDX.DXGI.Adapter> adapters = new List<SharpDX.DXGI.Adapter>();

    for (int i = 0; i < factory.GetAdapterCount(); i++)
    {
        SharpDX.DXGI.Adapter adapter = factory.GetAdapter(i);
        if (SharpDX.Direct3D11.Device.IsSupportedFeatureLevel(adapter, SharpDX.Direct3D.FeatureLevel.Level_10_1))
            adapters.Add(adapter);
    }

    try
    {
        foreach (var adapter in adapters)
            if (adapter.Description.Description != null && (adapter.Description.Description.Contains("GeForce") || adapter.Description.Description.Contains("Radeon")))
            {
                return adapter;
            }
    }
    catch
    {
    }

    return adapters.First();
}

So all I am doing is asking my Factory1 for a list of all adapters that support FeatureLevel 10.1 and search for a "GeForce" or "Radeon" one. Very simple and use that one.

This works like a charm. HOWEVER, there is one big problem: When I use this code in the Release build the game crashes when going to fullscreen using the code below.


public void SetFullscreenState(bool isFullscreen)
{
    if (isFullscreen != _swapChain.IsFullScreen)
        _swapChain.SetFullscreenState(isFullscreen, null);
}

The error code is DXGI_ERROR_UNSUPPORTED and after doing some research I found out, that this problem only happens for the Release build but not for the Debug build. The Debug build works like a charm. It also crashes only, if the selected adapter is not the first one in the list. So, if I use the first adapter (factory.GetAdapter(0)), it works! If I change my computer settings so that my GeForce is used as primary adapter for my game, it works.

It only does not work for the Release build, if the selected adapter is not the first adapter in the list and I can't figure out why... The problem is independent from the used screen or other running applications in a multi-windowed application. I already tested that.

There's nothing you can do. Fullscreen exclusive mode only supports monitors connected to the GPU where you're rendering.

The one exception is in hybrid laptops, where the fullscreen mode has a cross-adapter copy before it hits the screen, but this scenario is only available if the adapter enumeration says that the monitor is connected to the discrete GPU, which generally happens based on IHV control panel settings or specifically-named exports from your EXE.

Your only option is a borderless windowed approach if you want to explicitly target a different render adapter from the display.

Advertisement

Thanks for your response, but why does it work if I compile my game with the Debug configuration? I do not use other externals, libraries, etc. Do you know how other games do it? I mean, I do not need to change any settings for a game like Battlefield 1. It automatically uses the correct adapter - in fullscreen.

I"m afraid I have no idea what SharpDX does differently in debug or release configurations, the only way I know of to programattically change this behavior is by:

1. Changing your application EXE name.

2. Exporting the vendor-specific globals to change this behavior.

Are you enumerating the SharpDX.DXGI.Output objects connected to each DXGI.Adaptor?  If you want to render on an Output which is not considered your “Primary Display” by Windows, your application will need to create its main window at the co-orxinates which translate to that Output.  Otherwise, it will just be created on your Primary Display and will render using the default Adaptor.

Actually, the list of outputs is empty for all adapters except the first one (whichever that is).

Advertisement
On 10/23/2018 at 7:28 AM, GalacticCrew said:

Actually, the list of outputs is empty for all adapters except the first one (whichever that is).

I apologise for not reading your OP carefully before replying.  Am I right in assuming that your test system is a laptop like the one you described? 

Some of these laptops will default to the Intel adapter unless otherwise specified in the nVidia driver options, or by starting the application by choosing the adapter at runtime using the context menu option which is available by right clicking the applications icon.

To complicate matters further, some newer laptops can only use either the nVidia or the Intel adapter, and switching between them requires a manufacturer-supplied utility followed by a reboot.  If you’re writing code that you plan to run on systems other than your own, this is worth keeping in mind.

I’m not sure why you’re experiencing a crash in release mode, as your code appears sound.  I suggest creating a debug Device (you will need Windows SDK installed).  Programmatically, DXGI allows you to query each adapters capabilities to help you decide which one you want to use.

Switching to full screen mode is a little more involved than calling SetFullscreenState. Are you calling ResizeTarget/ResizeBuffers as needed?

 

This topic is closed to new replies.

Advertisement