[PR #511] [CLOSED] Implement GPU load balancing #501

Closed
opened 2026-10-15 13:01:03 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/games-on-whales/wolf/pull/511
Author: @tjacoby2006
Created: 9/25/2026
Status: ❌ Closed

Base: stable ← Head: features/gpu-load-balancing-implementation


📝 Commits (1)

  • 1f6acc0 Implement GPU load balancing

📊 Changes

28 files changed (+634 additions, -100 deletions)

View changed files

📝 docs/modules/user/pages/configuration.adoc (+29 -1)
📝 src/gst-video-context/CMakeLists.txt (+3 -2)
📝 src/gst-video-context/gst-video-context/gst-video-context.cpp (+61 -59)
📝 src/gst-video-context/gst-video-context/gst-video-context.hpp (+19 -1)
📝 src/gst-video-context/gst-video-context/no-cuda-context.cpp (+13 -1)
📝 src/moonlight-server/api/endpoints.cpp (+4 -4)
📝 src/moonlight-server/events/events.hpp (+4 -0)
📝 src/moonlight-server/platforms/hw.hpp (+3 -0)
📝 src/moonlight-server/platforms/hw_linux.cpp (+23 -0)
📝 src/moonlight-server/platforms/hw_unknown.cpp (+5 -1)
📝 src/moonlight-server/runners/docker.cpp (+16 -6)
📝 src/moonlight-server/sessions/common.cpp (+25 -0)
📝 src/moonlight-server/sessions/lobbies.cpp (+1 -1)
📝 src/moonlight-server/sessions/moonlight.cpp (+9 -2)
📝 src/moonlight-server/state/configTOML.cpp (+32 -1)
📝 src/moonlight-server/state/data-structures.hpp (+10 -3)
📝 src/moonlight-server/state/default/config.include.toml (+8 -1)
📝 src/moonlight-server/state/default/config.v7.toml (+11 -0)
➕ src/moonlight-server/state/gpu_scheduler.cpp (+102 -0)
➕ src/moonlight-server/state/gpu_scheduler.hpp (+45 -0)

...and 8 more files

📄 Description

Addresses #173 and #449.

Multi-GPU session scheduling for Wolf Moonlight server

Replaces the "multi-GPU load balancing planned for future releases" note with an actual implementation: each Moonlight session is now independently assigned to a GPU using weighted least-loaded scheduling, and all pipeline components (compositor, encoder, CUDA context) are bound to that same GPU.

Configuration

  • New [[gpus]] entries in config.toml allow explicit render nodes with relative weights; empty list (default) auto-discovers /dev/dri/renderD* with equal weight
  • New excluded_gpus list to omit specific render nodes from scheduling
  • Config validation rejects invalid entries, duplicate nodes, and GPUs without a compatible H.264 encoder at startup

Scheduling (gpu_scheduler.{hpp,cpp})

  • Thread-safe GPUScheduler with weighted least-loaded selection using cross-multiplication (avoids floating-point tie instability)
  • Acquire/release lifecycle tied to session start/stop via event bus handlers

Correct GPU binding per session

  • CUDA context: Replaces the single shared global gst_context atom with a GstVideoContextProvider that caches contexts per PCI bus ID. Contexts are set on pipelines before NULL→READY transition to avoid implicit device-0 fallback.
  • NVIDIA encoder selection: Fixes getCudaDeviceIndexFromPciBusId to use the CUDA driver API (cuDeviceGetByPCIBusId) instead of sorting /proc/driver/nvidia/gpus entries (which reports kernel minors, not CUDA ordinals). Pipeline strings are rewritten at session start to use device-specific NVENC factories (e.g., nvh265device1enc), with a hard failure if the factory doesn't exist rather than silently encoding on the wrong GPU.
  • VAAPI encoder selection: Pipeline strings are rewritten to use per-render-node element names (e.g., varenderD129h264enc).
  • Guest compositor / Mesa: WLR_DRM_DEVICES and DRI_PRIME environment variables are set so the in-container Wayland compositor and Mesa also target the selected GPU.
  • Docker runner: NVIDIA device requests and NVIDIA_VISIBLE_DEVICES are scoped to the specific device matching the render node instead of "all".

API change

  • The API-supplied encoder render node is no longer honored for the video session; the scheduler's assignment always wins, preventing a client from silently moving encoding to a different GPU.

Tests

  • testGPUScheduler.cpp: weighted scheduling order, invalid/duplicate entry handling, exclusion behavior, and "no fallback to discovery when all configured GPUs are excluded"
  • testNvidiaEncoderBinding.cpp: pipeline string rewriting for device-specific NVENC factories

Testing
This was tested on a machine with 2x NVidia P100s. Would be nice to get a variety of hardware to test.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/games-on-whales/wolf/pull/511 **Author:** [@tjacoby2006](https://github.com/tjacoby2006) **Created:** 9/25/2026 **Status:** ❌ Closed **Base:** `stable` ← **Head:** `features/gpu-load-balancing-implementation` --- ### 📝 Commits (1) - [`1f6acc0`](https://github.com/games-on-whales/wolf/commit/1f6acc09efe1005fb58b27f9188ad33dbe6db8cc) Implement GPU load balancing ### 📊 Changes **28 files changed** (+634 additions, -100 deletions) <details> <summary>View changed files</summary> 📝 `docs/modules/user/pages/configuration.adoc` (+29 -1) 📝 `src/gst-video-context/CMakeLists.txt` (+3 -2) 📝 `src/gst-video-context/gst-video-context/gst-video-context.cpp` (+61 -59) 📝 `src/gst-video-context/gst-video-context/gst-video-context.hpp` (+19 -1) 📝 `src/gst-video-context/gst-video-context/no-cuda-context.cpp` (+13 -1) 📝 `src/moonlight-server/api/endpoints.cpp` (+4 -4) 📝 `src/moonlight-server/events/events.hpp` (+4 -0) 📝 `src/moonlight-server/platforms/hw.hpp` (+3 -0) 📝 `src/moonlight-server/platforms/hw_linux.cpp` (+23 -0) 📝 `src/moonlight-server/platforms/hw_unknown.cpp` (+5 -1) 📝 `src/moonlight-server/runners/docker.cpp` (+16 -6) 📝 `src/moonlight-server/sessions/common.cpp` (+25 -0) 📝 `src/moonlight-server/sessions/lobbies.cpp` (+1 -1) 📝 `src/moonlight-server/sessions/moonlight.cpp` (+9 -2) 📝 `src/moonlight-server/state/configTOML.cpp` (+32 -1) 📝 `src/moonlight-server/state/data-structures.hpp` (+10 -3) 📝 `src/moonlight-server/state/default/config.include.toml` (+8 -1) 📝 `src/moonlight-server/state/default/config.v7.toml` (+11 -0) ➕ `src/moonlight-server/state/gpu_scheduler.cpp` (+102 -0) ➕ `src/moonlight-server/state/gpu_scheduler.hpp` (+45 -0) _...and 8 more files_ </details> ### 📄 Description Addresses #173 and #449. **Multi-GPU session scheduling for Wolf Moonlight server** Replaces the "multi-GPU load balancing planned for future releases" note with an actual implementation: each Moonlight session is now independently assigned to a GPU using weighted least-loaded scheduling, and all pipeline components (compositor, encoder, CUDA context) are bound to that same GPU. **Configuration** - New `[[gpus]]` entries in `config.toml` allow explicit render nodes with relative weights; empty list (default) auto-discovers `/dev/dri/renderD*` with equal weight - New `excluded_gpus` list to omit specific render nodes from scheduling - Config validation rejects invalid entries, duplicate nodes, and GPUs without a compatible H.264 encoder at startup **Scheduling** (`gpu_scheduler.{hpp,cpp}`) - Thread-safe `GPUScheduler` with weighted least-loaded selection using cross-multiplication (avoids floating-point tie instability) - Acquire/release lifecycle tied to session start/stop via event bus handlers **Correct GPU binding per session** - **CUDA context**: Replaces the single shared global `gst_context` atom with a `GstVideoContextProvider` that caches contexts per PCI bus ID. Contexts are set on pipelines *before* NULL→READY transition to avoid implicit device-0 fallback. - **NVIDIA encoder selection**: Fixes `getCudaDeviceIndexFromPciBusId` to use the CUDA driver API (`cuDeviceGetByPCIBusId`) instead of sorting `/proc/driver/nvidia/gpus` entries (which reports kernel minors, not CUDA ordinals). Pipeline strings are rewritten at session start to use device-specific NVENC factories (e.g., `nvh265device1enc`), with a hard failure if the factory doesn't exist rather than silently encoding on the wrong GPU. - **VAAPI encoder selection**: Pipeline strings are rewritten to use per-render-node element names (e.g., `varenderD129h264enc`). - **Guest compositor / Mesa**: `WLR_DRM_DEVICES` and `DRI_PRIME` environment variables are set so the in-container Wayland compositor and Mesa also target the selected GPU. - **Docker runner**: NVIDIA device requests and `NVIDIA_VISIBLE_DEVICES` are scoped to the specific device matching the render node instead of `"all"`. **API change** - The API-supplied encoder render node is no longer honored for the video session; the scheduler's assignment always wins, preventing a client from silently moving encoding to a different GPU. **Tests** - `testGPUScheduler.cpp`: weighted scheduling order, invalid/duplicate entry handling, exclusion behavior, and "no fallback to discovery when all configured GPUs are excluded" - `testNvidiaEncoderBinding.cpp`: pipeline string rewriting for device-specific NVENC factories **Testing** This was tested on a machine with 2x NVidia P100s. Would be nice to get a variety of hardware to test. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-15 13:01:03 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
games-on-whales/wolf#501
No description provided.