diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index af1692302a..d9f9e7a84e 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1385,6 +1385,9 @@ namespace Avalonia.Win32.Interop [DllImport("dwmapi.dll")] public static extern int DwmIsCompositionEnabled(out bool enabled); + [DllImport("dwmapi.dll")] + public static extern void DwmFlush(); + [DllImport("dwmapi.dll")] public static extern bool DwmDefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref IntPtr plResult); diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index e79d9bbde7..b51f80e397 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -25,13 +25,8 @@ namespace Avalonia.Win32 var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay()); if (egl is { } && - opts?.UseWindowsUIComposition == true) - { - var compositionConnector = WinUICompositorConnection.TryCreate(egl); - - if (compositionConnector != null) - AvaloniaLocator.CurrentMutable.BindToSelf(compositionConnector); - } + opts?.UseWindowsUIComposition == true) + WinUICompositorConnection.TryCreateAndRegister(egl); return egl; } diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs index 393019b547..7e1f97ab84 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Numerics; using System.Runtime.InteropServices; using System.Threading; @@ -13,12 +14,13 @@ using Avalonia.Win32.Interop; namespace Avalonia.Win32.WinRT.Composition { - class WinUICompositorConnection + class WinUICompositorConnection : IRenderTimer { private readonly EglContext _syncContext; private IntPtr _queue; private ICompositor _compositor; private ICompositor2 _compositor2; + private ICompositor5 _compositor5; private ICompositorInterop _compositorInterop; private AngleWin32EglDisplay _angle; private ICompositionGraphicsDevice _device; @@ -35,22 +37,25 @@ namespace Avalonia.Win32.WinRT.Composition _angle = (AngleWin32EglDisplay)_gl.Display; _compositor = NativeWinRTMethods.CreateInstance("Windows.UI.Composition.Compositor"); _compositor2 = _compositor.QueryInterface(); + _compositor5 = _compositor.QueryInterface(); _compositorInterop = _compositor.QueryInterface(); _compositorDesktopInterop = _compositor.QueryInterface(); using var device = MicroComRuntime.CreateProxyFor(_angle.GetDirect3DDevice(), true); _device = _compositorInterop.CreateGraphicsDevice(device); _blurBrush = CreateBlurBrush(); + } public EglPlatformOpenGlInterface Egl => _gl; - static WinUICompositorConnection TryCreateCore(EglPlatformOpenGlInterface angle) + static bool TryCreateAndRegisterCore(EglPlatformOpenGlInterface angle) { - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(); var pumpLock = new object(); var th = new Thread(() => { + WinUICompositorConnection connect; try { NativeWinRTMethods.CreateDispatcherQueueController(new NativeWinRTMethods.DispatcherQueueOptions @@ -59,22 +64,18 @@ namespace Avalonia.Win32.WinRT.Composition dwSize = Marshal.SizeOf(), threadType = NativeWinRTMethods.DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT }); - tcs.SetResult(new WinUICompositorConnection(angle, pumpLock)); - while (true) - { - while (true) - { - if (UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0) == 0) - return; - lock (pumpLock) - UnmanagedMethods.DispatchMessage(ref msg); - } - } + connect = new WinUICompositorConnection(angle, pumpLock); + AvaloniaLocator.CurrentMutable.BindToSelf(connect); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(connect); + tcs.SetResult(true); + } catch (Exception e) { tcs.SetException(e); + return; } + connect.RunLoop(); }) { IsBackground = true @@ -83,8 +84,54 @@ namespace Avalonia.Win32.WinRT.Composition th.Start(); return tcs.Task.Result; } + + class RunLoopHandler : IAsyncActionCompletedHandler, IMicroComShadowContainer + { + private readonly WinUICompositorConnection _parent; + private Stopwatch _st = Stopwatch.StartNew(); + + public RunLoopHandler(WinUICompositorConnection parent) + { + _parent = parent; + } + public void Dispose() + { + + } + + public void Invoke(IAsyncAction asyncInfo, AsyncStatus asyncStatus) + { + _parent.Tick?.Invoke(_st.Elapsed); + using var act = _parent._compositor5.RequestCommitAsync(); + act.SetCompleted(this); + } + + public MicroComShadow Shadow { get; set; } + public void OnReferencedFromNative() + { + } + + public void OnUnreferencedFromNative() + { + } + } - public static WinUICompositorConnection TryCreate(EglPlatformOpenGlInterface angle) + private void RunLoop() + { + { + var st = Stopwatch.StartNew(); + using (var act = _compositor5.RequestCommitAsync()) + act.SetCompleted(new RunLoopHandler(this)); + while (true) + { + UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0); + lock (_pumpLock) + UnmanagedMethods.DispatchMessage(ref msg); + } + } + } + + public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle) { const int majorRequired = 10; const int buildRequired = 16299; @@ -97,14 +144,13 @@ namespace Avalonia.Win32.WinRT.Composition { try { - return TryCreateCore(angle); + TryCreateAndRegisterCore(angle); } catch (Exception e) { Logger.TryGet(LogEventLevel.Error, "WinUIComposition") ?.Log(null, "Unable to initialize WinUI compositor: {0}", e); - return null; } } @@ -113,8 +159,6 @@ namespace Avalonia.Win32.WinRT.Composition Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null, $"Unable to initialize WinUI compositor: {osVersionNotice}"); - - return null; } @@ -187,7 +231,8 @@ namespace Avalonia.Win32.WinRT.Composition return visual.CloneReference(); } - - + + + public event Action Tick; } } diff --git a/src/Windows/Avalonia.Win32/WinRT/winrt.idl b/src/Windows/Avalonia.Win32/WinRT/winrt.idl index bdc757138c..929377999c 100644 --- a/src/Windows/Avalonia.Win32/WinRT/winrt.idl +++ b/src/Windows/Avalonia.Win32/WinRT/winrt.idl @@ -691,3 +691,28 @@ interface ICompositionScopedBatch : IInspectable [eventadd] HRESULT AddCompleted([in] void* handler, [out] [retval] int* token); [eventremove] HRESULT RemoveCompleted([in] int token); } + +[uuid(48EA31AD-7FCD-4076-A79C-90CC4B852C9B)] +interface ICompositor5 : IInspectable +{ + [propget] HRESULT GetComment([out] [retval] HSTRING* value); + [propput] HRESULT SetComment([in] HSTRING value); + [propget] HRESULT GetGlobalPlaybackRate([out] [retval] FLOAT* value); + [propput] HRESULT SetGlobalPlaybackRate([in] FLOAT value); + HRESULT CreateBounceScalarAnimation([out] [retval] void** result); + HRESULT CreateBounceVector2Animation([out] [retval] void** result); + HRESULT CreateBounceVector3Animation([out] [retval] void** result); + HRESULT CreateContainerShape([out] [retval] void** result); + HRESULT CreateEllipseGeometry([out] [retval] void** result); + HRESULT CreateLineGeometry([out] [retval] void** result); + [overload("CreatePathGeometry")] HRESULT CreatePathGeometry([out] [retval] void** result); + [overload("CreatePathGeometry")] HRESULT CreatePathGeometryWithPath([in] void* path, [out] [retval] void** result); + HRESULT CreatePathKeyFrameAnimation([out] [retval] void** result); + HRESULT CreateRectangleGeometry([out] [retval] void** result); + HRESULT CreateRoundedRectangleGeometry([out] [retval] void** result); + HRESULT CreateShapeVisual([out] [retval] void** result); + [overload("CreateSpriteShape")] HRESULT CreateSpriteShape([out] [retval] void** result); + [overload("CreateSpriteShape")] HRESULT CreateSpriteShapeWithGeometry([in] void* geometry, [out] [retval] void** result); + HRESULT CreateViewBox([out] [retval] void** result); + HRESULT RequestCommitAsync([out] [retval] IAsyncAction** operation); +} diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index fcd92acd57..c603128a18 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -454,9 +454,14 @@ namespace Avalonia.Win32 if (customRendererFactory != null) return customRendererFactory.Create(root, loop); - return Win32Platform.UseDeferredRendering ? - (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) : - new ImmediateRenderer(root); + return Win32Platform.UseDeferredRendering + ? _isUsingComposition + ? new DeferredRenderer(root, loop) + { + RenderOnlyOnRenderThread = true + } + : (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) + : new ImmediateRenderer(root); } public void Resize(Size value)