Browse Source

ShaderBuilder wip

flabbet 1 year ago
parent
commit
85c23193fd

+ 11 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ApplyFilterNode.cs

@@ -17,6 +17,8 @@ public class ApplyFilterNode : Node
     public InputProperty<Texture?> Input { get; }
     
     public InputProperty<Filter?> Filter { get; }
+    
+    private Texture _workingSurface;
 
     public ApplyFilterNode()
     {
@@ -34,12 +36,17 @@ public class ApplyFilterNode : Node
         
         _paint.SetFilters(Filter.Value);
 
-        var workingSurface = new Texture(input.Size);
+        if (_workingSurface == null || _workingSurface.Size != input.Size)
+        {
+            _workingSurface?.Dispose();
+            _workingSurface = new Texture(input.Size);
+            _workingSurface.DrawingSurface.Canvas.Clear();
+        }
         
-        workingSurface.DrawingSurface.Canvas.DrawSurface(input.DrawingSurface, 0, 0, _paint);
+        _workingSurface.DrawingSurface.Canvas.DrawSurface(input.DrawingSurface, 0, 0, _paint);
 
-        Output.Value = workingSurface;
-        return workingSurface;
+        Output.Value = _workingSurface;
+        return _workingSurface;
     }
 
     public override Node CreateCopy() => new ApplyFilterNode();

+ 31 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs

@@ -5,6 +5,7 @@ using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Numerics;
@@ -27,6 +28,7 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
     public override string DisplayName { get; set; } = "MODIFY_IMAGE_RIGHT_NODE";
 
     private Texture surface;
+    private string _lastSksl;
 
     public ModifyImageRightNode()
     {
@@ -55,15 +57,37 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
         var width = size.X;
         var height = size.Y;
         
-        surface = new Texture(size);
+        if (surface == null || surface.Size != size)
+        {
+            surface?.Dispose();
+            surface = new Texture(size);
+            surface.DrawingSurface.Canvas.Clear();
+        }
 
-        startNode.PreparePixmap(renderingContext);
-        
-        using Pixmap targetPixmap = surface.PeekReadOnlyPixels();
+        if (!surface.IsHardwareAccelerated)
+        {
+            startNode.PreparePixmap(renderingContext);
 
-        ModifyImageInParallel(renderingContext, targetPixmap, width, height);
-        
-        startNode.DisposePixmap(renderingContext);
+            using Pixmap targetPixmap = surface.PeekReadOnlyPixels();
+
+            ModifyImageInParallel(renderingContext, targetPixmap, width, height);
+
+            startNode.DisposePixmap(renderingContext);
+        }
+        else
+        {
+            ShaderBuilder builder = new();
+            builder.WithTexture("original", startNode.Image.Value);
+
+            string sksl = builder.ToSkSl();
+            if (sksl != _lastSksl)
+            {
+                _lastSksl = sksl;
+                drawingPaint.Shader = builder.BuildShader();
+            }
+            
+            surface.DrawingSurface.Canvas.DrawPaint(drawingPaint);
+        }
 
         Output.Value = surface;
 

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

@@ -2,6 +2,7 @@
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Numerics;
 

+ 1 - 0
src/PixiEditor.DrawingApi.Core/Bridge/NativeObjectsImpl/IPaintImplementation.cs

@@ -1,5 +1,6 @@
 using System;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 

+ 2 - 0
src/PixiEditor.DrawingApi.Core/Bridge/NativeObjectsImpl/IShaderImplementation.cs

@@ -1,5 +1,6 @@
 using System;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Numerics;
 
@@ -10,6 +11,7 @@ public interface IShaderImplementation
     public IntPtr CreateShader();
     public void Dispose(IntPtr shaderObjPointer);
     public Shader? CreateFromSksl(string sksl, bool isOpaque, out string errors);
+    public Shader? CreateFromSksl(string sksl, bool isOpaque, Uniforms uniforms, out string errors);
     public Shader CreateLinearGradient(VecI p1, VecI p2, Color[] colors);
     public Shader CreatePerlinNoiseTurbulence(float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed);
     public Shader CreatePerlinFractalNoise(float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed);

+ 2 - 0
src/PixiEditor.DrawingApi.Core/Bridge/Operations/IImageImplementation.cs

@@ -1,6 +1,7 @@
 using System;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
 using PixiEditor.Numerics;
@@ -24,5 +25,6 @@ namespace PixiEditor.DrawingApi.Core.Bridge.Operations
         public Image Clone(Image image);
         public Pixmap PeekPixels(IntPtr objectPointer);
         public ImageInfo GetImageInfo(IntPtr objectPointer);
+        public Shader ToShader(IntPtr objectPointer);
     }
 }

+ 10 - 0
src/PixiEditor.DrawingApi.Core/Exceptions/ShaderCompilationException.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace PixiEditor.DrawingApi.Core.Exceptions;
+
+public class ShaderCompilationException : Exception
+{
+    public ShaderCompilationException(string errors) : base(errors)
+    {
+    }
+}

+ 12 - 2
src/PixiEditor.DrawingApi.Core/Surfaces/PaintImpl/Shader.cs → src/PixiEditor.DrawingApi.Core/Shaders/Shader.cs

@@ -1,9 +1,11 @@
 using System;
 using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Exceptions;
+using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.Numerics;
 
-namespace PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+namespace PixiEditor.DrawingApi.Core.Shaders;
 
 public class Shader : NativeObject
 {
@@ -12,7 +14,15 @@ public class Shader : NativeObject
     public Shader(IntPtr objPtr) : base(objPtr)
     {
     }
-    
+
+    public Shader(string sksl, Uniforms uniforms) : base(DrawingBackendApi.Current.ShaderImplementation.CreateFromSksl(sksl, false, uniforms, out string errors)?.ObjectPointer ?? IntPtr.Zero)
+    {
+        if (!string.IsNullOrEmpty(errors))
+        {
+            throw new ShaderCompilationException(errors);
+        }
+    }
+
     public static Shader? CreateFromSksl(string sksl, bool isOpaque, out string errors)
     {
        return DrawingBackendApi.Current.ShaderImplementation.CreateFromSksl(sksl, isOpaque, out errors);

+ 51 - 0
src/PixiEditor.DrawingApi.Core/Shaders/ShaderBuilder.cs

@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.Text;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.DrawingApi.Core.Shaders;
+
+public class ShaderBuilder
+{
+    public Uniforms Uniforms { get; } = new Uniforms();
+
+    public Shader BuildShader()
+    {
+        string generatedSksl = ToSkSl();
+        return new Shader(generatedSksl, Uniforms);
+    }
+
+    public string ToSkSl()
+    {
+        StringBuilder sb = new StringBuilder();
+        AppendUniforms(sb);
+        sb.AppendLine("half4 main(float2 p)");
+        sb.AppendLine("{");
+        sb.AppendLine("return original.eval(p).rgba;");
+        sb.AppendLine("}");
+        
+        return sb.ToString();
+    }
+
+    private void AppendUniforms(StringBuilder sb)
+    {
+        foreach (var uniform in Uniforms)
+        {
+            sb.AppendLine($"uniform {uniform.Value.UniformName} {uniform.Value.Name};");
+        }
+    }
+
+    public void WithTexture(string name, Texture texture)
+    {
+        Uniforms[name] = new Uniform(name, texture.DrawingSurface.Snapshot().ToShader());
+    }
+
+    public void WithFloat(string name, float value)
+    {
+        Uniforms[name] = new Uniform(name, value);
+    }
+
+    public void WithVecD(string name, VecD vector)
+    {
+        Uniforms[name] = new Uniform(name, vector);
+    }
+}

+ 54 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Uniform.cs

@@ -0,0 +1,54 @@
+using System;
+using System.ComponentModel;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.DrawingApi.Core.Shaders;
+
+public struct Uniform
+{
+    public string Name { get; }
+    public float FloatValue { get; }
+    public float[] FloatArrayValue { get; }
+    public Shader ShaderValue { get; }
+    
+    public string UniformName { get; }
+
+    public UniformValueType DataType { get; }
+    
+    public Uniform(string name, float value)
+    {
+        Name = name;
+        FloatValue = value;
+        FloatArrayValue = default;
+        ShaderValue = default;
+        DataType = UniformValueType.Float;
+        UniformName = "float";
+    }
+    
+    public Uniform(string name, VecD vector)
+    {
+        Name = name;
+        FloatValue = default;
+        FloatArrayValue = new float[] { (float)vector.X, (float)vector.Y };
+        ShaderValue = default;
+        DataType = UniformValueType.FloatArray;
+        UniformName = "float2";
+    }
+    
+    public Uniform(string name, Shader value)
+    {
+        Name = name;
+        FloatValue = default;
+        FloatArrayValue = default;
+        ShaderValue = value;
+        DataType = UniformValueType.Shader;
+        UniformName = "shader";
+    }
+}
+
+public enum UniformValueType
+{
+    Float,
+    FloatArray,
+    Shader
+}

+ 8 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Uniforms.cs

@@ -0,0 +1,8 @@
+using System.Collections.Generic;
+
+namespace PixiEditor.DrawingApi.Core.Shaders;
+
+public class Uniforms : Dictionary<string, Uniform>
+{
+  
+}

+ 6 - 0
src/PixiEditor.DrawingApi.Core/Surfaces/ImageData/Image.cs

@@ -1,5 +1,6 @@
 using System;
 using PixiEditor.DrawingApi.Core.Bridge;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.DrawingApi.Core.Surfaces.ImageData
@@ -67,5 +68,10 @@ namespace PixiEditor.DrawingApi.Core.Surfaces.ImageData
         {
             return DrawingBackendApi.Current.ImageImplementation.Clone(this);
         }
+
+        public Shader ToShader()
+        {
+            return DrawingBackendApi.Current.ImageImplementation.ToShader(ObjectPointer);
+        }
     }
 }

+ 1 - 0
src/PixiEditor.DrawingApi.Core/Surfaces/PaintImpl/Paint.cs

@@ -1,6 +1,7 @@
 using System;
 using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Shaders;
 
 namespace PixiEditor.DrawingApi.Core.Surfaces.PaintImpl
 {

+ 2 - 0
src/PixiEditor.DrawingApi.Core/Texture.cs

@@ -1,5 +1,6 @@
 using System;
 using System.IO;
+using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
@@ -16,6 +17,7 @@ public class Texture : IDisposable
     public event SurfaceChangedEventHandler? Changed;
 
     public bool IsDisposed { get; private set; }
+    public bool IsHardwareAccelerated { get; } = DrawingBackendApi.Current.IsHardwareAccelerated;
 
     private bool pixmapUpToDate;
     private Pixmap pixmap;

+ 7 - 0
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaImageImplementation.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using PixiEditor.DrawingApi.Core.Bridge.Operations;
 using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
 using PixiEditor.Numerics;
@@ -143,6 +144,12 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             return info.ToImageInfo();
         }
 
+        public Shader ToShader(IntPtr objectPointer)
+        {
+            var shader = ManagedInstances[objectPointer].ToShader();
+            return new Shader(shader.Handle);
+        }
+
         public object GetNativeImage(IntPtr objectPointer)
         {
             return ManagedInstances[objectPointer];

+ 1 - 0
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaPaintImplementation.cs

@@ -1,6 +1,7 @@
 using System;
 using PixiEditor.DrawingApi.Core.Bridge.NativeObjectsImpl;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using SkiaSharp;

+ 48 - 0
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaShaderImplementation.cs

@@ -1,6 +1,7 @@
 using System;
 using PixiEditor.DrawingApi.Core.Bridge.NativeObjectsImpl;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Numerics;
 using SkiaSharp;
@@ -20,6 +21,21 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             return skShader.Handle;
         }
 
+        public Shader? CreateFromSksl(string sksl, bool isOpaque, Uniforms uniforms, out string errors)
+        {
+            SKRuntimeEffect effect = SKRuntimeEffect.Create(sksl, out errors);
+            if (string.IsNullOrEmpty(errors))
+            {
+                SKRuntimeEffectUniforms effectUniforms = UniformsToSkUniforms(uniforms, effect); 
+                SKRuntimeEffectChildren effectChildren = UniformsToSkChildren(uniforms, effect);
+                SKShader shader = effect.ToShader(isOpaque, effectUniforms, effectChildren);
+                ManagedInstances[shader.Handle] = shader;
+                return new Shader(shader.Handle);
+            }
+            
+            return null;
+        }
+        
         public Shader? CreateFromSksl(string sksl, bool isOpaque, out string errors)
         {
             SKRuntimeEffect effect = SKRuntimeEffect.Create(sksl, out errors);
@@ -83,5 +99,37 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             shader.Dispose();
             ManagedInstances.TryRemove(shaderObjPointer, out _);
         }
+        
+        private SKRuntimeEffectUniforms UniformsToSkUniforms(Uniforms uniforms, SKRuntimeEffect effect)
+        {
+            SKRuntimeEffectUniforms skUniforms = new SKRuntimeEffectUniforms(effect);
+            foreach (var uniform in uniforms)
+            {
+                if (uniform.Value.DataType == UniformValueType.Float)
+                {
+                    skUniforms.Add(uniform.Value.Name, uniform.Value.FloatValue);
+                }
+                else if (uniform.Value.DataType == UniformValueType.FloatArray)
+                {
+                    skUniforms.Add(uniform.Value.Name, uniform.Value.FloatArrayValue);
+                }
+            }
+
+            return skUniforms;
+        }
+        
+        private SKRuntimeEffectChildren UniformsToSkChildren(Uniforms uniforms, SKRuntimeEffect effect)
+        {
+            SKRuntimeEffectChildren skChildren = new SKRuntimeEffectChildren(effect);
+            foreach (var uniform in uniforms)
+            {
+                if (uniform.Value.DataType == UniformValueType.Shader)
+                {
+                    skChildren.Add(uniform.Value.Name, this[uniform.Value.ShaderValue.ObjectPointer]);
+                }
+            }
+
+            return skChildren;
+        }
     }
 }

+ 1 - 1
src/PixiEditor.UI.Common/Accents/Base.axaml

@@ -90,7 +90,7 @@
             <SolidColorBrush x:Key="SelectionFillBrush" Color="{StaticResource SelectionFillColor}"/>
             
             <SolidColorBrush x:Key="DefaultSocketBrush" Color="{StaticResource DefaultSocketColor}"/>
-            <SolidColorBrush x:Key="SurfaceSocketBrush" Color="{StaticResource ImageSocketColor}"/>
+            <SolidColorBrush x:Key="TextureSocketBrush" Color="{StaticResource ImageSocketColor}"/>
             <SolidColorBrush x:Key="BooleanSocketBrush" Color="{StaticResource BoolSocketColor}"/>
             <SolidColorBrush x:Key="SingleSocketBrush" Color="{StaticResource FloatSocketColor}"/>
             <SolidColorBrush x:Key="DoubleSocketBrush" Color="{StaticResource DoubleSocketColor}"/>