Games, Graphics, Profiling: pick the discrete GPU of a hybrid laptop, type the GPU preference, keep the profiler page at 1 or more - #3388
Conversation
Ethereal77
left a comment
There was a problem hiding this comment.
A nice addition. This lacked configurability.
I must say however that I put the HighPerformance as default for the exact case you mention. I was developing this at the tame on an old laptop with integrated Intel Graphics + a dedicated nVidia GPU, and this was enough for Stride to always choose the nVidia adapter. Did not work for you?
And sorry if I'm being annoying 🙏 but... the profiler change maybe should have been a separate PR, even if small. Having several unrelated changes in a single PR makes it difficult in the future to locate them or understand them at a glance. Just my honest opinion, I like to have things tidy 😁
| VendorId = (int) dxgiAdapterDesc.VendorId; | ||
| // DXGI_ADAPTER_FLAG_SOFTWARE (2) marks WARP; the Basic Render Driver predates the | ||
| // flag on some Windows builds, so its Microsoft vendor id is the fallback test. | ||
| IsSoftwareAdapter = (dxgiAdapterDesc.Flags & 0x2) != 0 || dxgiAdapterDesc.VendorId == 0x1414; |
There was a problem hiding this comment.
Instead of hardcoding flags, prefer to use the Silk-provided enum: Silk.NET.DXGI.AdapterFlag.Software
There was a problem hiding this comment.
Done: (uint) AdapterFlag.Software, and the Microsoft vendor id moved to a named constant.
| // Enumerates all the Graphics Adapters in the system, using GPU preference (DXGI 1.6+). | ||
| // | ||
| static List<GraphicsAdapter> EnumerateAdaptersPrefer(GpuPreference gpuPreference) | ||
| static List<GraphicsAdapter> EnumerateAdaptersPrefer(GpuPreference preference) |
There was a problem hiding this comment.
Err... why rename this? 😀
Even though the type is different (it is now in Stride), the old name still made sense
There was a problem hiding this comment.
Reverted, the parameter keeps its original name.
| @@ -0,0 +1,29 @@ | |||
| // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) | |||
There was a problem hiding this comment.
No need for Silicon Studio copyright in headers of new files
There was a problem hiding this comment.
Done, header trimmed to .NET Foundation and Contributors.
|
General remark (apply to your other PRs): comments are overly long and verbose (some of those 9 lines!), giving way too much details, relevant only to previous code or your specific context/discussion. |
FindBestDevices skipped every adapter with no display output, meant to keep WARP and the Basic Render Driver from being picked. On a hybrid laptop that filter catches the wrong card: every display is wired to the integrated GPU, so the discrete one - the adapter DXGI just sorted to the front of the list as the fastest - never has an output, and every Stride game lands on the integrated GPU with the fast one idle. Windows' per-app GPU preference papers over it by re-attributing the iGPU's outputs, which is why the problem only shows on binaries nobody has configured. The skip now tests what it meant: IsSoftwareAdapter, new on GraphicsAdapter (the DXGI software flag plus the Microsoft vendor id on D3D, the CPU device type on Vulkan). A hardware adapter with no output is a fine renderer for a window - Windows composes across adapters routinely - and exclusive fullscreen already requires PreferredFullScreenOutputIndex to name a real output, so an output-less adapter yields no fullscreen candidate on its own. The enumeration preference also stops being hard-coded, answering the TODO next to it: STRIDE_GPU_PREFERENCE=minimum-power puts the battery-friendly GPU first and "unspecified" keeps the system's order, machine-wide and without touching the game; RequiredAdapterUid remains the per-game way to pick one exactly. Measured on a muxless RTX 3050 Ti + Intel UHD laptop: the same demo frame moves from the Intel (99% busy, TDR at its heaviest tier) to the RTX by default, and back with minimum-power. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 06dabb4)
An environment variable is no API to review a GPU picker on: the game itself had no typed way to say which GPU it wants first. GraphicsAdapterFactory.GpuPreference is that way - a Stride enum (HighPerformance, the default; MinimumPower; Unspecified), set before the first adapter query, or re-applied with the Reset() the factory already has. The Direct3D factory maps it to DXGI's own preference. STRIDE_GPU_PREFERENCE stays, demoted to what an environment variable is good at: the machine's word over the game's, for steering a title that never exposes the choice - same precedent as STRIDE_GRAPHICS_SOFTWARE_RENDERING. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 55dcfaa)
…shorter comments Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
77b1173 to
7809bc9
Compare
|
Thanks! On the profiler: no problem, done, it moved to its own PR: #3405. |
That's weird. My laptop's nVidia dGPU has no display attached either, yet using Anyway, if your change is a more general solution, it's a nice addition. |
xen2
left a comment
There was a problem hiding this comment.
Vulkan: nothing happens as current code in
| // Software rendering adapters (e.g. WARP) have no outputs either, so allow them through | ||
| if (graphicsAdapter.Outputs.Length == 0 | ||
| // Skip software rasterizers (WARP, Basic Render Driver) unless explicitly requested. | ||
| if (graphicsAdapter.IsSoftwareAdapter |
There was a problem hiding this comment.
On Vulkan, there's always a fake output graphicsOutputs = [ new GraphicsOutput() ];
So previous code with Outputs.Length == 0 never removed anything but the new one does.
Esp. software-only renderer like CI will only have IsSoftwareAdapter devices, which will be removed, so nothing left.
On D3D: same, since VendorId == 0x1414 (Microsoft) means any machine with only Microsoft Basic device and/or VM with virtual GPU will go through that path.
Maybe your fix should only be applied if something survived.
If graphicsDeviceInfos.Count == 0, run it again without software filtering.
As a result, it can still allow software rendering if nothing else.
| Debug.Assert(dxgiFactoryVersion >= 6); | ||
| var dxgiFactory6 = (IDXGIFactory6*) dxgiFactory; | ||
|
|
||
| var dxgiPreference = gpuPreference == GpuPreference.MinimumPower |
There was a problem hiding this comment.
What about Vulkan? GPUPreference is ignored?
| // if we want to switch to fullscreen, try to find only needed output, otherwise check them all | ||
| if (preferredParameters.IsFullScreen) | ||
| { | ||
| if (preferredParameters.PreferredFullScreenOutputIndex < graphicsAdapter.Outputs.Length) |
There was a problem hiding this comment.
An adapter with no outputs still produces no fullscreen candidate, so a game that starts fullscreen keeps landing on the iGPU.
On a muxless laptop (panel wired to the Intel, no MUX to switch it):
GPU 0: NVIDIA GPU (0 output)
GPU 1: Intel UHD Graphics (1 output)
GraphicsDeviceManager.IsFullScreen = true -> the NVIDIA adds nothing here, only the Intel does, so FindBestDevice returns the Intel and the device is created on it. Windowed gets the NVIDIA, fullscreen gets the Intel, on the same machine.
However, exclusive fullscreen on an output-less adapter is not really possible in DXGI...
So not sure what the expected behavior is? Is it to do borderless fullscreen rather than an output lookup?
| /// </summary> | ||
| /// <remarks> | ||
| /// Set it before the first use of <see cref="Adapters"/> or <see cref="DefaultAdapter"/>, or call | ||
| /// <see cref="Reset"/> afterwards. The <c>STRIDE_GPU_PREFERENCE</c> environment variable |
There was a problem hiding this comment.
Have to be careful with Reset() recommendation: it dispose adapters, which could still be used by an alive GraphicsDevice.
I wonder if we should change our API so that GraphicsAdapterFactory list doesn't magically change depending on some state like GpuPreference being setup early enough.
It could simply report what the system has, untouched. With proper fact like IsSoftwareAdapter, and discrete/integrated hint (Vulkan has this info). Also possibly IsDefaultAdapter (item 0, could be Intel UHD) or PerformanceRank (from DXGI EnumAdapterByGpuPreference).
GPU preference can be stored like other preference on the appropriate object, like in GameGraphicsParameters then GraphicsDeviceManager (i.e. PreferredGpu).
As a result, creation won't use indirectly this mutable static anymore.
Example API for GraphicsAdapter:
public enum GraphicsAdapterType { Unknown, Integrated, Discrete, Virtual, Software }
public GraphicsAdapterType Type { get; private init; } // set by each backend ctor
public bool IsSoftwareAdapter => Type == GraphicsAdapterType.Software; // maybe not needed, we can use Type
// Index of this adapter in the system enumeration order. 0 is the system default (use if GPU preference is unspecified -- on Windows it should return the OS pref depending on power saving settings)
public int AdapterOrder { get; private init; }
public bool IsDefaultAdapter => AdapterOrder == 0;
// Position in the platform's fastest-first order. 0 is the fastest
public int PerformanceRank { get; private init; }
// Position in the platform's most-efficient-first order. 0 is the most power efficient
public int PowerEfficiencyRank { get; private init; }
D3D can fill using DXGI ordering method like EnumerateLuidsByPreference
Vulkan can use stuff like Discrete > Integrated > Virtual > Cpu for perf (and swap Discrete/Integrated for efficiency)
PR Details
Two related fixes to adapter selection. The profiler page clamp that was here moved to its own PR, as requested in review.
FindBestDevicesskipped every adapter with no display output, meant to keep WARP and the Basic Render Driver out. On a hybrid laptop every display is wired to the integrated GPU, so the discrete one - the adapter DXGI sorted to the front as the fastest - never has an output, and every Stride game landed on the integrated GPU. The skip now tests what it meant: a newGraphicsAdapter.IsSoftwareAdapter(the DXGI software flag plus the Microsoft vendor id on D3D, the CPU device type on Vulkan).GraphicsAdapterFactory.GpuPreferenceis a Stride enum (HighPerformance, the default;MinimumPower;Unspecified), set before the first adapter query or re-applied with the factory'sReset(); the Direct3D factory maps it to DXGI's preference.STRIDE_GPU_PREFERENCEstays as the machine's override, the same precedent asSTRIDE_GRAPHICS_SOFTWARE_RENDERING.Types of changes
Checklist