2
0
Эх сурвалжийг харах

Added visible region optimization

Krzysztof Krysiński 2 долоо хоног өмнө
parent
commit
1a2e63de9a

+ 4 - 7
src/ChunkyImageLib/Chunk.cs

@@ -67,7 +67,6 @@ public class Chunk : IDisposable
     /// </summary>
     public static Chunk Create(ColorSpace chunkCs, ChunkResolution resolution = ChunkResolution.Full)
     {
-        /*
         var chunk = ChunkPool.Instance.Get(resolution, chunkCs);
         if (chunk == null || chunk.Disposed)
         {
@@ -76,8 +75,7 @@ public class Chunk : IDisposable
 
         chunk.returned = false;
         Interlocked.Increment(ref chunkCounter);
-        */
-        return new Chunk(resolution, chunkCs);
+        return chunk;
     }
 
     /// <summary>
@@ -136,13 +134,12 @@ public class Chunk : IDisposable
     /// </summary>
     public void Dispose()
     {
-        Surface.Dispose();
-        returned = true;
-        /*if (returned)
+        if (returned)
             return;
         Interlocked.Decrement(ref chunkCounter);
         Surface.DrawingSurface.Canvas.Clear();
+        Surface.DrawingSurface.Canvas.SetMatrix(Matrix3X3.Identity);
         ChunkPool.Instance.Push(this);
-        returned = true;*/
+        returned = true;
     }
 }

+ 11 - 16
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -31,7 +31,6 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     private ColorSpace colorSpace;
     private ChunkyImage layerImage => keyFrames[0]?.Data as ChunkyImage;
 
-    private Texture fullResrenderedSurface;
     private int renderedSurfaceFrame = -1;
 
     public ImageLayerNode(VecI size, ColorSpace colorSpace)
@@ -129,27 +128,29 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     private void DrawLayer(DrawingSurface workingSurface, Paint paint, SceneObjectRenderContext ctx)
     {
         int saved = workingSurface.Canvas.Save();
+        AllowHighDpiRendering = true;
 
         var sceneSize = GetSceneSize(ctx.FrameTime);
-        VecD topLeft = sceneSize / 2f;
+        var region = ctx.VisibleDocumentRegion ?? new RectI(0, 0, layerImage.LatestSize.X, layerImage.LatestSize.Y);
+        VecD topLeft = region.TopLeft - sceneSize / 2;
 
         //if (renderedSurfaceFrame == null || ctx.FullRerender || ctx.FrameTime.Frame != renderedSurfaceFrame)
         {
             topLeft *= ctx.ChunkResolution.Multiplier();
             workingSurface.Canvas.Scale((float)ctx.ChunkResolution.InvertedMultiplier());
-            if (ctx?.AffectedArea.Chunks?.Count > 0)
+            if (!ctx.FullRerender)
             {
                 GetLayerImageAtFrame(ctx.FrameTime.Frame).DrawMostUpToDateRegionOnWithAffected(
-                    new RectI(0, 0, layerImage.LatestSize.X, layerImage.LatestSize.Y),
+                    region,
                     ctx.ChunkResolution,
-                    workingSurface, ctx.AffectedArea, -topLeft, paint);
+                    workingSurface, ctx.AffectedArea, topLeft, paint, ctx.DesiredSamplingOptions);
             }
             else
             {
                 GetLayerImageAtFrame(ctx.FrameTime.Frame).DrawMostUpToDateRegionOn(
-                    new RectI(0, 0, layerImage.LatestSize.X, layerImage.LatestSize.Y),
+                    region,
                     ctx.ChunkResolution,
-                    workingSurface, -topLeft, paint);
+                    workingSurface, topLeft, paint, ctx.DesiredSamplingOptions);
             }
         }
         /*else
@@ -270,7 +271,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         {
             int saved = renderOnto.Canvas.Save();
             renderOnto.Canvas.Scale((float)context.ChunkResolution.Multiplier());
-            if (context.DesiredSamplingOptions == SamplingOptions.Default)
+            /*if (context.DesiredSamplingOptions == SamplingOptions.Default)
             {
                 renderOnto.Canvas.DrawSurface(
                     fullResrenderedSurface.DrawingSurface, 0, 0, blendPaint);
@@ -279,7 +280,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
             {
                 using var snapshot = fullResrenderedSurface.DrawingSurface.Snapshot();
                 renderOnto.Canvas.DrawImage(snapshot, 0, 0, context.DesiredSamplingOptions, blendPaint);
-            }
+            }*/
 
             renderOnto.Canvas.RestoreToCount(saved);
         }
@@ -343,12 +344,6 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return image;
     }
 
-    public override void Dispose()
-    {
-        base.Dispose();
-        fullResrenderedSurface?.Dispose();
-    }
-
     IReadOnlyChunkyImage IReadOnlyImageNode.GetLayerImageAtFrame(int frame) => GetLayerImageAtFrame(frame);
 
     IReadOnlyChunkyImage IReadOnlyImageNode.GetLayerImageByKeyFrameGuid(Guid keyFrameGuid) =>
@@ -367,7 +362,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
         var img = GetLayerImageAtFrame(frameTime.Frame);
 
-        RenderChunkyImageChunk(chunkPos, resolution, img, 85, processColorSpace, ref fullResrenderedSurface);
+        //RenderChunkyImageChunk(chunkPos, resolution, img, 85, processColorSpace, ref fullResrenderedSurface);
         renderedSurfaceFrame = frameTime.Frame;
     }
 

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

@@ -37,6 +37,7 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
 
     private void RenderContent(SceneObjectRenderContext context, DrawingSurface renderOnto, bool useFilters)
     {
+        AllowHighDpiRendering = true;
         if (!HasOperations())
         {
             if (Background.Value != null)

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

@@ -187,6 +187,7 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
             context.ProcessingColorSpace, context.DesiredSamplingOptions, context.Opacity);
         renderObjectContext.FullRerender = context.FullRerender;
         renderObjectContext.AffectedArea = context.AffectedArea;
+        renderObjectContext.VisibleDocumentRegion = context.VisibleDocumentRegion;
         return renderObjectContext;
     }
 

+ 1 - 0
src/PixiEditor.ChangeableDocument/Rendering/RenderContext.cs

@@ -13,6 +13,7 @@ public class RenderContext
 
     public KeyFrameTime FrameTime { get; }
     public ChunkResolution ChunkResolution { get; set; }
+    public RectI? VisibleDocumentRegion { get; set; } = null;
     public SamplingOptions DesiredSamplingOptions { get; set; } = SamplingOptions.Default;
     public VecI RenderOutputSize { get; set; }
 

+ 1 - 0
src/PixiEditor/Models/Position/ViewportInfo.cs

@@ -13,6 +13,7 @@ internal readonly record struct ViewportInfo(
     VecD Center,
     VecD RealDimensions,
     Matrix3X3 Transform,
+    RectI? VisibleDocumentRegion,
     string RenderOutput,
     SamplingOptions Sampling,
     VecD Dimensions,

+ 19 - 27
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -57,6 +57,7 @@ internal class SceneRenderer : IDisposable
                     viewport.Value.Transform,
                     viewport.Value.Resolution,
                     affectedArea,
+                    viewport.Value.VisibleDocumentRegion,
                     viewport.Value.Sampling,
                     viewport.Value.RenderOutput.Equals("DEFAULT", StringComparison.InvariantCultureIgnoreCase)
                         ? null
@@ -74,6 +75,7 @@ internal class SceneRenderer : IDisposable
     public Texture? RenderScene(Guid viewportId, VecI renderTargetSize, Matrix3X3 targetMatrix,
         ChunkResolution resolution,
         AffectedArea affectedArea,
+        RectI? visibleDocumentRegion,
         SamplingOptions samplingOptions,
         string? targetOutput = null)
     {
@@ -85,42 +87,31 @@ internal class SceneRenderer : IDisposable
 
         IReadOnlyNodeGraph finalGraph = RenderingUtils.SolveFinalNodeGraph(targetOutput, Document);
         bool shouldRerender =
-            ShouldRerender(renderTargetSize, targetMatrix, resolution, viewportId, finalGraph);
+            ShouldRerender(renderTargetSize, targetMatrix, resolution, viewportId, targetOutput, finalGraph);
 
         // TODO: Check if clipping to visible area improves performance on full resolution
         // Meaning zoomed big textures
 
         if (shouldRerender)
         {
-            return RenderGraph(renderTargetSize, targetMatrix, resolution, samplingOptions, affectedArea, targetOutput, finalGraph);
+            return RenderGraph(renderTargetSize, targetMatrix, viewportId, resolution, samplingOptions, affectedArea, visibleDocumentRegion, targetOutput,
+                finalGraph);
         }
         //previewRenderer.RenderPreviews(DocumentViewModel.AnimationHandler.ActiveFrameTime);
 
         var cachedTexture = DocumentViewModel.SceneTextures[viewportId];
-
-        return cachedTexture;
         Matrix3X3 matrixDiff = SolveMatrixDiff(targetMatrix, cachedTexture);
         var target = cachedTexture.DrawingSurface;
-        int saved = target.Canvas.Save();
         target.Canvas.SetMatrix(matrixDiff);
-        if (samplingOptions == SamplingOptions.Default)
-        {
-            target.Canvas.DrawSurface(cachedTexture.DrawingSurface, 0, 0);
-        }
-        else
-        {
-            using var img = cachedTexture.DrawingSurface.Snapshot();
-            target.Canvas.DrawImage(img, 0, 0, samplingOptions);
-        }
-
-        target.Canvas.RestoreToCount(saved);
 
         return cachedTexture;
     }
 
-    private Texture RenderGraph(VecI renderTargetSize, Matrix3X3 targetMatrix, ChunkResolution resolution,
+    private Texture RenderGraph(VecI renderTargetSize, Matrix3X3 targetMatrix, Guid viewportId,
+        ChunkResolution resolution,
         SamplingOptions samplingOptions,
         AffectedArea area,
+        RectI? visibleDocumentRegion,
         string? targetOutput,
         IReadOnlyNodeGraph finalGraph)
     {
@@ -129,7 +120,7 @@ internal class SceneRenderer : IDisposable
         int restoreCanvasTo;
 
         VecI finalSize = SolveRenderOutputSize(targetOutput, finalGraph, Document.Size);
-        if (RenderInOutputSize(finalGraph))
+        if (RenderInOutputSize(finalGraph, renderTargetSize, finalSize))
         {
             finalSize = (VecI)(finalSize * resolution.Multiplier());
 
@@ -144,7 +135,7 @@ internal class SceneRenderer : IDisposable
         }
         else
         {
-            renderTexture = textureCache.RequestTexture(0, renderTargetSize, Document.ProcessingColorSpace);
+            renderTexture = textureCache.RequestTexture(viewportId.GetHashCode(), renderTargetSize, Document.ProcessingColorSpace);
 
             renderTarget = renderTexture.DrawingSurface;
 
@@ -152,16 +143,15 @@ internal class SceneRenderer : IDisposable
             renderTarget.Canvas.Save();*/
 
             /*target.Canvas.SetMatrix(Matrix3X3.Identity);*/
-            renderTarget.Canvas.SetMatrix(targetMatrix);
             renderTarget.Canvas.ClipRect(new RectD(0, 0, finalSize.X, finalSize.Y));
-            resolution = ChunkResolution.Full;
+            renderTarget.Canvas.SetMatrix(targetMatrix);
         }
 
         RenderContext context = new(renderTarget, DocumentViewModel.AnimationHandler.ActiveFrameTime,
             resolution, finalSize, Document.Size, Document.ProcessingColorSpace, samplingOptions);
         context.TargetOutput = targetOutput;
         context.AffectedArea = area;
-
+        context.VisibleDocumentRegion = visibleDocumentRegion;
         finalGraph.Execute(context);
 
         renderTarget.Canvas.Restore();
@@ -209,16 +199,17 @@ internal class SceneRenderer : IDisposable
         return finalSize;
     }
 
-    private bool RenderInOutputSize(IReadOnlyNodeGraph finalGraph)
+    private bool RenderInOutputSize(IReadOnlyNodeGraph finalGraph, VecI renderTargetSize, VecI finalSize)
     {
-        return !HighResRendering || !HighDpiRenderNodePresent(finalGraph);
+        return !HighResRendering || (!HighDpiRenderNodePresent(finalGraph) && renderTargetSize.Length > finalSize.Length);
     }
 
     private bool ShouldRerender(VecI targetSize, Matrix3X3 matrix, ChunkResolution resolution,
-        Guid viewporId,
+        Guid viewportId,
+        string targetOutput,
         IReadOnlyNodeGraph finalGraph)
     {
-        if (!DocumentViewModel.SceneTextures.TryGetValue(viewporId, out var cachedTexture) || cachedTexture == null ||
+        if (!DocumentViewModel.SceneTextures.TryGetValue(viewportId, out var cachedTexture) || cachedTexture == null ||
             cachedTexture.IsDisposed)
         {
             return true;
@@ -236,7 +227,8 @@ internal class SceneRenderer : IDisposable
             return true;
         }
 
-        bool renderInDocumentSize = RenderInOutputSize(finalGraph);
+        VecI finalSize = SolveRenderOutputSize(targetOutput, finalGraph, Document.Size);
+        bool renderInDocumentSize = RenderInOutputSize(finalGraph, targetSize, finalSize);
         VecI compareSize = renderInDocumentSize
             ? (VecI)(Document.Size * resolution.Multiplier())
             : targetSize;

+ 1 - 0
src/PixiEditor/Views/Main/ViewportControls/FixedViewport.axaml.cs

@@ -126,6 +126,7 @@ internal partial class FixedViewport : UserControl, INotifyPropertyChanged
             docSize / 2,
             new VecD(mainImage.Bounds.Width, mainImage.Bounds.Height),
             Matrix3X3.Identity,
+            null,
             "DEFAULT",
             SamplingOptions.Bilinear,
             docSize,

+ 41 - 7
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml.cs

@@ -121,14 +121,17 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     public static readonly StyledProperty<bool> AutoBackgroundScaleProperty = AvaloniaProperty.Register<Viewport, bool>(
         nameof(AutoBackgroundScale), true);
 
-    public static readonly StyledProperty<double> CustomBackgroundScaleXProperty = AvaloniaProperty.Register<Viewport, double>(
-        nameof(CustomBackgroundScaleX));
+    public static readonly StyledProperty<double> CustomBackgroundScaleXProperty =
+        AvaloniaProperty.Register<Viewport, double>(
+            nameof(CustomBackgroundScaleX));
 
-    public static readonly StyledProperty<double> CustomBackgroundScaleYProperty = AvaloniaProperty.Register<Viewport, double>(
-        nameof(CustomBackgroundScaleY));
+    public static readonly StyledProperty<double> CustomBackgroundScaleYProperty =
+        AvaloniaProperty.Register<Viewport, double>(
+            nameof(CustomBackgroundScaleY));
 
-    public static readonly StyledProperty<Bitmap> BackgroundBitmapProperty = AvaloniaProperty.Register<Viewport, Bitmap>(
-        nameof(BackgroundBitmap));
+    public static readonly StyledProperty<Bitmap> BackgroundBitmapProperty =
+        AvaloniaProperty.Register<Viewport, Bitmap>(
+            nameof(BackgroundBitmap));
 
     public Bitmap BackgroundBitmap
     {
@@ -141,6 +144,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         get => GetValue(CustomBackgroundScaleYProperty);
         set => SetValue(CustomBackgroundScaleYProperty, value);
     }
+
     public double CustomBackgroundScaleX
     {
         get => GetValue(CustomBackgroundScaleXProperty);
@@ -384,6 +388,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
     private MouseUpdateController? mouseUpdateController;
     private ViewportOverlays builtInOverlays = new();
+
     public static readonly StyledProperty<int> MaxBilinearSamplingSizeProperty
         = AvaloniaProperty.Register<Viewport, int>("MaxBilinearSamplingSize", 4096);
 
@@ -514,7 +519,10 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
     private ViewportInfo GetLocation()
     {
-        return new(AngleRadians, Center, RealDimensions, Scene.CalculateTransformMatrix().ToSKMatrix().ToMatrix3X3(), ViewportRenderOutput, Scene.CalculateSampling(), Dimensions,CalculateResolution(), GuidValue, Delayed,
+        return new(AngleRadians, Center, RealDimensions,
+            Scene.CalculateTransformMatrix().ToSKMatrix().ToMatrix3X3(),
+            CalculateVisibleRegion(),
+            ViewportRenderOutput, Scene.CalculateSampling(), Dimensions, CalculateResolution(), GuidValue, Delayed,
             ForceRefreshFinalImage);
     }
 
@@ -613,6 +621,32 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         scene.CenterContent(Document.GetRenderOutputSize(ViewportRenderOutput));
     }
 
+    private RectI? CalculateVisibleRegion()
+    {
+        if (Document is null) return null;
+        VecD viewportDimensions = scene.RealDimensions;
+        var transform = scene.CanvasTransform.Value.ToSKMatrix().ToMatrix3X3();
+        var docSize = Document.GetRenderOutputSize(ViewportRenderOutput);
+        if (docSize.X == 0 || docSize.Y == 0) return null;
+
+        VecD topLeft = new(0, 0);
+        VecD topRight = new(viewportDimensions.X, 0);
+        VecD bottomLeft = new(0, viewportDimensions.Y);
+        VecD bottomRight = new(viewportDimensions.X, viewportDimensions.Y);
+        topLeft = transform.Invert().MapPoint(topLeft);
+        topRight = transform.Invert().MapPoint(topRight);
+        bottomLeft = transform.Invert().MapPoint(bottomLeft);
+        bottomRight = transform.Invert().MapPoint(bottomRight);
+
+        double minX = Math.Min(Math.Min(topLeft.X, topRight.X), Math.Min(bottomLeft.X, bottomRight.X));
+        double maxX = Math.Max(Math.Max(topLeft.X, topRight.X), Math.Max(bottomLeft.X, bottomRight.X));
+        double minY = Math.Min(Math.Min(topLeft.Y, topRight.Y), Math.Min(bottomLeft.Y, bottomRight.Y));
+        double maxY = Math.Max(Math.Max(topLeft.Y, topRight.Y), Math.Max(bottomLeft.Y, bottomRight.Y));
+        RectD visibleRect = new(minX, minY, maxX - minX, maxY - minY);
+        visibleRect = visibleRect.Intersect(new RectD(0, 0, docSize.X, docSize.Y));
+        return (RectI)visibleRect.RoundOutwards();
+    }
+
     private static void CenterViewportTriggerChanged(AvaloniaPropertyChangedEventArgs<ExecutionTrigger<VecI>> e)
     {
         Viewport? viewport = (Viewport)e.Sender;