Browse Source

[macOS] Synchronize Metal drawable presentation (#21588)

* Add demo app

* [macOS] Present Metal drawable synchronously on main-thread resize frames

* write through the out-parameter

* Add ResizePattern to RenderDemo

* Remove MetalResizeDemo
pull/21613/head
Tim Miller 2 months ago
committed by GitHub
parent
commit
7fa021e562
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 33
      native/Avalonia.Native/src/OSX/metal.mm
  2. 73
      samples/RenderDemo/Controls/ResizePattern.cs
  3. 3
      samples/RenderDemo/MainWindow.xaml
  4. 7
      samples/RenderDemo/Pages/ResizePatternPage.xaml
  5. 18
      samples/RenderDemo/Pages/ResizePatternPage.xaml.cs

33
native/Avalonia.Native/src/OSX/metal.mm

@ -181,12 +181,13 @@ class AvnMetalRenderSession : public ComSingleObject<IAvnMetalRenderingSession,
CAMetalLayer* _layer;
AvnPixelSize _size;
double _scaling;
bool _presentWithTransaction;
public:
FORWARD_IUNKNOWN()
AvnMetalRenderSession(AvnMetalDevice* device, CAMetalLayer* layer, id <CAMetalDrawable> drawable, const AvnPixelSize &size, double scaling)
AvnMetalRenderSession(AvnMetalDevice* device, CAMetalLayer* layer, id <CAMetalDrawable> drawable, const AvnPixelSize &size, double scaling, bool presentWithTransaction)
: _drawable(drawable), _size(size), _scaling(scaling), _queue(device->queue),
_texture([drawable texture]) {
_texture([drawable texture]), _presentWithTransaction(presentWithTransaction) {
_layer = layer;
}
@ -207,8 +208,19 @@ public:
{
START_ARP_CALL;
auto buffer = [_queue commandBuffer];
[buffer presentDrawable: _drawable];
[buffer commit];
if(_presentWithTransaction)
{
[buffer commit];
[buffer waitUntilScheduled];
[_drawable present];
// Restore the default asynchronous presentation for the off-thread render loop.
_layer.presentsWithTransaction = NO;
}
else
{
[buffer presentDrawable: _drawable];
[buffer commit];
}
}
};
@ -230,7 +242,8 @@ public:
HRESULT BeginDrawing(IAvnMetalRenderingSession **ret) override {
START_COM_ARP_CALL;
if([NSThread isMainThread])
bool onMainThread = [NSThread isMainThread];
if(onMainThread)
{
// Flush all existing rendering
auto buffer = [_device->queue commandBuffer];
@ -240,15 +253,21 @@ public:
_scaling= PendingScaling;
CGSize layerSize = {(CGFloat)_size.Width, (CGFloat)_size.Height};
[CATransaction begin];
[CATransaction setDisableActions:YES];
[_layer setDrawableSize: layerSize];
_layer.presentsWithTransaction = YES;
[CATransaction commit];
}
auto drawable = [_layer nextDrawable];
if(drawable == nil)
{
ret = nil;
if(onMainThread)
_layer.presentsWithTransaction = NO;
*ret = nullptr;
return E_FAIL;
}
*ret = new AvnMetalRenderSession(_device, _layer, drawable, _size, _scaling);
*ret = new AvnMetalRenderSession(_device, _layer, drawable, _size, _scaling, onMainThread);
return 0;
}
};

73
samples/RenderDemo/Controls/ResizePattern.cs

@ -0,0 +1,73 @@
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
namespace RenderDemo.Controls
{
public sealed class ResizePattern : Control
{
private static readonly IPen GridPen = new Pen(new SolidColorBrush(Color.FromRgb(60, 90, 130)), 1);
private static readonly IPen DiagonalPen = new Pen(new SolidColorBrush(Color.FromRgb(90, 90, 90)), 1);
private static readonly IPen CirclePen = new Pen(Brushes.OrangeRed, 4);
private static readonly IPen EdgePen = new Pen(Brushes.Lime, 6);
private static readonly IBrush Background = new SolidColorBrush(Color.FromRgb(16, 16, 24));
private static readonly IBrush TextBrush = Brushes.White;
private const double GridStep = 40;
static ResizePattern()
{
AffectsRender<ResizePattern>(BoundsProperty);
}
public override void Render(DrawingContext context)
{
var w = Bounds.Width;
var h = Bounds.Height;
if (w <= 0 || h <= 0)
return;
var rect = new Rect(0, 0, w, h);
context.FillRectangle(Background, rect);
// Grid -- uneven spacing on the far edges is the tell-tale of stretching.
for (double x = 0; x <= w; x += GridStep)
context.DrawLine(GridPen, new Point(x, 0), new Point(x, h));
for (double y = 0; y <= h; y += GridStep)
context.DrawLine(GridPen, new Point(0, y), new Point(w, y));
// Corner-to-corner diagonals -- they only meet exactly at the centre
// when the aspect ratio is correct.
context.DrawLine(DiagonalPen, new Point(0, 0), new Point(w, h));
context.DrawLine(DiagonalPen, new Point(w, 0), new Point(0, h));
// Edge frame -- a stretched drawable makes this peel away from the window edge.
context.DrawRectangle(null, EdgePen, rect.Deflate(3));
// Concentric perfect circles centred in the window.
var center = new Point(w / 2, h / 2);
var maxRadius = Math.Max(10, Math.Min(w, h) / 2 - 16);
for (var i = 1; i <= 3; i++)
{
var r = maxRadius * i / 3;
context.DrawEllipse(null, CirclePen, center, r, r);
}
// Centre crosshair.
context.DrawLine(CirclePen, new Point(center.X - 20, center.Y), new Point(center.X + 20, center.Y));
context.DrawLine(CirclePen, new Point(center.X, center.Y - 20), new Point(center.X, center.Y + 20));
// Live size read-out.
var text = new FormattedText(
string.Create(CultureInfo.InvariantCulture, $"{w:0} x {h:0} DIP"),
CultureInfo.InvariantCulture,
FlowDirection.LeftToRight,
Typeface.Default,
22,
TextBrush);
context.DrawText(text, new Point(12, 8));
}
}
}

3
samples/RenderDemo/MainWindow.xaml

@ -97,5 +97,8 @@
<TabItem Header="3D Transformation">
<pages:Transform3DPage />
</TabItem>
<TabItem Header="Resize Pattern">
<pages:ResizePatternPage />
</TabItem>
</controls:HamburgerMenu>
</Window>

7
samples/RenderDemo/Pages/ResizePatternPage.xaml

@ -0,0 +1,7 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:RenderDemo.Controls"
x:Class="RenderDemo.Pages.ResizePatternPage">
<controls:ResizePattern />
</UserControl>

18
samples/RenderDemo/Pages/ResizePatternPage.xaml.cs

@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace RenderDemo.Pages
{
public class ResizePatternPage : UserControl
{
public ResizePatternPage()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
Loading…
Cancel
Save