diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index e1dbd20b07..e16791b894 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -43,6 +43,8 @@ + + diff --git a/samples/RenderDemo/MainWindow.xaml.cs b/samples/RenderDemo/MainWindow.xaml.cs index a2f3f0eac8..96e21f0d2f 100644 --- a/samples/RenderDemo/MainWindow.xaml.cs +++ b/samples/RenderDemo/MainWindow.xaml.cs @@ -1,6 +1,7 @@ using System; using System.Linq.Expressions; using Avalonia.Controls; +using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.Rendering; using RenderDemo.ViewModels; @@ -37,5 +38,15 @@ namespace RenderDemo { AvaloniaXamlLoader.Load(this); } + + private void SetNotTransparencyMenuItem_OnClick(object? sender, RoutedEventArgs e) + { + TransparencyLevelHint = [WindowTransparencyLevel.None]; + } + + private void SetTransparencyMenuItem_OnClick(object? sender, RoutedEventArgs e) + { + TransparencyLevelHint = [WindowTransparencyLevel.Transparent]; + } } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index ceb9590564..6b83573a60 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -217,9 +217,9 @@ namespace Avalonia.Controls _scaling = ValidateScaling(impl.RenderScaling); _actualTransparencyLevel = PlatformImpl.TransparencyLevel; + - - + _accessKeyHandler = TryGetService(dependencyResolver); _inputManager = TryGetService(dependencyResolver); diff --git a/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs b/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs index 8935e8bcbf..de8caa85cc 100644 --- a/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs +++ b/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs @@ -1,6 +1,6 @@ using System; -using System.Numerics; using System.Threading; +using Avalonia.Controls; using Avalonia.OpenGL.Egl; using Avalonia.Reactive; using MicroCom.Runtime; @@ -51,4 +51,13 @@ internal class DirectCompositedWindow : IDisposable Monitor.Exit(_shared.SyncRoot); }); } + + public bool IsTransparency => _transparencyLevel != WindowTransparencyLevel.None; + + public void SetTransparencyLevel(WindowTransparencyLevel transparencyLevel) + { + _transparencyLevel = transparencyLevel; + } + + private WindowTransparencyLevel _transparencyLevel; } diff --git a/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs index d0651e08ee..15e0f60c3c 100644 --- a/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs +++ b/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs @@ -1,9 +1,15 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; + +using Avalonia.Controls; +using Avalonia.Controls.Shapes; using Avalonia.OpenGL.Egl; using Avalonia.Platform; using Avalonia.Win32.DirectX; using Avalonia.Win32.Interop; +using Avalonia.Win32.WinRT; + using MicroCom.Runtime; namespace Avalonia.Win32.DComposition; @@ -25,6 +31,7 @@ internal class DirectCompositedWindowSurface : IDirect3D11TexturePlatformSurface { _window ??= new DirectCompositedWindow(_info, _shared); SetBlur(_blurEffect); + _window.SetTransparencyLevel(_windowTransparencyLevel); return new DirectCompositedWindowRenderTarget(context, d3dDevice, _shared, _window); } @@ -43,6 +50,14 @@ internal class DirectCompositedWindowSurface : IDirect3D11TexturePlatformSurface _blurEffect = enable; // _window?.SetBlur(enable); } + + public void SetTransparencyLevel(WindowTransparencyLevel transparencyLevel) + { + _windowTransparencyLevel = transparencyLevel; + _window?.SetTransparencyLevel(transparencyLevel); + } + + private WindowTransparencyLevel _windowTransparencyLevel; } internal class DirectCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget @@ -50,11 +65,13 @@ internal class DirectCompositedWindowRenderTarget : IDirect3D11TextureRenderTarg private static readonly Guid IID_ID3D11Texture2D = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); private readonly IPlatformGraphicsContext _context; + private readonly DirectCompositionShared _shared; private readonly DirectCompositedWindow _window; - private readonly IDCompositionVirtualSurface _surface; + private IDCompositionVirtualSurface _surface; private bool _lost; private PixelSize _size; private readonly IUnknown _d3dDevice; + private bool _isSurfaceSupportTransparency; public DirectCompositedWindowRenderTarget( IPlatformGraphicsContext context, IntPtr d3dDevice, @@ -63,13 +80,25 @@ internal class DirectCompositedWindowRenderTarget : IDirect3D11TextureRenderTarg _d3dDevice = MicroComRuntime.CreateProxyFor(d3dDevice, false).CloneReference(); _context = context; + _shared = shared; _window = window; - using (var surfaceFactory = shared.Device.CreateSurfaceFactory(_d3dDevice)) - { - _surface = surfaceFactory.CreateVirtualSurface(1, 1, DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM, - DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_PREMULTIPLIED); - } + CreateSurface(window); + } + + [MemberNotNull(nameof(_surface))] + private void CreateSurface(DirectCompositedWindow window) + { + using var surfaceFactory = _shared.Device.CreateSurfaceFactory(_d3dDevice); + + const uint initialSize = 1; + var alphaMode = window.IsTransparency ? + DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_PREMULTIPLIED : + DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_IGNORE; + _isSurfaceSupportTransparency = window.IsTransparency; + + _surface = surfaceFactory.CreateVirtualSurface(initialSize, initialSize, DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM, + alphaMode); } public void Dispose() @@ -88,9 +117,19 @@ internal class DirectCompositedWindowRenderTarget : IDirect3D11TextureRenderTarg bool needsEndDraw = false; try { + bool forceResize = false; + if (_window.IsTransparency != _isSurfaceSupportTransparency) + { + _surface.Dispose(); + + CreateSurface(_window); + + forceResize = true; + } + var size = _window.WindowInfo.Size; var scale = _window.WindowInfo.Scaling; - if (_size != size) + if (forceResize || _size != size) { _surface.Resize((ushort)size.Width, (ushort)size.Height); _size = size; diff --git a/src/Windows/Avalonia.Win32/IBlurHost.cs b/src/Windows/Avalonia.Win32/IBlurHost.cs index fcaf58eaac..1b5dd9c9b4 100644 --- a/src/Windows/Avalonia.Win32/IBlurHost.cs +++ b/src/Windows/Avalonia.Win32/IBlurHost.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Win32; +using Avalonia.Controls; + +namespace Avalonia.Win32; internal enum BlurEffect { @@ -14,4 +16,5 @@ internal interface ICompositionEffectsSurface bool IsBlurSupported(BlurEffect effect); void SetBlur(BlurEffect enable); + void SetTransparencyLevel(WindowTransparencyLevel transparencyLevel); } diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs index d720e525d3..4bade8530a 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs @@ -1,6 +1,7 @@ using System; using System.Numerics; using System.Threading; +using Avalonia.Controls; using Avalonia.OpenGL.Egl; using Avalonia.Reactive; using MicroCom.Runtime; @@ -112,4 +113,13 @@ internal class WinUiCompositedWindow : IDisposable } } } + + public bool IsTransparency => _transparencyLevel != WindowTransparencyLevel.None; + + public void SetTransparencyLevel(WindowTransparencyLevel transparencyLevel) + { + _transparencyLevel = transparencyLevel; + } + + private WindowTransparencyLevel _transparencyLevel; } diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs index 2addbc6524..ee4c326fd6 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics.CodeAnalysis; +using Avalonia.Controls; using Avalonia.OpenGL.Egl; using Avalonia.Platform; using Avalonia.Win32.DirectX; @@ -26,6 +28,7 @@ namespace Avalonia.Win32.WinRT.Composition ?.WinUICompositionBackdropCornerRadius; _window ??= new WinUiCompositedWindow(_info, _shared, cornerRadius); _window.SetBlur(_blurEffect); + _window.SetTransparencyLevel(_windowTransparencyLevel); return new WinUiCompositedWindowRenderTarget(context, _window, d3dDevice, _shared.Compositor); } @@ -50,6 +53,14 @@ namespace Avalonia.Win32.WinRT.Composition _blurEffect = enable; _window?.SetBlur(enable); } + + public void SetTransparencyLevel(WindowTransparencyLevel transparencyLevel) + { + _windowTransparencyLevel = transparencyLevel; + _window?.SetTransparencyLevel(transparencyLevel); + } + + private WindowTransparencyLevel _windowTransparencyLevel; } internal class WinUiCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget @@ -63,11 +74,11 @@ namespace Avalonia.Win32.WinRT.Composition private readonly ICompositorInterop _interop; private readonly ICompositionGraphicsDevice _compositionDevice; private readonly ICompositionGraphicsDevice2 _compositionDevice2; - private readonly ICompositionSurface _surface; + private ICompositionSurface _surface; private PixelSize _size; private bool _lost; - private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; - private readonly ICompositionDrawingSurface _drawingSurface; + private ICompositionDrawingSurfaceInterop _surfaceInterop; + private ICompositionDrawingSurface _drawingSurface; public WinUiCompositedWindowRenderTarget(IPlatformGraphicsContext context, WinUiCompositedWindow window, IntPtr device, @@ -83,10 +94,8 @@ namespace Avalonia.Win32.WinRT.Composition _interop = compositor.QueryInterface(); _compositionDevice = _interop.CreateGraphicsDevice(_d3dDevice); _compositionDevice2 = _compositionDevice.QueryInterface(); - _drawingSurface = _compositionDevice2.CreateDrawingSurface2(new UnmanagedMethods.SIZE(), - DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); - _surface = _drawingSurface.QueryInterface(); - _surfaceInterop = _drawingSurface.QueryInterface(); + + CreateSurface(window); } catch { @@ -102,6 +111,17 @@ namespace Avalonia.Win32.WinRT.Composition } } + [MemberNotNull(nameof(_drawingSurface), nameof(_surface), nameof(_surfaceInterop))] + private void CreateSurface(WinUiCompositedWindow window) + { + // Do not use Premultiplied when the window is not Transparency. Because the Premultiplied AlphaMode will increase the performance loss of DWM. See https://github.com/AvaloniaUI/Avalonia/issues/20643 + var alphaMode = window.IsTransparency ? DirectXAlphaMode.Premultiplied : DirectXAlphaMode.Ignore; + _drawingSurface = _compositionDevice2.CreateDrawingSurface2(new UnmanagedMethods.SIZE(), + DirectXPixelFormat.B8G8R8A8UIntNormalized, alphaMode); + _surface = _drawingSurface.QueryInterface(); + _surfaceInterop = _drawingSurface.QueryInterface(); + } + public void Dispose() { _surface.Dispose(); @@ -121,9 +141,25 @@ namespace Avalonia.Win32.WinRT.Composition if (IsCorrupted) throw new RenderTargetCorruptedException(); var transaction = _window.BeginTransaction(); + bool needsEndDraw = false; try { + bool forceResize = false; + var supportTransparency = _drawingSurface.AlphaMode == DirectXAlphaMode.Premultiplied; + if (_window.IsTransparency != supportTransparency) + { + // Re-create the surface with correct alpha mode if the transparency support is not correct. This can happen when the transparency level is changed. + _surface.Dispose(); + _surfaceInterop.Dispose(); + _drawingSurface.Dispose(); + + CreateSurface(_window); + + // The _drawingSurface.Size != _size, so that require force resize to update the size of surface. + forceResize = true; + } + var size = _window.WindowInfo.Size; var scale = _window.WindowInfo.Scaling; _window.ResizeIfNeeded(size); @@ -133,7 +169,7 @@ namespace Avalonia.Win32.WinRT.Composition UnmanagedMethods.POINT off; try { - if (_size != size) + if (forceResize || _size != size) { _surfaceInterop.Resize(new UnmanagedMethods.POINT { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 92564d296d..cd20a5dee5 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + using Avalonia.Collections.Pooled; using Avalonia.Controls; using Avalonia.Controls.Platform; @@ -12,22 +14,24 @@ using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Input.Raw; using Avalonia.Input.TextInput; +using Avalonia.Logging; using Avalonia.OpenGL.Egl; using Avalonia.Platform; using Avalonia.Platform.Surfaces; using Avalonia.Platform.Storage; +using Avalonia.Platform.Storage.FileIO; using Avalonia.Rendering.Composition; +using Avalonia.Threading; using Avalonia.Win32.DirectX; using Avalonia.Win32.Input; using Avalonia.Win32.Interop; using Avalonia.Win32.OpenGl; using Avalonia.Win32.OpenGl.Angle; using Avalonia.Win32.WinRT.Composition; -using static Avalonia.Win32.Interop.UnmanagedMethods; -using Avalonia.Platform.Storage.FileIO; -using Avalonia.Threading; + using static Avalonia.Controls.Win32Properties; -using Avalonia.Logging; +using static Avalonia.Rendering.Composition.Animations.PropertySetSnapshot; +using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 { @@ -180,6 +184,7 @@ namespace Avalonia.Win32 _nativeControlHost = new Win32NativeControlHost(this, !UseRedirectionBitmap); _defaultTransparencyLevel = UseRedirectionBitmap ? WindowTransparencyLevel.None : WindowTransparencyLevel.Transparent; _transparencyLevel = _defaultTransparencyLevel; + SetTransparencyLevel(_transparencyLevel); lock (s_instances) s_instances.Add(this); @@ -321,6 +326,7 @@ namespace Avalonia.Win32 if (_transparencyLevel != value) { _transparencyLevel = value; + SetTransparencyLevel(value); TransparencyLevelChanged?.Invoke(value); } } @@ -372,6 +378,13 @@ namespace Avalonia.Win32 public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) { + if (transparencyLevels.Count == 1 && transparencyLevels[0] == WindowTransparencyLevel.None) + { + // Explicitly disable transparency. Ignore the UseRedirectionBitmap property. + TransparencyLevel = WindowTransparencyLevel.None; + return; + } + foreach (var level in transparencyLevels) { if (!IsSupported(level)) @@ -506,6 +519,11 @@ namespace Avalonia.Win32 return result == 0; } + private void SetTransparencyLevel(WindowTransparencyLevel transparencyLevel) + { + CompositionEffectsSurface?.SetTransparencyLevel(transparencyLevel); + } + public IPlatformRenderSurface[] Surfaces => _glSurface is null ? [(IPlatformRenderSurface)Handle, _framebuffer] :