[1.15.0] Runtime mode switches revert + BAR1 surface budget exhausted (256 MB BAR1, RTX 2070 Super, 4K@60)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 8/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- c, macos
- Domain
- operating-systems
Research direction
Start with the nvAllocVram allocation and park/retry path in the NVRMFB kext, plus the kernel log lines and the trace.txt counter snapshots in /Library/NullMoth/display-trace/. Reproducing needs a 256 MB BAR1 card and a runtime mode switch. Done means a mode switch to a smaller mode applies and holds on this configuration, and the surface budget is released before new surfaces are allocated. The issue's R1 to R3 are design changes and need maintainer direction first.
Written by the indexing model from the issue text.
Description
NullMoth 1.15.0 — runtime mode switches revert; BAR1 surface budget exhausted on a 256 MB-BAR1 card
Driver: 1.15.0 · macOS: 26.7 (Tahoe), 25G229, x86_64 · OpenCore: REL-108
GPU: NVIDIA GeForce RTX 2070 Super (10de:1e91), Turing, 8 GB, PCIe x16
BAR1: 256 MB — the card's VBIOS advertises only 64/128/256 MB (sizes supported mask 0x1c0)
Display: LG UltraFine (G1), native 3840x2560 @60 Hz, single display, on the NVIDIA card
SMBIOS: MacPro7,1
Summary
The surface budget inside BAR1 is consumed exactly 5 × 39,321,600 B (five 3840x2560
framebuffers = 196,608,000 of 201,326,592 B). Any further allocation is refused, which produces:
- Every runtime mode switch reverts.
CGDisplaySetDisplayModereports success, the mode applies
for a moment (a visible black flash), then snaps back. New surfaces cannot be allocated while the
old ones are still held. - Zero-copy scan-out never starts (
bufferFlipCount0) → copy path + timer vblank. - WebKit's GPU process spins at 180–211 % CPU retrying refused allocations (~60/s).
- WindowServer holds ~57 % CPU.
The card cannot reach a larger BAR1 by any software means (details below), so this is a request for
the driver to make the constrained case work rather than a config question.
The mode-switch deadlock (measured)
A switch to a smaller mode was attempted with CGDisplaySetDisplayMode:
BEFORE 1920x1280 px=3840x2560 @60.00
AFTER 1920x1080 px=1920x1080 @60.00
OK <- call reported success, mode applied
Five seconds later the display was back on the original mode, and the counters showed the rollback:
CURRENT 1920x1280 px=3840x2560 @60.00
debug.nvrmfb_vram_grants = 32 debug.nvrmfb_vram_releases = 27 outstanding = 5
(6 grants + 6 releases, net 0)
Kernel log during that attempt:
NVRM-fb: nvAllocVram(18481152): REFUSED — BAR1 budget spent (196608000 of 201326592 bytes).
This is the resizable-BAR ceiling, not a VRAM shortage.
NVAccel: NVVidMemory::allocPhysical(<private>): 18452480 bytes REFUSED by the framebuffer (0xe00002bd)
NVRM-fb: nvAllocVram(25034752): REFUSED — BAR1 budget spent (196608000 of 201326592 bytes).
NVAccel: NVVidMemory::allocPhysical(<private>): 24985600 bytes REFUSED by the framebuffer (0xe00002bd)
NVRM-fb: nvAllocVram: attempt 0 phys 0x4000000000 OVERLAPS THE CONSOLE — parking it and rolling again
NVRM-fb: nvAllocVram: attempt 1 phys 0x4000000000 OVERLAPS THE CONSOLE — parking it and rolling again
... attempts 2 .. 7 ...
NVRM-fb: nvAllocVram: park ceiling reached (49807360 bytes over 64 slots) — releasing the reject
The new surfaces are requested before the old ones are released, so the request cannot be
satisfied and the switch is rolled back. Both blockers are present: the budget counter is at its
ceiling and the candidate address overlaps the console aperture.
SURFACE BUDGET — the numbers
BAR1 metadata: 256 MB at 0x4000000000; normal surface budget 192 MB
Boot allocation sequence (all at boot, before any application runs):
nvAllocVram #1: 39321600 bytes [BAR1 39,321,600 / 201,326,592]
nvAllocVram #2: 39321600 bytes [BAR1 39,321,600 / 201,326,592]
nvAllocVram #3: 39321600 bytes [BAR1 39,321,600 / 201,326,592]
nvAllocVram #4: 39321600 bytes [BAR1 78,643,200 / 201,326,592]
nvAllocVram #5: 39321600 bytes [BAR1 78,643,200 / 201,326,592]
nvAllocVram #6: 39321600 bytes [BAR1 117,964,800 / 201,326,592]
nvAllocVram #7: 39321600 bytes [BAR1 157,286,400 / 201,326,592]
nvAllocVram #8: 39321600 bytes [BAR1 196,608,000 / 201,326,592] <- budget full
nvAllocVram #9: 39321600 bytes [BAR1 196,608,000 / 201,326,592] <- wanted a further one, refused
outstanding = 5 × 39,321,600 = 196,608,000 B = 97.7 % of the 192 MB surface budget
holders = WindowServer ("IOUserClientCreator" = "pid 608, WindowServer")
Each entry is exactly one 3840x2560x4 frame, and WindowServer wanted more than the five it got.
Applications (Safari's WebKit.GPU and others) hold nothing — all of their requests are refused
(18,386 refusals in one 10-minute window), so closing applications frees no BAR1.
A ~47 MB region inside BAR1 is reserved and never used
console range BEFORE allocating: 0x4000000000 .. 0x4000800000 (8388608 B, rowBytes 8192)
console probe phys 0x4000000000 pitch 8192 size 8388608
first surface allocation: phys 0x4002f50000
(0x4002f50000 - 0x4000000000 = 49,545,216 B ≈ 47.25 MB)
nvAllocVram: park ceiling reached (49807360 bytes over 64 slots) — releasing the reject
The console aperture is only 8 MB, yet the first surface lands 47.25 MB into BAR1, and the driver's
park ceiling reports 49,807,360 B parked. With 192 MB of budget and 187.5 MB of framebuffers,
that reserved region is roughly what is missing. The driver has a path that can use it:
NVRM-fb: %s: STILL on the console with %d parked -- taking it anyway;
the console is silenced, so nothing else writes there
but in this configuration it parks and gives up instead.
What cannot be changed from the OS side (all measured)
| # | Attempt | Result |
|---|---|---|
| 1 | OpenCore UEFI > Output > Resolution = 1920x1080 + ForceResolution |
No effect. The boot raster stayed 4000x2622; the driver reads the EDID from the display itself. |
| 2 | macOS EDID override — IODisplayEDID on /Library/Displays/Overrides/DisplayVendorID-6d5e/DisplayProductID-2820, preferred detailed timing rewritten to 2560x1706 (validated: both block checksums correct) |
Ignored entirely. ioreg still reports the monitor's original EDID (…0101d0f500a0f0003ea02840…), i.e. the override never reaches the system. The driver logs dpy 2/6 0x400: connected 1 edid 256 B — it reads the panel directly. |
| 3 | -nvrmnobootscreen |
Snapshot confirmed off (boot screen: snapshot OFF) and the console released (framebufferConsoleDisabled() sent), but the console is only 8 MB and BAR1 stayed at 97.7 %. No change. |
| 4 | Removed -nvrmnoflip |
No change — the driver forces the copy path itself (BAR1 not placed). |
| 5 | BIOS parameters (user changed Above 4G Decoding / related) | The capability mask stayed 0x1c0. Above 4G Decoding is working (BAR1 is placed at 0x4000000000), but the mask is set on the GPU side and cannot be changed by the motherboard. |
| 6 | Runtime mode switch to a smaller mode | Applies, then reverts (deadlock above). |
| 7 | Freeing memory by closing GPU-using applications | Frees nothing — the holders are WindowServer framebuffers. |
Why a larger BAR1 is not reachable here
OpenCore's own ResizeGpuBars cannot help — from SetResizableBar.c:
NewCapabilities = ... | Entries[Index].ResizableBarCapability.Bits.BarSizeCapability;
NewCapabilities &= PCI_BAR_CAP_LIMIT (Size); // restricts, never expands
It only chooses among the sizes the GPU already advertises. Changing the advertised set requires
writing the GPU STRAPS bits from a firmware DXE driver (the NvStrapsReBar approach), which means
modifying and re-flashing the motherboard image — on this board (Comet Lake-H LPC 8086:068d, most
likely a small-form-factor board with a mobile CPU) that is not a realistic option, and the GPU VBIOS
itself is signed and encrypted.
The driver states the same thing in its own words:
NVRM-xnu: starting the NVIDIA card: waiting 10 s before it takes over the screen, because its
memory window (BAR1) is too small to move off the boot screen. This is not a freeze.
To start in about a second instead, enable "Above 4G Decoding" and "Resizable BAR"
in the BIOS.
Requests
R1 — Use the reserved region instead of parking. ~47 MB of BAR1 sits unused between the console
aperture and the first surface. Since the driver already has the "taking it anyway; the console is
silenced" path, taking it here would raise the free budget from 4.5 MB to roughly 50 MB, which is
enough for a mode switch on this panel.
R2 — Release before allocating on a mode switch (suggested by a user here, and this is the one I
would most like to see). The current shape is:
allocate new surfaces -> refused -> roll back to the old mode
What is needed:
1. quiesce the display (the panel may go black for a moment — that is acceptable)
2. release the current scanout surfaces so the BAR1 budget counter drops
3. allocate the new mode's surfaces
4. if any step fails, rebuild the previous mode from the released data
nvAllocVram's existing park/retry machinery already covers the failure path, so this looks like a
narrower change than it might first appear. It would turn "any mode switch on a small-BAR1 card
reverts" into "mode switches work, with a brief black frame".
R3 — Skip or shrink the console reservation when -nvrmnobootscreen is set. The console is only
8 MB but the first surface still lands 47.25 MB in, so the reservation looks larger than the console
itself.
Evidence available on this machine
The 1.15.0 collectors are running:
/Library/NullMoth/display-trace/trace.txt live counter snapshots
/Library/NullMoth/display-trace/kernel.txt driver kernel log
/Library/NullMoth/display-trace/ws.txt WindowServer state
/Library/NullMoth/wsreset.log before/after WindowServer restart
trace.txt also contains four USER: flicker seen now markers recorded with Record the flicker,
so the strobe was live at capture time. Send logs uploads all of it.
I can run a diagnostic build, enable extra logging, or test a patched NVRMFB and report back.
Machine detail
GPU NVIDIA GeForce RTX 2070 Super (0x10de:0x1e91), 8 GB, PCIe x16, Slot-1
GPU firmware GSP r610 — /Users/Shared/nvfw/nvidia/610.57.04
kexts loaded com.nullmoth.NVRM / NVAccel / NVRMFB / NVRMAGDC (all 4)
Metal Metal 3
BAR1 pa 0x4000000000 len 268435456 (256 MB), surface budget 192 MB
BAR0 len 16777216 BAR3 len 33554432
Display LG UltraFine (G1), 3840x2560, 24-bit ARGB8888, single display, main
Current mode 1920x1280 @2x (framebuffer 3840x2560) @ 60.00 Hz
Modes offered 26 (59.88 / 60.00 / 60.25 Hz)
debug.nvaccel_iop_flip 0 bufferFlipCount 0
nvrmfb_vram_mapped_bytes 196608000 nvrmfb_vram_budget_bytes 201326592 (97.7 %)
- Dominant language
- Rust
- Stars
- 2.5k
- Forks
- 233
- Avg merge
- 1h 37m
- Merged PRs (30d)
- 1
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from nullmoth/nvidia-macos-driver
-
[Bug] `nullmoth-wsreset.sh` never restarts WindowServer at the real login window: `/dev/console` is `_windowserver`, not `root` (guard + test blind spot)Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
nullmoth/nvidia-macos-driver#141 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
nullmoth/nvidia-macos-driver#28 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 12/100
nullmoth/nvidia-macos-driver#206 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 5/5 Over a week Newbie friendliness 12/100
nullmoth/nvidia-macos-driver#205 · 1 comment ·
Maintainers usually reply within 1 day
-
bug
Difficulty 5/5 Over a week Newbie friendliness 5/100
nullmoth/nvidia-macos-driver#201 ·
Maintainers usually reply within 1 day
All issues in nullmoth/nvidia-macos-driver
Similar issues
-
agent:iq3-xxs area: cli duplication enhancement priority: P3 ready-for-agent
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 85/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
bytecodealliance/rustix#1703 ·
Maintainers usually reply within 3 days
-
bug discussion
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
medialab/xan#1201 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
farion1231/cc-switch#8298 ·
Maintainers usually reply within 1 day