[android] cache `Join` and `Cap` enum values (#24248)

Context: https://github.com/dotnet/maui/issues/23991
Context: https://github.com/chabiss/periodictable

In the above sample, a lot of time is spent in:

    921.46ms (4.3%) mono!Android.Graphics.Paint.Cap.get_Butt()
    872.40ms (4.1%) mono!Android.Graphics.Paint.Join.get_Miter()

This exposes a performance issue with `Java.Lang.Enum` values:

* `java.lang.Enum` in Java are objects (not `int` like C#)

* When accessing an enum value, Java returns an object we have to wrap
  in a C# object.

* .NET for Android has to do bookkeeping around this, lookup in a hash
  table, etc.

To avoid this, we can store the `Join` and `Cap` values in a static
field and avoid calling into Java. This approach is already working
in .NET MAUI for `ImageView.ScaleType`:

9361f90a5d/src/Core/src/Platform/Android/AspectExtensions.cs (L7-L10)

After this change, the time spent is completely gone:

    2.41ms (0.02%) mono.android!Android.Graphics.Paint.Join.get_Miter()

I can't find the same call for (the unfortunately named) `get_Butt()`
at all.

In the future, we might consider changing the C# binding for
`Java.Lang.Enum` to "auto-cache" values in C# static fields. Not sure
if there is enough time left for it to happen in .NET 9, though.
This commit is contained in:
Jonathan Peppers 2024-08-16 11:54:01 -05:00 коммит произвёл GitHub
Родитель 0370361000
Коммит a02f1d420f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
1 изменённых файлов: 20 добавлений и 26 удалений

Просмотреть файл

@ -15,6 +15,14 @@ namespace Microsoft.Maui.Graphics
{
public class MauiDrawable : PaintDrawable
{
static Join? JoinMiter;
static Join? JoinBevel;
static Join? JoinRound;
static Cap? CapButt;
static Cap? CapSquare;
static Cap? CapRound;
readonly AContext? _context;
readonly float _density;
@ -298,20 +306,13 @@ namespace Microsoft.Maui.Graphics
public void SetBorderLineJoin(LineJoin lineJoin)
{
Join? aLineJoin = Join.Miter;
switch (lineJoin)
Join? aLineJoin = lineJoin switch
{
case LineJoin.Miter:
aLineJoin = Join.Miter;
break;
case LineJoin.Bevel:
aLineJoin = Join.Bevel;
break;
case LineJoin.Round:
aLineJoin = Join.Round;
break;
}
LineJoin.Miter => JoinMiter ??= Join.Miter,
LineJoin.Bevel => JoinBevel ??= Join.Bevel,
LineJoin.Round => JoinRound ??= Join.Round,
_ => JoinMiter ??= Join.Miter,
};
if (_strokeLineJoin == aLineJoin)
return;
@ -323,20 +324,13 @@ namespace Microsoft.Maui.Graphics
public void SetBorderLineCap(LineCap lineCap)
{
Cap? aLineCap = Cap.Butt;
switch (lineCap)
Cap? aLineCap = lineCap switch
{
case LineCap.Butt:
aLineCap = Cap.Butt;
break;
case LineCap.Square:
aLineCap = Cap.Square;
break;
case LineCap.Round:
aLineCap = Cap.Round;
break;
}
LineCap.Butt => CapButt ??= Cap.Butt,
LineCap.Square => CapSquare ??= Cap.Square,
LineCap.Round => CapRound ??= Cap.Round,
_ => CapButt ??= Cap.Butt,
};
if (_strokeLineCap == aLineCap)
return;