diff --git a/samples/ControlCatalog.Browser/ControlCatalog.Browser.csproj b/samples/ControlCatalog.Browser/ControlCatalog.Browser.csproj
index f223eb0725..309d54722c 100644
--- a/samples/ControlCatalog.Browser/ControlCatalog.Browser.csproj
+++ b/samples/ControlCatalog.Browser/ControlCatalog.Browser.csproj
@@ -5,6 +5,8 @@
wwwroot/main.js
Exe
true
+ true
+ 5
@@ -12,6 +14,13 @@
+
+
+
+
diff --git a/src/Avalonia.Base/Platform/IPlatformGpu.cs b/src/Avalonia.Base/Platform/IPlatformGpu.cs
index f6953619d2..b9f11c3ebb 100644
--- a/src/Avalonia.Base/Platform/IPlatformGpu.cs
+++ b/src/Avalonia.Base/Platform/IPlatformGpu.cs
@@ -11,6 +11,19 @@ public interface IPlatformGraphics
IPlatformGraphicsContext GetSharedContext();
}
+[Unstable]
+public interface IPlatformGraphicsWithFeatures : IPlatformGraphics, IOptionalFeatureProvider
+{
+
+}
+
+[Unstable]
+public interface IPlatformGraphicsReadyStateFeature
+{
+ bool IsReady { get; }
+ bool UsesContexts { get; }
+}
+
[Unstable]
public interface IPlatformGraphicsContext : IDisposable, IOptionalFeatureProvider
{
diff --git a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs
index 92ec820b2c..9a7582ca5f 100644
--- a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs
+++ b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs
@@ -33,6 +33,7 @@ internal class UnmanagedBlob : IDisposable
GC.WaitForPendingFinalizers();
}
#endif
+ public static bool SuppressFinalizerWarning { get; set; }
public UnmanagedBlob(int size)
{
@@ -78,7 +79,7 @@ internal class UnmanagedBlob : IDisposable
public void Dispose()
{
#if DEBUG
- if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId)
+ if (!SuppressFinalizerWarning && Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId)
{
lock (_lock)
{
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
index f2de59dda7..73d9b89504 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
@@ -140,7 +140,6 @@ namespace Avalonia.Rendering.Composition.Server
if (!_redrawRequested)
return;
- _redrawRequested = false;
var renderTargetWithProperties = _renderTarget as IRenderTargetWithProperties;
@@ -199,6 +198,7 @@ namespace Avalonia.Rendering.Composition.Server
RenderedVisuals = 0;
+ _redrawRequested = false;
DirtyRects.Reset();
}
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
index 196bd88409..a25c958e4f 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
@@ -218,6 +218,8 @@ namespace Avalonia.Rendering.Composition.Server
try
{
+ if(!RenderInterface.IsReady)
+ return;
RenderInterface.EnsureValidBackendContext();
ExecuteServerJobs();
foreach (var t in _activeTargets)
diff --git a/src/Avalonia.Base/Rendering/PlatformRenderInterfaceContextManager.cs b/src/Avalonia.Base/Rendering/PlatformRenderInterfaceContextManager.cs
index d6576511b9..db61ad84f1 100644
--- a/src/Avalonia.Base/Rendering/PlatformRenderInterfaceContextManager.cs
+++ b/src/Avalonia.Base/Rendering/PlatformRenderInterfaceContextManager.cs
@@ -11,16 +11,24 @@ internal class PlatformRenderInterfaceContextManager
private readonly IPlatformGraphics? _graphics;
private IPlatformRenderInterfaceContext? _backend;
private OwnedDisposable? _gpuContext;
+ private readonly IPlatformGraphicsReadyStateFeature? _readyStateFeature;
public event Action? ContextDisposed;
public event Action? ContextCreated;
public PlatformRenderInterfaceContextManager(IPlatformGraphics? graphics)
{
_graphics = graphics;
+ _readyStateFeature = (_graphics as IPlatformGraphicsWithFeatures)
+ ?.TryGetFeature();
}
+ public bool IsReady => _readyStateFeature?.IsReady ?? true;
+
public void EnsureValidBackendContext()
{
+ if (!IsReady)
+ throw new InvalidOperationException("Platform graphics isn't ready yet");
+
if (_backend == null || _gpuContext?.Value.IsLost == true)
{
_backend?.Dispose();
@@ -34,10 +42,14 @@ internal class PlatformRenderInterfaceContextManager
if (_graphics != null)
{
- if (_graphics.UsesSharedContext)
- _gpuContext = new OwnedDisposable(_graphics.GetSharedContext(), false);
- else
- _gpuContext = new OwnedDisposable(_graphics.CreateContext(), true);
+ if (_readyStateFeature?.UsesContexts != false)
+ {
+ if (_graphics.UsesSharedContext)
+ _gpuContext =
+ new OwnedDisposable(_graphics.GetSharedContext(), false);
+ else
+ _gpuContext = new OwnedDisposable(_graphics.CreateContext(), true);
+ }
}
_backend = AvaloniaLocator.Current.GetRequiredService()
diff --git a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs
index 950d07f207..02fa530fae 100644
--- a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs
+++ b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Browser.Interop;
+using Avalonia.Browser.Rendering;
using Avalonia.Metadata;
namespace Avalonia.Browser;
@@ -107,6 +108,10 @@ public static class BrowserAppBuilder
AvaloniaLocator.CurrentMutable.Bind().ToConstant(options);
await AvaloniaModule.ImportMain();
+ if (BrowserWindowingPlatform.IsThreadingEnabled)
+ {
+ await RenderWorker.InitializeAsync();
+ }
if (builder.WindowingSubsystemInitializer is null)
{
diff --git a/src/Browser/Avalonia.Browser/BrowserDispatcherImpl.cs b/src/Browser/Avalonia.Browser/BrowserDispatcherImpl.cs
index d7eaa81abf..2d3d031c9b 100644
--- a/src/Browser/Avalonia.Browser/BrowserDispatcherImpl.cs
+++ b/src/Browser/Avalonia.Browser/BrowserDispatcherImpl.cs
@@ -14,16 +14,17 @@ internal class BrowserDispatcherImpl : IDispatcherImpl
private bool _signaled;
private int? _timerId;
- private readonly Action _timerCallback;
- private readonly Action _signalCallback;
-
public BrowserDispatcherImpl()
{
_thread = Thread.CurrentThread;
_clock = Stopwatch.StartNew();
- _timerCallback = () => Timer?.Invoke();
- _signalCallback = () =>
+ TimerHelper.Interval += () =>
+ {
+ Timer?.Invoke();
+ };
+
+ TimerHelper.Timeout = () =>
{
_signaled = false;
Signaled?.Invoke();
@@ -44,7 +45,7 @@ internal class BrowserDispatcherImpl : IDispatcherImpl
// NOTE: by HTML5 spec minimal timeout is 4ms, but Chrome seems to work well with 1ms as well.
var interval = 1;
- TimerHelper.SetTimeout(_signalCallback, interval);
+ TimerHelper.SetTimeout(interval);
}
public void UpdateTimer(long? dueTimeInMs)
@@ -58,7 +59,7 @@ internal class BrowserDispatcherImpl : IDispatcherImpl
if (dueTimeInMs.HasValue)
{
var interval = Math.Max(1, dueTimeInMs.Value - _clock.ElapsedMilliseconds);
- _timerId = TimerHelper.SetInterval(_timerCallback, (int)interval);
+ _timerId = TimerHelper.SetInterval((int)interval);
}
}
}
diff --git a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
index 9e5742954b..dc997b840a 100644
--- a/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
+++ b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
@@ -39,10 +39,9 @@ namespace Avalonia.Browser
{
InputHelper.InitializeBackgroundHandlers();
}
-
+
public BrowserTopLevelImpl(JSObject container, JSObject nativeControlHost, JSObject inputElement)
{
- Surfaces = Enumerable.Empty