mono / SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
MIT License
4.14k stars 522 forks source link

[BUG] Custom SKSL Shaders - no longer compile #2795

Open najak3d opened 2 months ago

najak3d commented 2 months ago

Description

As we upgrade from 2.88.6 to 3.0.0-preview, our custom shaders now don't compile, and have many errors.

What I am requesting here is documentation on the changes made to SKGL, to guide us in porting our custom shaders to 3.0.0.

Code

Here is our rather long Terrain Shader that works wonderfully inside SkiaSharp 2.88.6 (other than the inefficiencies, as seen in our doing a Quadruple Sampling and Lerping to simulate "LINEAR" sampling mode):

    in fragmentProcessor elev_map;
    in fragmentProcessor vfr_map;

    uniform float cDarken; // Used to uniformly darked the entire result (for Night Time, etc).
    uniform float cShadingOn; // 0.0 = OFF,  > 0 is ON
    uniform float cAlpha; // Overall Transparency for Relief and Alert Highlights (can be throbbed)

    // Values used for showing Elevation Highlights (Red/Yellow)
    uniform float cShowAlerts;
    uniform float cOwnshipAltitudeFt;
    uniform float cAltThreshWarn;
    uniform float cAltThreshDanger;

    // Values for Elevation Highlights with DISTANCE Radius
    uniform float cCtrX;
    uniform float cCtrY;
    uniform float cScaleX;
    uniform float cScaleY;
    uniform float4 cRad; // x=rad, y=radEnd, z=ctrX, w=ctrY
    uniform float2 cRot; // x=sinRot, y=cosRot

    // Values to interpret the Elevations
    uniform float cAltMin;
    uniform float cAltSpan;

    // Values to Draw Contour Lines
    uniform float cContourWidth;
    uniform float cInvContourSpacing;
    uniform float cContourAlpha;

float smoothstep(float a, float b, float x) {
        float t = clamp((x - a) / (b - a), 0.0, 1.0);
        return t * t * (3.0 - 2.0 * t);
    }
float smoothstepFAST(float min, float max, float val) {
    return (val <= min) ? 0.0 : (val >= max) ? 1.0 : ((val - min) / (max - min));
}

float3 getElev(float2 fc)
{
    return sample(elev_map, fc).xyz;
}

float4 main(float2 cf) 
{
    bool UseRadius = (cScaleX > 0.0);
    bool ShowAlerts = (cShowAlerts > 0.0);
    bool IsColored = (cShadingOn > 0.0); // true; // Toggle Colored Mode below
    bool IsReliefMode = IsColored; // true; // RRRRRRRRRRRRRRRR true;
    bool ShowContours = IsColored; // true;
    bool OnlyShowAlerts = !IsColored && !IsReliefMode;

    if (OnlyShowAlerts && !ShowAlerts)
        discard; // This shader is entirely off!

    float2 fc;
    float3 elevNorm;
    float fwidthN = 0.0;
    const float MinUV = 2.0; // 2 pixel margin around the tile Elevation image.  Rendered Terrain Tiles are only 124 pixels dim.

    if (IsReliefMode || ShowAlerts)
    {
        const float UVIncr = 1.0; // / 128.0;
        const float InvUVIncr = 1.0; //128.0;

        float fcxi = (cf.x * InvUVIncr);
        float fcyi = (cf.y * InvUVIncr);
        float fcxi0 = floor(fcxi);
        float fcyi0 = floor(fcyi);

        // These are 'lerps'.
        float lpX = fcxi - fcxi0;
        float lpY = fcyi - fcyi0;
        float iLpX = 1.0 - lpX;
        float iLpY = 1.0 - lpY;

        float fcx0 = fcxi0 * UVIncr;
        float fcy0 = fcyi0 * UVIncr;
        fc = (float2(MinUV + fcx0, MinUV + fcy0));
        float3 el00 = getElev(fc);
        fc.x += UVIncr;
        float3 el10 = getElev(fc);
        fc.y += UVIncr;
        float3 el11 = getElev(fc);
        fc.x -= UVIncr;
        float3 el01 = getElev(fc);

        elevNorm = (el00 * iLpX * iLpY) + (el10 * lpX * iLpY) + (el01 * iLpX * lpY) + (el11 * lpX * lpY);

        if (ShowContours)
        {
            float v00 = (el00.z + el00.y);
            float v01 = (el01.z + el01.y);
            float v10 = (el10.z + el10.y);
            float v11 = (el11.z + el11.y);

            const float dlerp = 1.0;
            const float iDlerp = 1.0 - dlerp;
            float dd = iDlerp * abs(v00 - v11);
            float adx = (dlerp * abs(v00 - v10)) + dd;
            float ady = (dlerp * abs(v00 - v01)) + dd;

            fwidthN = adx + ady; //((el00.z + el00.y) - (el11.z + el11.y));
        }
    }
    else
    {
        fwidthN = 9999;
        fc = (float2(MinUV, MinUV) + cf);
        elevNorm = sample(elev_map, fc).xyz; 
    }

    float elevChans = elevNorm.z + elevNorm.y; // Range 0..2
    float elevFt = (cAltMin + (elevChans * cAltSpan));
    float elevFtp2000 = elevFt + 2000.0;
    float alertElev = (ShowAlerts) ? (elevFt - cOwnshipAltitudeFt) : -99999.0;

    if (OnlyShowAlerts)
    {
        if (alertElev < cAltThreshWarn)
            discard;
    }

    const float BaseBright = 0.83;
    const float InvBaseBright = 1.0 / BaseBright; // 1.20482; // inverse 0.83.   1.42857; // inverse of 0.7, which is the brightness for level (non-sloped) areas.

    float4 color;
    float bVal = (IsReliefMode) ? elevNorm.x : BaseBright; // NOTE: .a for Droid
    float radFactor = 1.0;
    float alertFactor = 0.0;

    if (UseRadius) // RADIUS ===================================================================================================
    {
        bool HasRotation = (cRot.x != 0.0);
        float cfx = cf.x * cScaleX; // Scaled X offset
        float cfy = cf.y * cScaleY; // Scaled Y offset
        float radOfsX;
        float radOfsY;

        if (HasRotation)
        {   // Calc Rotation impact - more math
            float cfRotX = (cfx * cRot.y) - (cfy * cRot.x); // (cfx * cos) - (cfy * sin)
            float cfRotY = (cfx * cRot.x) + (cfy * cRot.y); // (cfx * sin) + (cfy * cos)
            radOfsX = cCtrX + cfRotX - cRad.z;
            radOfsY = cCtrY + cfRotY - cRad.w;
        }
        else
        {   // No Rotation logic - cheaper
            radOfsX = cCtrX + cfx - cRad.z;
            radOfsY = cCtrY + cfy - cRad.w;
        }

        // Now Determine if it's inside the Radius
        float distOffset2 = (radOfsX * radOfsX) + (radOfsY * radOfsY);
        if (distOffset2 > cRad.x)
        {
            radFactor = 1.0 - smoothstep(distOffset2, cRad.x, cRad.y);
        }
    }

    if (OnlyShowAlerts)
    {   // Outside of Alert Radius, nothing to render
        if (radFactor <= 0.2)
            discard;
    }

    if (IsColored)
    {
        const float invPaletteSize = 0.015625; // 1 / 64
        const float PaletteOffset = 0.0078125; // 1 / 128

        float palettePixel = 64.0 * floor(elevFtp2000 * 0.001); // rounds down to nearest 1000.
        float paletteU = PaletteOffset + (palettePixel * invPaletteSize);
        vec2 paletteUV = vec2(paletteU, 0.0);

        color = sample(vfr_map, paletteUV).rgba; // abgr; // argb; // rgba;  // texture2D(sEnvMap, paletteUV).rgba;
    }
    else
    {
        color = float4(bVal, bVal, bVal, 1.0);
    }

    float boost = 0.0;
    float brightness = InvBaseBright * bVal;

    if (IsReliefMode)
    {
        color.rgb *= (0.9 * brightness);
        boost = 0.2 * (brightness - BaseBright);
        color.rgb += float3(boost, boost, boost);
    }

    // Now allow Alerts to override
    if (ShowAlerts && alertElev > cAltThreshWarn && radFactor > 0.1)
    {
        boost *= 3.0;
        float warnThresh2 = cAltThreshWarn + 50.0;  // RRRRRE: should be const
        bool isYellow = (alertElev < cAltThreshDanger);
        float green = (isYellow) ? (0.777 * brightness) : boost;
        float blue = (isYellow) ? (1.7 * boost) : boost;

        float alphaFactor = (alertElev < warnThresh2) ? 0.5 + (0.5 * smoothstep(cAltThreshWarn, warnThresh2, alertElev)) : 1.0;
        float alpha = (cAlpha * alphaFactor * radFactor);
        float invAlpha = 1.0 - alpha;
        color.rgb = (color.rgb * invAlpha) + (alpha * float3(brightness, green, blue));  // BLEND
    }

    if (ShowContours)
    {
        float cl = elevFt * cInvContourSpacing;
        float cl0 = floor(cl);
        float cr = cl - cl0; // same as 'fract'
        float df = 0.0003 * cAltSpan * cContourWidth * abs(fwidthN);
        //df = pow(df, 0.7);

        float c = 1.0 - smoothstep(df * 1.0, df * 2.0, cr);
        float showC = (elevFt > 400.0) ? 1.0 : 0.0;
        color.rgb += (showC * c * cContourAlpha);

        //if (cr < (0.05 * pow(1 - bVal, 0.1))) // && bVal < 0.9)
        //  color = float4(0.0, 0.0, 0.0, 1.0);
    }

    color.rgb *= cDarken; // Darken the entire result
    color.a = 1.0;
    return color;
}

Expected Behavior

Expecting this to compile the same as it does for 2.88.6 -- or if not, then would like to have docs that guide me on the changes I need to make.

Actual Behavior

Instead I now get 23 compiler errors, as follows: error: 2: no type named 'fragmentProcessor' in fragmentProcessor elev_map; ^^^^^^^^^^^^^^^^^ error: 2: no type named 'elev_map' in fragmentProcessor elev_map; ^^^^^^^^ error: 2: expected a declaration, but found ';' in fragmentProcessor elev_map; ^ error: 3: no type named 'fragmentProcessor' in fragmentProcessor vfr_map; ^^^^^^^^^^^^^^^^^ error: 3: no type named 'vfr_map' in fragmentProcessor vfr_map; ^^^^^^^ error: 3: expected a declaration, but found ';' in fragmentProcessor vfr_map; ^ error: 32: duplicate definition of 'float smoothstep(float a, float b, float x)' float smoothstep(float a, float b, float x) { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: 33: unknown identifier 'x' float t = clamp((x - a) / (b - a), 0.0, 1.0); ^ error: 33: unknown identifier 'a' float t = clamp((x - a) / (b - a), 0.0, 1.0); ^ error: 33: unknown identifier 'b' float t = clamp((x - a) / (b - a), 0.0, 1.0); ^ error: 33: unknown identifier 'a' float t = clamp((x - a) / (b - a), 0.0, 1.0); ^ error: 34: unknown identifier 't' return t t (3.0 - 2.0 t); ^ error: 34: unknown identifier 't' return t t (3.0 - 2.0 t); ^ error: 34: unknown identifier 't' return t t (3.0 - 2.0 * t); ^ error: 42: unknown identifier 'sample' return sample(elev_map, fc).xyz; ^^^^^^ error: 42: unknown identifier 'elev_map' return sample(elev_map, fc).xyz; ^^^^^^^^ error: 55: discard statement is only permitted in fragment shaders discard; // This shader is entirely off! ^^^^^^^ error: 111: unknown identifier 'sample' elevNorm = sample(elev_map, fc).xyz; ^^^^^^ error: 111: unknown identifier 'elev_map' elevNorm = sample(elev_map, fc).xyz; ^^^^^^^^ error: 122: discard statement is only permitted in fragment shaders discard; ^^^^^^^ error: 165: discard statement is only permitted in fragment shaders discard; ^^^^^^^ error: 178: unknown identifier 'sample' color = sample(vfr_map, paletteUV).rgba; // abgr; // argb; // rgba; // texture2D(sEnvMap, paletteUV).rgba; ^^^^^^ error: 178: unknown identifier 'vfr_map' color = sample(vfr_map, paletteUV).rgba; // abgr; // argb; // rgba; // texture2D(sEnvMap, paletteUV).rgba; ^^^^^^^ 23 errors

Version of SkiaSharp

3.x (Alpha)

Last Known Good Version of SkiaSharp

2.88.2 (Previous)

IDE / Editor

Visual Studio (Windows)

Platform / Operating System

Windows

Platform / Operating System Version

Windows 11 Home

Devices

Windows 11, CyberPowerPC, Intel i9 14900K

Relevant Screenshots

No response

Relevant Log Output

No response

Code of Conduct