From 56cba543d058b9fd053963c8caa9efa6b4d656da Mon Sep 17 00:00:00 2001 From: danwalmsley Date: Thu, 6 Feb 2020 16:18:43 -0300 Subject: [PATCH] Merge pull request #3525 from AvaloniaUI/iosurf [OSX] Use backing CALayer with offscreen-rendered IOSurface # Conflicts: # native/Avalonia.Native/src/OSX/window.mm # src/Avalonia.Native/AvaloniaNativePlatform.cs --- native/Avalonia.Native/inc/avalonia-native.h | 28 +- native/Avalonia.Native/inc/comimpl.h | 13 + native/Avalonia.Native/inc/rendertarget.h | 12 + .../project.pbxproj | 20 +- 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 | 284 ++++++++++++++++++ native/Avalonia.Native/src/OSX/window.mm | 208 ++++--------- .../AvaloniaNativeDeferredRendererLock.cs | 42 --- src/Avalonia.Native/AvaloniaNativePlatform.cs | 10 +- src/Avalonia.Native/DeferredFramebuffer.cs | 2 +- src/Avalonia.Native/GlPlatformFeature.cs | 31 +- src/Avalonia.Native/PopupImpl.cs | 12 +- src/Avalonia.Native/WindowImpl.cs | 10 +- src/Avalonia.Native/WindowImplBase.cs | 63 ++-- 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 + 23 files changed, 630 insertions(+), 559 deletions(-) create mode 100644 native/Avalonia.Native/inc/rendertarget.h create mode 100644 native/Avalonia.Native/src/OSX/cgl.mm delete mode 100644 native/Avalonia.Native/src/OSX/gl.mm 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 06d6bdf311..2ae456750d 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -167,14 +167,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; @@ -209,15 +209,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 @@ -350,24 +347,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 new file mode 100644 index 0000000000..2b0338d099 --- /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; +-(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget; +@end + +@interface IOSurfaceRenderTarget : NSObject +-(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 c0a49382a7..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 @@ -8,6 +8,10 @@ /* 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 */; }; + 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 */; }; 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; }; @@ -18,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 */; }; @@ -26,6 +29,10 @@ /* 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; }; + 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 = ""; }; 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; }; @@ -41,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 = ""; }; @@ -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 */, ); @@ -78,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 */, @@ -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,12 +191,13 @@ 5B21A982216530F500CEE36E /* cursor.mm in Sources */, 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 new file mode 100644 index 0000000000..1565417c1a --- /dev/null +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -0,0 +1,284 @@ +#include "common.h" +#include "rendertarget.h" +#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; + @public IOSurfaceHolder* surface; + @public NSObject* lock; + ComPtr _glContext; +} + +- (IOSurfaceRenderTarget*) initWithOpenGlContext: (IAvnGlContext*) context; +{ + self = [super init]; + _glContext = context; + 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 + || 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) { + if(_layer == nil) + return; + [_layer setContents: nil]; + if(surface != nil) + { + [_layer setContentsScale: surface->scale]; + [_layer setContents: (__bridge IOSurface*) surface->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(surface == nil) + return E_FAIL; + 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(surf); + size_t fstride = fb->Stride; + char*pSurface = (char*)IOSurfaceGetBaseAddress(surf); + char*pFb = (char*)fb->Data; + for(size_t y = 0; y < h; y++) + { + memcpy(pSurface + y*sstride, pFb + y*fstride, wbytes); + } + IOSurfaceUnlock(surf, 0, nil); + [self updateLayer]; + return S_OK; + } +} + +-(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget +{ + return CreateGlRenderTarget(self); +} + +@end + +class AvnGlRenderingSession : public ComSingleObject +{ + 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 75112d5dab..483b70f7e9 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -7,44 +7,9 @@ #include "cursor.h" #include "menu.h" #include +#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,15 +26,18 @@ public: AvnView* View; AvnWindow* Window; ComPtr BaseEvents; - SoftwareDrawingOperation CurrentSwDrawingOperation; + ComPtr _glContext; + NSObject* renderTarget; AvnPoint lastPositionSet; NSString* _lastTitle; IAvnAppMenu* _mainMenu; - WindowBaseImpl(IAvnWindowBaseEvents* events) + WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl) { _mainMenu = nullptr; BaseEvents = events; + _glContext = gl; + renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: gl]; View = [[AvnView alloc] initWithParent:this]; Window = [[AvnWindow alloc] initWithParent:this]; @@ -291,29 +259,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 +353,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 @@ -451,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: @@ -490,7 +425,7 @@ private: } ComPtr WindowEvents; - WindowImpl(IAvnWindowEvents* events) : WindowBaseImpl(events) + WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) { WindowEvents = events; [Window setCanBecomeKeyAndMain]; @@ -731,6 +666,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent NSEvent* _lastMouseDownEvent; bool _lastKeyHandled; AvnPixelSize _lastPixelSize; + NSObject* _renderTarget; } - (void)onClosed @@ -741,19 +677,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } -- (BOOL)lockFocusIfCanDraw -{ - @synchronized (self) - { - if(_parent == nullptr) - { - return NO; - } - } - - return [super lockFocusIfCanDraw]; -} - -(AvnPixelSize) getPixelSize { return _lastPixelSize; @@ -764,17 +687,43 @@ 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; + return self; } +- (BOOL)isFlipped +{ + return YES; +} + +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (void)setLayer:(CALayer *)layer +{ + [_renderTarget setNewLayer: layer]; + [super setLayer: layer]; +} + - (BOOL)isOpaque { return YES; @@ -822,87 +771,32 @@ 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(); - return; } --(void) redrawSelf +- (void)drawRect:(NSRect)dirtyRect { - @autoreleasepool - { - @synchronized(self) - { - if(!_queuedDisplayFromThread) - return; - _queuedDisplayFromThread = false; - } - [self setNeedsDisplayInRect:[self frame]]; - [self display]; - - } + return; } -(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(); } } @@ -927,7 +821,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]; @@ -1446,7 +1340,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]; @@ -1470,20 +1364,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/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 571475c7ea..36167b1b4d 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; @@ -17,6 +18,7 @@ namespace Avalonia.Native { private readonly IAvaloniaNativeFactory _factory; private AvaloniaNativePlatformOptions _options; + private GlPlatformFeature _glFeature; [DllImport("libAvaloniaNative")] static extern IntPtr CreateAvaloniaNative(); @@ -87,7 +89,7 @@ namespace Avalonia.Native _factory.MacOptions.ShowInDock = macOpts?.ShowInDock != false ? 1 : 0; } - + AvaloniaLocator.CurrentMutable .Bind() .ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface())) @@ -100,14 +102,16 @@ 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(InputModifiers.Windows)) .Bind().ToConstant(new MacOSMountedVolumeInfoProvider()); + if (_options.UseGpu) + AvaloniaLocator.CurrentMutable.Bind() + .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/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/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 5d701dc8df..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; @@ -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); } 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 15f38b1c4f..39baeb6a55 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -45,6 +45,7 @@ namespace Avalonia.Skia { GrContext = GRContext.Create(GRBackend.OpenGL, iface); } + display.ClearContext(); } }