diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index aaf84068f5..590fbdc538 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -1,4 +1,4 @@ - + @@ -19,6 +19,24 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.Platform.IRenderTarget.RenderTargetSceneInfo.#ctor(Avalonia.PixelSize,System.Double,Avalonia.Size) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Platform.IRenderTarget.RenderTargetSceneInfo.#ctor(Avalonia.PixelSize,System.Double) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Platform.IRenderTarget.RenderTargetSceneInfo.Deconstruct(Avalonia.PixelSize@,System.Double@,Avalonia.Size@) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0002 M:Avalonia.Platform.IRenderTarget.RenderTargetSceneInfo.Deconstruct(Avalonia.PixelSize@,System.Double@) @@ -49,6 +67,24 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.Platform.IRenderTarget.RenderTargetSceneInfo.#ctor(Avalonia.PixelSize,System.Double,Avalonia.Size) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Platform.IRenderTarget.RenderTargetSceneInfo.#ctor(Avalonia.PixelSize,System.Double) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Platform.IRenderTarget.RenderTargetSceneInfo.Deconstruct(Avalonia.PixelSize@,System.Double@,Avalonia.Size@) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0002 M:Avalonia.Platform.IRenderTarget.RenderTargetSceneInfo.Deconstruct(Avalonia.PixelSize@,System.Double@) @@ -61,4 +97,4 @@ baseline/Avalonia/lib/net8.0/Avalonia.OpenGL.dll current/Avalonia/lib/net8.0/Avalonia.OpenGL.dll - \ No newline at end of file + diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index c3e68ab735..058cc14278 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.Base/Platform/IRenderTarget.cs b/src/Avalonia.Base/Platform/IRenderTarget.cs index b9da6a35b6..0bc84c7a14 100644 --- a/src/Avalonia.Base/Platform/IRenderTarget.cs +++ b/src/Avalonia.Base/Platform/IRenderTarget.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Metadata; +using Avalonia.Rendering.Composition; namespace Avalonia.Platform { @@ -32,9 +33,9 @@ namespace Avalonia.Platform /// PlatformRenderTargetState PlatformRenderTargetState => PlatformRenderTargetState.Ready; - public record struct RenderTargetSceneInfo(PixelSize Size, double Scaling, Size LogicalSize) + public record struct RenderTargetSceneInfo(PixelSize Size, double Scaling, Size LogicalSize, CompositionTransparencyLevel TransparencyLevel) { - public RenderTargetSceneInfo(PixelSize size, double scaling) : this(size, scaling, size.ToSize(scaling)) + public RenderTargetSceneInfo(PixelSize size, double scaling, CompositionTransparencyLevel transparencyLevel) : this(size, scaling, size.ToSize(scaling), transparencyLevel) { } } diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTransparencyLevel.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTransparencyLevel.cs new file mode 100644 index 0000000000..fd2abd75ee --- /dev/null +++ b/src/Avalonia.Base/Rendering/Composition/CompositionTransparencyLevel.cs @@ -0,0 +1,10 @@ +namespace Avalonia.Rendering.Composition; + +public enum CompositionTransparencyLevel : byte +{ + None = 0, + Transparent = 1, + Blur = 2, + AcrylicBlur = 3, + Mica = 4 +} diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs index 3ce37aec28..72d1eac278 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs @@ -205,7 +205,7 @@ namespace Avalonia.Rendering.Composition.Server try { renderTargetContext = - _renderTarget.CreateDrawingContext(new(PixelSize, Scaling, Size), out properties); + _renderTarget.CreateDrawingContext(new(PixelSize, Scaling, Size, TransparencyLevel), out properties); } catch (RenderTargetNotReadyException) { diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml index 207f80647b..442d0c9197 100644 --- a/src/Avalonia.Base/composition-schema.xml +++ b/src/Avalonia.Base/composition-schema.xml @@ -59,6 +59,7 @@ + diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 7a9cfa4abb..c30047fe7a 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -218,7 +218,8 @@ namespace Avalonia.Controls _scaling = LayoutHelper.ValidateScaling(impl.RenderScaling); _actualTransparencyLevel = PlatformImpl.TransparencyLevel; - + _source.Renderer.CompositionTarget.TransparencyLevel = + ToCompositionTransparencyLevel(_actualTransparencyLevel); _accessKeyHandler = TryGetService(dependencyResolver); @@ -731,6 +732,8 @@ namespace Avalonia.Controls } ActualTransparencyLevel = transparencyLevel; + Renderer.CompositionTarget.TransparencyLevel = + ToCompositionTransparencyLevel(transparencyLevel); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -833,5 +836,18 @@ namespace Avalonia.Controls { // Do nothing becuase TopLevel should't apply MirrorTransform on himself. } + + private static CompositionTransparencyLevel ToCompositionTransparencyLevel(WindowTransparencyLevel level) + { + if (level == WindowTransparencyLevel.Transparent) + return CompositionTransparencyLevel.Transparent; + if (level == WindowTransparencyLevel.Blur) + return CompositionTransparencyLevel.Blur; + if (level == WindowTransparencyLevel.AcrylicBlur) + return CompositionTransparencyLevel.AcrylicBlur; + if (level == WindowTransparencyLevel.Mica) + return CompositionTransparencyLevel.Mica; + return CompositionTransparencyLevel.None; + } } } diff --git a/src/Avalonia.Wayland/Server/Persistent/WaylandBitmapCursor.cs b/src/Avalonia.Wayland/Server/Persistent/WaylandBitmapCursor.cs index 716dd6ae99..e46d9d0d15 100644 --- a/src/Avalonia.Wayland/Server/Persistent/WaylandBitmapCursor.cs +++ b/src/Avalonia.Wayland/Server/Persistent/WaylandBitmapCursor.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Runtime.InteropServices; using Avalonia; using Avalonia.Platform; +using Avalonia.Rendering.Composition; using Avalonia.Wayland.Server.Interop; using Avalonia.Wayland.Server.Transient; using Avalonia.Wayland.Server.Transient.Rendering; @@ -75,7 +76,7 @@ sealed class WaylandBitmapCursor : WaylandCursor, IPersistentWaylandObject, IWay var framebuffer = new WaylandFramebuffer(this); using var renderTarget = framebuffer.CreateFramebufferRenderTarget(); - using var locked = renderTarget.Lock(new IRenderTarget.RenderTargetSceneInfo(_size, 1), out _); + using var locked = renderTarget.Lock(new IRenderTarget.RenderTargetSceneInfo(_size, 1, CompositionTransparencyLevel.None), out _); // The locked framebuffer is Bgra8888 premultiplied with stride == Width * 4, matching the // packed pixel data we captured on the UI thread. Disposing the lock attaches + commits. Marshal.Copy(_pixels, 0, locked.Address, _pixels.Length); diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs index dabf1b6bfd..e0292c44e6 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs @@ -8,6 +8,7 @@ using Avalonia.OpenGL; using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Surfaces; using Avalonia.Platform; +using Avalonia.Rendering.Composition; using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods; using static Avalonia.LinuxFramebuffer.Output.LibDrm; @@ -377,7 +378,7 @@ namespace Avalonia.LinuxFramebuffer.Output { //Go through two cycles of buffer swapping (there are render artifacts otherwise) for (var c = 0; c < 2; c++) - using (CreateGlRenderTarget().BeginDraw(new IRenderTarget.RenderTargetSceneInfo(PixelSize, 1))) + using (CreateGlRenderTarget().BeginDraw(new IRenderTarget.RenderTargetSceneInfo(PixelSize, 1, CompositionTransparencyLevel.None))) { _deferredContext.GlInterface.ClearColor(initialBufferSwappingColorR, initialBufferSwappingColorG, initialBufferSwappingColorB, initialBufferSwappingColorA); diff --git a/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs b/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs index c5002510f9..8712cfc2b5 100644 --- a/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/RenderTargetBitmapImpl.cs @@ -1,5 +1,6 @@ using Avalonia.Platform.Surfaces; using Avalonia.Platform; +using Avalonia.Rendering.Composition; using SkiaSharp; namespace Avalonia.Skia; @@ -20,7 +21,7 @@ internal class RenderTargetBitmapImpl : WriteableBitmapImpl, public IDrawingContextImpl CreateDrawingContext() { return _renderTarget.CreateDrawingContext(new IRenderTarget.RenderTargetSceneInfo( - PixelSize, Dpi.X / 96.0), out _); + PixelSize, Dpi.X / 96.0, CompositionTransparencyLevel.None), out _); } diff --git a/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs b/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs index 8935e8bcbf..4689ad9bc4 100644 --- a/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs +++ b/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs @@ -1,5 +1,4 @@ using System; -using System.Numerics; using System.Threading; using Avalonia.OpenGL.Egl; using Avalonia.Reactive; diff --git a/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs index 9c6f5707bf..e91ca786f7 100644 --- a/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs +++ b/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindowSurface.cs @@ -1,14 +1,17 @@ using System; -using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; + using Avalonia.OpenGL.Egl; using Avalonia.Platform; +using Avalonia.Rendering.Composition; using Avalonia.Win32.DirectX; using Avalonia.Win32.Interop; + using MicroCom.Runtime; namespace Avalonia.Win32.DComposition; -internal class DirectCompositedWindowSurface : IDirect3D11TexturePlatformSurface, IDisposable, ICompositionEffectsSurface +internal class DirectCompositedWindowSurface : IDirect3D11TexturePlatformSurface, IDirect3D11TexturePlatformSurface2, IDisposable, ICompositionEffectsSurface { private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; private readonly DirectCompositionShared _shared; @@ -21,7 +24,12 @@ internal class DirectCompositedWindowSurface : IDirect3D11TexturePlatformSurface _info = info; } - public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice) + IDirect3D11TextureRenderTarget IDirect3D11TexturePlatformSurface.CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice) + { + return (IDirect3D11TextureRenderTarget) CreateRenderTarget(context, d3dDevice); + } + + public IDirect3D11TextureRenderTarget2 CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice) { _window ??= new DirectCompositedWindow(_info, _shared); SetBlur(_blurEffect); @@ -45,16 +53,18 @@ internal class DirectCompositedWindowSurface : IDirect3D11TexturePlatformSurface } } -internal class DirectCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget +internal class DirectCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget, IDirect3D11TextureRenderTarget2 { 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,24 +73,44 @@ 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); - } + [MemberNotNull(nameof(_surface))] + private void CreateSurface(in IRenderTarget.RenderTargetSceneInfo sceneInfo) + { + using var surfaceFactory = _shared.Device.CreateSurfaceFactory(_d3dDevice); + + bool isTransparency = sceneInfo.TransparencyLevel != CompositionTransparencyLevel.None; + var surfaceSize = sceneInfo.Size; + + var alphaMode = isTransparency ? + DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_PREMULTIPLIED : + DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_IGNORE; + + _surface = surfaceFactory.CreateVirtualSurface((uint)surfaceSize.Width, (uint)surfaceSize.Height, DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM, alphaMode); + + _isSurfaceSupportTransparency = isTransparency; + _size = surfaceSize; } public void Dispose() { - _surface.Dispose(); + _surface?.Dispose(); _d3dDevice.Dispose(); } public PlatformRenderTargetState State => _context.IsLost || _lost ? PlatformRenderTargetState.Corrupted : PlatformRenderTargetState.Ready; - - public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw() + + IDirect3D11TextureRenderTargetRenderSession IDirect3D11TextureRenderTarget.BeginDraw() + { + var fallbackSceneInfo = new IRenderTarget.RenderTargetSceneInfo(_window.WindowInfo.Size, + _window.WindowInfo.Scaling, CompositionTransparencyLevel.None); + return BeginDraw(fallbackSceneInfo); + } + + public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw(IRenderTarget.RenderTargetSceneInfo sceneInfo) { if (State.IsCorrupted) throw new RenderTargetCorruptedException(); @@ -88,11 +118,19 @@ internal class DirectCompositedWindowRenderTarget : IDirect3D11TextureRenderTarg bool needsEndDraw = false; try { - var size = _window.WindowInfo.Size; - var scale = _window.WindowInfo.Scaling; + bool isTransparency = sceneInfo.TransparencyLevel != CompositionTransparencyLevel.None; + if (_surface is null || isTransparency != _isSurfaceSupportTransparency) + { + _surface?.Dispose(); + + CreateSurface(in sceneInfo); + } + + var size = sceneInfo.Size; + var scale = sceneInfo.Scaling; if (_size != size) { - _surface.Resize((ushort)size.Width, (ushort)size.Height); + _surface.Resize((uint)size.Width, (uint)size.Height); _size = size; } @@ -125,7 +163,7 @@ internal class DirectCompositedWindowRenderTarget : IDirect3D11TextureRenderTarg if (transaction != null) { if (needsEndDraw) - _surface.EndDraw(); + _surface?.EndDraw(); transaction.Dispose(); } } diff --git a/src/Windows/Avalonia.Win32/DirectX/IDirect3D11TexturePlatformSurface.cs b/src/Windows/Avalonia.Win32/DirectX/IDirect3D11TexturePlatformSurface.cs index ce077d6ce0..c0dda8592b 100644 --- a/src/Windows/Avalonia.Win32/DirectX/IDirect3D11TexturePlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/DirectX/IDirect3D11TexturePlatformSurface.cs @@ -1,4 +1,6 @@ using System; + +using Avalonia.Metadata; using Avalonia.OpenGL; using Avalonia.OpenGL.Surfaces; using Avalonia.Platform; @@ -11,6 +13,11 @@ public interface IDirect3D11TexturePlatformSurface : IPlatformRenderSurface public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext graphicsContext, IntPtr d3dDevice); } +[PrivateApi] +public interface IDirect3D11TexturePlatformSurface2 : IPlatformRenderSurface +{ + IDirect3D11TextureRenderTarget2 CreateRenderTarget(IPlatformGraphicsContext graphicsContext, IntPtr d3dDevice); +} public interface IDirect3D11TextureRenderTarget : IPlatformRenderSurfaceRenderTarget, IDisposable @@ -18,6 +25,12 @@ public interface IDirect3D11TextureRenderTarget : IPlatformRenderSurfaceRenderTa IDirect3D11TextureRenderTargetRenderSession BeginDraw(); } +[PrivateApi] +public interface IDirect3D11TextureRenderTarget2 : IPlatformRenderSurfaceRenderTarget, IDisposable +{ + IDirect3D11TextureRenderTargetRenderSession BeginDraw(IRenderTarget.RenderTargetSceneInfo sceneInfo); +} + public interface IDirect3D11TextureRenderTargetRenderSession : IDisposable { public IntPtr D3D11Texture2D { get; } diff --git a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleD3DTextureFeature.cs b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleD3DTextureFeature.cs index 60882fb62e..f2e2f270a4 100644 --- a/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleD3DTextureFeature.cs +++ b/src/Windows/Avalonia.Win32/OpenGl/Angle/AngleD3DTextureFeature.cs @@ -14,16 +14,16 @@ internal class AngleD3DTextureFeature : IGlPlatformSurfaceRenderTargetFactory context is EglContext { Display: AngleWin32EglDisplay { PlatformApi: AngleOptions.PlatformApi.DirectX11 } - } && surface is IDirect3D11TexturePlatformSurface; + } && surface is IDirect3D11TexturePlatformSurface2; private class RenderTargetWrapper : EglPlatformSurfaceRenderTargetBase { private readonly AngleWin32EglDisplay _angle; - private readonly IDirect3D11TextureRenderTarget _target; + private readonly IDirect3D11TextureRenderTarget2 _target; public RenderTargetWrapper(EglContext context, AngleWin32EglDisplay angle, - IDirect3D11TextureRenderTarget target) : base(context) + IDirect3D11TextureRenderTarget2 target) : base(context) { _angle = angle; _target = target; @@ -40,7 +40,7 @@ internal class AngleD3DTextureFeature : IGlPlatformSurfaceRenderTargetFactory { try { - session = _target.BeginDraw(); + session = _target.BeginDraw(sceneInfo); } catch (RenderTargetCorruptedException e) { @@ -91,7 +91,7 @@ internal class AngleD3DTextureFeature : IGlPlatformSurfaceRenderTargetFactory { var ctx = (EglContext)context; var angle = (AngleWin32EglDisplay)ctx.Display; - var textureSurface = (IDirect3D11TexturePlatformSurface)surface; + var textureSurface = (IDirect3D11TexturePlatformSurface2)surface; try { var target = textureSurface.CreateRenderTarget(context, angle.GetDirect3DDevice()); diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs index 96e5fae00b..179d0c2060 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs @@ -1,13 +1,15 @@ using System; +using System.Diagnostics.CodeAnalysis; using Avalonia.OpenGL.Egl; using Avalonia.Platform; +using Avalonia.Rendering.Composition; using Avalonia.Win32.DirectX; using Avalonia.Win32.Interop; using MicroCom.Runtime; namespace Avalonia.Win32.WinRT.Composition { - internal class WinUiCompositedWindowSurface : IDirect3D11TexturePlatformSurface, IDisposable, ICompositionEffectsSurface + internal class WinUiCompositedWindowSurface : IDirect3D11TexturePlatformSurface, IDirect3D11TexturePlatformSurface2, IDisposable, ICompositionEffectsSurface { private readonly WinUiCompositionShared _shared; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; @@ -20,7 +22,12 @@ namespace Avalonia.Win32.WinRT.Composition _info = info; } - public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice) + IDirect3D11TextureRenderTarget IDirect3D11TexturePlatformSurface.CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice) + { + return (IDirect3D11TextureRenderTarget) CreateRenderTarget(context, d3dDevice); + } + + public IDirect3D11TextureRenderTarget2 CreateRenderTarget(IPlatformGraphicsContext context, IntPtr d3dDevice) { var cornerRadius = AvaloniaLocator.Current.GetService() ?.WinUICompositionBackdropCornerRadius; @@ -52,7 +59,7 @@ namespace Avalonia.Win32.WinRT.Composition } } - internal class WinUiCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget + internal class WinUiCompositedWindowRenderTarget : IDirect3D11TextureRenderTarget, IDirect3D11TextureRenderTarget2 { private static readonly Guid IID_ID3D11Texture2D = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); @@ -63,11 +70,12 @@ 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 bool _isSurfaceSupportTransparency; + private ICompositionDrawingSurfaceInterop? _surfaceInterop; + private ICompositionDrawingSurface? _drawingSurface; public WinUiCompositedWindowRenderTarget(IPlatformGraphicsContext context, WinUiCompositedWindow window, IntPtr device, @@ -83,16 +91,9 @@ 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(); } catch { - _surface?.Dispose(); - _surfaceInterop?.Dispose(); - _drawingSurface?.Dispose(); _compositionDevice2?.Dispose(); _compositionDevice?.Dispose(); _interop?.Dispose(); @@ -104,9 +105,9 @@ namespace Avalonia.Win32.WinRT.Composition public void Dispose() { - _surface.Dispose(); - _surfaceInterop.Dispose(); - _drawingSurface.Dispose(); + _surface?.Dispose(); + _surfaceInterop?.Dispose(); + _drawingSurface?.Dispose(); _compositionDevice2.Dispose(); _compositionDevice.Dispose(); _interop.Dispose(); @@ -114,19 +115,61 @@ namespace Avalonia.Win32.WinRT.Composition _d3dDevice.Dispose(); } + [MemberNotNull(nameof(_drawingSurface), nameof(_surface), nameof(_surfaceInterop))] + private void CreateSurface(in IRenderTarget.RenderTargetSceneInfo sceneInfo) + { + bool isTransparency = sceneInfo.TransparencyLevel != CompositionTransparencyLevel.None; + var surfaceSize = sceneInfo.Size; + + // 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 = isTransparency ? DirectXAlphaMode.Premultiplied : DirectXAlphaMode.Ignore; + _drawingSurface = _compositionDevice2.CreateDrawingSurface2(new UnmanagedMethods.SIZE() + { + X = surfaceSize.Width, + Y = surfaceSize.Height, + }, + DirectXPixelFormat.B8G8R8A8UIntNormalized, alphaMode); + _surface = _drawingSurface.QueryInterface(); + _surfaceInterop = _drawingSurface.QueryInterface(); + + _isSurfaceSupportTransparency = isTransparency; + _size = surfaceSize; + } + public PlatformRenderTargetState State => _context.IsLost || _lost ? PlatformRenderTargetState.Corrupted : PlatformRenderTargetState.Ready; - public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw() + IDirect3D11TextureRenderTargetRenderSession IDirect3D11TextureRenderTarget.BeginDraw() + { + var fallbackSceneInfo = new IRenderTarget.RenderTargetSceneInfo(_window.WindowInfo.Size, + _window.WindowInfo.Scaling, CompositionTransparencyLevel.None); + return BeginDraw(fallbackSceneInfo); + } + + public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw( + IRenderTarget.RenderTargetSceneInfo sceneInfo) { if (State.IsCorrupted) throw new RenderTargetCorruptedException(); var transaction = _window.BeginTransaction(); + bool needsEndDraw = false; try { - var size = _window.WindowInfo.Size; - var scale = _window.WindowInfo.Scaling; + bool isTransparency = sceneInfo.TransparencyLevel != CompositionTransparencyLevel.None; + + if (_surface is null || _surfaceInterop is null || _drawingSurface is null || _isSurfaceSupportTransparency != isTransparency) + { + // 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(in sceneInfo); + } + + var size = sceneInfo.Size; + var scale = sceneInfo.Scaling; _window.ResizeIfNeeded(size); _window.SetSurface(_surface); @@ -165,7 +208,7 @@ namespace Avalonia.Win32.WinRT.Composition if (transaction != null) { if (needsEndDraw) - _surfaceInterop.EndDraw(); + _surfaceInterop?.EndDraw(); transaction.Dispose(); } } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 2c778c86c8..a858761a64 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -373,6 +373,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)) diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs index cd5713b9a2..1db7556b46 100644 --- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs +++ b/tests/Avalonia.RenderTests/Media/BitmapTests.cs @@ -8,6 +8,7 @@ using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Platform.Surfaces; +using Avalonia.Rendering.Composition; using Xunit; using Path = System.IO.Path; @@ -71,7 +72,7 @@ namespace Avalonia.Skia.RenderTests var r = AvaloniaLocator.Current.GetRequiredService(); using(var cpuContext = r.CreateBackendContext(null)) using (var target = cpuContext.CreateRenderTarget(new IPlatformRenderSurface[] { fb })) - using (var ctx = target.CreateDrawingContext(new IRenderTarget.RenderTargetSceneInfo(fb.Size, 1), out _)) + using (var ctx = target.CreateDrawingContext(new IRenderTarget.RenderTargetSceneInfo(fb.Size, 1, CompositionTransparencyLevel.None), out _)) { ctx.Clear(Colors.Transparent); ctx.PushOpacity(0.8, new Rect(0, 0, 80, 80));