diff --git a/Directory.Build.props b/Directory.Build.props index 117c0964d20..6cb7a683303 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,5 +9,7 @@ false False 12 + + false diff --git a/dirs.proj b/dirs.proj index 28a91c8b684..3a9f2ed9fce 100644 --- a/dirs.proj +++ b/dirs.proj @@ -17,9 +17,10 @@ - + + diff --git a/global.json b/global.json index 28c43eff46d..2ff28310526 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.404", + "rollForward": "latestFeature" }, "msbuild-sdks": { diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index c6942e430c3..1d781e047b7 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -124,14 +124,14 @@ DotNetTestSettings ApplySetting(DotNetTestSettings c, Configure !Parameters.SkipPreviewer) .Executes(() => { - var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp"; - - NpmTasks.NpmInstall(c => c - .SetProcessWorkingDirectory(webappDir) - .SetProcessArgumentConfigurator(a => a.Add("--silent"))); - NpmTasks.NpmRun(c => c - .SetProcessWorkingDirectory(webappDir) - .SetCommand("dist")); + //var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp"; + + //NpmTasks.NpmInstall(c => c + // .SetProcessWorkingDirectory(webappDir) + // .SetProcessArgumentConfigurator(a => a.Add("--silent"))); + //NpmTasks.NpmRun(c => c + // .SetProcessWorkingDirectory(webappDir) + // .SetCommand("dist")); }); Target CompileNative => _ => _ diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index 41e075a64c9..2bda94c4352 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -57,7 +57,7 @@ public class BuildParameters public bool IsNuGetRelease { get; } public bool PublishTestResults { get; } public string Version { get; set; } - public const string LocalBuildVersion = "9999.0.0-localbuild"; + public const string LocalBuildVersion = "11.3.6-hotfix.4"; public bool IsPackingToLocalCache { get; private set; } public AbsolutePath ArtifactsDir { get; } diff --git a/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj b/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj index 314632e0db5..77e96cb8945 100644 --- a/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj +++ b/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj @@ -1,8 +1,9 @@ - + $(AvsCurrentTargetFramework);$(AvsCurrentBrowserTargetFramework) enable true + true diff --git a/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs b/src/Windows/Avalonia.Win32/DComposition/DirectCompositedWindow.cs index 8935e8bcbff..de8caa85ccd 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 @@ public IDisposable BeginTransaction() 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 d0651e08eec..15e0f60c3ce 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 @@ public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContex { _window ??= new DirectCompositedWindow(_info, _shared); SetBlur(_blurEffect); + _window.SetTransparencyLevel(_windowTransparencyLevel); return new DirectCompositedWindowRenderTarget(context, d3dDevice, _shared, _window); } @@ -43,6 +50,14 @@ public void SetBlur(BlurEffect enable) _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 @@ public DirectCompositedWindowRenderTarget( _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 @@ public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw() 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/DirectX/DirectXEnums.cs b/src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs index 1357e8aa633..ac6f0678d72 100644 --- a/src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs +++ b/src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs @@ -213,6 +213,14 @@ internal enum DXGI_ERROR : uint DXGI_ERROR_WAS_STILL_DRAWING = 0x887A000A } + [Flags] + internal enum DXGI_MWA : uint + { + DXGI_MWA_NO_WINDOW_CHANGES = 1, + DXGI_MWA_NO_ALT_ENTER = 2, + DXGI_MWA_NO_PRINT_SCREEN = 4 + } + internal static class DxgiErrorExtensions { public static bool IsDeviceLostError(this DXGI_ERROR error) diff --git a/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs b/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs index e7007c34481..fc18c9d1c89 100644 --- a/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs +++ b/src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs @@ -1,13 +1,20 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Avalonia.Logging; using Avalonia.OpenGL.Egl; using Avalonia.Rendering; -using static Avalonia.Win32.Interop.UnmanagedMethods; -using static Avalonia.Win32.DirectX.DirectXUnmanagedMethods; + using MicroCom.Runtime; +using Windows.Win32; +using Windows.Win32.Graphics.Gdi; + +using static Avalonia.Win32.DirectX.DirectXUnmanagedMethods; +using static Avalonia.Win32.Interop.UnmanagedMethods; + namespace Avalonia.Win32.DirectX { internal unsafe class DxgiConnection : IRenderTimer, IWindowsSurfaceFactory @@ -111,6 +118,8 @@ private void GetBestOutputToVWaitOn() ushort adapterIndex = 0; + Dictionary monitorFrequencies = GetAllMonitorFrequencies(); + // this looks odd, but that's just how one enumerates adapters in DXGI while (fact.EnumAdapters(adapterIndex, &adapterPointer) == 0) { @@ -122,8 +131,12 @@ private void GetBestOutputToVWaitOn() using var output = MicroComRuntime.CreateProxyFor(outputPointer, true); DXGI_OUTPUT_DESC outputDesc = output.Desc; - var screen = Win32Platform.Instance.Screen.ScreenFromHMonitor((IntPtr)outputDesc.Monitor.Value); - var frequency = screen?.Frequency ?? highestRefreshRate; + var hMonitor = new HMONITOR((nint) outputDesc.Monitor.Value); + + var frequency = + monitorFrequencies.TryGetValue(hMonitor, out uint frequencyValue) ? + frequencyValue : + highestRefreshRate; if (highestRefreshRate < frequency) { @@ -145,6 +158,33 @@ private void GetBestOutputToVWaitOn() } + private unsafe Dictionary GetAllMonitorFrequencies() + { + var monitorHandlers = ScreenImpl.GetAllDisplayMonitorHandlers(); + var dictionary = new Dictionary(monitorHandlers.Count); + + foreach (var monitorHandler in monitorHandlers) + { + var info = MONITORINFOEX.Create(); + var hMonitor = new HMONITOR(monitorHandler); + PInvoke.GetMonitorInfo(hMonitor, (MONITORINFO*)&info); + + var deviceMode = new DEVMODEW + { + dmFields = DEVMODE_FIELD_FLAGS.DM_DISPLAYORIENTATION | DEVMODE_FIELD_FLAGS.DM_DISPLAYFREQUENCY, + dmSize = (ushort)Marshal.SizeOf() + }; + PInvoke.EnumDisplaySettings(info.szDevice.ToString(), ENUM_DISPLAY_SETTINGS_MODE.ENUM_CURRENT_SETTINGS, + ref deviceMode); + + var frequency = deviceMode.dmDisplayFrequency; + + dictionary[hMonitor] = frequency; + } + + return dictionary; + } + // Used the windows composition as a blueprint for this startup/creation private static bool TryCreateAndRegisterCore() { @@ -168,12 +208,22 @@ private static bool TryCreateAndRegisterCore() }); thread.IsBackground = true; thread.SetApartmentState(System.Threading.ApartmentState.STA); + thread.Name = "DxgiRenderTimerLoop"; thread.Start(); // block until return tcs.Task.Result; } - public bool RequiresNoRedirectionBitmap => false; + public bool RequiresNoRedirectionBitmap => IsTransparencySupported() + ? true + : false; + public object CreateSurface(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) => new DxgiSwapchainWindow(this, info); + + public static bool IsTransparencySupported() + { + // We can use the DirectComposited+CreateSwapChainForComposition to create the Transparency window. + return Win32Platform.WindowsVersion >= PlatformConstants.Windows8_1; + } } } diff --git a/src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs b/src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs index 513aba4f200..b0151aae1e5 100644 --- a/src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs +++ b/src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs @@ -1,8 +1,17 @@ using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; + +using Avalonia.Controls; using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Surfaces; +using Avalonia.Platform; +using Avalonia.Win32.DComposition; +using Avalonia.Win32.Interop; using Avalonia.Win32.OpenGl.Angle; + using MicroCom.Runtime; + using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32.DirectX @@ -17,17 +26,21 @@ internal unsafe class DxgiRenderTarget : EglPlatformSurfaceRenderTargetBase private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _window; private readonly DxgiConnection _connection; private readonly IDXGIDevice? _dxgiDevice; - private readonly IDXGIFactory2? _dxgiFactory; - private readonly IDXGISwapChain1? _swapChain; - private readonly uint _flagsUsed; + private readonly IDXGIFactory2 _dxgiFactory; + private IDXGISwapChain1? _swapChain; + private DXGI_SWAP_CHAIN_FLAG _dxgiSwapChainDescFlagsUsed; + private const uint SwapChainDescBufferCount = 2; private IUnknown? _renderTexture; - private RECT _clientRect; + private PixelSize _size; + private EglSurface? _surface; - public DxgiRenderTarget(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo window, EglContext context, DxgiConnection connection) : base(context) + public DxgiRenderTarget(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo window, EglContext context, + DxgiConnection connection, WindowTransparencyLevel transparencyLevel) : base(context) { _window = window; _connection = connection; + _transparencyLevel = transparencyLevel; // the D3D device is expected to at least be an ID3D11Device // but how do I wrap an IntPtr as a managed IUnknown now? Like this. @@ -42,6 +55,35 @@ public DxgiRenderTarget(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo win _dxgiFactory = MicroComRuntime.CreateProxyFor(adapterPointer.GetParent(&factoryGuid), true); } + CreateSurface(window.Size); + + _dxgiFactory.MakeWindowAssociation(window.Handle, (uint)(DXGI_MWA.DXGI_MWA_NO_ALT_ENTER | DXGI_MWA.DXGI_MWA_NO_PRINT_SCREEN)); + } + + private IDCompositionDesktopDevice? _compositionDesktopDevice; + private IDCompositionTarget? _compositionTarget; + + [MemberNotNull(nameof(_swapChain))] + private void CreateSurface(PixelSize expectedPixelSize) + { + _swapChain?.Dispose(); + _swapChain = null; + + _compositionDesktopDevice?.Dispose(); + _compositionDesktopDevice = null; + + _compositionTarget?.Dispose(); + _compositionTarget = null; + + _surface?.Dispose(); + _surface = null; + + _renderTexture?.Dispose(); + _renderTexture = null; + + var windowInfo = _window; + var size = expectedPixelSize; + DXGI_SWAP_CHAIN_DESC1 dxgiSwapChainDesc = new DXGI_SWAP_CHAIN_DESC1(); // standard swap chain really. @@ -50,26 +92,51 @@ public DxgiRenderTarget(EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo win dxgiSwapChainDesc.SampleDesc.Quality = 0U; dxgiSwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; dxgiSwapChainDesc.AlphaMode = DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_IGNORE; - dxgiSwapChainDesc.Width = (uint)_window.Size.Width; - dxgiSwapChainDesc.Height = (uint)_window.Size.Height; - dxgiSwapChainDesc.BufferCount = 2U; + dxgiSwapChainDesc.Width = (uint)size.Width; + dxgiSwapChainDesc.Height = (uint)size.Height; + dxgiSwapChainDesc.BufferCount = SwapChainDescBufferCount; dxgiSwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT.DXGI_SWAP_EFFECT_FLIP_DISCARD; - // okay I know this looks bad, but we're hitting our render-calls by awaiting via dxgi - // this is done in the DxgiConnection itself - _flagsUsed = dxgiSwapChainDesc.Flags = (uint)(DXGI_SWAP_CHAIN_FLAG.DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING); + _dxgiSwapChainDescFlagsUsed = DXGI_SWAP_CHAIN_FLAG.DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + dxgiSwapChainDesc.Flags = (uint)_dxgiSwapChainDescFlagsUsed; - _swapChain = _dxgiFactory.CreateSwapChainForHwnd - ( + if (IsTransparency && DxgiConnection.IsTransparencySupported()) + { + dxgiSwapChainDesc.AlphaMode = DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_PREMULTIPLIED; + + _swapChain = _dxgiFactory.CreateSwapChainForComposition(_dxgiDevice, &dxgiSwapChainDesc, null); + + Guid IID_IDCompositionDesktopDevice = Guid.Parse("5f4633fe-1e08-4cb8-8c75-ce24333f5602"); + var result = NativeMethods.DCompositionCreateDevice2(default, IID_IDCompositionDesktopDevice, out var cDevice); + if (result != UnmanagedMethods.HRESULT.S_OK) + { + throw new Win32Exception((int)result); + } + + var device = MicroComRuntime.CreateProxyFor(cDevice, ownsHandle: true); + _compositionDesktopDevice = device; + using IDCompositionVisual compositionVisual = + device.CreateTargetForHwnd(windowInfo.Handle, topmost: true); + var compositionTarget = compositionVisual.QueryInterface(); + _compositionTarget = compositionTarget; + IDCompositionVisual container = device.CreateVisual(); + container.SetContent(_swapChain); + compositionTarget.SetRoot(container); + device.Commit(); + } + else + { + _swapChain = _dxgiFactory.CreateSwapChainForHwnd + ( _dxgiDevice, - window.Handle, + windowInfo.Handle, &dxgiSwapChainDesc, null, null - ); + ); + } - GetClientRect(_window.Handle, out var pClientRect); - _clientRect = pClientRect; + _dxgiFactory.MakeWindowAssociation(windowInfo.Handle, (uint)(DXGI_MWA.DXGI_MWA_NO_ALT_ENTER | DXGI_MWA.DXGI_MWA_NO_PRINT_SCREEN)); } /// @@ -81,50 +148,65 @@ public override IGlPlatformSurfaceRenderingSession BeginDrawCore() } var contextLock = Context.EnsureCurrent(); - EglSurface? surface = null; IDisposable? transaction = null; var success = false; try { - GetClientRect(_window.Handle, out var pClientRect); - if (!RectsEqual(pClientRect, _clientRect)) + var size = _window.Size; + var scale = _window.Scaling; + + var shouldTransparency = IsTransparency && DxgiConnection.IsTransparencySupported(); + var isSupportTransparency = _swapChain.Desc1.AlphaMode is DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_PREMULTIPLIED or DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_STRAIGHT; + + if (shouldTransparency != isSupportTransparency) { - // we gotta resize - _clientRect = pClientRect; + CreateSurface(size); + } + if (_size != size) + { + // we gotta resize if (_renderTexture is not null) { + _surface?.Dispose(); + _surface = null; + _renderTexture.Dispose(); _renderTexture = null; } - _swapChain.ResizeBuffers(2, - (ushort)(pClientRect.right - pClientRect.left), - (ushort)(pClientRect.bottom - pClientRect.top), + _swapChain.ResizeBuffers(SwapChainDescBufferCount, + (ushort)size.Width, (ushort)size.Height, DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM, - (ushort)_flagsUsed + (uint)_dxgiSwapChainDescFlagsUsed ); + _size = size; } - var size = _window.Size; - // Get swapchain texture here var texture = _renderTexture; if (texture is null) { + _surface?.Dispose(); + _surface = null; + Guid textureGuid = ID3D11Texture2DGuid; texture = MicroComRuntime.CreateProxyFor(_swapChain.GetBuffer(0, &textureGuid), true); } _renderTexture = texture; - // I also have to get the pointer to this texture directly - surface = ((AngleWin32EglDisplay)Context.Display).WrapDirect3D11Texture(MicroComRuntime.GetNativeIntPtr(_renderTexture), - 0, 0, size.Width, size.Height); + if (_surface is null) + { + // I also have to get the pointer to this texture directly + _surface = ((AngleWin32EglDisplay)Context.Display).WrapDirect3D11Texture(MicroComRuntime.GetNativeIntPtr(_renderTexture), + 0, 0, size.Width, size.Height); + } - var res = base.BeginDraw(surface, _window.Size, _window.Scaling, () => + var res = base.BeginDraw(_surface, size, scale, () => { _swapChain.Present((ushort)0U, (ushort)0U); - surface.Dispose(); + // No need to Dispose here. The _surface only Dispose when _renderTexture Disposed. + //_surface.Dispose(); transaction?.Dispose(); contextLock?.Dispose(); }, true); @@ -135,7 +217,8 @@ public override IGlPlatformSurfaceRenderingSession BeginDrawCore() { if (!success) { - surface?.Dispose(); + _surface?.Dispose(); + _surface = null; if (_renderTexture is not null) { _renderTexture.Dispose(); @@ -153,7 +236,11 @@ public override void Dispose() _dxgiDevice?.Dispose(); _dxgiFactory?.Dispose(); _swapChain?.Dispose(); + _surface?.Dispose(); _renderTexture?.Dispose(); + + _compositionDesktopDevice?.Dispose(); + _compositionTarget?.Dispose(); } internal static bool RectsEqual(in RECT l, in RECT r) @@ -164,5 +251,13 @@ internal static bool RectsEqual(in RECT l, in RECT r) && (l.bottom == r.bottom); } + public bool IsTransparency => _transparencyLevel != WindowTransparencyLevel.None; + + public void SetTransparencyLevel(WindowTransparencyLevel transparencyLevel) + { + _transparencyLevel = transparencyLevel; + } + + private WindowTransparencyLevel _transparencyLevel; } } diff --git a/src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs b/src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs index a4c6598473a..b57f3e6214e 100644 --- a/src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs +++ b/src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs @@ -3,16 +3,18 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Avalonia.Controls; using Avalonia.OpenGL; using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Surfaces; namespace Avalonia.Win32.DirectX { - internal class DxgiSwapchainWindow : EglGlPlatformSurfaceBase + internal class DxgiSwapchainWindow : EglGlPlatformSurfaceBase, ICompositionEffectsSurface, IDisposable { private DxgiConnection _connection; private EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _window; + private DxgiRenderTarget? _renderTarget; public DxgiSwapchainWindow(DxgiConnection connection, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo window) { @@ -22,11 +24,37 @@ public DxgiSwapchainWindow(DxgiConnection connection, EglGlPlatformSurface.IEglW public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context) { + _renderTarget?.Dispose(); + var eglContext = (EglContext)context; using (eglContext.EnsureCurrent()) { - return new DxgiRenderTarget(_window, eglContext, _connection); + _renderTarget = new DxgiRenderTarget(_window, eglContext, _connection, _windowTransparencyLevel); } + + return _renderTarget; + } + + public bool IsBlurSupported(BlurEffect effect) + => effect == BlurEffect.None; + + public void SetBlur(BlurEffect enable) + { + // do nothing + } + + public void SetTransparencyLevel(WindowTransparencyLevel transparencyLevel) + { + _windowTransparencyLevel = transparencyLevel; + _renderTarget?.SetTransparencyLevel(transparencyLevel); + } + + private WindowTransparencyLevel _windowTransparencyLevel; + + public void Dispose() + { + _renderTarget?.Dispose(); + _renderTarget = null; } } } diff --git a/src/Windows/Avalonia.Win32/IBlurHost.cs b/src/Windows/Avalonia.Win32/IBlurHost.cs index fcaf58eaacd..1b5dd9c9b44 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/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 0f73d39249a..b4bff3da35d 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -15,6 +15,11 @@ internal unsafe class ScreenImpl : ScreensBase protected override int GetScreenCount() => GetSystemMetrics(SystemMetric.SM_CMONITORS); protected override IReadOnlyList GetAllScreenKeys() + { + return GetAllDisplayMonitorHandlers(); + } + + public static List GetAllDisplayMonitorHandlers() { var screens = new List(); var gcHandle = GCHandle.Alloc(screens); diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs index d720e525d3a..4bade8530a7 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 @@ public void ResizeIfNeeded(PixelSize size) } } } + + 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 2addbc65248..ee4c326fd67 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 @@ public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContex ?.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 @@ public void SetBlur(BlurEffect enable) _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 @@ internal class WinUiCompositedWindowRenderTarget : IDirect3D11TextureRenderTarge 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 @@ public WinUiCompositedWindowRenderTarget(IPlatformGraphicsContext context, _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 @@ public WinUiCompositedWindowRenderTarget(IPlatformGraphicsContext context, } } + [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 @@ public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw() 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 @@ public unsafe IDirect3D11TextureRenderTargetRenderSession BeginDraw() 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 c85b434761f..dbea9936fdb 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; + using Avalonia.Collections.Pooled; using Avalonia.Controls; using Avalonia.Controls.Platform; @@ -11,21 +12,23 @@ 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.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 { @@ -177,7 +180,10 @@ public WindowImpl() _nativeControlHost = new Win32NativeControlHost(this, !UseRedirectionBitmap); _defaultTransparencyLevel = UseRedirectionBitmap ? WindowTransparencyLevel.None : WindowTransparencyLevel.Transparent; _transparencyLevel = _defaultTransparencyLevel; - s_instances.Add(this); + SetTransparencyLevel(_transparencyLevel); + + lock (s_instances) + s_instances.Add(this); } internal IInputRoot Owner @@ -318,6 +324,7 @@ private set if (_transparencyLevel != value) { _transparencyLevel = value; + SetTransparencyLevel(value); TransparencyLevelChanged?.Invoke(value); } } @@ -369,6 +376,13 @@ private set 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)) @@ -503,6 +517,11 @@ private unsafe bool SetUseHostBackdropBrush(bool useHostBackdropBrush) return result == 0; } + private void SetTransparencyLevel(WindowTransparencyLevel transparencyLevel) + { + CompositionEffectsSurface?.SetTransparencyLevel(transparencyLevel); + } + public IEnumerable Surfaces => _glSurface is null ? new object[] { Handle, _framebuffer } :