Skip to content

Commit

Permalink
AvaloniaControl manages its own texture
Browse files Browse the repository at this point in the history
  • Loading branch information
MrJul committed May 5, 2023
1 parent 1ba1809 commit e92fdf3
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace HelloWorld;

public partial class HelloContainer : AvaloniaContainer {
public partial class HelloAvalonia : AvaloniaControl {

public override void _Ready() {
AppBuilder.Configure<App>()
Expand Down
3 changes: 2 additions & 1 deletion samples/HelloWorld/TestControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@

<TextBlock
Text="Hello from Avalonia in Godot!"
HorizontalAlignment="Center"
TextAlignment="Center"
VerticalAlignment="Center"
TextTrimming="{x:Static TextTrimming.CharacterEllipsis}"
Margin="16"
FontSize="48"
Foreground="Orange" />
Expand Down
36 changes: 3 additions & 33 deletions samples/HelloWorld/main.tscn
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://cpbtafkboh7wa"]

[ext_resource type="Script" path="res://HelloContainer.cs" id="1_0xlul"]
[ext_resource type="Script" path="res://HelloAvalonia.cs" id="1_fyecm"]

[node name="UI" type="Control"]
layout_mode = 3
Expand All @@ -10,44 +10,14 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2

[node name="SubViewportContainer" type="SubViewportContainer" parent="."]
[node name="AvaloniaContainer" type="Control" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
stretch = true

[node name="SubViewport" type="SubViewport" parent="SubViewportContainer"]
transparent_bg = true
handle_input_locally = false
size = Vector2i(1280, 720)
render_target_clear_mode = 1
render_target_update_mode = 0

[node name="AvaloniaContainer" type="Control" parent="SubViewportContainer/SubViewport"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_0xlul")

[node name="Button" type="Button" parent="SubViewportContainer/SubViewport"]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -90.0
offset_top = -15.5
offset_right = 90.0
offset_bottom = 15.5
grow_horizontal = 2
grow_vertical = 2
text = "button in subviewport"
script = ExtResource("1_fyecm")

[node name="Button" type="Button" parent="."]
layout_mode = 1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Platform;
Expand All @@ -9,7 +10,7 @@

namespace JLeb.Estragonia;

public class AvaloniaContainer : GdControl {
public class AvaloniaControl : GdControl {

private AvControl? _control;
private GodotTopLevel? _topLevel;
Expand Down Expand Up @@ -58,11 +59,6 @@ protected override bool HasGodotClassMethod(in godot_string_name method)
public override void _Ready() {
base._Ready();

if (GetViewport() is not SubViewport viewport) {
GD.PrintErr($"The {nameof(AvaloniaContainer)} must be contained inside a {nameof(SubViewport)}");
return;
}

if (Engine.IsEditorHint())
return;

Expand All @@ -71,30 +67,24 @@ public override void _Ready() {
return;
}

_topLevel = new GodotTopLevel(new GodotTopLevelImpl(graphics));
_topLevel.Background = null;
_topLevel.Content = Control;
_topLevel.TransparencyLevelHint = viewport.TransparentBg ? WindowTransparencyLevel.Transparent : WindowTransparencyLevel.None;
UpdateSurface();
var topLevelImpl = new GodotTopLevelImpl(graphics) {
ClientSize = GetAvaloniaSize()
};

_topLevel = new GodotTopLevel(topLevelImpl) {
Background = null,
Content = Control,
TransparencyLevelHint = WindowTransparencyLevel.Transparent
};

_topLevel.Prepare();
_topLevel.Renderer.Start();
}

private void UpdateSurface() {
var texture = GetViewport().GetTexture();
_topLevel!.Impl.Surface = _topLevel.Impl.PlatformGraphics.GetSharedContext().CreateSurfaceFromTexture(texture);
Resized += OnSizeChanged;
}

public override void _Process(double delta) {
if (_topLevel is null)
return;

var size = GetAvaloniaSize();
if (_topLevel.ClientSize != size)
OnSizeChanged();

_topLevel.Impl.RenderTimer.TriggerTick(new TimeSpan((long) (Time.GetTicksUsec() * 10UL)));
}
public override void _Process(double delta)
=> _topLevel?.Impl.RenderTimer.TriggerTick(new TimeSpan((long) (Time.GetTicksUsec() * 10UL)));

private Size GetAvaloniaSize() {
var size = Size;
Expand All @@ -105,15 +95,19 @@ private void OnSizeChanged() {
if (_topLevel is null)
return;

UpdateSurface();

var size = GetAvaloniaSize();
_topLevel.Impl.ClientSize = size;
_topLevel.Measure(size);
_topLevel.Arrange(new Rect(size));
}

public override void _Draw()
=> _topLevel?.Renderer.Paint(new Rect(GetAvaloniaSize()));
public override void _Draw() {
if (_topLevel is null)
return;

_topLevel.Renderer.Paint(new Rect(GetAvaloniaSize()));
DrawTexture(_topLevel.Impl.Surface.GdTexture, Vector2.Zero);
}

protected override void Dispose(bool disposing) {
if (disposing) {
Expand Down
2 changes: 1 addition & 1 deletion src/JLeb.Estragonia/GodotSkiaSurface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ public void Dispose() {
GdTexture.Dispose();
}

}
}
49 changes: 21 additions & 28 deletions src/JLeb.Estragonia/GodotTopLevelImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,48 +29,34 @@ internal sealed class GodotTopLevelImpl : ITopLevelImpl {
public double RenderScaling
=> 1.0;

public GodotSkiaSurface Surface {
get {
ThrowIfDisposed();
return _surface ?? throw new InvalidOperationException($"{nameof(Surface)} must be set");
}
public Size ClientSize {
get => _clientSize;
set {
var oldValue = _surface;
if (oldValue == value)
if (_clientSize.Equals(value))
return;

ThrowIfDisposed();

if (oldValue is not null)
oldValue.IsValid = false;

_surface = value;
ClientSize = new Size(value.GdTexture.GetWidth(), value.GdTexture.GetHeight());
}
}
_clientSize = value;

private void ThrowIfDisposed() {
if (_isDisposed)
throw new ObjectDisposedException(nameof(GodotTopLevelImpl));
}
if (_surface is not null) {
_surface.IsValid = false;
_surface = null;
}

public Size ClientSize {
get => _clientSize;
private set {
if (_clientSize == value)
if (_isDisposed)
return;

_clientSize = value;
Resized?.Invoke(value, PlatformResizeReason.Layout);
_surface = CreateSurface();
Resized?.Invoke(value, PlatformResizeReason.Unspecified);
}
}

public GodotSkiaSurface Surface
=> _surface ??= CreateSurface();

public Action<Size, PlatformResizeReason>? Resized { get;set; }

public Action? Closed { get; set; }

public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }

IEnumerable<object> ITopLevelImpl.Surfaces
=> GetSurfaces();

Expand All @@ -89,6 +75,8 @@ AcrylicPlatformCompensationLevels ITopLevelImpl.AcrylicCompensationLevels

Action<double>? ITopLevelImpl.ScalingChanged { get; set; }

public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }

Action? ITopLevelImpl.LostFocus { get;set; }

public GodotTopLevelImpl(GodotVkPlatformGraphics platformGraphics) {
Expand All @@ -97,6 +85,11 @@ public GodotTopLevelImpl(GodotVkPlatformGraphics platformGraphics) {
_compositor = new Compositor(new RenderLoop(RenderTimer, Dispatcher.UIThread), platformGraphics);
}

private GodotSkiaSurface CreateSurface()
=> _isDisposed
? throw new ObjectDisposedException(nameof(GodotTopLevelImpl))
: PlatformGraphics.GetSharedContext().CreateSurface(PixelSize.FromSize(_clientSize, RenderScaling));

private IEnumerable<object> GetSurfaces()
=> new object[] { Surface };

Expand Down
29 changes: 15 additions & 14 deletions src/JLeb.Estragonia/GodotVkSkiaGpu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,18 @@ IDisposable IPlatformGraphicsContext.EnsureCurrent()
? new GodotSkiaRenderTarget(surface, _grContext)
: null;

public GodotSkiaSurface CreateSurfaceFromTexture(Texture2D gdTexture) {
public GodotSkiaSurface CreateSurface(PixelSize size) {
size = new PixelSize(Math.Max(size.Width, 1), Math.Max(size.Height, 1));

var gdViewport = new SubViewport {
RenderTargetClearMode = SubViewport.ClearMode.Never,
TransparentBg = true,
RenderTargetUpdateMode = SubViewport.UpdateMode.Disabled,
Size = new Vector2I(size.Width, size.Height)
};

var gdTexture = gdViewport.GetTexture();

var gdRdTexture = RenderingServer.TextureGetRdTexture(gdTexture.GetRid());
if (!gdRdTexture.IsValid)
throw new InvalidOperationException("Couldn't get Godot rendering device texture");
Expand Down Expand Up @@ -117,7 +128,7 @@ public GodotSkiaSurface CreateSurfaceFromTexture(Texture2D gdTexture) {

var skSurface = SKSurface.Create(
_grContext,
new GRBackendRenderTarget(gdTexture.GetWidth(), gdTexture.GetHeight(), 1, grVkImageInfo),
new GRBackendRenderTarget(size.Width, size.Height, 1, grVkImageInfo),
GRSurfaceOrigin.TopLeft,
SKColorType.Rgba8888,
new SKSurfaceProperties(SKPixelGeometry.RgbHorizontal)
Expand All @@ -129,18 +140,8 @@ public GodotSkiaSurface CreateSurfaceFromTexture(Texture2D gdTexture) {
return new GodotSkiaSurface(skSurface, gdTexture);
}

ISkiaSurface ISkiaGpu.TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session) {
size = new PixelSize(Math.Max(size.Width, 1), Math.Max(size.Height, 1));

var viewport = new SubViewport {
RenderTargetClearMode = SubViewport.ClearMode.Never,
TransparentBg = true,
RenderTargetUpdateMode = SubViewport.UpdateMode.Disabled,
Size = new Vector2I(size.Width, size.Height)
};

return CreateSurfaceFromTexture(viewport.GetTexture());
}
ISkiaSurface ISkiaGpu.TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session)
=> CreateSurface(size);

public void Dispose()
=> _grContext.Dispose();
Expand Down

0 comments on commit e92fdf3

Please sign in to comment.