Is Nvidia Control Panel Sharpen CAS Derived?

Discussion in 'Videocards - NVIDIA GeForce Drivers Section' started by BlindBison, Dec 29, 2023.

  1. BlindBison

    BlindBison Ancient Guru

    Messages:
    2,451
    Likes Received:
    1,167
    GPU:
    RTX 3070
    I've not seen the claim sourced, but I've heard it suggested in other forums that the "updated" Sharpen option for games in the Nvidia Control Panel is AMD CAS derived or at least that it's supposed to work "similarly". I recall in Hardware Unboxed's video sometime ago on it they said to their eye the quality looked "similar" between AMD CAS and nvidia control panel sharpen.

    The thing is, if I use Reshade CAS vs the Nvidia Control Panel sharpen they look quite different to me. Maybe it's placebo? I wouldn't think so, but perhaps if they really are similar it would be the strengths that differ?

    Even at lower strengths I find the Nvidia sharpen seems to make aliasing in the image look a lot worse which is why I typically avoid it. Reshade CAS seems quite subtle in its effect at 1.0 compared to when games implement it in their menu (something like RAGE 2), but am I crazy to think it looks less bad than the Nvidia Control Panel one?

    It's hard to match the strength 1 to 1 and games that implement CAS usually don't let you customize the strength at all. I assume the control panel sharpen maybe is the same sharpen people have complained about in all the DLSS posts. Thanks,
     
  2. I really doubt it's AMD's CAS I remember having the same observation as you, that it makes aliasing worse even at moderate strength. CAS is way better yeah.
     
  3. Krizby

    Krizby Ancient Guru

    Messages:
    3,333
    Likes Received:
    2,041
    GPU:
    Asus RTX 4090 TUF
    Nvidia CP Sharpen use Lanczos, same as FSR1, where it find edges and sharpen them

    AMD CAS, as the name suggest, modify the contrast of everything

    IMO both look horrible at 0.4 strength and above

    For me I use Sharpen+ inside Geforce Experience at 0.3 strength to make DLSS Balanced as sharp as Native TAA (and not more)
     
    OnnA, pharma and BlindBison like this.
  4. Sledgepainter

    Sledgepainter Member Guru

    Messages:
    149
    Likes Received:
    34
    GPU:
    RTX 4070 Ti
    CAS at default value of 1 looks great. Been using it via ReShade for years.
     
    OnnA and BlindBison like this.

  5. dr_rus

    dr_rus Ancient Guru

    Messages:
    4,036
    Likes Received:
    1,138
    GPU:
    RTX 4090
    Nvidia sharpen options (CPL+GFE) are also contrast adaptive but not "CAS derived".
     
    BlindBison likes this.
  6. BlindBison

    BlindBison Ancient Guru

    Messages:
    2,451
    Likes Received:
    1,167
    GPU:
    RTX 3070
    I just wonder why Nvidia control panel sharpen can look so bad even at low strength. I recognize that the default reshade CAS settings are perhaps tuned a bit on the "weaker" strength side of things, but specifically Nvidia control panel sharpen seems to make aliasing in the image look horrific. It's kind of hard to match up the strengths though. If it were contrast adaptive I would've expected it be "smarter" about sharpening the already very sharp aliased edges present in the image?
     
  7. BlindBison

    BlindBison Ancient Guru

    Messages:
    2,451
    Likes Received:
    1,167
    GPU:
    RTX 3070
    Yeah, CAS via Reshade is one where I'm not quite sure I'm tuning the settings correctly (especially that slider which defaults to a value of 0 -- there are two, one that defaults to 0 and one that defaults to 1).

    But despite the effect at its default being quite subtle I'd say, it tends to produce fewer ugly side effects compared to whatever the nvidia control panel sharpen is doing in my early tests. I keep saying this, but I could be wrong given its hard to match up the strength 1 to 1. Perhaps I'm being too hard on Nvidia's control panel sharpen, it's just that even at low strength it doesn't seem very good at avoiding sharpening the already sharp aliased edges present in the image compared to CAS.
     
  8. janos666

    janos666 Ancient Guru

    Messages:
    1,686
    Likes Received:
    421
    GPU:
    MSI RTX3080 10Gb
    I always hated the Lanczos filter. From the algorithms offered by madVR for video, I prefer "Jinc". It's sharp with little ringing and little-to-no other artifacts. I am not sure what it's "real name" is (Google didn't find anything).
     
    BlindBison likes this.
  9. dr_rus

    dr_rus Ancient Guru

    Messages:
    4,036
    Likes Received:
    1,138
    GPU:
    RTX 4090
    Different levels of strengths between different sharpening shaders. I too prefer CAS but mostly because it's easier to setup through Reshade than messing with CPL or installing GFE.

    Btw has anyone seen a NIS/NVSharpen port for Reshade? Would be fun to be able to compare it with CAS this way.

    Edit: to answer my own question here's the port: https://reshade.me/forum/shader-discussion/7825-about-nvidia-sharpen#43564

    Code:
    uniform float sharpness <
        ui_type = "slider";
        ui_label = "sharpness";
        ui_min = 0.0; ui_max = 1.0;ui_step = 0.01;
    > = 0.2;
    uniform float kContrastBoost <
        ui_type = "slider";
        ui_label = "kContrastBoost";
        ui_min = 0.0; ui_max = 1.0;ui_step = 0.01;
    > = 1.0;
    #include "ReShade.fxh"
    #define inputPtX BUFFER_RCP_WIDTH
    #define inputPtY BUFFER_RCP_HEIGHT
    #define kDetectRatio (1127.f / 1024.f)
    #define kDetectThres (64.0f / 1024.0f)
    #define kEps 1.0f
    #define NIS_SCALE_FLOAT 1.0f
    #define kMinContrastRatio 2.0f
    #define kMaxContrastRatio 10.0f
    #define kRatioNorm (1.0f / (kMaxContrastRatio - kMinContrastRatio))
    #define kSharpStartY 0.45f
    #define kSharpEndY 0.9f
    #define kSharpScaleY (1.0f / (kSharpEndY - kSharpStartY))
    #define sharpen_slider (sharpness - 0.5f)
    #define MinScale ((sharpen_slider >= 0.0f) ? 1.25f : 1.0f)
    #define MaxScale ((sharpen_slider >= 0.0f) ? 1.25f : 1.75f)
    #define kSharpStrengthMin max(0.0f, 0.4f + sharpen_slider * MinScale * 1.2f)
    #define kSharpStrengthMax (1.6f + sharpen_slider * 1.8f)
    #define kSharpStrengthScale (kSharpStrengthMax - kSharpStrengthMin)
    #define kSharpLimitMin max(0.1f, 0.14f + sharpen_slider * LimitScale * 0.32f)
    #define kSharpLimitMax (0.5f + sharpen_slider * LimitScale * 0.6f)
    #define kSharpLimitScale (kSharpLimitMax - kSharpLimitMin)
    #define LimitScale ((sharpen_slider >= 0.0f) ? 1.25f : 1.0f)
    #define kSupportSize 5
    float getY(float3 rgba) {
        return 0.2126f * rgba.x + 0.7152f * rgba.y + 0.0722f * rgba.z;
    }
    float4 GetEdgeMap(float p[25], int i, int j) {
        const float g_0 = abs(p[i+5*j] + p[i+5*(j+1)] + p[i+5*(j+2)] - p[i+2+5*j] - p[i+2+5*(j+1)] - p[i+2+5*(j+2)]);
        const float g_45 = abs(p[i+1+5*j] + p[i+5*j] + p[i+5*(j+1)] - p[i+2+5*(j+1)] - p[i+2+5*(j+2)] - p[i+1+5*(j+2)]);
        const float g_90 = abs(p[i+5*j] + p[i+1+5*j] + p[i+2+5*j] - p[i+5*(j+2)] - p[i+1+5*(j+2)] - p[i+2+5*(j+2)]);
        const float g_135 = abs(p[i+1+5*j] + p[i+2+5*j] + p[i+2+5*(j+1)] - p[i+5*(j+1)] - p[i+5*(j+2)] - p[i+1+5*(j+2)]);
        const float g_0_90_max = max(g_0, g_90);
        const float g_0_90_min = min(g_0, g_90);
        const float g_45_135_max = max(g_45, g_135);
        const float g_45_135_min = min(g_45, g_135);
        float e_0_90 = 0;
        float e_45_135 = 0;
        if ((g_0_90_max + g_45_135_max) != 0) {
            e_0_90 = g_0_90_max / (g_0_90_max + g_45_135_max);
            e_0_90 = min(e_0_90, 1.0f);
            e_45_135 = 1.0f - e_0_90;
        }
        float e = ((g_0_90_max > (g_0_90_min * kDetectRatio)) && (g_0_90_max > kDetectThres) && (g_0_90_max > g_45_135_min)) ? 1.f : 0.f;
        float edge_0 = (g_0_90_max == g_0) ? e : 0.f;
        float edge_90 = (g_0_90_max == g_0) ? 0.f : e;
        e = ((g_45_135_max > (g_45_135_min * kDetectRatio)) && (g_45_135_max > kDetectThres) && (g_45_135_max > g_0_90_min)) ? 1.f : 0.f;
        float edge_45 = (g_45_135_max == g_45) ? e : 0.f;
        float edge_135 = (g_45_135_max == g_45) ? 0.f : e;
        float weight_0 = 0.f;
        float weight_90 = 0.f;
        float weight_45 = 0.f;
        float weight_135 = 0.f;
        if ((edge_0 + edge_90 + edge_45 + edge_135) >= 2.0f) {
            weight_0 = (edge_0 == 1.0f) ? e_0_90 : 0.f;
            weight_90 = (edge_0 == 1.0f) ? 0.f : e_0_90;
            weight_45 = (edge_45 == 1.0f) ? e_45_135 : 0.f;
            weight_135 = (edge_45 == 1.0f) ? 0.f : e_45_135;
        } else if ((edge_0 + edge_90 + edge_45 + edge_135) >= 1.0f) {
            weight_0 = edge_0;
            weight_90 = edge_90;
            weight_45 = edge_45;
            weight_135 = edge_135;
        }
        return float4(weight_0, weight_90, weight_45, weight_135);
    }
    float CalcLTIFast(const float y[5]) {
        const float a_min = min(min(y[0], y[1]), y[2]);
        const float a_max = max(max(y[0], y[1]), y[2]);
        const float b_min = min(min(y[2], y[3]), y[4]);
        const float b_max = max(max(y[2], y[3]), y[4]);
        const float a_cont = a_max - a_min;
        const float b_cont = b_max - b_min;
        const float cont_ratio = max(a_cont, b_cont) / (min(a_cont, b_cont) + kEps * (1.0f / NIS_SCALE_FLOAT));
        return (1.0f - saturate((cont_ratio - kMinContrastRatio) * kRatioNorm)) * kContrastBoost;
    }
    float EvalUSM(const float pxl[5], const float sharpnessStrength, const float sharpnessLimit) {
        // USM profile
        float y_usm = -0.6001f * pxl[1] + 1.2002f * pxl[2] - 0.6001f * pxl[3];
        // boost USM profile
        y_usm *= sharpnessStrength;
        // clamp to the limit
        y_usm = min(sharpnessLimit, max(-sharpnessLimit, y_usm));
        // reduce ringing
        y_usm *= CalcLTIFast(pxl);
        return y_usm;
    }
    float4 GetDirUSM(const float p[25]) {
        // sharpness boost & limit are the same for all directions
        const float scaleY = 1.0f - saturate((p[12] - kSharpStartY) * kSharpScaleY);
        // scale the ramp to sharpen as a function of luma
        const float sharpnessStrength = scaleY * kSharpStrengthScale + kSharpStrengthMin;
        // scale the ramp to limit USM as a function of luma
        const float sharpnessLimit = (scaleY * kSharpLimitScale + kSharpLimitMin) * p[12];
        float4 rval;
        // 0 deg filter
        float interp0Deg[5];
        {
            [unroll]
            for (int i = 0; i < 5; ++i) {
                interp0Deg[i] = p[i+10];
            }
        }
        rval.x = EvalUSM(interp0Deg, sharpnessStrength, sharpnessLimit);
        // 90 deg filter
        float interp90Deg[5];
        {
            [unroll]
            for (int i = 0; i < 5; ++i) {
                interp90Deg[i] = p[2+5*i];
            }
        }
        rval.y = EvalUSM(interp90Deg, sharpnessStrength, sharpnessLimit);
        //45 deg filter
        float interp45Deg[5];
        interp45Deg[0] = p[6];
        interp45Deg[1] = lerp(p[7], p[11], 0.5f);
        interp45Deg[2] = p[12];
        interp45Deg[3] = lerp(p[13], p[17], 0.5f);
        interp45Deg[4] = p[18];
        rval.z = EvalUSM(interp45Deg, sharpnessStrength, sharpnessLimit);
        //135 deg filter
        float interp135Deg[5];
        interp135Deg[0] = p[8];
        interp135Deg[1] = lerp(p[13], p[7], 0.5f);
        interp135Deg[2] = p[12];
        interp135Deg[3] = lerp(p[17], p[11], 0.5f);
        interp135Deg[4] = p[16];
        rval.w = EvalUSM(interp135Deg, sharpnessStrength, sharpnessLimit);
        return rval;
    }
    float4 NVSPass(float4 vpos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target
    {
        float p[25];
        [unroll]
        for (int i = 0; i < 5; ++i) {
            [unroll]
            for (int j = 0; j < 5; ++j) {
                p[i+5*j] = getY(tex2D(ReShade::BackBuffer, texcoord + float2(j - 2, i - 2) * float2(inputPtX, inputPtY)).rgb);
            }
        }
        // get directional filter bank output
        const float4 dirUSM = GetDirUSM(p);
      
        // generate weights for directional filters
        float4 w = GetEdgeMap(p, kSupportSize / 2 - 1, kSupportSize / 2 - 1);
      
        // final USM is a weighted sum filter outputs
        const float usmY = (dirUSM.x * w.x + dirUSM.y * w.y + dirUSM.z * w.z + dirUSM.w * w.w);
        float4 op = tex2D(ReShade::BackBuffer, texcoord);
        op.x += usmY;
        op.y += usmY;
        op.z += usmY;
        return op;
    }
    technique NVSharpen
    {
        pass
        {
            VertexShader = PostProcessVS;
            PixelShader = NVSPass;
        }
    }
    
    Just like CAS it has two sliders to control but contrary to CAS you actually has to change both to get good results - in CAS case the contrast adaptation slider doesn't do much while here the contrast boost one does arguably more than the sharpness amount one.
     
    Last edited: Dec 29, 2023
    BlindBison likes this.
  10. Trunks0

    Trunks0 Maha Guru

    Messages:
    1,353
    Likes Received:
    857
    GPU:
    PC RedDevil 7900XTX
    @dr_rus Comparisons would be curious. Just reading the code, it use's NIS, it looks like it has something to reduce ringing and a edge/contrast detection method.
     
    BlindBison likes this.

  11. EdKiefer

    EdKiefer Ancient Guru

    Messages:
    3,161
    Likes Received:
    402
    GPU:
    ASUS TUF 3060ti
    Isn't it still better to use old sharpening method vers default NVCP scaling/sharpening one?

    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\nvlddmkm\FTS]
    "EnableGR535"=dword:00000000
     
    OnnA and big ROBOT bill like this.
  12. Sacvoyage

    Sacvoyage Member

    Messages:
    14
    Likes Received:
    13
    GPU:
    16gb
    For me, this breaks the game into colorful pictures, dx12 is at least not supported, as I understand it)
     
  13. Smough

    Smough Master Guru

    Messages:
    988
    Likes Received:
    305
    GPU:
    GTX 1660
    I prefer it, i always do this change. The new sharpening looks cartoony and horribly overdone, it's much worse. If you use the old sharpening you do lose the NIS upscaler function, so it's a small sacrifice, since not all games support FSR, so NIS would be much welcomed.
     
  14. Smough

    Smough Master Guru

    Messages:
    988
    Likes Received:
    305
    GPU:
    GTX 1660
    I can use sharpening in any DX12 game with Nvidia's old version, never had this sort of problem you mention.
     
  15. Sacvoyage

    Sacvoyage Member

    Messages:
    14
    Likes Received:
    13
    GPU:
    16gb
    in call of duty MW3 I had the whole screen flooded with multicolored paints, pink mostly :))))
     
    BlindBison likes this.

  16. BlindBison

    BlindBison Ancient Guru

    Messages:
    2,451
    Likes Received:
    1,167
    GPU:
    RTX 3070
    Well that's fun :D I'm gonna have to try that out when I get that game
     
  17. EdKiefer

    EdKiefer Ancient Guru

    Messages:
    3,161
    Likes Received:
    402
    GPU:
    ASUS TUF 3060ti
    old sharpening works on the Battlefield series fine.
     
  18. Maybe it's just the new trendy war paint?
     
    Sacvoyage likes this.
  19. Daemonjax

    Daemonjax Active Member

    Messages:
    53
    Likes Received:
    11
    GPU:
    NVIDIA 4070ti super
    I like to use something CAS based (whatever qUINT's latest sharpening shader is, probably) in reshade if I'm going to do any post processing sharpening at all because then I can modify the shader to test the depth buffer so distant objects (like the sky, clouds, distant crap on the horizon or whatever) aren't sharpened at all (by using a simple lerp iirc). There's no public shader already made that does that afaik, but it's not hard to add that feature to whatever.
     
    Last edited: Feb 25, 2024
  20. big ROBOT bill

    big ROBOT bill Ancient Guru

    Messages:
    1,804
    Likes Received:
    2,756
    GPU:
    TUF 4070Ti OC
    Clear your shader cache and reload them on next game start
     
    Sacvoyage likes this.

Share This Page