Game uses iGPU instead of dedicated GPU

Started by
6 comments, last by SoldierOfLight 6 years, 9 months ago

Hi,

on my Dell laptop, my game uses the Intel iGPU instead of the dedicated AMD GPU (verified by looking at GPU usage in GPU-Z). Other games like World of Warcraft use the dedicated GPU, so this has to be an issue of my game. I remember having already tried something, but it was not successful so I undid the changes.

Here is the code I use to initialize Direct3D:


	// Create a DirectX graphics interface factory.
	result = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory);
	if(FAILED(result))
	{
		return false;
	}

	// Use the factory to create an adapter for the primary graphics interface (video card).
	result = factory->EnumAdapters(0, &adapter);
	if(FAILED(result))
	{
		return false;
	}

	// Enumerate the primary adapter output (monitor).
	result = adapter->EnumOutputs(0, &adapterOutput);
	if(FAILED(result))
	{
		return false;
	}

	// Get the number of modes that fit the DXGI_FORMAT_R8G8B8A8_UNORM display format for the adapter output (monitor).
	result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
	if(FAILED(result))
	{
		return false;
	}

	// Create a list to hold all the possible display modes for this monitor/video card combination.
	displayModeList = new DXGI_MODE_DESC[numModes];
	if(!displayModeList)
	{
		return false;
	}

	// Now fill the display mode list structures.
	result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, displayModeList);
	if(FAILED(result))
	{
		return false;
	}

	// Now go through all the display modes and find the one that matches the screen width and height.
	// When a match is found store the numerator and denominator of the refresh rate for that monitor.
	for(unsigned int i=0; i<numModes; i++)
	{
		if(displayModeList[i].Width == (unsigned int)ScreenWidth)
		{
			if(displayModeList[i].Height == (unsigned int)ScreenHeight)
			{
				numerator = displayModeList[i].RefreshRate.Numerator;
				denominator = displayModeList[i].RefreshRate.Denominator;
			}
		}
	}

	// Get the adapter (video card) description.
	result = adapter->GetDesc(&adapterDesc);
	if(FAILED(result))
	{
		return false;
	}

	// Store the dedicated video card memory in megabytes.
	VideoCardMemory = (int)(adapterDesc.DedicatedVideoMemory / 1024 / 1024);

	VideoCardDescription = adapterDesc.Description + (L"\nVRAM: "  + std::to_wstring(VideoCardMemory)) + L" MB";

	// Release the display mode list.
	delete [] displayModeList;
	displayModeList = 0;

	// Release the adapter output.
	adapterOutput->Release();
	adapterOutput = 0;

	// Release the adapter.
	adapter->Release();
	adapter = 0;

	// Release the factory.
	factory->Release();
	factory = 0;

	// Initialize the swap chain description.
    ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));

	// Set to a single back buffer.
    swapChainDesc.BufferCount = 1;

	// Set the width and height of the back buffer.
    swapChainDesc.BufferDesc.Width = ScreenWidth;
    swapChainDesc.BufferDesc.Height = ScreenHeight;

	// Set regular 32-bit surface for the back buffer.
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

	// Set the refresh rate of the back buffer.
	if(VsyncEnabled && FullscreenMode == EFullscreenMode::Fullscreen)
	{
	    swapChainDesc.BufferDesc.RefreshRate.Numerator = numerator;
		swapChainDesc.BufferDesc.RefreshRate.Denominator = denominator;
	}
	else
	{
	    swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
		swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
	}

	// Set the usage of the back buffer.
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

	// Set the handle for the window to render to.
    swapChainDesc.OutputWindow = hwnd;

	swapChainDesc.SampleDesc.Count = Settings::MSAA;
    swapChainDesc.SampleDesc.Quality = 0;

	// Set to full screen or windowed mode.
	if(FullscreenMode == EFullscreenMode::Fullscreen)
	{
		swapChainDesc.Windowed = false;
	}
	else
	{
		swapChainDesc.Windowed = true;
	}

	// Set the scan line ordering and scaling to unspecified.
	swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

	// Discard the back buffer contents after presenting.
	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

	// Don't set the advanced flags.
	swapChainDesc.Flags = 0;

	D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 };

	ID3D11DeviceContext* lDeviceContext;
	// Create the swap chain, Direct3D device, and Direct3D device context.
	result = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, 0, D3D11_CREATE_DEVICE_BGRA_SUPPORT
#if defined(_DEBUG) || defined(DEBUG_GRAPHICS)
		| D3D11_CREATE_DEVICE_DEBUG 
#endif
		, FeatureLevels, 3, D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &Device, &FeatureLevel, &lDeviceContext);

	DeviceContext = lDeviceContext;
	if(FAILED(result))
	{
		return false;
	}

 

Advertisement

You should use EnumAdapters to iterate over all adapters, instead of just getting the first.

https://msdn.microsoft.com/en-us/library/windows/desktop/ff476877(v=vs.85).aspx

 

You are grabbing the first adapter, which could be the warp driver (software). The way you should be enumerating the adapters is iterating through them until you find the one you want:


    while (dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND)
    {
        DXGI_ADAPTER_DESC1 desc;
        adapter->GetDesc1(&desc);

        if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
        {
            // we dont want a software device
                adapterIndex++; // next adapter
            continue;
        }

        // we want a device that is compatible with direct3d 12 (feature level 11 or higher)
        hr = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr);
        if (SUCCEEDED(hr))
        {
            adapterFound = true;
            break;
        }

        adapterIndex++;
    }

something like that. notice i'm enumerating not on just the 0 index adapter, but also checking if its a software adapter and that it supports d3d12 (D3D_FEATURE_LEVEL_11_0).

Also, the discreet adapter should be the default adapter you will end up with, but you are usually able to change the default adapter (either discrete or integrated) in your nvidia control panel if you have a nvidia card. not sure how to do it with amd but i'm sure they have that option too.

But how do I get the best one?

And I think the problem is that adapter 1 (the AMD GPU) is not connected to any display directly, so the code after that does not work. If I just change the 0 to a 1 for testing it does not work on my laptop.

DXGI_ADAPTER_DESC does not have the member Flags, so I cannot test for software adapters like you did, at least not without changing from Direct3D 11 to Direct3D 12 API.

from microsoft:

Quote

EnumAdapters1 first returns the adapter with the output on which the desktop primary is displayed. This adapter corresponds with an index of zero.

EnumAdapters1 next returns other adapters with outputs. EnumAdapters1 finally returns adapters without outputs.

https://msdn.microsoft.com/en-us/library/windows/desktop/ff471336(v=vs.85).aspx

I believe this means you will need both adapters. your discrete adapter (gpu) for rendering, and your integrated graphics for your output, if your main monitor is hooked up to your motherboard.

I actually haven't ever dealt with this situation myself, so i'm not 100% sure about how you'd go about it, but i assume you set up your graphics pipeline on your discrete adapter device, and your swapchain on your integrated graphics device

So, you would expect your integrated graphics to return on index 0, which it seems you are currently experiencing and makes sense. The next index could either return the warp driver (software device) or your discrete gpu card, which is why you check to see if its a software adapter, and if it is, get the next adapter

For NVIDIA Optimus use the following:


extern "C" {
    _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
}

For AMD use the following:


extern "C" {
    __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}

Reference (for AMD): https://community.amd.com/thread/169965

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

@mhagain is correct, that's the easiest way to get what you want - the discrete AMD GPU enumerated as adapter 0 and appearing to be connected to the laptop output.

@iedoc isn't entirely incorrect. On these hybrid laptops, the OS will essentially do what he suggested under the covers, using an efficient cross-adapter copy mechanism with only GPU-side synchronization, no CPU synchronization.

Also, you don't typically have to worry about the WARP adapter being enumerated first, it'll almost always be last. The general sorting order that DXGI uses is:

  1. The adapter with the primary output.
  2. Other hardware adapters with outputs.
  3. Software adapters with outputs (can happen in some circumstances, but not a common scenario).
  4. Hardware adapters without outputs.
  5. Software adapters without outputs.

This topic is closed to new replies.

Advertisement