Forráskód Böngészése

Delayed viewports

Equbuxu 3 éve
szülő
commit
711950f4c7

+ 5 - 5
src/PixiEditor.ChangeableDocument.Gen/ChangeActionGenerator.cs

@@ -8,8 +8,8 @@ namespace PixiEditor.ChangeableDocument.Gen
     {
         public void Initialize(IncrementalGeneratorInitializationContext context)
         {
-            // find contrustors with the attribute
-            var contructorSymbolProvider = context.SyntaxProvider.CreateSyntaxProvider(
+            // find constructors with the attribute
+            var constructorSymbolProvider = context.SyntaxProvider.CreateSyntaxProvider(
                 predicate: Helpers.IsConstructorWithAttribute,
                 transform: static (context, cancelToken) =>
                 {
@@ -22,8 +22,8 @@ namespace PixiEditor.ChangeableDocument.Gen
                         ))
                         return null;
 
-                    var contructorSymbol = context.SemanticModel.GetDeclaredSymbol(constructor, cancelToken);
-                    if (contructorSymbol is not IMethodSymbol methodConstructorSymbol ||
+                    var constructorSymbol = context.SemanticModel.GetDeclaredSymbol(constructor, cancelToken);
+                    if (constructorSymbol is not IMethodSymbol methodConstructorSymbol ||
                         methodConstructorSymbol.Kind != SymbolKind.Method)
                         return null;
                     return methodConstructorSymbol;
@@ -31,7 +31,7 @@ namespace PixiEditor.ChangeableDocument.Gen
             ).Where(a => a is not null);
 
             // generate action source code
-            var actionSourceCodeProvider = contructorSymbolProvider.Select(
+            var actionSourceCodeProvider = constructorSymbolProvider.Select(
                 static (constructor, _) =>
                 {
                     var info = Helpers.ExtractMethodInfo(constructor!);

+ 5 - 6
src/PixiEditor.ChangeableDocument/Changes/Drawing/PixelPerfectPen_UpdateableChange.cs

@@ -1,5 +1,4 @@
-using System.ComponentModel.Design;
-using ChunkyImageLib.Operations;
+using ChunkyImageLib.Operations;
 using SkiaSharp;
 
 namespace PixiEditor.ChangeableDocument.Changes.Drawing;
@@ -65,9 +64,9 @@ internal class PixelPerfectPen_UpdateableChange : UpdateableChange
         }
 
         
-        confirmedPixels!.UnionWith(pixelsToConfirm2!);
+        confirmedPixels.UnionWith(pixelsToConfirm2);
         (pixelsToConfirm2, pixelsToConfirm) = (pixelsToConfirm, pixelsToConfirm2);
-        pixelsToConfirm!.Clear();
+        pixelsToConfirm.Clear();
         
         SKPoint[] line = BresenhamLineHelper.GetBresenhamLine(incomingPoints[pointsCount - 2], incomingPoints[pointsCount - 1]);
         foreach (VecI pixel in line)
@@ -106,8 +105,8 @@ internal class PixelPerfectPen_UpdateableChange : UpdateableChange
         if (firstApply)
         {
             incomingPoints = null;
-            confirmedPixels!.UnionWith(pixelsToConfirm!);
-            confirmedPixels.UnionWith(pixelsToConfirm2!);
+            confirmedPixels.UnionWith(pixelsToConfirm);
+            confirmedPixels.UnionWith(pixelsToConfirm2);
         }
         else
         {

+ 4 - 4
src/PixiEditorPrototype/Models/ActionAccumulator.cs

@@ -74,12 +74,12 @@ internal class ActionAccumulator
             {
                 bitmap.Lock();
             }
-            bool refreshPreviews = toExecute.Any(static action => action is ChangeBoundary_Action or Redo_Action or Undo_Action);
-            if (refreshPreviews)
+            bool refreshDelayed = toExecute.Any(static action => action is ChangeBoundary_Action or Redo_Action or Undo_Action);
+            if (refreshDelayed)
                 LockPreviewBitmaps(document.StructureRoot);
 
             // update bitmaps
-            var renderResult = await renderer.UpdateGatheredChunks(affectedChunks, refreshPreviews);
+            var renderResult = await renderer.UpdateGatheredChunks(affectedChunks, refreshDelayed);
             AddDirtyRects(renderResult);
 
             // unlock bitmaps
@@ -87,7 +87,7 @@ internal class ActionAccumulator
             {
                 bitmap.Unlock();
             }
-            if (refreshPreviews)
+            if (refreshDelayed)
                 UnlockPreviewBitmaps(document.StructureRoot);
 
             // force refresh viewports for better responsiveness

+ 1 - 1
src/PixiEditorPrototype/Models/DocumentState.cs

@@ -5,5 +5,5 @@ namespace PixiEditorPrototype.Models;
 
 internal class DocumentState
 {
-    public Dictionary<Guid, ViewportLocation> Viewports { get; set; } = new();
+    public Dictionary<Guid, ViewportInfo> Viewports { get; set; } = new();
 }

+ 1 - 1
src/PixiEditorPrototype/Models/DocumentUpdater.cs

@@ -149,7 +149,7 @@ internal class DocumentUpdater
 
     private void ProcessRefreshViewport(RefreshViewport_PassthroughAction info)
     {
-        helper.State.Viewports[info.Location.GuidValue] = info.Location;
+        helper.State.Viewports[info.Info.GuidValue] = info.Info;
     }
 
     private void ProcessRemoveViewport(RemoveViewport_PassthroughAction info)

+ 1 - 1
src/PixiEditorPrototype/Models/RefreshViewport_PassthroughAction.cs

@@ -3,4 +3,4 @@ using PixiEditor.ChangeableDocument.ChangeInfos;
 
 namespace PixiEditorPrototype.Models;
 
-internal record class RefreshViewport_PassthroughAction(ViewportLocation Location) : IAction, IChangeInfo;
+internal record class RefreshViewport_PassthroughAction(ViewportInfo Info) : IAction, IChangeInfo;

+ 72 - 18
src/PixiEditorPrototype/Models/Rendering/WriteableBitmapUpdater.cs

@@ -30,8 +30,24 @@ internal class WriteableBitmapUpdater
         [ChunkResolution.Eighth] = new()
     };
 
-    private Dictionary<Guid, HashSet<VecI>> previewPostponedChunks = new();
-    private Dictionary<Guid, HashSet<VecI>> maskPostponedChunks = new();
+    private readonly Dictionary<ChunkResolution, HashSet<VecI>> globalPostponedForDelayed = new()
+    {
+        [ChunkResolution.Full] = new(),
+        [ChunkResolution.Half] = new(),
+        [ChunkResolution.Quarter] = new(),
+        [ChunkResolution.Eighth] = new()
+    };
+    
+    private readonly Dictionary<ChunkResolution, HashSet<VecI>> globalDelayedChunks = new()
+    {
+        [ChunkResolution.Full] = new(),
+        [ChunkResolution.Half] = new(),
+        [ChunkResolution.Quarter] = new(),
+        [ChunkResolution.Eighth] = new()
+    };
+    
+    private Dictionary<Guid, HashSet<VecI>> previewDelayedChunks = new();
+    private Dictionary<Guid, HashSet<VecI>> maskPreviewDelayedChunks = new();
 
     public WriteableBitmapUpdater(DocumentViewModel doc, DocumentHelpers helpers)
     {
@@ -39,19 +55,30 @@ internal class WriteableBitmapUpdater
         this.helpers = helpers;
     }
 
-    public async Task<List<IRenderInfo>> UpdateGatheredChunks(AffectedChunkGatherer chunkGatherer, bool updatePreviews)
+    public async Task<List<IRenderInfo>> UpdateGatheredChunks
+        (AffectedChunkGatherer chunkGatherer, bool updateDelayed)
     {
-        return await Task.Run(() => Render(chunkGatherer, updatePreviews)).ConfigureAwait(true);
+        return await Task.Run(() => Render(chunkGatherer, updateDelayed)).ConfigureAwait(true);
     }
 
-    private Dictionary<ChunkResolution, HashSet<VecI>> FindGlobalChunksToRerender(AffectedChunkGatherer chunkGatherer)
+    private Dictionary<ChunkResolution, HashSet<VecI>> FindGlobalChunksToRerender(AffectedChunkGatherer chunkGatherer, bool renderDelayed)
     {
+        // add all affected chunks to postponed
         foreach (var (_, postponed) in globalPostponedChunks)
         {
             postponed.UnionWith(chunkGatherer.mainImageChunks);
         }
 
-        var chunksOnScreen = new Dictionary<ChunkResolution, HashSet<VecI>>()
+        // find all chunks that are on viewports and on delayed viewports
+        var chunksToUpdate = new Dictionary<ChunkResolution, HashSet<VecI>>()
+        {
+            [ChunkResolution.Full] = new(),
+            [ChunkResolution.Half] = new(),
+            [ChunkResolution.Quarter] = new(),
+            [ChunkResolution.Eighth] = new()
+        };
+        
+        var chunksOnDelayedViewports = new Dictionary<ChunkResolution, HashSet<VecI>>()
         {
             [ChunkResolution.Full] = new(),
             [ChunkResolution.Half] = new(),
@@ -66,16 +93,43 @@ internal class WriteableBitmapUpdater
                 viewport.Dimensions,
                 -viewport.Angle,
                 ChunkResolution.Full.PixelSize());
-            chunksOnScreen[viewport.Resolution].UnionWith(viewportChunks);
+            if (viewport.Delayed)
+                chunksOnDelayedViewports[viewport.Resolution].UnionWith(viewportChunks);
+            else
+                chunksToUpdate[viewport.Resolution].UnionWith(viewportChunks);
         }
 
+        // exclude the chunks that don't need to be updated, remove chunks that will be updated from postponed
         foreach (var (res, postponed) in globalPostponedChunks)
         {
-            chunksOnScreen[res].IntersectWith(postponed);
-            postponed.ExceptWith(chunksOnScreen[res]);
+            chunksToUpdate[res].IntersectWith(postponed);
+            chunksOnDelayedViewports[res].IntersectWith(postponed);
+            postponed.ExceptWith(chunksToUpdate[res]);
+        }
+        
+        // decide what to do about the delayed chunks
+        if (renderDelayed)
+        {
+            foreach (var (res, postponed) in globalPostponedChunks)
+            {
+                chunksToUpdate[res].UnionWith(chunksOnDelayedViewports[res]);
+                postponed.ExceptWith(chunksOnDelayedViewports[res]);
+                globalPostponedForDelayed[res] = new HashSet<VecI>(postponed);
+            }
+        }
+        else
+        {
+            foreach (var (res, postponed) in globalPostponedChunks)
+            {
+                chunksOnDelayedViewports[res].IntersectWith(globalPostponedForDelayed[res]);
+                globalPostponedForDelayed[res].ExceptWith(chunksOnDelayedViewports[res]);
+                
+                chunksToUpdate[res].UnionWith(chunksOnDelayedViewports[res]);
+                postponed.ExceptWith(chunksOnDelayedViewports[res]);
+            }
         }
 
-        return chunksOnScreen;
+        return chunksToUpdate;
     }
 
 
@@ -91,24 +145,24 @@ internal class WriteableBitmapUpdater
     private (Dictionary<Guid, HashSet<VecI>> image, Dictionary<Guid, HashSet<VecI>> mask) FindPreviewChunksToRerender
         (AffectedChunkGatherer chunkGatherer, bool postpone)
     {
-        AddChunks(chunkGatherer.imagePreviewChunks, previewPostponedChunks);
-        AddChunks(chunkGatherer.maskPreviewChunks, maskPostponedChunks);
+        AddChunks(chunkGatherer.imagePreviewChunks, previewDelayedChunks);
+        AddChunks(chunkGatherer.maskPreviewChunks, maskPreviewDelayedChunks);
         if (postpone)
             return (new(), new());
-        var result = (previewPostponedChunks, maskPostponedChunks);
-        previewPostponedChunks = new();
-        maskPostponedChunks = new();
+        var result = (previewPostponedChunks: previewDelayedChunks, maskPostponedChunks: maskPreviewDelayedChunks);
+        previewDelayedChunks = new();
+        maskPreviewDelayedChunks = new();
         return result;
     }
 
-    private List<IRenderInfo> Render(AffectedChunkGatherer chunkGatherer, bool updatePreviews)
+    private List<IRenderInfo> Render(AffectedChunkGatherer chunkGatherer, bool updateDelayed)
     {
-        Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender = FindGlobalChunksToRerender(chunkGatherer);
+        Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender = FindGlobalChunksToRerender(chunkGatherer, updateDelayed);
 
         List<IRenderInfo> infos = new();
         UpdateMainImage(chunksToRerender, infos);
 
-        var (imagePreviewChunksToRerender, maskPreviewChunksToRerender) = FindPreviewChunksToRerender(chunkGatherer, !updatePreviews);
+        var (imagePreviewChunksToRerender, maskPreviewChunksToRerender) = FindPreviewChunksToRerender(chunkGatherer, !updateDelayed);
         var previewSize = StructureMemberViewModel.CalculatePreviewSize(helpers.Tracker.Document.Size);
         float scaling = (float)previewSize.X / doc.SizeBindable.X;
         UpdateImagePreviews(imagePreviewChunksToRerender, scaling, infos);

+ 13 - 0
src/PixiEditorPrototype/Models/ViewportInfo.cs

@@ -0,0 +1,13 @@
+using System;
+using ChunkyImageLib.DataHolders;
+
+namespace PixiEditorPrototype.Models;
+internal readonly record struct ViewportInfo(
+    double Angle,
+    VecD Center,
+    VecD RealDimensions,
+    VecD Dimensions,
+    ChunkResolution Resolution,
+    Guid GuidValue,
+    bool Delayed,
+    Action InvalidateVisual);

+ 0 - 6
src/PixiEditorPrototype/Models/ViewportLocation.cs

@@ -1,6 +0,0 @@
-using System;
-using ChunkyImageLib.DataHolders;
-
-namespace PixiEditorPrototype.Models;
-internal readonly record struct ViewportLocation
-    (double Angle, VecD Center, VecD RealDimensions, VecD Dimensions, ChunkResolution Resolution, Guid GuidValue, Action InvalidateVisual);

+ 11 - 2
src/PixiEditorPrototype/UserControls/Viewport/Viewport.xaml.cs

@@ -41,6 +41,15 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     private static readonly DependencyProperty BitmapsProperty =
         DependencyProperty.Register(nameof(Bitmaps), typeof(Dictionary<ChunkResolution, WriteableBitmap>), typeof(Viewport), new(null, OnBitmapsChange));
 
+    public static readonly DependencyProperty DelayedProperty = DependencyProperty.Register(
+        nameof(Delayed), typeof(bool), typeof(Viewport), new PropertyMetadata(false));
+
+    public bool Delayed
+    {
+        get => (bool)GetValue(DelayedProperty);
+        set => SetValue(DelayedProperty, value);
+    }
+    
     public Dictionary<ChunkResolution, WriteableBitmap>? Bitmaps
     {
         get => (Dictionary<ChunkResolution, WriteableBitmap>?)GetValue(BitmapsProperty);
@@ -218,8 +227,8 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         return ChunkResolution.Full;
     }
 
-    private ViewportLocation GetLocation()
+    private ViewportInfo GetLocation()
     {
-        return new(Angle, Center, RealDimensions / 2, Dimensions / 2, CalculateResolution(), GuidValue, ForceRefreshFinalImage);
+        return new(Angle, Center, RealDimensions / 2, Dimensions / 2, CalculateResolution(), GuidValue, Delayed, ForceRefreshFinalImage);
     }
 }

+ 2 - 2
src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs

@@ -588,9 +588,9 @@ internal class DocumentViewModel : INotifyPropertyChanged
         Helpers.ActionAccumulator.AddFinishedActions(new FloodFill_Action(member.GuidValue, pos, color, member.ShouldDrawOnMask));
     }
 
-    public void AddOrUpdateViewport(ViewportLocation location)
+    public void AddOrUpdateViewport(ViewportInfo info)
     {
-        Helpers.ActionAccumulator.AddActions(new RefreshViewport_PassthroughAction(location));
+        Helpers.ActionAccumulator.AddActions(new RefreshViewport_PassthroughAction(info));
     }
 
     public void RemoveViewport(Guid viewportGuid)

+ 1 - 0
src/PixiEditorPrototype/Views/MainWindow.xaml

@@ -669,6 +669,7 @@
                             BorderBrush="Black"
                             Margin="5">
                             <vp:Viewport
+                                Delayed="True"
                                 Document="{Binding}"
                                 FlipX="{Binding ElementName=flipXCheckbox, Path=IsChecked}"
                                 FlipY="{Binding ElementName=flipYCheckbox, Path=IsChecked}"