diff --git a/src/Avalonia.Base/Platform/IRenderTarget.cs b/src/Avalonia.Base/Platform/IRenderTarget.cs index 39504ac9fbe..1a46b376acd 100644 --- a/src/Avalonia.Base/Platform/IRenderTarget.cs +++ b/src/Avalonia.Base/Platform/IRenderTarget.cs @@ -25,30 +25,39 @@ public interface IRenderTarget : IDisposable public bool IsCorrupted { get; } } - [PrivateApi] + [PrivateApi, Obsolete("Use IRenderTarget2", true)] + // TODO12: Remove public interface IRenderTargetWithProperties : IRenderTarget + { + RenderTargetProperties Properties { get; } + } + + [PrivateApi] + // TODO12: Merge into IRenderTarget + public interface IRenderTarget2 : IRenderTarget { RenderTargetProperties Properties { get; } /// /// Creates an for a rendering session. /// - /// Apply DPI reported by the render target as a hidden transform matrix + /// The pixel size of the surface /// Returns various properties about the returned drawing context - IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing, out RenderTargetDrawingContextProperties properties); + IDrawingContextImpl CreateDrawingContext(PixelSize expectedPixelSize, + out RenderTargetDrawingContextProperties properties); } internal static class RenderTargetExtensions { public static IDrawingContextImpl CreateDrawingContextWithProperties( this IRenderTarget renderTarget, - bool useScaledDrawing, + PixelSize expectedPixelSize, out RenderTargetDrawingContextProperties properties) { - if (renderTarget is IRenderTargetWithProperties target) - return target.CreateDrawingContext(useScaledDrawing, out properties); + if (renderTarget is IRenderTarget2 target) + return target.CreateDrawingContext(expectedPixelSize, out properties); properties = default; - return renderTarget.CreateDrawingContext(useScaledDrawing); + return renderTarget.CreateDrawingContext(false); } } } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs index e5b5e0e6c26..e942761fcf0 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs @@ -141,7 +141,7 @@ public void Render() if (!_redrawRequested) return; - var renderTargetWithProperties = _renderTarget as IRenderTargetWithProperties; + var renderTargetWithProperties = _renderTarget as IRenderTarget2; var needLayer = _overlays.RequireLayer // Check if we don't need overlays @@ -149,7 +149,8 @@ public void Render() || !(renderTargetWithProperties?.Properties.RetainsPreviousFrameContents == true && renderTargetWithProperties?.Properties.IsSuitableForDirectRendering == true); - using (var renderTargetContext = _renderTarget.CreateDrawingContextWithProperties(false, out var properties)) + using (var renderTargetContext = _renderTarget.CreateDrawingContextWithProperties( + this.PixelSize, out var properties)) { if(needLayer && (PixelSize != _layerSize || _layer == null || _layer.IsCorrupted)) { diff --git a/src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs b/src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs index f89b6f04f5c..13e89e95508 100644 --- a/src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs +++ b/src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; namespace Avalonia.OpenGL.Surfaces { @@ -11,4 +12,11 @@ public interface IGlPlatformSurfaceRenderTargetWithCorruptionInfo : IGlPlatformS { bool IsCorrupted { get; } } + + [PrivateApi] + public interface IGlPlatformSurfaceRenderTarget2 : IGlPlatformSurfaceRenderTargetWithCorruptionInfo + { + IGlPlatformSurfaceRenderingSession BeginDraw(PixelSize expectedPixelSize); + } + } diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs index 190677074a8..9ce34ec7c41 100644 --- a/src/Avalonia.X11/Glx/GlxDisplay.cs +++ b/src/Avalonia.X11/Glx/GlxDisplay.cs @@ -18,6 +18,8 @@ internal unsafe class GlxDisplay public XVisualInfo* VisualInfo => _visual; public GlxContext DeferredContext { get; } public GlxInterface Glx { get; } = new GlxInterface(); + public X11Info X11Info => _x11; + public IntPtr FbConfig => _fbconfig; public GlxDisplay(X11Info x11, IList probeProfiles) { _x11 = x11; diff --git a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs index 17c5909a39a..0c7c7d21659 100644 --- a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs +++ b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs @@ -21,10 +21,11 @@ public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context) return new RenderTarget((GlxContext)context, _info); } - private class RenderTarget : IGlPlatformSurfaceRenderTarget + private class RenderTarget : IGlPlatformSurfaceRenderTarget2 { private readonly GlxContext _context; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; + private PixelSize? _lastSize; public RenderTarget(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info) { @@ -36,29 +37,51 @@ public void Dispose() { // No-op } - - public IGlPlatformSurfaceRenderingSession BeginDraw() + + public bool IsCorrupted => false; + public IGlPlatformSurfaceRenderingSession BeginDraw(PixelSize size) => BeginDrawCore(size); + public IGlPlatformSurfaceRenderingSession BeginDraw() => BeginDrawCore(null); + public IGlPlatformSurfaceRenderingSession BeginDrawCore(PixelSize? expectedSize) { + var size = expectedSize ?? _info.Size; + if (expectedSize.HasValue) + { + XLib.XConfigureResizeWindow(_context.Display.X11Info.DeferredDisplay, + _info.Handle, size.Width, size.Height); + XLib.XFlush(_context.Display.X11Info.DeferredDisplay); + + if (_lastSize != size) + { + XLib.XSync(_context.Display.X11Info.DeferredDisplay, true); + _lastSize = size; + } + _context.Glx.WaitX(); + } + + var oldContext = _context.MakeCurrent(_info.Handle); // Reset to default FBO first _context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, 0); - return new Session(_context, _info, oldContext); + return new Session(_context, _info, size, oldContext); } private class Session : IGlPlatformSurfaceRenderingSession { private readonly GlxContext _context; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; + private readonly PixelSize? _size; private readonly IDisposable _clearContext; public IGlContext Context => _context; public Session(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info, + PixelSize? size, IDisposable clearContext) { _context = context; _info = info; + _size = size; _clearContext = clearContext; } @@ -71,7 +94,7 @@ public void Dispose() _clearContext.Dispose(); } - public PixelSize Size => _info.Size; + public PixelSize Size => _size ?? _info.Size; public double Scaling => _info.Scaling; public bool IsYFlipped { get; } } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 0a254b6b2b3..7ad0cd44615 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -67,6 +67,7 @@ internal unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client, private TransparencyHelper? _transparencyHelper; private RawEventGrouper? _rawEventGrouper; private bool _useRenderWindow = false; + private bool _useCompositorDrivenRenderWindowResize = false; private bool _usePositioningFlags = false; private X11FocusProxy? _focusProxy; @@ -111,7 +112,12 @@ public X11Window(AvaloniaX11Platform platform, IWindowImpl? popupParent, bool ov var glx = glfeature as GlxPlatformGraphics; if (glx != null) + { visualInfo = *glx.Display.VisualInfo; + // TODO: We should query this from the active render surface, however we don't actually track what + // the target sufrace currently is + _useCompositorDrivenRenderWindowResize = true; + } else if (glfeature == null) visualInfo = _x11.TransparentVisualInfo; @@ -569,7 +575,8 @@ private void OnEvent(ref XEvent ev) Resized?.Invoke(ClientSize, WindowResizeReason.Unspecified); }, DispatcherPriority.AsyncRenderTargetResize); - if (_useRenderWindow) + + if (_useRenderWindow && !_useCompositorDrivenRenderWindowResize) XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width, ev.ConfigureEvent.height); if (_xSyncState == XSyncState.WaitConfigure) @@ -1081,7 +1088,7 @@ private void Resize(Size clientSize, bool force, WindowResizeReason reason) var pixelSize = ToPixelSize(clientSize); UpdateSizeHints(pixelSize); XConfigureResizeWindow(_x11.Display, _handle, pixelSize); - if (_useRenderWindow) + if (_useRenderWindow && !_useCompositorDrivenRenderWindowResize) XConfigureResizeWindow(_x11.Display, _renderHandle, pixelSize); XFlush(_x11.Display); diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs index 4b8f1791842..318cdac22c0 100644 --- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs @@ -10,7 +10,7 @@ namespace Avalonia.Skia /// /// Skia render target that renders to a framebuffer surface. No gpu acceleration available. /// - internal class FramebufferRenderTarget : IRenderTargetWithProperties + internal class FramebufferRenderTarget : IRenderTarget2 { private SKImageInfo _currentImageInfo; private IntPtr _currentFramebufferAddress; @@ -50,10 +50,15 @@ public void Dispose() /// public IDrawingContextImpl CreateDrawingContext(bool scaleDrawingToDpi) => - CreateDrawingContext(scaleDrawingToDpi, out _); + CreateDrawingContextCore(scaleDrawingToDpi, out _); /// - public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing, out RenderTargetDrawingContextProperties properties) + public IDrawingContextImpl CreateDrawingContext(PixelSize expectedPixelSize, + out RenderTargetDrawingContextProperties properties) + => CreateDrawingContextCore(false, out properties); + + IDrawingContextImpl CreateDrawingContextCore(bool scaleDrawingToDpi, + out RenderTargetDrawingContextProperties properties) { if (_renderTarget == null) throw new ObjectDisposedException(nameof(FramebufferRenderTarget)); @@ -77,7 +82,7 @@ public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing, out Rende { Surface = _framebufferSurface, Dpi = framebuffer.Dpi, - ScaleDrawingToDpi = useScaledDrawing + ScaleDrawingToDpi = scaleDrawingToDpi }; properties = new() diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderTarget.cs index bd5b170a688..f8be7dc66f0 100644 --- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderTarget.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; using SkiaSharp; namespace Avalonia.Skia @@ -16,4 +17,11 @@ public interface ISkiaGpuRenderTarget : IDisposable bool IsCorrupted { get; } } + + [PrivateApi] + //TODO12: Merge with ISkiaGpuRenderTarget + public interface ISkiaGpuRenderTarget2 : ISkiaGpuRenderTarget + { + ISkiaGpuRenderSession BeginRenderingSession(PixelSize pixelSize); + } } diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs index 25e004f4efe..56ce4a71945 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs @@ -9,7 +9,7 @@ namespace Avalonia.Skia { - internal class GlRenderTarget : ISkiaGpuRenderTarget + internal class GlRenderTarget : ISkiaGpuRenderTarget2 { private readonly GRContext _grContext; private IGlPlatformSurfaceRenderTarget _surface; @@ -59,9 +59,16 @@ public void Dispose() public double ScaleFactor => _glSession.Scaling; } - public ISkiaGpuRenderSession BeginRenderingSession() + public ISkiaGpuRenderSession BeginRenderingSession(PixelSize size) => BeginRenderingSessionCore(size); + public ISkiaGpuRenderSession BeginRenderingSession() => BeginRenderingSessionCore(null); + + ISkiaGpuRenderSession BeginRenderingSessionCore(PixelSize? expectedSize) { - var glSession = _surface.BeginDraw(); + var glSession = + expectedSize != null && _surface is IGlPlatformSurfaceRenderTarget2 surface2 + ? surface2.BeginDraw(expectedSize.Value) + : _surface.BeginDraw(); + bool success = false; try { diff --git a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs index 8cc7ed68688..8d231a57849 100644 --- a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs @@ -5,7 +5,7 @@ namespace Avalonia.Skia /// /// Adapts to be used within our rendering pipeline. /// - internal class SkiaGpuRenderTarget : IRenderTarget + internal class SkiaGpuRenderTarget : IRenderTarget2 { private readonly ISkiaGpu _skiaGpu; private readonly ISkiaGpuRenderTarget _renderTarget; @@ -21,9 +21,23 @@ public void Dispose() _renderTarget.Dispose(); } + public IDrawingContextImpl CreateDrawingContext(PixelSize expectedPixelSize, + out RenderTargetDrawingContextProperties properties) => + CreateDrawingContextCore(expectedPixelSize, false, out properties); + public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing) + => CreateDrawingContextCore(null, useScaledDrawing, out _); + + + IDrawingContextImpl CreateDrawingContextCore(PixelSize? expectedPixelSize, + bool useScaledDrawing, + out RenderTargetDrawingContextProperties properties) { - var session = _renderTarget.BeginRenderingSession(); + properties = default; + var session = + expectedPixelSize.HasValue && _renderTarget is ISkiaGpuRenderTarget2 target2 + ? target2.BeginRenderingSession(expectedPixelSize.Value) + : _renderTarget.BeginRenderingSession(); var nfo = new DrawingContextImpl.CreateInfo { @@ -39,5 +53,8 @@ public IDrawingContextImpl CreateDrawingContext(bool useScaledDrawing) } public bool IsCorrupted => _renderTarget.IsCorrupted; + public RenderTargetProperties Properties { get; } + + } }