Select adapters lazily in request_adapter instead of exposing every adapter in the system - #10011
Select adapters lazily in request_adapter instead of exposing every adapter in the system#10011AdrianEddy wants to merge 3 commits into
request_adapter instead of exposing every adapter in the system#10011Conversation
|
It seems to me there are two things going on here:
It may be necessary to do (2) in order to accomplish (1) while also maintaining the power preference functionality. But I'd like to think some about how we can cleanly separate the pieces, and have a vision to how we might do #9932 (both the original request and also the Firefox use case, which is picking the adapter that's driving the active display. It's currently done by LUID, but it doesn't have to be done that way. However, the notion of "active display" in Firefox is totally disconnected from anything that is using One question that comes out of all that, is whether picking based on the DXGI power preference API is truly required, or if your use case would be addressed by an LUID-style filter (which could be driven by DXGI enumeration in the app). I am thinking about whether implementing/exposing backend-dependent enumeration behaviors risks them getting out of sync with the standard enumeration behavior in A different way of structuring this would be for HALs to expose some kind of "pre-adapter descriptor" that can be used by preference logic in wgpu-core, or passed to an application-provided selection hook. The "pre-adapter descriptor" could have fields "low power preference rank" and "high performance perference rank", obtained in the dx12 case from the DXGI enumeration API. |
|
My use case would work with an LUID-style filter. However, I think making the users resort to handwritten DXGI calls before using wgpu is not great, so we could maybe expose some independent wgpu-helper to address this, essentially separating these two pieces - Main initialization based on LUIDs alone, and a separate power preference->LUID helper. Let me know if that works and I'll update the PR |
|
I do think that more sophisticated functionality than an LUID filter could be useful, I don't mean to discourage pursuing that if you want to, but the LUID filter seemed like it might be a good compromise with broad appeal. The DXGI helper would make it a lot easier on users, I'd be inclined to include it, but loose APIs like that can slip through cracks in maintenance or testing; there might be differing opinions. I have (created last week, for unrelated reasons) an LLM-generated prototype of an LUID filter, based on @jimblandy's proposal for passthrough extensions in #9980. That branch is here: trunk...andyleiserson:wgpu:hal-extensions. I've put the extension API topic on the agenda for the maintainer's meeting next week, the extension API again seems like added complexity that may extend the timeline for getting something included. The obvious shape that fits better with existing APIs is to add something to |
…dapter wgpu-hal gains a defaulted Instance::request_adapter hook that backends can implement to answer an adapter request without exposing every adapter in the system. The DX12 backend implements it by ranking raw DXGI adapters (EnumAdapterByGpuPreference for the preference modes) and exposing candidates one at a time, so only the selected adapter pays D3D12CreateDevice. wgpu-core validates the lazily selected adapter with the same filter pipeline as a full enumeration and falls back to the existing path whenever the backend declines or the result is rejected.
3e64492 to
d653d46
Compare
Connections
None.
Description
Instance::request_adaptercurrently answers a request for one adapter by fully exposing every adapter on every enabled backend, then filtering and sorting the results. On DX12, exposing an adapter means callingD3D12CreateDeviceplus the capability queries — and driver initialization is not cheap. Machines with more than one adapter are the common case now, not the exception: virtually every hybrid-graphics laptop and every desktop with an iGPU-carrying CPU enumerates at least two hardware adapters plus WARP.Measured on a desktop with a discrete NVIDIA GPU and an AMD iGPU (Windows 11),
request_adapterpaid for three device creations to pick one adapter:request_adapter(HighPerformance)(warm)request_adapter(None)(cold)request_adapter(force_fallback_adapter)enumerate_adapters(3 adapters)The iGPU's driver initialization alone was ~2.7 s on every process start, paid just to lose the ranking to the discrete GPU. Firefox already instruments this exact cost in the wild (the
d3d12_expose_adaptertelemetry hook).The change:
wgpu-hal: a new defaultedInstance::request_adapter(power_preference, force_fallback_adapter, surface_hint)trait method. The default returnsNone, meaning "no lazy selection available" — out-of-tree backends compile and behave exactly as before. Mirrored onDynInstance.wgpu-core:Instance::request_adapterfirst asks each backend for a lazy selection. A returned adapter goes through the same validation pipeline as a full enumeration (fallback/device-type check, surface compatibility, indirect-validation limit adjustment, feature/limit filtering,adapter_allowed, limit bucketing); if it fails any step — or the backend returnedNone— the existing enumerate-everything path runs for that backend unchanged, so selection results andRequestAdapterErrorreporting are preserved by construction.IDXGIFactory6::EnumAdapterByGpuPreferenceforLowPower/HighPerformance(the OS is the authority on hybrid-graphics power splits; before DXGI 1.6 the backend declines lazy selection so the full-enumeration ranking runs unchanged), plainEnumAdapters1order forNone(matching what the unsorted full enumeration selects today, with no reordering). Under the preference modes, software rasterizers are ranked last (matchingDeviceType::Cpusorting last in the existing ranking); whenforce_fallback_adapteris set they are the only candidates. Adapters are then exposed one at a time and the first success is returned, so exactly oneID3D12Deviceis created on the common path.No public API changes in
wgpu,wgpu-core, orwgpu-types;enumerate_adaptersis untouched for callers who genuinely want the full list. Other backends can adopt the hook later (Vulkan's per-device property queries are comparatively cheap, but many-GPU compute rigs would still benefit).One deliberate behavioral note: on DX12 systems with several adapters of the same device type (e.g. two discrete GPUs),
LowPower/HighPerformanceselection now follows DXGI's GPU-preference ordering rather than the stable device-type bucket sort overEnumAdapters1order, so ties within a bucket can resolve to a different adapter than before. On the common iGPU + dGPU configurations the two orderings agree. Where they disagree, DXGI's ordering reflects the OS's power/performance policy for the machine rather than DXGI enumeration order.A possible follow-up (not in this PR): once an earlier backend has produced a validated adapter whose device-type rank is the best achievable for the request,
wgpu-corecould skip querying later backends entirely — on default multi-backend instances that would avoid even the one device creation this PR still pays when an earlier backend wins.Testing
None,LowPower,HighPerformance,force_fallback_adapter) select the identical adapter the full-enumeration ranking selects, with the timings above. (This configuration cannot exhibit the same-device-type tie-break divergence described above; that case is documented rather than tested.)request_adapterin every harness bootstrap; CI runs it across backends including DX12/WARP.Squash or Rebase?
Squash.
Checklist
wgpumay be affected behaviorally.CHANGELOG.mdentries for the user-facing effects of this change are present.This PR was generated with Claude