diff --git a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs index a3cad3cebd..2dd15dfedc 100644 --- a/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs +++ b/src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Server; @@ -27,6 +28,37 @@ public record struct BatchStreamSegment public int ElementCount { get; set; } } + +// Unsafe.ReadUnaligned/Unsafe.WriteUnaligned are broken on arm, +// see https://github.com/dotnet/runtime/issues/80068 +static unsafe class UnalignedMemoryHelper +{ + public static T ReadUnaligned(byte* src) where T : unmanaged + { +#if NET6_0_OR_GREATER + Unsafe.SkipInit(out var rv); +#else + T rv; +#endif + UnalignedMemcpy((byte*)&rv, src, Unsafe.SizeOf()); + return rv; + } + + public static void WriteUnaligned(byte* dst, T value) where T : unmanaged + { + UnalignedMemcpy(dst, (byte*)&value, Unsafe.SizeOf()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static unsafe void UnalignedMemcpy(byte* dst, byte* src, int count) + { + for (var c = 0; c < count; c++) + { + dst[c] = src[c]; + } + } +} + internal class BatchStreamWriter : IDisposable { private readonly BatchStreamData _output; @@ -74,7 +106,15 @@ internal class BatchStreamWriter : IDisposable var size = Unsafe.SizeOf(); if (_currentDataSegment.Data == IntPtr.Zero || _currentDataSegment.ElementCount + size > _memoryPool.BufferSize) NextDataSegment(); - Unsafe.WriteUnaligned((byte*)_currentDataSegment.Data + _currentDataSegment.ElementCount, item); + var ptr = (byte*)_currentDataSegment.Data + _currentDataSegment.ElementCount; + + // Unsafe.ReadUnaligned/Unsafe.WriteUnaligned are broken on arm32, + // see https://github.com/dotnet/runtime/issues/80068 + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) + UnalignedMemoryHelper.WriteUnaligned(ptr, item); + else + *(T*)ptr = item; + _currentDataSegment.ElementCount += size; } @@ -125,7 +165,16 @@ internal class BatchStreamReader : IDisposable if (_memoryOffset + size > _currentDataSegment.ElementCount) throw new InvalidOperationException("Attempted to read more memory then left in the current segment"); - var rv = Unsafe.ReadUnaligned((byte*)_currentDataSegment.Data + _memoryOffset); + var ptr = (byte*)_currentDataSegment.Data + _memoryOffset; + T rv; + + // Unsafe.ReadUnaligned/Unsafe.WriteUnaligned are broken on arm32, + // see https://github.com/dotnet/runtime/issues/80068 + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm) + rv = UnalignedMemoryHelper.ReadUnaligned(ptr); + else + rv = *(T*)ptr; + _memoryOffset += size; if (_memoryOffset == _currentDataSegment.ElementCount) {