Browse Source

GPU interop features now don't require to skip the first frame and available earlier in general (#14651)

pull/14664/head
Nikita Tsukanov 2 years ago
committed by GitHub
parent
commit
7fb26639f5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      src/Android/Avalonia.Android/AndroidPlatform.cs
  2. 5
      src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
  3. 64
      src/Avalonia.Base/Rendering/Composition/Compositor.cs
  4. 50
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.UserApis.cs
  5. 6
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
  6. 11
      src/Avalonia.Base/Rendering/PlatformRenderInterfaceContextManager.cs
  7. 1
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  8. 1
      src/Avalonia.X11/X11Platform.cs
  9. 1
      src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  10. 18
      src/Skia/Avalonia.Skia/SkiaBackendContext.cs
  11. 1
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  12. 1
      src/Windows/Avalonia.Win32/Win32Platform.cs
  13. 1
      src/iOS/Avalonia.iOS/Platform.cs

1
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -90,6 +90,7 @@ namespace Avalonia.Android
}
Compositor = new Compositor(graphics);
AvaloniaLocator.CurrentMutable.Bind<Compositor>().ToConstant(Compositor);
}
private static IPlatformGraphics InitializeGraphics(AndroidPlatformOptions opts)

5
src/Avalonia.Base/Platform/IPlatformRenderInterface.cs

@ -217,5 +217,10 @@ namespace Avalonia.Platform
/// Indicates that the context is no longer usable. This method should be thread-safe
/// </summary>
bool IsLost { get; }
/// <summary>
/// Exposes features that should be available for consumption while context isn't active (e. g. from the UI thread)
/// </summary>
IReadOnlyDictionary<Type, object> PublicFeatures { get; }
}
}

64
src/Avalonia.Base/Rendering/Composition/Compositor.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Platform;
@ -256,33 +257,56 @@ namespace Avalonia.Rendering.Composition
return tcs.Task;
}
internal ValueTask<IReadOnlyDictionary<Type, object>> GetRenderInterfacePublicFeatures()
{
if (Server.AT_TryGetCachedRenderInterfaceFeatures() is { } rv)
return new(rv);
if (!Loop.RunsInBackground)
return new(Server.RT_GetRenderInterfaceFeatures());
return new(InvokeServerJobAsync(Server.RT_GetRenderInterfaceFeatures));
}
/// <summary>
/// Attempts to query for a feature from the platform render interface
/// </summary>
public ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType) =>
new(InvokeServerJobAsync(() =>
{
using (Server.RenderInterface.EnsureCurrent())
{
return Server.RenderInterface.Value.TryGetFeature(featureType);
}
}));
public async ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType)
{
(await GetRenderInterfacePublicFeatures().ConfigureAwait(false)).TryGetValue(featureType, out var rv);
return rv;
}
/// <summary>
/// Attempts to query for GPU interop feature from the platform render interface
/// </summary>
/// <returns></returns>
public async ValueTask<ICompositionGpuInterop?> TryGetCompositionGpuInterop()
{
var externalObjects =
(IExternalObjectsRenderInterfaceContextFeature?)await TryGetRenderInterfaceFeature(
typeof(IExternalObjectsRenderInterfaceContextFeature)).ConfigureAwait(false);
public ValueTask<ICompositionGpuInterop?> TryGetCompositionGpuInterop() =>
new(InvokeServerJobAsync<ICompositionGpuInterop?>(() =>
{
using (Server.RenderInterface.EnsureCurrent())
{
var feature = Server.RenderInterface.Value
.TryGetFeature<IExternalObjectsRenderInterfaceContextFeature>();
if (feature == null)
return null;
return new CompositionInterop(this, feature);
}
}));
if (externalObjects == null)
return null;
return new CompositionInterop(this, externalObjects);
}
internal bool UnitTestIsRegisteredForSerialization(ICompositorSerializable serializable) =>
_objectSerializationHashSet.Contains(serializable);
/// <summary>
/// Attempts to get the Compositor instance that will be used by default for new <see cref="Avalonia.Controls.TopLevel"/>s
/// created by the current platform backend.
///
/// This won't work for every single platform backend and backend settings, e. g. with web we'll need to have
/// separate Compositor instances per output HTML canvas since they don't share OpenGL state.
/// Another case where default compositor won't be available is our planned multithreaded rendering mode
/// where each window would get its own Compositor instance
///
/// This method is still useful for obtaining GPU device LUID to speed up initialization, but you should
/// always check if default Compositor matches one used by our control once it gets attached to a TopLevel
/// </summary>
/// <returns></returns>
public static Compositor? TryGetDefaultCompositor() => AvaloniaLocator.Current.GetService<Compositor>();
}
internal interface ICompositorScheduler

50
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.UserApis.cs

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Platform;
using Avalonia.Utilities;
namespace Avalonia.Rendering.Composition.Server;
internal partial class ServerCompositor
{
private IReadOnlyDictionary<Type, object>? _renderInterfaceFeatureCache;
private readonly object _renderInterfaceFeaturesUserApiLock = new();
void RT_OnContextCreated(IPlatformRenderInterfaceContext context)
{
lock (_renderInterfaceFeaturesUserApiLock)
{
_renderInterfaceFeatureCache = null;
_renderInterfaceFeatureCache = context.PublicFeatures.ToDictionary(x => x.Key, x => x.Value);
}
}
bool RT_OnContextLostExceptionFilterObserver(Exception e)
{
if (e is PlatformGraphicsContextLostException)
{
lock (_renderInterfaceFeaturesUserApiLock)
_renderInterfaceFeatureCache = null;
}
return false;
}
void RT_OnContextDisposed()
{
lock (_renderInterfaceFeaturesUserApiLock)
_renderInterfaceFeatureCache = null;
}
public IReadOnlyDictionary<Type, object>? AT_TryGetCachedRenderInterfaceFeatures()
{
lock (_renderInterfaceFeaturesUserApiLock)
return _renderInterfaceFeatureCache;
}
public IReadOnlyDictionary<Type, object> RT_GetRenderInterfaceFeatures()
{
lock (_renderInterfaceFeaturesUserApiLock)
return _renderInterfaceFeatureCache ??= RenderInterface.Value.PublicFeatures;
}
}

6
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs

@ -44,6 +44,8 @@ namespace Avalonia.Rendering.Composition.Server
{
_renderLoop = renderLoop;
RenderInterface = new PlatformRenderInterfaceContextManager(platformGraphics);
RenderInterface.ContextDisposed += RT_OnContextDisposed;
RenderInterface.ContextCreated += RT_OnContextCreated;
BatchObjectPool = batchObjectPool;
BatchMemoryPool = batchMemoryPool;
_renderLoop.Add(this);
@ -187,6 +189,10 @@ namespace Avalonia.Rendering.Composition.Server
_safeThread = Thread.CurrentThread;
RenderCore();
}
catch (Exception e) when (RT_OnContextLostExceptionFilterObserver(e) && false)
// Will never get here, only using exception filter side effect
{
}
finally
{
NotifyBatchesRendered();

11
src/Avalonia.Base/Rendering/PlatformRenderInterfaceContextManager.cs

@ -11,6 +11,8 @@ internal class PlatformRenderInterfaceContextManager
private readonly IPlatformGraphics? _graphics;
private IPlatformRenderInterfaceContext? _backend;
private OwnedDisposable<IPlatformGraphicsContext>? _gpuContext;
public event Action? ContextDisposed;
public event Action<IPlatformRenderInterfaceContext>? ContextCreated;
public PlatformRenderInterfaceContextManager(IPlatformGraphics? graphics)
{
@ -23,8 +25,12 @@ internal class PlatformRenderInterfaceContextManager
{
_backend?.Dispose();
_backend = null;
_gpuContext?.Dispose();
_gpuContext = null;
if (_gpuContext != null)
{
_gpuContext?.Dispose();
_gpuContext = null;
ContextDisposed?.Invoke();
}
if (_graphics != null)
{
@ -36,6 +42,7 @@ internal class PlatformRenderInterfaceContextManager
_backend = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>()
.CreateBackendContext(_gpuContext?.Value);
ContextCreated?.Invoke(_backend);
}
}

1
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -161,6 +161,7 @@ namespace Avalonia.Native
Compositor = new Compositor(_platformGraphics, true);
AvaloniaLocator.CurrentMutable.Bind<Compositor>().ToConstant(Compositor);
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
}

1
src/Avalonia.X11/X11Platform.cs

@ -98,6 +98,7 @@ namespace Avalonia.X11
}
Compositor = new Compositor(graphics);
AvaloniaLocator.CurrentMutable.Bind<Compositor>().ToConstant(Compositor);
}
public IntPtr DeferredDisplay { get; set; }

1
src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -60,6 +60,7 @@ namespace Avalonia.Headless
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) => new HeadlessRenderTarget();
public bool IsLost => false;
public IReadOnlyDictionary<Type, object> PublicFeatures { get; } = new Dictionary<Type, object>();
public object? TryGetFeature(Type featureType) => null;
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)

18
src/Skia/Avalonia.Skia/SkiaBackendContext.cs

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.OpenGL;
using Avalonia.Platform;
namespace Avalonia.Skia;
@ -14,6 +15,22 @@ internal class SkiaContext : IPlatformRenderInterfaceContext
public SkiaContext(ISkiaGpu? gpu)
{
_gpu = gpu;
var features = new Dictionary<Type, object>();
if (gpu != null)
{
void TryFeature<T>() where T : class
{
if (gpu!.TryGetFeature<T>() is { } feature)
features!.Add(typeof(T), feature);
}
// TODO12: extend ISkiaGpu with PublicFeatures instead
TryFeature<IOpenGlTextureSharingRenderInterfaceContextFeature>();
TryFeature<IExternalObjectsRenderInterfaceContextFeature>();
}
PublicFeatures = features;
}
public void Dispose()
@ -44,6 +61,7 @@ internal class SkiaContext : IPlatformRenderInterfaceContext
}
public bool IsLost => _gpu?.IsLost ?? false;
public IReadOnlyDictionary<Type, object> PublicFeatures { get; }
public object? TryGetFeature(Type featureType) => _gpu?.TryGetFeature(featureType);
}

1
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -181,6 +181,7 @@ namespace Avalonia.Direct2D1
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) => _platform.CreateRenderTarget(surfaces);
public bool IsLost => false;
public IReadOnlyDictionary<Type, object> PublicFeatures { get; } = new Dictionary<Type, object>();
}
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) =>

1
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -127,6 +127,7 @@ namespace Avalonia.Win32
AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>();
s_compositor = new Compositor( platformGraphics);
AvaloniaLocator.CurrentMutable.Bind<Compositor>().ToConstant(s_compositor);
}
public event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested;

1
src/iOS/Avalonia.iOS/Platform.cs

@ -78,6 +78,7 @@ namespace Avalonia.iOS
.Bind<IKeyboardDevice>().ToConstant(keyboard);
Compositor = new Compositor(AvaloniaLocator.Current.GetService<IPlatformGraphics>());
AvaloniaLocator.CurrentMutable.Bind<Compositor>().ToConstant(Compositor);
}
private static IPlatformGraphics InitializeGraphics(iOSPlatformOptions opts)

Loading…
Cancel
Save