From 01d26ed2ee5b919cc8d47fc6638d1ae5ebdf77bf Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 30 Jan 2020 22:21:45 +0100 Subject: [PATCH 01/16] Failing unit tests for ComboBox losing focus on detach and Popup.Close raising Closed events for already closed popup. --- .../ComboBoxTests.cs | 31 +++++++++++++++++++ .../Primitives/PopupTests.cs | 28 ++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs index 599e214b31..81908b5147 100644 --- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs @@ -108,5 +108,36 @@ namespace Avalonia.Controls.UnitTests }; }); } + + [Fact] + public void Detaching_Closed_ComboBox_Keeps_Current_Focus() + { + using (UnitTestApplication.Start(TestServices.RealFocus)) + { + var target = new ComboBox + { + Items = new[] { new Canvas() }, + SelectedIndex = 0, + Template = GetTemplate(), + }; + + var other = new Control { Focusable = true }; + + StackPanel panel; + + var root = new TestRoot { Child = panel = new StackPanel { Children = { target, other } } }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + other.Focus(); + + Assert.True(other.IsFocused); + + panel.Children.Remove(target); + + Assert.True(other.IsFocused); + } + } } } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index 7cb9fccee8..1f07482617 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -223,7 +223,33 @@ namespace Avalonia.Controls.UnitTests.Primitives } } - + [Fact] + public void Popup_Close_On_Closed_Popup_Should_Not_Raise_Closed_Event() + { + using (CreateServices()) + { + var window = PreparedWindow(); + var target = new Popup() { PlacementMode = PlacementMode.Pointer }; + + window.Content = target; + window.ApplyTemplate(); + + int closedCount = 0; + + target.Closed += (sender, args) => + { + closedCount++; + }; + + target.Close(); + target.Close(); + target.Close(); + target.Close(); + + Assert.Equal(0, closedCount); + } + } + [Fact] public void Templated_Control_With_Popup_In_Template_Should_Set_TemplatedParent() { From 91c112b62c3b4698f8019077d71733131b8ef2b8 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 30 Jan 2020 22:22:14 +0100 Subject: [PATCH 02/16] Fix Popup.Close raising Closed events even when it is already closed. --- src/Avalonia.Controls/Primitives/Popup.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 77febf9384..0dbe4213e3 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -277,6 +277,11 @@ namespace Avalonia.Controls.Primitives /// public void Close() { + if (_topLevel is null) + { + return; + } + if (_popupHost != null) { _popupHost.TemplateApplied -= RootTemplateApplied; From 52dc41af022bfcdfc8fa028f1a012b29e12ee3ae Mon Sep 17 00:00:00 2001 From: Symbai <14368203+Symbai@users.noreply.github.com> Date: Tue, 4 Feb 2020 23:18:53 +0100 Subject: [PATCH 03/16] Update Default.xaml --- src/Avalonia.Controls.DataGrid/Themes/Default.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index 7b6870fec3..7a399349f6 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -217,12 +217,12 @@ - + - + From 6540d0635b3ac93a2689f5f8ef51cb9a3eb16aeb Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 5 Feb 2020 09:26:46 +0300 Subject: [PATCH 04/16] IOSurface render target for sw-rendering --- native/Avalonia.Native/inc/avalonia-native.h | 3 - native/Avalonia.Native/inc/rendertarget.h | 12 ++ .../project.pbxproj | 12 ++ .../Avalonia.Native/src/OSX/rendertarget.mm | 105 +++++++++++ native/Avalonia.Native/src/OSX/window.mm | 172 +++++------------- .../AvaloniaNativeDeferredRendererLock.cs | 42 ----- src/Avalonia.Native/AvaloniaNativePlatform.cs | 7 +- src/Avalonia.Native/DeferredFramebuffer.cs | 2 +- src/Avalonia.Native/WindowImplBase.cs | 55 ++---- 9 files changed, 197 insertions(+), 213 deletions(-) create mode 100644 native/Avalonia.Native/inc/rendertarget.h create mode 100644 native/Avalonia.Native/src/OSX/rendertarget.mm delete mode 100644 src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index 677fa2f4c5..24607c26cb 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -219,15 +219,12 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown virtual HRESULT SetTopMost (bool value) = 0; virtual HRESULT SetCursor(IAvnCursor* cursor) = 0; virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret) = 0; - virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) = 0; virtual HRESULT SetMainMenu(IAvnAppMenu* menu) = 0; virtual HRESULT ObtainMainMenu(IAvnAppMenu** retOut) = 0; virtual HRESULT ObtainNSWindowHandle(void** retOut) = 0; virtual HRESULT ObtainNSWindowHandleRetained(void** retOut) = 0; virtual HRESULT ObtainNSViewHandle(void** retOut) = 0; virtual HRESULT ObtainNSViewHandleRetained(void** retOut) = 0; - virtual bool TryLock() = 0; - virtual void Unlock() = 0; }; AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase diff --git a/native/Avalonia.Native/inc/rendertarget.h b/native/Avalonia.Native/inc/rendertarget.h new file mode 100644 index 0000000000..00d4b4bd4a --- /dev/null +++ b/native/Avalonia.Native/inc/rendertarget.h @@ -0,0 +1,12 @@ + +@protocol IRenderTarget +-(void) setNewLayer: (CALayer*) layer; +-(HRESULT) setSwFrame: (AvnFramebuffer*) fb; +-(void) resize: (AvnPixelSize) size withScale: (float) scale; +-(AvnPixelSize) pixelSize; +@end + +@interface IOSurfaceRenderTarget : NSObject +-(IOSurfaceRenderTarget*) init; +@end + diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index c0a49382a7..686c37bc24 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -8,6 +8,9 @@ /* Begin PBXBuildFile section */ 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; + 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; + 1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; }; + 1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; }; 37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; }; 37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; }; 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; }; @@ -26,6 +29,9 @@ /* Begin PBXFileReference section */ 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; + 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = ""; }; + 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; + 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = ""; }; 379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = ""; }; 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; }; @@ -54,6 +60,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */, + 1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */, AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */, AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */, ); @@ -65,6 +73,8 @@ AB661C1C2148230E00291242 /* Frameworks */ = { isa = PBXGroup; children = ( + 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */, + 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */, AB1E522B217613570091CD71 /* OpenGL.framework */, AB661C1D2148230F00291242 /* AppKit.framework */, ); @@ -91,6 +101,7 @@ AB00E4F62147CA920032A60A /* main.mm */, 37155CE3233C00EB0034DCE9 /* menu.h */, 520624B222973F4100C4DCEF /* menu.mm */, + 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */, 37A517B22159597E00FBA241 /* Screens.mm */, 37C09D8721580FE4006A6758 /* SystemDialogs.mm */, AB7A61F02147C815003C5833 /* Products */, @@ -180,6 +191,7 @@ 5B21A982216530F500CEE36E /* cursor.mm in Sources */, 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */, AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */, + 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */, 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */, 520624B322973F4100C4DCEF /* menu.mm in Sources */, 37A517B32159597E00FBA241 /* Screens.mm in Sources */, diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm new file mode 100644 index 0000000000..3a86339496 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -0,0 +1,105 @@ +#include "common.h" +#include "rendertarget.h" +#import +#import + +@implementation IOSurfaceRenderTarget +{ + CALayer* _layer; + IOSurfaceRef _surface; + AvnPixelSize _size; + float _scale; + NSObject* _lock; +} + +- (IOSurfaceRenderTarget*) init +{ + self = [super init]; + _lock = [NSObject new]; + _surface = nil; + [self resize:{1,1} withScale: 1]; + + return self; +} + +- (AvnPixelSize) pixelSize { + return {1, 1}; +} + +- (CALayer *)layer { + return _layer; +} + +- (void)resize:(AvnPixelSize)size withScale: (float) scale;{ + @synchronized (_lock) { + if(_surface != nil) + { + IOSurfaceDecrementUseCount(_surface); + _surface = nil; + } + NSDictionary* options = @{ + (id)kIOSurfaceWidth: @(size.Width), + (id)kIOSurfaceHeight: @(size.Height), + (id)kIOSurfacePixelFormat: @((uint)'BGRA'), + (id)kIOSurfaceBytesPerElement: @(4), + //(id)kIOSurfaceBytesPerRow: @(bytesPerRow), + //(id)kIOSurfaceAllocSize: @(m_totalBytes), + + //(id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache), + (id)kIOSurfaceElementWidth: @(1), + (id)kIOSurfaceElementHeight: @(1) + }; + + _surface = IOSurfaceCreate((CFDictionaryRef)options); + _scale = scale; + _size = size; + } +} + +- (void)updateLayer { + if ([NSThread isMainThread]) + { + @synchronized (_lock) { + if(_layer == nil) + return; + [_layer setContentsScale: _scale]; + [_layer setContents: nil]; + [_layer setContents: (__bridge IOSurface* )_surface]; + } + } + else + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateLayer]; + }); +} + +- (void) setNewLayer:(CALayer *)layer { + _layer = layer; + [self updateLayer]; +} + +- (HRESULT)setSwFrame:(AvnFramebuffer *)fb { + @synchronized (_lock) { + if(fb->PixelFormat == AvnPixelFormat::kAvnRgb565) + return E_INVALIDARG; + if(IOSurfaceLock(_surface, 0, nil)) + return E_FAIL; + size_t w = MIN(fb->Width, IOSurfaceGetWidth(_surface)); + size_t h = MIN(fb->Height, IOSurfaceGetHeight(_surface)); + size_t wbytes = w*4; + size_t sstride = IOSurfaceGetBytesPerRow(_surface); + size_t fstride = fb->Stride; + char*pSurface = (char*)IOSurfaceGetBaseAddress(_surface); + char*pFb = (char*)fb->Data; + for(size_t y = 0; y +#include "rendertarget.h" + -class SoftwareDrawingOperation -{ -public: - void* Data = 0; - AvnFramebuffer Desc; - void Alloc(NSView* view) - { - auto logicalSize = [view frame].size; - auto pixelSize = [view convertSizeToBacking:logicalSize]; - int w = pixelSize.width; - int h = pixelSize.height; - int stride = w * 4; - Data = malloc(h * stride); - Desc = { - .Data = Data, - .Stride = stride, - .Width = w, - .Height = h, - .PixelFormat = kAvnRgba8888, - .Dpi = AvnVector { .X = w / logicalSize.width * 96, .Y = h / logicalSize.height * 96} - }; - } - - void Dealloc() - { - if(Data != NULL) - { - free(Data); - Data = NULL; - } - } - - ~SoftwareDrawingOperation() - { - Dealloc(); - } -}; class WindowBaseImpl : public virtual ComSingleObject, public INSWindowHolder { @@ -61,7 +26,7 @@ public: AvnView* View; AvnWindow* Window; ComPtr BaseEvents; - SoftwareDrawingOperation CurrentSwDrawingOperation; + NSObject* renderTarget; AvnPoint lastPositionSet; NSString* _lastTitle; IAvnAppMenu* _mainMenu; @@ -70,6 +35,7 @@ public: { _mainMenu = nullptr; BaseEvents = events; + renderTarget = [IOSurfaceRenderTarget new]; View = [[AvnView alloc] initWithParent:this]; Window = [[AvnWindow alloc] initWithParent:this]; @@ -291,29 +257,6 @@ public: return S_OK; } - virtual bool TryLock() override - { - @autoreleasepool - { - @try - { - return [View lockFocusIfCanDraw] == YES; - } - @catch (NSException*) - { - return NO; - } - } - } - - virtual void Unlock() override - { - @autoreleasepool - { - [View unlockFocus]; - } - } - virtual HRESULT BeginMoveDrag () override { @autoreleasepool @@ -408,16 +351,6 @@ public: return S_OK; } - virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) override - { - if(![[NSThread currentThread] isMainThread]) - return E_FAIL; - if(CurrentSwDrawingOperation.Data == NULL) - CurrentSwDrawingOperation.Alloc(View); - *ret = CurrentSwDrawingOperation.Desc; - return S_OK; - } - virtual HRESULT SetCursor(IAvnCursor* cursor) override { @autoreleasepool @@ -731,6 +664,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent NSEvent* _lastMouseDownEvent; bool _lastKeyHandled; AvnPixelSize _lastPixelSize; + NSObject* _renderTarget; } - (void)onClosed @@ -741,6 +675,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } +/* - (BOOL)lockFocusIfCanDraw { @synchronized (self) @@ -752,7 +687,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } return [super lockFocusIfCanDraw]; -} +}*/ -(AvnPixelSize) getPixelSize { @@ -764,18 +699,47 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent return _lastMouseDownEvent; } +- (void) updateRenderTarget +{ + [_renderTarget resize:_lastPixelSize withScale: [[self window] backingScaleFactor]]; + [self setNeedsDisplayInRect:[self frame]]; +} + -(AvnView*) initWithParent: (WindowBaseImpl*) parent { self = [super init]; - [self setWantsBestResolutionOpenGLSurface:true]; + _renderTarget = parent->renderTarget; [self setWantsLayer:YES]; + [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize]; + _parent = parent; _area = nullptr; _lastPixelSize.Height = 100; _lastPixelSize.Width = 100; +/* uncomment to verify that embedding isn't broken + NSTextField* txt = [NSTextField new]; + [self addSubview:txt]; + [txt setFrame:{0,0, 100,100}]; + */ return self; } +- (BOOL)isFlipped +{ + return YES; +} + +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (void)setLayer:(CALayer *)layer +{ + [_renderTarget setNewLayer: layer]; + [super setLayer: layer]; +} + - (BOOL)isOpaque { return YES; @@ -823,56 +787,24 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent auto fsize = [self convertSizeToBacking: [self frame].size]; _lastPixelSize.Width = (int)fsize.width; _lastPixelSize.Height = (int)fsize.height; - + [self updateRenderTarget]; _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}); } -- (void) drawFb: (AvnFramebuffer*) fb -{ - auto colorSpace = CGColorSpaceCreateDeviceRGB(); - auto dataProvider = CGDataProviderCreateWithData(NULL, fb->Data, fb->Height*fb->Stride, NULL); - - auto image = CGImageCreate(fb->Width, fb->Height, 8, 32, fb->Stride, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast, - dataProvider, nullptr, false, kCGRenderingIntentDefault); - - auto ctx = [NSGraphicsContext currentContext]; - - [ctx saveGraphicsState]; - auto cgc = [ctx CGContext]; - - CGContextDrawImage(cgc, CGRect{0,0, fb->Width/(fb->Dpi.X/96), fb->Height/(fb->Dpi.Y/96)}, image); - CGImageRelease(image); - CGColorSpaceRelease(colorSpace); - CGDataProviderRelease(dataProvider); - - [ctx restoreGraphicsState]; - -} - -- (void)drawRect:(NSRect)dirtyRect +- (void)updateLayer { if (_parent == nullptr) { return; } - - _parent->BaseEvents->RunRenderPriorityJobs(); - - @synchronized (self) { - if(_swRenderedFrame != NULL) - { - [self drawFb: &_swRenderedFrameBuffer]; - return; - } - } - auto swOp = &_parent->CurrentSwDrawingOperation; + _parent->BaseEvents->RunRenderPriorityJobs(); _parent->BaseEvents->Paint(); - if(swOp->Data != NULL) - [self drawFb: &swOp->Desc]; - - swOp->Dealloc(); +} + +- (void)drawRect:(NSRect)dirtyRect +{ return; } @@ -886,24 +818,16 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent return; _queuedDisplayFromThread = false; } - [self setNeedsDisplayInRect:[self frame]]; + [self setNeedsDisplayInRect: [self frame]]; [self display]; - } } -(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose { @autoreleasepool { - @synchronized (self) { - _swRenderedFrame = dispose; - _swRenderedFrameBuffer = *fb; - if(!_queuedDisplayFromThread) - { - _queuedDisplayFromThread = true; - [self performSelector:@selector(redrawSelf) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes: AllLoopModes]; - } - } + [_renderTarget setSwFrame:fb]; + dispose->Release(); } } @@ -928,7 +852,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent auto fsize = [self convertSizeToBacking: [self frame].size]; _lastPixelSize.Width = (int)fsize.width; _lastPixelSize.Height = (int)fsize.height; - + [self updateRenderTarget]; _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]); [super viewDidChangeBackingProperties]; diff --git a/src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs b/src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs deleted file mode 100644 index ce2b03e355..0000000000 --- a/src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) The Avalonia Project. All rights reserved. -// Licensed under the MIT license. See licence.md file in the project root for full license information. - -using System; -using System.Threading; -using Avalonia.Native.Interop; -using Avalonia.Rendering; - -namespace Avalonia.Native -{ - public class AvaloniaNativeDeferredRendererLock : IDeferredRendererLock - { - private readonly IAvnWindowBase _window; - - public AvaloniaNativeDeferredRendererLock(IAvnWindowBase window) - { - _window = window; - } - - public IDisposable TryLock() - { - if (_window.TryLock()) - return new UnlockDisposable(_window); - return null; - } - - private sealed class UnlockDisposable : IDisposable - { - private IAvnWindowBase _window; - - public UnlockDisposable(IAvnWindowBase window) - { - _window = window; - } - - public void Dispose() - { - Interlocked.Exchange(ref _window, null)?.Unlock(); - } - } - } -} diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 756619fa9f..825666b909 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Runtime.InteropServices; +using System.Security.Cryptography; using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Platform; @@ -88,6 +89,8 @@ namespace Avalonia.Native _factory.MacOptions.ShowInDock = macOpts?.ShowInDock != false ? 1 : 0; } + _options.UseGpu = false; + AvaloniaLocator.CurrentMutable .Bind() .ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface())) @@ -100,9 +103,11 @@ namespace Avalonia.Native .Bind().ToConstant(new RenderLoop()) .Bind().ToConstant(new DefaultRenderTimer(60)) .Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())) - .Bind().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature())) .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta)) .Bind().ToConstant(new MacOSMountedVolumeInfoProvider()); + if (_options.UseGpu) + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature())); } public IWindowImpl CreateWindow() diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs index adc721de42..0c32bce145 100644 --- a/src/Avalonia.Native/DeferredFramebuffer.cs +++ b/src/Avalonia.Native/DeferredFramebuffer.cs @@ -21,7 +21,7 @@ namespace Avalonia.Native Size = new PixelSize(width, height); RowBytes = width * 4; Dpi = dpi; - Format = PixelFormat.Rgba8888; + Format = PixelFormat.Bgra8888; } public IntPtr Address { get; set; } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 5d701dc8df..a2148f6782 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -103,25 +103,20 @@ namespace Avalonia.Native public ILockedFramebuffer Lock() { - if(_deferredRendering) + var w = _savedLogicalSize.Width * _savedScaling; + var h = _savedLogicalSize.Height * _savedScaling; + var dpi = _savedScaling * 96; + return new DeferredFramebuffer(cb => { - var w = _savedLogicalSize.Width * _savedScaling; - var h = _savedLogicalSize.Height * _savedScaling; - var dpi = _savedScaling * 96; - return new DeferredFramebuffer(cb => + lock (_syncRoot) { - lock (_syncRoot) - { - if (_native == null) - return false; - cb(_native); - _lastRenderedLogicalSize = _savedLogicalSize; - return true; - } - }, (int)w, (int)h, new Vector(dpi, dpi)); - } - - return new FramebufferWrapper(_native.GetSoftwareFramebuffer()); + if (_native == null) + return false; + cb(_native); + _lastRenderedLogicalSize = _savedLogicalSize; + return true; + } + }, (int)w, (int)h, new Vector(dpi, dpi)); } public Action Paint { get; set; } @@ -130,28 +125,6 @@ namespace Avalonia.Native public IMouseDevice MouseDevice => _mouse; public abstract IPopupImpl CreatePopup(); - - class FramebufferWrapper : ILockedFramebuffer - { - public FramebufferWrapper(AvnFramebuffer fb) - { - Address = fb.Data; - Size = new PixelSize(fb.Width, fb.Height); - RowBytes = fb.Stride; - Dpi = new Vector(fb.Dpi.X, fb.Dpi.Y); - Format = (PixelFormat)fb.PixelFormat; - } - public IntPtr Address { get; set; } - public PixelSize Size { get; set; } - public int RowBytes {get;set;} - public Vector Dpi { get; set; } - public PixelFormat Format { get; } - public void Dispose() - { - // Do nothing - } - } - protected class WindowBaseEvents : CallbackBase, IAvnWindowBaseEvents { private readonly WindowBaseImpl _parent; @@ -278,9 +251,7 @@ namespace Avalonia.Native public IRenderer CreateRenderer(IRenderRoot root) { if (_deferredRendering) - return new DeferredRenderer(root, AvaloniaLocator.Current.GetService(), - rendererLock: - _gpu ? new AvaloniaNativeDeferredRendererLock(_native) : null); + return new DeferredRenderer(root, AvaloniaLocator.Current.GetService()); return new ImmediateRenderer(root); } From 12e483acd16303256d68c5fa4a64de81fba64b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Wed, 5 Feb 2020 20:31:25 +0000 Subject: [PATCH 05/16] Replaced obsolete PointerEventArgs.Device usages. --- .../DataGridColumnHeader.cs | 4 ++-- src/Avalonia.Controls/Calendar/CalendarItem.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index 062fdeb4d3..bff2b500ae 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs @@ -326,7 +326,7 @@ namespace Avalonia.Controls if (OwningGrid != null && OwningGrid.ColumnHeaders != null) { - args.Device.Capture(this); + args.Pointer.Capture(this); _dragMode = DragMode.MouseDown; _frozenColumnsWidth = OwningGrid.ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth(); @@ -391,7 +391,7 @@ namespace Avalonia.Controls SetDragCursor(mousePosition); // Variables that track drag mode states get reset in DataGridColumnHeader_LostMouseCapture - args.Device.Capture(null); + args.Pointer.Capture(null); OnLostMouseCapture(); _dragMode = DragMode.None; handled = true; diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs index d6874e1ad6..5a2d1bbfd5 100644 --- a/src/Avalonia.Controls/Calendar/CalendarItem.cs +++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs @@ -943,8 +943,8 @@ namespace Avalonia.Controls.Primitives { CalendarDayButton b = (CalendarDayButton)sender; // The button is in Pressed state. Change the state to normal. - if (e.Device.Captured == b) - e.Device.Capture(null); + if (e.Pointer.Captured == b) + e.Pointer.Capture(null); _lastCalendarDayButton = b; } } @@ -1213,8 +1213,8 @@ namespace Avalonia.Controls.Primitives { CalendarButton b = (CalendarButton)sender; // The button is in Pressed state. Change the state to normal. - if (e.Device.Captured == b) - e.Device.Capture(null); + if (e.Pointer.Captured == b) + e.Pointer.Capture(null); //b.ReleaseMouseCapture(); _lastCalendarButton = b; @@ -1224,7 +1224,7 @@ namespace Avalonia.Controls.Primitives { if (_lastCalendarDayButton != null) { - e.Device.Capture(_lastCalendarDayButton); + e.Pointer.Capture(_lastCalendarDayButton); } } @@ -1232,7 +1232,7 @@ namespace Avalonia.Controls.Primitives { if (_lastCalendarButton != null) { - e.Device.Capture(_lastCalendarButton); + e.Pointer.Capture(_lastCalendarButton); } } From 07f73bf01c5be02f2ce7d97b777825a0f8f73159 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 5 Feb 2020 22:34:53 +0100 Subject: [PATCH 06/16] Cleanup Popup control. --- src/Avalonia.Controls/Primitives/Popup.cs | 271 ++++++++++++++-------- 1 file changed, 172 insertions(+), 99 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 0dbe4213e3..1a42f8d70a 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -2,12 +2,10 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reactive.Disposables; using Avalonia.Controls.Presenters; -using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Interactivity; @@ -15,6 +13,8 @@ using Avalonia.LogicalTree; using Avalonia.Metadata; using Avalonia.VisualTree; +#nullable enable + namespace Avalonia.Controls.Primitives { /// @@ -43,11 +43,13 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty PlacementModeProperty = AvaloniaProperty.Register(nameof(PlacementMode), defaultValue: PlacementMode.Bottom); +#pragma warning disable 618 /// /// Defines the property. /// public static readonly StyledProperty ObeyScreenEdgesProperty = AvaloniaProperty.Register(nameof(ObeyScreenEdges), true); +#pragma warning restore 618 /// /// Defines the property. @@ -80,12 +82,8 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(Topmost)); private bool _isOpen; - private IPopupHost _popupHost; - private TopLevel _topLevel; - private IDisposable _nonClientListener; - private IDisposable _presenterSubscription; - bool _ignoreIsOpenChanged = false; - private List _bindings = new List(); + private bool _ignoreIsOpenChanged; + private PopupOpenState? _openState; /// /// Initializes static members of the class. @@ -97,22 +95,17 @@ namespace Avalonia.Controls.Primitives IsOpenProperty.Changed.AddClassHandler((x, e) => x.IsOpenChanged(e)); } - public Popup() - { - - } - /// /// Raised when the popup closes. /// - public event EventHandler Closed; + public event EventHandler? Closed; /// /// Raised when the popup opens. /// - public event EventHandler Opened; + public event EventHandler? Opened; - public IPopupHost Host => _popupHost; + public IPopupHost? Host => _openState?.PopupHost; /// /// Gets or sets the control to display in the popup. @@ -131,7 +124,7 @@ namespace Avalonia.Controls.Primitives /// This property allows a client to customize the behaviour of the popup by injecting /// a specialized dependency resolver into the 's constructor. /// - public IAvaloniaDependencyResolver DependencyResolver + public IAvaloniaDependencyResolver? DependencyResolver { get; set; @@ -211,7 +204,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets the root of the popup window. /// - IVisual IVisualTreeHost.Root => _popupHost?.HostedVisualTreeRoot; + IVisual? IVisualTreeHost.Root => _openState?.PopupHost.HostedVisualTreeRoot; /// /// Opens the popup. @@ -219,50 +212,91 @@ namespace Avalonia.Controls.Primitives public void Open() { // Popup is currently open - if (_topLevel != null) + if (_openState != null) + { return; - CloseCurrent(); + } + var placementTarget = PlacementTarget ?? this.GetLogicalAncestors().OfType().FirstOrDefault(); + if (placementTarget == null) + { throw new InvalidOperationException("Popup has no logical parent and PlacementTarget is null"); + } - _topLevel = placementTarget.GetVisualRoot() as TopLevel; + var topLevel = placementTarget.VisualRoot as TopLevel; - if (_topLevel == null) + if (topLevel == null) { throw new InvalidOperationException( "Attempted to open a popup not attached to a TopLevel"); } - _popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver); + var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver); + + var handlerCleanup = new CompositeDisposable(5); - _bindings.Add(_popupHost.BindConstraints(this, WidthProperty, MinWidthProperty, MaxWidthProperty, + void DeferCleanup(IDisposable? disposable) + { + if (disposable is null) + { + return; + } + + handlerCleanup.Add(disposable); + } + + DeferCleanup(popupHost.BindConstraints(this, WidthProperty, MinWidthProperty, MaxWidthProperty, HeightProperty, MinHeightProperty, MaxHeightProperty, TopmostProperty)); - _popupHost.SetChild(Child); - ((ISetLogicalParent)_popupHost).SetParent(this); - _popupHost.ConfigurePosition(placementTarget, - PlacementMode, new Point(HorizontalOffset, VerticalOffset)); - _popupHost.TemplateApplied += RootTemplateApplied; - - var window = _topLevel as Window; - if (window != null) + popupHost.SetChild(Child); + ((ISetLogicalParent)popupHost).SetParent(this); + + popupHost.ConfigurePosition( + placementTarget, + PlacementMode, + new Point(HorizontalOffset, VerticalOffset)); + + DeferCleanup(SubscribeToEventHandler>(popupHost, RootTemplateApplied, + (x, handler) => x.TemplateApplied += handler, + (x, handler) => x.TemplateApplied -= handler)); + + if (topLevel is Window window) { - window.Deactivated += WindowDeactivated; + DeferCleanup(SubscribeToEventHandler(window, WindowDeactivated, + (x, handler) => x.Deactivated += handler, + (x, handler) => x.Deactivated -= handler)); } else { - var parentPopuproot = _topLevel as PopupRoot; - if (parentPopuproot?.Parent is Popup popup) + var parentPopupRoot = topLevel as PopupRoot; + + if (parentPopupRoot?.Parent is Popup popup) { - popup.Closed += ParentClosed; + DeferCleanup(SubscribeToEventHandler(popup, ParentClosed, + (x, handler) => x.Closed += handler, + (x, handler) => x.Closed -= handler)); } } - _topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel); - _nonClientListener = InputManager.Instance?.Process.Subscribe(ListenForNonClientClick); - - _popupHost.Show(); + DeferCleanup(topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel)); + + DeferCleanup(InputManager.Instance?.Process.Subscribe(ListenForNonClientClick)); + + var cleanupPopup = Disposable.Create((popupHost, handlerCleanup), state => + { + state.handlerCleanup.Dispose(); + + state.popupHost.SetChild(null); + state.popupHost.Hide(); + + ((ISetLogicalParent)state.popupHost).SetParent(null); + state.popupHost.Dispose(); + }); + + _openState = new PopupOpenState(topLevel, popupHost, cleanupPopup); + + popupHost.Show(); using (BeginIgnoringIsOpen()) { @@ -277,19 +311,19 @@ namespace Avalonia.Controls.Primitives /// public void Close() { - if (_topLevel is null) + if (_openState is null) { - return; - } + using (BeginIgnoringIsOpen()) + { + IsOpen = false; + } - if (_popupHost != null) - { - _popupHost.TemplateApplied -= RootTemplateApplied; + return; } - _presenterSubscription?.Dispose(); + _openState.Dispose(); + _openState = null; - CloseCurrent(); using (BeginIgnoringIsOpen()) { IsOpen = false; @@ -298,41 +332,6 @@ namespace Avalonia.Controls.Primitives Closed?.Invoke(this, EventArgs.Empty); } - void CloseCurrent() - { - if (_topLevel != null) - { - _topLevel.RemoveHandler(PointerPressedEvent, PointerPressedOutside); - var window = _topLevel as Window; - if (window != null) - window.Deactivated -= WindowDeactivated; - else - { - var parentPopuproot = _topLevel as PopupRoot; - if (parentPopuproot?.Parent is Popup popup) - { - popup.Closed -= ParentClosed; - } - } - _nonClientListener?.Dispose(); - _nonClientListener = null; - - _topLevel = null; - } - if (_popupHost != null) - { - foreach(var b in _bindings) - b.Dispose(); - _bindings.Clear(); - _popupHost.SetChild(null); - _popupHost.Hide(); - ((ISetLogicalParent)_popupHost).SetParent(null); - _popupHost.Dispose(); - _popupHost = null; - } - - } - /// /// Measures the control. /// @@ -350,6 +349,12 @@ namespace Avalonia.Controls.Primitives Close(); } + private static IDisposable SubscribeToEventHandler(T target, TEventHandler handler, Action subscribe, Action unsubscribe) + { + subscribe(target, handler); + + return Disposable.Create((unsubscribe, target, handler), state => state.unsubscribe(state.target, state.handler)); + } /// /// Called when the property changes. @@ -378,7 +383,7 @@ namespace Avalonia.Controls.Primitives { LogicalChildren.Clear(); - ((ISetLogicalParent)e.OldValue)?.SetParent(null); + ((ISetLogicalParent?)e.OldValue)?.SetParent(null); if (e.NewValue != null) { @@ -411,22 +416,28 @@ namespace Avalonia.Controls.Primitives private void RootTemplateApplied(object sender, TemplateAppliedEventArgs e) { - _popupHost.TemplateApplied -= RootTemplateApplied; - - if (_presenterSubscription != null) + if (_openState is null) { - _presenterSubscription.Dispose(); - _presenterSubscription = null; + return; } + var popupHost = _openState.PopupHost; + + popupHost.TemplateApplied -= RootTemplateApplied; + + _openState.CleanupPresenterSubscription(); + // If the Popup appears in a control template, then the child controls // that appear in the popup host need to have their TemplatedParent // properties set. - if (TemplatedParent != null) + if (TemplatedParent != null && popupHost.Presenter != null) { - _popupHost.Presenter?.ApplyTemplate(); - _popupHost.Presenter?.GetObservable(ContentPresenter.ChildProperty) + popupHost.Presenter.ApplyTemplate(); + + var presenterSubscription = popupHost.Presenter.GetObservable(ContentPresenter.ChildProperty) .Subscribe(SetTemplatedParentAndApplyChildTemplates); + + _openState.DeferCleanupPresenterSubscription(presenterSubscription); } } @@ -445,7 +456,7 @@ namespace Avalonia.Controls.Primitives if (!(control is IPresenter) && control.TemplatedParent == templatedParent) { - foreach (IControl child in control.GetVisualChildren()) + foreach (IControl child in control.VisualChildren) { SetTemplatedParentAndApplyChildTemplates(child); } @@ -455,22 +466,41 @@ namespace Avalonia.Controls.Primitives private bool IsChildOrThis(IVisual child) { - IVisual root = child.GetVisualRoot(); - while (root is IHostedVisualTreeRoot hostedRoot ) + if (_openState is null) { - if (root == this._popupHost) + return false; + } + + var popupHost = _openState.PopupHost; + + IVisual? root = child.VisualRoot; + + while (root is IHostedVisualTreeRoot hostedRoot) + { + if (root == popupHost) + { return true; - root = hostedRoot.Host?.GetVisualRoot(); + } + + root = hostedRoot.Host?.VisualRoot; } + return false; } public bool IsInsidePopup(IVisual visual) { - return _popupHost != null && ((IVisual)_popupHost)?.IsVisualAncestorOf(visual) == true; + if (_openState is null) + { + return false; + } + + var popupHost = _openState.PopupHost; + + return popupHost != null && ((IVisual)popupHost).IsVisualAncestorOf(visual); } - public bool IsPointerOverPopup => ((IInputElement)_popupHost).IsPointerOver; + public bool IsPointerOverPopup => ((IInputElement?)_openState?.PopupHost)?.IsPointerOver ?? false; private void WindowDeactivated(object sender, EventArgs e) { @@ -508,5 +538,48 @@ namespace Avalonia.Controls.Primitives _owner._ignoreIsOpenChanged = false; } } + + private class PopupOpenState : IDisposable + { + private readonly IDisposable _cleanup; + private IDisposable? _presenterCleanup; + + public PopupOpenState(TopLevel topLevel, IPopupHost popupHost, IDisposable cleanup) + { + TopLevel = topLevel; + PopupHost = popupHost; + _cleanup = cleanup; + } + + public TopLevel TopLevel { get; } + + public IPopupHost PopupHost { get; } + + public void CleanupPresenterSubscription() + { + _presenterCleanup?.Dispose(); + _presenterCleanup = null; + } + + public void DeferCleanupPresenterSubscription(IDisposable presenterCleanup) + { + if (_presenterCleanup != null) + { + // Shouldn't really happen since we always cleanup before calling this. + Debug.Assert(false); + + _presenterCleanup.Dispose(); + } + + _presenterCleanup = presenterCleanup; + } + + public void Dispose() + { + _presenterCleanup?.Dispose(); + + _cleanup.Dispose(); + } + } } } From adf59a3e6af90439639d7cd78012bdb6947cf851 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 5 Feb 2020 22:41:24 +0100 Subject: [PATCH 07/16] More Popup nullable annotations and simplify presenter cleanup. --- src/Avalonia.Controls/Primitives/Popup.cs | 45 ++++++++--------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 1a42f8d70a..9dd481cf40 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -25,8 +25,8 @@ namespace Avalonia.Controls.Primitives /// /// Defines the property. /// - public static readonly StyledProperty ChildProperty = - AvaloniaProperty.Register(nameof(Child)); + public static readonly StyledProperty ChildProperty = + AvaloniaProperty.Register(nameof(Child)); /// /// Defines the property. @@ -66,8 +66,8 @@ namespace Avalonia.Controls.Primitives /// /// Defines the property. /// - public static readonly StyledProperty PlacementTargetProperty = - AvaloniaProperty.Register(nameof(PlacementTarget)); + public static readonly StyledProperty PlacementTargetProperty = + AvaloniaProperty.Register(nameof(PlacementTarget)); /// /// Defines the property. @@ -92,7 +92,7 @@ namespace Avalonia.Controls.Primitives { IsHitTestVisibleProperty.OverrideDefaultValue(false); ChildProperty.Changed.AddClassHandler((x, e) => x.ChildChanged(e)); - IsOpenProperty.Changed.AddClassHandler((x, e) => x.IsOpenChanged(e)); + IsOpenProperty.Changed.AddClassHandler((x, e) => x.IsOpenChanged((AvaloniaPropertyChangedEventArgs)e)); } /// @@ -111,7 +111,7 @@ namespace Avalonia.Controls.Primitives /// Gets or sets the control to display in the popup. /// [Content] - public Control Child + public Control? Child { get { return GetValue(ChildProperty); } set { SetValue(ChildProperty, value); } @@ -176,7 +176,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets or sets the control that is used to determine the popup's position. /// - public Control PlacementTarget + public Control? PlacementTarget { get { return GetValue(PlacementTargetProperty); } set { SetValue(PlacementTargetProperty, value); } @@ -360,11 +360,11 @@ namespace Avalonia.Controls.Primitives /// Called when the property changes. /// /// The event args. - private void IsOpenChanged(AvaloniaPropertyChangedEventArgs e) + private void IsOpenChanged(AvaloniaPropertyChangedEventArgs e) { if (!_ignoreIsOpenChanged) { - if ((bool)e.NewValue) + if (e.NewValue.Value) { Open(); } @@ -404,13 +404,10 @@ namespace Avalonia.Controls.Primitives private void PointerPressedOutside(object sender, PointerPressedEventArgs e) { - if (!StaysOpen) + if (!StaysOpen && !IsChildOrThis((IVisual)e.Source)) { - if (!IsChildOrThis((IVisual)e.Source)) - { - Close(); - e.Handled = true; - } + Close(); + e.Handled = true; } } @@ -425,7 +422,7 @@ namespace Avalonia.Controls.Primitives popupHost.TemplateApplied -= RootTemplateApplied; - _openState.CleanupPresenterSubscription(); + _openState.SetPresenterSubscription(null); // If the Popup appears in a control template, then the child controls // that appear in the popup host need to have their TemplatedParent @@ -437,7 +434,7 @@ namespace Avalonia.Controls.Primitives var presenterSubscription = popupHost.Presenter.GetObservable(ContentPresenter.ChildProperty) .Subscribe(SetTemplatedParentAndApplyChildTemplates); - _openState.DeferCleanupPresenterSubscription(presenterSubscription); + _openState.SetPresenterSubscription(presenterSubscription); } } @@ -555,21 +552,9 @@ namespace Avalonia.Controls.Primitives public IPopupHost PopupHost { get; } - public void CleanupPresenterSubscription() + public void SetPresenterSubscription(IDisposable? presenterCleanup) { _presenterCleanup?.Dispose(); - _presenterCleanup = null; - } - - public void DeferCleanupPresenterSubscription(IDisposable presenterCleanup) - { - if (_presenterCleanup != null) - { - // Shouldn't really happen since we always cleanup before calling this. - Debug.Assert(false); - - _presenterCleanup.Dispose(); - } _presenterCleanup = presenterCleanup; } From 643217452896a0bfd1d037ce6af4fa3ffaa36b9c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 6 Feb 2020 15:13:23 +0300 Subject: [PATCH 08/16] Use CGL for offscreen rendering to IOSurface --- native/Avalonia.Native/inc/avalonia-native.h | 25 +- native/Avalonia.Native/inc/comimpl.h | 13 + native/Avalonia.Native/inc/rendertarget.h | 4 +- .../project.pbxproj | 8 +- native/Avalonia.Native/src/OSX/cgl.mm | 166 +++++++++++ native/Avalonia.Native/src/OSX/common.h | 7 +- native/Avalonia.Native/src/OSX/gl.mm | 261 ----------------- native/Avalonia.Native/src/OSX/main.mm | 12 +- .../Avalonia.Native/src/OSX/rendertarget.mm | 263 +++++++++++++++--- native/Avalonia.Native/src/OSX/window.mm | 28 +- src/Avalonia.Native/AvaloniaNativePlatform.cs | 9 +- src/Avalonia.Native/GlPlatformFeature.cs | 31 ++- src/Avalonia.Native/PopupImpl.cs | 12 +- src/Avalonia.Native/WindowImpl.cs | 10 +- src/Avalonia.Native/WindowImplBase.cs | 8 +- src/Avalonia.OpenGL/EglGlPlatformSurface.cs | 1 + .../IGlPlatformSurfaceRenderingSession.cs | 1 + src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs | 1 + .../Output/DrmOutput.cs | 2 + src/Skia/Avalonia.Skia/GlRenderTarget.cs | 2 +- .../Avalonia.Skia/PlatformRenderInterface.cs | 1 + 21 files changed, 490 insertions(+), 375 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/cgl.mm delete mode 100644 native/Avalonia.Native/src/OSX/gl.mm diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index 24607c26cb..4a960d47a1 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -177,14 +177,14 @@ AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown public: virtual HRESULT Initialize() = 0; virtual IAvnMacOptions* GetMacOptions() = 0; - virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) = 0; - virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnPopup** ppv) = 0; + virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv) = 0; + virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv) = 0; virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) = 0; virtual HRESULT CreateSystemDialogs (IAvnSystemDialogs** ppv) = 0; virtual HRESULT CreateScreens (IAvnScreens** ppv) = 0; virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0; virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0; - virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) = 0; + virtual HRESULT ObtainGlDisplay(IAvnGlDisplay** ppv) = 0; virtual HRESULT ObtainAppMenu(IAvnAppMenu** retOut) = 0; virtual HRESULT SetAppMenu(IAvnAppMenu* menu) = 0; virtual HRESULT CreateMenu (IAvnAppMenu** ppv) = 0; @@ -357,24 +357,21 @@ AVNCOM(IAvnCursorFactory, 11) : IUnknown virtual HRESULT GetCursor (AvnStandardCursorType cursorType, IAvnCursor** retOut) = 0; }; - -AVNCOM(IAvnGlFeature, 12) : IUnknown -{ - virtual HRESULT ObtainDisplay(IAvnGlDisplay**retOut) = 0; - virtual HRESULT ObtainImmediateContext(IAvnGlContext**retOut) = 0; -}; - AVNCOM(IAvnGlDisplay, 13) : IUnknown { - virtual HRESULT GetSampleCount(int* ret) = 0; - virtual HRESULT GetStencilSize(int* ret) = 0; - virtual HRESULT ClearContext() = 0; + virtual HRESULT CreateContext(IAvnGlContext* share, IAvnGlContext**ppv) = 0; + virtual void LegacyClearCurrentContext() = 0; + virtual HRESULT WrapContext(void* native, IAvnGlContext**ppv) = 0; virtual void* GetProcAddress(char* proc) = 0; }; AVNCOM(IAvnGlContext, 14) : IUnknown { - virtual HRESULT MakeCurrent() = 0; + virtual HRESULT MakeCurrent(IUnknown** ppv) = 0; + virtual HRESULT LegacyMakeCurrent() = 0; + virtual int GetSampleCount() = 0; + virtual int GetStencilSize() = 0; + virtual void* GetNativeHandle() = 0; }; AVNCOM(IAvnGlSurfaceRenderTarget, 15) : IUnknown diff --git a/native/Avalonia.Native/inc/comimpl.h b/native/Avalonia.Native/inc/comimpl.h index cf1aa4c735..0ff64b7215 100644 --- a/native/Avalonia.Native/inc/comimpl.h +++ b/native/Avalonia.Native/inc/comimpl.h @@ -162,6 +162,19 @@ public: return _obj; } + TInterface* getRetainedReference() + { + if(_obj == NULL) + return NULL; + _obj->AddRef(); + return _obj; + } + + TInterface** getPPV() + { + return &_obj; + } + operator TInterface*() const { return _obj; diff --git a/native/Avalonia.Native/inc/rendertarget.h b/native/Avalonia.Native/inc/rendertarget.h index 00d4b4bd4a..2b0338d099 100644 --- a/native/Avalonia.Native/inc/rendertarget.h +++ b/native/Avalonia.Native/inc/rendertarget.h @@ -4,9 +4,9 @@ -(HRESULT) setSwFrame: (AvnFramebuffer*) fb; -(void) resize: (AvnPixelSize) size withScale: (float) scale; -(AvnPixelSize) pixelSize; +-(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget; @end @interface IOSurfaceRenderTarget : NSObject --(IOSurfaceRenderTarget*) init; +-(IOSurfaceRenderTarget*) initWithOpenGlContext: (IAvnGlContext*) context; @end - diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 686c37bc24..50a85bdf9f 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; 1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; }; + 1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; }; 1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; }; 37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; }; 37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; }; @@ -21,7 +22,6 @@ 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; }; AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; }; AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; }; - AB573DC4217605E400D389A2 /* gl.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB573DC3217605E400D389A2 /* gl.mm */; }; AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; }; AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; }; AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; }; @@ -31,6 +31,7 @@ 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = ""; }; 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; + 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = ""; }; 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = ""; }; 379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = ""; }; @@ -47,7 +48,6 @@ 5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = ""; }; AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; - AB573DC3217605E400D389A2 /* gl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = gl.mm; sourceTree = ""; }; AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = ""; }; AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; }; @@ -88,7 +88,7 @@ 37DDA9B121933371002E132B /* AvnString.h */, 37DDA9AF219330F8002E132B /* AvnString.mm */, 37A4E71A2178846A00EACBCD /* headers */, - AB573DC3217605E400D389A2 /* gl.mm */, + 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */, 5BF943652167AD1D009CAE35 /* cursor.h */, 5B21A981216530F500CEE36E /* cursor.mm */, 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */, @@ -192,12 +192,12 @@ 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */, AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */, 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */, + 1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */, 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */, 520624B322973F4100C4DCEF /* menu.mm in Sources */, 37A517B32159597E00FBA241 /* Screens.mm in Sources */, AB00E4F72147CA920032A60A /* main.mm in Sources */, 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, - AB573DC4217605E400D389A2 /* gl.mm in Sources */, AB661C202148286E00291242 /* window.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/native/Avalonia.Native/src/OSX/cgl.mm b/native/Avalonia.Native/src/OSX/cgl.mm new file mode 100644 index 0000000000..a9d94cdf04 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/cgl.mm @@ -0,0 +1,166 @@ +#include "common.h" +#include + +static CGLContextObj CreateCglContext(CGLContextObj share) +{ + int attributes[] = { + kCGLPFAAccelerated, + kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core, + kCGLPFADepthSize, 8, + kCGLPFAStencilSize, 8, + kCGLPFAColorSize, 32, + 0 + }; + + CGLPixelFormatObj pix; + CGLError errorCode; + GLint num; // stores the number of possible pixel formats + errorCode = CGLChoosePixelFormat( (CGLPixelFormatAttribute*)attributes, &pix, &num ); + if(errorCode != 0) + return nil; + CGLContextObj ctx = nil; + errorCode = CGLCreateContext(pix, share, &ctx ); + CGLDestroyPixelFormat( pix ); + if(errorCode != 0) + return nil; + return ctx; +}; + + + +class AvnGlContext : public virtual ComSingleObject +{ + // Debug + int _usageCount = 0; +public: + CGLContextObj Context; + int SampleCount = 0, StencilBits = 0; + FORWARD_IUNKNOWN() + + class SavedGlContext : public virtual ComUnknownObject + { + CGLContextObj _savedContext; + ComPtr _parent; + public: + SavedGlContext(CGLContextObj saved, AvnGlContext* parent) + { + _savedContext = saved; + _parent = parent; + _parent->_usageCount++; + } + + ~SavedGlContext() + { + if(_parent->Context == CGLGetCurrentContext()) + CGLSetCurrentContext(_savedContext); + _parent->_usageCount--; + CGLUnlockContext(_parent->Context); + } + }; + + AvnGlContext(CGLContextObj context) + { + Context = context; + CGLPixelFormatObj fmt = CGLGetPixelFormat(context); + CGLDescribePixelFormat(fmt, 0, kCGLPFASamples, &SampleCount); + CGLDescribePixelFormat(fmt, 0, kCGLPFAStencilSize, &StencilBits); + + } + + virtual HRESULT LegacyMakeCurrent() override + { + if(CGLSetCurrentContext(Context) != 0) + return E_FAIL; + return S_OK; + } + + virtual HRESULT MakeCurrent(IUnknown** ppv) override + { + CGLContextObj saved = CGLGetCurrentContext(); + CGLLockContext(Context); + if(CGLSetCurrentContext(Context) != 0) + { + CGLUnlockContext(Context); + return E_FAIL; + } + *ppv = new SavedGlContext(saved, this); + + return S_OK; + } + + virtual int GetSampleCount() override + { + return SampleCount; + } + + virtual int GetStencilSize() override + { + return StencilBits; + } + + virtual void* GetNativeHandle() override + { + return Context; + } + + ~AvnGlContext() + { + CGLReleaseContext(Context); + } +}; + +class AvnGlDisplay : public virtual ComSingleObject +{ + void* _libgl; + +public: + FORWARD_IUNKNOWN() + + AvnGlDisplay() + { + _libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", RTLD_LAZY); + } + + virtual void* GetProcAddress(char* proc) override + { + return dlsym(_libgl, proc); + } + + virtual HRESULT CreateContext(IAvnGlContext* share, IAvnGlContext**ppv) override + { + CGLContextObj shareContext = nil; + if(share != nil) + { + AvnGlContext* shareCtx = dynamic_cast(share); + if(shareCtx != nil) + shareContext = shareCtx->Context; + } + CGLContextObj ctx = ::CreateCglContext(shareContext); + if(ctx == nil) + return E_FAIL; + *ppv = new AvnGlContext(ctx); + return S_OK; + } + + virtual HRESULT WrapContext(void* native, IAvnGlContext**ppv) override + { + if(native == nil) + return E_INVALIDARG; + *ppv = new AvnGlContext((CGLContextObj) native); + return S_OK; + } + + virtual void LegacyClearCurrentContext() override + { + CGLSetCurrentContext(nil); + } +}; + +static IAvnGlDisplay* GlDisplay = new AvnGlDisplay(); + + +extern IAvnGlDisplay* GetGlDisplay() +{ + return GlDisplay; +}; + diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 10534dea26..d7eda20f65 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -11,14 +11,13 @@ #include extern IAvnPlatformThreadingInterface* CreatePlatformThreading(); -extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events); -extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events); +extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl); +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl); extern IAvnSystemDialogs* CreateSystemDialogs(); extern IAvnScreens* CreateScreens(); extern IAvnClipboard* CreateClipboard(); extern IAvnCursorFactory* CreateCursorFactory(); -extern IAvnGlFeature* GetGlFeature(); -extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view); +extern IAvnGlDisplay* GetGlDisplay(); extern IAvnAppMenu* CreateAppMenu(); extern IAvnAppMenuItem* CreateAppMenuItem(); extern IAvnAppMenuItem* CreateAppMenuItemSeperator(); diff --git a/native/Avalonia.Native/src/OSX/gl.mm b/native/Avalonia.Native/src/OSX/gl.mm deleted file mode 100644 index feb0643654..0000000000 --- a/native/Avalonia.Native/src/OSX/gl.mm +++ /dev/null @@ -1,261 +0,0 @@ -#include "common.h" -#include -#include -#include "window.h" - -template char (&ArrayCounter(T (&a)[N]))[N]; -#define ARRAY_COUNT(a) (sizeof(ArrayCounter(a))) - -NSOpenGLPixelFormat* CreateFormat() -{ - NSOpenGLPixelFormatAttribute attribs[] = - { - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAColorSize, 32, - NSOpenGLPFAStencilSize, 8, - NSOpenGLPFADepthSize, 8, - 0 - }; - return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; -} - -class AvnGlContext : public virtual ComSingleObject -{ -public: - FORWARD_IUNKNOWN() - NSOpenGLContext* GlContext; - GLuint Framebuffer, RenderBuffer, StencilBuffer; - AvnGlContext(NSOpenGLContext* gl, bool offscreen) - { - Framebuffer = 0; - RenderBuffer = 0; - StencilBuffer = 0; - GlContext = gl; - if(offscreen) - { - [GlContext makeCurrentContext]; - - glGenFramebuffersEXT(1, &Framebuffer); - glBindFramebufferEXT(GL_FRAMEBUFFER, Framebuffer); - glGenRenderbuffersEXT(1, &RenderBuffer); - glGenRenderbuffersEXT(1, &StencilBuffer); - - glBindRenderbufferEXT(GL_RENDERBUFFER, StencilBuffer); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, StencilBuffer); - glBindRenderbufferEXT(GL_RENDERBUFFER, RenderBuffer); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, RenderBuffer); - } - - } - - - virtual HRESULT MakeCurrent() override - { - [GlContext makeCurrentContext];/* - glBindFramebufferEXT(GL_FRAMEBUFFER, Framebuffer); - glBindRenderbufferEXT(GL_RENDERBUFFER, RenderBuffer); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, RenderBuffer); - glBindRenderbufferEXT(GL_RENDERBUFFER, StencilBuffer); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, StencilBuffer);*/ - return S_OK; - } -}; - -class AvnGlDisplay : public virtual ComSingleObject -{ - int _sampleCount, _stencilSize; - void* _libgl; - -public: - FORWARD_IUNKNOWN() - - AvnGlDisplay(int sampleCount, int stencilSize) - { - _sampleCount = sampleCount; - _stencilSize = stencilSize; - _libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", RTLD_LAZY); - } - - virtual HRESULT GetSampleCount(int* ret) override - { - *ret = _sampleCount; - return S_OK; - } - virtual HRESULT GetStencilSize(int* ret) override - { - *ret = _stencilSize; - return S_OK; - } - - virtual HRESULT ClearContext() override - { - [NSOpenGLContext clearCurrentContext]; - return S_OK; - } - - virtual void* GetProcAddress(char* proc) override - { - return dlsym(_libgl, proc); - } -}; - - -class GlFeature : public virtual ComSingleObject -{ - IAvnGlDisplay* _display; - AvnGlContext *_immediate; - NSOpenGLContext* _shared; -public: - FORWARD_IUNKNOWN() - NSOpenGLPixelFormat* _format; - GlFeature(IAvnGlDisplay* display, AvnGlContext* immediate, NSOpenGLPixelFormat* format) - { - _display = display; - _immediate = immediate; - _format = format; - _shared = [[NSOpenGLContext alloc] initWithFormat:_format shareContext:_immediate->GlContext]; - } - - NSOpenGLContext* CreateContext() - { - return _shared; - //return [[NSOpenGLContext alloc] initWithFormat:_format shareContext:nil]; - } - - virtual HRESULT ObtainDisplay(IAvnGlDisplay**retOut) override - { - *retOut = _display; - _display->AddRef(); - return S_OK; - } - - virtual HRESULT ObtainImmediateContext(IAvnGlContext**retOut) override - { - *retOut = _immediate; - _immediate->AddRef(); - return S_OK; - } -}; - -static GlFeature* Feature; - -GlFeature* CreateGlFeature() -{ - auto format = CreateFormat(); - if(format == nil) - { - NSLog(@"Unable to choose pixel format"); - return NULL; - } - - auto immediateContext = [[NSOpenGLContext alloc] initWithFormat:format shareContext:nil]; - if(immediateContext == nil) - { - NSLog(@"Unable to create NSOpenGLContext"); - return NULL; - } - - int stencilBits = 0, sampleCount = 0; - - auto fmt = CGLGetPixelFormat([immediateContext CGLContextObj]); - CGLDescribePixelFormat(fmt, 0, kCGLPFASamples, &sampleCount); - CGLDescribePixelFormat(fmt, 0, kCGLPFAStencilSize, &stencilBits); - - auto offscreen = new AvnGlContext(immediateContext, true); - auto display = new AvnGlDisplay(sampleCount, stencilBits); - - return new GlFeature(display, offscreen, format); -} - - -static GlFeature* GetFeature() -{ - if(Feature == nil) - Feature = CreateGlFeature(); - return Feature; -} - -extern IAvnGlFeature* GetGlFeature() -{ - return GetFeature(); -} - -class AvnGlRenderingSession : public ComSingleObject -{ - AvnView* _view; - AvnWindow* _window; - NSOpenGLContext* _context; -public: - FORWARD_IUNKNOWN() - AvnGlRenderingSession(AvnWindow*window, AvnView* view, NSOpenGLContext* context) - { - _context = context; - _window = window; - _view = view; - } - - virtual HRESULT GetPixelSize(AvnPixelSize* ret) override - { - *ret = [_view getPixelSize]; - return S_OK; - } - virtual HRESULT GetScaling(double* ret) override - { - *ret = [_window getScaling]; - return S_OK; - } - - virtual ~AvnGlRenderingSession() - { - [_context flushBuffer]; - [NSOpenGLContext clearCurrentContext]; - CGLUnlockContext([_context CGLContextObj]); - [_view unlockFocus]; - } -}; - -class AvnGlRenderTarget : public ComSingleObject -{ - NSView* _view; - NSWindow* _window; - NSOpenGLContext* _context; -public: - FORWARD_IUNKNOWN() - AvnGlRenderTarget(NSWindow* window, NSView*view) - { - _window = window; - _view = view; - _context = GetFeature()->CreateContext(); - } - - virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) override - { - auto f = GetFeature(); - if(f == NULL) - return E_FAIL; - - @try - { - if(![_view lockFocusIfCanDraw]) - return E_ABORT; - } - @catch(NSException* exception) - { - return E_ABORT; - } - - - auto gl = _context; - CGLLockContext([_context CGLContextObj]); - [gl setView: _view]; - [gl update]; - [gl makeCurrentContext]; - *ret = new AvnGlRenderingSession(_window, _view, gl); - return S_OK; - } -}; - -extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view) -{ - return new AvnGlRenderTarget(window, view); -} diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 9418782fd1..2b6b24cfda 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -174,20 +174,20 @@ public: return (IAvnMacOptions*)new MacOptions(); } - virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) override + virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv) override { if(cb == nullptr || ppv == nullptr) return E_POINTER; - *ppv = CreateAvnWindow(cb); + *ppv = CreateAvnWindow(cb, gl); return S_OK; }; - virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv) override + virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv) override { if(cb == nullptr || ppv == nullptr) return E_POINTER; - *ppv = CreateAvnPopup(cb); + *ppv = CreateAvnPopup(cb, gl); return S_OK; } @@ -221,9 +221,9 @@ public: return S_OK; } - virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) override + virtual HRESULT ObtainGlDisplay(IAvnGlDisplay** ppv) override { - auto rv = ::GetGlFeature(); + auto rv = ::GetGlDisplay(); if(rv == NULL) return E_FAIL; rv->AddRef(); diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 3a86339496..1565417c1a 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -3,20 +3,133 @@ #import #import +#include +#include +#include +#include +#include + +@interface IOSurfaceHolder : NSObject +@end + +@implementation IOSurfaceHolder +{ + @public IOSurfaceRef surface; + @public AvnPixelSize size; + @public float scale; + ComPtr _context; + GLuint _framebuffer, _texture, _renderbuffer; +} + +- (IOSurfaceHolder*) initWithSize: (AvnPixelSize) size + withScale: (float)scale + withOpenGlContext: (IAvnGlContext*) context +{ + long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, size.Width * 4); + long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, size.Height * bytesPerRow); + NSDictionary* options = @{ + (id)kIOSurfaceWidth: @(size.Width), + (id)kIOSurfaceHeight: @(size.Height), + (id)kIOSurfacePixelFormat: @((uint)'BGRA'), + (id)kIOSurfaceBytesPerElement: @(4), + (id)kIOSurfaceBytesPerRow: @(bytesPerRow), + (id)kIOSurfaceAllocSize: @(allocSize), + + //(id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache), + (id)kIOSurfaceElementWidth: @(1), + (id)kIOSurfaceElementHeight: @(1) + }; + + surface = IOSurfaceCreate((CFDictionaryRef)options); + self->scale = scale; + self->size = size; + self->_context = context; + return self; +} + +-(HRESULT) prepareForGlRender +{ + if(_context == nil) + return E_FAIL; + if(CGLGetCurrentContext() != _context->GetNativeHandle()) + return E_FAIL; + if(_framebuffer == 0) + glGenFramebuffersEXT(1, &_framebuffer); + + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _framebuffer); + if(_texture == 0) + { + glGenTextures(1, &_texture); + + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); + CGLError res = CGLTexImageIOSurface2D((CGLContextObj)_context->GetNativeHandle(), + GL_TEXTURE_RECTANGLE_EXT, GL_RGBA8, + size.Width, size.Height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface, 0); + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0); + + if(res != 0) + { + glDeleteTextures(1, &_texture); + _texture = 0; + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + return E_FAIL; + } + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_EXT, _texture, 0); + } + + if(_renderbuffer == 0) + { + glGenRenderbuffers(1, &_renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.Width, size.Height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderbuffer); + } + + return S_OK; +} + +-(void) finishDraw +{ + ComPtr release; + _context->MakeCurrent(release.getPPV()); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glFlush(); +} + +-(void) dealloc +{ + + if(_framebuffer != 0) + { + ComPtr release; + _context->MakeCurrent(release.getPPV()); + glDeleteFramebuffers(1, &_framebuffer); + if(_texture != 0) + glDeleteTextures(1, &_texture); + if(_renderbuffer != 0) + glDeleteRenderbuffers(1, &_renderbuffer); + } + IOSurfaceDecrementUseCount(surface); +} +@end + +static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* target); + @implementation IOSurfaceRenderTarget { CALayer* _layer; - IOSurfaceRef _surface; - AvnPixelSize _size; - float _scale; - NSObject* _lock; + @public IOSurfaceHolder* surface; + @public NSObject* lock; + ComPtr _glContext; } -- (IOSurfaceRenderTarget*) init +- (IOSurfaceRenderTarget*) initWithOpenGlContext: (IAvnGlContext*) context; { self = [super init]; - _lock = [NSObject new]; - _surface = nil; + _glContext = context; + lock = [NSObject new]; + surface = nil; [self resize:{1,1} withScale: 1]; return self; @@ -31,40 +144,27 @@ } - (void)resize:(AvnPixelSize)size withScale: (float) scale;{ - @synchronized (_lock) { - if(_surface != nil) - { - IOSurfaceDecrementUseCount(_surface); - _surface = nil; - } - NSDictionary* options = @{ - (id)kIOSurfaceWidth: @(size.Width), - (id)kIOSurfaceHeight: @(size.Height), - (id)kIOSurfacePixelFormat: @((uint)'BGRA'), - (id)kIOSurfaceBytesPerElement: @(4), - //(id)kIOSurfaceBytesPerRow: @(bytesPerRow), - //(id)kIOSurfaceAllocSize: @(m_totalBytes), - - //(id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache), - (id)kIOSurfaceElementWidth: @(1), - (id)kIOSurfaceElementHeight: @(1) - }; - - _surface = IOSurfaceCreate((CFDictionaryRef)options); - _scale = scale; - _size = size; + @synchronized (lock) { + if(surface == nil + || surface->size.Width != size.Width + || surface->size.Height != size.Height + || surface->scale != scale) + surface = [[IOSurfaceHolder alloc] initWithSize:size withScale:scale withOpenGlContext:_glContext.getRaw()]; } } - (void)updateLayer { if ([NSThread isMainThread]) { - @synchronized (_lock) { + @synchronized (lock) { if(_layer == nil) return; - [_layer setContentsScale: _scale]; [_layer setContents: nil]; - [_layer setContents: (__bridge IOSurface* )_surface]; + if(surface != nil) + { + [_layer setContentsScale: surface->scale]; + [_layer setContents: (__bridge IOSurface*) surface->surface]; + } } } else @@ -79,27 +179,106 @@ } - (HRESULT)setSwFrame:(AvnFramebuffer *)fb { - @synchronized (_lock) { + @synchronized (lock) { if(fb->PixelFormat == AvnPixelFormat::kAvnRgb565) return E_INVALIDARG; - if(IOSurfaceLock(_surface, 0, nil)) + if(surface == nil) return E_FAIL; - size_t w = MIN(fb->Width, IOSurfaceGetWidth(_surface)); - size_t h = MIN(fb->Height, IOSurfaceGetHeight(_surface)); + IOSurfaceRef surf = surface->surface; + if(IOSurfaceLock(surf, 0, nil)) + return E_FAIL; + size_t w = MIN(fb->Width, IOSurfaceGetWidth(surf)); + size_t h = MIN(fb->Height, IOSurfaceGetHeight(surf)); size_t wbytes = w*4; - size_t sstride = IOSurfaceGetBytesPerRow(_surface); + size_t sstride = IOSurfaceGetBytesPerRow(surf); size_t fstride = fb->Stride; - char*pSurface = (char*)IOSurfaceGetBaseAddress(_surface); + char*pSurface = (char*)IOSurfaceGetBaseAddress(surf); char*pFb = (char*)fb->Data; - for(size_t y = 0; y +{ + ComPtr _releaseContext; + IOSurfaceRenderTarget* _target; + IOSurfaceHolder* _surface; +public: + FORWARD_IUNKNOWN() + AvnGlRenderingSession(IOSurfaceRenderTarget* target, ComPtr releaseContext) + { + _target = target; + // This happens in a synchronized block set up by AvnRenderTarget, so we take the current surface for this + // particular render session + _surface = _target->surface; + _releaseContext = releaseContext; + } + + virtual HRESULT GetPixelSize(AvnPixelSize* ret) override + { + if(!_surface) + return E_FAIL; + *ret = _surface->size; + return S_OK; + } + + virtual HRESULT GetScaling(double* ret) override + { + if(!_surface) + return E_FAIL; + *ret = _surface->scale; + return S_OK; + } + + virtual ~AvnGlRenderingSession() + { + [_surface finishDraw]; + [_target updateLayer]; + _releaseContext = nil; + } +}; + +class AvnGlRenderTarget : public ComSingleObject +{ + IOSurfaceRenderTarget* _target; +public: + FORWARD_IUNKNOWN() + AvnGlRenderTarget(IOSurfaceRenderTarget* target) + { + _target = target; + } + + virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) override + { + ComPtr releaseContext; + @synchronized (_target->lock) { + if(_target->surface == nil) + return E_FAIL; + _target->_glContext->MakeCurrent(releaseContext.getPPV()); + HRESULT res = [_target->surface prepareForGlRender]; + if(res) + return res; + *ret = new AvnGlRenderingSession(_target, releaseContext); + return S_OK; + } + } +}; + + +static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* target) +{ + return new AvnGlRenderTarget(target); +} diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 4ce8193bbb..3d6a72969f 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -26,16 +26,18 @@ public: AvnView* View; AvnWindow* Window; ComPtr BaseEvents; + ComPtr _glContext; NSObject* renderTarget; AvnPoint lastPositionSet; NSString* _lastTitle; IAvnAppMenu* _mainMenu; - WindowBaseImpl(IAvnWindowBaseEvents* events) + WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl) { _mainMenu = nullptr; BaseEvents = events; - renderTarget = [IOSurfaceRenderTarget new]; + _glContext = gl; + renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: gl]; View = [[AvnView alloc] initWithParent:this]; Window = [[AvnWindow alloc] initWithParent:this]; @@ -384,8 +386,8 @@ public: { if(View == NULL) return E_FAIL; - *ppv = ::CreateGlRenderTarget(Window, View); - return S_OK; + *ppv = [renderTarget createSurfaceRenderTarget]; + return *ppv == nil ? E_FAIL : S_OK; } protected: @@ -423,7 +425,7 @@ private: } ComPtr WindowEvents; - WindowImpl(IAvnWindowEvents* events) : WindowBaseImpl(events) + WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) { WindowEvents = events; [Window setCanBecomeKeyAndMain]; @@ -716,11 +718,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _area = nullptr; _lastPixelSize.Height = 100; _lastPixelSize.Width = 100; -/* uncomment to verify that embedding isn't broken - NSTextField* txt = [NSTextField new]; - [self addSubview:txt]; - [txt setFrame:{0,0, 100,100}]; - */ + return self; } @@ -1402,7 +1400,7 @@ private: END_INTERFACE_MAP() virtual ~PopupImpl(){} ComPtr WindowEvents; - PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events) + PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) { WindowEvents = events; [Window setLevel:NSPopUpMenuWindowLevel]; @@ -1426,20 +1424,20 @@ protected: } }; -extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events) +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) { @autoreleasepool { - IAvnPopup* ptr = dynamic_cast(new PopupImpl(events)); + IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); return ptr; } } -extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events) +extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) { @autoreleasepool { - IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events); + IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl); return ptr; } } diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 825666b909..7fc067ee4e 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -18,6 +18,7 @@ namespace Avalonia.Native { private readonly IAvaloniaNativeFactory _factory; private AvaloniaNativePlatformOptions _options; + private GlPlatformFeature _glFeature; [DllImport("libAvaloniaNative")] static extern IntPtr CreateAvaloniaNative(); @@ -88,9 +89,7 @@ namespace Avalonia.Native _factory.MacOptions.ShowInDock = macOpts?.ShowInDock != false ? 1 : 0; } - - _options.UseGpu = false; - + AvaloniaLocator.CurrentMutable .Bind() .ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface())) @@ -107,12 +106,12 @@ namespace Avalonia.Native .Bind().ToConstant(new MacOSMountedVolumeInfoProvider()); if (_options.UseGpu) AvaloniaLocator.CurrentMutable.Bind() - .ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature())); + .ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay())); } public IWindowImpl CreateWindow() { - return new WindowImpl(_factory, _options); + return new WindowImpl(_factory, _options, _glFeature); } public IEmbeddableWindowImpl CreateEmbeddableWindow() diff --git a/src/Avalonia.Native/GlPlatformFeature.cs b/src/Avalonia.Native/GlPlatformFeature.cs index e39680bdee..8e3448795c 100644 --- a/src/Avalonia.Native/GlPlatformFeature.cs +++ b/src/Avalonia.Native/GlPlatformFeature.cs @@ -8,24 +8,31 @@ namespace Avalonia.Native { class GlPlatformFeature : IWindowingPlatformGlFeature { - - public GlPlatformFeature(IAvnGlFeature feature) + public GlPlatformFeature(IAvnGlDisplay display) { - Display = new GlDisplay(feature.ObtainDisplay()); - ImmediateContext = new GlContext(Display, feature.ObtainImmediateContext()); + var immediate = display.CreateContext(null); + var deferred = display.CreateContext(immediate); + GlDisplay = new GlDisplay(display, immediate.SampleCount, immediate.StencilSize); + + ImmediateContext = new GlContext(Display, immediate); + DeferredContext = new GlContext(Display, deferred); } public IGlContext ImmediateContext { get; } - public GlDisplay Display { get; } + internal GlContext DeferredContext { get; } + internal GlDisplay GlDisplay; + public GlDisplay Display => GlDisplay; } class GlDisplay : IGlDisplay { private readonly IAvnGlDisplay _display; - public GlDisplay(IAvnGlDisplay display) + public GlDisplay(IAvnGlDisplay display, int sampleCount, int stencilSize) { _display = display; + SampleCount = sampleCount; + StencilSize = stencilSize; GlInterface = new GlInterface((name, optional) => { var rv = _display.GetProcAddress(name); @@ -39,11 +46,11 @@ namespace Avalonia.Native public GlInterface GlInterface { get; } - public int SampleCount => _display.GetSampleCount(); + public int SampleCount { get; } - public int StencilSize => _display.GetStencilSize(); + public int StencilSize { get; } - public void ClearContext() => _display.ClearContext(); + public void ClearContext() => _display.LegacyClearCurrentContext(); } class GlContext : IGlContext @@ -60,7 +67,7 @@ namespace Avalonia.Native public void MakeCurrent() { - Context.MakeCurrent(); + Context.LegacyMakeCurrent(); } } @@ -109,6 +116,9 @@ namespace Avalonia.Native public double Scaling => _session.GetScaling(); + + public bool IsYFlipped => true; + public void Dispose() { _session?.Dispose(); @@ -128,5 +138,6 @@ namespace Avalonia.Native { return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget()); } + } } diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs index d7fa1052ff..9423b2aa2a 100644 --- a/src/Avalonia.Native/PopupImpl.cs +++ b/src/Avalonia.Native/PopupImpl.cs @@ -8,19 +8,23 @@ using Avalonia.Platform; namespace Avalonia.Native { - public class PopupImpl : WindowBaseImpl, IPopupImpl + class PopupImpl : WindowBaseImpl, IPopupImpl { private readonly IAvaloniaNativeFactory _factory; private readonly AvaloniaNativePlatformOptions _opts; + private readonly GlPlatformFeature _glFeature; + public PopupImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, - IWindowBaseImpl parent) : base(opts) + GlPlatformFeature glFeature, + IWindowBaseImpl parent) : base(opts, glFeature) { _factory = factory; _opts = opts; + _glFeature = glFeature; using (var e = new PopupEvents(this)) { - Init(factory.CreatePopup(e), factory.CreateScreens()); + Init(factory.CreatePopup(e, _opts.UseGpu ? glFeature?.DeferredContext.Context : null), factory.CreateScreens()); } PopupPositioner = new ManagedPopupPositioner(new OsxManagedPopupPositionerPopupImplHelper(parent, MoveResize)); } @@ -51,7 +55,7 @@ namespace Avalonia.Native } } - public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, this); + public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, _glFeature, this); public IPopupPositioner PopupPositioner { get; } } } diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index a7828bedaf..c757576017 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -14,14 +14,18 @@ namespace Avalonia.Native { private readonly IAvaloniaNativeFactory _factory; private readonly AvaloniaNativePlatformOptions _opts; + private readonly GlPlatformFeature _glFeature; IAvnWindow _native; - public WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(opts) + internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, + GlPlatformFeature glFeature) : base(opts, glFeature) { _factory = factory; _opts = opts; + _glFeature = glFeature; using (var e = new WindowEvents(this)) { - Init(_native = factory.CreateWindow(e), factory.CreateScreens()); + Init(_native = factory.CreateWindow(e, + _opts.UseGpu ? glFeature?.DeferredContext.Context : null), factory.CreateScreens()); } NativeMenuExporter = new AvaloniaNativeMenuExporter(_native, factory); @@ -113,6 +117,6 @@ namespace Avalonia.Native public void Move(PixelPoint point) => Position = point; public override IPopupImpl CreatePopup() => - _opts.OverlayPopups ? null : new PopupImpl(_factory, _opts, this); + _opts.OverlayPopups ? null : new PopupImpl(_factory, _opts, _glFeature, this); } } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index a2148f6782..64cea1c430 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -60,9 +60,9 @@ namespace Avalonia.Native private double _savedScaling; private GlPlatformSurface _glSurface; - public WindowBaseImpl(AvaloniaNativePlatformOptions opts) + internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, GlPlatformFeature glFeature) { - _gpu = opts.UseGpu; + _gpu = opts.UseGpu && glFeature != null; _deferredRendering = opts.UseDeferredRendering; _keyboard = AvaloniaLocator.Current.GetService(); @@ -75,8 +75,8 @@ namespace Avalonia.Native _native = window; Handle = new MacOSTopLevelWindowHandle(window); - - _glSurface = new GlPlatformSurface(window); + if (_gpu) + _glSurface = new GlPlatformSurface(window); Screen = new ScreenImpl(screens); _savedLogicalSize = ClientSize; _savedScaling = Scaling; diff --git a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs index d2e4543af3..a4666bbcbf 100644 --- a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs +++ b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs @@ -107,6 +107,7 @@ namespace Avalonia.OpenGL public IGlDisplay Display => _context.Display; public PixelSize Size => _info.Size; public double Scaling => _info.Scaling; + public bool IsYFlipped { get; } } } } diff --git a/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs b/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs index 4b0de05b77..f56f47095e 100644 --- a/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs +++ b/src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs @@ -7,5 +7,6 @@ namespace Avalonia.OpenGL IGlDisplay Display { get; } PixelSize Size { get; } double Scaling { get; } + bool IsYFlipped { get; } } } diff --git a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs index 45a46bd6f5..c49903cc16 100644 --- a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs +++ b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs @@ -80,6 +80,7 @@ namespace Avalonia.X11.Glx public IGlDisplay Display => _context.Display; public PixelSize Size => _info.Size; public double Scaling => _info.Scaling; + public bool IsYFlipped { get; } } } } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs index 273265a6dc..1ebfd1a47f 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs @@ -234,6 +234,8 @@ namespace Avalonia.LinuxFramebuffer.Output public PixelSize Size => _parent._mode.Resolution; public double Scaling => _parent.Scaling; + + public bool IsYFlipped { get; } } public IGlPlatformSurfaceRenderingSession BeginDraw() diff --git a/src/Skia/Avalonia.Skia/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/GlRenderTarget.cs index 61ccf09e52..10d8ff3234 100644 --- a/src/Skia/Avalonia.Skia/GlRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/GlRenderTarget.cs @@ -54,7 +54,7 @@ namespace Avalonia.Skia new GRBackendRenderTarget(size.Width, size.Height, disp.SampleCount, disp.StencilSize, new GRGlFramebufferInfo((uint)fb, GRPixelConfig.Rgba8888.ToGlSizedFormat())); var surface = SKSurface.Create(_grContext, renderTarget, - GRSurfaceOrigin.BottomLeft, + session.IsYFlipped ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft, GRPixelConfig.Rgba8888.ToColorType()); var nfo = new DrawingContextImpl.CreateInfo diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 05c3bbdaa0..65ed1f506e 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -43,6 +43,7 @@ namespace Avalonia.Skia { GrContext = GRContext.Create(GRBackend.OpenGL, iface); } + display.ClearContext(); } } From 2722f509fa8eeea53521966ad956d21316a03ec6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 14 Jan 2020 12:53:33 +0100 Subject: [PATCH 09/16] Swap order of parameters to event args. As described in #3437 the argument ordering was different to WPF/UWP causing bugs in some code ported from WPF/UWP. Use the same argument ordering as WPF/UWP. Fixes #3437 --- src/Avalonia.Controls/AutoCompleteBox.cs | 2 +- src/Avalonia.Controls/Calendar/DatePicker.cs | 2 +- src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs | 4 ++-- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 8 ++++---- .../Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs | 4 ++-- src/Avalonia.Controls/SelectionChangedEventArgs.cs | 6 +++--- src/Avalonia.Controls/TreeView.cs | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 6deddef0d0..bf177d64cd 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -683,7 +683,7 @@ namespace Avalonia.Controls added.Add(e.NewValue); } - OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, added, removed)); + OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, removed, added)); } /// diff --git a/src/Avalonia.Controls/Calendar/DatePicker.cs b/src/Avalonia.Controls/Calendar/DatePicker.cs index 46a1f5557b..ea06bdb394 100644 --- a/src/Avalonia.Controls/Calendar/DatePicker.cs +++ b/src/Avalonia.Controls/Calendar/DatePicker.cs @@ -788,7 +788,7 @@ namespace Avalonia.Controls removedItems.Add(removedDate.Value); } - handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, addedItems, removedItems)); + handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems)); } } private void OnCalendarClosed(EventArgs e) diff --git a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs index e6e0cf7c4f..3573c67057 100644 --- a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs +++ b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs @@ -49,7 +49,7 @@ namespace Avalonia.Controls.Primitives private void InvokeCollectionChanged(System.Collections.IList removedItems, System.Collections.IList addedItems) { - _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, addedItems, removedItems)); + _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, removedItems, addedItems)); } /// @@ -119,7 +119,7 @@ namespace Avalonia.Controls.Primitives } } - _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _addedItems, _owner.RemovedItems)); + _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _owner.RemovedItems, _addedItems)); _owner.RemovedItems.Clear(); _owner.UpdateMonths(); _isRangeAdded = false; diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 69da211aa4..6bc4e71508 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -939,8 +939,8 @@ namespace Avalonia.Controls.Primitives { var changed = new SelectionChangedEventArgs( SelectionChangedEvent, - added ?? Empty, - removed ?? Empty); + removed ?? Empty, + added ?? Empty); RaiseEvent(changed); } } @@ -1055,8 +1055,8 @@ namespace Avalonia.Controls.Primitives var e = new SelectionChangedEventArgs( SelectionChangedEvent, - added != -1 ? new[] { ElementAt(Items, added) } : Array.Empty(), - removed?.Select(x => ElementAt(Items, x)).ToArray() ?? Array.Empty()); + removed?.Select(x => ElementAt(Items, x)).ToArray() ?? Array.Empty(), + added != -1 ? new[] { ElementAt(Items, added) } : Array.Empty()); RaiseEvent(e); } diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs b/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs index 7ca68140b2..bf1b80f947 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs @@ -12,11 +12,11 @@ namespace Avalonia.Controls /// public class ItemsRepeaterElementIndexChangedEventArgs : EventArgs { - internal ItemsRepeaterElementIndexChangedEventArgs(IControl element, int newIndex, int oldIndex) + internal ItemsRepeaterElementIndexChangedEventArgs(IControl element, int oldIndex, int newIndex) { Element = element; - NewIndex = newIndex; OldIndex = oldIndex; + NewIndex = newIndex; } /// diff --git a/src/Avalonia.Controls/SelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionChangedEventArgs.cs index 267ddf036e..fb412a64a8 100644 --- a/src/Avalonia.Controls/SelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionChangedEventArgs.cs @@ -16,13 +16,13 @@ namespace Avalonia.Controls /// Initializes a new instance of the class. /// /// The event being raised. - /// The items added to the selection. /// The items removed from the selection. - public SelectionChangedEventArgs(RoutedEvent routedEvent, IList addedItems, IList removedItems) + /// The items added to the selection. + public SelectionChangedEventArgs(RoutedEvent routedEvent, IList removedItems, IList addedItems) : base(routedEvent) { - AddedItems = addedItems; RemovedItems = removedItems; + AddedItems = addedItems; } /// diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 6c8d58a8dd..99e9ac2ecf 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -324,8 +324,8 @@ namespace Avalonia.Controls { var changed = new SelectionChangedEventArgs( SelectingItemsControl.SelectionChangedEvent, - added ?? Empty, - removed ?? Empty); + removed ?? Empty, + added ?? Empty); RaiseEvent(changed); } } From 119a64a1fb17fca3d70d0a5967be74a8874a1c51 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 6 Feb 2020 16:01:17 -0300 Subject: [PATCH 10/16] remove commented code. --- native/Avalonia.Native/src/OSX/window.mm | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 3d6a72969f..28393fe4c8 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -677,20 +677,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } -/* -- (BOOL)lockFocusIfCanDraw -{ - @synchronized (self) - { - if(_parent == nullptr) - { - return NO; - } - } - - return [super lockFocusIfCanDraw]; -}*/ - -(AvnPixelSize) getPixelSize { return _lastPixelSize; From 1afb8458fccee86e9849f637bdda54f3d2fbde4d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 6 Feb 2020 22:05:22 +0300 Subject: [PATCH 11/16] [OSX] Removed unused method --- native/Avalonia.Native/src/OSX/window.mm | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 28393fe4c8..b6ce172ffa 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -792,21 +792,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent return; } --(void) redrawSelf -{ - @autoreleasepool - { - @synchronized(self) - { - if(!_queuedDisplayFromThread) - return; - _queuedDisplayFromThread = false; - } - [self setNeedsDisplayInRect: [self frame]]; - [self display]; - } -} - -(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose { @autoreleasepool { From 4848e42d7ebedf60025c472c65d9a3e29d119dcc Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 6 Feb 2020 16:45:13 -0300 Subject: [PATCH 12/16] Update (c) year to 2020 --- build/SharedVersion.props | 2 +- src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/SharedVersion.props b/build/SharedVersion.props index 897e70ff81..bd183faab3 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -3,7 +3,7 @@ Avalonia 0.9.999 - Copyright 2019 © The AvaloniaUI Project + Copyright 2020 © The AvaloniaUI Project https://avaloniaui.net https://github.com/AvaloniaUI/Avalonia/ true diff --git a/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml b/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml index e227fc2812..e51cc2f3ce 100644 --- a/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml +++ b/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml @@ -99,7 +99,7 @@ - + \ No newline at end of file From 010ec8273d587c9b648574c79151c84ab5b4deb2 Mon Sep 17 00:00:00 2001 From: Symbai <14368203+Symbai@users.noreply.github.com> Date: Sat, 8 Feb 2020 14:57:19 +0100 Subject: [PATCH 13/16] Update Default.xaml --- src/Avalonia.Controls.DataGrid/Themes/Default.xaml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index 7a399349f6..14ad9c23e6 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -45,7 +45,7 @@ - + + +