The nvgfx branch is NullMoth's 2.1.0 plus the changes in the README table. This file is the index of what the fork adds and how to switch it. The reasoning behind each change is in notes/.
main: upstream as pulled (tagsv2.1.0..v2.4.3).nvgfx-2.4.3(current): the fork's changes on top of the upstream 2.4.3 tag, packages labelled2.4.3-nvgfx.N(package.shwrites the label intoNVGFX-VERSION, REF = NullMoth's 2.4.3 pkgroot). 2.4.3 brought upstream's own fix for the thread_call pile-up (#295, every NVRMFB callback runs one at a time) and the merged PR 294 (AIR digest kept on the text), so the fork's digest half is upstream's now and only the per-module memo stays ours. The branch also carries the boot hold fix (nvrmholdcap). Earlier:nvgfx-2.4.1(2.4.1-nvgfx.1, N-181 to N-184). It carries everything fromnvgfx-2.3.7: the build harness (build/nvgfx), the NVRM power glue (nvrmtgp115 W,nvrmgps/nvrmnvpcf,nvrmphysctl), the WindowServer shader-cache fallback, the plugin work from the 2.3.7 branch (a function that calls through a visible or intersection function table waits for its pipeline link; the per-module work of newFunctionWithName is kept across libraries; the commit-time ahead pass runs on its own queue; one framebuffer-fetch descriptor set per pass; refused pipeline links are remembered; CPU-written IOSurfaces upload asynchronously at bind; a repeated setRenderPipelineState inside a pass is skipped; the render encoder keeps its sampler-variant choice), the Dock badge mip fill, the unbound-slot sampler, and upstream PR 273 (Leaf-lsgtky: scaled desktop modes at the head viewport) ahead of its merge. For the 2.4.1 base: the fork's own_PSRanswer tonv_acpi_get_powersourceis gone (2.4.1 reads the battery service itself;nvrmgps/nvrmnvpcf/nvrmtgpstill answer notebook and battery from ACPI), the harness links upstream'snv_xnu_power.cpp, and the TGP tick refreshes the power source itself. Every fork hunk is marked with<wtl>comment tags. Older branches:nvgfx-2.3.7(2.3.7-nvgfx.1, N-169 to N-180 and the N-184/N-185 rollback),nvgfx-2.3.5(2.3.5-nvgfx.1 to .3).nvgfx: our changes on top of the upstream 2.1.0 tag (2.1.0-nvgfx.N, last tested2.1.0-nvgfx.20). Kept for the trace tooling ondock-traceand as the rollback.
All default off. With none set the kexts behave as upstream, apart from the Terminal fix and the present path, which are always on.
| Boot-arg | Kext | Effect |
|---|---|---|
nvrmtgp=1 |
NVRM | at boot on AC, put the GSP in TGP mode (115 W on the RTX 5070 Laptop instead of the 50 W default) and follow _PSR afterwards |
nvrmgps=1 |
NVRM | answer the GPS _DSM on the GPU node, so the RM's platform request handler finds GPS_2X |
nvrmnvpcf=1 |
NVRM | answer the NVPCF _DSM on \_SB.NPCF, as Linux does |
nvrmphysctl=1 |
NVRM | root-only control escape (0xE4) used by nvgfx-smi --phys and --ctrl |
nvrmholdcap=<s> |
NVRM | ceiling for the boot hold while the bring-up is still running (40 to 600 s, default 120; upstream releases the hold at settle + 40 s regardless, which loses the desktop when the GSP bring-up is slow) |
nvgfxwsfetch=1 |
plugin | enable framebuffer fetch in WindowServer only (Dock blur investigation) |
nvgfxfetchcopy=1 |
plugin | emulate framebuffer fetch with copies in WindowServer when fetch is off |
Upstream's own boot-args (nvfb, nvaccel, nvfbheads, nvrmacpibl, -nvrmnoflip, -nvaccelnoheadless and the rest) are unchanged.
debug.nvgfx_present_depth: how many flips may be in flight (default 2).debug.nvgfx_fb_*: present path counters (submits, latched, refused, superseded, timeouts, commit and latch times, vblank hops).debug.nvgfx_dirty_*: why NVAccel surface dirty marks were refused or re-wired (parked VRAM, no resource, scanout).debug.nvgfx_tgp_*: TGP mode state and the last control results.
Read from /Library/GPUBundles/nvmtl/nvrm610.conf in release builds, since the plugin unsets NVMTL_* variables at load.
| Switch | Effect |
|---|---|
NVMTL_NVGFX_LEGACY_FBFETCH=1 |
every render pass is a fetch pass again (2.1.0 behaviour, brings the Terminal overdraw back) |
NVMTL_NVGFX_LEGACY_SEMU=1 |
2.1.0's broad stencil-export detector |
NVMTL_NVGFX_FETCH_COPY=1 |
framebuffer fetch emulated by copies for every process |
NVMTL_NVGFX_FCOPY_FORCE_FAIL=1 |
make the fetch-copy pipeline fail, to test the rebuild-without-it path |
NVMTL_NVGFX_SURFOUT_TRACE=1 |
syslog trace of surface copy-outs |
NVMTL_NVGFX_WSTRACE=1 |
WindowServer trace: samplers, pipelines, small render targets, the backdrop composite draw, draws that sample small square textures |
NVMTL_NO_POOL_REUSE=1 |
upstream switch: command pools and address pages are created and destroyed per commit instead of being kept behind the completion witness; set on the test machine since 2026-10-20 as the workaround for the per-process backend shutdown below (I-085) |
| Path | What |
|---|---|
plugin/nvmtl_vk.c, plugin/nvmtl_vk.h |
render pass kinds per pipeline, pass restart, fetch-copy emulation |
plugin/NVMTLObjects.m |
encoder changes for the pass restart, WindowServer trace |
plugin/NVMTLLibraryLoad.m |
shader cache fallback folder |
plugin/nvgfx_fetch_copy.h, plugin/nvgfx_wstrace.h |
the fetch-copy and trace helpers |
kexts/NVRMFB/fb/nvrm-fb.cpp, kexts/NVRMFB/fb/nvgfx_present_abi.h |
the present path |
kexts/NVRM/accel/nvrm-accel.cpp |
dirty-mark accounting, parked VRAM re-wiring |
kexts/NVRM/nvrm_power.cpp, nvrm_physctl.c, nvrm_physctl_abi.h |
TGP mode, platform _DSMs, control escape |
kexts/NVRM/os-xnu.cpp, os-xnu2.cpp, NVRM.cpp |
the glue the harness needed to rebuild NVRM from source |
nvk/nvgfx/01-nv_push-opencl.patch |
one build fix on top of NullMoth's NVK patches |
build/nvgfx/ |
build scripts, the NVRM harness, comparison tools, package.sh |
tools/nvgfx-smi, tools/nvgfx-burn |
telemetry and load tools |
notes/ |
the working notes behind each change |
RTX 5070 Laptop (10DE:2D18), 2560x1600 at 165 Hz, macOS 26.7.1 (25G241), OpenCore 1.0.8, package 2.1.0-nvgfx.20:
- Metal 3 device, accelerated desktop, Liquid Glass renders.
- Present path: flips latched at the panel rate (165/s), window drags about 148 fps; shipped 2.1.0 on the same machine did about 54.
- Power: 115 W enforced with
nvrmtgp=1(VBIOS default 50 W; Windows runs the same GPU at 115 W with Dynamic Boost). - Terminal overdraw from 2.1.0: gone since nvgfx.1.
- Geekbench 6.7.1 Metal: 103094 on 2.4.3-nvgfx.1 (2026-10-20, https://browser.geekbench.com/v6/compute/6944564); Background Blur completes here (upstream #292 reports a hang at that subtest on other machines).
- WindowServer CPU after boot: normal since nvgfx.20 (before: about 150 % while it re-translated every shader).
- Still wrong (I-085, 2026-10-20): Terminal.app and iTerm2 froze for good three times in two hours under heavy text output.
The plugin logged "vk: completion witness missing; disabling backend and retaining all in-flight resources" in the
process (upstream
nvmtl_vk_submit_finish, unchanged here), every later command buffer was nil and CoreAnimation's CG glyph queue waited forever. Workaround:NVMTL_NO_POOL_REUSE=1in nvrm610.conf (not yet proven). Upstream issue to be written by hand; record in project-legionevidence/native/I-085-completion-witness-hang/. - Still wrong: the Dock's blur draws as blocks (a WindowServer framebuffer-fetch problem, under investigation with the trace switches).
All of the driver is NullMoth's work unless a commit on the nvgfx branch says otherwise. The full upstream history is kept here unchanged. Bug reports and fixes that apply to everyone go to NullMoth first.
Required Notice: Copyright (c) 2026 NullMoth Systems (https://nullmothsystems.com)
Distributed under the PolyForm Noncommercial License 1.0.0 (LICENSE, https://polyformproject.org/licenses/noncommercial/1.0.0): noncommercial use only. Third-party parts keep their own terms, listed in NOTICE.