Browse Source
* Skip drawing visual content if bounds don't intersect with the current clip and dirty rect clip * PopOpacityMask should reset the current transform to what it was during PushOpacityMask call before applying the mask * Buffer pending Push/Transform commands until an actual drawing command gets issued. That way we can avoid otherwise expensive platform calls for visual subtrees that don't actually produce any render results due to being clipped --------- Co-authored-by: Max Katz <maxkatz6@outlook.com>release/11.1.0-beta2
committed by
Max Katz
9 changed files with 310 additions and 53 deletions
@ -0,0 +1,152 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.Collections.Pooled; |
|||
using Avalonia.Media; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Rendering.Composition.Server; |
|||
|
|||
internal partial class CompositorDrawingContextProxy |
|||
{ |
|||
|
|||
private PooledList<PendingCommand> _commands = new(); |
|||
private bool _autoFlush; |
|||
|
|||
|
|||
enum PendingCommandType |
|||
{ |
|||
SetTransform, |
|||
PushClip, |
|||
PushOpacity, |
|||
PushOpacityMask, |
|||
PushGeometryClip, |
|||
PushRenderOptions, |
|||
PushEffect |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Explicit)] |
|||
struct PendingCommandObjectUnion |
|||
{ |
|||
[FieldOffset(0)] public IEffect? Effect; |
|||
[FieldOffset(0)] public IBrush? Mask; |
|||
[FieldOffset(0)] public IGeometryImpl? Clip; |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Explicit)] |
|||
struct PendingCommandDataUnion |
|||
{ |
|||
// PushOpacity
|
|||
[FieldOffset(0)] public double Opacity; |
|||
[FieldOffset(8)] public Rect? NullableOpacityRect; |
|||
|
|||
[FieldOffset(0)] public Matrix Transform; |
|||
|
|||
[FieldOffset(0)] public RenderOptions RenderOptions; |
|||
|
|||
// PushClip/PushOpacityMask
|
|||
[FieldOffset(0)] public bool IsRoundRect; |
|||
[FieldOffset(4)] public RoundedRect RoundRect; |
|||
[FieldOffset(4)] public Rect NormalRect; |
|||
} |
|||
|
|||
struct PendingCommand |
|||
{ |
|||
public PendingCommandType Type; |
|||
public PendingCommandObjectUnion ObjectUnion; |
|||
public PendingCommandDataUnion DataUnion; |
|||
|
|||
} |
|||
|
|||
public bool AutoFlush |
|||
{ |
|||
get => _autoFlush; |
|||
set |
|||
{ |
|||
_autoFlush = value; |
|||
if (value) |
|||
Flush(); |
|||
} |
|||
} |
|||
|
|||
public void SetTransform(Matrix m) |
|||
{ |
|||
if (_autoFlush) |
|||
{ |
|||
SetImplTransform(m); |
|||
return; |
|||
} |
|||
var cmd = new PendingCommand |
|||
{ |
|||
Type = PendingCommandType.SetTransform, |
|||
DataUnion = { Transform = m } |
|||
}; |
|||
if (_commands.Count > 0 && _commands[_commands.Count - 1].Type == PendingCommandType.SetTransform) |
|||
_commands[_commands.Count - 1] = cmd; |
|||
else |
|||
_commands.Add(cmd); |
|||
} |
|||
|
|||
|
|||
private bool TryDiscard(PendingCommandType type) |
|||
{ |
|||
while (_commands.Count > 0 && _commands[_commands.Count - 1].Type == PendingCommandType.SetTransform) |
|||
_commands.RemoveAt(_commands.Count - 1); |
|||
if (_commands.Count == 0) |
|||
return false; |
|||
if (_commands[_commands.Count - 1].Type == type) |
|||
{ |
|||
_commands.RemoveAt(_commands.Count - 1); |
|||
return true; |
|||
} |
|||
|
|||
// Not sure how exactly can we get here, but flush commands just in case
|
|||
Flush(); |
|||
return false; |
|||
} |
|||
|
|||
void AddCommand(PendingCommand command) |
|||
{ |
|||
if(_autoFlush) |
|||
ExecCommand(ref command); |
|||
else |
|||
_commands.Add(command); |
|||
} |
|||
|
|||
void ExecCommand(ref PendingCommand cmd) |
|||
{ |
|||
if (cmd.Type == PendingCommandType.SetTransform) |
|||
SetImplTransform(cmd.DataUnion.Transform); |
|||
else if (cmd.Type == PendingCommandType.PushOpacity) |
|||
_impl.PushOpacity(cmd.DataUnion.Opacity, cmd.DataUnion.NullableOpacityRect); |
|||
else if (cmd.Type == PendingCommandType.PushOpacityMask) |
|||
_impl.PushOpacityMask(cmd.ObjectUnion.Mask!, cmd.DataUnion.NormalRect); |
|||
else if (cmd.Type == PendingCommandType.PushClip) |
|||
{ |
|||
if (cmd.DataUnion.IsRoundRect) |
|||
_impl.PushClip(cmd.DataUnion.RoundRect); |
|||
else |
|||
_impl.PushClip(cmd.DataUnion.NormalRect); |
|||
} |
|||
else if (cmd.Type == PendingCommandType.PushGeometryClip) |
|||
_impl.PushGeometryClip(cmd.ObjectUnion.Clip!); |
|||
else if (cmd.Type == PendingCommandType.PushEffect) |
|||
{ |
|||
if (_impl is IDrawingContextImplWithEffects effects) |
|||
effects.PushEffect(cmd.ObjectUnion.Effect!); |
|||
} |
|||
else if (cmd.Type == PendingCommandType.PushRenderOptions) |
|||
_impl.PushRenderOptions(cmd.DataUnion.RenderOptions); |
|||
else |
|||
Debug.Assert(false); |
|||
} |
|||
|
|||
public void Flush() |
|||
{ |
|||
var commands = _commands.AsSpan(); |
|||
for (var index = 0; index < commands.Length; index++) |
|||
ExecCommand(ref commands[index]); |
|||
|
|||
_commands.Clear(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue