You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SkiaSharp SKCanvasView的自定义SkSL着色器导致.NET MAUI应用崩溃

问题描述

我正在开发一款面向iPad Pro的.NET MAUI应用,需要根据参数采样图像区域并显示,因此选择SkiaSharp的SKRuntimeEffect和SKShader类实现自定义SkSL着色器。我创建了继承自SKCanvasView的自定义控件,但每当OnPaintSurface方法执行到DrawRect()调用时,应用立即崩溃。

我制作了极简测试应用,仅包含将像素设为红色的简单着色器,崩溃问题仍存在。自定义控件代码如下:

using SkiaSharp;
using SkiaSharp.Views.Maui.Controls;
using System.Diagnostics;

namespace SkiaShaderTest.Controls
{
    public class Panel : SKCanvasView
    {
        public static BindableProperty ShaderOnProperty = BindableProperty.Create(
            nameof(ShaderOn),
            typeof(bool),
            typeof(Panel),
            false,
            propertyChanged: (bindable, oldValue, newValue) =>
            {
                var panel = (Panel)bindable;
                panel.InvalidateSurface();
            }
        );

        public bool ShaderOn
        {
            get => (bool)GetValue(ShaderOnProperty);
            set => SetValue(ShaderOnProperty, value);
        }


        protected override void OnPaintSurface(SkiaSharp.Views.Maui.SKPaintSurfaceEventArgs args)
        {
            var canvas = args.Surface.Canvas;
            var info = args.Info;

            canvas.Clear();

            if (ShaderOn)
            {
                var src = @"
                    float4 main(float2 fragCoord) {
                        return float4(1,0,0,1);
                    }";
                using var effect = SKRuntimeEffect.Create(src, out string error);
                using var shader = effect.ToShader(true);


                using (SKPaint paint = new SKPaint() { Shader = shader })
                {
                    try
                    {
                        canvas.DrawRect(info.Rect, paint);
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine($"Exception {e.GetType()}: {e.Message}");
                    }
                }
            }
            else
            {
                using (SKPaint paint = new SKPaint())
                {
                    paint.Color = SKColors.BlueViolet;
                    canvas.DrawRect(info.Rect, paint);
                }
            }
        }

    }
}

应用的XAML布局如下:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="SkiaShaderTest.MainPage"
             xmlns:controls="clr-namespace:SkiaShaderTest.Controls"
>

 <Grid RowDefinitions="*,auto" IgnoreSafeArea="True">
        <controls:Panel x:Name="panel"
                HorizontalOptions="FillAndExpand"
                VerticalOptions="FillAndExpand"
                Grid.Row="0"
                Grid.RowSpan="2"/>

        <CheckBox
            IsChecked="{Binding ShaderOn, Source={x:Reference panel}, Mode=TwoWay}"
            MinimumWidthRequest="100"
            BackgroundColor="RoyalBlue"
            Color="AliceBlue"
            HorizontalOptions="Center"
            VerticalOptions="End"
            />
    </Grid>

</ContentPage>

每当ShaderOn设为true并执行到canvas.DrawRect(info.Rect, paint);时,应用立即崩溃。我在iPad Pro真机、iOS模拟器、Mac Mini和Windows 11笔记本测试:iPad无错误信息,Mac有控制台输出,Windows抛出SEHException:

System.Runtime.InteropServices.SEHException
  HResult=0x80004005
  Message=External component has thrown an exception.
  Source=<Cannot evaluate the exception source>
  StackTrace:
<Cannot evaluate the exception stack trace>

(注:代码未进入catch块,调试器直接在DrawRect()行抛出异常)

我尝试使用SkiaSharp自带的着色器(如下),运行完全正常:

using (SKPaint paint = new SKPaint())
{
    SKShader shader = SKShader.CreateLinearGradient(
        new SKPoint(0, 0),
        new SKPoint(1000, 1000),
        new SKColor[]
        {
            new SKColor(0,0,255),
            new SKColor(0,255,0)
         },
         null,
         SKShaderTileMode.Repeat
     );
     paint.Shader = shader;
     canvas.DrawRect(info.Rect, paint);
}

我已安装NuGet上最新的SkiaSharp包,控制台输出和异常指向SkiaSharp内部错误,但我也怀疑自身代码问题。请问我可能哪里出错了?该如何解决此问题?


解决方案

问题出在SkSL着色器的入口函数签名和SKRuntimeEffect的使用方式上,具体修正点如下:

1. 修正SkSL入口函数的参数签名

Skia的SKRuntimeEffect要求片段着色器的入口函数必须使用规范的参数定义,你当前使用的fragCoord不是默认提供的输入变量,应该改用默认的coords参数,同时推荐使用sk_in修饰符明确输入属性:

float4 main(sk_in float2 coords) {
    return float4(1,0,0,1);
}

2. 修正ToShader的调用方式

effect.ToShader(true)中的true表示使用设备坐标,但你的着色器未处理该坐标空间逻辑,应改为使用本地坐标(默认行为),可以直接省略参数或传入false:

using var shader = effect.ToShader();

3. 添加着色器编译错误检查

在创建SKRuntimeEffect后,立即检查编译错误,避免后续执行无效的着色器:

if (!string.IsNullOrEmpty(error))
{
    Debug.WriteLine($"Shader compile error: {error}");
    return;
}

修正后的完整OnPaintSurface代码

protected override void OnPaintSurface(SkiaSharp.Views.Maui.SKPaintSurfaceEventArgs args)
{
    var canvas = args.Surface.Canvas;
    var info = args.Info;

    canvas.Clear();

    if (ShaderOn)
    {
        var src = @"
            float4 main(sk_in float2 coords) {
                return float4(1,0,0,1);
            }";
        using var effect = SKRuntimeEffect.Create(src, out string error);
        if (!string.IsNullOrEmpty(error))
        {
            Debug.WriteLine($"Shader compile error: {error}");
            return;
        }
        using var shader = effect.ToShader();

        using (SKPaint paint = new SKPaint() { Shader = shader })
        {
            canvas.DrawRect(info.Rect, paint);
        }
    }
    else
    {
        using (SKPaint paint = new SKPaint())
        {
            paint.Color = SKColors.BlueViolet;
            canvas.DrawRect(info.Rect, paint);
        }
    }
}

原因解释

  • SKRuntimeEffect的SkSL语法有严格规范,使用未定义的输入变量会导致Skia内部渲染管线崩溃,且这类原生异常无法被C#的try-catch捕获。
  • ToShader(true)传入的设备坐标未被着色器正确处理,会触发坐标空间不匹配的底层错误,进而导致应用崩溃。

内容的提问来源于stack exchange,提问作者LFLumi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 14:37:08