Source
- URL: https://x.com/NOTimothyLottes/status/1881908248544166159
- Author: NOTimothyLottes (@NOTimothyLottes)
- Posted: 2025-01-22 03:34:25
Thread
1/ @NOTimothyLottes
Simple napkin analysis of that high-end CRT scalar (monochome one) … notes about cleaning up the code.
I track state usage throughout the code to ballpark expected max VGPR count. This needs around 57 VGPRs mid way through. Note <=64 VGPRs is often important.
2/ @NOTimothyLottes
So yeah packed 16-bit is the reason complex shaders like this are possible on PC GPUs. It is the way to single pass and avoid multiple trips through DRAM.
3/ @NOTimothyLottes
This code does 2 parallel kernels, but also breaks the computation of each kernel into 4 weighted sums. This is done for 2 reasons: (a.) possibly higher precision, (b.) to enable parallel work to hide VALU latency (ILP)!
4/ @NOTimothyLottes
This shader needs roughly
410 VALU op clocks
And 253 of those (more than half)
are in the load latency window
So this shader will trivially hide a lot of latency without getting to high wave/CU counts
5/ @NOTimothyLottes
BUT
Traditionally this has been a curse, because AMD’s oldest wave first scheduling breaks down when the oldest is ultra high VALU load without stalls! The other waves often cannot get forward progress to get loads out to debubble the memory sub-system
6/ @NOTimothyLottes
Worst case (no scaling, no compression) this would use 6-bytes/pixel of bandwidth (damn), and amortize closer to 4-bytes/pixel with scaling. And GPUs have capacity often for 32 VALU op clocks per byte. So this is certain to be VALU bound (410+ VALU clocks)
7/ @NOTimothyLottes
But things like Strix Halo (laptops) are quite a bit more bandwidth starved, so generally I try to stay VALU bound to be friendly towards scaling down
8/ @NOTimothyLottes
Estimation that something like a 7900 XT would be able to do over 10,000 of these passes at 4K per second, so this technique is good for 480 Hz! Of course I don’t have a 7900 XT, and my only working AMD GPU laptop (the other has bad HDMI port) refuses to profile, so numbers later
9/ @NOTimothyLottes
Writing code (below, note FMA_MIX is the hot ticket for free FP32->FP16 conversion) with instruction intrinsic macros (that unfortunately map to high-level shader code). But it at least enables me to ballpark instruction counts. With one exception I count transcendentals as 4 ops
10/ @NOTimothyLottes
Can compare guesses with actuals on the disassembly/
76 VGPRs! haha, decade+ and AMD still cannot manage basic VGPR allocation well. This is using roughly 30% more VGPRs than needed. Historically AMD HW has bumped up the VGPR count and added NSA to fix their SW problems …
11/ @NOTimothyLottes
I use 13 transcendentals so adjusting my 410 count by -13*3 I get 371 VALU ops. The actual shader uses 438 (and is VALU bound) an extra 18% extra slop somewhere. Some of that is that I didn’t count the CS swizzle logic (but that won’t account for most of the extra 67 ops) …
12/ @NOTimothyLottes
Some of the slop, it’s not using NSA (this is GFX9), so lots of extra V_MOV_B32s. The gather offsets are packed in an extra VGPR so even if the base {v34,v35} are shared across all the gather4s, it’s duplicating those VGPR pairs 8 extra times (no NSA here).
13/ @NOTimothyLottes
GFX9 is Vega, so no NSA here, and 1 SGPR read per operation. It was RDNA1 that introduced the good stuffs (NSA and 2 SGPRs/op).
Can see below cases where 2 SGPRs are needed Vega will introduce extra V_MOV_B32 ops to put one in a VGPR.
14/ @NOTimothyLottes
Now some actual compiler bugs,
(1.) Vega has V_MAD_MIX, but the compiler won’t use it, instead it wastes extra V_CVT_PKRTZ_F16_F32 ops
(2.) Compiler fails basic pattern matching with {x,-x} and instead issues extra V_PACK_B32_F16 ops