Browse Source

Shader builder works

flabbet 1 year ago
parent
commit
1645b382d4

+ 14 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/Context/FuncContext.cs

@@ -1,5 +1,9 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Shaders.Generation;
+using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Context;
@@ -8,10 +12,12 @@ public class FuncContext
 {
     public static FuncContext NoContext { get; } = new();
     
-    public VecD Position { get; private set; }
+    public Float2 Position { get; private set; }
     public VecI Size { get; private set; }
     public bool HasContext { get; private set; }
     public RenderingContext RenderingContext { get; set; }
+    
+    public ShaderBuilder Builder { get; set; }
 
     public void ThrowOnMissingContext()
     {
@@ -26,15 +32,17 @@ public class FuncContext
         
     }
     
-    public FuncContext(RenderingContext renderingContext)
+    public FuncContext(RenderingContext renderingContext, ShaderBuilder builder)
     {
         RenderingContext = renderingContext;
+        Builder = builder;
+        HasContext = true;
+        Position = new Float2("coords"); // input argument 'half4 main(float2 coords)'
     }
 
-    public void UpdateContext(VecD position, VecI size)
+    public Half4 SampleTexture(Texture? imageValue, Float2 pos)
     {
-        Position = position;
-        Size = size;
-        HasContext = true;
+        TextureSampler texName = Builder.AddTexture(imageValue);
+        return Builder.Sample(texName, pos);
     }
 }

+ 8 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/FuncInputProperty.cs

@@ -2,6 +2,8 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changes.NodeGraph;
+using PixiEditor.DrawingApi.Core.Shaders;
+using PixiEditor.DrawingApi.Core.Shaders.Generation;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 
@@ -40,6 +42,12 @@ public class FuncInputProperty<T> : InputProperty<Func<FuncContext, T>>, IFuncIn
             constantNonOverrideValue = (T)value;
             return;
         }
+
+        if (constantNonOverrideValue is ShaderExpressionVariable shaderExpressionVariable)
+        {
+            shaderExpressionVariable.SetConstantValue(value, ConversionTable.Convert);
+            return;
+        }
         
         if(ConversionTable.TryConvert(value, typeof(T), out var result))
         {

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/FuncOutputProperty.cs

@@ -1,6 +1,7 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.DrawingApi.Core.Shaders;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 
@@ -8,5 +9,6 @@ public class FuncOutputProperty<T> : OutputProperty<Func<FuncContext, T>>
 {
     internal FuncOutputProperty(Node node, string internalName, string displayName, Func<FuncContext, T> defaultValue) : base(node, internalName, displayName, defaultValue)
     {
+        
     }
 }

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs

@@ -16,7 +16,8 @@ public class ImageSpaceNode : Node
     public override string DisplayName { get; set; } = "IMAGE_SPACE_NODE";
     public ImageSpaceNode()
     {
-        SpacePosition = CreateFuncOutput(nameof(SpacePosition), "UV", ctx => ctx.Position);
+        // TODO: Implement this
+        //SpacePosition = CreateFuncOutput(nameof(SpacePosition), "UV", ctx => ctx.Position);
         Size = CreateFuncOutput(nameof(Size), "SIZE", ctx => ctx.Size);
     }
 

+ 13 - 8
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs

@@ -5,6 +5,9 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Shaders;
+using PixiEditor.DrawingApi.Core.Shaders.Generation;
+using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.Numerics;
 
@@ -16,9 +19,9 @@ public class ModifyImageLeftNode : Node
 {
     public InputProperty<Texture?> Image { get; }
     
-    public FuncOutputProperty<VecD> Coordinate { get; }
+    public FuncOutputProperty<Float2> Coordinate { get; }
     
-    public FuncOutputProperty<Color> Color { get; }
+    public FuncOutputProperty<Half4> Color { get; }
 
     public override string DisplayName { get; set; } = "MODIFY_IMAGE_LEFT_NODE";
     
@@ -31,19 +34,21 @@ public class ModifyImageLeftNode : Node
         Color = CreateFuncOutput("Color", "COLOR", GetColor);
     }
 
-    private Color GetColor(FuncContext context)
+    private Half4 GetColor(FuncContext context)
     {
         context.ThrowOnMissingContext();
 
-        var targetPixmap = pixmapCache[context.RenderingContext];
-        
+        return context.SampleTexture(Image.Value, context.Position);
+
+        /*var targetPixmap = pixmapCache[context.RenderingContext];
+
         if (targetPixmap == null)
             return new Color();
-        
+
         var x = context.Position.X * context.Size.X;
         var y = context.Position.Y * context.Size.Y;
-        
-        return targetPixmap.GetPixelColor((int)x, (int)y);
+
+        return targetPixmap.GetPixelColor((int)x, (int)y);*/
     }
 
     internal void PreparePixmap(RenderingContext forContext)

+ 35 - 14
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs

@@ -6,6 +6,8 @@ using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Shaders;
+using PixiEditor.DrawingApi.Core.Shaders.Generation;
+using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Numerics;
@@ -20,8 +22,8 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
 
     private Paint drawingPaint = new Paint() { BlendMode = BlendMode.Src };
 
-    public FuncInputProperty<VecD> Coordinate { get; }
-    public FuncInputProperty<Color> Color { get; }
+    public FuncInputProperty<Float2> Coordinate { get; }
+    public FuncInputProperty<Half4> Color { get; }
 
     public OutputProperty<Texture> Output { get; }
 
@@ -32,8 +34,8 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
 
     public ModifyImageRightNode()
     {
-        Coordinate = CreateFuncInput(nameof(Coordinate), "UV", new VecD());
-        Color = CreateFuncInput(nameof(Color), "COLOR", new Color());
+        Coordinate = CreateFuncInput(nameof(Coordinate), "UV", new Float2("coords", VecD.Zero));
+        Color = CreateFuncInput(nameof(Color), "COLOR", new Half4("", DrawingApi.Core.ColorsImpl.Color.Empty));
         Output = CreateOutput<Texture>(nameof(Output), "OUTPUT", null);
     }
 
@@ -53,10 +55,10 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
         {
             return null;
         }
-        
+
         var width = size.X;
         var height = size.Y;
-        
+
         if (surface == null || surface.Size != size)
         {
             surface?.Dispose();
@@ -77,7 +79,25 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
         else
         {
             ShaderBuilder builder = new();
-            builder.WithTexture("original", startNode.Image.Value);
+            FuncContext context = new(renderingContext, builder);
+
+            if (Coordinate.Connection != null)
+            {
+                builder.Set(context.Position, Coordinate.Value(context));
+            }
+            else
+            {
+                builder.SetConstant(context.Position, Coordinate.NonOverridenValue(FuncContext.NoContext));
+            }
+
+            if (Color.Connection != null)
+            {
+                builder.ReturnVar(Color.Value(context));
+            }
+            else
+            {
+                builder.ReturnConst(Color.NonOverridenValue(FuncContext.NoContext));
+            }
 
             string sksl = builder.ToSkSl();
             if (sksl != _lastSksl)
@@ -85,7 +105,7 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
                 _lastSksl = sksl;
                 drawingPaint.Shader = builder.BuildShader();
             }
-            
+
             surface.DrawingSurface.Canvas.DrawPaint(drawingPaint);
         }
 
@@ -94,15 +114,16 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
         return Output.Value;
     }
 
-    private unsafe void ModifyImageInParallel(RenderingContext renderingContext, Pixmap targetPixmap, int width, int height)
+    private unsafe void ModifyImageInParallel(RenderingContext renderingContext, Pixmap targetPixmap, int width,
+        int height)
     {
         int threads = Environment.ProcessorCount;
         int chunkHeight = height / threads;
 
-        Parallel.For(0, threads, i =>
+        /*Parallel.For(0, threads, i =>
         {
             FuncContext context = new(renderingContext);
-            
+
             int startY = i * chunkHeight;
             int endY = (i + 1) * chunkHeight;
             if (i == threads - 1)
@@ -119,16 +140,16 @@ public class ModifyImageRightNode : Node, IPairNodeEnd
                     context.UpdateContext(new VecD((double)x / width, (double)y / height), new VecI(width, height));
                     var coordinate = Coordinate.Value(context);
                     context.UpdateContext(coordinate, new VecI(width, height));
-                    
+
                     var color = Color.Value(context);
                     ulong colorBits = color.ToULong();
-                    
+
                     int pixelOffset = (y * width + x) * 4;
                     Half* drawPixel = drawArray + pixelOffset;
                     *(ulong*)drawPixel = colorBits;
                 }
             }
-        });
+        });*/
     }
 
     private void FindStartNode()

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

@@ -6,6 +6,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;

+ 5 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SampleImageNode.cs

@@ -23,11 +23,12 @@ public class SampleImageNode : Node
     public SampleImageNode()
     {
         Image = CreateInput<Texture>(nameof(Texture), "IMAGE", null);
-        Coordinate = CreateFuncOutput(nameof(Coordinate), "UV", ctx => ctx.Position);
-        Color = CreateFuncOutput(nameof(Color), "COLOR", GetColor);
+        //TODO: Implement this
+        //Coordinate = CreateFuncOutput(nameof(Coordinate), "UV", ctx => ctx.Position);
+        //Color = CreateFuncOutput(nameof(Color), "COLOR", GetColor);
     }
 
-    private Color GetColor(FuncContext context)
+    /*private Color GetColor(FuncContext context)
     {
         context.ThrowOnMissingContext();
 
@@ -38,7 +39,7 @@ public class SampleImageNode : Node
         var y = context.Position.Y * context.Size.Y;
 
         return pixmap.GetPixelColor((int)x, (int)y);
-    }
+    }*/
 
     internal void PreparePixmap()
     {

+ 11 - 1
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/ConversionTable.cs

@@ -79,7 +79,7 @@ public static class ConversionTable
         
         try
         {
-            result = Convert.ChangeType(arg, targetType);
+            result = System.Convert.ChangeType(arg, targetType);
             return true;
         }
         catch
@@ -88,6 +88,16 @@ public static class ConversionTable
             return false;
         }
     }
+    
+    public static object Convert(object arg, Type targetType)
+    {
+        if (TryConvert(arg, targetType, out var result))
+        {
+            return result;
+        }
+
+        throw new InvalidCastException($"Cannot convert {arg.GetType()} to {targetType}");
+    }
 
     private static int DoubleToInt(double d)
     {

+ 20 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Generation/Expressions/Float2.cs

@@ -0,0 +1,20 @@
+using PixiEditor.Numerics;
+
+namespace PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
+
+public class Float2(string name, VecD constant) : ShaderExpressionVariable<VecD>(name, constant)
+{
+    public Float2(string name) : this(name, VecD.Zero)
+    {
+    }
+
+    public override string ConstantValueString
+    {
+        get
+        {
+            string x = ConstantValue.X.ToString(System.Globalization.CultureInfo.InvariantCulture);
+            string y = ConstantValue.Y.ToString(System.Globalization.CultureInfo.InvariantCulture);
+            return $"float2({x}, {y})";
+        }
+    }
+}

+ 12 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Generation/Expressions/Half4.cs

@@ -0,0 +1,12 @@
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+
+namespace PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
+
+public class Half4(string name, Color constant) : ShaderExpressionVariable<Color>(name, constant)
+{
+    public Half4(string name) : this(name, Colors.Transparent)
+    {
+    }
+
+    public override string ConstantValueString => $"half4({ConstantValue.R}, {ConstantValue.G}, {ConstantValue.B}, {ConstantValue.A})";
+}

+ 10 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Generation/Expressions/TextureSampler.cs

@@ -0,0 +1,10 @@
+namespace PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
+
+public class TextureSampler : ShaderExpressionVariable<Texture>
+{
+    public TextureSampler(string name) : base(name, null)
+    {
+    }
+
+    public override string ConstantValueString { get; } = "";
+}

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

@@ -0,0 +1,73 @@
+using System.Text;
+using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.DrawingApi.Core.Shaders.Generation;
+
+public class ShaderBuilder
+{
+    public Uniforms Uniforms { get; } = new Uniforms();
+
+    private StringBuilder _bodyBuilder = new StringBuilder();
+
+    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 coords)");
+        sb.AppendLine("{");
+        sb.Append(_bodyBuilder);
+        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 TextureSampler AddTexture(Texture texture)
+    {
+        string name = $"texture_{Uniforms.Count}";
+        Uniforms[name] = new Uniform(name, texture.DrawingSurface.Snapshot().ToShader());
+        return new TextureSampler(name);
+    }
+    
+    public Half4 Sample(TextureSampler texName, Float2 pos)
+    {
+        string resultName = $"color_{Uniforms.Count}";
+        Half4 result = new Half4(resultName);
+        _bodyBuilder.AppendLine($"half4 {resultName} = sample({texName.UniformName}, {pos.UniformName});"); 
+        return result;
+    }
+
+    public void ReturnVar(Half4 colorValue)
+    {
+        _bodyBuilder.AppendLine($"return {colorValue.UniformName};");
+    }
+    
+    public void ReturnConst(Half4 colorValue)
+    {
+        _bodyBuilder.AppendLine($"return {colorValue.ConstantValueString};");
+    }
+
+    public void Set<T>(T contextPosition, T coordinateValue) where T : ShaderExpressionVariable
+    {
+        _bodyBuilder.AppendLine($"{contextPosition.UniformName} = {coordinateValue.UniformName};");
+    }
+
+    public void SetConstant<T>(T contextPosition, T constantValueVar) where T : ShaderExpressionVariable
+    {
+        _bodyBuilder.AppendLine($"{contextPosition.UniformName} = {constantValueVar.ConstantValueString};"); 
+    }
+}

+ 42 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Generation/ShaderExpressionVariable.cs

@@ -0,0 +1,42 @@
+using System;
+
+namespace PixiEditor.DrawingApi.Core.Shaders.Generation;
+
+public abstract class ShaderExpressionVariable(string name)
+{
+    public string UniformName { get; set; } = name;
+    public abstract string ConstantValueString { get; }
+
+    public override string ToString()
+    {
+        return UniformName;
+    }
+
+    public abstract void SetConstantValue(object? value, Func<object, Type, object> convertFunc);
+}
+
+public abstract class ShaderExpressionVariable<TConstant>(string name, TConstant constant)
+    : ShaderExpressionVariable(name)
+{
+    public TConstant? ConstantValue { get; set; } = constant;
+
+    public override void SetConstantValue(object? value, Func<object, Type, object> convertFunc)
+    {
+        if (value is TConstant constantValue)
+        {
+            ConstantValue = constantValue;
+        }
+        else
+        {
+            try
+            {
+                constantValue = (TConstant)convertFunc(value, typeof(TConstant));
+                ConstantValue = constantValue;
+            }
+            catch (InvalidCastException)
+            {
+                ConstantValue = default;
+            }
+        }
+    }
+}

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

@@ -1,51 +0,0 @@
-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 sample(original, p);");
-        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);
-    }
-}

+ 10 - 0
src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs

@@ -111,6 +111,16 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
             connection.OutputProperty.ConnectedInputs.Remove(connection.InputProperty);
             Connections.Remove(connection);
         }
+        
+        var node = AllNodes.FirstOrDefault(x => x.Id == nodeId);
+        if (node != null)
+        {
+            var input = node.Inputs.FirstOrDefault(x => x.PropertyName == property);
+            if (input != null)
+            {
+                input.ConnectedOutput = null;
+            }
+        }
 
         StructureTree.Update(this);
     }

+ 11 - 0
src/PixiEditor/ViewModels/Nodes/NodePropertyViewModel.cs

@@ -2,6 +2,7 @@
 using Avalonia;
 using Avalonia.Media;
 using Avalonia.Styling;
+using PixiEditor.DrawingApi.Core.Shaders.Generation;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.Handlers;
 using PixiEditor.ViewModels.Nodes.Properties;
@@ -139,6 +140,11 @@ internal abstract class NodePropertyViewModel : ViewModelBase, INodePropertyHand
         {
             propertyType = type.GetMethod("Invoke").ReturnType;
         }
+
+        if (IsShaderType(propertyType))
+        {
+            propertyType = type.GetMethod("Invoke").ReturnType.BaseType.GenericTypeArguments[0];
+        }
         
         string name = $"{propertyType.Name}PropertyViewModel";
         
@@ -157,6 +163,11 @@ internal abstract class NodePropertyViewModel : ViewModelBase, INodePropertyHand
     }
 
     public void InternalSetValue(object? value) => SetProperty(ref _value, value, nameof(Value));
+    
+    private static bool IsShaderType(Type type)
+    {
+        return type.IsAssignableTo(typeof(ShaderExpressionVariable));
+    }
 }
 
 internal abstract class NodePropertyViewModel<T> : NodePropertyViewModel