Skip to content

Commit a86d4bd

Browse files
committed
Refactored remaining dependencies in the Toolkit
1 parent d6c44da commit a86d4bd

10 files changed

+53
-30
lines changed

Microsoft.Toolkit.Uwp.UI.Animations/Builders/AnimationBuilder.Factories.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,33 @@ public Timeline GetAnimation(DependencyObject targetHint)
228228
private TValue GetToAs<TValue>()
229229
where TValue : unmanaged
230230
{
231-
T to = To;
232-
233-
return Unsafe.As<T, TValue>(ref to);
231+
// We employ this (T2)(object)t1 pattern multiple times in this library to alter generics.
232+
// This is an equivalent but safer alternative to using Unsafe.As<TFrom, TTo>(ref TFrom).
233+
// For instance, this method will result in the following IL being emitted:
234+
// =============================
235+
// IL_0000: ldarg.0
236+
// IL_0001: call instance !0 class AnimationFactory`1<!T>::get_To()
237+
// IL_0006: box !T
238+
// IL_000b: unbox.any !!TValue
239+
// IL_0010: ret
240+
// =============================
241+
// The key value is that the JIT (and AOT compilers such as .NET Native) can recognize this
242+
// pattern and optimize the boxing away in case the types match. This is the case whenever
243+
// the generic arguments are value types, which due to generic types in .NET being reified
244+
// results in a completely different generic instantiation of the same method, making the
245+
// type arguments effectively constant values known at compile time, ie. at JIT time.
246+
// As a result of this, the boxing is completely avoided and the value is returned directly.
247+
// Leveraging this pattern lets us keep the same optimal codegen while avoiding the extra
248+
// NuGet package dependency on UWP, and the more dangerous path using the Unsafe APIs.
249+
// As an example, assuming T is float, the JIT will produce the following codegen on x64:
250+
// =============================
251+
// L0000: vzeroupper
252+
// L0003: vmovss xmm0, [rcx+8]
253+
// L0008: ret
254+
// =============================
255+
// We can see how the property value is loaded directly from the underlying field and
256+
// then returned to the caller: no boxing or unwanted overhead is introduced at all.
257+
return (TValue)(object)To;
234258
}
235259

236260
/// <summary>
@@ -250,7 +274,7 @@ private TValue GetToAs<TValue>()
250274

251275
T from = From.GetValueOrDefault();
252276

253-
return Unsafe.As<T, TValue>(ref from);
277+
return (TValue)(object)from;
254278
}
255279
}
256280

Microsoft.Toolkit.Uwp.UI.Animations/Builders/Helpers/ListBuilder{T}.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
namespace Microsoft.Toolkit.Uwp.UI.Animations.Builders.Helpers
1010
{
1111
/// <summary>
12-
/// A small generic builder type that allows to create <see cref="ReadOnlySpan{T}"/> instances.
12+
/// A small generic builder type that allows to create <see cref="ArraySegment{T}"/> instances.
1313
/// </summary>
1414
/// <typeparam name="T">The type of items to create a sequence of.</typeparam>
1515
internal struct ListBuilder<T>
@@ -56,14 +56,14 @@ public void Append(T item)
5656
}
5757

5858
/// <summary>
59-
/// Gets a <see cref="ReadOnlySpan{T}"/> instance with the current items.
59+
/// Gets a <see cref="ArraySegment{T}"/> instance with the current items.
6060
/// </summary>
61-
/// <returns>A <see cref="ReadOnlySpan{T}"/> instance with the current items.</returns>
61+
/// <returns>A <see cref="ArraySegment{T}"/> instance with the current items.</returns>
6262
[Pure]
6363
[MethodImpl(MethodImplOptions.AggressiveInlining)]
64-
public ReadOnlySpan<T> AsSpan()
64+
public ArraySegment<T> GetArraySegment()
6565
{
66-
return this.array.AsSpan(0, this.index);
66+
return new(this.array, 0, this.index);
6767
}
6868
}
6969
}

Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Composition.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static CompositionAnimation GetAnimation<TKeyFrame>(
3232
TimeSpan? delay,
3333
TimeSpan duration,
3434
RepeatOption repeat,
35-
ReadOnlySpan<TKeyFrame> keyFrames)
35+
ArraySegment<TKeyFrame> keyFrames)
3636
where TKeyFrame : struct, IKeyFrameInfo
3737
{
3838
KeyFrameAnimation animation;
@@ -41,7 +41,7 @@ public static CompositionAnimation GetAnimation<TKeyFrame>(
4141
{
4242
BooleanKeyFrameAnimation boolAnimation = target.Compositor.CreateBooleanKeyFrameAnimation();
4343

44-
foreach (ref readonly var keyFrame in keyFrames)
44+
foreach (var keyFrame in keyFrames)
4545
{
4646
if (keyFrame.TryInsertExpressionKeyFrame(boolAnimation, duration))
4747
{
@@ -57,7 +57,7 @@ public static CompositionAnimation GetAnimation<TKeyFrame>(
5757
{
5858
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
5959

60-
foreach (ref readonly var keyFrame in keyFrames)
60+
foreach (var keyFrame in keyFrames)
6161
{
6262
if (keyFrame.TryInsertExpressionKeyFrame(scalarAnimation, duration))
6363
{
@@ -82,7 +82,7 @@ public static CompositionAnimation GetAnimation<TKeyFrame>(
8282
{
8383
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
8484

85-
foreach (ref readonly var keyFrame in keyFrames)
85+
foreach (var keyFrame in keyFrames)
8686
{
8787
if (keyFrame.TryInsertExpressionKeyFrame(scalarAnimation, duration))
8888
{
@@ -107,7 +107,7 @@ public static CompositionAnimation GetAnimation<TKeyFrame>(
107107
{
108108
Vector2KeyFrameAnimation vector2Animation = target.Compositor.CreateVector2KeyFrameAnimation();
109109

110-
foreach (ref readonly var keyFrame in keyFrames)
110+
foreach (var keyFrame in keyFrames)
111111
{
112112
if (keyFrame.TryInsertExpressionKeyFrame(vector2Animation, duration))
113113
{
@@ -132,7 +132,7 @@ public static CompositionAnimation GetAnimation<TKeyFrame>(
132132
{
133133
Vector3KeyFrameAnimation vector3Animation = target.Compositor.CreateVector3KeyFrameAnimation();
134134

135-
foreach (ref readonly var keyFrame in keyFrames)
135+
foreach (var keyFrame in keyFrames)
136136
{
137137
if (keyFrame.TryInsertExpressionKeyFrame(vector3Animation, duration))
138138
{
@@ -157,7 +157,7 @@ public static CompositionAnimation GetAnimation<TKeyFrame>(
157157
{
158158
Vector4KeyFrameAnimation vector4Animation = target.Compositor.CreateVector4KeyFrameAnimation();
159159

160-
foreach (ref readonly var keyFrame in keyFrames)
160+
foreach (var keyFrame in keyFrames)
161161
{
162162
if (keyFrame.TryInsertExpressionKeyFrame(vector4Animation, duration))
163163
{
@@ -182,7 +182,7 @@ public static CompositionAnimation GetAnimation<TKeyFrame>(
182182
{
183183
ColorKeyFrameAnimation colorAnimation = target.Compositor.CreateColorKeyFrameAnimation();
184184

185-
foreach (ref readonly var keyFrame in keyFrames)
185+
foreach (var keyFrame in keyFrames)
186186
{
187187
if (keyFrame.TryInsertExpressionKeyFrame(colorAnimation, duration))
188188
{
@@ -207,7 +207,7 @@ public static CompositionAnimation GetAnimation<TKeyFrame>(
207207
{
208208
QuaternionKeyFrameAnimation quaternionAnimation = target.Compositor.CreateQuaternionKeyFrameAnimation();
209209

210-
foreach (ref readonly var keyFrame in keyFrames)
210+
foreach (var keyFrame in keyFrames)
211211
{
212212
if (keyFrame.TryInsertExpressionKeyFrame(quaternionAnimation, duration))
213213
{
@@ -283,7 +283,7 @@ public CompositionAnimation GetAnimation(CompositionObject targetHint, out Compo
283283
this.delay,
284284
this.duration,
285285
this.repeat,
286-
this.keyFrames.AsSpan());
286+
this.keyFrames.GetArraySegment());
287287
}
288288
}
289289
}

Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public Timeline GetAnimation(DependencyObject targetHint)
4545
this.delay,
4646
this.duration,
4747
this.repeat,
48-
this.keyFrames.AsSpan());
48+
this.keyFrames.GetArraySegment());
4949
}
5050
}
5151
}

Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public KeyFrameInfo(
150150
[MethodImpl(MethodImplOptions.AggressiveInlining)]
151151
public TValue GetValueAs<TValue>()
152152
{
153-
return Unsafe.As<T, TValue>(ref Unsafe.AsRef(in this.value));
153+
return (TValue)(object)this.value;
154154
}
155155

156156
/// <inheritdoc/>

Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Composition.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ public CompositionAnimation GetAnimation(CompositionObject targetHint, out Compo
4747

4848
// We can retrieve the total duration from the last timed keyframe, and then set
4949
// this as the target duration and use it to normalize the keyframe progresses.
50-
ReadOnlySpan<KeyFrameInfo> keyFrames = this.keyFrames.AsSpan();
51-
TimeSpan duration = keyFrames[keyFrames.Length - 1].GetTimedProgress(default);
50+
ArraySegment<KeyFrameInfo> keyFrames = this.keyFrames.GetArraySegment();
51+
TimeSpan duration = keyFrames[keyFrames.Count - 1].GetTimedProgress(default);
5252

5353
return NormalizedKeyFrameAnimationBuilder<T>.GetAnimation(
5454
targetHint,

Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Xaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static Timeline GetAnimation<TKeyFrame>(
3131
TimeSpan? delay,
3232
TimeSpan duration,
3333
RepeatOption repeat,
34-
ReadOnlySpan<TKeyFrame> keyFrames)
34+
ArraySegment<TKeyFrame> keyFrames)
3535
where TKeyFrame : struct, IKeyFrameInfo
3636
{
3737
Timeline animation;
@@ -164,7 +164,7 @@ public Timeline GetAnimation(DependencyObject targetHint)
164164
this.delay,
165165
default,
166166
this.repeat,
167-
this.keyFrames.AsSpan());
167+
this.keyFrames.GetArraySegment());
168168
}
169169
}
170170
}

Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public KeyFrameInfo(
143143
[MethodImpl(MethodImplOptions.AggressiveInlining)]
144144
public TValue GetValueAs<TValue>()
145145
{
146-
return Unsafe.As<T, TValue>(ref Unsafe.AsRef(in this.value));
146+
return (TValue)(object)this.value;
147147
}
148148

149149
/// <inheritdoc/>

Microsoft.Toolkit.Uwp.UI/Extensions/Visual/VisualExtensions.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Globalization;
77
using System.Linq;
88
using System.Numerics;
9-
using System.Runtime.CompilerServices;
109
using Windows.UI.Composition;
1110
using Windows.UI.Xaml;
1211
using Windows.UI.Xaml.Hosting;
@@ -165,11 +164,11 @@ public static Vector4 ToVector4(this string str)
165164
/// </summary>
166165
/// <param name="str">A string in the format of "float, float, float, float"</param>
167166
/// <returns><see cref="Quaternion"/></returns>
168-
public static Quaternion ToQuaternion(this string str)
167+
public static unsafe Quaternion ToQuaternion(this string str)
169168
{
170169
Vector4 vector = str.ToVector4();
171170

172-
return Unsafe.As<Vector4, Quaternion>(ref vector);
171+
return *(Quaternion*)&vector;
173172
}
174173

175174
/// <summary>

Microsoft.Toolkit.Uwp.UI/Microsoft.Toolkit.Uwp.UI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@
4040
- ThemeListener: Class which listens for changes to Application Theme or High Contrast Modes and Signals an Event when they occur.
4141
</Description>
4242
<PackageTags>UWP Toolkit Windows UI Converters XAML extensions helpers</PackageTags>
43-
4443
<UseWindowsDesktopSdk>true</UseWindowsDesktopSdk>
44+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
4545
</PropertyGroup>
4646

4747
<ItemGroup>

0 commit comments

Comments
 (0)