On a laptop with both an integrated and a discrete GPU, Casso takes whatever
adapter Windows hands it. The desk scene is the first presentation heavy
enough for that choice to matter — and it is exactly the kind of app where a
user might reasonably want either answer (the discrete part for the smoothest
scene, the integrated one to stop the fans and save battery).
What to add
A global preference — the host's graphics budget is a property of the machine
Casso is running ON, not of the machine it is emulating, so this belongs
beside the antialiasing setting rather than per emulated machine or per
monitor.
Three-way, defaulting to Automatic:
- Automatic — whatever Windows picks (today's behavior)
- Integrated —
DXGI_GPU_PREFERENCE_MINIMUM_POWER
- Discrete —
DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE
Deliberately NOT an AC/battery auto-switch. Windows already has a per-app
graphics preference, and a second policy fighting it tends to surprise people
more than it helps.
Prior art
MatrixRain (peer repo) already solved the enumeration half:
MatrixRainCore/WindowsAdapterProvider.cpp walks EnumAdapters1, filters
DXGI_ADAPTER_FLAG_SOFTWARE, and resolves the system default through
IDXGIFactory6::EnumAdapterByGpuPreference(UNSPECIFIED) — all behind an
IAdapterProvider interface so tests drive it from a vector rather than from
real hardware. That shape ports over directly.
The real work: device recreation
Honouring a changed preference means tearing down the D3D device and building
a new one, which is the same machinery as device-loss recovery — still
open from the superseded spec 008:
FR-018: The mesh renderer MUST handle D3D device loss and recreation by
rebuilding shaders, depth target, mesh registry, and faceplate
render-textures without leaking resources.
That spec's adjacent edge cases belong with it: a minimized / zero-client-area
window must skip the 3D pass cleanly without depth-buffer churn, and a runtime
DPI change must re-rasterize so labels stay crisp.
The scene has accumulated a lot of device-owned state since 008 was written,
and all of it dies with the device:
- the multisampled scene target, its resolve texture, and the depth buffer
- the two cached scene plates (
DeskScene::m_backPlate* / m_frontPlate*)
- every
Dxui3DRenderer::StaticMesh vertex buffer — note these are keyed by a
revision counter, so after a device change the cache would happily hand back
ComPtrs belonging to a destroyed device with the revision unchanged. This
one is a silent failure rather than a loud one, and needs an explicit
invalidation hook.
CrtPostProcess's ping-pong textures and the offscreen scene-content target
Testing note
Device recreation is the part that cannot be proven on a single-adapter
machine. The interface split MatrixRain uses makes the selection logic
unit-testable without hardware, but the teardown/rebuild path wants a real
dual-GPU laptop to validate against.
On a laptop with both an integrated and a discrete GPU, Casso takes whatever
adapter Windows hands it. The desk scene is the first presentation heavy
enough for that choice to matter — and it is exactly the kind of app where a
user might reasonably want either answer (the discrete part for the smoothest
scene, the integrated one to stop the fans and save battery).
What to add
A global preference — the host's graphics budget is a property of the machine
Casso is running ON, not of the machine it is emulating, so this belongs
beside the antialiasing setting rather than per emulated machine or per
monitor.
Three-way, defaulting to Automatic:
DXGI_GPU_PREFERENCE_MINIMUM_POWERDXGI_GPU_PREFERENCE_HIGH_PERFORMANCEDeliberately NOT an AC/battery auto-switch. Windows already has a per-app
graphics preference, and a second policy fighting it tends to surprise people
more than it helps.
Prior art
MatrixRain(peer repo) already solved the enumeration half:MatrixRainCore/WindowsAdapterProvider.cppwalksEnumAdapters1, filtersDXGI_ADAPTER_FLAG_SOFTWARE, and resolves the system default throughIDXGIFactory6::EnumAdapterByGpuPreference(UNSPECIFIED)— all behind anIAdapterProviderinterface so tests drive it from a vector rather than fromreal hardware. That shape ports over directly.
The real work: device recreation
Honouring a changed preference means tearing down the D3D device and building
a new one, which is the same machinery as device-loss recovery — still
open from the superseded spec 008:
That spec's adjacent edge cases belong with it: a minimized / zero-client-area
window must skip the 3D pass cleanly without depth-buffer churn, and a runtime
DPI change must re-rasterize so labels stay crisp.
The scene has accumulated a lot of device-owned state since 008 was written,
and all of it dies with the device:
DeskScene::m_backPlate*/m_frontPlate*)Dxui3DRenderer::StaticMeshvertex buffer — note these are keyed by arevision counter, so after a device change the cache would happily hand back
ComPtrs belonging to a destroyed device with the revision unchanged. Thisone is a silent failure rather than a loud one, and needs an explicit
invalidation hook.
CrtPostProcess's ping-pong textures and the offscreen scene-content targetTesting note
Device recreation is the part that cannot be proven on a single-adapter
machine. The interface split MatrixRain uses makes the selection logic
unit-testable without hardware, but the teardown/rebuild path wants a real
dual-GPU laptop to validate against.