Skip to content

Commit

Permalink
Super.OffscreenRenderingAtCanvasLevel
Browse files Browse the repository at this point in the history
Reverted background rendering to faster previous
  • Loading branch information
taublast committed Dec 6, 2024
1 parent 3abcd21 commit b7a45fc
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 51 deletions.
67 changes: 65 additions & 2 deletions src/Engine/Draw/Base/SkiaControl.Cache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -469,12 +469,75 @@ public Action GetOffscreenRenderingAction()

public void PushToOffscreenRendering(Action action)
{
_offscreenCacheRenderingQueue.Push(action);
Superview?.PushToOffscreenRendering(this);
if (Super.OffscreenRenderingAtCanvasLevel)
{
_offscreenCacheRenderingQueue.Push(action);
Superview?.PushToOffscreenRendering(this);
}
else
{
_offscreenCacheRenderingQueue.Push(action);
if (!_processingOffscrenRendering)
{
_processingOffscrenRendering = true;
Task.Run(async () =>
{
await ProcessOffscreenCacheRenderingAsync();
}).ConfigureAwait(false);
}
}
}



private bool _processingOffscrenRendering = false;
protected SemaphoreSlim semaphoreOffsecreenProcess = new(1);

public async Task ProcessOffscreenCacheRenderingAsync()
{

await semaphoreOffsecreenProcess.WaitAsync();

if (_offscreenCacheRenderingQueue.Count == 0)
return;

_processingOffscrenRendering = true;

try
{
Action action = _offscreenCacheRenderingQueue.Pop();
while (!IsDisposed && !IsDisposing && action != null)
{
try
{
action.Invoke();

if (_offscreenCacheRenderingQueue.Count > 0)
action = _offscreenCacheRenderingQueue.Pop();
else
break;
}
catch (Exception e)
{
Super.Log(e);
}
}

//if (NeedUpdate || RenderObjectNeedsUpdate) //someone changed us while rendering inner content
//{
// Update(); //kick
//}

}
finally
{
_processingOffscrenRendering = false;
semaphoreOffsecreenProcess.Release();
}

}



/// <summary>
/// Used by the UseCacheDoubleBuffering process. This is the new cache beign created in background. It will be copied to RenderObject when ready.
Expand Down
15 changes: 11 additions & 4 deletions src/Engine/Super.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,21 @@ public partial class Super
{
static Super()
{
//Tasks.StartDelayed(TimeSpan.FromSeconds(1), () =>
//{
// ProcessBackgroundQueue().ConfigureAwait(false);
//});

}

/// <summary>
/// Experimental for dev use. Set OffscreenRenderingAtCanvasLevel to true when this is true.
/// Default is False
/// </summary>
public static bool Multithreaded = false;

/// <summary>
/// If set to True will process all ofscreen rendering in one background thread at canvas level, otherwise every control will launch its own background processing thread.
/// Default is False
/// </summary>
public static bool OffscreenRenderingAtCanvasLevel { get; set; }

#if (!ONPLATFORM)

protected static void SetupFrameLooper()
Expand Down
86 changes: 52 additions & 34 deletions src/Engine/Views/DrawnView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1774,14 +1774,28 @@ protected void CommitInvalidations()
invalidations.Clear();
}

#region BACKGROUND RENDERING





#region BACKGROUND RENDERING

protected object LockStartOffscreenQueue = new();
private bool _processingOffscrenRendering = false;

// Holds the incoming commands without blocking
private readonly ConcurrentQueue<OffscreenCommand> _incomingCommands = new ConcurrentQueue<OffscreenCommand>();

// Holds the latest commands for each control (only processed by background thread)
private readonly Dictionary<SkiaControl, OffscreenCommand> _offscreenCommands = new();

protected SemaphoreSlim semaphoreOffscreenProcess = new(1);

public record OffscreenCommand(SkiaControl Control);

/// <summary>
/// Make sure offscreen rendering queue is running
/// Ensures offscreen rendering queue is running
/// </summary>
public void KickOffscreenCacheRendering()
{
Expand All @@ -1790,87 +1804,91 @@ public void KickOffscreenCacheRendering()
if (!_processingOffscrenRendering)
{
_processingOffscrenRendering = true;
Task.Run(async () => //100% background thread
Task.Run(async () =>
{
await ProcessOffscreenCacheRenderingAsync();

}).ConfigureAwait(false);
}
}
}

/// <summary>
/// Push an offscreen rendering command without blocking the UI thread.
/// </summary>
public void PushToOffscreenRendering(SkiaControl control)
{
_offscreenCacheRenderingQueue.Enqueue(new OffscreenCommand(control));
_incomingCommands.Enqueue(new OffscreenCommand(control));
KickOffscreenCacheRendering();
}

public record OffscreenCommand(SkiaControl Control);

protected SemaphoreSlim semaphoreOffscreenProcess = new(1);

private readonly Queue<OffscreenCommand> _offscreenCacheRenderingQueue = new();

public async Task ProcessOffscreenCacheRenderingAsync()
{

await semaphoreOffscreenProcess.WaitAsync();

try
{
if (_offscreenCacheRenderingQueue.Count == 0)
// Drain the ConcurrentQueue into a local list
var drainedCommands = new List<OffscreenCommand>();
while (_incomingCommands.TryDequeue(out var cmd))
{
drainedCommands.Add(cmd);
}

// If nothing was drained, we can safely return
if (drainedCommands.Count == 0)
return;

lock (_offscreenCommands)
{
foreach (var command in drainedCommands)
{
_offscreenCommands[command.Control] = command;
}
}

// Process the latest commands now
// Reading dictionary under lock might not be strictly necessary if we trust only this background thread modifies it
// but we can snapshot under lock for safety.
OffscreenCommand[] toProcess;
lock (_offscreenCommands)
{
toProcess = _offscreenCommands.Values.ToArray();
_offscreenCommands.Clear(); // We clear after processing to avoid memory growth
}

_processingOffscrenRendering = true;

var command = _offscreenCacheRenderingQueue.Dequeue();
while (command != null)
foreach (var command in toProcess)
{
try
{
if (command.Control.IsDisposed || command.Control.IsDisposing)
{
_offscreenCacheRenderingQueue.Clear();
break;
// If control is no longer valid, skip it.
continue;
}

var action = command.Control.GetOffscreenRenderingAction();
action?.Invoke();

//command.Control.Repaint();

if (_offscreenCacheRenderingQueue.Count > 0)
command = _offscreenCacheRenderingQueue.Dequeue();
else
break;

}
catch (Exception e)
{
Super.Log(e);
}
}

//if (NeedUpdate || RenderObjectNeedsUpdate) //someone changed us while rendering inner content
//{
// Update(); //kick
//}
//Update(); //kick



}
finally
{
_processingOffscrenRendering = false;
semaphoreOffscreenProcess.Release();
}

}


#endregion


protected virtual void Draw(SkiaDrawingContext context, SKRect destination, float scale)
{
++renderedFrames;
Expand Down
4 changes: 3 additions & 1 deletion src/samples/Sandbox/Resources/Styles.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:draw="http://schemas.appomobi.com/drawnUi/2023/draw">

<Style TargetType="draw:SkiaLabel" ApplyToDerivedTypes="True">
<Style
ApplyToDerivedTypes="True"
TargetType="draw:SkiaLabel">
<Setter Property="TextColor" Value="Black" />
<Setter Property="FontSize" Value="14" />
</Style>
Expand Down
16 changes: 8 additions & 8 deletions src/samples/Sandbox/View1.xaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<draw:SkiaLayout
<?xml version="1.0" encoding="UTF-8" ?>
<draw:SkiaLayout
x:Class="Sandbox.View1"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:draw="http://schemas.appomobi.com/drawnUi/2023/draw"
x:Class="Sandbox.View1">

<!--bugged on propose to text DisplayException-->
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:draw="http://schemas.appomobi.com/drawnUi/2023/draw">

<draw:SkiaLabel Text="{x:StaticResource Unexisting}"/>
<!-- bugged on propose to text DisplayException -->

<draw:SkiaLabel Text="{x:StaticResource Unexisting}" />

</draw:SkiaLayout>
4 changes: 2 additions & 2 deletions src/samples/Sandbox/Views/MainPageDynamicHeightCells.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@
-->

<draw:SkiaLayout
VirtualisationInflated="350"
x:Name="StackCells"
BackgroundColor="White"
HorizontalOptions="Fill"
ItemSizingStrategy="MeasureAllItems"
ItemsSource="{Binding Items}"
RecyclingTemplate="Enabled"
Type="Column">
Type="Column"
VirtualisationInflated="350">

<draw:SkiaLayout.ItemTemplate>
<DataTemplate x:DataType="demo:ChatMessage">
Expand Down

0 comments on commit b7a45fc

Please sign in to comment.