浏览代码

Command buffer approach

flabbet 1 年之前
父节点
当前提交
76c1d60814

+ 1 - 1
src/ChunkyImageLib/Surface.cs

@@ -176,7 +176,7 @@ public class Surface : IDisposable, ICloneable, IPixelsMap
 #if DEBUG
     public void SaveToDesktop(string filename = "savedSurface.png")
     {
-        using var final = DrawingSurface.Create(new ImageInfo(Size.X, Size.Y, ColorType.Rgba8888, AlphaType.Premul, ColorSpace.CreateSrgb()));
+        using var final = DrawingSurface.Create(new ImageInfo(Size.X, Size.Y, ColorType.Rgba8888, AlphaType.Premul, ColorSpace.CreateSrgb()) { ForceCpu = true});
         final.Canvas.DrawSurface(DrawingSurface, 0, 0);
         using (var snapshot = final.Snapshot())
         {

+ 2 - 10
src/InjectedDrawingApiAvalonia/App.axaml.cs

@@ -24,22 +24,14 @@ public partial class App : Application
         if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
         {
             var grContext = App.GetGrContext();
-            DrawingBackendApi.SetupBackend(new SkiaDrawingBackend(CreateGpuSurface));
+            DrawingBackendApi.SetupBackend(new SkiaDrawingBackend());
             
             desktop.MainWindow = new MainWindow();
         }
 
         base.OnFrameworkInitializationCompleted();
     }
-
-    private SKSurface CreateGpuSurface(VecI size)
-    {
-        WriteableBitmap bitmap = new WriteableBitmap(new PixelSize(size.X, size.Y), new Vector(96, 96), PixelFormat.Bgra8888);
-        using var fb = bitmap.Lock();
-        SKImageInfo info = new(size.X, size.Y, SKColorType.Bgra8888, SKAlphaType.Premul);
-        return SKSurface.Create(info, fb.Address, fb.RowBytes);
-    }
-
+    
     public static GRContext GetGrContext()
     {
         Compositor compositor = Compositor.TryGetDefaultCompositor();

+ 50 - 0
src/InjectedDrawingApiAvalonia/CommandBuffer.cs

@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
+using SkiaSharp;
+
+namespace InjectedDrawingApiAvalonia;
+
+public class CommandBuffer
+{
+    private List<Command> _commands = new List<Command>();
+    public void DrawRect(int x, int y, int width, int height, Paint paint)
+    {
+        _commands.Add(new DrawRectCommand(x, y, width, height, paint));
+    }
+    
+    public void Dispatch(SKCanvas surface)
+    {
+        foreach (var command in _commands)
+        {
+            command.Execute(surface);
+        }
+    }
+}
+
+public abstract class Command
+{
+    public abstract void Execute(SKCanvas surface);
+}
+
+public class DrawRectCommand : Command
+{
+    private readonly int _x;
+    private readonly int _y;
+    private readonly int _width;
+    private readonly int _height;
+    private readonly SKPaint _paint;
+
+    public DrawRectCommand(int x, int y, int width, int height, Paint paint)
+    {
+        _x = x;
+        _y = y;
+        _width = width;
+        _height = height;
+        _paint = paint.Native as SKPaint;
+    }
+
+    public override void Execute(SKCanvas surface)
+    {
+        surface.DrawRect(_x, _y, _width, _height, _paint);
+    }
+}

+ 1 - 2
src/InjectedDrawingApiAvalonia/MainWindow.axaml

@@ -8,7 +8,6 @@
         Name="window" Background="CornflowerBlue"
         Title="InjectedDrawingApiAvalonia">
     
-    <visuals:SurfaceControl Width="128" Height="128" Name="SurfaceControl" VerticalAlignment="Top" HorizontalAlignment="Left"
-        Surface="{Binding Surface, ElementName=window}"/>
+    <visuals:SurfaceControl Width="128" Height="128" Name="SurfaceControl" VerticalAlignment="Top" HorizontalAlignment="Left"/>
     
 </Window>

+ 20 - 17
src/InjectedDrawingApiAvalonia/MainWindow.axaml.cs

@@ -1,40 +1,43 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
 using Avalonia.Interactivity;
+using Avalonia.Media;
 using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
 using PixiEditor.DrawingApi.Skia;
+using SkiaSharp;
+using Colors = PixiEditor.DrawingApi.Core.ColorsImpl.Colors;
 
 namespace InjectedDrawingApiAvalonia;
 
 public partial class MainWindow : Window
 {
-    public static readonly StyledProperty<DrawingSurface> SurfaceProperty = AvaloniaProperty.Register<MainWindow, DrawingSurface>(
-        "Surface");
-
-    public DrawingSurface Surface
-    {
-        get => GetValue(SurfaceProperty);
-        set => SetValue(SurfaceProperty, value);
-    }
-    
+    CommandBuffer CommandBuffer = new CommandBuffer();
     public MainWindow()
     {
         InitializeComponent();
+        SurfaceControl.Draw += SurfaceControlOnDraw;
+
+        Task.Run(() =>
+        {
+            CommandBuffer.DrawRect(10, 10, 100, 100, new Paint { Color = Colors.Red });
+        });
     }
 
-    protected override void OnLoaded(RoutedEventArgs e)
+    protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
     {
-        base.OnLoaded(e);
-        
-        Surface = DrawingSurface.Create(new ImageInfo(128, 128));
-        
-        Surface.Canvas.Clear(Colors.Brown);
-        Surface.Canvas.DrawRect(0, 0, 128, 128, new Paint(){ Color = Colors.Green });
-        Surface.Canvas.Flush();
         SurfaceControl.InvalidateVisual();
     }
+
+    private void SurfaceControlOnDraw(SKCanvas draw)
+    {
+        CommandBuffer.Dispatch(draw);
+    }
 }

+ 12 - 22
src/InjectedDrawingApiAvalonia/Visuals/SurfaceControl.cs

@@ -6,7 +6,9 @@ using Avalonia.Media.Imaging;
 using Avalonia.Rendering.SceneGraph;
 using Avalonia.Skia;
 using Avalonia.Threading;
+using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.Surface;
+using PixiEditor.DrawingApi.Skia.Implementations;
 using PixiEditor.Numerics;
 using SkiaSharp;
 
@@ -43,6 +45,8 @@ internal class SurfaceControl : Control
         set => SetValue(SurfaceProperty, value);
     }
 
+    public Action<SKCanvas> Draw { get; set; }
+
     private RectI? nextDirtyRect;
 
     static SurfaceControl()
@@ -82,18 +86,8 @@ internal class SurfaceControl : Control
 
     public override void Render(DrawingContext context)
     {
-        if (Background != null)
-        {
-            context.FillRectangle(Background, new Rect(0, 0, Bounds.Width, Bounds.Height));
-        }
-
-        if (Surface == null || Surface.IsDisposed)
-        {
-            return;
-        }
-
         var bounds = new Rect(Bounds.Size);
-        var operation = new DrawSurfaceOperation(bounds, Surface, Stretch, Opacity);
+        var operation = new DrawSurfaceOperation(Draw, bounds, Stretch, Opacity);
         context.Custom(operation);
         
         Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
@@ -138,42 +132,38 @@ internal class SurfaceControl : Control
 
 internal class DrawSurfaceOperation : SkiaDrawOperation
 {
-    public DrawingSurface Surface { get; }
     public Stretch Stretch { get; }
 
     public double Opacity { get; set; } = 1.0;
+    
+    public Action<SKCanvas> Draw;
 
     private SKPaint _paint = new SKPaint();
-    
-    private WriteableBitmap targetBitmap;
 
-    public DrawSurfaceOperation(Rect dirtyBounds, DrawingSurface surface, Stretch stretch, double opacity = 1) :
+    public DrawSurfaceOperation(Action<SKCanvas> drawEvent, Rect dirtyBounds, Stretch stretch, double opacity = 1) :
         base(dirtyBounds)
     {
-        Surface = surface;
         Stretch = stretch;
         Opacity = opacity;
+        Draw = drawEvent;
     }
 
     public override void Render(ISkiaSharpApiLease lease)
     {
         SKCanvas canvas = lease.SkCanvas;
 
-        if (Surface == null || Surface.IsDisposed)
-        {
-            return;
-        }
+        (DrawingBackendApi.Current.SurfaceImplementation as SkiaSurfaceImplementation).GrContext = lease.GrContext;
             
         canvas.Save();
         _paint.Color = _paint.Color.WithAlpha((byte)(Opacity * 255));
-        canvas.DrawSurface((SKSurface)Surface.Native, new SKPoint(0, 0), _paint);
+        Draw?.Invoke(canvas);
         canvas.Restore();
     }
 
 
     public override bool Equals(ICustomDrawOperation? other)
     {
-        return other is DrawSurfaceOperation otherOp && otherOp.Surface == Surface && otherOp.Stretch == Stretch;
+        return other is DrawSurfaceOperation otherOp && otherOp.Stretch == Stretch;
     }
 
     public override void Dispose()

+ 2 - 2
src/PixiEditor.AvaloniaUI/Models/DocumentModels/ActionAccumulator.cs

@@ -92,8 +92,8 @@ internal class ActionAccumulator
             // update the contents of the bitmaps
             var affectedAreas = new AffectedAreasGatherer(document.AnimationHandler.ActiveFrameTime, internals.Tracker, optimizedChanges);
             List<IRenderInfo> renderResult = new();
-            renderResult.AddRange(await canvasUpdater.UpdateGatheredChunks(affectedAreas, undoBoundaryPassed || viewportRefreshRequest));
-            renderResult.AddRange(await previewUpdater.UpdateGatheredChunks(affectedAreas, undoBoundaryPassed));
+            renderResult.AddRange(canvasUpdater.UpdateGatheredChunksSync(affectedAreas, undoBoundaryPassed || viewportRefreshRequest));
+            renderResult.AddRange(previewUpdater.UpdateGatheredChunksSync(affectedAreas, undoBoundaryPassed));
 
             if (undoBoundaryPassed)
             {

+ 2 - 3
src/PixiEditor.AvaloniaUI/Views/Rendering/Scene.cs

@@ -507,14 +507,13 @@ internal class DrawSceneOperation : SkiaDrawOperation
             return;
         }
         
-        ((SKSurface)Surface.DrawingSurface.Native).Flush(true);
-        using Image snapshot = Surface.DrawingSurface.Snapshot(SurfaceRectToRender);
+        //using Image snapshot = Surface.DrawingSurface.Snapshot(SurfaceRectToRender);
 
         var matrixValues = new float[ColorMatrix.Width * ColorMatrix.Height];
         ColorMatrix.TryGetMembers(matrixValues);
         
         _paint.ColorFilter = SKColorFilter.CreateColorMatrix(matrixValues);
-        canvas.DrawImage((SKImage)snapshot.Native, SurfaceRectToRender.X, SurfaceRectToRender.Y, _paint);
+        //canvas.DrawImage((SKImage)snapshot.Native, SurfaceRectToRender.X, SurfaceRectToRender.Y, _paint);
 
         canvas.Restore();
     }

+ 2 - 4
src/PixiEditor.AvaloniaUI/Views/Visuals/SurfaceControl.cs

@@ -198,11 +198,9 @@ internal class DrawSurfaceOperation : SkiaDrawOperation
         }*/
 
         _paint.Color = _paint.Color.WithAlpha((byte)(Opacity * 255));
-        ((SKSurface)Surface.DrawingSurface.Native).Flush(true);
-        canvas.DrawSurface(((SKSurface)Surface.DrawingSurface.Native), new SKPoint(0, 0), _paint);
+        //((SKSurface)Surface.DrawingSurface.Native).Flush(true);
+        //canvas.DrawSurface(((SKSurface)Surface.DrawingSurface.Native), new SKPoint(0, 0), _paint);
         canvas.Restore();
-        
-        canvas.Flush();
     }
 
 

+ 1 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ShaderNode.cs

@@ -46,6 +46,7 @@ public class ShaderNode : Node
         }
 
         Input.Value.DrawingSurface.Canvas.DrawPaint(new Paint {Shader = shader});
+        Output.Value = Input.Value;
         return Input.Value;
     }
 

+ 2 - 0
src/PixiEditor.DrawingApi.Core/Surface/ImageData/ImageInfo.cs

@@ -138,6 +138,8 @@ public struct ImageInfo : System.IEquatable<ImageInfo>
     /// <value />
     public readonly RectI Rect => RectI.Create(this.Width, this.Height);
 
+    public bool ForceCpu { get; set; } = false;
+
 
     public readonly ImageInfo WithSize(VecI size) => this.WithSize(size.X, size.Y);
 

+ 10 - 5
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaSurfaceImplementation.cs

@@ -53,7 +53,7 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         
         public DrawingSurface Create(ImageInfo imageInfo, IntPtr pixels, int rowBytes)
         {
-            SKSurface skSurface = CreateSkiaSurface(imageInfo.Size);
+            SKSurface skSurface = CreateSkiaSurface(imageInfo.Size, !imageInfo.ForceCpu);
             
             var canvas = skSurface.Canvas;
             canvas.DrawImage(SKImage.FromPixelCopy(imageInfo.ToSkImageInfo(), pixels, rowBytes), new SKPoint(0, 0));
@@ -63,7 +63,7 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
 
         public DrawingSurface Create(ImageInfo imageInfo, IntPtr pixelBuffer)
         {
-            SKSurface skSurface = CreateSkiaSurface(imageInfo.Size);
+            SKSurface skSurface = CreateSkiaSurface(imageInfo.Size, !imageInfo.ForceCpu);
             
             var canvas = skSurface.Canvas;
             canvas.DrawImage(SKImage.FromPixelCopy(imageInfo.ToSkImageInfo(), pixelBuffer), new SKPoint(0, 0));
@@ -75,7 +75,7 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         {
             SKPixmap skPixmap = _pixmapImplementation[pixmap.ObjectPointer];
             SKImageInfo info = skPixmap.Info;
-            SKSurface skSurface = CreateSkiaSurface(new VecI(info.Width, info.Height));
+            SKSurface skSurface = CreateSkiaSurface(new VecI(info.Width, info.Height), true);
             
             var canvas = skSurface.Canvas;
             canvas.DrawImage(SKImage.FromPixels(skPixmap), new SKPoint(0, 0));
@@ -85,12 +85,17 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
 
         public DrawingSurface Create(ImageInfo imageInfo)
         {
-            SKSurface skSurface = CreateSkiaSurface(imageInfo.Size);
+            SKSurface skSurface = CreateSkiaSurface(imageInfo.Size, !imageInfo.ForceCpu);
             return CreateDrawingSurface(skSurface);
         }
 
-        private SKSurface CreateSkiaSurface(VecI size)
+        private SKSurface CreateSkiaSurface(VecI size, bool gpu)
         {
+            if (!gpu)
+            {
+                return SKSurface.Create(new SKImageInfo(size.X, size.Y));
+            }
+            
             return SKSurface.Create(GrContext, false, new SKImageInfo(size.X, size.Y));
         }