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 3 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); Compositor = new Compositor(graphics);
AvaloniaLocator.CurrentMutable.Bind<Compositor>().ToConstant(Compositor);
} }
private static IPlatformGraphics InitializeGraphics(AndroidPlatformOptions opts) 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 /// Indicates that the context is no longer usable. This method should be thread-safe
/// </summary> /// </summary>
bool IsLost { get; } 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 System.Threading.Tasks;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Animation.Easings; using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Metadata; using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
@ -256,33 +257,56 @@ namespace Avalonia.Rendering.Composition
return tcs.Task; 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> /// <summary>
/// Attempts to query for a feature from the platform render interface /// Attempts to query for a feature from the platform render interface
/// </summary> /// </summary>
public ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType) => public async ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType)
new(InvokeServerJobAsync(() => {
{ (await GetRenderInterfacePublicFeatures().ConfigureAwait(false)).TryGetValue(featureType, out var rv);
using (Server.RenderInterface.EnsureCurrent()) return rv;
{ }
return Server.RenderInterface.Value.TryGetFeature(featureType);
} /// <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() => if (externalObjects == null)
new(InvokeServerJobAsync<ICompositionGpuInterop?>(() => return null;
{ return new CompositionInterop(this, externalObjects);
using (Server.RenderInterface.EnsureCurrent()) }
{
var feature = Server.RenderInterface.Value
.TryGetFeature<IExternalObjectsRenderInterfaceContextFeature>();
if (feature == null)
return null;
return new CompositionInterop(this, feature);
}
}));
internal bool UnitTestIsRegisteredForSerialization(ICompositorSerializable serializable) => internal bool UnitTestIsRegisteredForSerialization(ICompositorSerializable serializable) =>
_objectSerializationHashSet.Contains(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 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; _renderLoop = renderLoop;
RenderInterface = new PlatformRenderInterfaceContextManager(platformGraphics); RenderInterface = new PlatformRenderInterfaceContextManager(platformGraphics);
RenderInterface.ContextDisposed += RT_OnContextDisposed;
RenderInterface.ContextCreated += RT_OnContextCreated;
BatchObjectPool = batchObjectPool; BatchObjectPool = batchObjectPool;
BatchMemoryPool = batchMemoryPool; BatchMemoryPool = batchMemoryPool;
_renderLoop.Add(this); _renderLoop.Add(this);
@ -187,6 +189,10 @@ namespace Avalonia.Rendering.Composition.Server
_safeThread = Thread.CurrentThread; _safeThread = Thread.CurrentThread;
RenderCore(); RenderCore();
} }
catch (Exception e) when (RT_OnContextLostExceptionFilterObserver(e) && false)
// Will never get here, only using exception filter side effect
{
}
finally finally
{ {
NotifyBatchesRendered(); NotifyBatchesRendered();

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

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

1
src/Avalonia.Native/AvaloniaNativePlatform.cs

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

1
src/Avalonia.X11/X11Platform.cs

@ -98,6 +98,7 @@ namespace Avalonia.X11
} }
Compositor = new Compositor(graphics); Compositor = new Compositor(graphics);
AvaloniaLocator.CurrentMutable.Bind<Compositor>().ToConstant(Compositor);
} }
public IntPtr DeferredDisplay { get; set; } 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 IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) => new HeadlessRenderTarget();
public bool IsLost => false; public bool IsLost => false;
public IReadOnlyDictionary<Type, object> PublicFeatures { get; } = new Dictionary<Type, object>();
public object? TryGetFeature(Type featureType) => null; public object? TryGetFeature(Type featureType) => null;
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi) 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.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Controls.Platform.Surfaces; using Avalonia.Controls.Platform.Surfaces;
using Avalonia.OpenGL;
using Avalonia.Platform; using Avalonia.Platform;
namespace Avalonia.Skia; namespace Avalonia.Skia;
@ -14,6 +15,22 @@ internal class SkiaContext : IPlatformRenderInterfaceContext
public SkiaContext(ISkiaGpu? gpu) public SkiaContext(ISkiaGpu? gpu)
{ {
_gpu = 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() public void Dispose()
@ -44,6 +61,7 @@ internal class SkiaContext : IPlatformRenderInterfaceContext
} }
public bool IsLost => _gpu?.IsLost ?? false; public bool IsLost => _gpu?.IsLost ?? false;
public IReadOnlyDictionary<Type, object> PublicFeatures { get; }
public object? TryGetFeature(Type featureType) => _gpu?.TryGetFeature(featureType); 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 IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) => _platform.CreateRenderTarget(surfaces);
public bool IsLost => false; public bool IsLost => false;
public IReadOnlyDictionary<Type, object> PublicFeatures { get; } = new Dictionary<Type, object>();
} }
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) => 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>(); AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>();
s_compositor = new Compositor( platformGraphics); s_compositor = new Compositor( platformGraphics);
AvaloniaLocator.CurrentMutable.Bind<Compositor>().ToConstant(s_compositor);
} }
public event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested; public event EventHandler<ShutdownRequestedEventArgs>? ShutdownRequested;

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

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

Loading…
Cancel
Save