Brand new AMD temporal dither disable method discovered by me a few months ago.
This method does NOT use AHKInject. Changes happen in realtime without needing to reboot every time so you can actually see the screen change in front of your own eyes 🙂
✅ Works best for external monitors! AMD dither disable method is especially needed for external because on AMD Macs, the AMD GPU is hard-wired to the HDMI port — there is no way to use Intel on external at all (aside from something like a DisplayLink adapter that skips the GPU entirely…)
This method uses a different signed(!!) kext I discovered within an open-source repository related to vision and neuroscience research — a field where uncoincidentally, ensuring display color output is completely clean and unaltered is critical. Even the source code for the kext itself is available. The repository has been maintained since 1998 and is still actively updated today.
This may also work on 2016 and 2017 Macs with AMD dGPU, but your mileage may vary. All I know is that it works with HDMI and Mini DisplayPort output on my 2015 15" Retina MacBook Pro 🦌 ;)
I am on macOS Monterey 12.6.8.
@macsforme @jordan
⚠️ Disclaimer: Unfortunately, on the internal built-in Retina Display this does NOT seem to "fully" disable temporal dithering (at least on my 2015 15" rMBP).
Even the Intel method (nvram boot-args="dither=0") doesn't work entirely on the 2015 rMBP's built-in display — both the Intel and AMD methods actually do successfully disable dithering on at least the color profile (after disabling dithering, changing Software Brightness in BetterDisplay will now cause banding shifts). However, I still notice "shimmering" on the built-in display despite this.
I think what's going on here is that the 2015 15" rMBP has a 6bit+FRC panel at the hardware level, so even if the built-in display sent an undithered 8bit signal, the panel itself will still dither to achieve 16M colors. I don't think there's currently a way to force a true 6bit signal on modern macOS (correct me if I'm wrong).
Otherwise, this method still works great on external HDMI and Mini DisplayPort output… »»» as long as your external monitor is true 8bit. «««
(It does "kind of" improve the built-in display "slightly"… but unfortunately not enough to make the 2015 15" rMBP built-in display actually comfortable for me in either graphics mode.
FWIW, currently my most usable built-in macOS laptop display is the 2015 12" "fanless" MacBook with macOS Mojave 10.4.6 and boot-args="dither=0", which on the 12" is actually able to significantly change built-in display output unlike many other Macs.)
⬥⋯ THE METHOD ⋯⬥
16 steps remain.
1. System Integrity Protection likely needs to be disabled for this to work, so reboot into recovery, run csrutil disable, and reboot back to macOS if needed.
2. Go to this GitHub repository: https://github.com/kleinerm/Psychtoolbox-3
3. Click the “<> Code” button and download the source code ZIP. For future reference, I downloaded the commit from Jan 25, 2024 🌀
4. Unzip “Psychtoolbox-3-master.zip” somewhere.
5. Navigate to Psychtoolbox-3-master » Psychtoolbox » PsychHardware. There should be a file called “PsychtoolboxKernelDriver64Bit.kext.zip”. Unzip it.
(Note that there is two ZIP files, “64Bit” and “UpToDate_Unsigned”. For the 2015 15" rMBP, use the 64Bit kext because it’s officially signed.
Interestingly, the 64Bit kext apparently only supports AMD cards up to ~2017, but the UpToDate kext includes experimental support for 2018 AMD cards and later. I haven’t tested this, and the newer kext is unsigned so it may require some extra steps to install, but this might be useful if you have a newer Mac with a dGPU.)
6. Download Kext Droplet from https://github.com/chris1111/Kext-Droplet-macOS/releases/tag/V1. Move the application inside the .dmg somewhere — launch it through right click » Open to bypass Gatekeeper.
7. Drag "PsychtoolboxKernelDriver64Bit.kext" onto the Kext Droplet window. It will pop up a bunch of dialogs, just confirm through them to get the kext installed into /Library/Extensions.
8. Go to System Preferences » Security & Privacy — you might need to click through a “new system software” thing at the bottom to get the kext to install. For some reason, the allow button wasn’t doing anything the first time I opened System Preferences, so I just quit and reopened System Preferences again and it worked 🙂
9. Reboot.
THIS is where it gets interesting…
10. Install the Brew package manager if needed. Then run brew install octave
in Terminal to install GNU Octave, which is needed to run Psychtoolbox.
11. Disable automatic graphics switching in System Preferences » Battery, install gfxCardStatus if needed, and force discrete graphics. (The dGPU needs to be active before opening Octave for this to work. You should now see "d" or "a" on the gfxCardStatus menu bar icon. If you still see "i", try again.)
12. (For external monitors) install BetterDisplay and ensure Color Depth is set to 8bpc.
13. Then change these settings in BetterDisplay to prevent the app from messing with your color profile: Turn Apple display hardware brightness control OFF on the internal display. Turn Combined brightness OFF on all displays. Turn Brightness control using native brightness keys OFF in the Keyboard tab.
13. Run octave --gui
in Terminal to launch Octave.
14. The Octave window should open with its own command prompt. Switch to the Octave prompt, then cd to the folder called Psychtoolbox inside the repo. For example, I typed cd ~/Downloads/Psychtoolbox-3-master/Psychtoolbox
.
15. Type SetupPsychtoolbox
and press return. Wait for setup to complete, for me this worked the first time without any errors.
The next step uses an infinite loop because otherwise, dithering will re-enable after resuming from sleep. This forces dithering to re-disable every few seconds to work around that.
The third number on the ScreenToHead lines select which monitor to use, 0 is internal and and 1 is external. I've only tested HDMI and MiniDisplayPort, potentially dedicated Thunderbolt monitors use a different number.
The second number on the ConfigureDisplay lines disables dithering when set to 0. I'm not sure what the first number does.
If you don't have an external monitor and want to experiment with only the built-in display, omit the ScreenToHead line with "0, 0, 1" and the second ConfigureDisplay line below it. However, given my disclaimer at the beginning about the built-in display potentially using hardware-level FRC, don't expect this to 100% fix temporal dithering on your Retina display. (it will get slightly better but not fully disable, at least on my 2015 15" rMBP).
〜 The final step 〜
- 16. Paste in these lines and press return.
while(1)
Screen('Preference', 'ScreenToHead', 0, 0, 0);
Screen('ConfigureDisplay', 'Dithering', 0, 0);
Screen('Preference', 'ScreenToHead', 0, 0, 1);
Screen('ConfigureDisplay', 'Dithering', 0, 0);
pause(5);
end
As long as your external monitor is true 8bit… temporal dithering should now be DISABLED on your HDMI or DisplayPort external monitor!
⬥⋯ NOTES ⋯⬥
You’ll need to keep Octave running — so minimize the Octave window, do NOT fully close it. If you quit Octave, log out, or reboot, you’ll need to run the same script again. Press the up key to cycle back through previous commands to quickly recall each line.
- The script re-runs itself every five seconds — because usually, returning from sleep mode, changing the resolution, switching between integrated and discrete graphics, or connecting/disconnecting an external display would re-enable temporal dithering.
- The loop should mitigate all of these, but you might still notice dithering on the password screen after returning from sleep, before 5 seconds pass and it disables again.
- Locking the graphics mode to discrete is recommended to avoid automatic switching, as per Step 11.
Depending on your monitor, the "Force RGB EDID" method may also be needed for optimal output.
Monitors with DVI inputs get a bit weird:
Sometimes I've noticed that the second number actually seems to disable dithering when set to "1" instead on certain DVI monitors — even though "1" would usually re-enable temporal dithering on HDMI and DisplayPort monitors. For these DVI monitors, mess around with both 'Dithering', 0, 0
and 'Dithering', 0, 1
in the ConfigureDisplay command that's after the "ScreenToHead 0,0,1" command, and pick whichever results in the least "moving static".
If you want to experiment, there are numbers after 0 and 1 that do interesting things:
I haven't tested these more obscure values fully and you may need to reboot after to get back to a state that works as expected.
However, if you mess around with setting the second number on both lines to other random values like 5, 6, 8, whatever (I do not know which numbers to specifically use to predictably reproduce this)…
You'll eventually end up in a mysterious state where you still technically have an 8bit signal, but now it's effectively "truncated" to 6bit with even MORE banding.
Aside from this, I do not know anything about these higher numbers or what they do.