Skip to content

Commit

Permalink
Restore 0.10.x behavior for re-creating visual brush content on prope…
Browse files Browse the repository at this point in the history
…rty change (#15838)
  • Loading branch information
kekekeks committed May 31, 2024
1 parent 0063a5e commit 9be975b
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 41 deletions.
2 changes: 2 additions & 0 deletions src/Avalonia.Base/Media/Brush.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
private protected void RegisterForSerialization() =>
_resource.RegisterForInvalidationOnAllCompositors(this);

private protected bool IsOnCompositor(Compositor c) => _resource.TryGetForCompositor(c) != null;

private CompositorResourceHolder<ServerCompositionSimpleBrush> _resource;

IBrush ICompositionRenderResource<IBrush>.GetForCompositor(Compositor c) => _resource.GetForCompositor(c);
Expand Down
18 changes: 6 additions & 12 deletions src/Avalonia.Base/Media/DrawingBrush.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public Drawing? Drawing
internal override Func<Compositor, ServerCompositionSimpleBrush> Factory =>
static c => new ServerCompositionSimpleContentBrush(c.Server);

private InlineDictionary<Compositor, CompositionRenderDataSceneBrushContent?> _renderDataDictionary;
private InlineDictionary<Compositor, CompositionRenderData?> _renderDataDictionary;

private protected override void OnReferencedFromCompositor(Compositor c)
{
Expand All @@ -69,33 +69,27 @@ private protected override void OnReferencedFromCompositor(Compositor c)
protected override void OnUnreferencedFromCompositor(Compositor c)
{
if (_renderDataDictionary.TryGetAndRemoveValue(c, out var content))
content?.RenderData.Dispose();
content?.Dispose();
base.OnUnreferencedFromCompositor(c);
}

private protected override void SerializeChanges(Compositor c, BatchStreamWriter writer)
{
base.SerializeChanges(c, writer);
if (_renderDataDictionary.TryGetValue(c, out var content))
writer.WriteObject(content);
if (_renderDataDictionary.TryGetValue(c, out var content) && content != null)
writer.WriteObject(new CompositionRenderDataSceneBrushContent.Properties(content.Server, null, true));
else
writer.WriteObject(null);
}

CompositionRenderDataSceneBrushContent? CreateServerContent(Compositor c)
CompositionRenderData? CreateServerContent(Compositor c)
{
if (Drawing == null)
return null;

using var recorder = new RenderDataDrawingContext(c);
Drawing?.Draw(recorder);
var renderData = recorder.GetRenderResults();
if (renderData == null)
return null;

return new CompositionRenderDataSceneBrushContent(
(ServerCompositionSimpleContentBrush)((ICompositionRenderResource<IBrush>)this).GetForCompositor(c),
renderData, null, true);
return recorder.GetRenderResults();
}
}
}
65 changes: 49 additions & 16 deletions src/Avalonia.Base/Media/VisualBrush.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Avalonia.Collections.Pooled;
using Avalonia.Media.Immutable;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
Expand Down Expand Up @@ -62,31 +63,65 @@ public Visual? Visual
internal override Func<Compositor, ServerCompositionSimpleBrush> Factory =>
static c => new ServerCompositionSimpleContentBrush(c.Server);

private InlineDictionary<Compositor, CompositionRenderDataSceneBrushContent?> _renderDataDictionary;

private protected override void OnReferencedFromCompositor(Compositor c)
class RenderDataItem(CompositionRenderData data, Rect rect) : IDisposable
{
_renderDataDictionary.Add(c, CreateServerContent(c));
base.OnReferencedFromCompositor(c);
public CompositionRenderData Data { get; } = data;
public Rect Rect { get; } = rect;
public bool IsDirty;
public void Dispose() => Data?.Dispose();
}


private InlineDictionary<Compositor, RenderDataItem?> _renderDataDictionary;

protected override void OnUnreferencedFromCompositor(Compositor c)
{
if (_renderDataDictionary.TryGetAndRemoveValue(c, out var content))
content?.RenderData.Dispose();
content?.Dispose();
base.OnUnreferencedFromCompositor(c);
}

private protected override void SerializeChanges(Compositor c, BatchStreamWriter writer)
{
base.SerializeChanges(c, writer);
if (_renderDataDictionary.TryGetValue(c, out var content))
writer.WriteObject(content);
else
writer.WriteObject(null);
CompositionRenderDataSceneBrushContent.Properties? content = null;
if (IsOnCompositor(c)) // Should always be true here, but just in case do this check
{
_renderDataDictionary.TryGetValue(c, out var data);
if (data == null || data.IsDirty)
{
var created = CreateServerContent(c);
// Dispose the old render list _after_ creating a new one to avoid unnecessary detach/attach
// sequence for referenced resources
if (data != null)
data.Dispose();

_renderDataDictionary[c] = data = created;
}

if (data != null)
content = new(data.Data.Server, data.Rect, false);
}

writer.WriteObject(content);
}

CompositionRenderDataSceneBrushContent? CreateServerContent(Compositor c)
void InvalidateContent()
{
foreach(var item in _renderDataDictionary)
if (item.Value != null)
item.Value.IsDirty = true;
RegisterForSerialization();
}

protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
// We are supposed to be only calling this when content is actually changed,
// but instead we are calling this on brush property change for backwards compat with 0.10.x
InvalidateContent();
base.OnPropertyChanged(change);
}

RenderDataItem? CreateServerContent(Compositor c)
{
if (Visual == null)
return null;
Expand All @@ -99,10 +134,8 @@ private protected override void SerializeChanges(Compositor c, BatchStreamWriter
var renderData = recorder.GetRenderResults();
if (renderData == null)
return null;

return new CompositionRenderDataSceneBrushContent(
(ServerCompositionSimpleContentBrush)((ICompositionRenderResource<IBrush>)this).GetForCompositor(c),
renderData, new(Visual.Bounds.Size), false);

return new(renderData, new(Visual.Bounds.Size));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ namespace Avalonia.Rendering.Composition.Server;

internal sealed class ServerCompositionSimpleContentBrush : ServerCompositionSimpleTileBrush, ITileBrush, ISceneBrush
{
private CompositionRenderDataSceneBrushContent? _content;
private CompositionRenderDataSceneBrushContent.Properties? _content;


internal ServerCompositionSimpleContentBrush(ServerCompositor compositor) : base(compositor)
{
}

// TODO: Figure out something about disposable
public ISceneBrushContent? CreateContent() => _content;
public ISceneBrushContent? CreateContent() =>
_content == null || _content.RenderData.IsDisposed
? null
: new CompositionRenderDataSceneBrushContent(this, _content);

protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan committedAt)
{
base.DeserializeChangesCore(reader, committedAt);
_content = reader.ReadObject<CompositionRenderDataSceneBrushContent?>();
_content = reader.ReadObject<CompositionRenderDataSceneBrushContent.Properties?>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ namespace Avalonia.Rendering.Composition.Drawing;

internal class CompositionRenderDataSceneBrushContent : ISceneBrushContent
{
public CompositionRenderData RenderData { get; }
public ServerCompositionRenderData RenderData { get; }
private readonly Rect? _rect;

public CompositionRenderDataSceneBrushContent(ITileBrush brush, CompositionRenderData renderData, Rect? rect,
bool useScalableRasterization)
public record Properties(ServerCompositionRenderData RenderData, Rect? Rect, bool UseScalableRasterization);

public CompositionRenderDataSceneBrushContent(ITileBrush brush, Properties properties)
{
Brush = brush;
_rect = rect;
UseScalableRasterization = useScalableRasterization;
RenderData = renderData;
_rect = properties.Rect;
UseScalableRasterization = properties.UseScalableRasterization;
RenderData = properties.RenderData;
}

public ITileBrush Brush { get; }
public Rect Rect => _rect ?? (RenderData.Server?.Bounds?.ToRect() ?? default);
public Rect Rect => _rect ?? (RenderData?.Bounds?.ToRect() ?? default);

public double Opacity => Brush.Opacity;
public ITransform? Transform => Brush.Transform;
Expand All @@ -36,11 +37,11 @@ public void Render(IDrawingContextImpl context, Matrix? transform)
{
var oldTransform = context.Transform;
context.Transform = transform.Value * oldTransform;
RenderData.Server.Render(context);
RenderData.Render(context);
context.Transform = oldTransform;
}
else
RenderData.Server.Render(context);
RenderData.Render(context);
}

public bool UseScalableRasterization { get; }
Expand Down
12 changes: 12 additions & 0 deletions src/Avalonia.Base/Utilities/SmallDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ public bool Remove(TKey key)
return false;
}

public void Clear()
{
if(_data == null)
return;
if(_data is KeyValuePair[] arr)
Array.Clear(arr, 0, arr.Length);
else if (_data is Dictionary<TKey, TValue?> dic)
dic.Clear();
else
_data = null;
}

public bool HasEntries => _data != null;

public bool TryGetValue(TKey key, [MaybeNullWhen(false)]out TValue value)
Expand Down

0 comments on commit 9be975b

Please sign in to comment.