AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.31k stars 2.2k forks source link

SK Shaders cause previewers to crash. #16963

Closed sirdoombox closed 1 week ago

sirdoombox commented 2 weeks ago

Describe the bug

When trying to preview a control that utilises an ICustomDrawOperation which in turn uses a SKRuntimeEffect for rendering, the previewers in both VS and Rider crash. The full stack trace of the crash in Rider is as follows:

Initializing application in design mode
Obtaining AppBuilder instance from ReproShaderPreviewerCrash.Program
Sending StartDesignerSessionMessage
Fatal error. System.Runtime.InteropServices.SEHException (0x80004005): External component has thrown an exception.
Repeat 2 times:
--------------------------------
   at SkiaSharp.SkiaApi.sk_canvas_draw_rect(IntPtr, SkiaSharp.SKRect*, IntPtr)
--------------------------------
   at SkiaSharp.SKCanvas.DrawRect(SkiaSharp.SKRect, SkiaSharp.SKPaint)
   at ReproShaderPreviewerCrash.EffectDraw.Render(Avalonia.Media.ImmediateDrawingContext)
   at Avalonia.Rendering.Composition.Drawing.Nodes.RenderDataCustomNode.Invoke(Avalonia.Rendering.Composition.Drawing.Nodes.RenderDataNodeRenderContext ByRef)
   at Avalonia.Rendering.Composition.Drawing.ServerCompositionRenderData.Render(Avalonia.Platform.IDrawingContextImpl)
   at Avalonia.Rendering.Composition.Server.ServerCompositionDrawListVisual.RenderCore(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, Avalonia.Platform.LtrbRect)
   at Avalonia.Rendering.Composition.Server.ServerCompositionVisual.Render(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, System.Nullable`1<Avalonia.Platform.LtrbRect>)
   at Avalonia.Rendering.Composition.Server.ServerCompositionContainerVisual.RenderCore(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, Avalonia.Platform.LtrbRect)
   at Avalonia.Rendering.Composition.Server.ServerCompositionDrawListVisual.RenderCore(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, Avalonia.Platform.LtrbRect)
   at Avalonia.Rendering.Composition.Server.ServerCompositionVisual.Render(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, System.Nullable`1<Avalonia.Platform.LtrbRect>)
   at Avalonia.Rendering.Composition.Server.ServerCompositionContainerVisual.RenderCore(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, Avalonia.Platform.LtrbRect)
   at Avalonia.Rendering.Composition.Server.ServerCompositionDrawListVisual.RenderCore(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, Avalonia.Platform.LtrbRect)
   at Avalonia.Rendering.Composition.Server.ServerCompositionVisual.Render(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, System.Nullable`1<Avalonia.Platform.LtrbRect>)
   at Avalonia.Rendering.Composition.Server.ServerCompositionContainerVisual.RenderCore(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, Avalonia.Platform.LtrbRect)
   at Avalonia.Rendering.Composition.Server.ServerCompositionDrawListVisual.RenderCore(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, Avalonia.Platform.LtrbRect)
   at Avalonia.Rendering.Composition.Server.ServerCompositionVisual.Render(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, System.Nullable`1<Avalonia.Platform.LtrbRect>)
   at Avalonia.Rendering.Composition.Server.ServerCompositionContainerVisual.RenderCore(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, Avalonia.Platform.LtrbRect)
   at Avalonia.Rendering.Composition.Server.ServerCompositionDrawListVisual.RenderCore(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, Avalonia.Platform.LtrbRect)
   at Avalonia.Rendering.Composition.Server.ServerCompositionVisual.Render(Avalonia.Rendering.Composition.Server.ServerVisualRenderContext, System.Nullable`1<Avalonia.Platform.LtrbRect>)
   at Avalonia.Rendering.Composition.Server.ServerCompositionTarget.RenderRootToContextWithClip(Avalonia.Platform.IDrawingContextImpl, Avalonia.Rendering.Composition.Server.ServerCompositionVisual)
   at Avalonia.Rendering.Composition.Server.ServerCompositionTarget.Render()
   at Avalonia.Rendering.Composition.Server.ServerCompositor.RenderCore(Boolean)
   at Avalonia.Rendering.Composition.Server.ServerCompositor.RenderReentrancySafe(Boolean)
   at Avalonia.Rendering.Composition.Server.ServerCompositor.Render(Boolean)
   at Avalonia.Rendering.Composition.Server.ServerCompositor.Render()
   at Avalonia.Rendering.RenderLoop.TimerTick(System.TimeSpan)
   at Avalonia.Rendering.DefaultRenderTimer.InternalTick(System.TimeSpan)
   at Avalonia.Rendering.UiThreadRenderTimer+<>c__DisplayClass3_0.<StartCore>b__0()
   at Avalonia.Threading.DispatcherTimer+<>c__DisplayClass14_0.<Run>b__0(System.Object, System.EventArgs)
   at Avalonia.Threading.DispatcherTimer.FireTick()
   at Avalonia.Threading.DispatcherOperation.InvokeCore()
   at Avalonia.Threading.DispatcherOperation.Execute()
   at Avalonia.Threading.Dispatcher.ExecuteJob(Avalonia.Threading.DispatcherOperation)
   at Avalonia.Threading.Dispatcher.ExecuteJobsCore(Boolean)
   at Avalonia.Threading.Dispatcher.Signaled()
   at Avalonia.Controls.Platform.ManagedDispatcherImpl.RunLoop(System.Threading.CancellationToken)
   at Avalonia.Threading.DispatcherFrame.Run(Avalonia.Threading.IControlledDispatcherImpl)
   at Avalonia.Threading.Dispatcher.PushFrame(Avalonia.Threading.DispatcherFrame)
   at Avalonia.Threading.Dispatcher.MainLoop(System.Threading.CancellationToken)
   at Avalonia.DesignerSupport.Remote.RemoteDesignerEntryPoint.Main(System.String[])
   at Avalonia.Designer.HostApp.Program.Main(System.String[])

Related source issue here: https://github.com/kikipoulet/SukiUI/issues/288

To Reproduce

Expected behavior

No response

Avalonia version

11.X.X

OS

Windows

Additional context

No response

kekekeks commented 1 week ago

Previewer uses Skia in software rendering mode. Your Skia API calls are likely to somehow be incompatible with Skia raster mode.

You can try checking for GrContext being null to determine if sw rendering is used.

sirdoombox commented 1 week ago

Is this something we should be accounting for ourselves or an oversight in the previewer not failing gracefully? Are there any other contexts in which Skia could be running in software rendering mode or is this a design-time only consideration?

maxkatz6 commented 1 week ago

That's something you need to account yourselves.

Avalonia already handles software rendering well everywhere. And you shouldn't developer apps or especially libraries with an assumption that every target has a hardware renderer. If can be the case with Browser (especially on older Avalonia versions), it can be the case with app specific configuration (you can disable hardware acceleration per target).

In this specific case you are operating with Skia drawing directly, and it's your responsibility to write graceful fallbacks, if there is no GrContext. Or even if there is no Skia to begin with (which is only the case, if app developer used Direct2D or custom rendering backend explicitly).

kekekeks commented 1 week ago

Various VMs (especially linux and mac ones) will fall back to software rendering.

sirdoombox commented 1 week ago

Excellent, I'll focus on implementing some fallbacks in that case. I've not really spent much time with Avalonia outside of desktop and don't use the previewer personally so I've not encountered this issue before.

Thanks for the input!