From fad3775bde399473b172328535076ba0e77cd9e3 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 16 Oct 2018 21:03:12 +0300 Subject: [PATCH 1/3] OpenGL acceleration support --- samples/ControlCatalog/Program.cs | 6 +- .../project.pbxproj | 8 + src/Avalonia.Native.OSX/common.h | 2 + src/Avalonia.Native.OSX/gl.mm | 251 ++++++++++++++++++ src/Avalonia.Native.OSX/main.mm | 10 + src/Avalonia.Native.OSX/window.mm | 85 ++++-- src/Avalonia.Native/AvaloniaNativePlatform.cs | 9 +- src/Avalonia.Native/GlPlatformFeature.cs | 134 ++++++++++ src/Avalonia.Native/WindowImplBase.cs | 67 +++-- src/headers/avalonia-native.h | 45 +++- 10 files changed, 568 insertions(+), 49 deletions(-) create mode 100644 src/Avalonia.Native.OSX/gl.mm create mode 100644 src/Avalonia.Native/GlPlatformFeature.cs diff --git a/samples/ControlCatalog/Program.cs b/samples/ControlCatalog/Program.cs index 372a887952..db4adfe61e 100644 --- a/samples/ControlCatalog/Program.cs +++ b/samples/ControlCatalog/Program.cs @@ -58,7 +58,11 @@ namespace ControlCatalog.NetCore var libraryPath = Path.Combine(Directory.GetCurrentDirectory(), "../../src/Avalonia.Native.OSX/build/Avalonia.Native.OSX/Build/Products/Debug/libAvalonia.Native.OSX.dylib"); - return AppBuilder.Configure().UseAvaloniaNative(libraryPath).UseSkia(); + return AppBuilder.Configure().UseAvaloniaNative(libraryPath, opts => + { + opts.UseGpu = true; + opts.UseDeferredRendering = true; + }).UseSkia(); } } diff --git a/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index e44345cfbc..57de0fc36d 100644 --- a/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; }; 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 */; }; @@ -29,6 +31,8 @@ 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 = ""; }; 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 = ""; }; @@ -41,6 +45,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */, AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -51,6 +56,7 @@ AB661C1C2148230E00291242 /* Frameworks */ = { isa = PBXGroup; children = ( + AB1E522B217613570091CD71 /* OpenGL.framework */, AB661C1D2148230F00291242 /* AppKit.framework */, ); name = Frameworks; @@ -59,6 +65,7 @@ AB7A61E62147C814003C5833 = { isa = PBXGroup; children = ( + AB573DC3217605E400D389A2 /* gl.mm */, 5BF943652167AD1D009CAE35 /* cursor.h */, 5B21A981216530F500CEE36E /* cursor.mm */, 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */, @@ -159,6 +166,7 @@ 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/src/Avalonia.Native.OSX/common.h b/src/Avalonia.Native.OSX/common.h index 11ee753aeb..1647569269 100644 --- a/src/Avalonia.Native.OSX/common.h +++ b/src/Avalonia.Native.OSX/common.h @@ -17,6 +17,8 @@ extern IAvnSystemDialogs* CreateSystemDialogs(); extern IAvnScreens* CreateScreens(); extern IAvnClipboard* CreateClipboard(); extern IAvnCursorFactory* CreateCursorFactory(); +extern IAvnGlFeature* GetGlFeature(); +extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view); extern NSPoint ToNSPoint (AvnPoint p); extern AvnPoint ToAvnPoint (NSPoint p); diff --git a/src/Avalonia.Native.OSX/gl.mm b/src/Avalonia.Native.OSX/gl.mm new file mode 100644 index 0000000000..dbe373b26f --- /dev/null +++ b/src/Avalonia.Native.OSX/gl.mm @@ -0,0 +1,251 @@ +#include "common.h" +#include +#include + +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() + { + [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) + { + *ret = _sampleCount; + return S_OK; + } + virtual HRESULT GetStencilSize(int* ret) + { + *ret = _stencilSize; + return S_OK; + } + + virtual HRESULT ClearContext() + { + [NSOpenGLContext clearCurrentContext]; + return S_OK; + } + + virtual void* GetProcAddress(char* proc) + { + return dlsym(_libgl, proc); + } +}; + + +class GlFeature : public virtual ComSingleObject +{ + IAvnGlDisplay* _display; + IAvnGlContext *_immediate; +public: + FORWARD_IUNKNOWN() + AvnGlContext* ViewContext; + GlFeature(IAvnGlDisplay* display, IAvnGlContext* immediate, AvnGlContext* viewContext) + { + _display = display; + _immediate = immediate; + ViewContext = viewContext; + } + + + virtual HRESULT ObtainDisplay(IAvnGlDisplay**retOut) + { + *retOut = _display; + _display->AddRef(); + return S_OK; + } + + virtual HRESULT ObtainImmediateContext(IAvnGlContext**retOut) + { + *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; + } + NSOpenGLContext* viewContext = [[NSOpenGLContext alloc] initWithFormat: format shareContext: immediateContext]; + if(viewContext == nil) + { + NSLog(@"Unable to create shared 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 view = new AvnGlContext(viewContext, false); + auto display = new AvnGlDisplay(sampleCount, stencilBits); + + return new GlFeature(display, offscreen, view); +} + + +static GlFeature* GetFeature() +{ + if(Feature == nil) + Feature = CreateGlFeature(); + return Feature; +} + +extern IAvnGlFeature* GetGlFeature() +{ + return GetFeature(); +} + +class AvnGlRenderingSession : public ComSingleObject +{ + NSView* _view; + NSWindow* _window; + NSOpenGLContext* _context; +public: + FORWARD_IUNKNOWN() + AvnGlRenderingSession(NSWindow*window, NSView* view, NSOpenGLContext* context) + { + _context = context; + _window = window; + _view = view; + } + + virtual HRESULT GetPixelSize(AvnPixelSize* ret) + { + auto fsize = [_view convertSizeToBacking: [_view frame].size]; + ret->Width = (int)fsize.width; + ret->Height = (int)fsize.height; + return S_OK; + } + virtual HRESULT GetScaling(double* ret) + { + *ret = [_window backingScaleFactor]; + return S_OK; + } + + virtual ~AvnGlRenderingSession() + { + glFlush(); + [_context flushBuffer]; + [_context setView:nil]; + [_view unlockFocus]; + } +}; + +class AvnGlRenderTarget : public ComSingleObject +{ + NSView* _view; + NSWindow* _window; +public: + FORWARD_IUNKNOWN() + AvnGlRenderTarget(NSWindow* window, NSView*view) + { + _window = window; + _view = view; + } + + virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) + { + auto f = GetFeature(); + if(f == NULL) + return E_FAIL; + if(![_view lockFocusIfCanDraw]) + return E_ABORT; + + + auto gl = f->ViewContext->GlContext; + [gl setView: _view]; + [gl makeCurrentContext]; + auto frame = [_view frame]; + + *ret = new AvnGlRenderingSession(_window, _view, gl); + return S_OK; + } +}; + +extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view) +{ + return new AvnGlRenderTarget(window, view); +} diff --git a/src/Avalonia.Native.OSX/main.mm b/src/Avalonia.Native.OSX/main.mm index b2919f850f..bb32c44918 100644 --- a/src/Avalonia.Native.OSX/main.mm +++ b/src/Avalonia.Native.OSX/main.mm @@ -123,6 +123,16 @@ public: *ppv = ::CreateCursorFactory(); return S_OK; } + + virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) + { + auto rv = ::GetGlFeature(); + if(rv == NULL) + return E_FAIL; + rv->AddRef(); + *ppv = rv; + return S_OK; + } }; extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative() diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index e80c0e67b3..c5f68f3741 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -5,6 +5,45 @@ #include "window.h" #include "KeyTransform.h" #include "cursor.h" +#include + +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 { @@ -22,6 +61,7 @@ public: AvnView* View; AvnWindow* Window; ComPtr BaseEvents; + SoftwareDrawingOperation CurrentSwDrawingOperation; AvnPoint lastPositionSet; NSString* _lastTitle; @@ -279,6 +319,16 @@ public: return S_OK; } + virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) + { + 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) { @autoreleasepool @@ -300,6 +350,14 @@ public: [cursor set]; } } + + virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv) + { + if(View == NULL) + return E_FAIL; + *ppv = ::CreateGlRenderTarget(Window, View); + return S_OK; + } protected: virtual NSWindowStyleMask GetStyle() @@ -625,6 +683,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent -(AvnView*) initWithParent: (WindowBaseImpl*) parent { self = [super init]; + [self setWantsBestResolutionOpenGLSurface:true]; _parent = parent; _area = nullptr; return self; @@ -632,7 +691,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (BOOL)isOpaque { - return false; + return YES; } - (BOOL)acceptsFirstResponder @@ -706,23 +765,13 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } - auto logicalSize = [self frame].size; - auto pixelSize = [self convertSizeToBacking:logicalSize]; - int w = pixelSize.width; - int h = pixelSize.height; - int stride = w * 4; - void*ptr = malloc(h * stride); - AvnFramebuffer fb = { - .Data = ptr, - .Stride = stride, - .Width = w, - .Height = h, - .PixelFormat = kAvnRgba8888, - .Dpi = AvnVector { .X = w / logicalSize.width * 96, .Y = h / logicalSize.height * 96} - }; - _parent->BaseEvents->SoftwareDraw(&fb); - [self drawFb: &fb]; - free(ptr); + auto swOp = &_parent->CurrentSwDrawingOperation; + _parent->BaseEvents->Paint(); + if(swOp->Data != NULL) + [self drawFb: &swOp->Desc]; + + swOp->Dealloc(); + return; } -(void) redrawSelf diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 6ba26d4923..9fc1677611 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -8,6 +8,7 @@ using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Native.Interop; +using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Rendering; @@ -74,7 +75,9 @@ namespace Avalonia.Native .Bind().ToConstant(new ClipboardImpl(_factory.CreateClipboard())) .Bind().ToConstant(new RenderLoop()) .Bind().ToConstant(new DefaultRenderTimer(60)) - .Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())); + .Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())) + .Bind().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature())) + .Bind().ToConstant(opts); } public IWindowImpl CreateWindow() @@ -116,7 +119,9 @@ namespace Avalonia.Native public class AvaloniaNativeOptions { - public AvaloniaNativeMacOptions MacOptions { get; set; } + public AvaloniaNativeMacOptions MacOptions { get; set; } + public bool UseDeferredRendering { get; set; } = true; + public bool UseGpu { get; set; } = false; internal AvaloniaNativeOptions(IAvaloniaNativeFactory factory) { var mac = factory.GetMacOptions(); diff --git a/src/Avalonia.Native/GlPlatformFeature.cs b/src/Avalonia.Native/GlPlatformFeature.cs new file mode 100644 index 0000000000..dbcdd244cd --- /dev/null +++ b/src/Avalonia.Native/GlPlatformFeature.cs @@ -0,0 +1,134 @@ +using System; +using Avalonia.OpenGL; +using Avalonia.Native.Interop; +using System.Drawing; +using Avalonia.Threading; + +namespace Avalonia.Native +{ + class GlPlatformFeature : IWindowingPlatformGlFeature + { + + public GlPlatformFeature(IAvnGlFeature feature) + { + Display = new GlDisplay(feature.ObtainDisplay()); + ImmediateContext = new GlContext(Display, feature.ObtainImmediateContext()); + } + + public IGlContext ImmediateContext { get; } + public GlDisplay Display { get; } + } + + class GlDisplay : IGlDisplay + { + private readonly IAvnGlDisplay _display; + + public GlDisplay(IAvnGlDisplay display) + { + _display = display; + GlInterface = new GlInterface((name, optional) => + { + var rv = _display.GetProcAddress(name); + if (rv == IntPtr.Zero && !optional) + throw new OpenGlException($"{name} not found in system OpenGL"); + return rv; + }); + } + + public GlDisplayType Type => GlDisplayType.OpenGL2; + + public GlInterface GlInterface { get; } + + public int SampleCount => _display.GetSampleCount(); + + public int StencilSize => _display.GetStencilSize(); + + public void ClearContext() => _display.ClearContext(); + } + + class GlContext : IGlContext + { + public IAvnGlContext Context { get; } + + public GlContext(GlDisplay display, IAvnGlContext context) + { + Display = display; + Context = context; + } + + public IGlDisplay Display { get; } + + public void MakeCurrent(IGlSurface surface) + { + if (surface != null) + throw new ArgumentException(nameof(surface)); + Context.MakeCurrent(); + } + } + + + class GlPlatformSurfaceRenderTarget : IGlPlatformSurfaceRenderTarget + { + private IAvnGlSurfaceRenderTarget _target; + public GlPlatformSurfaceRenderTarget(IAvnGlSurfaceRenderTarget target) + { + _target = target; + } + + public IGlPlatformSurfaceRenderingSession BeginDraw() + { + var feature = (GlPlatformFeature)AvaloniaLocator.Current.GetService(); + return new GlPlatformSurfaceRenderingSession(feature.Display, _target.BeginDrawing()); + } + + public void Dispose() + { + _target?.Dispose(); + _target = null; + } + } + + class GlPlatformSurfaceRenderingSession : IGlPlatformSurfaceRenderingSession + { + private IAvnGlSurfaceRenderingSession _session; + + public GlPlatformSurfaceRenderingSession(GlDisplay display, IAvnGlSurfaceRenderingSession session) + { + Display = display; + _session = session; + } + + public IGlDisplay Display { get; } + + public System.Drawing.Size PixelSize + { + get + { + var s = _session.GetPixelSize(); + return new System.Drawing.Size(s.Width, s.Height); + } + } + + public double Scaling => _session.GetScaling(); + + public void Dispose() + { + _session?.Dispose(); + _session = null; + } + } + + class GlPlatformSurface : IGlPlatformSurface + { + private readonly IAvnWindowBase _window; + + public GlPlatformSurface(IAvnWindowBase window) + { + _window = window; + } + public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() + { + return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget()); + } + } +} diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index ef3ec00ff4..2f93ccb068 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -8,27 +8,42 @@ using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Native.Interop; +using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; namespace Avalonia.Native { - public class WindowBaseImpl : IWindowBaseImpl, IFramebufferPlatformSurface + public class WindowBaseImpl : IWindowBaseImpl, + IFramebufferPlatformSurface { IInputRoot _inputRoot; IAvnWindowBase _native; + bool _isClosed; private object _syncRoot = new object(); - private bool _deferredRendering = true; + private bool _deferredRendering = false; + private bool _gpu = false; private readonly IMouseDevice _mouse; private readonly IKeyboardDevice _keyboard; private readonly IStandardCursorFactory _cursorFactory; private Size _savedLogicalSize; private Size _lastRenderedLogicalSize; private double _savedScaling; + private GlPlatformSurface _glSurface; public WindowBaseImpl() { + var opts = AvaloniaLocator.Current.GetService(); + + // GPU is currently not compatible with DeferredRenderer + if (opts.UseGpu) + { + _gpu = true; + } + else + _deferredRendering = opts.UseDeferredRendering; + _keyboard = AvaloniaLocator.Current.GetService(); _mouse = AvaloniaLocator.Current.GetService(); _cursorFactory = AvaloniaLocator.Current.GetService(); @@ -37,7 +52,7 @@ namespace Avalonia.Native protected void Init(IAvnWindowBase window, IAvnScreens screens) { _native = window; - + _glSurface = new GlPlatformSurface(window); Screen = new ScreenImpl(screens); _savedLogicalSize = ClientSize; _savedScaling = Scaling; @@ -51,9 +66,12 @@ namespace Avalonia.Native return new Size(s.Width, s.Height); } } - SavedFramebuffer _framebuffer; - public IEnumerable Surfaces => new[] { this }; + public IEnumerable Surfaces => new[] { + (_gpu ? _glSurface : (object)null), + this + }; + public ILockedFramebuffer Lock() { if(_deferredRendering) @@ -72,13 +90,9 @@ namespace Avalonia.Native return true; } }, (int)w, (int)h, new Vector(dpi, dpi)); - } + } - var fb = _framebuffer; - _framebuffer = null; - if (fb == null) - throw new InvalidOperationException("Lock call without corresponding Paint event"); - return fb; + return new FramebufferWrapper(_native.GetSoftwareFramebuffer()); } public Action Paint { get; set; } @@ -87,14 +101,23 @@ namespace Avalonia.Native public IMouseDevice MouseDevice => AvaloniaNativePlatform.MouseDevice; - class SavedFramebuffer : ILockedFramebuffer + class FramebufferWrapper : ILockedFramebuffer { + public FramebufferWrapper(AvnFramebuffer fb) + { + Address = fb.Data; + Width = fb.Width; + Height = fb.Height; + RowBytes = fb.Stride; + Dpi = new Vector(fb.Dpi.X, fb.Dpi.Y); + Format = (PixelFormat)fb.PixelFormat; + } public IntPtr Address { get; set; } public int Width { get; set; } public int Height { get; set; } public int RowBytes {get;set;} public Vector Dpi { get; set; } - public PixelFormat Format => PixelFormat.Rgba8888; + public PixelFormat Format { get; } public void Dispose() { // Do nothing @@ -128,21 +151,11 @@ namespace Avalonia.Native void IAvnWindowBaseEvents.Deactivated() => _parent.Deactivated?.Invoke(); - void IAvnWindowBaseEvents.SoftwareDraw(ref AvnFramebuffer fb) + void IAvnWindowBaseEvents.Paint() { Dispatcher.UIThread.RunJobs(DispatcherPriority.Render); - - _parent._framebuffer = new SavedFramebuffer - { - Address = fb.Data, - RowBytes = fb.Stride, - Width = fb.Width, - Height = fb.Height, - Dpi = new Vector(fb.Dpi.X, fb.Dpi.Y) - }; - - _parent.Paint?.Invoke(new Rect(0, 0, fb.Width / (fb.Dpi.X / 96), fb.Height / (fb.Dpi.Y / 96))); - + var s = _parent.ClientSize; + _parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height)); } void IAvnWindowBaseEvents.Resized(AvnSize size) @@ -256,7 +269,7 @@ namespace Avalonia.Native public void Invalidate(Rect rect) { - if (!_deferredRendering) + if (!_deferredRendering && _native != null) _native.Invalidate(new AvnRect { Height = rect.Height, Width = rect.Width, X = rect.X, Y = rect.Y }); } diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 05ac379ce9..8afd69e422 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -17,12 +17,22 @@ struct IAvnScreens; struct IAvnClipboard; struct IAvnCursor; struct IAvnCursorFactory; +struct IAvnGlFeature; +struct IAvnGlContext; +struct IAvnGlDisplay; +struct IAvnGlSurfaceRenderTarget; +struct IAvnGlSurfaceRenderingSession; struct AvnSize { double Width, Height; }; +struct AvnPixelSize +{ + int Width, Height; +}; + struct AvnRect { double X, Y, Width, Height; @@ -160,6 +170,7 @@ public: virtual HRESULT CreateScreens (IAvnScreens** ppv) = 0; virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0; virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0; + virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) = 0; }; AVNCOM(IAvnWindowBase, 02) : IUnknown @@ -183,6 +194,8 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) = 0; virtual HRESULT SetTopMost (bool value) = 0; virtual HRESULT SetCursor(IAvnCursor* cursor) = 0; + virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret) = 0; + virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) = 0; }; AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase @@ -203,7 +216,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown { - virtual HRESULT SoftwareDraw(AvnFramebuffer* fb) = 0; + virtual HRESULT Paint() = 0; virtual void Closed() = 0; virtual void Activated() = 0; virtual void Deactivated() = 0; @@ -315,4 +328,34 @@ AVNCOM(IAvnCursorFactory, 11) : IUnknown }; +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 void* GetProcAddress(char* proc) = 0; +}; + +AVNCOM(IAvnGlContext, 14) : IUnknown +{ + virtual HRESULT MakeCurrent() = 0; +}; + +AVNCOM(IAvnGlSurfaceRenderTarget, 15) : IUnknown +{ + virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) = 0; +}; + +AVNCOM(IAvnGlSurfaceRenderingSession, 16) : IUnknown +{ + virtual HRESULT GetPixelSize(AvnPixelSize* ret) = 0; + virtual HRESULT GetScaling(double* ret) = 0; +}; + extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative(); From 1766c686e3219795d1d2046c7c6f725c45676efe Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 16 Oct 2018 21:03:22 +0300 Subject: [PATCH 2/3] Signaler fix --- src/Avalonia.Native.OSX/platformthreading.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Native.OSX/platformthreading.mm b/src/Avalonia.Native.OSX/platformthreading.mm index 07e307af74..c1ca6b6421 100644 --- a/src/Avalonia.Native.OSX/platformthreading.mm +++ b/src/Avalonia.Native.OSX/platformthreading.mm @@ -157,6 +157,7 @@ NSArray* _modes; -(void) perform { @synchronized (self) { + _signaled = false; if(_parent != NULL && _parent->SignaledCallback != NULL) _parent->SignaledCallback->Signaled(0, false); } From 2e5c249402b3816bf05ca980b34d74b2aedb1bd2 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 16 Oct 2018 21:06:16 +0300 Subject: [PATCH 3/3] =?UTF-8?q?Don=E2=80=99t=20register=20GL=20feature=20i?= =?UTF-8?q?f=20UseGPU=20=3D=20false?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Avalonia.Native/AvaloniaNativePlatform.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 9fc1677611..a8e290b551 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -76,8 +76,10 @@ 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(opts); + if (opts.UseGpu) + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature())); } public IWindowImpl CreateWindow()