diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf
index 5e770a0170..6089a06d4f 100644
--- a/Avalonia.Desktop.slnf
+++ b/Avalonia.Desktop.slnf
@@ -24,6 +24,7 @@
"src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj",
"src\\Avalonia.Fonts.Inter\\Avalonia.Fonts.Inter.csproj",
"src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj",
+ "src\\Avalonia.Metal\\Avalonia.Metal.csproj",
"src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj",
"src\\Avalonia.Native\\Avalonia.Native.csproj",
"src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj",
diff --git a/Avalonia.sln b/Avalonia.sln
index 107c55aa6f..d5419365ac 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -274,6 +274,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.XUnit.Uni
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Browser", "samples\MobileSandbox.Browser\MobileSandbox.Browser.csproj", "{43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Metal", "src\Avalonia.Metal\Avalonia.Metal.csproj", "{60B4ED1F-ECFA-453B-8A70-1788261C8355}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -661,6 +663,10 @@ Global
{43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props
index 00a1e3094b..a9af587a45 100644
--- a/build/CoreLibraries.props
+++ b/build/CoreLibraries.props
@@ -4,6 +4,7 @@
+
diff --git a/native/Avalonia.Native/inc/com.h b/native/Avalonia.Native/inc/com.h
index df251514ef..42a989e050 100644
--- a/native/Avalonia.Native/inc/com.h
+++ b/native/Avalonia.Native/inc/com.h
@@ -28,6 +28,7 @@ typedef DWORD ULONG;
#define E_UNEXPECTED 0x8000FFFFL
#define E_HANDLE 0x80070006L
#define E_INVALIDARG 0x80070057L
+#define COR_E_INVALIDOPERATION 0x80131509L
struct IUnknown
{
diff --git a/native/Avalonia.Native/inc/comimpl.h b/native/Avalonia.Native/inc/comimpl.h
index 47b0a3c5f2..017de01ee9 100644
--- a/native/Avalonia.Native/inc/comimpl.h
+++ b/native/Avalonia.Native/inc/comimpl.h
@@ -43,6 +43,18 @@ public:
_obj->AddRef();
}
}
+
+ ComPtr(TInterface* pObj, bool ownsHandle)
+ {
+ _obj = 0;
+
+ if (pObj)
+ {
+ _obj = pObj;
+ if(!ownsHandle)
+ _obj->AddRef();
+ }
+ }
ComPtr(const ComPtr& ptr)
{
@@ -92,6 +104,13 @@ public:
{
return &_obj;
}
+
+ void setNoAddRef(TInterface* value)
+ {
+ if(_obj != nullptr)
+ _obj->Release();
+ _obj = value;
+ }
operator TInterface*() const
{
diff --git a/native/Avalonia.Native/inc/noarc.h b/native/Avalonia.Native/inc/noarc.h
new file mode 100644
index 0000000000..c49d975ade
--- /dev/null
+++ b/native/Avalonia.Native/inc/noarc.h
@@ -0,0 +1,11 @@
+#import
+
+class CppAutoreleasePool
+{
+ void* _pool;
+public:
+ CppAutoreleasePool();
+ ~CppAutoreleasePool();
+};
+
+#define START_ARP_CALL CppAutoreleasePool __autoreleasePool
\ No newline at end of file
diff --git a/native/Avalonia.Native/inc/rendertarget.h b/native/Avalonia.Native/inc/rendertarget.h
index a59915777f..28572bc566 100644
--- a/native/Avalonia.Native/inc/rendertarget.h
+++ b/native/Avalonia.Native/inc/rendertarget.h
@@ -5,13 +5,21 @@
#include "avalonia-native.h"
@protocol IRenderTarget
--(void) setNewLayer: (CALayer*) layer;
--(HRESULT) setSwFrame: (AvnFramebuffer*) fb;
+
-(void) resize: (AvnPixelSize) size withScale: (float) scale;
--(AvnPixelSize) pixelSize;
--(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget;
+-(CALayer*) layer;
+
@end
@interface IOSurfaceRenderTarget : NSObject
-(IOSurfaceRenderTarget*) initWithOpenGlContext: (IAvnGlContext*) context;
+-(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget;
+-(IAvnSoftwareRenderTarget*) createSoftwareRenderTarget;
+-(HRESULT) setSwFrame: (AvnFramebuffer*) fb;
+-(void)consumeSurfaces;
@end
+
+@interface MetalRenderTarget : NSObject
+-(MetalRenderTarget*) initWithDevice: (IAvnMetalDevice*) device;
+-(void) getRenderTarget: (IAvnMetalRenderTarget**) ppv;
+@end
\ No newline at end of file
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 eb0b528fd3..f11b027173 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
@@ -43,6 +43,11 @@
523484CA26EA688F00EA0C2C /* trayicon.mm in Sources */ = {isa = PBXBuildFile; fileRef = 523484C926EA688F00EA0C2C /* trayicon.mm */; };
5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; };
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
+ 64B1EA48E308E574685AFB07 /* metal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 64B1EBEECBE13D8616D7C934 /* metal.mm */; };
+ 64B1ECA861163C0EFF0E502B /* noarc.h in Headers */ = {isa = PBXBuildFile; fileRef = 64B1E26F2B1B9C577BF52F06 /* noarc.h */; };
+ 64B1ED7C5433AC3B815FFCC5 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64B1E23CAFE5FEC7AD981B1B /* Metal.framework */; };
+ 64B1EE5DD4D882D587CE76CE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64B1E677F7791226F783ACE4 /* CoreGraphics.framework */; };
+ 64B1EF3C757B71526FFAF436 /* noarc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 64B1E4FA7D9D6E5F47AA8606 /* noarc.mm */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */ = {isa = PBXBuildFile; fileRef = 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */; };
8D2F3512292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */; };
8D300D65292D0A6800320C49 /* AvnTextInputMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */; };
@@ -99,6 +104,11 @@
5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = ""; };
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = ""; };
5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = ""; };
+ 64B1E23CAFE5FEC7AD981B1B /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
+ 64B1E26F2B1B9C577BF52F06 /* noarc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = noarc.h; path = ../../inc/noarc.h; sourceTree = ""; };
+ 64B1E4FA7D9D6E5F47AA8606 /* noarc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = noarc.mm; sourceTree = ""; };
+ 64B1E677F7791226F783ACE4 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ 64B1EBEECBE13D8616D7C934 /* metal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = metal.mm; sourceTree = ""; };
855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformBehaviorInhibition.mm; sourceTree = ""; };
8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnTextInputMethodDelegate.h; sourceTree = ""; };
8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnTextInputMethod.h; sourceTree = ""; };
@@ -126,6 +136,8 @@
AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */,
522D5959258159C1006F7F7A /* Carbon.framework in Frameworks */,
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */,
+ 64B1ED7C5433AC3B815FFCC5 /* Metal.framework in Frameworks */,
+ 64B1EE5DD4D882D587CE76CE /* CoreGraphics.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -141,6 +153,8 @@
1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */,
AB1E522B217613570091CD71 /* OpenGL.framework */,
AB661C1D2148230F00291242 /* AppKit.framework */,
+ 64B1E23CAFE5FEC7AD981B1B /* Metal.framework */,
+ 64B1E677F7791226F783ACE4 /* CoreGraphics.framework */,
);
name = Frameworks;
sourceTree = "";
@@ -198,6 +212,9 @@
18391DB45C7D892E61BF388C /* WindowInterfaces.h */,
18391BB698579F40F1783F31 /* PopupImpl.mm */,
183910513F396141938832B5 /* PopupImpl.h */,
+ 64B1EBEECBE13D8616D7C934 /* metal.mm */,
+ 64B1E4FA7D9D6E5F47AA8606 /* noarc.mm */,
+ 64B1E26F2B1B9C577BF52F06 /* noarc.h */,
);
sourceTree = "";
};
@@ -230,6 +247,7 @@
18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */,
183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */,
18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */,
+ 64B1ECA861163C0EFF0E502B /* noarc.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -319,6 +337,8 @@
18391AC16726CBC45856233B /* AvnWindow.mm in Sources */,
18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */,
EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */,
+ 64B1EA48E308E574685AFB07 /* metal.mm in Sources */,
+ 64B1EF3C757B71526FFAF436 /* noarc.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h
index 256caa70e9..e058656d3f 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.h
+++ b/native/Avalonia.Native/src/OSX/AvnView.h
@@ -11,15 +11,16 @@
#include "KeyTransform.h"
@class AvnAccessibilityElement;
+@protocol IRenderTarget;
-@interface AvnView : NSView
+@interface AvnView : NSView
-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(NSEvent* _Nonnull) lastMouseDownEvent;
-(AvnPoint) translateLocalPoint:(AvnPoint)pt;
--(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose;
-(void) onClosed;
-(AvnPlatformResizeReason) getResizeReason;
-(void) setResizeReason:(AvnPlatformResizeReason)reason;
+-(void) setRenderTarget:(NSObject*)target;
+ (AvnPoint)toAvnPoint:(CGPoint)p;
@end
diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm
index 86bacfb819..a4999b8df3 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.mm
+++ b/native/Avalonia.Native/src/OSX/AvnView.mm
@@ -17,7 +17,7 @@
NSEvent* _lastMouseDownEvent;
bool _lastKeyHandled;
AvnPixelSize _lastPixelSize;
- NSObject* _renderTarget;
+ NSObject* _currentRenderTarget;
AvnPlatformResizeReason _resizeReason;
AvnAccessibilityElement* _accessibilityChild;
NSRect _cursorRect;
@@ -41,15 +41,39 @@
- (void) updateRenderTarget
{
- [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])];
- [self setNeedsDisplayInRect:[self frame]];
+ if(_currentRenderTarget) {
+ [_currentRenderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])];
+ [self setNeedsDisplayInRect:[self frame]];
+ }
+}
+
+
+-(void) setRenderTarget:(NSObject*)target
+{
+ if([self layer])
+ {
+ [self layer].delegate = nil;
+ }
+ _currentRenderTarget = target;
+ auto layer = [target layer];
+ [self setLayer: layer];
+ [layer setDelegate: self];
+ layer.needsDisplayOnBoundsChange = YES;
+ [self updateRenderTarget];
+}
+
+-(void)displayLayer: (CALayer*)layer
+{
+ [self updateLayer];
}
-(AvnView*) initWithParent: (WindowBaseImpl*) parent
{
self = [super init];
- _renderTarget = parent->renderTarget;
[self setWantsLayer:YES];
+ [self setLayerContentsPlacement: NSViewLayerContentsPlacementTopLeft];
+
+ [self setCanDrawSubviewsIntoLayer: NO];
[self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize];
_parent = parent;
@@ -77,12 +101,6 @@
return YES;
}
-- (void)setLayer:(CALayer *)layer
-{
- [_renderTarget setNewLayer: layer];
- [super setLayer: layer];
-}
-
- (BOOL)isOpaque
{
return YES;
@@ -164,14 +182,6 @@
return;
}
--(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose
-{
- @autoreleasepool {
- [_renderTarget setSwFrame:fb];
- dispose->Release();
- }
-}
-
- (AvnPoint) translateLocalPoint:(AvnPoint)pt
{
pt.Y = [self bounds].size.height - pt.Y;
diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm
index 972d03d08c..39aa568134 100644
--- a/native/Avalonia.Native/src/OSX/PopupImpl.mm
+++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm
@@ -23,7 +23,7 @@ private:
END_INTERFACE_MAP()
virtual ~PopupImpl(){}
ComPtr WindowEvents;
- PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
+ PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events)
{
WindowEvents = events;
[Window setLevel:NSPopUpMenuWindowLevel];
@@ -47,11 +47,11 @@ public:
};
-extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)
+extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events)
{
@autoreleasepool
{
- IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl));
+ IAvnPopup* ptr = dynamic_cast(new PopupImpl(events));
return ptr;
}
}
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
index bc35ca670f..83c7aed5d3 100644
--- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
@@ -27,7 +27,7 @@ BEGIN_INTERFACE_MAP()
virtual ~WindowBaseImpl();
- WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel = false);
+ WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel = false);
virtual HRESULT ObtainNSWindowHandle(void **ret) override;
@@ -81,13 +81,15 @@ BEGIN_INTERFACE_MAP()
virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override;
- virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) override;
-
virtual HRESULT SetCursor(IAvnCursor *cursor) override;
virtual void UpdateCursor();
- virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) override;
+ virtual HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) override;
+
+ virtual HRESULT CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) override;
+
+ virtual HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) override;
virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override;
@@ -118,7 +120,6 @@ private:
void CleanNSWindow ();
NSCursor *cursor;
- ComPtr _glContext;
bool hasPosition;
NSSize lastSize;
NSSize lastMinSize;
@@ -132,7 +133,7 @@ protected:
bool _shown;
public:
- NSObject *renderTarget;
+ NSObject *currentRenderTarget;
NSWindow * Window;
ComPtr BaseEvents;
ComPtr InputMethod;
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
index edf6bcf508..b954bdd4e3 100644
--- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
@@ -16,6 +16,7 @@
#import "WindowInterfaces.h"
#include "WindowBaseImpl.h"
#include "AvnTextInputMethod.h"
+#include "AvnView.h"
WindowBaseImpl::~WindowBaseImpl() {
@@ -23,12 +24,10 @@ WindowBaseImpl::~WindowBaseImpl() {
Window = nullptr;
}
-WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel) {
+WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel) {
_shown = false;
_inResize = false;
BaseEvents = events;
- _glContext = gl;
- renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl];
View = [[AvnView alloc] initWithParent:this];
InputMethod = new AvnTextInputMethod(View);
StandardContainer = [[AutoFitContentView new] initWithContent:View];
@@ -448,13 +447,6 @@ HRESULT WindowBaseImpl::PointToScreen(AvnPoint point, AvnPoint *ret) {
}
}
-HRESULT WindowBaseImpl::ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) {
- START_COM_CALL;
-
- [View setSwRenderedFrame:fb dispose:dispose];
- return S_OK;
-}
-
HRESULT WindowBaseImpl::SetCursor(IAvnCursor *cursor) {
START_COM_CALL;
@@ -479,13 +471,49 @@ void WindowBaseImpl::UpdateCursor() {
}
}
-HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) {
+HRESULT WindowBaseImpl::CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) {
START_COM_CALL;
+ if(![NSThread isMainThread])
+ return COR_E_INVALIDOPERATION;
+
if (View == NULL)
return E_FAIL;
- *ppv = [renderTarget createSurfaceRenderTarget];
- return static_cast(*ppv == nil ? E_FAIL : S_OK);
+
+ auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: nil];
+ *ppv = [target createSoftwareRenderTarget];
+ [View setRenderTarget: target];
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) {
+ START_COM_CALL;
+
+ if(![NSThread isMainThread])
+ return COR_E_INVALIDOPERATION;
+
+ if (View == NULL)
+ return E_FAIL;
+
+ auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: glContext];
+ *ppv = [target createSurfaceRenderTarget];
+ [View setRenderTarget: target];
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) {
+ START_COM_CALL;
+
+ if(![NSThread isMainThread])
+ return COR_E_INVALIDOPERATION;
+
+ if (View == NULL)
+ return E_FAIL;
+
+ auto target = [[MetalRenderTarget alloc] initWithDevice: device];
+ [View setRenderTarget: target];
+ [target getRenderTarget: ppv];
+ return S_OK;
}
HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) {
@@ -615,11 +643,11 @@ HRESULT WindowBaseImpl::GetInputMethod(IAvnTextInputMethod **retOut) {
return S_OK;
}
-extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events)
{
@autoreleasepool
{
- IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl);
+ IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events);
return ptr;
}
}
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h
index 5140124a17..049ef755ff 100644
--- a/native/Avalonia.Native/src/OSX/WindowImpl.h
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.h
@@ -39,7 +39,7 @@ BEGIN_INTERFACE_MAP()
ComPtr WindowEvents;
- WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl);
+ WindowImpl(IAvnWindowEvents* events);
virtual HRESULT Show (bool activate, bool isDialog) override;
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm
index 925880534a..be761e0af7 100644
--- a/native/Avalonia.Native/src/OSX/WindowImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm
@@ -9,7 +9,7 @@
#include "automation.h"
#include "WindowProtocol.h"
-WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) {
+WindowImpl::WindowImpl(IAvnWindowEvents *events) : WindowBaseImpl(events) {
_isEnabled = true;
_children = std::list();
_isClientAreaExtended = false;
diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h
index 4353737dc8..5cf1b94a2f 100644
--- a/native/Avalonia.Native/src/OSX/common.h
+++ b/native/Avalonia.Native/src/OSX/common.h
@@ -6,11 +6,13 @@
#import
#import
#include
+#include "noarc.h"
extern IAvnPlatformThreadingInterface* CreatePlatformThreading();
extern void FreeAvnGCHandle(void* handle);
-extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl);
-extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl);
+extern void PostDispatcherCallback(IAvnActionCallback* cb);
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events);
+extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events);
extern IAvnSystemDialogs* CreateSystemDialogs();
extern IAvnScreens* CreateScreens();
extern IAvnClipboard* CreateClipboard(NSPasteboard*, NSPasteboardItem*);
@@ -21,6 +23,7 @@ extern NSString* GetAvnCustomDataType();
extern AvnDragDropEffects ConvertDragDropEffects(NSDragOperation nsop);
extern IAvnCursorFactory* CreateCursorFactory();
extern IAvnGlDisplay* GetGlDisplay();
+extern IAvnMetalDisplay* GetMetalDisplay();
extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events);
extern IAvnTrayIcon* CreateTrayIcon();
extern IAvnMenuItem* CreateAppMenuItem();
@@ -56,6 +59,27 @@ template inline T* objc_cast(id from) {
return nil;
}
+template class ObjCWrapper {
+public:
+ T* Value;
+ ObjCWrapper(T* value)
+ {
+ Value = value;
+ }
+ operator T*() const
+ {
+ return Value;
+ }
+ T* operator->() const
+ {
+ return Value;
+ }
+ ~ObjCWrapper()
+ {
+ Value = nil;
+ }
+};
+
@interface ActionCallback : NSObject
- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback;
- (void) action;
@@ -79,5 +103,8 @@ public:
virtual HRESULT ShowAll() override;
virtual HRESULT HideOthers() override;
};
+#define NSApp [NSApplication sharedApplication]
+
+#define START_COM_ARP_CALL START_ARP_CALL; START_COM_CALL
#endif
diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm
index 4bfda4b531..1c7e2cf25a 100644
--- a/native/Avalonia.Native/src/OSX/main.mm
+++ b/native/Avalonia.Native/src/OSX/main.mm
@@ -191,16 +191,20 @@ public:
@end
static ComPtr _deallocator;
+static ComPtr _dispatcher;
class AvaloniaNative : public ComSingleObject
{
public:
FORWARD_IUNKNOWN()
- virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, IAvnApplicationEvents* events) override
+ virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator,
+ IAvnApplicationEvents* events,
+ IAvnDispatcher* dispatcher) override
{
START_COM_CALL;
_deallocator = deallocator;
+ _dispatcher = dispatcher;
@autoreleasepool{
[[ThreadingInitializer new] do];
}
@@ -213,7 +217,7 @@ public:
return (IAvnMacOptions*)new MacOptions();
}
- virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv) override
+ virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) override
{
START_COM_CALL;
@@ -221,12 +225,12 @@ public:
{
if(cb == nullptr || ppv == nullptr)
return E_POINTER;
- *ppv = CreateAvnWindow(cb, gl);
+ *ppv = CreateAvnWindow(cb);
return S_OK;
}
};
- virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv) override
+ virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv) override
{
START_COM_CALL;
@@ -235,7 +239,7 @@ public:
if(cb == nullptr || ppv == nullptr)
return E_POINTER;
- *ppv = CreateAvnPopup(cb, gl);
+ *ppv = CreateAvnPopup(cb);
return S_OK;
}
}
@@ -320,7 +324,22 @@ public:
return S_OK;
}
}
-
+
+ virtual HRESULT ObtainMetalDisplay(IAvnMetalDisplay** ppv) override
+ {
+ START_COM_CALL;
+ @autoreleasepool
+ {
+ auto rv = ::GetMetalDisplay();
+ if(rv == NULL)
+ return E_FAIL;
+ rv->AddRef();
+ *ppv = rv;
+ return S_OK;
+ }
+ }
+
+
virtual HRESULT CreateTrayIcon (IAvnTrayIcon** ppv) override
{
START_COM_CALL;
@@ -432,6 +451,11 @@ extern void FreeAvnGCHandle(void* handle)
_deallocator->FreeGCHandle(handle);
}
+extern void PostDispatcherCallback(IAvnActionCallback* cb)
+{
+ _dispatcher->Post(cb);
+}
+
NSSize ToNSSize (AvnSize s)
{
NSSize result;
diff --git a/native/Avalonia.Native/src/OSX/metal.mm b/native/Avalonia.Native/src/OSX/metal.mm
new file mode 100644
index 0000000000..8a66592ec4
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/metal.mm
@@ -0,0 +1,161 @@
+#import
+#import
+#import
+#include "common.h"
+#include "rendertarget.h"
+
+class AvnMetalDevice : public ComSingleObject
+{
+public:
+ id device;
+ id queue;
+ FORWARD_IUNKNOWN()
+
+ void *GetDevice() override {
+ return (__bridge void*) device;
+ }
+
+ void *GetQueue() override {
+ return (__bridge void*) queue;
+ }
+
+ AvnMetalDevice(id device, id queue) : device(device), queue(queue) {
+ }
+
+};
+
+
+class AvnMetalRenderSession : public ComSingleObject
+{
+ id _drawable;
+ id _queue;
+ id _texture;
+ CAMetalLayer* _layer;
+ AvnPixelSize _size;
+ double _scaling;
+public:
+ FORWARD_IUNKNOWN()
+
+ AvnMetalRenderSession(AvnMetalDevice* device, CAMetalLayer* layer, id drawable, const AvnPixelSize &size, double scaling)
+ : _drawable(drawable), _size(size), _scaling(scaling), _queue(device->queue),
+ _texture([drawable texture]) {
+ _layer = layer;
+ }
+
+ HRESULT GetPixelSize(AvnPixelSize *ret) override {
+ *ret = _size;
+ return 0;
+ }
+
+ double GetScaling() override {
+ return _scaling;
+ }
+
+ void *GetTexture() override {
+ return (__bridge void*) _texture;
+ }
+
+ ~AvnMetalRenderSession()
+ {
+ auto buffer = [_queue commandBuffer];
+ [buffer presentDrawable: _drawable];
+ [buffer commit];
+ }
+};
+
+class AvnMetalRenderTarget : public ComSingleObject
+{
+ CAMetalLayer* _layer;
+ double _scaling = 1;
+ AvnPixelSize _size = {1,1};
+ ComPtr _device;
+public:
+ double PendingScaling = 1;
+ AvnPixelSize PendingSize = {1,1};
+ FORWARD_IUNKNOWN()
+ AvnMetalRenderTarget(CAMetalLayer* layer, ComPtr device)
+ {
+ _layer = layer;
+ _device = device;
+ }
+
+ HRESULT BeginDrawing(IAvnMetalRenderingSession **ret) override {
+ if([NSThread isMainThread])
+ {
+ // Flush all existing rendering
+ auto buffer = [_device->queue commandBuffer];
+ [buffer commit];
+ [buffer waitUntilCompleted];
+ _size = PendingSize;
+ _scaling= PendingScaling;
+ CGSize layerSize = {(CGFloat)_size.Width, (CGFloat)_size.Height};
+
+ [_layer setDrawableSize: layerSize];
+ }
+ auto drawable = [_layer nextDrawable];
+ if(drawable == nil)
+ {
+ ret = nil;
+ return E_FAIL;
+ }
+ *ret = new AvnMetalRenderSession(_device, _layer, drawable, _size, _scaling);
+ return 0;
+ }
+};
+
+@implementation MetalRenderTarget
+{
+ ComPtr _device;
+ CAMetalLayer* _layer;
+ ComPtr _target;
+}
+- (MetalRenderTarget *)initWithDevice:(IAvnMetalDevice *)device {
+ _device = dynamic_cast(device);
+ _layer = [CAMetalLayer new];
+ _layer.device = _device->device;
+ _target.setNoAddRef(new AvnMetalRenderTarget(_layer, _device));
+ return self;
+}
+
+
+-(void) getRenderTarget: (IAvnMetalRenderTarget**) ppv
+{
+ *ppv = static_cast(_target.getRetainedReference());
+}
+
+- (void)resize:(AvnPixelSize)size withScale:(float)scale {
+ CGSize layerSize = {(CGFloat)size.Width, (CGFloat)size.Height};
+ _target->PendingScaling = scale;
+ _target->PendingSize = size;
+ [_layer setNeedsDisplay];
+}
+
+- (CALayer *)layer {
+ return _layer;
+}
+@end
+
+
+class AvnMetalDisplay : public ComSingleObject
+{
+public:
+ FORWARD_IUNKNOWN()
+ HRESULT CreateDevice(IAvnMetalDevice **ret) override {
+
+ auto device = MTLCreateSystemDefaultDevice();
+ if(device == nil) {
+ ret = nil;
+ return E_FAIL;
+ }
+ auto queue = [device newCommandQueue];
+ *ret = new AvnMetalDevice(device, queue);
+ return S_OK;
+ }
+};
+
+static AvnMetalDisplay* _display = new AvnMetalDisplay();
+
+extern IAvnMetalDisplay* GetMetalDisplay()
+{
+ return _display;
+}
\ No newline at end of file
diff --git a/native/Avalonia.Native/src/OSX/noarc.mm b/native/Avalonia.Native/src/OSX/noarc.mm
new file mode 100644
index 0000000000..82378ce84c
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/noarc.mm
@@ -0,0 +1,11 @@
+#include "noarc.h"
+
+CppAutoreleasePool::CppAutoreleasePool()
+{
+ _pool = [[NSAutoreleasePool alloc] init];
+}
+
+CppAutoreleasePool::~CppAutoreleasePool() {
+ auto ptr = (NSAutoreleasePool*)_pool;
+ [ptr release];
+}
diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm
index 1c22c91207..b44a605803 100644
--- a/native/Avalonia.Native/src/OSX/rendertarget.mm
+++ b/native/Avalonia.Native/src/OSX/rendertarget.mm
@@ -5,15 +5,12 @@
#include
#include
+#include
-@interface IOSurfaceHolder : NSObject
-@end
-
-@implementation IOSurfaceHolder
+@implementation IOSurfaceHolder : NSObject
{
@public IOSurfaceRef surface;
@public AvnPixelSize size;
- @public bool hasContent;
@public float scale;
ComPtr _context;
GLuint _framebuffer, _texture, _renderbuffer;
@@ -42,7 +39,6 @@
self->scale = scale;
self->size = size;
self->_context = context;
- self->hasContent = false;
return self;
}
@@ -76,7 +72,7 @@
}
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_EXT, _texture, 0);
}
-
+
if(_renderbuffer == 0)
{
glGenRenderbuffers(1, &_renderbuffer);
@@ -84,48 +80,72 @@
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);
+ if(_framebuffer != 0) {
+ glDeleteFramebuffers(1, &_framebuffer);
+ _framebuffer = 0;
+ }
+ if(_texture != 0) {
+ glDeleteTextures(1, &_texture);
+ _texture = 0;
+ }
+ if(_renderbuffer != 0) {
+ glDeleteRenderbuffers(1, &_renderbuffer);
+ _renderbuffer = 0;
+ }
glFlush();
- self->hasContent = true;
}
-(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);
- }
-
if(surface != nullptr)
{
CFRelease(surface);
+ surface = nil;
}
}
@end
+
static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* target);
+static IAvnSoftwareRenderTarget* CreateSoftwareRenderTarget(IOSurfaceRenderTarget* target);
+
+static bool SizeEquals(AvnPixelSize& left, AvnPixelSize& right)
+{
+ return left.Width == right.Width && right.Height == left.Height;
+}
+
+class ConsumeSurfacesCallback : public ComSingleObject
+{
+ IOSurfaceRenderTarget* _target;
+public:
+ FORWARD_IUNKNOWN()
+ ConsumeSurfacesCallback(IOSurfaceRenderTarget* target)
+ {
+ _target = target;
+ }
+
+ void Run() override {
+ [_target consumeSurfaces];
+ }
+};
@implementation IOSurfaceRenderTarget
{
CALayer* _layer;
- @public IOSurfaceHolder* surface;
@public NSObject* lock;
ComPtr _glContext;
+ bool _consumeSurfacesScheduled;
+ std::queue> _surfaces;
+ IOSurfaceHolder* _activeSurface;
+ AvnPixelSize _size;
+ float _scale;
}
- (IOSurfaceRenderTarget*) initWithOpenGlContext: (IAvnGlContext*) context;
@@ -133,16 +153,12 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
self = [super init];
_glContext = context;
lock = [NSObject new];
- surface = nil;
+ _layer = [CALayer new];
[self resize:{1,1} withScale: 1];
return self;
}
-- (AvnPixelSize) pixelSize {
- return {1, 1};
-}
-
- (CALayer *)layer {
return _layer;
}
@@ -155,57 +171,110 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
size.Width = 1;
@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()];
-
- [self updateLayer];
- }
+ _size = size;
+ _scale = scale;
}
}
-- (void)updateLayer {
- if ([NSThread isMainThread])
+- (void)setSurfaceInUiThreadContext: (IOSurfaceHolder*) surface
+{
+ [CATransaction begin];
+ if(surface == _activeSurface)
+ [_layer setContents: nil];
+ _activeSurface = surface;
+ [_layer setContentsScale: _activeSurface->scale];
+ [_layer setContents: (__bridge IOSurface*) _activeSurface->surface];
+ [CATransaction commit];
+}
+
+- (void)consumeSurfaces {
+ @synchronized (lock) {
+ _consumeSurfacesScheduled = false;
+
+ while(_surfaces.size() > 1)
+ _surfaces.pop();
+
+ if(_surfaces.size() == 0)
+ return;
+
+ auto targetSurface = _surfaces.front();
+ _surfaces.pop();
+
+ [self setSurfaceInUiThreadContext: targetSurface];
+ }
+ // This can trigger event processing on the main thread
+ // which might need to lock the renderer
+ // which can cause a deadlock. So flush call is outside of the lock
+ [CATransaction flush];
+
+}
+
+- (IOSurfaceHolder*) getNextSurfaceInSafeContext
+{
+ IOSurfaceHolder* targetSurface = nil;
+ if([NSThread isMainThread])
{
- @synchronized (lock) {
- if(_layer == nil)
- return;
- if(!surface->hasContent)
- return;
- [CATransaction begin];
- [_layer setContents: nil];
- if(surface != nil)
+ // Drain the surface queue and try to find a surface usable for rendering
+ while(_surfaces.size() > 0)
+ {
+ auto front = _surfaces.front();
+ _surfaces.pop();
+ if(targetSurface == nil && SizeEquals(front.Value->size, _size))
{
- [_layer setContentsScale: surface->scale];
- [_layer setContents: (__bridge IOSurface*) surface->surface];
+ targetSurface = front;
}
- [CATransaction commit];
}
- // This can trigger event processing on the main thread
- // which might need to lock the renderer
- // which can cause a deadlock. So flush call is outside of the lock
- [CATransaction flush];
+ if(targetSurface == nil && _activeSurface != nil && SizeEquals(_activeSurface->size, _size))
+ targetSurface = _activeSurface;
+ }
+ else
+ {
+ // Try to reuse an outdated surface that is still not picked up by the UI thread
+ while(_surfaces.size() > 1)
+ {
+ auto front = _surfaces.front();
+ _surfaces.pop();
+
+ // Simply discard the surface on size mismatch
+ if(SizeEquals(front.Value->size, _size))
+ targetSurface = front;
+ }
}
+
+ if(targetSurface == nil)
+ targetSurface = [[IOSurfaceHolder alloc] initWithSize: _size withScale: _scale withOpenGlContext: _glContext];
+ return targetSurface;
+}
+
+- (void) presentSurfaceInSafeContext: (IOSurfaceHolder*) surface
+{
+ if([NSThread isMainThread])
+ [self setSurfaceInUiThreadContext: surface];
else
- dispatch_async(dispatch_get_main_queue(), ^{
- [self updateLayer];
- });
+ {
+ _surfaces.push(surface);
+ if(_consumeSurfacesScheduled)
+ return;
+ _consumeSurfacesScheduled = true;
+ __block auto strongSelf = self;
+ ComPtr cb(new ConsumeSurfacesCallback(self), true);
+ PostDispatcherCallback(cb);
+ }
}
-- (void) setNewLayer:(CALayer *)layer {
- _layer = layer;
- [self updateLayer];
+- (void) presentSurface: (IOSurfaceHolder*) surface
+{
+ @synchronized(lock)
+ {
+ [self presentSurfaceInSafeContext: surface];
+ }
}
- (HRESULT)setSwFrame:(AvnFramebuffer *)fb {
@synchronized (lock) {
if(fb->PixelFormat == AvnPixelFormat::kAvnRgb565)
return E_INVALIDARG;
- if(surface == nil)
- return E_FAIL;
+ auto surface = [self getNextSurfaceInSafeContext];
IOSurfaceRef surf = surface->surface;
if(IOSurfaceLock(surf, 0, nil))
return E_FAIL;
@@ -221,8 +290,7 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
memcpy(pSurface + y*sstride, pFb + y*fstride, wbytes);
}
IOSurfaceUnlock(surf, 0, nil);
- surface->hasContent = true;
- [self updateLayer];
+ [self presentSurfaceInSafeContext: surface];
return S_OK;
}
}
@@ -232,6 +300,11 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
return CreateGlRenderTarget(self);
}
+-(IAvnSoftwareRenderTarget*) createSoftwareRenderTarget
+{
+ return CreateSoftwareRenderTarget(self);
+}
+
@end
class AvnGlRenderingSession : public ComSingleObject
@@ -241,12 +314,12 @@ class AvnGlRenderingSession : public ComSingleObject releaseContext)
+ AvnGlRenderingSession(IOSurfaceRenderTarget* target, IOSurfaceHolder* surface, 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;
+ _surface = surface;
_releaseContext = releaseContext;
}
@@ -272,8 +345,9 @@ public:
virtual ~AvnGlRenderingSession()
{
+ START_ARP_CALL;
[_surface finishDraw];
- [_target updateLayer];
+ [_target presentSurface: _surface];
_releaseContext = nil;
}
};
@@ -290,17 +364,15 @@ public:
virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) override
{
- START_COM_CALL;
-
+ START_COM_ARP_CALL;
ComPtr releaseContext;
@synchronized (_target->lock) {
- if(_target->surface == nil)
- return E_FAIL;
+ auto surface = [_target getNextSurfaceInSafeContext];
_target->_glContext->MakeCurrent(releaseContext.getPPV());
- HRESULT res = [_target->surface prepareForGlRender];
+ HRESULT res = [surface prepareForGlRender];
if(res)
return res;
- *ret = new AvnGlRenderingSession(_target, releaseContext);
+ *ret = new AvnGlRenderingSession(_target, surface, releaseContext);
return S_OK;
}
}
@@ -310,4 +382,27 @@ public:
static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* target)
{
return new AvnGlRenderTarget(target);
-}
+};
+
+
+class AvnSoftwareRenderTarget : public ComSingleObject
+{
+ IOSurfaceRenderTarget* _target;
+public:
+ FORWARD_IUNKNOWN()
+
+ AvnSoftwareRenderTarget(IOSurfaceRenderTarget *target) {
+ _target = target;
+ }
+
+ HRESULT SetFrame(AvnFramebuffer *fb) override {
+ START_COM_ARP_CALL;
+ [_target setSwFrame: fb];
+ return 0;
+ }
+};
+
+static IAvnSoftwareRenderTarget* CreateSoftwareRenderTarget(IOSurfaceRenderTarget* target)
+{
+ return new AvnSoftwareRenderTarget(target);
+};
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs
index 56a4eb22d4..365c9cdb42 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/FramebufferManager.cs
@@ -13,5 +13,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
}
public ILockedFramebuffer Lock() => new AndroidFramebuffer(_topLevel.InternalView.Holder.Surface, _topLevel.RenderScaling);
+
+ public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock);
}
}
diff --git a/src/Avalonia.Base/RenderTargetNotReadyException.cs b/src/Avalonia.Base/RenderTargetNotReadyException.cs
new file mode 100644
index 0000000000..e34dd41709
--- /dev/null
+++ b/src/Avalonia.Base/RenderTargetNotReadyException.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Avalonia;
+
+public class RenderTargetNotReadyException : Exception
+{
+ public RenderTargetNotReadyException()
+ {
+ }
+
+ public RenderTargetNotReadyException(string message)
+ : base(message)
+ {
+ }
+
+ public RenderTargetNotReadyException(Exception innerException)
+ : base(null, innerException)
+ {
+ }
+
+ public RenderTargetNotReadyException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+}
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
index 8f1aa1cb49..3883e4e574 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
@@ -135,7 +135,18 @@ namespace Avalonia.Rendering.Composition.Server
_redrawRequested = true;
}
- _renderTarget ??= _compositor.CreateRenderTarget(_surfaces());
+ try
+ {
+ _renderTarget ??= _compositor.CreateRenderTarget(_surfaces());
+ }
+ catch (RenderTargetNotReadyException)
+ {
+ return;
+ }
+ catch (RenderTargetCorruptedException)
+ {
+ return;
+ }
if ((_dirtyRect.Width == 0 && _dirtyRect.Height == 0) && !_redrawRequested)
return;
diff --git a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs
index 0a7daeaa24..22ee322e36 100644
--- a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs
+++ b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs
@@ -1,10 +1,17 @@
-using Avalonia.Metadata;
+using System;
+using Avalonia.Metadata;
using Avalonia.Platform;
namespace Avalonia.Controls.Platform.Surfaces
{
[Unstable]
public interface IFramebufferPlatformSurface
+ {
+ IFramebufferRenderTarget CreateFramebufferRenderTarget();
+ }
+
+ [Unstable]
+ public interface IFramebufferRenderTarget : IDisposable
{
///
/// Provides a framebuffer descriptor for drawing.
@@ -14,4 +21,24 @@ namespace Avalonia.Controls.Platform.Surfaces
///
ILockedFramebuffer Lock();
}
+
+ ///
+ /// For simple cases when framebuffer is always available
+ ///
+ public class FuncFramebufferRenderTarget : IFramebufferRenderTarget
+ {
+ private readonly Func _lockFramebuffer;
+
+ public FuncFramebufferRenderTarget(Func lockFramebuffer)
+ {
+ _lockFramebuffer = lockFramebuffer;
+ }
+
+ public void Dispose()
+ {
+ // No-op
+ }
+
+ public ILockedFramebuffer Lock() => _lockFramebuffer();
+ }
}
diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
index 74f12280bb..ac7e8ec179 100644
--- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
@@ -282,9 +282,6 @@ namespace Avalonia.Controls.Remote.Server
}
}
- public ILockedFramebuffer Lock()
- => GetOrCreateFramebuffer().Lock(_sendLastFrameIfNeeded);
-
private void SendLastFrameIfNeeded()
{
if (IsDisposed)
@@ -328,5 +325,8 @@ namespace Avalonia.Controls.Remote.Server
public override IMouseDevice MouseDevice { get; } = new MouseDevice();
public IKeyboardDevice KeyboardDevice { get; }
+
+ public IFramebufferRenderTarget CreateFramebufferRenderTarget() =>
+ new FuncFramebufferRenderTarget(() => GetOrCreateFramebuffer().Lock(_sendLastFrameIfNeeded));
}
}
diff --git a/src/Avalonia.Metal/Avalonia.Metal.csproj b/src/Avalonia.Metal/Avalonia.Metal.csproj
new file mode 100644
index 0000000000..6b17caecbb
--- /dev/null
+++ b/src/Avalonia.Metal/Avalonia.Metal.csproj
@@ -0,0 +1,15 @@
+
+
+ net6.0;netstandard2.0
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Metal/IMetalDevice.cs b/src/Avalonia.Metal/IMetalDevice.cs
new file mode 100644
index 0000000000..0910e6aa17
--- /dev/null
+++ b/src/Avalonia.Metal/IMetalDevice.cs
@@ -0,0 +1,34 @@
+using System;
+using Avalonia.Metadata;
+using Avalonia.Platform;
+
+namespace Avalonia.Metal;
+
+
+[PrivateApi]
+public interface IMetalDevice : IPlatformGraphicsContext
+{
+ IntPtr Device { get; }
+ IntPtr CommandQueue { get; }
+}
+
+[PrivateApi]
+public interface IMetalPlatformSurface
+{
+ IMetalPlatformSurfaceRenderTarget CreateMetalRenderTarget(IMetalDevice device);
+}
+
+[PrivateApi]
+public interface IMetalPlatformSurfaceRenderTarget : IDisposable
+{
+ IMetalPlatformSurfaceRenderingSession BeginRendering();
+}
+
+[PrivateApi]
+public interface IMetalPlatformSurfaceRenderingSession : IDisposable
+{
+ IntPtr Texture { get; }
+ PixelSize Size { get; }
+ double Scaling { get; }
+ bool IsYFlipped { get; }
+}
diff --git a/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs b/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs
index 8e40960af6..16280a90cd 100644
--- a/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs
+++ b/src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs
@@ -192,10 +192,13 @@ namespace Avalonia.Native
{
_window = window;
}
+
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context)
{
+ if (!Dispatcher.UIThread.CheckAccess())
+ throw new RenderTargetNotReadyException();
var avnContext = (GlContext)context;
- return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget(), avnContext);
+ return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget(avnContext.Context), avnContext);
}
}
diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs
index 5d5e17839f..7da15811db 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs
@@ -19,7 +19,7 @@ namespace Avalonia.Native
{
private readonly IAvaloniaNativeFactory _factory;
private AvaloniaNativePlatformOptions? _options;
- private AvaloniaNativeGlPlatformGraphics? _platformGl;
+ private IPlatformGraphics? _platformGraphics;
[DllImport("libAvaloniaNative")]
static extern IntPtr CreateAvaloniaNative();
@@ -93,7 +93,7 @@ namespace Avalonia.Native
if (_factory.MacOptions != null)
_factory.MacOptions.SetDisableAppDelegate(macOpts.DisableAvaloniaAppDelegate ? 1 : 0);
- _factory.Initialize(new GCHandleDeallocator(), applicationPlatform);
+ _factory.Initialize(new GCHandleDeallocator(), applicationPlatform, new AvnDispatcher());
if (_factory.MacOptions != null)
{
@@ -126,20 +126,38 @@ namespace Avalonia.Native
if (_options.UseGpu)
{
- try
+ if (_options.UseMetal)
{
- _platformGl = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay());
- AvaloniaLocator.CurrentMutable
- .Bind().ToConstant(_platformGl);
-
+ try
+ {
+ var metal = new MetalPlatformGraphics(_factory);
+ metal.CreateContext().Dispose();
+ _platformGraphics = metal;
+ }
+ catch
+ {
+ // Ignored
+ }
}
- catch (Exception)
+
+ if (_platformGraphics == null)
{
- // ignored
+ try
+ {
+ _platformGraphics = new AvaloniaNativeGlPlatformGraphics(_factory.ObtainGlDisplay());
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
}
+
+ if(_platformGraphics != null)
+ AvaloniaLocator.CurrentMutable
+ .Bind().ToConstant(_platformGraphics);
}
-
- Compositor = new Compositor(_platformGl, true);
+
+ Compositor = new Compositor(_platformGraphics, true);
}
public ITrayIconImpl CreateTrayIcon()
@@ -149,7 +167,7 @@ namespace Avalonia.Native
public IWindowImpl CreateWindow()
{
- return new WindowImpl(_factory, _options, _platformGl);
+ return new WindowImpl(_factory, _options);
}
public IWindowImpl CreateEmbeddableWindow()
diff --git a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
index 2b989ce733..78b4b0fbf3 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
@@ -35,6 +35,8 @@ namespace Avalonia
///
public bool UseGpu { get; set; } = true;
+ public bool UseMetal { get; set; }
+
///
/// Embeds popups to the window when set to true. The default value is false.
///
diff --git a/src/Avalonia.Native/AvnDispatcher.cs b/src/Avalonia.Native/AvnDispatcher.cs
new file mode 100644
index 0000000000..b3658d5e67
--- /dev/null
+++ b/src/Avalonia.Native/AvnDispatcher.cs
@@ -0,0 +1,18 @@
+using Avalonia.Native.Interop;
+using Avalonia.Threading;
+using MicroCom.Runtime;
+
+namespace Avalonia.Native;
+
+class AvnDispatcher : NativeCallbackBase, IAvnDispatcher
+{
+ public void Post(IAvnActionCallback cb)
+ {
+ var callback = cb.CloneReference();
+ Dispatcher.UIThread.Post(() =>
+ {
+ using (callback)
+ callback.Run();
+ }, DispatcherPriority.Send);
+ }
+}
diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs
index c390459286..0942bee508 100644
--- a/src/Avalonia.Native/DeferredFramebuffer.cs
+++ b/src/Avalonia.Native/DeferredFramebuffer.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
+using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Native.Interop;
using Avalonia.Platform;
@@ -7,11 +8,13 @@ namespace Avalonia.Native
{
internal unsafe class DeferredFramebuffer : ILockedFramebuffer
{
- private readonly Func, bool> _lockWindow;
-
- public DeferredFramebuffer(Func, bool> lockWindow,
+ private readonly IAvnSoftwareRenderTarget _renderTarget;
+ private readonly Action> _lockWindow;
+
+ public DeferredFramebuffer(IAvnSoftwareRenderTarget renderTarget, Action> lockWindow,
int width, int height, Vector dpi)
{
+ _renderTarget = renderTarget;
_lockWindow = lockWindow;
Address = Marshal.AllocHGlobal(width * height * 4);
Size = new PixelSize(width, height);
@@ -27,54 +30,28 @@ namespace Avalonia.Native
public Vector Dpi { get; set; }
public PixelFormat Format { get; set; }
- class Disposer : NativeCallbackBase
- {
- private IntPtr _ptr;
-
- public Disposer(IntPtr ptr)
- {
- _ptr = ptr;
- }
-
- protected override void Destroyed()
- {
- if(_ptr != IntPtr.Zero)
- {
- Marshal.FreeHGlobal(_ptr);
- _ptr = IntPtr.Zero;
- }
- }
- }
-
public void Dispose()
{
if (Address == IntPtr.Zero)
return;
- if (!_lockWindow(win =>
+ _lockWindow(win =>
{
var fb = new AvnFramebuffer
{
Data = Address.ToPointer(),
- Dpi = new AvnVector
- {
- X = Dpi.X,
- Y = Dpi.Y
- },
+ Dpi = new AvnVector { X = Dpi.X, Y = Dpi.Y },
Width = Size.Width,
Height = Size.Height,
PixelFormat = (AvnPixelFormat)Format.FormatEnum,
Stride = RowBytes
};
- using (var d = new Disposer(Address))
- {
- win.ThreadSafeSetSwRenderedFrame(&fb, d);
- }
- }))
- {
- Marshal.FreeHGlobal(Address);
- }
+ _renderTarget.SetFrame(&fb);
+
+ });
+
+ Marshal.FreeHGlobal(Address);
Address = IntPtr.Zero;
}
diff --git a/src/Avalonia.Native/Metal.cs b/src/Avalonia.Native/Metal.cs
new file mode 100644
index 0000000000..8c8d293576
--- /dev/null
+++ b/src/Avalonia.Native/Metal.cs
@@ -0,0 +1,119 @@
+using System;
+using Avalonia.Metal;
+using Avalonia.Native.Interop;
+using Avalonia.Platform;
+using Avalonia.Threading;
+using Avalonia.Utilities;
+
+namespace Avalonia.Native;
+
+class MetalPlatformGraphics : IPlatformGraphics
+{
+ private readonly IAvnMetalDisplay _display;
+
+ public MetalPlatformGraphics(IAvaloniaNativeFactory factory)
+ {
+ _display = factory.ObtainMetalDisplay();
+ }
+ public bool UsesSharedContext => false;
+ public IPlatformGraphicsContext CreateContext() => new MetalDevice(_display.CreateDevice());
+
+ public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException();
+}
+
+class MetalDevice : IMetalDevice
+{
+ public IAvnMetalDevice Native { get; private set; }
+ private DisposableLock _syncRoot = new();
+
+
+ public MetalDevice(IAvnMetalDevice native)
+ {
+ Native = native;
+ }
+
+ public void Dispose()
+ {
+ Native?.Dispose();
+ Native = null;
+ }
+
+ public object TryGetFeature(Type featureType) => null;
+
+ public bool IsLost => false;
+
+ public IDisposable EnsureCurrent() => _syncRoot.Lock();
+
+ public IntPtr Device => Native.Device;
+ public IntPtr CommandQueue => Native.Queue;
+}
+
+class MetalPlatformSurface : IMetalPlatformSurface
+{
+ private readonly IAvnWindowBase _window;
+
+ public MetalPlatformSurface(IAvnWindowBase window)
+ {
+ _window = window;
+ }
+ public IMetalPlatformSurfaceRenderTarget CreateMetalRenderTarget(IMetalDevice device)
+ {
+ if (!Dispatcher.UIThread.CheckAccess())
+ throw new RenderTargetNotReadyException();
+
+ var dev = (MetalDevice)device;
+ var target = _window.CreateMetalRenderTarget(dev.Native);
+ return new MetalRenderTarget(target);
+ }
+}
+
+internal class MetalRenderTarget : IMetalPlatformSurfaceRenderTarget
+{
+ private IAvnMetalRenderTarget _native;
+
+ public MetalRenderTarget(IAvnMetalRenderTarget native)
+ {
+ _native = native;
+ }
+
+ public void Dispose()
+ {
+ _native?.Dispose();
+ _native = null;
+ }
+
+ public IMetalPlatformSurfaceRenderingSession BeginRendering()
+ {
+ var session = _native.BeginDrawing();
+ return new MetalDrawingSession(session);
+ }
+}
+
+internal class MetalDrawingSession : IMetalPlatformSurfaceRenderingSession
+{
+ private IAvnMetalRenderingSession _session;
+
+ public MetalDrawingSession(IAvnMetalRenderingSession session)
+ {
+ _session = session;
+ }
+
+ public void Dispose()
+ {
+ _session?.Dispose();
+ _session = null;
+ }
+
+ public IntPtr Texture => _session.Texture;
+ public PixelSize Size
+ {
+ get
+ {
+ var size = _session.PixelSize;
+ return new(size.Width, size.Height);
+ }
+ }
+
+ public double Scaling => _session.Scaling;
+ public bool IsYFlipped => false;
+}
diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs
index 6b7f7e8883..2dad083767 100644
--- a/src/Avalonia.Native/PopupImpl.cs
+++ b/src/Avalonia.Native/PopupImpl.cs
@@ -8,21 +8,15 @@ namespace Avalonia.Native
{
class PopupImpl : WindowBaseImpl, IPopupImpl
{
- private readonly AvaloniaNativePlatformOptions _opts;
- private readonly AvaloniaNativeGlPlatformGraphics _glFeature;
private readonly IWindowBaseImpl _parent;
public PopupImpl(IAvaloniaNativeFactory factory,
- AvaloniaNativePlatformOptions opts,
- AvaloniaNativeGlPlatformGraphics glFeature,
- IWindowBaseImpl parent) : base(factory, opts, glFeature)
+ IWindowBaseImpl parent) : base(factory)
{
- _opts = opts;
- _glFeature = glFeature;
_parent = parent;
using (var e = new PopupEvents(this))
{
- Init(factory.CreatePopup(e, _glFeature.SharedContext.Context), factory.CreateScreens());
+ Init(factory.CreatePopup(e), factory.CreateScreens());
}
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));
}
@@ -68,7 +62,7 @@ namespace Avalonia.Native
base.Show(false, isDialog);
}
- public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, _glFeature, this);
+ public override IPopupImpl CreatePopup() => new PopupImpl(_factory, this);
public void SetWindowManagerAddShadowHint(bool enabled)
{
diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs
index 817fe3d080..f1b7f6876c 100644
--- a/src/Avalonia.Native/WindowImpl.cs
+++ b/src/Avalonia.Native/WindowImpl.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Native
internal class WindowImpl : WindowBaseImpl, IWindowImpl
{
private readonly AvaloniaNativePlatformOptions _opts;
- private readonly AvaloniaNativeGlPlatformGraphics _glFeature;
+ private readonly AvaloniaNativeGlPlatformGraphics _graphics;
IAvnWindow _native;
private double _extendTitleBarHeight = -1;
private DoubleClickHelper _doubleClickHelper;
@@ -23,16 +23,14 @@ namespace Avalonia.Native
private readonly AvaloniaNativeTextInputMethod _inputMethod;
private bool _canResize = true;
- internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
- AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, opts, glFeature)
+ internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(factory)
{
_opts = opts;
- _glFeature = glFeature;
_doubleClickHelper = new DoubleClickHelper();
using (var e = new WindowEvents(this))
{
- Init(_native = factory.CreateWindow(e, glFeature.SharedContext.Context), factory.CreateScreens());
+ Init(_native = factory.CreateWindow(e), factory.CreateScreens());
}
_nativeMenuExporter = new AvaloniaNativeMenuExporter(_native, factory);
@@ -215,7 +213,7 @@ namespace Avalonia.Native
public void Move(PixelPoint point) => Position = point;
public override IPopupImpl CreatePopup() =>
- _opts.OverlayPopups ? null : new PopupImpl(_factory, _opts, _glFeature, this);
+ _opts.OverlayPopups ? null : new PopupImpl(_factory, this);
public Action GotInputWhenDisabled { get; set; }
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 760816643e..3b4e457ba5 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -54,24 +54,20 @@ namespace Avalonia.Native
protected IInputRoot _inputRoot;
IAvnWindowBase _native;
private object _syncRoot = new object();
- private bool _gpu = false;
private readonly MouseDevice _mouse;
private readonly IKeyboardDevice _keyboard;
private readonly ICursorFactory _cursorFactory;
private Size _savedLogicalSize;
private Size _lastRenderedLogicalSize;
private double _savedScaling;
- private GlPlatformSurface _glSurface;
private NativeControlHostImpl _nativeControlHost;
private IStorageProvider _storageProvider;
private PlatformBehaviorInhibition _platformBehaviorInhibition;
private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None;
- internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
- AvaloniaNativeGlPlatformGraphics glFeature)
+ internal WindowBaseImpl(IAvaloniaNativeFactory factory)
{
_factory = factory;
- _gpu = opts.UseGpu && glFeature != null;
_keyboard = AvaloniaLocator.Current.GetService();
_mouse = new MouseDevice();
@@ -82,9 +78,8 @@ namespace Avalonia.Native
{
_native = window;
+ Surfaces = new object[] { new GlPlatformSurface(window), new MetalPlatformSurface(window), this };
Handle = new MacOSTopLevelWindowHandle(window);
- if (_gpu)
- _glSurface = new GlPlatformSurface(window);
Screen = new ScreenImpl(screens);
_savedLogicalSize = ClientSize;
@@ -133,29 +128,55 @@ namespace Avalonia.Native
}
}
- public IEnumerable