From cff0c680ac7d51919c61e4c7323c7895ba896f63 Mon Sep 17 00:00:00 2001 From: ahopper Date: Sat, 22 Jan 2022 17:24:42 +0000 Subject: [PATCH 1/2] remove lock from X11Window --- src/Avalonia.X11/X11Window.cs | 60 ++++++++++++++--------------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index d745b4765b..07469b7362 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reactive.Disposables; using System.Text; using System.Threading.Tasks; +using System.Threading; using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives.PopupPositioning; @@ -50,8 +51,6 @@ namespace Avalonia.X11 private bool _disabled; private TransparencyHelper _transparencyHelper; - public object SyncRoot { get; } = new object(); - class InputEventContainer { public RawInputEventArgs Event; @@ -317,13 +316,8 @@ namespace Avalonia.X11 public double RenderScaling { - get - { - lock (SyncRoot) - return _scaling; - - } - private set => _scaling = value; + get => Interlocked.CompareExchange(ref _scaling, 0.0, 0.0); + private set => Interlocked.Exchange(ref _scaling, value); } public double DesktopScaling => RenderScaling; @@ -378,11 +372,6 @@ namespace Avalonia.X11 } void OnEvent(ref XEvent ev) - { - lock (SyncRoot) - OnEventSync(ref ev); - } - void OnEventSync(ref XEvent ev) { if (ev.type == XEventName.MapNotify) { @@ -544,32 +533,29 @@ namespace Avalonia.X11 private bool UpdateScaling(bool skipResize = false) { - lock (SyncRoot) + double newScaling; + if (_scalingOverride.HasValue) + newScaling = _scalingOverride.Value; + else { - double newScaling; - if (_scalingOverride.HasValue) - newScaling = _scalingOverride.Value; - else - { - var monitor = _platform.X11Screens.Screens.OrderBy(x => x.PixelDensity) - .FirstOrDefault(m => m.Bounds.Contains(Position)); - newScaling = monitor?.PixelDensity ?? RenderScaling; - } - - if (RenderScaling != newScaling) - { - var oldScaledSize = ClientSize; - RenderScaling = newScaling; - ScalingChanged?.Invoke(RenderScaling); - UpdateImePosition(); - SetMinMaxSize(_scaledMinMaxSize.minSize, _scaledMinMaxSize.maxSize); - if(!skipResize) - Resize(oldScaledSize, true, PlatformResizeReason.DpiChange); - return true; - } + var monitor = _platform.X11Screens.Screens.OrderBy(x => x.PixelDensity) + .FirstOrDefault(m => m.Bounds.Contains(Position)); + newScaling = monitor?.PixelDensity ?? RenderScaling; + } - return false; + if (RenderScaling != newScaling) + { + var oldScaledSize = ClientSize; + RenderScaling = newScaling; + ScalingChanged?.Invoke(RenderScaling); + UpdateImePosition(); + SetMinMaxSize(_scaledMinMaxSize.minSize, _scaledMinMaxSize.maxSize); + if(!skipResize) + Resize(oldScaledSize, true, PlatformResizeReason.DpiChange); + return true; } + + return false; } private WindowState _lastWindowState; From cd2c6b4859def31b9fcb8915515e2686330a48fa Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 23 Jan 2022 20:27:23 +0000 Subject: [PATCH 2/2] optimize inital setup and fix initial render pass. --- .../Avalonia.Web.Blazor/AvaloniaView.razor.cs | 137 +++++++++--------- 1 file changed, 65 insertions(+), 72 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index dc8b091563..bb8ae9e119 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -26,8 +26,8 @@ namespace Avalonia.Web.Blazor private InputHelperInterop _canvasHelper = null!; private ElementReference _htmlCanvas; private ElementReference _inputElement; - private double _dpi; - private SKSize _canvasSize; + private double _dpi = 1; + private SKSize _canvasSize = new (100, 100); private GRContext? _context; private GRGlInterface? _glInterface; @@ -249,71 +249,55 @@ namespace Avalonia.Web.Blazor [Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary? AdditionalAttributes { get; set; } - protected override void OnAfterRender(bool firstRender) + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - Threading.Dispatcher.UIThread.Post(async () => + _inputHelper = await InputHelperInterop.ImportAsync(Js, _inputElement); + _canvasHelper = await InputHelperInterop.ImportAsync(Js, _htmlCanvas); + + _inputHelper.Hide(); + _canvasHelper.SetCursor("default"); + _topLevelImpl.SetCssCursor = x => + { + _inputHelper.SetCursor(x); //macOS + _canvasHelper.SetCursor(x); //windows + }; + + Console.WriteLine("starting html canvas setup"); + _interop = await SKHtmlCanvasInterop.ImportAsync(Js, _htmlCanvas, OnRenderFrame); + + Console.WriteLine("Interop created"); + _jsGlInfo = _interop.InitGL(); + + Console.WriteLine("jsglinfo created - init gl"); + + // create the SkiaSharp context + if (_context == null) { - _inputHelper = await InputHelperInterop.ImportAsync(Js, _inputElement); - _canvasHelper = await InputHelperInterop.ImportAsync(Js, _htmlCanvas); - - _inputHelper.Hide(); - _canvasHelper.SetCursor("default"); - _topLevelImpl.SetCssCursor = x => - { - _inputHelper.SetCursor(x);//macOS - _canvasHelper.SetCursor(x);//windows - }; - - Console.WriteLine("starting html canvas setup"); - _interop = await SKHtmlCanvasInterop.ImportAsync(Js, _htmlCanvas, OnRenderFrame); - - Console.WriteLine("Interop created"); - _jsGlInfo = _interop.InitGL(); - - Console.WriteLine("jsglinfo created - init gl"); - - _sizeWatcher = await SizeWatcherInterop.ImportAsync(Js, _htmlCanvas, OnSizeChanged); - _dpiWatcher = await DpiWatcherInterop.ImportAsync(Js, OnDpiChanged); - - Console.WriteLine("watchers created."); - - // create the SkiaSharp context - if (_context == null) - { - Console.WriteLine("create glcontext"); - _glInterface = GRGlInterface.Create(); - _context = GRContext.CreateGl(_glInterface); - - var options = AvaloniaLocator.Current.GetService(); - // bump the default resource cache limit - _context.SetResourceCacheLimit(options?.MaxGpuResourceSizeBytes ?? 32 * 1024 * 1024); - Console.WriteLine("glcontext created and resource limit set"); - } - - _topLevelImpl.SetSurface(_context, _jsGlInfo, ColorType, - new PixelSize((int)_canvasSize.Width, (int)_canvasSize.Height), _dpi); - - _initialised = true; - - _topLevel.Prepare(); - - _topLevel.Renderer.Start(); - - // Note: this is technically a hack, but it's a kinda unique use case when - // we want to blit the previous frame - // renderer doesn't have much control over the render target - // we render on the UI thread - // We also don't want to have it as a meaningful public API. - // Therefore we have InternalsVisibleTo hack here. - if (_topLevel.Renderer is DeferredRenderer dr) - { - dr.Render(true); - } - - Invalidate(); - }); + Console.WriteLine("create glcontext"); + _glInterface = GRGlInterface.Create(); + _context = GRContext.CreateGl(_glInterface); + + var options = AvaloniaLocator.Current.GetService(); + // bump the default resource cache limit + _context.SetResourceCacheLimit(options?.MaxGpuResourceSizeBytes ?? 32 * 1024 * 1024); + Console.WriteLine("glcontext created and resource limit set"); + } + + _topLevelImpl.SetSurface(_context, _jsGlInfo, ColorType, + new PixelSize((int)_canvasSize.Width, (int)_canvasSize.Height), _dpi); + + _initialised = true; + + _topLevel.Prepare(); + + _topLevel.Renderer.Start(); + + Invalidate(); + + _sizeWatcher = await SizeWatcherInterop.ImportAsync(Js, _htmlCanvas, OnSizeChanged); + _dpiWatcher = await DpiWatcherInterop.ImportAsync(Js, OnDpiChanged); } } @@ -335,16 +319,28 @@ namespace Avalonia.Web.Blazor _interop.Dispose(); } - private void OnDpiChanged(double newDpi) + private void ForceBlit() { - _dpi = newDpi; + // Note: this is technically a hack, but it's a kinda unique use case when + // we want to blit the previous frame + // renderer doesn't have much control over the render target + // we render on the UI thread + // We also don't want to have it as a meaningful public API. + // Therefore we have InternalsVisibleTo hack here. - _topLevelImpl.SetClientSize(_canvasSize, _dpi); - if (_topLevel.Renderer is DeferredRenderer dr) { dr.Render(true); } + } + + private void OnDpiChanged(double newDpi) + { + _dpi = newDpi; + + _topLevelImpl.SetClientSize(_canvasSize, _dpi); + + ForceBlit(); Invalidate(); } @@ -357,17 +353,14 @@ namespace Avalonia.Web.Blazor _topLevelImpl.SetClientSize(_canvasSize, _dpi); - if (_topLevel.Renderer is DeferredRenderer dr) - { - dr.Render(true); - } + ForceBlit(); Invalidate(); } public void Invalidate() { - if (!_initialised || _canvasSize.Width <= 0 || _canvasSize.Height <= 0 || _dpi <= 0 || _jsGlInfo == null) + if (!_initialised || _jsGlInfo == null) { Console.WriteLine("invalidate ignored"); return;