Browse Source

Merge branch 'render-only-on-render-thread' into winrt-interop

pull/5012/head
Nikita Tsukanov 6 years ago
parent
commit
8fa841753a
  1. 28
      src/Avalonia.Controls/Templates/DataTemplateExtensions.cs
  2. 73
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  3. 7
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  4. 10
      src/Avalonia.X11/X11Window.cs
  5. 39
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlFontFamilyAstNode.cs
  6. 51
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
  7. 10
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  8. 2
      src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
  9. 3
      src/Windows/Avalonia.Win32/PopupImpl.cs

28
src/Avalonia.Controls/Templates/DataTemplateExtensions.cs

@ -1,6 +1,7 @@
using System.Linq;
using Avalonia.LogicalTree;
#nullable enable
namespace Avalonia.Controls.Templates
{
/// <summary>
@ -18,21 +19,23 @@ namespace Avalonia.Controls.Templates
/// tree are searched.
/// </param>
/// <returns>The data template or null if no matching data template was found.</returns>
public static IDataTemplate FindDataTemplate(
public static IDataTemplate? FindDataTemplate(
this IControl control,
object data,
IDataTemplate primary = null)
IDataTemplate? primary = null)
{
if (primary?.Match(data) == true)
{
return primary;
}
foreach (var i in control.GetSelfAndLogicalAncestors().OfType<IDataTemplateHost>())
var currentTemplateHost = control as ILogical;
while (currentTemplateHost != null)
{
if (i.IsDataTemplatesInitialized)
if (currentTemplateHost is IDataTemplateHost hostCandidate && hostCandidate.IsDataTemplatesInitialized)
{
foreach (IDataTemplate dt in i.DataTemplates)
foreach (IDataTemplate dt in hostCandidate.DataTemplates)
{
if (dt.Match(data))
{
@ -40,20 +43,19 @@ namespace Avalonia.Controls.Templates
}
}
}
currentTemplateHost = currentTemplateHost.LogicalParent;
}
IGlobalDataTemplates global = AvaloniaLocator.Current.GetService<IGlobalDataTemplates>();
if (global != null)
if (global != null && global.IsDataTemplatesInitialized)
{
if (global.IsDataTemplatesInitialized)
foreach (IDataTemplate dt in global.DataTemplates)
{
foreach (IDataTemplate dt in global.DataTemplates)
if (dt.Match(data))
{
if (dt.Match(data))
{
return dt;
}
return dt;
}
}
}

73
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -99,6 +99,11 @@ namespace Avalonia.Rendering
/// </summary>
public string DebugFramesPath { get; set; }
/// <summary>
/// Forces the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered
/// </summary>
public bool RenderOnlyOnRenderThread { get; set; } = true;
/// <inheritdoc/>
public event EventHandler<SceneInvalidatedEventArgs> SceneInvalidated;
@ -180,11 +185,38 @@ namespace Avalonia.Rendering
/// <inheritdoc/>
public void Paint(Rect rect)
{
var t = (IRenderLoopTask)this;
if(t.NeedsUpdate)
UpdateScene();
if(_scene?.Item != null)
Render(true);
if (RenderOnlyOnRenderThread)
{
while (true)
{
Scene scene;
bool? updated;
lock (_sceneLock)
{
updated = UpdateScene();
scene = _scene?.Item;
}
// Renderer is in invalid state, skip drawing
if(updated == null)
return;
// Wait for the scene to be rendered or disposed
scene?.Rendered.Wait();
// That was an up-to-date scene, we can return immediately
if (updated == true)
return;
}
}
else
{
var t = (IRenderLoopTask)this;
if (t.NeedsUpdate)
UpdateScene();
if (_scene?.Item != null)
Render(true);
}
}
/// <inheritdoc/>
@ -270,13 +302,20 @@ namespace Avalonia.Rendering
{
if (scene?.Item != null)
{
var overlay = DrawDirtyRects || DrawFps;
if (DrawDirtyRects)
_dirtyRectsDisplay.Tick();
if (overlay)
RenderOverlay(scene.Item, ref context);
if (updated || forceComposite || overlay)
RenderComposite(scene.Item, ref context);
try
{
var overlay = DrawDirtyRects || DrawFps;
if (DrawDirtyRects)
_dirtyRectsDisplay.Tick();
if (overlay)
RenderOverlay(scene.Item, ref context);
if (updated || forceComposite || overlay)
RenderComposite(scene.Item, ref context);
}
finally
{
scene.Item.MarkAsRendered();
}
}
}
}
@ -559,15 +598,15 @@ namespace Avalonia.Rendering
UpdateScene();
}
private void UpdateScene()
private bool? UpdateScene()
{
Dispatcher.UIThread.VerifyAccess();
lock (_sceneLock)
{
if (_disposed)
return;
return null;
if (_scene?.Item.Generation > _lastSceneId)
return;
return false;
}
if (_root.IsVisible)
{
@ -619,6 +658,8 @@ namespace Avalonia.Rendering
SceneInvalidated(this, new SceneInvalidatedEventArgs((IRenderRoot)_root, rect));
}
return true;
}
else
{
@ -628,6 +669,8 @@ namespace Avalonia.Rendering
_scene = null;
oldScene?.Dispose();
}
return null;
}
}

7
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Collections.Pooled;
using Avalonia.VisualTree;
@ -13,6 +14,7 @@ namespace Avalonia.Rendering.SceneGraph
public class Scene : IDisposable
{
private readonly Dictionary<IVisual, IVisualNode> _index;
private readonly TaskCompletionSource<bool> _rendered = new TaskCompletionSource<bool>();
/// <summary>
/// Initializes a new instance of the <see cref="Scene"/> class.
@ -41,6 +43,8 @@ namespace Avalonia.Rendering.SceneGraph
root.LayerRoot = root.Visual;
}
public Task Rendered => _rendered.Task;
/// <summary>
/// Gets a value identifying the scene's generation. This is incremented each time the scene is cloned.
/// </summary>
@ -97,6 +101,7 @@ namespace Avalonia.Rendering.SceneGraph
public void Dispose()
{
_rendered.TrySetResult(false);
foreach (var node in _index.Values)
{
node.Dispose();
@ -340,5 +345,7 @@ namespace Avalonia.Rendering.SceneGraph
}
}
}
public void MarkAsRendered() => _rendered.TrySetResult(true);
}
}

10
src/Avalonia.X11/X11Window.cs

@ -189,6 +189,11 @@ namespace Avalonia.X11
if (platform.Options.UseDBusMenu)
NativeMenuExporter = DBusMenuExporter.TryCreate(_handle);
NativeControlHost = new X11NativeControlHost(_platform, this);
DispatcherTimer.Run(() =>
{
Paint?.Invoke(default);
return _handle != IntPtr.Zero;
}, TimeSpan.FromMilliseconds(100));
}
class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
@ -338,7 +343,10 @@ namespace Avalonia.X11
return customRendererFactory.Create(root, loop);
return _platform.Options.UseDeferredRendering ?
new DeferredRenderer(root, loop) :
new DeferredRenderer(root, loop)
{
RenderOnlyOnRenderThread = true
} :
(IRenderer)new X11ImmediateRendererProxy(root, loop);
}

39
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlFontFamilyAstNode.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
{
class AvaloniaXamlIlFontFamilyAstNode: XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
{
private readonly AvaloniaXamlIlWellKnownTypes _types;
private readonly string _text;
public IXamlAstTypeReference Type { get; }
public AvaloniaXamlIlFontFamilyAstNode(AvaloniaXamlIlWellKnownTypes types,
string text,
IXamlLineInfo lineInfo) : base(lineInfo)
{
_types = types;
_text = text;
Type = new XamlAstClrTypeReference(lineInfo, types.FontFamily, false);
}
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen
.Ldloc(context.ContextLocal)
.Castclass(context.Configuration.TypeMappings.UriContextProvider)
.EmitCall(context.Configuration.TypeMappings.UriContextProvider.FindMethod(
"get_BaseUri", _types.Uri, false))
.Ldstr(_text)
.Newobj(_types.FontFamilyConstructorUriName);
return XamlILNodeEmitResult.Type(0, _types.FontFamily);
}
}
}

51
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX;
using XamlX.Ast;
@ -166,17 +168,41 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public static bool CustomValueConverter(AstTransformationContext context,
IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result)
{
if (type.FullName == "System.TimeSpan"
&& node is XamlAstTextNode tn
&& !tn.Text.Contains(":"))
if (!(node is XamlAstTextNode textNode))
{
var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture);
result = new XamlStaticOrTargetedReturnMethodCallNode(tn,
type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double),
new[]
{
new XamlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds)
});
result = null;
return false;
}
var text = textNode.Text;
var types = context.GetAvaloniaTypes();
if (type.FullName == "System.TimeSpan")
{
var tsText = text.Trim();
if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan))
{
// // shorthand seconds format (ie. "0.25")
if (!tsText.Contains(":") && double.TryParse(tsText,
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out var seconds))
timeSpan = TimeSpan.FromSeconds(seconds);
else
throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node);
}
result = new XamlStaticOrTargetedReturnMethodCallNode(node,
type.FindMethod("FromTicks", type, false, types.Long),
new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) });
return true;
}
if (type.Equals(types.FontFamily))
{
result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node);
return true;
}
@ -185,9 +211,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
var scope = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault();
if (scope == null)
throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
if (!(node is XamlAstTextNode text))
throw new XamlX.XamlLoadException("Property should be a text node", node);
result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text);
result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text, scope.TargetType, node );
return true;
}

10
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -1,3 +1,4 @@
using System.Collections.Generic;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Transform;
@ -47,6 +48,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType ReflectionBindingExtension { get; }
public IXamlType RelativeSource { get; }
public IXamlType Long { get; }
public IXamlType Uri { get; }
public IXamlType FontFamily { get; }
public IXamlConstructor FontFamilyConstructorUriName { get; }
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{
@ -104,6 +110,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
ItemsRepeater = cfg.TypeSystem.GetType("Avalonia.Controls.ItemsRepeater");
ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension");
RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource");
Long = cfg.TypeSystem.GetType("System.Int64");
Uri = cfg.TypeSystem.GetType("System.Uri");
FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily");
FontFamilyConstructorUriName = FontFamily.FindConstructor(new List<IXamlType> { Uri, XamlIlTypes.String });
}
}

2
src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs

@ -16,7 +16,7 @@ namespace Avalonia.Markup.Parsers
internal static class BindingExpressionGrammar
{
public static (IList<INode> Nodes, SourceMode Mode) Parse(ref CharacterReader r)
public static (List<INode> Nodes, SourceMode Mode) Parse(ref CharacterReader r)
{
var nodes = new List<INode>();
var state = State.Start;

3
src/Windows/Avalonia.Win32/PopupImpl.cs

@ -69,7 +69,8 @@ namespace Avalonia.Win32
{
UnmanagedMethods.WindowStyles style =
UnmanagedMethods.WindowStyles.WS_POPUP |
UnmanagedMethods.WindowStyles.WS_CLIPSIBLINGS;
UnmanagedMethods.WindowStyles.WS_CLIPSIBLINGS |
UnmanagedMethods.WindowStyles.WS_CLIPCHILDREN;
UnmanagedMethods.WindowStyles exStyle =
UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW |

Loading…
Cancel
Save