Skip to content

Commit

Permalink
feat(gtk): Add OpenGL acceleration
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromelaban committed Mar 14, 2022
1 parent 487cc9e commit e89870d
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"SamplesApp.Skia.Gtk": {
"commandName": "Project",
"commandLineArgs": "--_auto-screenshots=C:\\temp\\screenshots",
"nativeDebugging": false
"nativeDebugging": true
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Windows.UI.Composition
{
public partial class CompositionShape
{
internal virtual void Render(SKSurface surface, SKImageInfo info)
internal virtual void Render(SKSurface surface)
{

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public partial class CompositionSpriteShape : CompositionShape
private SKPaint? _strokePaint;
private SKPaint? _fillPaint;

internal override void Render(SKSurface surface, SKImageInfo info)
internal override void Render(SKSurface surface)
{
SkiaGeometrySource2D? geometrySource = Geometry?.BuildGeometry() as SkiaGeometrySource2D;

Expand Down
10 changes: 5 additions & 5 deletions src/Uno.UI.Composition/Composition/Compositor.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void Dispose()

internal float CurrentOpacity => _currentOpacity;

internal void Render(SKSurface surface, SKImageInfo info)
internal void Render(SKSurface surface)
{
_isDirty = false;

Expand All @@ -50,12 +50,12 @@ internal void Render(SKSurface surface, SKImageInfo info)
var children = RootVisual.GetChildrenInRenderOrder();
for (var i = 0; i < children.Count; i++)
{
RenderVisual(surface, info, children[i]);
RenderVisual(surface, children[i]);
}
}
}

private void RenderVisual(SKSurface surface, SKImageInfo info, Visual visual)
private void RenderVisual(SKSurface surface, Visual visual)
{
if (visual.Opacity != 0 && visual.IsVisible)
{
Expand All @@ -82,14 +82,14 @@ private void RenderVisual(SKSurface surface, SKImageInfo info, Visual visual)

using var opacityDisposable = PushOpacity(visual.Opacity);

visual.Render(surface, info);
visual.Render(surface);

if (visual is ContainerVisual containerVisual)
{
var children = containerVisual.GetChildrenInRenderOrder();
for (var i = 0; i < children.Count; i++)
{
RenderVisual(surface, info, children[i]);
RenderVisual(surface, children[i]);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/Uno.UI.Composition/Composition/ShapeVisual.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Windows.UI.Composition
{
public partial class ShapeVisual
{
internal override void Render(SKSurface surface, SKImageInfo info)
internal override void Render(SKSurface surface)
{
foreach(var shape in Shapes)
{
Expand All @@ -34,7 +34,7 @@ internal override void Render(SKSurface surface, SKImageInfo info)

surface.Canvas.SetMatrix(visualMatrix);

shape.Render(surface, info);
shape.Render(surface);

surface.Canvas.Restore();
}
Expand Down
4 changes: 2 additions & 2 deletions src/Uno.UI.Composition/Composition/SpriteVisual.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ private void UpdatePaint()
Brush?.UpdatePaint(_paint, new SKRect(left: 0, top: 0, right: Size.X, bottom: Size.Y));
}

internal override void Render(SKSurface surface, SKImageInfo info)
internal override void Render(SKSurface surface)
{
base.Render(surface, info);
base.Render(surface);

surface.Canvas.Save();

Expand Down
2 changes: 1 addition & 1 deletion src/Uno.UI.Composition/Composition/Visual.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Windows.UI.Composition
{
public partial class Visual : global::Windows.UI.Composition.CompositionObject
{
internal virtual void Render(SKSurface surface, SKImageInfo info)
internal virtual void Render(SKSurface surface)
{

}
Expand Down
180 changes: 180 additions & 0 deletions src/Uno.UI.Runtime.Skia.Gtk/GLRenderSurface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#nullable enable

using System;
using System.IO;
using SkiaSharp;
using Uno.Extensions;
using Uno.UI.Xaml.Core;
using Windows.UI.Xaml.Input;
using WUX = Windows.UI.Xaml;
using Uno.Foundation.Logging;
using Windows.UI.Xaml.Controls;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Uno.UI.Runtime.Skia.Helpers.Windows;
using Uno.UI.Runtime.Skia.Helpers.Dpi;
using Windows.Graphics.Display;
using Gdk;
using System.Reflection;
using Gtk;
using Silk.NET.OpenGL;
using Silk.NET.Core.Loader;

namespace Uno.UI.Runtime.Skia
{

internal class GLRenderSurface : GLArea, IRenderSurface
{
private const SKColorType colorType = SKColorType.Rgba8888;
private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft;

private readonly DisplayInformation _displayInformation;
private FocusManager? _focusManager;

private float? _dpi = 1;
private GRContext? _grContext;
private GL _gl;
private GRBackendRenderTarget? _renderTarget;
private SKSurface? _surface;

public GLRenderSurface()
{
_displayInformation = DisplayInformation.GetForCurrentView();
_displayInformation.DpiChanged += OnDpiChanged;
WUX.Window.InvalidateRender
+= () =>
{
// TODO Uno: Make this invalidation less often if possible.
InvalidateOverlays();
QueueRender();
};

// Set some event handlers
Render += UnoGLDrawingArea_Render;
Realized += GLRenderSurface_Realized;

HasDepthBuffer = false;
HasStencilBuffer = false;
AutoRender = true;
SetRequiredVersion(3, 3);

_gl = new GL(new Silk.NET.Core.Contexts.DefaultNativeContext(new GLCoreLibraryNameContainer().GetLibraryName()));
}

private void GLRenderSurface_Realized(object? sender, EventArgs e)
{
Context.MakeCurrent();
}

private void UnoGLDrawingArea_Render(object o, RenderArgs args)
{
var sw = Stopwatch.StartNew();

args.Context.MakeCurrent();


// create the contexts if not done already
if (_grContext == null)
{
var glInterface = GRGlInterface.Create();
_grContext = GRContext.CreateGl(glInterface);
}

_gl.Clear(ClearBufferMask.ColorBufferBit);
_gl.ClearColor(1.0f, 1.0f, 1.0f, 1.0f);

// manage the drawing surface
var res = (int)Math.Max(1.0, Screen.Resolution / 96.0);
var w = Math.Max(0, AllocatedWidth * res);
var h = Math.Max(0, AllocatedHeight * res);

if (_renderTarget == null || _surface == null || _renderTarget.Width != w || _renderTarget.Height != h)
{
// create or update the dimensions
_renderTarget?.Dispose();

_gl.GetInteger(GLEnum.FramebufferBinding, out var framebuffer);
_gl.GetInteger(GLEnum.Stencil, out var stencil);
_gl.GetInteger(GLEnum.Samples, out var samples);
var maxSamples = _grContext.GetMaxSurfaceSampleCount(colorType);

if (samples > maxSamples)
{
samples = maxSamples;
}

var glInfo = new GRGlFramebufferInfo((uint)framebuffer, colorType.ToGlSizedFormat());

_renderTarget = new GRBackendRenderTarget(w, h, samples, stencil, glInfo);

// create the surface
_surface?.Dispose();
_surface = SKSurface.Create(_grContext, _renderTarget, surfaceOrigin, colorType);

Console.WriteLine($"Recreated surfaces: {w}x{h} Samples:{samples} colorType:{colorType}");
}

using (new SKAutoCanvasRestore(_surface.Canvas, true))
{
_surface.Canvas.Clear(SKColors.White);

// _surface.Canvas.Scale((float)(1/_dpi));

WUX.Window.Current.Compositor.Render(_surface);
}

// update the control
_surface.Canvas.Flush();

_gl.Flush();
sw.Stop();
Console.WriteLine($"Frame: {sw.Elapsed}");
}

private void OnDpiChanged(DisplayInformation sender, object args) =>
UpdateDpi();

private void InvalidateOverlays()
{
_focusManager ??= VisualTree.GetFocusManagerForElement(Windows.UI.Xaml.Window.Current?.RootElement);
_focusManager?.FocusRectManager?.RedrawFocusVisual();
if (_focusManager?.FocusedElement is TextBox textBox)
{
textBox.TextBoxView?.Extension?.InvalidateLayout();
}
}

public void TakeScreenshot(string filePath)
{
using Stream memStream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None);

var image = _surface.Snapshot();
var pngData = image.Encode();
pngData.SaveTo(memStream);
}

private void UpdateDpi() => _dpi = (float)_displayInformation.RawPixelsPerViewPixel;

// Extracted from https://github.com/dotnet/Silk.NET/blob/23f9bd4d67ad21c69fbd69cc38a62fb2c0ec3927/src/OpenGL/Silk.NET.OpenGL/GLCoreLibraryNameContainer.cs
internal class GLCoreLibraryNameContainer : SearchPathContainer
{
/// <inheritdoc />
public override string Linux => "libGL.so.1";

/// <inheritdoc />
public override string MacOS => "/System/Library/Frameworks/OpenGL.framework/OpenGL";

/// <inheritdoc />
public override string Android => "libGL.so.1";

/// <inheritdoc />
public override string IOS => "/System/Library/Frameworks/OpenGL.framework/OpenGL";

/// <inheritdoc />
public override string Windows64 => "opengl32.dll";

/// <inheritdoc />
public override string Windows86 => "opengl32.dll";
}
}
}
31 changes: 28 additions & 3 deletions src/Uno.UI.Runtime.Skia.Gtk/GtkHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
using Windows.UI.Xaml.Controls;
using UnoApplication = Windows.UI.Xaml.Application;
using WUX = Windows.UI.Xaml;
using Uno.UI.Runtime.Skia.GTK.System.Profile;
using Uno.UI.Runtime.Skia.Helpers;
using Uno.UI.Runtime.Skia.Helpers.Dpi;
using System.Runtime.InteropServices;

namespace Uno.UI.Runtime.Skia
{
Expand All @@ -41,13 +45,18 @@ public class GtkHost : ISkiaHost
private readonly Func<WUX.Application> _appBuilder;
private static Gtk.Window _window;
private static Gtk.EventBox _eventBox;
private UnoDrawingArea _area;
private Widget _area;
private Fixed _fix;
private GtkDisplayInformationExtension _displayInformationExtension;

public static Gtk.Window Window => _window;
public static Gtk.EventBox EventBox => _eventBox;

public RenderSurfaceType RenderSurfaceType { get; set; }
= RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
? RenderSurfaceType.Software // OpenGL support on macOS is currently broken
: RenderSurfaceType.OpenGL;

public GtkHost(Func<WUX.Application> appBuilder, string[] args)
{
_args = args;
Expand Down Expand Up @@ -130,13 +139,18 @@ void Dispatch(System.Action d)
var overlay = new Overlay();

_eventBox = new EventBox();
_area = new UnoDrawingArea();
_area = BuildRenderSurfaceType();
_fix = new Fixed();
overlay.Add(_area);
overlay.AddOverlay(_fix);
_eventBox.Add(overlay);
_window.Add(_eventBox);

if (this.Log().IsEnabled(LogLevel.Information))
{
this.Log().Info($"Using {RenderSurfaceType} rendering");
}

_area.Realized += (s, e) =>
{
WUX.Window.Current.OnNativeSizeChanged(new Windows.Foundation.Size(_area.AllocatedWidth, _area.AllocatedHeight));
Expand Down Expand Up @@ -186,6 +200,14 @@ private void WindowClosing(object sender, DeleteEventArgs args)
Gtk.Main.Quit();
}

private Widget BuildRenderSurfaceType()
=> RenderSurfaceType switch
{
RenderSurfaceType.Software => new SoftwareRenderSurface(),
RenderSurfaceType.OpenGL => new GLRenderSurface(),
_ => throw new InvalidOperationException($"Unsupported RenderSurfaceType {RenderSurfaceType}")
};

private void OnWindowStateChanged(object o, WindowStateEventArgs args)
{
var winUIApplication = WUX.Application.Current;
Expand Down Expand Up @@ -258,7 +280,10 @@ private void UpdateWindowPropertiesFromPackage()

public void TakeScreenshot(string filePath)
{
_area.TakeScreenshot(filePath);
if (_area is IRenderSurface renderSurface)
{
renderSurface.TakeScreenshot(filePath);
}
}

private void SetupTheme()
Expand Down
7 changes: 7 additions & 0 deletions src/Uno.UI.Runtime.Skia.Gtk/IRenderSurface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Uno.UI.Runtime.Skia
{
internal interface IRenderSurface
{
void TakeScreenshot(string filePath);
}
}
8 changes: 8 additions & 0 deletions src/Uno.UI.Runtime.Skia.Gtk/RenderSurfaceType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Uno.UI.Runtime.Skia
{
public enum RenderSurfaceType
{
Software,
OpenGL,
}
}
Loading

0 comments on commit e89870d

Please sign in to comment.