|
@@ -5,6 +5,8 @@ using PixiEditor.ChangeableDocument.Changeables.Interfaces;
|
|
using PixiEditor.ChangeableDocument.Rendering;
|
|
using PixiEditor.ChangeableDocument.Rendering;
|
|
using Drawie.Backend.Core.Surfaces;
|
|
using Drawie.Backend.Core.Surfaces;
|
|
using Drawie.Backend.Core.Surfaces.ImageData;
|
|
using Drawie.Backend.Core.Surfaces.ImageData;
|
|
|
|
+using Drawie.Numerics;
|
|
|
|
+using PixiEditor.ChangeableDocument.Changeables.Animations;
|
|
using PixiEditor.ChangeableDocument.Changeables.Graph;
|
|
using PixiEditor.ChangeableDocument.Changeables.Graph;
|
|
using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
|
|
using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
|
|
using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
|
|
using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
|
|
@@ -12,12 +14,17 @@ using PixiEditor.Models.Handlers;
|
|
|
|
|
|
namespace PixiEditor.Models.Rendering;
|
|
namespace PixiEditor.Models.Rendering;
|
|
|
|
|
|
-internal class SceneRenderer
|
|
|
|
|
|
+internal class SceneRenderer
|
|
{
|
|
{
|
|
|
|
+ public const double ZoomDiffToRerender = 20;
|
|
public IReadOnlyDocument Document { get; }
|
|
public IReadOnlyDocument Document { get; }
|
|
public IDocument DocumentViewModel { get; }
|
|
public IDocument DocumentViewModel { get; }
|
|
public bool HighResRendering { get; set; } = true;
|
|
public bool HighResRendering { get; set; } = true;
|
|
|
|
|
|
|
|
+ private Dictionary<string, Texture> cachedTextures = new();
|
|
|
|
+ private bool lastHighResRendering = true;
|
|
|
|
+ private int lastGraphCacheHash = -1;
|
|
|
|
+ private KeyFrameTime lastFrameTime;
|
|
|
|
|
|
public SceneRenderer(IReadOnlyDocument trackerDocument, IDocument documentViewModel)
|
|
public SceneRenderer(IReadOnlyDocument trackerDocument, IDocument documentViewModel)
|
|
{
|
|
{
|
|
@@ -29,16 +36,39 @@ internal class SceneRenderer
|
|
{
|
|
{
|
|
if (Document.Renderer.IsBusy || DocumentViewModel.Busy) return;
|
|
if (Document.Renderer.IsBusy || DocumentViewModel.Busy) return;
|
|
RenderOnionSkin(target, resolution, targetOutput);
|
|
RenderOnionSkin(target, resolution, targetOutput);
|
|
- RenderGraph(target, resolution, targetOutput);
|
|
|
|
|
|
+
|
|
|
|
+ string adjustedTargetOutput = targetOutput ?? "";
|
|
|
|
+
|
|
|
|
+ IReadOnlyNodeGraph finalGraph = SolveFinalNodeGraph(targetOutput);
|
|
|
|
+ bool shouldRerender = ShouldRerender(target, resolution, adjustedTargetOutput, finalGraph);
|
|
|
|
+ if (shouldRerender)
|
|
|
|
+ {
|
|
|
|
+ if (cachedTextures.ContainsKey(adjustedTargetOutput))
|
|
|
|
+ {
|
|
|
|
+ cachedTextures[adjustedTargetOutput]?.Dispose();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var rendered = RenderGraph(target, resolution, targetOutput, finalGraph);
|
|
|
|
+ cachedTextures[adjustedTargetOutput] = rendered;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ var cachedTexture = cachedTextures[adjustedTargetOutput];
|
|
|
|
+ Matrix3X3 matrixDiff = SolveMatrixDiff(target, cachedTexture);
|
|
|
|
+ int saved = target.Canvas.Save();
|
|
|
|
+ target.Canvas.SetMatrix(matrixDiff);
|
|
|
|
+ target.Canvas.DrawSurface(cachedTexture.DrawingSurface, 0, 0);
|
|
|
|
+ target.Canvas.RestoreToCount(saved);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- private void RenderGraph(DrawingSurface target, ChunkResolution resolution, string? targetOutput)
|
|
|
|
|
|
+ private Texture RenderGraph(DrawingSurface target, ChunkResolution resolution, string? targetOutput, IReadOnlyNodeGraph finalGraph)
|
|
{
|
|
{
|
|
DrawingSurface renderTarget = target;
|
|
DrawingSurface renderTarget = target;
|
|
Texture? renderTexture = null;
|
|
Texture? renderTexture = null;
|
|
bool restoreCanvas = false;
|
|
bool restoreCanvas = false;
|
|
|
|
|
|
- if (!HighResRendering || !HighDpiRenderNodePresent(Document.NodeGraph))
|
|
|
|
|
|
+ if (RenderInDocumentSize())
|
|
{
|
|
{
|
|
renderTexture = Texture.ForProcessing(Document.Size, Document.ProcessingColorSpace);
|
|
renderTexture = Texture.ForProcessing(Document.Size, Document.ProcessingColorSpace);
|
|
renderTarget = renderTexture.DrawingSurface;
|
|
renderTarget = renderTexture.DrawingSurface;
|
|
@@ -47,10 +77,10 @@ internal class SceneRenderer
|
|
{
|
|
{
|
|
renderTexture = Texture.ForProcessing(renderTarget.DeviceClipBounds.Size, Document.ProcessingColorSpace);
|
|
renderTexture = Texture.ForProcessing(renderTarget.DeviceClipBounds.Size, Document.ProcessingColorSpace);
|
|
renderTarget = renderTexture.DrawingSurface;
|
|
renderTarget = renderTexture.DrawingSurface;
|
|
-
|
|
|
|
|
|
+
|
|
target.Canvas.Save();
|
|
target.Canvas.Save();
|
|
renderTarget.Canvas.Save();
|
|
renderTarget.Canvas.Save();
|
|
-
|
|
|
|
|
|
+
|
|
renderTarget.Canvas.SetMatrix(target.Canvas.TotalMatrix);
|
|
renderTarget.Canvas.SetMatrix(target.Canvas.TotalMatrix);
|
|
target.Canvas.SetMatrix(Matrix3X3.Identity);
|
|
target.Canvas.SetMatrix(Matrix3X3.Identity);
|
|
restoreCanvas = true;
|
|
restoreCanvas = true;
|
|
@@ -59,18 +89,81 @@ internal class SceneRenderer
|
|
RenderContext context = new(renderTarget, DocumentViewModel.AnimationHandler.ActiveFrameTime,
|
|
RenderContext context = new(renderTarget, DocumentViewModel.AnimationHandler.ActiveFrameTime,
|
|
resolution, Document.Size, Document.ProcessingColorSpace);
|
|
resolution, Document.Size, Document.ProcessingColorSpace);
|
|
context.TargetOutput = targetOutput;
|
|
context.TargetOutput = targetOutput;
|
|
- SolveFinalNodeGraph(context.TargetOutput).Execute(context);
|
|
|
|
|
|
+ finalGraph.Execute(context);
|
|
|
|
|
|
if (renderTexture != null)
|
|
if (renderTexture != null)
|
|
{
|
|
{
|
|
target.Canvas.DrawSurface(renderTexture.DrawingSurface, 0, 0);
|
|
target.Canvas.DrawSurface(renderTexture.DrawingSurface, 0, 0);
|
|
- renderTexture.Dispose();
|
|
|
|
|
|
|
|
if (restoreCanvas)
|
|
if (restoreCanvas)
|
|
{
|
|
{
|
|
target.Canvas.Restore();
|
|
target.Canvas.Restore();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ return renderTexture;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private bool RenderInDocumentSize()
|
|
|
|
+ {
|
|
|
|
+ return !HighResRendering || !HighDpiRenderNodePresent(Document.NodeGraph);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private bool ShouldRerender(DrawingSurface target, ChunkResolution resolution, string? targetOutput,
|
|
|
|
+ IReadOnlyNodeGraph finalGraph)
|
|
|
|
+ {
|
|
|
|
+ if (!cachedTextures.TryGetValue(targetOutput ?? "", out var cachedTexture) || cachedTexture == null ||
|
|
|
|
+ cachedTexture.IsDisposed)
|
|
|
|
+ {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (lastHighResRendering != HighResRendering)
|
|
|
|
+ {
|
|
|
|
+ lastHighResRendering = HighResRendering;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bool renderInDocumentSize = RenderInDocumentSize();
|
|
|
|
+ VecI compareSize = renderInDocumentSize ? Document.Size : target.DeviceClipBounds.Size;
|
|
|
|
+
|
|
|
|
+ if (cachedTexture.DrawingSurface.DeviceClipBounds.Size != compareSize)
|
|
|
|
+ {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(lastFrameTime.Frame != DocumentViewModel.AnimationHandler.ActiveFrameTime.Frame)
|
|
|
|
+ {
|
|
|
|
+ lastFrameTime = DocumentViewModel.AnimationHandler.ActiveFrameTime;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!renderInDocumentSize)
|
|
|
|
+ {
|
|
|
|
+ double lengthDiff = target.LocalClipBounds.Size.Length - cachedTexture.DrawingSurface.LocalClipBounds.Size.Length;
|
|
|
|
+ if (lengthDiff > 0 || target.LocalClipBounds.Pos != cachedTexture.DrawingSurface.LocalClipBounds.Pos || lengthDiff < -ZoomDiffToRerender)
|
|
|
|
+ {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int currentGraphCacheHash = finalGraph.GetCacheHash();
|
|
|
|
+ if (lastGraphCacheHash != currentGraphCacheHash)
|
|
|
|
+ {
|
|
|
|
+ lastGraphCacheHash = currentGraphCacheHash;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Matrix3X3 SolveMatrixDiff(DrawingSurface target, Texture cachedTexture)
|
|
|
|
+ {
|
|
|
|
+ Matrix3X3 old = cachedTexture.DrawingSurface.Canvas.TotalMatrix;
|
|
|
|
+ Matrix3X3 current = target.Canvas.TotalMatrix;
|
|
|
|
+
|
|
|
|
+ Matrix3X3 solveMatrixDiff = current.Concat(old.Invert());
|
|
|
|
+ return solveMatrixDiff;
|
|
}
|
|
}
|
|
|
|
|
|
private IReadOnlyNodeGraph SolveFinalNodeGraph(string? targetOutput)
|
|
private IReadOnlyNodeGraph SolveFinalNodeGraph(string? targetOutput)
|