Browse Source

OpenGL acceleration support

Nikita Tsukanov 7 years ago
parent
commit
fad3775bde

+ 5 - 1
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<App>().UseAvaloniaNative(libraryPath).UseSkia();
+            return AppBuilder.Configure<App>().UseAvaloniaNative(libraryPath, opts =>
+            {
+                opts.UseGpu = true;
+                opts.UseDeferredRendering = true;
+            }).UseSkia();
         }
 
     }

+ 8 - 0
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 = "<group>"; };
 		5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; };
 		AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
+		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 = "<group>"; };
 		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 = "<group>"; };
 		AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
@@ -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;

+ 2 - 0
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);

+ 251 - 0
src/Avalonia.Native.OSX/gl.mm

@@ -0,0 +1,251 @@
+#include "common.h"
+#include <OpenGL/gl.h>
+#include <dlfcn.h>
+
+template <typename T, size_t N> 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<IAvnGlContext, &IID_IAvnGlContext>
+{
+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<IAvnGlDisplay, &IID_IAvnGlDisplay>
+{
+    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<IAvnGlFeature, &IID_IAvnGlFeature>
+{
+    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<IAvnGlSurfaceRenderingSession, &IID_IAvnGlSurfaceRenderingSession>
+{
+    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<IAvnGlSurfaceRenderTarget, &IID_IAvnGlSurfaceRenderTarget>
+{
+    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);
+}

+ 10 - 0
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()

+ 67 - 18
src/Avalonia.Native.OSX/window.mm

@@ -5,6 +5,45 @@
 #include "window.h"
 #include "KeyTransform.h"
 #include "cursor.h"
+#include <OpenGL/gl.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<IAvnWindowBase, &IID_IAvnWindowBase>, public INSWindowHolder
 {
@@ -22,6 +61,7 @@ public:
     AvnView* View;
     AvnWindow* Window;
     ComPtr<IAvnWindowBaseEvents> 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

+ 7 - 2
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<IClipboard>().ToConstant(new ClipboardImpl(_factory.CreateClipboard()))
                 .Bind<IRenderLoop>().ToConstant(new RenderLoop())
                 .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
-                .Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()));       
+                .Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()))
+                .Bind<IWindowingPlatformGlFeature>().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature()))
+                .Bind<AvaloniaNativeOptions>().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();

+ 134 - 0
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<IWindowingPlatformGlFeature>();
+            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());
+        }
+    }
+}

+ 40 - 27
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<AvaloniaNativeOptions>();
+
+            // GPU is currently not compatible with DeferredRenderer
+            if (opts.UseGpu)
+            {
+                _gpu = true;
+            }
+            else
+                _deferredRendering = opts.UseDeferredRendering;
+
             _keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
             _mouse = AvaloniaLocator.Current.GetService<IMouseDevice>();
             _cursorFactory = AvaloniaLocator.Current.GetService<IStandardCursorFactory>();
@@ -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<object> Surfaces => new[] { this };
+        public IEnumerable<object> 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<Rect> 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 });
         }
 

+ 44 - 1
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();