From 758eaa4bc0db95a397c9388ccb3d0518698fa2ae Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 18 Dec 2018 18:35:01 +0300 Subject: [PATCH] Added support for critical render time visuals to ImmediateRenderer --- src/Avalonia.Native/WindowImplBase.cs | 5 +- .../Rendering/ImmediateRenderer.cs | 55 +++++++++++++++---- src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 4 +- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 629c91a2e8..91e84cf24a 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -240,11 +240,12 @@ namespace Avalonia.Native public IRenderer CreateRenderer(IRenderRoot root) { + var loop = AvaloniaLocator.Current.GetService(); if (_deferredRendering) - return new DeferredRenderer(root, AvaloniaLocator.Current.GetService(), + return new DeferredRenderer(root, loop, rendererLock: _gpu ? new AvaloniaNativeDeferredRendererLock(_native) : null); - return new ImmediateRenderer(root); + return new ImmediateRenderer(root, loop); } public virtual void Dispose() diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index 03ac004fd5..eb5ab36935 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -18,21 +18,26 @@ namespace Avalonia.Rendering /// The immediate renderer supports only clip-bound-based hit testing; a control's geometry is /// not taken into account. /// - public class ImmediateRenderer : RendererBase, IRenderer, IVisualBrushRenderer + public class ImmediateRenderer : RendererBase, IRenderer, IVisualBrushRenderer, + IRenderLoopTask { private readonly IVisual _root; + private readonly IRenderLoop _loop; private readonly IRenderRoot _renderRoot; private IRenderTarget _renderTarget; + private RenderPassInfo _lastRenderPassInfo; /// /// Initializes a new instance of the class. /// /// The control to render. - public ImmediateRenderer(IVisual root) + /// Render loop + public ImmediateRenderer(IVisual root, IRenderLoop loop = null) { Contract.Requires(root != null); _root = root; + _loop = loop; _renderRoot = root as IRenderRoot; } @@ -50,6 +55,7 @@ namespace Avalonia.Rendering _renderTarget = ((IRenderRoot)_root).CreateRenderTarget(); } + var scaling = (_root as IRenderRoot)?.RenderScaling ?? 1; try { using (var context = new DrawingContext(_renderTarget.CreateDrawingContext(this))) @@ -58,7 +64,7 @@ namespace Avalonia.Rendering using (context.PushTransformContainer()) { - Render(context, _root, _root.Bounds); + _lastRenderPassInfo = Render(context, _root, _root.Bounds, scaling); } if (DrawDirtyRects) @@ -95,10 +101,10 @@ namespace Avalonia.Rendering /// The render target. public static void Render(IVisual visual, IRenderTarget target) { - using (var renderer = new ImmediateRenderer(visual)) + using (var renderer = new ImmediateRenderer(visual, null)) using (var context = new DrawingContext(target.CreateDrawingContext(renderer))) { - renderer.Render(context, visual, visual.Bounds); + renderer.Render(context, visual, visual.Bounds, 1); } } @@ -107,11 +113,11 @@ namespace Avalonia.Rendering /// /// The visual. /// The drawing context. - public static void Render(IVisual visual, DrawingContext context) + public static void Render(IVisual visual, DrawingContext context, double scaling = 1) { - using (var renderer = new ImmediateRenderer(visual)) + using (var renderer = new ImmediateRenderer(visual, null)) { - renderer.Render(context, visual, visual.Bounds); + renderer.Render(context, visual, visual.Bounds, scaling); } } @@ -161,11 +167,13 @@ namespace Avalonia.Rendering /// public void Start() { + _loop?.Add(this); } /// public void Stop() { + _loop?.Remove(this); } /// @@ -179,7 +187,7 @@ namespace Avalonia.Rendering void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush) { var visual = brush.Visual; - Render(new DrawingContext(context), visual, visual.Bounds); + Render(new DrawingContext(context), visual, visual.Bounds, 1); } private static void ClearTransformedBounds(IVisual visual) @@ -243,11 +251,23 @@ namespace Avalonia.Rendering } } - private void Render(DrawingContext context, IVisual visual, Rect clipRect) + struct RenderPassInfo + { + public bool HasRenderTimeCriticalVisuals { get; set; } + public static RenderPassInfo operator + (RenderPassInfo left, RenderPassInfo right) + { + var rv = left; + rv.HasRenderTimeCriticalVisuals |= right.HasRenderTimeCriticalVisuals; + return rv; + } + } + + private RenderPassInfo Render(DrawingContext context, IVisual visual, Rect clipRect, double scaling) { var opacity = visual.Opacity; var clipToBounds = visual.ClipToBounds; var bounds = new Rect(visual.Bounds.Size); + var info = new RenderPassInfo(); if (visual.IsVisible && opacity > 0) { @@ -283,6 +303,11 @@ namespace Avalonia.Rendering using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default(DrawingContext.PushedState)) using (context.PushTransformContainer()) { + if (visual is IRenderTimeCriticalVisual critical) + { + critical.ThreadSafeRender(context, bounds.Size, scaling); + info.HasRenderTimeCriticalVisuals = true; + } visual.Render(context); #pragma warning disable 0618 @@ -299,7 +324,7 @@ namespace Avalonia.Rendering if (!child.ClipToBounds || clipRect.Intersects(childBounds)) { var childClipRect = clipRect.Translate(-childBounds.Position); - Render(context, child, childClipRect); + info += Render(context, child, childClipRect, scaling); } else { @@ -313,6 +338,14 @@ namespace Avalonia.Rendering { ClearTransformedBounds(visual); } + + return info; + } + + public bool NeedsUpdate => _lastRenderPassInfo.HasRenderTimeCriticalVisuals; + public void Update(TimeSpan time) => _root.InvalidateVisual(); + public void Render() + { } } } diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs index 6d4544bfbd..243524deb1 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs @@ -509,7 +509,7 @@ namespace Avalonia.Gtk3 var loop = AvaloniaLocator.Current.GetService(); return Gtk3Platform.UseDeferredRendering ? (IRenderer) new DeferredRenderer(root, loop) - : new ImmediateRenderer(root); + : new ImmediateRenderer(root, loop); } PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 6174bc360f..78aa6b06c3 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -148,7 +148,9 @@ namespace Avalonia.Win32 if (customRendererFactory != null) return customRendererFactory.Create(root, loop); - return Win32Platform.UseDeferredRendering ? (IRenderer)new DeferredRenderer(root, loop) : new ImmediateRenderer(root); + return Win32Platform.UseDeferredRendering + ? (IRenderer)new DeferredRenderer(root, loop) + : new ImmediateRenderer(root, loop); } public void Resize(Size value)